diff --git a/cmd/rofl/machine/logs.go b/cmd/rofl/machine/logs.go index 3e3023f7..0dd7bfe3 100644 --- a/cmd/rofl/machine/logs.go +++ b/cmd/rofl/machine/logs.go @@ -20,32 +20,25 @@ import ( ) var logsCmd = &cobra.Command{ - Use: "logs []", + Use: "logs [ | :]", Short: "Show logs from the given machine", Args: cobra.MaximumNArgs(1), Run: func(_ *cobra.Command, args []string) { - _, deployment, npa := roflCommon.LoadManifestAndSetNPA(&roflCommon.ManifestOptions{ + mCfg, err := resolveMachineCfg(args, &roflCommon.ManifestOptions{ NeedAppID: true, NeedAdmin: false, }) - - machine, _, machineID := resolveMachine(args, deployment) + cobra.CheckErr(err) // Establish connection with the target network. ctx := context.Background() - conn, err := connection.Connect(ctx, npa.Network) + conn, err := connection.Connect(ctx, mCfg.NPA.Network) cobra.CheckErr(err) - // Resolve provider address. - providerAddr, _, err := common.ResolveLocalAccountOrAddress(npa.Network, machine.Provider) - if err != nil { - cobra.CheckErr(fmt.Sprintf("Invalid provider address: %s", err)) - } - - providerDsc, err := conn.Runtime(npa.ParaTime).ROFLMarket.Provider(ctx, client.RoundLatest, *providerAddr) + providerDsc, err := conn.Runtime(mCfg.NPA.ParaTime).ROFLMarket.Provider(ctx, client.RoundLatest, *mCfg.ProviderAddr) cobra.CheckErr(err) - insDsc, err := conn.Runtime(npa.ParaTime).ROFLMarket.Instance(ctx, client.RoundLatest, *providerAddr, machineID) + insDsc, err := conn.Runtime(mCfg.NPA.ParaTime).ROFLMarket.Instance(ctx, client.RoundLatest, *mCfg.ProviderAddr, mCfg.MachineID) cobra.CheckErr(err) schedulerRAKRaw, ok := insDsc.Metadata[scheduler.MetadataKeySchedulerRAK] @@ -54,31 +47,31 @@ var logsCmd = &cobra.Command{ } var schedulerRAK ed25519.PublicKey if err := schedulerRAK.UnmarshalText([]byte(schedulerRAKRaw)); err != nil { - cobra.CheckErr(fmt.Sprintf("Malformed scheduler RAK metadata: %s", err)) + cobra.CheckErr(fmt.Errorf("malformed scheduler RAK metadata: %w", err)) } pk := types.PublicKey{PublicKey: schedulerRAK} - schedulerDsc, err := conn.Runtime(npa.ParaTime).ROFL.AppInstance(ctx, client.RoundLatest, providerDsc.SchedulerApp, pk) + schedulerDsc, err := conn.Runtime(mCfg.NPA.ParaTime).ROFL.AppInstance(ctx, client.RoundLatest, providerDsc.SchedulerApp, pk) cobra.CheckErr(err) client, err := scheduler.NewClient(schedulerDsc) cobra.CheckErr(err) // TODO: Cache authentication token so we don't need to re-authenticate. - acc := common.LoadAccount(cliConfig.Global(), npa.AccountName) + acc := common.LoadAccount(cliConfig.Global(), mCfg.NPA.AccountName) sigCtx := &signature.RichContext{ - RuntimeID: npa.ParaTime.Namespace(), - ChainContext: npa.Network.ChainContext, + RuntimeID: mCfg.NPA.ParaTime.Namespace(), + ChainContext: mCfg.NPA.Network.ChainContext, Base: []byte(scheduler.StdAuthContextBase), } - authRequest, err := scheduler.SignLogin(sigCtx, acc.Signer(), client.Host(), *providerAddr) + authRequest, err := scheduler.SignLogin(sigCtx, acc.Signer(), client.Host(), *mCfg.ProviderAddr) cobra.CheckErr(err) err = client.Login(ctx, authRequest) cobra.CheckErr(err) - logs, err := client.LogsGet(ctx, machineID, time.Time{}) + logs, err := client.LogsGet(ctx, mCfg.MachineID, time.Time{}) cobra.CheckErr(err) for _, line := range logs { fmt.Println(line) diff --git a/cmd/rofl/machine/mgmt.go b/cmd/rofl/machine/mgmt.go index dcf0c851..938700ca 100644 --- a/cmd/rofl/machine/mgmt.go +++ b/cmd/rofl/machine/mgmt.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "strings" "github.com/spf13/cobra" @@ -20,13 +21,14 @@ import ( buildRoflProvider "github.com/oasisprotocol/cli/build/rofl/provider" "github.com/oasisprotocol/cli/build/rofl/scheduler" "github.com/oasisprotocol/cli/cmd/common" + roflCmdBuild "github.com/oasisprotocol/cli/cmd/rofl/build" roflCommon "github.com/oasisprotocol/cli/cmd/rofl/common" cliConfig "github.com/oasisprotocol/cli/config" ) var ( restartCmd = &cobra.Command{ - Use: "restart []", + Use: "restart [ | :]", Short: "Restart a running machine or start a stopped one", Args: cobra.MaximumNArgs(1), Run: func(_ *cobra.Command, args []string) { @@ -42,7 +44,7 @@ var ( } stopCmd = &cobra.Command{ - Use: "stop []", + Use: "stop [ | :]", Short: "Stop a machine", Aliases: []string{"terminate"}, Args: cobra.MaximumNArgs(1), @@ -59,116 +61,98 @@ var ( } removeCmd = &cobra.Command{ - Use: "remove []", + Use: "remove [ | :]", Short: "Cancel rental and remove the machine", Aliases: []string{"cancel", "rm"}, Args: cobra.MaximumNArgs(1), Run: func(_ *cobra.Command, args []string) { - txCfg := common.GetTransactionConfig() - - manifest, deployment, npa := roflCommon.LoadManifestAndSetNPA(&roflCommon.ManifestOptions{ + mCfg, err := resolveMachineCfg(args, &roflCommon.ManifestOptions{ NeedAppID: true, NeedAdmin: false, }) - machine, machineName, machineID := resolveMachine(args, deployment) - - // Resolve provider address. - providerAddr, _, err := common.ResolveLocalAccountOrAddress(npa.Network, machine.Provider) - if err != nil { - cobra.CheckErr(fmt.Sprintf("Invalid provider address: %s", err)) - } - // When not in offline mode, connect to the given network endpoint. ctx := context.Background() var conn connection.Connection - if !txCfg.Offline { - conn, err = connection.Connect(ctx, npa.Network) + if !common.GetTransactionConfig().Offline { + conn, err = connection.Connect(ctx, mCfg.NPA.Network) cobra.CheckErr(err) } - fmt.Printf("Using provider: %s (%s)\n", machine.Provider, providerAddr) - fmt.Printf("Canceling machine: %s [%s]\n", machineName, machine.ID) + fmt.Printf("Using provider: %s (%s)\n", mCfg.Machine.Provider, mCfg.ProviderAddr) + fmt.Printf("Canceling machine: %s [%s]\n", mCfg.MachineName, mCfg.Machine.ID) common.Warn("WARNING: Canceling a machine will permanently destroy it including any persistent storage!") roflCommon.PrintRentRefundWarning() // Prepare transaction. tx := roflmarket.NewInstanceCancelTx(nil, &roflmarket.InstanceCancel{ - Provider: *providerAddr, - ID: machineID, + Provider: *mCfg.ProviderAddr, + ID: mCfg.MachineID, }) - acc := common.LoadAccount(cliConfig.Global(), npa.AccountName) - sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, nil) + acc := common.LoadAccount(cliConfig.Global(), mCfg.NPA.AccountName) + sigTx, meta, err := common.SignParaTimeTransaction(ctx, mCfg.NPA, acc, conn, tx, nil) cobra.CheckErr(err) - if !common.BroadcastOrExportTransaction(ctx, npa, conn, sigTx, meta, nil) { + if !common.BroadcastOrExportTransaction(ctx, mCfg.NPA, conn, sigTx, meta, nil) { return } fmt.Printf("Machine removed.\n") // Update manifest to clear the machine ID as it has been cancelled. - machine.ID = "" + mCfg.Machine.ID = "" - if err = manifest.Save(); err != nil { - cobra.CheckErr(fmt.Errorf("failed to update manifest: %w", err)) + if mCfg.Manifest != nil { + if err = mCfg.Manifest.Save(); err != nil { + cobra.CheckErr(fmt.Errorf("failed to update manifest: %w", err)) + } } }, } changeAdminCmd = &cobra.Command{ - Use: "change-admin [] ", + Use: "change-admin [ | :] ", Short: "Change the machine administrator", Args: cobra.RangeArgs(1, 2), Run: func(_ *cobra.Command, args []string) { txCfg := common.GetTransactionConfig() - - _, deployment, npa := roflCommon.LoadManifestAndSetNPA(&roflCommon.ManifestOptions{ + mCfg, err := resolveMachineCfg(args, &roflCommon.ManifestOptions{ NeedAppID: true, NeedAdmin: false, }) + cobra.CheckErr(err) var newAdminAddress string switch len(args) { case 1: // Just admin address. newAdminAddress = args[0] - args = nil case 2: // Machine and admin address. newAdminAddress = args[1] - args = args[:1] - } - - machine, machineName, machineID := resolveMachine(args, deployment) - - // Resolve provider address. - providerAddr, _, err := common.ResolveLocalAccountOrAddress(npa.Network, machine.Provider) - if err != nil { - cobra.CheckErr(fmt.Sprintf("Invalid provider address: %s", err)) } // Resolve new admin address. - newAdminAddr, _, err := common.ResolveLocalAccountOrAddress(npa.Network, newAdminAddress) + newAdminAddr, _, err := common.ResolveLocalAccountOrAddress(mCfg.NPA.Network, newAdminAddress) if err != nil { - cobra.CheckErr(fmt.Sprintf("Invalid admin address: %s", err)) + cobra.CheckErr(fmt.Errorf("invalid admin address: %w", err)) } // When not in offline mode, connect to the given network endpoint. ctx := context.Background() var conn connection.Connection if !txCfg.Offline { - conn, err = connection.Connect(ctx, npa.Network) + conn, err = connection.Connect(ctx, mCfg.NPA.Network) cobra.CheckErr(err) } - fmt.Printf("Provider: %s (%s)\n", machine.Provider, providerAddr) - fmt.Printf("Machine: %s [%s]\n", machineName, machine.ID) + fmt.Printf("Provider: %s (%s)\n", mCfg.Machine.Provider, mCfg.ProviderAddr) + fmt.Printf("Machine: %s [%s]\n", mCfg.MachineName, mCfg.Machine.ID) // Resolve old admin in online mode. if !txCfg.Offline { - insDsc, err := conn.Runtime(npa.ParaTime).ROFLMarket.Instance(ctx, client.RoundLatest, *providerAddr, machineID) + insDsc, err := conn.Runtime(mCfg.NPA.ParaTime).ROFLMarket.Instance(ctx, client.RoundLatest, *mCfg.ProviderAddr, mCfg.MachineID) cobra.CheckErr(err) fmt.Printf("Old admin: %s\n", insDsc.Admin) @@ -178,16 +162,16 @@ var ( // Prepare transaction. tx := roflmarket.NewInstanceChangeAdmin(nil, &roflmarket.InstanceChangeAdmin{ - Provider: *providerAddr, - ID: machineID, + Provider: *mCfg.ProviderAddr, + ID: mCfg.MachineID, Admin: *newAdminAddr, }) - acc := common.LoadAccount(cliConfig.Global(), npa.AccountName) - sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, nil) + acc := common.LoadAccount(cliConfig.Global(), mCfg.NPA.AccountName) + sigTx, meta, err := common.SignParaTimeTransaction(ctx, mCfg.NPA, acc, conn, tx, nil) cobra.CheckErr(err) - if !common.BroadcastOrExportTransaction(ctx, npa, conn, sigTx, meta, nil) { + if !common.BroadcastOrExportTransaction(ctx, mCfg.NPA, conn, sigTx, meta, nil) { return } @@ -196,29 +180,22 @@ var ( } topUpCmd = &cobra.Command{ - Use: "top-up []", + Use: "top-up [ | :]", Short: "Top-up payment for a machine", Args: cobra.MaximumNArgs(1), Run: func(_ *cobra.Command, args []string) { txCfg := common.GetTransactionConfig() - _, deployment, npa := roflCommon.LoadManifestAndSetNPA(&roflCommon.ManifestOptions{ + mCfg, err := resolveMachineCfg(args, &roflCommon.ManifestOptions{ NeedAppID: true, NeedAdmin: false, }) + cobra.CheckErr(err) ctx := context.Background() // This is required for the price pretty printer to work... - if npa.ParaTime != nil { - ctx = context.WithValue(ctx, config.ContextKeyParaTimeCfg, npa.ParaTime) - } - - machine, machineName, machineID := resolveMachine(args, deployment) - - // Resolve provider address. - providerAddr, _, err := common.ResolveLocalAccountOrAddress(npa.Network, machine.Provider) - if err != nil { - cobra.CheckErr(fmt.Sprintf("invalid provider address: %s", err)) + if mCfg.NPA.ParaTime != nil { + ctx = context.WithValue(ctx, config.ContextKeyParaTimeCfg, mCfg.NPA.ParaTime) } // Parse machine payment term. @@ -234,28 +211,40 @@ var ( var conn connection.Connection var offer *roflmarket.Offer if !txCfg.Offline { - conn, err = connection.Connect(ctx, npa.Network) + conn, err = connection.Connect(ctx, mCfg.NPA.Network) cobra.CheckErr(err) // Fetch chosen offer, so we can calculate price. - offers, err := conn.Runtime(npa.ParaTime).ROFLMarket.Offers(ctx, client.RoundLatest, *providerAddr) + offers, err := conn.Runtime(mCfg.NPA.ParaTime).ROFLMarket.Offers(ctx, client.RoundLatest, *mCfg.ProviderAddr) if err != nil { - cobra.CheckErr(fmt.Errorf("failed to query provider: %s", err)) + cobra.CheckErr(fmt.Errorf("failed to query provider: %w", err)) } for _, of := range offers { - if of.Metadata[buildRoflProvider.SchedulerMetadataOfferKey] == machine.Offer { + if of.Metadata[buildRoflProvider.SchedulerMetadataOfferKey] == mCfg.Machine.Offer { offer = of break } } if offer == nil { - cobra.CheckErr(fmt.Errorf("unable to find existing machine offer (%s) among market offers", machine.Offer)) + machineDsc, err := conn.Runtime(mCfg.NPA.ParaTime).ROFLMarket.Instance(ctx, client.RoundLatest, *mCfg.ProviderAddr, mCfg.MachineID) + if err != nil { + cobra.CheckErr(fmt.Errorf("failed to query machine: %w", err)) + } + for _, of := range offers { + if of.ID == machineDsc.Offer { + offer = of + break + } + } + } + if offer == nil { + cobra.CheckErr(fmt.Errorf("unable to find existing machine offer (%s) among market offers", mCfg.Machine.Offer)) } } - fmt.Printf("Using provider: %s (%s)\n", machine.Provider, providerAddr) - fmt.Printf("Top-up machine: %s [%s]\n", machineName, machine.ID) + fmt.Printf("Using provider: %s (%s)\n", mCfg.Machine.Provider, mCfg.ProviderAddr) + fmt.Printf("Top-up machine: %s [%s]\n", mCfg.MachineName, mCfg.Machine.ID) if txCfg.Offline { fmt.Printf("Top-up term: %d x %s\n", roflCommon.TermCount, roflCommon.Term) } else { @@ -276,17 +265,17 @@ var ( // Prepare transaction. tx := roflmarket.NewInstanceTopUpTx(nil, &roflmarket.InstanceTopUp{ - Provider: *providerAddr, - ID: machineID, + Provider: *mCfg.ProviderAddr, + ID: mCfg.MachineID, Term: term, TermCount: roflCommon.TermCount, }) - acc := common.LoadAccount(cliConfig.Global(), npa.AccountName) - sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, nil) + acc := common.LoadAccount(cliConfig.Global(), mCfg.NPA.AccountName) + sigTx, meta, err := common.SignParaTimeTransaction(ctx, mCfg.NPA, acc, conn, tx, nil) cobra.CheckErr(err) - if !common.BroadcastOrExportTransaction(ctx, npa, conn, sigTx, meta, nil) { + if !common.BroadcastOrExportTransaction(ctx, mCfg.NPA, conn, sigTx, meta, nil) { return } @@ -295,7 +284,75 @@ var ( } ) -func resolveMachine(args []string, deployment *buildRofl.Deployment) (*buildRofl.Machine, string, roflmarket.InstanceID) { +// MachineCfg contains all resolved machine configuration and related metadata. +type MachineCfg struct { + Machine *buildRofl.Machine + MachineName string + MachineID roflmarket.InstanceID + Manifest *buildRofl.Manifest + ExtraCfg *roflCmdBuild.AppExtraConfig + ProviderAddr *types.Address + NPA *common.NPASelection +} + +// resolveMachineCfg resolves machine configuration from command-line arguments or manifest file. +// If args contains a provider:machine-id pair, it constructs the configuration from that. +// Otherwise, it loads the manifest file and resolves the machine from the deployment section. +// The function also resolves the provider address and validates the configuration. +func resolveMachineCfg(args []string, manifestOpts *roflCommon.ManifestOptions) (*MachineCfg, error) { + mCfg, err := resolveMachineCfgFromArgs(args) + if err != nil { + return nil, err + } + + if mCfg != nil { + mCfg.NPA = common.GetNPASelection(cliConfig.Global()) + } else { + var deployment *buildRofl.Deployment + mCfg.Manifest, deployment, mCfg.NPA = roflCommon.LoadManifestAndSetNPA(manifestOpts) + mCfg.Machine, mCfg.MachineName, mCfg.MachineID = resolveMachineFromManifest(args, deployment) + + var appID rofl.AppID + if err = appID.UnmarshalText([]byte(deployment.AppID)); err != nil { + return nil, fmt.Errorf("malformed app id: %w", err) + } + + if mCfg.ExtraCfg, err = roflCmdBuild.ValidateApp(mCfg.Manifest, roflCmdBuild.ValidationOpts{ + Offline: true, + }); err != nil { + return nil, fmt.Errorf("failed to validate app: %w", err) + } + } + + if mCfg.ProviderAddr, _, err = common.ResolveLocalAccountOrAddress(mCfg.NPA.Network, mCfg.Machine.Provider); err != nil { + return nil, fmt.Errorf("invalid provider address: %w", err) + } + + return mCfg, nil +} + +func resolveMachineCfgFromArgs(args []string) (*MachineCfg, error) { + mCfg := &MachineCfg{} + if len(args) == 0 { + return nil, nil + } + parts := strings.Split(args[0], ":") + if len(parts) != 2 { + return nil, nil + } + + mCfg.MachineName = args[0] + mCfg.Machine = &buildRofl.Machine{ + Provider: parts[0], + ID: parts[1], + } + if err := mCfg.MachineID.UnmarshalText([]byte(mCfg.Machine.ID)); err != nil { + return nil, fmt.Errorf("malformed machine ID: %w", err) + } + return mCfg, nil +} + +func resolveMachineFromManifest(args []string, deployment *buildRofl.Deployment) (*buildRofl.Machine, string, roflmarket.InstanceID) { machineName := buildRofl.DefaultMachineName if len(args) > 0 { machineName = args[0] @@ -333,50 +390,41 @@ func resolveMachine(args []string, deployment *buildRofl.Deployment) (*buildRofl } func queueCommand(cliArgs []string, method string, args any, msgAfter string) { - txCfg := common.GetTransactionConfig() - - _, deployment, npa := roflCommon.LoadManifestAndSetNPA(&roflCommon.ManifestOptions{ + mCfg, err := resolveMachineCfg(cliArgs, &roflCommon.ManifestOptions{ NeedAppID: true, NeedAdmin: false, }) - - machine, machineName, machineID := resolveMachine(cliArgs, deployment) - - // Resolve provider address. - providerAddr, _, err := common.ResolveLocalAccountOrAddress(npa.Network, machine.Provider) - if err != nil { - cobra.CheckErr(fmt.Sprintf("Invalid provider address: %s", err)) - } + cobra.CheckErr(err) // When not in offline mode, connect to the given network endpoint. ctx := context.Background() var conn connection.Connection - if !txCfg.Offline { - conn, err = connection.Connect(ctx, npa.Network) + if !common.GetTransactionConfig().Offline { + conn, err = connection.Connect(ctx, mCfg.NPA.Network) cobra.CheckErr(err) } - fmt.Printf("Using provider: %s (%s)\n", machine.Provider, providerAddr) - fmt.Printf("Machine: %s [%s]\n", machineName, machine.ID) + fmt.Printf("Using provider: %s (%s)\n", mCfg.Machine.Provider, mCfg.ProviderAddr) + fmt.Printf("Machine: %s [%s]\n", mCfg.MachineName, mCfg.Machine.ID) fmt.Printf("Command: %s\n", method) fmt.Printf("Args:\n") - fmt.Println(common.PrettyPrint(npa, " ", args)) + fmt.Println(common.PrettyPrint(mCfg.NPA, " ", args)) // Prepare transaction. tx := roflmarket.NewInstanceExecuteCmdsTx(nil, &roflmarket.InstanceExecuteCmds{ - Provider: *providerAddr, - ID: machineID, + Provider: *mCfg.ProviderAddr, + ID: mCfg.MachineID, Cmds: [][]byte{cbor.Marshal(scheduler.Command{ Method: method, Args: cbor.Marshal(args), })}, }) - acc := common.LoadAccount(cliConfig.Global(), npa.AccountName) - sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, nil) + acc := common.LoadAccount(cliConfig.Global(), mCfg.NPA.AccountName) + sigTx, meta, err := common.SignParaTimeTransaction(ctx, mCfg.NPA, acc, conn, tx, nil) cobra.CheckErr(err) - if !common.BroadcastOrExportTransaction(ctx, npa, conn, sigTx, meta, nil) { + if !common.BroadcastOrExportTransaction(ctx, mCfg.NPA, conn, sigTx, meta, nil) { return } diff --git a/cmd/rofl/machine/show.go b/cmd/rofl/machine/show.go index 65c3ed01..fb42235d 100644 --- a/cmd/rofl/machine/show.go +++ b/cmd/rofl/machine/show.go @@ -25,42 +25,31 @@ import ( roflCommon "github.com/oasisprotocol/cli/cmd/rofl/common" ) +type machineShowOutput struct { + Machine *roflmarket.Instance `json:"machine,omitempty"` + MachineCommands []*roflmarket.QueuedCommand `json:"machine_commands,omitempty"` + Provider *roflmarket.Provider `json:"provider,omitempty"` + Replica *rofl.Registration `json:"replica,omitempty"` +} + var showCmd = &cobra.Command{ - Use: "show []", + Use: "show [ | :]", Short: "Show information about a machine", Args: cobra.MaximumNArgs(1), Run: func(_ *cobra.Command, args []string) { - manifest, deployment, npa := roflCommon.LoadManifestAndSetNPA(&roflCommon.ManifestOptions{ + var out machineShowOutput + mCfg, err := resolveMachineCfg(args, &roflCommon.ManifestOptions{ NeedAppID: true, NeedAdmin: false, }) - - machine, machineName, machineID := resolveMachine(args, deployment) - - var appID rofl.AppID - if err := appID.UnmarshalText([]byte(deployment.AppID)); err != nil { - cobra.CheckErr(fmt.Sprintf("malformed app id: %s", err)) - } - - extraCfg, err := roflCmdBuild.ValidateApp(manifest, roflCmdBuild.ValidationOpts{ - Offline: true, - }) - if err != nil { - cobra.CheckErr(fmt.Sprintf("failed to validate app: %s", err)) - } + cobra.CheckErr(err) // Establish connection with the target network. ctx := context.Background() - conn, err := connection.Connect(ctx, npa.Network) + conn, err := connection.Connect(ctx, mCfg.NPA.Network) cobra.CheckErr(err) - // Resolve provider address. - providerAddr, _, err := common.ResolveLocalAccountOrAddress(npa.Network, machine.Provider) - if err != nil { - cobra.CheckErr(fmt.Sprintf("Invalid provider address: %s", err)) - } - - insDsc, err := conn.Runtime(npa.ParaTime).ROFLMarket.Instance(ctx, client.RoundLatest, *providerAddr, machineID) + out.Machine, err = conn.Runtime(mCfg.NPA.ParaTime).ROFLMarket.Instance(ctx, client.RoundLatest, *mCfg.ProviderAddr, mCfg.MachineID) if err != nil { // The "instance not found" error originates from Rust code, so we can't compare it nicely here. if strings.Contains(err.Error(), "instance not found") { @@ -73,152 +62,156 @@ var showCmd = &cobra.Command{ } cobra.CheckErr(err) } - if common.OutputFormat() == common.FormatJSON { - data, err := json.MarshalIndent(insDsc, "", " ") - cobra.CheckErr(err) - fmt.Printf("%s\n", data) - return - } - insCmds, err := conn.Runtime(npa.ParaTime).ROFLMarket.InstanceCommands(ctx, client.RoundLatest, *providerAddr, machineID) + out.MachineCommands, err = conn.Runtime(mCfg.NPA.ParaTime).ROFLMarket.InstanceCommands(ctx, client.RoundLatest, *mCfg.ProviderAddr, mCfg.MachineID) cobra.CheckErr(err) - providerDsc, err := conn.Runtime(npa.ParaTime).ROFLMarket.Provider(ctx, client.RoundLatest, *providerAddr) + out.Provider, err = conn.Runtime(mCfg.NPA.ParaTime).ROFLMarket.Provider(ctx, client.RoundLatest, *mCfg.ProviderAddr) cobra.CheckErr(err) - var schedulerDsc *rofl.Registration - switch schedulerRAKRaw, ok := insDsc.Metadata[scheduler.MetadataKeySchedulerRAK]; ok { + switch schedulerRAKRaw, ok := out.Machine.Metadata[scheduler.MetadataKeySchedulerRAK]; ok { case true: var schedulerRAK ed25519.PublicKey if err := schedulerRAK.UnmarshalText([]byte(schedulerRAKRaw)); err != nil { - cobra.CheckErr(fmt.Sprintf("Malformed scheduler RAK metadata: %s", err)) + cobra.CheckErr(fmt.Errorf("malformed scheduler RAK metadata: %w", err)) } pk := types.PublicKey{PublicKey: schedulerRAK} - schedulerDsc, _ = conn.Runtime(npa.ParaTime).ROFL.AppInstance(ctx, client.RoundLatest, providerDsc.SchedulerApp, pk) + out.Replica, _ = conn.Runtime(mCfg.NPA.ParaTime).ROFL.AppInstance(ctx, client.RoundLatest, out.Provider.SchedulerApp, pk) default: } - paidUntil := time.Unix(int64(insDsc.PaidUntil), 0) - expired := !time.Now().Before(paidUntil) - - fmt.Printf("Name: %s\n", machineName) - fmt.Printf("Provider: %s\n", insDsc.Provider) - fmt.Printf("ID: %s\n", insDsc.ID) - fmt.Printf("Offer: %s\n", insDsc.Offer) - fmt.Printf("Status: %s", insDsc.Status) - if expired { - fmt.Printf(" (EXPIRED)") - } - fmt.Println() - fmt.Printf("Creator: %s\n", insDsc.Creator) - fmt.Printf("Admin: %s\n", insDsc.Admin) - switch insDsc.NodeID { - case nil: - fmt.Printf("Node ID: \n") - default: - fmt.Printf("Node ID: %s\n", insDsc.NodeID) + if common.OutputFormat() == common.FormatJSON { + data, err := json.MarshalIndent(out, "", " ") + cobra.CheckErr(err) + fmt.Printf("%s\n", data) + } else { + prettyPrintMachine(mCfg, &out) } + }, +} - fmt.Printf("Created at: %s\n", time.Unix(int64(insDsc.CreatedAt), 0)) - fmt.Printf("Updated at: %s\n", time.Unix(int64(insDsc.UpdatedAt), 0)) - fmt.Printf("Paid until: %s\n", paidUntil) +// prettyPrintMachine prints a compact human-readable info of the ROFL machine. +func prettyPrintMachine(mCfg *MachineCfg, out *machineShowOutput) { + paidUntil := time.Unix(int64(out.Machine.PaidUntil), 0) //nolint:gosec + expired := !time.Now().Before(paidUntil) + + fmt.Printf("Name: %s\n", mCfg.MachineName) + fmt.Printf("Provider: %s\n", out.Machine.Provider) + fmt.Printf("ID: %s\n", out.Machine.ID) + fmt.Printf("Offer: %s\n", out.Machine.Offer) + fmt.Printf("Status: %s", out.Machine.Status) + if expired { + fmt.Printf(" (EXPIRED)") + } + fmt.Println() + fmt.Printf("Creator: %s\n", out.Machine.Creator) + fmt.Printf("Admin: %s\n", out.Machine.Admin) + switch out.Machine.NodeID { + case nil: + fmt.Printf("Node ID: \n") + default: + fmt.Printf("Node ID: %s\n", out.Machine.NodeID) + } - if schedulerDsc != nil { - if proxyDomain, ok := schedulerDsc.Metadata[scheduler.MetadataKeyProxyDomain]; ok { - numericMachineID := binary.BigEndian.Uint64(machineID[:]) - proxyDomain = fmt.Sprintf("m%d.%s", numericMachineID, proxyDomain) + fmt.Printf("Created at: %s\n", time.Unix(int64(out.Machine.CreatedAt), 0)) //nolint:gosec + fmt.Printf("Updated at: %s\n", time.Unix(int64(out.Machine.UpdatedAt), 0)) //nolint:gosec + fmt.Printf("Paid until: %s\n", paidUntil) - fmt.Printf("Proxy:\n") - fmt.Printf(" Domain: %s\n", proxyDomain) + if out.Replica != nil { + if proxyDomain, ok := out.Replica.Metadata[scheduler.MetadataKeyProxyDomain]; ok { + numericMachineID := binary.BigEndian.Uint64(mCfg.MachineID[:]) + proxyDomain = fmt.Sprintf("m%d.%s", numericMachineID, proxyDomain) - showMachinePorts(extraCfg, appID, insDsc, proxyDomain) - } + fmt.Printf("Proxy:\n") + fmt.Printf(" Domain: %s\n", proxyDomain) + + prettyPrintMachinePorts(mCfg.ExtraCfg, out.Machine.Deployment.AppID, out.Machine, proxyDomain) } + } - if len(insDsc.Metadata) > 0 { - fmt.Printf("Metadata:\n") - for key, value := range insDsc.Metadata { - fmt.Printf(" %s: %s\n", key, value) - } + if len(out.Machine.Metadata) > 0 { + fmt.Printf("Metadata:\n") + for key, value := range out.Machine.Metadata { + fmt.Printf(" %s: %s\n", key, value) } + } - fmt.Printf("Resources:\n") + fmt.Printf("Resources:\n") - fmt.Printf(" TEE: ") - switch insDsc.Resources.TEE { - case roflmarket.TeeTypeSGX: - fmt.Printf("Intel SGX\n") - case roflmarket.TeeTypeTDX: - fmt.Printf("Intel TDX\n") - default: - fmt.Printf("[unknown: %d]\n", insDsc.Resources.TEE) - } + fmt.Printf(" TEE: ") + switch out.Machine.Resources.TEE { + case roflmarket.TeeTypeSGX: + fmt.Printf("Intel SGX\n") + case roflmarket.TeeTypeTDX: + fmt.Printf("Intel TDX\n") + default: + fmt.Printf("[unknown: %d]\n", out.Machine.Resources.TEE) + } - fmt.Printf(" Memory: %d MiB\n", insDsc.Resources.Memory) - fmt.Printf(" vCPUs: %d\n", insDsc.Resources.CPUCount) - fmt.Printf(" Storage: %d MiB\n", insDsc.Resources.Storage) - if insDsc.Resources.GPU != nil { - fmt.Printf(" GPU:\n") - if insDsc.Resources.GPU.Model != "" { - fmt.Printf(" Model: %s\n", insDsc.Resources.GPU.Model) - } else { - fmt.Printf(" Model: \n") - } - fmt.Printf(" Count: %d\n", insDsc.Resources.GPU.Count) + fmt.Printf(" Memory: %d MiB\n", out.Machine.Resources.Memory) + fmt.Printf(" vCPUs: %d\n", out.Machine.Resources.CPUCount) + fmt.Printf(" Storage: %d MiB\n", out.Machine.Resources.Storage) + if out.Machine.Resources.GPU != nil { + fmt.Printf(" GPU:\n") + if out.Machine.Resources.GPU.Model != "" { + fmt.Printf(" Model: %s\n", out.Machine.Resources.GPU.Model) + } else { + fmt.Printf(" Model: \n") } + fmt.Printf(" Count: %d\n", out.Machine.Resources.GPU.Count) + } - switch insDsc.Deployment { - default: - fmt.Printf("Deployment:\n") - fmt.Printf(" App ID: %s\n", insDsc.Deployment.AppID) + switch out.Machine.Deployment { + default: + fmt.Printf("Deployment:\n") + fmt.Printf(" App ID: %s\n", out.Machine.Deployment.AppID) - if len(insDsc.Deployment.Metadata) > 0 { - fmt.Printf(" Metadata:\n") - for key, value := range insDsc.Deployment.Metadata { - fmt.Printf(" %s: %s\n", key, value) - } + if len(out.Machine.Deployment.Metadata) > 0 { + fmt.Printf(" Metadata:\n") + for key, value := range out.Machine.Deployment.Metadata { + fmt.Printf(" %s: %s\n", key, value) } - case nil: - fmt.Printf("Deployment: \n") } + case nil: + fmt.Printf("Deployment: \n") + } - // Show commands. - fmt.Printf("Commands:\n") - if len(insCmds) > 0 { - for _, qc := range insCmds { - fmt.Printf(" - ID: %s\n", qc.ID) - - var cmd scheduler.Command - err := cbor.Unmarshal(qc.Cmd, &cmd) - switch err { - case nil: - // Decodable scheduler command. - fmt.Printf(" Method: %s\n", cmd.Method) - fmt.Printf(" Args:\n") - - switch cmd.Method { - case scheduler.MethodDeploy: - showCommandArgs(npa, cmd.Args, scheduler.DeployRequest{}) - case scheduler.MethodRestart: - showCommandArgs(npa, cmd.Args, scheduler.RestartRequest{}) - case scheduler.MethodTerminate: - showCommandArgs(npa, cmd.Args, scheduler.TerminateRequest{}) - default: - showCommandArgs(npa, cmd.Args, make(map[string]any)) - } + // Show commands. + fmt.Printf("Commands:\n") + if len(out.MachineCommands) > 0 { + for _, qc := range out.MachineCommands { + fmt.Printf(" - ID: %s\n", qc.ID) + + var cmd scheduler.Command + err := cbor.Unmarshal(qc.Cmd, &cmd) + switch err { + case nil: + // Decodable scheduler command. + fmt.Printf(" Method: %s\n", cmd.Method) + fmt.Printf(" Args:\n") + + switch cmd.Method { + case scheduler.MethodDeploy: + showCommandArgs(mCfg.NPA, cmd.Args, scheduler.DeployRequest{}) + case scheduler.MethodRestart: + showCommandArgs(mCfg.NPA, cmd.Args, scheduler.RestartRequest{}) + case scheduler.MethodTerminate: + showCommandArgs(mCfg.NPA, cmd.Args, scheduler.TerminateRequest{}) default: - // Unknown command format. - fmt.Printf(" \n", qc.Cmd) + showCommandArgs(mCfg.NPA, cmd.Args, make(map[string]any)) } + default: + // Unknown command format. + fmt.Printf(" \n", qc.Cmd) } - } else { - fmt.Printf(" \n") } - }, + } else { + fmt.Printf(" \n") + } } -func showMachinePorts(extraCfg *roflCmdBuild.AppExtraConfig, appID rofl.AppID, insDsc *roflmarket.Instance, domain string) { +func prettyPrintMachinePorts(extraCfg *roflCmdBuild.AppExtraConfig, appID rofl.AppID, insDsc *roflmarket.Instance, domain string) { if extraCfg == nil || len(extraCfg.Ports) == 0 { return }