diff --git a/cmd/rofl/set_admin.go b/cmd/rofl/set_admin.go index 7eb9876a..9062e46f 100644 --- a/cmd/rofl/set_admin.go +++ b/cmd/rofl/set_admin.go @@ -6,8 +6,10 @@ import ( "github.com/spf13/cobra" + "github.com/oasisprotocol/oasis-sdk/client-sdk/go/client" "github.com/oasisprotocol/oasis-sdk/client-sdk/go/connection" "github.com/oasisprotocol/oasis-sdk/client-sdk/go/modules/rofl" + "github.com/oasisprotocol/oasis-sdk/client-sdk/go/types" buildRofl "github.com/oasisprotocol/cli/build/rofl" "github.com/oasisprotocol/cli/cmd/common" @@ -16,45 +18,49 @@ import ( ) var setAdminCmd = &cobra.Command{ - Use: "set-admin ", - Short: "Change the administrator of the application in ROFL", + Use: "set-admin [] ", + Short: "Change the administrator of a ROFL", Aliases: []string{"change-admin"}, - Args: cobra.ExactArgs(1), + Args: cobra.RangeArgs(1, 2), Run: func(_ *cobra.Command, args []string) { + cfg := cliConfig.Global() + npa := common.GetNPASelection(cfg) txCfg := common.GetTransactionConfig() - manifest, deployment, npa := roflCommon.LoadManifestAndSetNPA(&roflCommon.ManifestOptions{ - NeedAppID: true, - NeedAdmin: true, - }) + var ( + rawAppID string + newAdminArg string + manifest *buildRofl.Manifest + deployment *buildRofl.Deployment + ) + switch len(args) { + case 2: + // Direct mode: oasis rofl set-admin + rawAppID = args[0] + newAdminArg = args[1] + case 1: + // Manifest mode: oasis rofl set-admin + newAdminArg = args[0] + manifest, deployment, npa = roflCommon.LoadManifestAndSetNPA(&roflCommon.ManifestOptions{ + NeedAppID: true, + NeedAdmin: true, + }) + rawAppID = deployment.AppID + } var appID rofl.AppID - if err := appID.UnmarshalText([]byte(deployment.AppID)); err != nil { + if err := appID.UnmarshalText([]byte(rawAppID)); err != nil { cobra.CheckErr(fmt.Errorf("malformed ROFL app ID: %w", err)) } npa.MustHaveAccount() npa.MustHaveParaTime() - if deployment.Policy == nil { - cobra.CheckErr("no policy configured in the manifest") - } - - oldAdminAddr, _, err := common.ResolveLocalAccountOrAddress(npa.Network, deployment.Admin) - if err != nil { - cobra.CheckErr(fmt.Errorf("bad current administrator address: %w", err)) - } - - newAdminAddr, newAdminEthAddr, err := common.ResolveLocalAccountOrAddress(npa.Network, args[0]) + newAdminAddr, newAdminEthAddr, err := common.ResolveLocalAccountOrAddress(npa.Network, newAdminArg) if err != nil { cobra.CheckErr(fmt.Errorf("invalid new admin address: %w", err)) } - if *oldAdminAddr == *newAdminAddr { - fmt.Println("New admin is the same as the current admin, nothing to do.") - return - } - // When not in offline mode, connect to the given network endpoint. ctx := context.Background() var conn connection.Connection @@ -63,26 +69,61 @@ var setAdminCmd = &cobra.Command{ cobra.CheckErr(err) } + fmt.Printf("App ID: %s\n", appID) + + updateBody := rofl.Update{ + ID: appID, + Admin: newAdminAddr, + } + + var oldAdminAddr *types.Address + if manifest != nil { + // Manifest mode: use local policy, metadata, secrets. + if deployment.Policy == nil { + cobra.CheckErr("no policy configured in the manifest") + } + + oldAdminAddr, _, err = common.ResolveLocalAccountOrAddress(npa.Network, deployment.Admin) + if err != nil { + cobra.CheckErr(fmt.Errorf("bad current administrator address: %w", err)) + } + + updateBody.Policy = *deployment.Policy.AsDescriptor() + updateBody.Metadata = manifest.GetMetadata(roflCommon.DeploymentName) + updateBody.Secrets = buildRofl.PrepareSecrets(deployment.Secrets) + } else { + // Direct mode: reuse current policy, metadata, secrets from chain. + if txCfg.Offline { + cobra.CheckErr("direct mode requires network access") + } + + appCfg, err := conn.Runtime(npa.ParaTime).ROFL.App(ctx, client.RoundLatest, appID) + cobra.CheckErr(err) + + oldAdminAddr = appCfg.Admin + updateBody.Policy = appCfg.Policy + updateBody.Metadata = appCfg.Metadata + updateBody.Secrets = appCfg.Secrets + } + + if oldAdminAddr != nil { + if *oldAdminAddr == *newAdminAddr { + fmt.Println("New admin is the same as the current admin, nothing to do.") + return + } + fmt.Printf("Old admin: %s\n", common.PrettyAddress(oldAdminAddr.String())) + } + newAdminStr := newAdminAddr.String() if newAdminEthAddr != nil { newAdminStr = newAdminEthAddr.Hex() } - fmt.Printf("App ID: %s\n", deployment.AppID) - fmt.Printf("Old admin: %s\n", common.PrettyAddress(oldAdminAddr.String())) fmt.Printf("New admin: %s\n", common.PrettyAddress(newAdminStr)) - secrets := buildRofl.PrepareSecrets(deployment.Secrets) - - tx := rofl.NewUpdateTx(nil, &rofl.Update{ - ID: appID, - Policy: *deployment.Policy.AsDescriptor(), - Admin: newAdminAddr, - Metadata: manifest.GetMetadata(roflCommon.DeploymentName), - Secrets: secrets, - }) + tx := rofl.NewUpdateTx(nil, &updateBody) - acc := common.LoadAccount(cliConfig.Global(), npa.AccountName) + acc := common.LoadAccount(cfg, npa.AccountName) sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, nil) cobra.CheckErr(err) @@ -90,10 +131,12 @@ var setAdminCmd = &cobra.Command{ return } - // Transaction succeeded — update the manifest with the new admin. - deployment.Admin = args[0] - if err = manifest.Save(); err != nil { - cobra.CheckErr(fmt.Errorf("failed to update manifest: %w", err)) + // Transaction succeeded — update the manifest if available. + if manifest != nil { + deployment.Admin = newAdminArg + if err = manifest.Save(); err != nil { + cobra.CheckErr(fmt.Errorf("failed to update manifest: %w", err)) + } } fmt.Printf("ROFL admin changed to %s.\n", common.PrettyAddress(newAdminStr)) @@ -101,7 +144,7 @@ var setAdminCmd = &cobra.Command{ } func init() { - common.AddAccountFlag(setAdminCmd) + common.AddSelectorFlags(setAdminCmd) setAdminCmd.Flags().AddFlagSet(common.RuntimeTxFlags) setAdminCmd.Flags().AddFlagSet(roflCommon.DeploymentFlags) } diff --git a/docs/rofl.md b/docs/rofl.md index aae89e84..1018807d 100644 --- a/docs/rofl.md +++ b/docs/rofl.md @@ -328,29 +328,33 @@ their latest versions. This includes: ### Change ROFL app administrator {#set-admin} Run `rofl set-admin` to transfer ownership of a ROFL app to a new -administrator. The transaction is signed by the current admin and, on success, -the manifest is updated with the new admin. +administrator. The ROFL app administrator owns the application, including +upgrades, policy changes and removal. The transaction must be signed by the +current admin. + +If the current directory contains a ROFL manifest, the manifest is updated with +the new admin after a successful transaction. ![code shell](../examples/rofl/set-admin.in.static) -### Change ROFL machine administrator {#machine-set-admin} +To change the administrator directly on an existing app without a local +manifest, run: -Run `rofl machine set-admin` to change the administrator of an individual -machine instance. +![code shell](../examples/rofl/set-admin-address.in.static) -![code shell](../examples/rofl/machine-set-admin.in.static) +This mode requires network access and does not modify the manifest. -:::info ROFL admin vs machine admin +### Change ROFL machine administrator {#machine-set-admin} -The **ROFL admin** (changed via `oasis rofl set-admin`) owns the -application — transfer ownership, upgrades, policy changes, removal. +Run `rofl machine set-admin` to change the administrator of an individual +machine instance. This is independent of the ROFL app administrator and only +affects management of the specific machine instance. -The **machine admin** (changed via `oasis rofl machine set-admin`) manages -an individual machine instance — execution, restarts, stops. +![code shell](../examples/rofl/machine-set-admin.in.static) -These are independent roles. +To change the administrator of the machine without a local manifest, run: -::: +![code shell](../examples/rofl/machine-set-admin-address.in.static) ### Remove ROFL app from the network {#remove} diff --git a/examples/rofl/machine-set-admin-address.in.static b/examples/rofl/machine-set-admin-address.in.static new file mode 100644 index 00000000..2235af7a --- /dev/null +++ b/examples/rofl/machine-set-admin-address.in.static @@ -0,0 +1 @@ +oasis rofl machine set-admin oasis1qp2ens0hsp7gh23wajxa4hpetkdek3swyyulyrmz:000000000000055c bob diff --git a/examples/rofl/machine-set-admin.in.static b/examples/rofl/machine-set-admin.in.static index f6d9993f..4db5f5fb 100644 --- a/examples/rofl/machine-set-admin.in.static +++ b/examples/rofl/machine-set-admin.in.static @@ -1 +1 @@ -oasis rofl machine set-admin [ | :] +oasis rofl machine set-admin bob diff --git a/examples/rofl/set-admin-address.in.static b/examples/rofl/set-admin-address.in.static new file mode 100644 index 00000000..6f1426bd --- /dev/null +++ b/examples/rofl/set-admin-address.in.static @@ -0,0 +1 @@ +oasis rofl set-admin rofl1qpw7gxp7dqq72sdtpv4jrmdfys9nsp73wysglhue bob diff --git a/examples/rofl/set-admin.in.static b/examples/rofl/set-admin.in.static index e7506af8..3f4bb31a 100644 --- a/examples/rofl/set-admin.in.static +++ b/examples/rofl/set-admin.in.static @@ -1 +1 @@ -oasis rofl set-admin +oasis rofl set-admin bob