Skip to content

Commit

Permalink
feat(cmd/rofl): Separate init from create
Browse files Browse the repository at this point in the history
  • Loading branch information
kostko committed Jan 16, 2025
1 parent c4cac3f commit 402f28e
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 46 deletions.
28 changes: 21 additions & 7 deletions build/rofl/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,24 @@ func (m *Manifest) SourceFileName() string {
return m.sourceFn
}

// Save serializes the manifest and writes it to the file returned by `SourceFileName`, overwriting
// any previous manifest.
func (m *Manifest) Save() error {
data, err := yaml.Marshal(m)
if err != nil {
return err
}
return os.WriteFile(m.SourceFileName(), data, 0o644) //nolint: gosec
}

// 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"`
AppID string `yaml:"app_id,omitempty" json:"app_id,omitempty"`
// 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.
Expand All @@ -184,12 +194,11 @@ type Deployment struct {

// 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 len(d.AppID) > 0 {
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")
Expand All @@ -200,6 +209,11 @@ func (d *Deployment) Validate() error {
return nil
}

// HasAppID returns true iff the deployment has an application identifier set.
func (d *Deployment) HasAppID() bool {
return len(d.AppID) > 0
}

// TrustRootConfig is the trust root configuration.
type TrustRootConfig struct {
// Height is the consensus layer block height where to take the trust root.
Expand Down
7 changes: 2 additions & 5 deletions cmd/rofl/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (

"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"gopkg.in/yaml.v3"

coreCommon "github.com/oasisprotocol/oasis-core/go/common"
"github.com/oasisprotocol/oasis-core/go/common/cbor"
Expand Down Expand Up @@ -45,7 +44,7 @@ var (
Run: func(_ *cobra.Command, _ []string) {
cfg := cliConfig.Global()
npa := common.GetNPASelection(cfg)
manifest, deployment := roflCommon.LoadManifestAndSetNPA(cfg, npa, deploymentName)
manifest, deployment := roflCommon.LoadManifestAndSetNPA(cfg, npa, deploymentName, true)

fmt.Println("Building a ROFL application...")
fmt.Printf("Deployment: %s\n", deploymentName)
Expand Down Expand Up @@ -155,9 +154,7 @@ var (
deployment.Policy.Enclaves = append(deployment.Policy.Enclaves, *eid)
}

// Serialize manifest and write it to file.
data, _ := yaml.Marshal(manifest)
if err = os.WriteFile(manifest.SourceFileName(), data, 0o644); err != nil { //nolint: gosec
if err = manifest.Save(); err != nil {
cobra.CheckErr(fmt.Errorf("failed to update manifest: %w", err))
}
}
Expand Down
5 changes: 4 additions & 1 deletion cmd/rofl/common/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ import (
// selection.
//
// In case there is an error in loading the manifest, it aborts the application.
func LoadManifestAndSetNPA(cfg *config.Config, npa *common.NPASelection, deployment string) (*rofl.Manifest, *rofl.Deployment) {
func LoadManifestAndSetNPA(cfg *config.Config, npa *common.NPASelection, deployment string, needAppID bool) (*rofl.Manifest, *rofl.Deployment) {
manifest, d, err := MaybeLoadManifestAndSetNPA(cfg, npa, deployment)
cobra.CheckErr(err)
if needAppID && !d.HasAppID() {
cobra.CheckErr(fmt.Errorf("deployment '%s' does not have an app ID set, maybe you need to run `oasis rofl create`", deployment))
}
return manifest, d
}

Expand Down
83 changes: 50 additions & 33 deletions cmd/rofl/mgmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"os"
"path/filepath"

"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
Expand Down Expand Up @@ -36,11 +37,12 @@ var (
appTEE string
appKind string
deploymentName string
doUpdate bool

initCmd = &cobra.Command{
Use: "init <name> [--tee TEE] [--kind KIND]",
Short: "Create a new ROFL app and initialize the manifest",
Args: cobra.ExactArgs(1),
Use: "init [<name>] [--tee TEE] [--kind KIND]",
Short: "Initialize a ROFL app manifest",
Args: cobra.MaximumNArgs(1),
Run: func(_ *cobra.Command, args []string) {
cfg := cliConfig.Global()
npa := common.GetNPASelection(cfg)
Expand All @@ -57,7 +59,15 @@ var (
}

// TODO: Support an interactive mode.
appName := args[0]
var appName string
if len(args) > 0 {
appName = args[0]
} else {
// Infer from current directory.
wd, err := os.Getwd()
cobra.CheckErr(err)
appName = filepath.Base(wd)
}
// Fail in case there is an existing manifest.
if buildRofl.ManifestExists() {
cobra.CheckErr("refusing to overwrite existing manifest")
Expand All @@ -73,7 +83,6 @@ var (

// Generate manifest and a default policy which does not accept any enclaves.
deployment := &buildRofl.Deployment{
AppID: rofl.NewAppIDGlobalName("").String(), // Temporary for initial validation.
Network: npa.NetworkName,
ParaTime: npa.ParaTimeName,
Admin: npa.AccountName,
Expand Down Expand Up @@ -104,7 +113,7 @@ var (
Memory: 512,
CPUCount: 1,
Storage: &buildRofl.StorageConfig{
Kind: buildRofl.StorageKindDiskEphemeral,
Kind: buildRofl.StorageKindDiskPersistent,
Size: 512,
},
},
Expand All @@ -125,32 +134,15 @@ var (
fmt.Printf(" ParaTime: %s\n", deployment.ParaTime)
fmt.Printf(" Admin: %s\n", deployment.Admin)

idScheme, ok := identifierSchemes[scheme]
if !ok {
cobra.CheckErr(fmt.Errorf("unknown scheme %s", scheme))
}

// Register a new ROFL application to determine the identifier.
tx := rofl.NewCreateTx(nil, &rofl.Create{
Policy: *deployment.Policy,
Scheme: idScheme,
})

acc := common.LoadAccount(cfg, npa.AccountName)
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, nil)
cobra.CheckErr(err)

var appID rofl.AppID
common.BroadcastTransaction(ctx, npa.ParaTime, conn, sigTx, meta, &appID)
deployment.AppID = appID.String()

fmt.Printf("Created ROFL application: %s\n", appID)

// Serialize manifest and write it to file.
const manifestFn = "rofl.yml"
data, _ := yaml.Marshal(manifest)
if err = os.WriteFile("rofl.yml", data, 0o644); err != nil { //nolint: gosec
if err = os.WriteFile(manifestFn, data, 0o644); err != nil { //nolint: gosec
cobra.CheckErr(fmt.Errorf("failed to write manifest: %w", err))
}

fmt.Printf("Created manifest in '%s'.\n", manifestFn)
fmt.Printf("Run `oasis rofl create --update-manifest` to register your ROFL app and configure an app ID.\n")
},
}

Expand All @@ -163,11 +155,15 @@ var (
npa := common.GetNPASelection(cfg)
txCfg := common.GetTransactionConfig()

var policy *rofl.AppAuthPolicy
var (
policy *rofl.AppAuthPolicy
manifest *buildRofl.Manifest
deployment *buildRofl.Deployment
)
if len(args) > 0 {
policy = loadPolicy(args[0])
} else {
_, deployment := roflCommon.LoadManifestAndSetNPA(cfg, npa, deploymentName)
manifest, deployment = roflCommon.LoadManifestAndSetNPA(cfg, npa, deploymentName, false)
policy = deployment.Policy
}

Expand Down Expand Up @@ -208,6 +204,26 @@ var (
}

fmt.Printf("Created ROFL application: %s\n", appID)

if deployment != nil {
switch doUpdate {
case false:
// Ask the user to update the manifest manually.
fmt.Println("Update the manifest with the following app identifier to use the new app:")
fmt.Println()
fmt.Printf("deployments:\n")
fmt.Printf(" %s:\n", deploymentName)
fmt.Printf(" app_id: %s\n", appID)
fmt.Println()
case true:
// Update the manifest with the given enclave identities, overwriting existing ones.
deployment.AppID = appID.String()

if err = manifest.Save(); err != nil {
cobra.CheckErr(fmt.Errorf("failed to update manifest: %w", err))
}
}
}
},
}

Expand All @@ -228,7 +244,7 @@ var (
rawAppID = args[0]
policy = loadPolicy(policyFn)
} else {
_, deployment := roflCommon.LoadManifestAndSetNPA(cfg, npa, deploymentName)
_, deployment := roflCommon.LoadManifestAndSetNPA(cfg, npa, deploymentName, true)
rawAppID = deployment.AppID

if adminAddress == "" && deployment.Admin != "" {
Expand Down Expand Up @@ -305,7 +321,7 @@ var (
if len(args) > 0 {
rawAppID = args[0]
} else {
_, deployment := roflCommon.LoadManifestAndSetNPA(cfg, npa, deploymentName)
_, deployment := roflCommon.LoadManifestAndSetNPA(cfg, npa, deploymentName, true)
rawAppID = deployment.AppID
}
var appID rofl.AppID
Expand Down Expand Up @@ -354,7 +370,7 @@ var (
if len(args) > 0 {
rawAppID = args[0]
} else {
_, deployment := roflCommon.LoadManifestAndSetNPA(cfg, npa, deploymentName)
_, deployment := roflCommon.LoadManifestAndSetNPA(cfg, npa, deploymentName, true)
rawAppID = deployment.AppID
}
var appID rofl.AppID
Expand Down Expand Up @@ -435,6 +451,7 @@ func init() {
createCmd.Flags().AddFlagSet(common.RuntimeTxFlags)
createCmd.Flags().AddFlagSet(deploymentFlags)
createCmd.Flags().StringVar(&scheme, "scheme", "cn", "app ID generation scheme: creator+round+index [cri] or creator+nonce [cn]")
createCmd.Flags().BoolVar(&doUpdate, "update-manifest", false, "automatically update the manifest")

updateCmd.Flags().AddFlagSet(common.SelectorFlags)
updateCmd.Flags().AddFlagSet(common.RuntimeTxFlags)
Expand Down

0 comments on commit 402f28e

Please sign in to comment.