Skip to content

Commit

Permalink
feat(cmd/rofl): Add support for multiple deployments
Browse files Browse the repository at this point in the history
  • Loading branch information
kostko committed Jan 14, 2025
1 parent e7a0f48 commit 3579777
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 96 deletions.
72 changes: 52 additions & 20 deletions build/rofl/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,31 +34,21 @@ const (

// Manifest is the ROFL app manifest that configures various aspects of the app in a single place.
type Manifest struct {
// AppID is the Bech32-encoded ROFL app ID.
AppID string `yaml:"app_id" json:"app_id"`
// Name is the human readable ROFL app name.
Name string `yaml:"name" json:"name"`
// Version is the ROFL app version.
Version string `yaml:"version" json:"version"`
// Network is the identifier of the network to deploy to by default.
Network string `yaml:"network,omitempty" json:"network,omitempty"`
// ParaTime is the identifier of the paratime to deploy to by default.
ParaTime string `yaml:"paratime,omitempty" json:"paratime,omitempty"`
// Admin is the identifier of the admin account.
Admin string `yaml:"admin,omitempty" json:"admin,omitempty"`
// TEE is the type of TEE to build for.
TEE string `yaml:"tee" json:"tee"`
// Kind is the kind of ROFL app to build.
Kind string `yaml:"kind" json:"kind"`
// TrustRoot is the optional trust root configuration.
TrustRoot *TrustRootConfig `yaml:"trust_root,omitempty" json:"trust_root,omitempty"`
// Resources are the requested ROFL app resources.
Resources ResourcesConfig `yaml:"resources" json:"resources"`
// Artifacts are the optional artifact location overrides.
Artifacts *ArtifactsConfig `yaml:"artifacts,omitempty" json:"artifacts,omitempty"`

// Policy is the ROFL app policy to deploy by default.
Policy *rofl.AppAuthPolicy `yaml:"policy,omitempty" json:"policy,omitempty"`
// Deployments are the ROFL app deployments.
Deployments map[string]*Deployment `yaml:"deployments" json:"deployments"`

// sourceFn is the filename from which the manifest has been loaded.
sourceFn string
Expand Down Expand Up @@ -111,14 +101,6 @@ func LoadManifest() (*Manifest, error) {

// Validate validates the manifest for correctness.
func (m *Manifest) Validate() error {
if len(m.AppID) == 0 {
return fmt.Errorf("app ID cannot be empty")
}
var appID rofl.AppID
if err := appID.UnmarshalText([]byte(m.AppID)); err != nil {
return fmt.Errorf("malformed app ID: %w", err)
}

if len(m.Name) == 0 {
return fmt.Errorf("name cannot be empty")
}
Expand Down Expand Up @@ -150,6 +132,18 @@ func (m *Manifest) Validate() error {
return fmt.Errorf("bad resources config: %w", err)
}

for name, d := range m.Deployments {
if d == nil {
return fmt.Errorf("bad deployment: %s", name)
}
if err := d.Validate(); err != nil {
return fmt.Errorf("bad deployment '%s': %w", name, err)
}
}
if _, ok := m.Deployments[DefaultDeploymentName]; !ok {
return fmt.Errorf("must define at least the '%s' deployment", DefaultDeploymentName)
}

return nil
}

Expand All @@ -159,6 +153,44 @@ func (m *Manifest) SourceFileName() string {
return m.sourceFn
}

// DefaultDeploymentName is the name of the default deployment that must always be defined and is
// used in case no deployment is passed.
const DefaultDeploymentName = "default"

// Deployment describes a single ROFL app deployment.
type Deployment struct {
// AppID is the Bech32-encoded ROFL app ID.
AppID string `yaml:"app_id" json:"app_id"`
// Network is the identifier of the network to deploy to.
Network string `yaml:"network" json:"network"`
// ParaTime is the identifier of the paratime to deploy to.
ParaTime string `yaml:"paratime" json:"paratime"`
// Admin is the identifier of the admin account.
Admin string `yaml:"admin,omitempty" json:"admin,omitempty"`
// TrustRoot is the optional trust root configuration.
TrustRoot *TrustRootConfig `yaml:"trust_root,omitempty" json:"trust_root,omitempty"`
// Policy is the ROFL app policy.
Policy *rofl.AppAuthPolicy `yaml:"policy,omitempty" json:"policy,omitempty"`
}

// Validate validates the manifest for correctness.
func (d *Deployment) Validate() error {
if len(d.AppID) == 0 {
return fmt.Errorf("app ID cannot be empty")
}
var appID rofl.AppID
if err := appID.UnmarshalText([]byte(d.AppID)); err != nil {
return fmt.Errorf("malformed app ID: %w", err)
}
if d.Network == "" {
return fmt.Errorf("network cannot be empty")
}
if d.ParaTime == "" {
return fmt.Errorf("paratime cannot be empty")
}
return nil
}

// TrustRootConfig is the trust root configuration.
type TrustRootConfig struct {
// Height is the consensus layer block height where to take the trust root.
Expand Down
47 changes: 26 additions & 21 deletions cmd/rofl/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ const (
)

var (
outputFn string
buildMode string
offline bool
doUpdate bool
outputFn string
buildMode string
offline bool
doUpdate bool
deploymentName string

Cmd = &cobra.Command{
Use: "build",
Expand All @@ -44,14 +45,17 @@ var (
Run: func(_ *cobra.Command, _ []string) {
cfg := cliConfig.Global()
npa := common.GetNPASelection(cfg)
manifest := roflCommon.LoadManifestAndSetNPA(cfg, npa)
manifest, deployment := roflCommon.LoadManifestAndSetNPA(cfg, npa, deploymentName)

fmt.Println("Building a ROFL application...")
fmt.Printf("App ID: %s\n", manifest.AppID)
fmt.Printf("Name: %s\n", manifest.Name)
fmt.Printf("Version: %s\n", manifest.Version)
fmt.Printf("TEE: %s\n", manifest.TEE)
fmt.Printf("Kind: %s\n", manifest.Kind)
fmt.Printf("Deployment: %s\n", deploymentName)
fmt.Printf("Network: %s\n", deployment.Network)
fmt.Printf("ParaTime: %s\n", deployment.ParaTime)
fmt.Printf("App ID: %s\n", deployment.AppID)
fmt.Printf("Name: %s\n", manifest.Name)
fmt.Printf("Version: %s\n", manifest.Version)
fmt.Printf("TEE: %s\n", manifest.TEE)
fmt.Printf("Kind: %s\n", manifest.Kind)

// Prepare temporary build directory.
tmpDir, err := os.MkdirTemp("", "oasis-build")
Expand All @@ -62,7 +66,7 @@ var (

bnd := &bundle.Bundle{
Manifest: &bundle.Manifest{
Name: manifest.AppID,
Name: deployment.AppID,
ID: npa.ParaTime.Namespace(),
},
}
Expand All @@ -80,14 +84,14 @@ var (
return
}

sgxBuild(npa, manifest, bnd)
sgxBuild(npa, manifest, deployment, bnd)
case buildRofl.TEETypeTDX:
// TDX.
switch manifest.Kind {
case buildRofl.AppKindRaw:
err = tdxBuildRaw(tmpDir, npa, manifest, bnd)
err = tdxBuildRaw(tmpDir, npa, manifest, deployment, bnd)
case buildRofl.AppKindContainer:
err = tdxBuildContainer(tmpDir, npa, manifest, bnd)
err = tdxBuildContainer(tmpDir, npa, manifest, deployment, bnd)
}
default:
fmt.Printf("unsupported TEE kind: %s\n", manifest.TEE)
Expand All @@ -99,7 +103,7 @@ var (
}

// Write the bundle out.
outFn := fmt.Sprintf("%s.orc", manifest.Name)
outFn := fmt.Sprintf("%s.%s.orc", manifest.Name, deploymentName)
if outputFn != "" {
outFn = outputFn
}
Expand All @@ -119,7 +123,7 @@ var (
}

// Override the update manifest flag in case the policy does not exist.
if manifest.Policy == nil {
if deployment.Policy == nil {
doUpdate = false
}

Expand All @@ -135,9 +139,9 @@ var (
fmt.Println()
case true:
// Update the manifest with the given enclave identities, overwriting existing ones.
manifest.Policy.Enclaves = make([]sgx.EnclaveIdentity, 0, len(eids))
deployment.Policy.Enclaves = make([]sgx.EnclaveIdentity, 0, len(eids))
for _, eid := range eids {
manifest.Policy.Enclaves = append(manifest.Policy.Enclaves, *eid)
deployment.Policy.Enclaves = append(deployment.Policy.Enclaves, *eid)
}

// Serialize manifest and write it to file.
Expand Down Expand Up @@ -173,12 +177,12 @@ func detectBuildMode(npa *common.NPASelection) {
}
}

func setupBuildEnv(manifest *buildRofl.Manifest, npa *common.NPASelection) {
func setupBuildEnv(deployment *buildRofl.Deployment, npa *common.NPASelection) {
// Configure app ID.
os.Setenv("ROFL_APP_ID", manifest.AppID)
os.Setenv("ROFL_APP_ID", deployment.AppID)

// Obtain and configure trust root.
trustRoot, err := fetchTrustRoot(npa, manifest.TrustRoot)
trustRoot, err := fetchTrustRoot(npa, deployment.TrustRoot)
cobra.CheckErr(err)
os.Setenv("ROFL_CONSENSUS_TRUST_ROOT", trustRoot)
}
Expand Down Expand Up @@ -250,6 +254,7 @@ func init() {
buildFlags.BoolVar(&offline, "offline", false, "do not perform any operations requiring network access")
buildFlags.StringVar(&outputFn, "output", "", "output bundle filename")
buildFlags.BoolVar(&doUpdate, "update-manifest", false, "automatically update the manifest")
buildFlags.StringVar(&deploymentName, "deployment", buildRofl.DefaultDeploymentName, "deployment name")

Cmd.Flags().AddFlagSet(buildFlags)
}
12 changes: 9 additions & 3 deletions cmd/rofl/build/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ const (
)

// tdxBuildContainer builds a TDX-based container ROFL app.
func tdxBuildContainer(tmpDir string, npa *common.NPASelection, manifest *buildRofl.Manifest, bnd *bundle.Bundle) error {
func tdxBuildContainer(
tmpDir string,
npa *common.NPASelection,
manifest *buildRofl.Manifest,
deployment *buildRofl.Deployment,
bnd *bundle.Bundle,
) error {
fmt.Println("Building a container-based TDX ROFL application...")

tdxStage2TemplateURI = defaultContainerStage2TemplateURI
Expand Down Expand Up @@ -54,11 +60,11 @@ func tdxBuildContainer(tmpDir string, npa *common.NPASelection, manifest *buildR
// Configure app ID.
var extraKernelOpts []string
extraKernelOpts = append(extraKernelOpts,
fmt.Sprintf("ROFL_APP_ID=%s", manifest.AppID),
fmt.Sprintf("ROFL_APP_ID=%s", deployment.AppID),
)

// Obtain and configure trust root.
trustRoot, err := fetchTrustRoot(npa, manifest.TrustRoot)
trustRoot, err := fetchTrustRoot(npa, deployment.TrustRoot)
if err != nil {
return err
}
Expand Down
13 changes: 9 additions & 4 deletions cmd/rofl/build/sgx.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,16 @@ import (
)

// sgxBuild builds an SGX-based "raw" ROFL app.
func sgxBuild(npa *common.NPASelection, manifest *buildRofl.Manifest, bnd *bundle.Bundle) {
func sgxBuild(
npa *common.NPASelection,
manifest *buildRofl.Manifest,
deployment *buildRofl.Deployment,
bnd *bundle.Bundle,
) {
fmt.Println("Building an SGX-based Rust ROFL application...")

detectBuildMode(npa)
features := sgxSetupBuildEnv(manifest, npa)
features := sgxSetupBuildEnv(deployment, npa)

// First build for the default target.
fmt.Println("Building ELF binary...")
Expand Down Expand Up @@ -209,8 +214,8 @@ NextSetOfPrimes:
}

// sgxSetupBuildEnv sets up the SGX build environment and returns the list of features to enable.
func sgxSetupBuildEnv(manifest *buildRofl.Manifest, npa *common.NPASelection) []string {
setupBuildEnv(manifest, npa)
func sgxSetupBuildEnv(deployment *buildRofl.Deployment, npa *common.NPASelection) []string {
setupBuildEnv(deployment, npa)

switch buildMode {
case buildModeProduction, buildModeAuto:
Expand Down
19 changes: 14 additions & 5 deletions cmd/rofl/build/tdx.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,21 @@ var (
)

// tdxBuildRaw builds a TDX-based "raw" ROFL app.
func tdxBuildRaw(tmpDir string, npa *common.NPASelection, manifest *buildRofl.Manifest, bnd *bundle.Bundle) error {
func tdxBuildRaw(
tmpDir string,
npa *common.NPASelection,
manifest *buildRofl.Manifest,
deployment *buildRofl.Deployment,
bnd *bundle.Bundle,
) error {
wantedArtifacts := tdxGetDefaultArtifacts()
tdxOverrideArtifacts(manifest, wantedArtifacts)
artifacts := tdxFetchArtifacts(wantedArtifacts)

fmt.Println("Building a TDX-based Rust ROFL application...")

detectBuildMode(npa)
tdxSetupBuildEnv(manifest, npa)
tdxSetupBuildEnv(deployment, npa)

// Obtain package metadata.
pkgMeta, err := cargo.GetMetadata()
Expand Down Expand Up @@ -246,7 +252,10 @@ func tdxBundleComponent(
return err
}

// TODO: For persistent disk, configure Stage2Persist flag and storage mode.
if tmpStorageKind == buildRofl.StorageKindDiskPersistent {
// TODO: For persistent disk, configure Stage2Persist flag and storage mode.
return fmt.Errorf("persistent disk not yet supported, use 'disk-ephemeral'")
}

comp.TDX.ExtraKernelOptions = append(comp.TDX.ExtraKernelOptions,
"oasis.stage2.storage_mode=disk",
Expand Down Expand Up @@ -282,8 +291,8 @@ func tdxBundleComponent(
}

// tdxSetupBuildEnv sets up the TDX build environment.
func tdxSetupBuildEnv(manifest *buildRofl.Manifest, npa *common.NPASelection) {
setupBuildEnv(manifest, npa)
func tdxSetupBuildEnv(deployment *buildRofl.Deployment, npa *common.NPASelection) {
setupBuildEnv(deployment, npa)

switch buildMode {
case buildModeProduction, buildModeAuto:
Expand Down
Loading

0 comments on commit 3579777

Please sign in to comment.