Skip to content

Commit 3579777

Browse files
committed
feat(cmd/rofl): Add support for multiple deployments
1 parent e7a0f48 commit 3579777

File tree

7 files changed

+177
-96
lines changed

7 files changed

+177
-96
lines changed

build/rofl/manifest.go

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,31 +34,21 @@ const (
3434

3535
// Manifest is the ROFL app manifest that configures various aspects of the app in a single place.
3636
type Manifest struct {
37-
// AppID is the Bech32-encoded ROFL app ID.
38-
AppID string `yaml:"app_id" json:"app_id"`
3937
// Name is the human readable ROFL app name.
4038
Name string `yaml:"name" json:"name"`
4139
// Version is the ROFL app version.
4240
Version string `yaml:"version" json:"version"`
43-
// Network is the identifier of the network to deploy to by default.
44-
Network string `yaml:"network,omitempty" json:"network,omitempty"`
45-
// ParaTime is the identifier of the paratime to deploy to by default.
46-
ParaTime string `yaml:"paratime,omitempty" json:"paratime,omitempty"`
47-
// Admin is the identifier of the admin account.
48-
Admin string `yaml:"admin,omitempty" json:"admin,omitempty"`
4941
// TEE is the type of TEE to build for.
5042
TEE string `yaml:"tee" json:"tee"`
5143
// Kind is the kind of ROFL app to build.
5244
Kind string `yaml:"kind" json:"kind"`
53-
// TrustRoot is the optional trust root configuration.
54-
TrustRoot *TrustRootConfig `yaml:"trust_root,omitempty" json:"trust_root,omitempty"`
5545
// Resources are the requested ROFL app resources.
5646
Resources ResourcesConfig `yaml:"resources" json:"resources"`
5747
// Artifacts are the optional artifact location overrides.
5848
Artifacts *ArtifactsConfig `yaml:"artifacts,omitempty" json:"artifacts,omitempty"`
5949

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

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

112102
// Validate validates the manifest for correctness.
113103
func (m *Manifest) Validate() error {
114-
if len(m.AppID) == 0 {
115-
return fmt.Errorf("app ID cannot be empty")
116-
}
117-
var appID rofl.AppID
118-
if err := appID.UnmarshalText([]byte(m.AppID)); err != nil {
119-
return fmt.Errorf("malformed app ID: %w", err)
120-
}
121-
122104
if len(m.Name) == 0 {
123105
return fmt.Errorf("name cannot be empty")
124106
}
@@ -150,6 +132,18 @@ func (m *Manifest) Validate() error {
150132
return fmt.Errorf("bad resources config: %w", err)
151133
}
152134

135+
for name, d := range m.Deployments {
136+
if d == nil {
137+
return fmt.Errorf("bad deployment: %s", name)
138+
}
139+
if err := d.Validate(); err != nil {
140+
return fmt.Errorf("bad deployment '%s': %w", name, err)
141+
}
142+
}
143+
if _, ok := m.Deployments[DefaultDeploymentName]; !ok {
144+
return fmt.Errorf("must define at least the '%s' deployment", DefaultDeploymentName)
145+
}
146+
153147
return nil
154148
}
155149

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

156+
// DefaultDeploymentName is the name of the default deployment that must always be defined and is
157+
// used in case no deployment is passed.
158+
const DefaultDeploymentName = "default"
159+
160+
// Deployment describes a single ROFL app deployment.
161+
type Deployment struct {
162+
// AppID is the Bech32-encoded ROFL app ID.
163+
AppID string `yaml:"app_id" json:"app_id"`
164+
// Network is the identifier of the network to deploy to.
165+
Network string `yaml:"network" json:"network"`
166+
// ParaTime is the identifier of the paratime to deploy to.
167+
ParaTime string `yaml:"paratime" json:"paratime"`
168+
// Admin is the identifier of the admin account.
169+
Admin string `yaml:"admin,omitempty" json:"admin,omitempty"`
170+
// TrustRoot is the optional trust root configuration.
171+
TrustRoot *TrustRootConfig `yaml:"trust_root,omitempty" json:"trust_root,omitempty"`
172+
// Policy is the ROFL app policy.
173+
Policy *rofl.AppAuthPolicy `yaml:"policy,omitempty" json:"policy,omitempty"`
174+
}
175+
176+
// Validate validates the manifest for correctness.
177+
func (d *Deployment) Validate() error {
178+
if len(d.AppID) == 0 {
179+
return fmt.Errorf("app ID cannot be empty")
180+
}
181+
var appID rofl.AppID
182+
if err := appID.UnmarshalText([]byte(d.AppID)); err != nil {
183+
return fmt.Errorf("malformed app ID: %w", err)
184+
}
185+
if d.Network == "" {
186+
return fmt.Errorf("network cannot be empty")
187+
}
188+
if d.ParaTime == "" {
189+
return fmt.Errorf("paratime cannot be empty")
190+
}
191+
return nil
192+
}
193+
162194
// TrustRootConfig is the trust root configuration.
163195
type TrustRootConfig struct {
164196
// Height is the consensus layer block height where to take the trust root.

cmd/rofl/build/build.go

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ const (
3232
)
3333

3434
var (
35-
outputFn string
36-
buildMode string
37-
offline bool
38-
doUpdate bool
35+
outputFn string
36+
buildMode string
37+
offline bool
38+
doUpdate bool
39+
deploymentName string
3940

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

4950
fmt.Println("Building a ROFL application...")
50-
fmt.Printf("App ID: %s\n", manifest.AppID)
51-
fmt.Printf("Name: %s\n", manifest.Name)
52-
fmt.Printf("Version: %s\n", manifest.Version)
53-
fmt.Printf("TEE: %s\n", manifest.TEE)
54-
fmt.Printf("Kind: %s\n", manifest.Kind)
51+
fmt.Printf("Deployment: %s\n", deploymentName)
52+
fmt.Printf("Network: %s\n", deployment.Network)
53+
fmt.Printf("ParaTime: %s\n", deployment.ParaTime)
54+
fmt.Printf("App ID: %s\n", deployment.AppID)
55+
fmt.Printf("Name: %s\n", manifest.Name)
56+
fmt.Printf("Version: %s\n", manifest.Version)
57+
fmt.Printf("TEE: %s\n", manifest.TEE)
58+
fmt.Printf("Kind: %s\n", manifest.Kind)
5559

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

6367
bnd := &bundle.Bundle{
6468
Manifest: &bundle.Manifest{
65-
Name: manifest.AppID,
69+
Name: deployment.AppID,
6670
ID: npa.ParaTime.Namespace(),
6771
},
6872
}
@@ -80,14 +84,14 @@ var (
8084
return
8185
}
8286

83-
sgxBuild(npa, manifest, bnd)
87+
sgxBuild(npa, manifest, deployment, bnd)
8488
case buildRofl.TEETypeTDX:
8589
// TDX.
8690
switch manifest.Kind {
8791
case buildRofl.AppKindRaw:
88-
err = tdxBuildRaw(tmpDir, npa, manifest, bnd)
92+
err = tdxBuildRaw(tmpDir, npa, manifest, deployment, bnd)
8993
case buildRofl.AppKindContainer:
90-
err = tdxBuildContainer(tmpDir, npa, manifest, bnd)
94+
err = tdxBuildContainer(tmpDir, npa, manifest, deployment, bnd)
9195
}
9296
default:
9397
fmt.Printf("unsupported TEE kind: %s\n", manifest.TEE)
@@ -99,7 +103,7 @@ var (
99103
}
100104

101105
// Write the bundle out.
102-
outFn := fmt.Sprintf("%s.orc", manifest.Name)
106+
outFn := fmt.Sprintf("%s.%s.orc", manifest.Name, deploymentName)
103107
if outputFn != "" {
104108
outFn = outputFn
105109
}
@@ -119,7 +123,7 @@ var (
119123
}
120124

121125
// Override the update manifest flag in case the policy does not exist.
122-
if manifest.Policy == nil {
126+
if deployment.Policy == nil {
123127
doUpdate = false
124128
}
125129

@@ -135,9 +139,9 @@ var (
135139
fmt.Println()
136140
case true:
137141
// Update the manifest with the given enclave identities, overwriting existing ones.
138-
manifest.Policy.Enclaves = make([]sgx.EnclaveIdentity, 0, len(eids))
142+
deployment.Policy.Enclaves = make([]sgx.EnclaveIdentity, 0, len(eids))
139143
for _, eid := range eids {
140-
manifest.Policy.Enclaves = append(manifest.Policy.Enclaves, *eid)
144+
deployment.Policy.Enclaves = append(deployment.Policy.Enclaves, *eid)
141145
}
142146

143147
// Serialize manifest and write it to file.
@@ -173,12 +177,12 @@ func detectBuildMode(npa *common.NPASelection) {
173177
}
174178
}
175179

176-
func setupBuildEnv(manifest *buildRofl.Manifest, npa *common.NPASelection) {
180+
func setupBuildEnv(deployment *buildRofl.Deployment, npa *common.NPASelection) {
177181
// Configure app ID.
178-
os.Setenv("ROFL_APP_ID", manifest.AppID)
182+
os.Setenv("ROFL_APP_ID", deployment.AppID)
179183

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

254259
Cmd.Flags().AddFlagSet(buildFlags)
255260
}

cmd/rofl/build/container.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@ const (
2121
)
2222

2323
// tdxBuildContainer builds a TDX-based container ROFL app.
24-
func tdxBuildContainer(tmpDir string, npa *common.NPASelection, manifest *buildRofl.Manifest, bnd *bundle.Bundle) error {
24+
func tdxBuildContainer(
25+
tmpDir string,
26+
npa *common.NPASelection,
27+
manifest *buildRofl.Manifest,
28+
deployment *buildRofl.Deployment,
29+
bnd *bundle.Bundle,
30+
) error {
2531
fmt.Println("Building a container-based TDX ROFL application...")
2632

2733
tdxStage2TemplateURI = defaultContainerStage2TemplateURI
@@ -54,11 +60,11 @@ func tdxBuildContainer(tmpDir string, npa *common.NPASelection, manifest *buildR
5460
// Configure app ID.
5561
var extraKernelOpts []string
5662
extraKernelOpts = append(extraKernelOpts,
57-
fmt.Sprintf("ROFL_APP_ID=%s", manifest.AppID),
63+
fmt.Sprintf("ROFL_APP_ID=%s", deployment.AppID),
5864
)
5965

6066
// Obtain and configure trust root.
61-
trustRoot, err := fetchTrustRoot(npa, manifest.TrustRoot)
67+
trustRoot, err := fetchTrustRoot(npa, deployment.TrustRoot)
6268
if err != nil {
6369
return err
6470
}

cmd/rofl/build/sgx.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,16 @@ import (
2424
)
2525

2626
// sgxBuild builds an SGX-based "raw" ROFL app.
27-
func sgxBuild(npa *common.NPASelection, manifest *buildRofl.Manifest, bnd *bundle.Bundle) {
27+
func sgxBuild(
28+
npa *common.NPASelection,
29+
manifest *buildRofl.Manifest,
30+
deployment *buildRofl.Deployment,
31+
bnd *bundle.Bundle,
32+
) {
2833
fmt.Println("Building an SGX-based Rust ROFL application...")
2934

3035
detectBuildMode(npa)
31-
features := sgxSetupBuildEnv(manifest, npa)
36+
features := sgxSetupBuildEnv(deployment, npa)
3237

3338
// First build for the default target.
3439
fmt.Println("Building ELF binary...")
@@ -209,8 +214,8 @@ NextSetOfPrimes:
209214
}
210215

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

215220
switch buildMode {
216221
case buildModeProduction, buildModeAuto:

cmd/rofl/build/tdx.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,21 @@ var (
3434
)
3535

3636
// tdxBuildRaw builds a TDX-based "raw" ROFL app.
37-
func tdxBuildRaw(tmpDir string, npa *common.NPASelection, manifest *buildRofl.Manifest, bnd *bundle.Bundle) error {
37+
func tdxBuildRaw(
38+
tmpDir string,
39+
npa *common.NPASelection,
40+
manifest *buildRofl.Manifest,
41+
deployment *buildRofl.Deployment,
42+
bnd *bundle.Bundle,
43+
) error {
3844
wantedArtifacts := tdxGetDefaultArtifacts()
3945
tdxOverrideArtifacts(manifest, wantedArtifacts)
4046
artifacts := tdxFetchArtifacts(wantedArtifacts)
4147

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

4450
detectBuildMode(npa)
45-
tdxSetupBuildEnv(manifest, npa)
51+
tdxSetupBuildEnv(deployment, npa)
4652

4753
// Obtain package metadata.
4854
pkgMeta, err := cargo.GetMetadata()
@@ -246,7 +252,10 @@ func tdxBundleComponent(
246252
return err
247253
}
248254

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

251260
comp.TDX.ExtraKernelOptions = append(comp.TDX.ExtraKernelOptions,
252261
"oasis.stage2.storage_mode=disk",
@@ -282,8 +291,8 @@ func tdxBundleComponent(
282291
}
283292

284293
// tdxSetupBuildEnv sets up the TDX build environment.
285-
func tdxSetupBuildEnv(manifest *buildRofl.Manifest, npa *common.NPASelection) {
286-
setupBuildEnv(manifest, npa)
294+
func tdxSetupBuildEnv(deployment *buildRofl.Deployment, npa *common.NPASelection) {
295+
setupBuildEnv(deployment, npa)
287296

288297
switch buildMode {
289298
case buildModeProduction, buildModeAuto:

0 commit comments

Comments
 (0)