Documentation
¶
Overview ¶
Package cubebox provides a CLI for managing cubebox.
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var Command = cli.Command{ Name: "cubebox", Aliases: []string{"cubebox"}, Usage: "manage cubebox", Subcommands: cli.Commands{ MultiRun, ListCommand, InfoCommand, DestroyCommand, RollbackCommand, SandboxCommand, NodeCommand, SnapshotCommand, StorageCommand, OperationCommand, TemplateCommand, VolumeCommand, }, }
View Source
var DestroyCommand = cli.Command{ Name: "destroy", Aliases: []string{"rm"}, Usage: "destroy sandbox instances", ArgsUsage: "<sandbox-id> [sandbox-id ...]", Action: func(c *cli.Context) error { if c.NArg() == 0 { _ = cli.ShowCommandHelp(c, "destroy") return errors.New("sandbox id is required") } serverList = getServerAddrs(c) if len(serverList) == 0 { log.Printf("no server addr") return errors.New("no server addr") } port = c.GlobalString("port") var rmErr error for _, sandboxID := range c.Args() { err := doInnerDestroySandbox(c, sandboxID, map[string]string{constants.Caller: "mastercli"}, "") if err != nil { log.Printf("destroy failed: %s %s\n", sandboxID, err.Error()) rmErr = errors.Join(rmErr, fmt.Errorf("%s: %w", sandboxID, err)) continue } log.Printf("destroyed: %s\n", sandboxID) } return rmErr }, }
View Source
var InfoCommand = cli.Command{ Name: "info", Usage: "info sandboxes", Flags: []cli.Flag{ cli.StringFlag{ Name: "sandboxid,s", Usage: "Either sandboxid or hostid; both can be specified, though specifying both has little effect since sandboxid determines the host", }, cli.StringFlag{ Name: "hostid,t", Usage: "Either hostid or sandboxid; both can be specified to query a specific instance", }, cli.BoolFlag{ Name: "old", Usage: "/cube/sandbox/info legacy API usage", }, cli.IntFlag{ Name: "containerport,p", Usage: "Optional, target container port when querying exposed port", }, cli.StringFlag{ Name: "callerhostip", Usage: "Optional, simulate the HostIP of the cube proxy node to select tap ip or host port", }, }, Action: func(c *cli.Context) error { hostID := c.String("hostid") sandboxID := c.String("sandboxid") if hostID == "" && sandboxID == "" { return errors.New("hostid and sandboxid cannot both be empty") } serverList = getServerAddrs(c) if len(serverList) == 0 { log.Printf("no server addr\n") return errors.New("no server addr") } port = c.GlobalString("port") requestID := uuid.New().String() host := serverList[rand.Int()%len(serverList)] containerPort := c.Int("containerport") url := "" var body io.Reader if c.Bool("old") { req := &types.GetCubeSandboxReq{ RequestID: requestID, SandboxID: sandboxID, HostID: hostID, ContainerPort: int32(containerPort), } reqEn, _ := jsoniter.Marshal(req) body = bytes.NewBuffer(reqEn) url = fmt.Sprintf("http://%s/cube/sandbox/info", net.JoinHostPort(host, port)) } else { if hostID != "" { url = fmt.Sprintf("http://%s/cube/sandbox/info?requestID=%s&host_id=%s", net.JoinHostPort(host, port), requestID, hostID) } else { url = fmt.Sprintf("http://%s/cube/sandbox/info?requestID=%s&sandbox_id=%s", net.JoinHostPort(host, port), requestID, sandboxID) } if containerPort > 0 { url = fmt.Sprintf("%s&container_port=%d", url, containerPort) } } rsp := &types.GetCubeSandboxRes{} err := doHttpReq(c, url, http.MethodGet, requestID, body, rsp) if err != nil { log.Printf("list_getBodyData err. %s. RequestId: %s\n", err.Error(), requestID) return err } if rsp.Ret.RetCode != 200 { log.Printf("rsp err. %s. RequestId: %s\n", rsp.Ret.RetMsg, requestID) return errors.New(rsp.Ret.RetMsg) } w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0) for idx, sb := range rsp.Data { if idx > 0 { fmt.Fprintln(w) } printSandboxInfoBlock(w, sb) } return w.Flush() }, }
View Source
var ListCommand = cli.Command{ Name: "list", Aliases: []string{"ls"}, Usage: "list sandboxes", Flags: []cli.Flag{ cli.IntFlag{ Name: "index,i", Value: 1, Usage: "Used with size; starting position of cube host list (sorted by primary key id in db), starts from 1, mutually exclusive with hostid", }, cli.IntFlag{ Name: "size,s", Value: 1, Usage: "Used with index; number of hosts to traverse in this request, mutually exclusive with hostid", }, cli.StringFlag{ Name: "hostid,t", Usage: "Required when (index,size) is not specified; mutually exclusive with (index,size)", }, cli.BoolFlag{ Name: "old", Usage: "/cube/sandbox/info legacy API usage", }, cli.StringSliceFlag{ Name: "filter", Usage: "Filter conditions, multiple supported, format: key=value,key=value,key=value", }, cli.StringFlag{ Name: "type", Value: "cubebox", Usage: "instancetype,cubebox", }, cli.BoolFlag{ Name: "delete", Usage: "Whether to delete, must be used with hostid", }, cli.BoolFlag{ Name: "quiet, q", Usage: "print only the container id", }, cli.BoolFlag{ Name: "wide,w", Usage: "display more detailed info", }, cli.BoolFlag{ Name: "all", Usage: "scan all healthy nodes across the cluster", }, cli.StringFlag{ Name: "sandboxid", Usage: "sandbox id", }, }, Action: func(c *cli.Context) error { hostID := c.String("hostid") startIdx := c.Int("index") size := c.Int("size") all := c.Bool("all") if all && hostID != "" { return errors.New("all flag cannot be used with hostid") } if hostID == "" && (startIdx == 0 || size == 0) { return errors.New("at least one of hostid or (start_idx, size) must be provided") } serverList = getServerAddrs(c) if len(serverList) == 0 { log.Printf("no server addr\n") return errors.New("no server addr") } port = c.GlobalString("port") requestID := uuid.New().String() host := serverList[rand.Int()%len(serverList)] quiet := c.Bool("quiet") delete := c.Bool("delete") sandboxID := c.String("sandboxid") if delete && (hostID == "" || !quiet) { return errors.New("delete flag must be used with hostid and quiet flag") } if delete && all { return errors.New("delete flag cannot be used with all flag") } filters, filterList := parseListFilters(c.StringSlice("filter")) req := &types.ListCubeSandboxReq{ RequestID: requestID, HostID: hostID, StartIdx: startIdx, Size: size, InstanceType: c.String("type"), } if len(filters) > 0 { req.Filter = &types.CubeSandboxFilter{ LabelSelector: filters, } } rsp, summary, err := runListQuery(c, host, req, filterList, all) if err != nil { log.Printf("list_getBodyData err. %s. RequestId: %s\n", err.Error(), requestID) return err } if rsp.Ret == nil { return errors.New("empty response") } if rsp.Ret.RetCode != 200 { log.Printf("rsp err. %s. RequestId: %s\n", rsp.Ret.RetMsg, requestID) return errors.New(rsp.Ret.RetMsg) } if quiet { var resolvedSandboxID string if sandboxID != "" { candidates := make([]string, 0, len(rsp.Data)) for _, sandbox := range rsp.Data { candidates = append(candidates, sandbox.SandboxID) } var err error resolvedSandboxID, err = sandboxid.Resolve(sandboxID, candidates) if err != nil { return err } } for _, sandbox := range rsp.Data { if delete { if resolvedSandboxID != "" && sandbox.SandboxID != resolvedSandboxID { continue } err = doInnerDestroySandbox(c, sandbox.SandboxID, sandbox.Labels, c.String("type")) if err != nil { log.Printf("doDestroySandbox err. %s. RequestId: %s\n", err.Error(), requestID) } log.Printf("doDestroySandbox success: %s\n", sandbox.SandboxID) } else { fmt.Println(sandbox.SandboxID) } } return nil } w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0) fmt.Fprintf(w, "NODE_SCOPE\t%s\n", summary.NodeScope) fmt.Fprintf(w, "NODES_SCANNED\t%d/%d\n", summary.NodesScanned, summary.NodesTotal) fmt.Fprintf(w, "SANDBOX_COUNT\t%d\n", summary.SandboxCount) fmt.Fprintln(w) tabHeader := "sandbox_id\tstatus\thost_id\tcreate_at\tpause_at" if c.Bool("wide") { tabHeader += "\ttemplate_id\tnamespace\thost_ip\tlabels" } sort.Slice(rsp.Data, func(i, j int) bool { return rsp.Data[i].CreateAt < rsp.Data[j].CreateAt }) fmt.Fprintln(w, tabHeader) for _, sandbox := range rsp.Data { row := fmt.Sprintf("%s\t%s\t%s\t%s\t%s", sandbox.SandboxID, getStatus(sandbox.Status), sandbox.HostID, formatTime(sandbox.CreateAt), formatTime(sandbox.PauseAt)) if c.Bool("wide") { row += fmt.Sprintf("\t%s\t%s\t%s\t%s", sandbox.TemplateID, sandbox.NameSpace, sandbox.HostIP, utils.InterfaceToString(sandbox.Labels)) } if _, err := fmt.Fprintln(w, row); err != nil { return err } } return w.Flush() }, }
View Source
var ListInventoryCommand = cli.Command{ Name: "listinventory", Usage: "list inventory", Aliases: []string{"li"}, Flags: []cli.Flag{ cli.StringSliceFlag{ Name: "filter", Usage: "Filter conditions, multiple supported, format: key=value,key=value,key=value", }, cli.StringFlag{ Name: "type", Value: "cubebox", Usage: "instancetype,cubebox", }, }, Action: func(c *cli.Context) error { serverList = getServerAddrs(c) if len(serverList) == 0 { log.Printf("no server addr\n") return errors.New("no server addr") } port = c.GlobalString("port") requestID := uuid.New().String() host := serverList[rand.Int()%len(serverList)] filterlist := c.StringSlice("filter") filters := make(map[string][]string) for _, filter := range filterlist { labels := strings.TrimSpace(filter) if labels == "" { continue } kv := strings.Split(labels, "=") if len(kv) >= 2 { filters[kv[0]] = append(filters[kv[0]], kv[1]) } } req := &types.ListInventoryReq{ RequestID: requestID, InstanceType: c.String("type"), } if len(filters) > 0 { for k, v := range filters { req.Filters = append(req.Filters, &types.FilterItem{ Name: k, Values: v, }) } } reqEn, _ := jsoniter.Marshal(req) url := fmt.Sprintf("http://%s/cube/listinventory", net.JoinHostPort(host, port)) rsp := &types.ListInventoryRes{} err := doHttpReq(c, url, http.MethodPost, requestID, bytes.NewBuffer(reqEn), rsp) if err != nil { log.Printf("list_getBodyData err. %s. RequestId: %s\n", err.Error(), requestID) return err } if rsp.Ret.RetCode != 200 { log.Printf("rsp err. %s. RequestId: %s\n", rsp.Ret.RetMsg, requestID) return errors.New(rsp.Ret.RetMsg) } w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0) fmt.Fprintln(w, "Zone\tCpuType\tCPU\tMemory") for _, res := range rsp.Data { fmt.Fprintf(w, "%s\t%s\t%d\t%d\n", res.Zone, res.CPUType, res.CPU, res.Memory) } return w.Flush() }, }
View Source
var MultiRun = cli.Command{ Name: "multirun", Usage: "run one or more containers", ArgsUsage: "[flags] req.json1 [req.json2, ...]", Action: multiRunAction, Flags: []cli.Flag{ &cli.Int64Flag{ Name: "runcnt", Value: 1, Usage: "loop every req count", }, &cli.IntFlag{ Name: "runcc", Value: 1, Usage: "concurrency of every req.json", }, &cli.BoolFlag{ Name: "norm", Usage: "bench not remove", }, &cli.BoolFlag{ Name: "printall", Usage: "print all result metric", }, &cli.StringFlag{ Name: "percents", Value: "0.05,0.5,0.8,0.99", Usage: "Percentiles param", }, &cli.BoolFlag{ Name: "fail_exit", Usage: "exit cli when create failure", }, &cli.DurationFlag{ Name: "sleep_before_del", Value: 0, Usage: "sleep before delete container", }, &cli.DurationFlag{ Name: "sleep_before_req", Value: 0, Usage: "sleep before req container", }, &cli.BoolFlag{ Name: "same", Usage: "Run with strict concurrent synchronization", }, &cli.IntFlag{ Name: "delcc", Usage: "concurrency of destroy", }, cli.StringFlag{ Name: "hostid,t", Usage: "Internal debug param; requires HTTP header to take effect. Specify physical machine ID to create containers", }, cli.StringFlag{ Name: "hostip,s", Usage: "Internal debug param; requires HTTP header to take effect. Specify physical IP to create containers", }, &cli.BoolFlag{ Name: "testmocksch", Usage: "test mock scheduling", }, &cli.StringFlag{ Name: "biztype", Value: "all", Usage: "business type,default is all,[all/sh/shtcb/gz/bj]", }, &cli.BoolFlag{ Name: "testmultireq", Usage: "test multi req json from file stub", }, cli.StringFlag{ Name: "multireq_user_id", Value: "123456789", Usage: "user_id parameter for multiple functions", }, &cli.IntFlag{ Name: "async_retry_max", Value: 100, Usage: "max retry count for async retry queue, 0 means give up immediately after sync retry failed", }, }, }
View Source
var NodeCommand = cli.Command{ Name: "node", Aliases: []string{"nodes"}, Usage: "list / isolate / unisolate cubemaster nodes", Subcommands: cli.Commands{ NodeListCommand, NodeIsolateCommand, NodeUnisolateCommand, }, }
View Source
var NodeIsolateCommand = cli.Command{ Name: "isolate", Usage: "cordon node(s) (block new sandbox scheduling; existing sandboxes unaffected)", ArgsUsage: "<node-id> [node-id ...]", Flags: nodeIsolationFlags, Action: func(c *cli.Context) error { return doNodeIsolation(c, http.MethodPut) }, }
View Source
var NodeListCommand = cli.Command{ Name: "list", Aliases: []string{"ls"}, Usage: "list node status from cubemaster internal endpoint", Flags: []cli.Flag{ cli.StringFlag{ Name: "hostid", Usage: "query single host/node id", }, cli.BoolFlag{ Name: "score-only", Usage: "only query score/update timestamps", }, cli.BoolFlag{ Name: "json", Usage: "print raw json response", }, }, Action: func(c *cli.Context) error { serverList = getServerAddrs(c) if len(serverList) == 0 { log.Printf("no server addr\n") return errors.New("no server addr") } port = c.GlobalString("port") requestID := uuid.New().String() host := serverList[rand.Int()%len(serverList)] url := fmt.Sprintf("http://%s/internal/node?requestID=%s", net.JoinHostPort(host, port), requestID) if hostID := c.String("hostid"); hostID != "" { url += "&host_id=" + hostID } if c.Bool("score-only") { url += "&score_only=true" } rsp := &nodeResponse{} if err := doHttpReq(c, url, http.MethodGet, requestID, nil, rsp); err != nil { log.Printf("node list request err. %s. RequestId: %s\n", err.Error(), requestID) return err } if rsp.Ret == nil { return errors.New("empty response") } if rsp.Ret.RetCode != 200 { log.Printf("node list failed. %s. RequestId: %s\n", rsp.Ret.RetMsg, requestID) return errors.New(rsp.Ret.RetMsg) } sort.Slice(rsp.Data, func(i, j int) bool { return rsp.Data[i].ID() < rsp.Data[j].ID() }) if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } printNodeSummary(rsp.Data, c.Bool("score-only")) return nil }, }
View Source
var NodeUnisolateCommand = cli.Command{ Name: "unisolate", Usage: "remove cordon so the node(s) can receive new sandboxes", ArgsUsage: "<node-id> [node-id ...]", Flags: nodeIsolationFlags, Action: func(c *cli.Context) error { return doNodeIsolation(c, http.MethodDelete) }, }
View Source
var OperationCommand = cli.Command{ Name: "operation", Usage: "inspect snapshot operations", Subcommands: cli.Commands{ OperationStatusCommand, OperationWatchCommand, }, }
View Source
var OperationStatusCommand = cli.Command{ Name: "status", Usage: "show snapshot operation status", Flags: []cli.Flag{ cli.StringFlag{Name: "operation-id", Usage: "snapshot operation id"}, cli.BoolFlag{Name: "json", Usage: "print raw json response"}, }, Action: func(c *cli.Context) error { operationID := c.String("operation-id") if operationID == "" { return errors.New("operation-id is required") } rsp, err := fetchOperationStatus(c, operationID) if err != nil { return err } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } printOperationResponse(rsp.Operation) return nil }, }
View Source
var OperationWatchCommand = cli.Command{ Name: "watch", Usage: "watch snapshot operation until completion", Flags: []cli.Flag{ cli.StringFlag{Name: "operation-id", Usage: "snapshot operation id"}, cli.DurationFlag{Name: "interval", Value: 2 * time.Second, Usage: "poll interval"}, cli.BoolFlag{Name: "json", Usage: "print final raw json response"}, }, Action: func(c *cli.Context) error { operationID := c.String("operation-id") if operationID == "" { return errors.New("operation-id is required") } var lastPrinted string for { rsp, err := fetchOperationStatus(c, operationID) if err != nil { return err } current := "" if rsp.Operation != nil { current = fmt.Sprintf("%s/%d/%s", rsp.Operation.Status, rsp.Operation.Progress, rsp.Operation.Phase) } if current != lastPrinted { printOperationResponse(rsp.Operation) lastPrinted = current } if operationFinished(rsp.Operation) { if c.Bool("json") { commands.PrintAsJSON(rsp) } if rsp.Operation.Status == "FAILED" { return errors.New(rsp.Operation.ErrorMessage) } return nil } time.Sleep(c.Duration("interval")) } }, }
View Source
var RollbackCommand = cli.Command{ Name: "rollback", Aliases: []string{"sandbox-rollback"}, Usage: "rollback a sandbox to a snapshot", Flags: []cli.Flag{ cli.StringFlag{Name: "sandbox-id", Usage: "sandbox id to rollback"}, cli.StringFlag{Name: "snapshot-id", Usage: "snapshot id to rollback to"}, cli.StringFlag{Name: "instance-type", Usage: "instance type override"}, cli.BoolFlag{Name: "json", Usage: "print raw json response"}, }, Action: func(c *cli.Context) error { sandboxID := c.String("sandbox-id") snapshotID := c.String("snapshot-id") if sandboxID == "" || snapshotID == "" { return errors.New("sandbox-id and snapshot-id are required") } requestID := uuid.NewString() req := &rollbackRequest{ RequestID: requestID, SandboxID: sandboxID, SnapshotID: snapshotID, InstanceType: c.String("instance-type"), } body, err := jsoniter.Marshal(req) if err != nil { return err } rsp := &operationResponse{} if err := doSnapshotReq(c, http.MethodPost, "/cube/sandbox/rollback", requestID, bytes.NewBuffer(body), rsp); err != nil { return err } if err := ensureSuccessRet(rsp.Ret); err != nil { return err } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } printOperationResponse(rsp.Operation) return nil }, }
View Source
var SandboxCommand = cli.Command{ Name: "sandbox", Usage: "sandbox operations", Subcommands: cli.Commands{ RollbackCommand, }, }
View Source
var SnapshotCommand = cli.Command{ Name: "snapshot", Usage: "manage runtime snapshots", Subcommands: cli.Commands{ SnapshotCreateCommand, SnapshotListCommand, SnapshotInfoCommand, SnapshotDeleteCommand, }, }
View Source
var SnapshotCreateCommand = cli.Command{ Name: "create", Usage: "create a snapshot from an existing sandbox", Flags: []cli.Flag{ cli.StringFlag{Name: "sandbox-id", Usage: "sandbox id to snapshot"}, cli.StringFlag{Name: "display-name", Usage: "snapshot display name"}, cli.BoolFlag{Name: "json", Usage: "print raw json response"}, }, Action: func(c *cli.Context) error { sandboxID := strings.TrimSpace(c.String("sandbox-id")) if sandboxID == "" { return errors.New("sandbox-id is required") } requestID := uuid.NewString() req := &snapshotCreateRequest{ RequestID: requestID, SandboxID: sandboxID, DisplayName: c.String("display-name"), } body, err := jsoniter.Marshal(req) if err != nil { return err } rsp := &snapshotResponse{} if err := doSnapshotReq(c, http.MethodPost, "/cube/snapshot", requestID, bytes.NewBuffer(body), rsp); err != nil { return err } if err := ensureSuccessRet(rsp.Ret); err != nil { return err } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } printSnapshotResponse(rsp) return nil }, }
View Source
var SnapshotDeleteCommand = cli.Command{ Name: "delete", Usage: "delete snapshot metadata and node replicas", Flags: []cli.Flag{ cli.StringFlag{Name: "snapshot-id", Usage: "snapshot id to delete"}, cli.StringFlag{Name: "instance-type", Usage: "instance type override"}, cli.BoolFlag{Name: "json", Usage: "print raw json response"}, }, Action: func(c *cli.Context) error { snapshotID := c.String("snapshot-id") if snapshotID == "" { return errors.New("snapshot-id is required") } requestID := uuid.NewString() query := fmt.Sprintf("?request_id=%s", requestID) if instanceType := strings.TrimSpace(c.String("instance-type")); instanceType != "" { query += "&instance_type=" + instanceType } rsp := &operationResponse{} if err := doSnapshotReq(c, http.MethodDelete, "/cube/snapshot/"+snapshotID+query, requestID, nil, rsp); err != nil { return err } if err := ensureSuccessRet(rsp.Ret); err != nil { return err } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } printOperationResponse(rsp.Operation) return nil }, }
View Source
var SnapshotInfoCommand = cli.Command{ Name: "info", Aliases: []string{"describe"}, Usage: "show snapshot metadata and node replicas", Flags: []cli.Flag{ cli.StringFlag{Name: "snapshot-id", Usage: "snapshot id to query"}, cli.BoolFlag{Name: "include-request", Usage: "include stored create request"}, cli.BoolFlag{Name: "json", Usage: "print raw json response"}, }, Action: func(c *cli.Context) error { snapshotID := c.String("snapshot-id") if snapshotID == "" { return errors.New("snapshot-id is required") } requestID := uuid.NewString() endpoint := fmt.Sprintf("/cube/snapshot/%s", snapshotID) if c.Bool("include-request") { endpoint += "?include_request=true" } rsp := &snapshotResponse{} if err := doSnapshotReq(c, http.MethodGet, endpoint, requestID, nil, rsp); err != nil { return err } if err := ensureSuccessRet(rsp.Ret); err != nil { return err } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } printSnapshotResponse(rsp) return nil }, }
View Source
var SnapshotListCommand = cli.Command{ Name: "list", Aliases: []string{"ls"}, Usage: "list runtime snapshots", Flags: []cli.Flag{ cli.BoolFlag{Name: "json", Usage: "print raw json response"}, cli.StringFlag{Name: "output,o", Usage: "output format, set to wide for more columns"}, }, Action: func(c *cli.Context) error { rsp := &snapshotListResponse{} requestID := uuid.NewString() if err := doSnapshotReq(c, http.MethodGet, "/cube/snapshot", requestID, nil, rsp); err != nil { return err } if err := ensureSuccessRet(rsp.Ret); err != nil { return err } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } wideOutput := strings.EqualFold(strings.TrimSpace(c.String("output")), "wide") w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0) header := "SNAPSHOT_ID\tSTATUS\tSANDBOX_ID\tNODE_ID\tCREATED_AT" if wideOutput { header = "SNAPSHOT_ID\tSTATUS\tDISPLAY_NAME\tSANDBOX_ID\tNODE_ID\tRUNTIME_REFS\tBACKEND\tLAST_ERROR" } fmt.Fprintln(w, header) for _, item := range rsp.Data { originNode := item.OriginNodeID if wideOutput { fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%d\t%s\t%s\n", item.SnapshotID, item.Status, item.DisplayName, item.OriginSandboxID, originNode, item.RuntimeRefCount, item.StorageBackend, item.LastError) continue } fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", item.SnapshotID, item.Status, item.OriginSandboxID, originNode, item.CreatedAt) } return w.Flush() }, }
View Source
var StorageCommand = cli.Command{ Name: "storage", Usage: "inspect snapshot storage state", Subcommands: cli.Commands{ StorageStatusCommand, }, }
View Source
var StorageStatusCommand = cli.Command{ Name: "status", Usage: "show aggregated snapshot storage status from cubemaster", Flags: []cli.Flag{ cli.BoolFlag{Name: "refresh", Usage: "refresh node metrics before returning"}, cli.BoolFlag{Name: "json", Usage: "print raw json response"}, }, Action: func(c *cli.Context) error { requestID := uuid.NewString() endpoint := "/cube/snapshot/storage" if c.Bool("refresh") { endpoint += "?refresh=true" } rsp := &snapshotStorageStatusResponse{} if err := doSnapshotReq(c, http.MethodGet, endpoint, requestID, nil, rsp); err != nil { return err } if err := ensureSuccessRet(rsp.Ret); err != nil { return err } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } printSnapshotStorageStatus(rsp.Data) return nil }, }
View Source
var TemplateBuildStatusCommand = cli.Command{ Name: "build-status", Usage: "show sandbox commit build status", Flags: []cli.Flag{ cli.StringFlag{Name: "build-id", Usage: "template build id"}, cli.BoolFlag{Name: "json", Usage: "print raw json response"}, }, Action: func(c *cli.Context) error { buildID := c.String("build-id") if buildID == "" { return errors.New("build-id is required") } rsp, err := fetchTemplateBuildStatus(c, buildID) if err != nil { return err } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } printTemplateBuildStatus(rsp) return nil }, }
View Source
var TemplateBuildWatchCommand = cli.Command{ Name: "build-watch", Usage: "watch sandbox commit build status until completion", Flags: []cli.Flag{ cli.StringFlag{Name: "build-id", Usage: "template build id"}, cli.DurationFlag{Name: "interval", Value: 2 * time.Second, Usage: "poll interval"}, cli.BoolFlag{Name: "json", Usage: "print final raw json response"}, }, Action: func(c *cli.Context) error { buildID := c.String("build-id") if buildID == "" { return errors.New("build-id is required") } return runBuildWatch(c, buildID) }, }
View Source
var TemplateCommand = cli.Command{ Name: "template", Aliases: []string{"tpl"}, Usage: "manage cubebox templates", Subcommands: cli.Commands{ TemplateCreateCommand, TemplateCommitCommand, TemplateCreateFromImageCommand, TemplateRedoCommand, TemplateDeleteCommand, TemplateStatusCommand, TemplateWatchCommand, TemplateBuildStatusCommand, TemplateBuildWatchCommand, TemplateListCommand, TemplateInfoCommand, TemplateRenderCommand, }, }
View Source
var TemplateCommitCommand = cli.Command{ Name: "commit", Usage: "commit an existing sandbox into a template", Flags: []cli.Flag{ cli.StringFlag{ Name: "sandbox-id", Usage: "sandbox id to commit", }, cli.StringFlag{ Name: "file, f", Usage: "original create_sandbox request json file", }, cli.BoolFlag{ Name: "allow-internet-access", Usage: "set allowInternetAccess on the network config for the create_request", }, cli.StringSliceFlag{ Name: "allow-out-cidr", Usage: "append an allowed egress CIDR to create_request.cube_network_config; repeat the flag to specify multiple CIDRs", }, cli.StringSliceFlag{ Name: "deny-out-cidr", Usage: "append a denied egress CIDR to create_request.cube_network_config; repeat the flag to specify multiple CIDRs", }, cli.BoolFlag{ Name: "detach, no-wait", Usage: "submit and exit immediately instead of watching the build to completion", }, cli.DurationFlag{ Name: "interval", Value: defaultWatchInterval, Usage: "poll interval while watching the build", }, cli.BoolFlag{ Name: "json", Usage: "print raw json response", }, }, Action: func(c *cli.Context) error { sandboxID := c.String("sandbox-id") filePath := c.String("file") if filePath == "" && c.NArg() > 0 { filePath = c.Args().First() } if sandboxID == "" || filePath == "" { return errors.New("sandbox-id and file are required") } reqBytes, err := getParams(filePath) if err != nil { return err } createReq := &types.CreateCubeSandboxReq{} if err = jsoniter.Unmarshal(reqBytes, createReq); err != nil { return err } if createReq.Request == nil { createReq.Request = &types.Request{} } requestID := uuid.New().String() createReq.RequestID = requestID if createReq.Annotations == nil { createReq.Annotations = map[string]string{} } createReq.CubeNetworkConfig = mergeCubeNetworkConfigFlags(c, createReq.CubeNetworkConfig) req := &templateCommitRequest{ RequestID: requestID, SandboxID: sandboxID, CreateRequest: createReq, } serverList = getServerAddrs(c) if len(serverList) == 0 { return errors.New("no server addr") } port = c.GlobalString("port") host := serverList[rand.Int()%len(serverList)] url := fmt.Sprintf("http://%s/cube/sandbox/commit", net.JoinHostPort(host, port)) body, err := jsoniter.Marshal(req) if err != nil { return err } rsp := &templateCommitResponse{} if err = doHttpReq(c, url, http.MethodPost, requestID, bytes.NewBuffer(body), rsp); err != nil { log.Printf("template commit request err. %s. RequestId: %s\n", err.Error(), requestID) return err } if rsp.Ret == nil { return errors.New("empty response") } if rsp.Ret.RetCode != 200 { log.Printf("template commit failed. %s. RequestId: %s\n", rsp.Ret.RetMsg, requestID) return errors.New(rsp.Ret.RetMsg) } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } log.Printf("template_id: %s\n", rsp.TemplateID) log.Printf("build_id: %s\n", rsp.BuildID) if detachRequested(c) || rsp.BuildID == "" { return nil } return runBuildWatch(c, rsp.BuildID) }, }
View Source
var TemplateCreateCommand = cli.Command{ Name: "create", Usage: "create template snapshots on healthy nodes", Flags: []cli.Flag{ cli.StringFlag{ Name: "file, f", Usage: "template create request json file", }, cli.StringFlag{ Name: "version", Value: "v2", Usage: "template version annotation", }, cli.StringFlag{ Name: "snapshot-dir", Usage: "override snapshot dir passed to cubemaster", }, cli.StringFlag{ Name: "instance-type", Usage: "override instance type in request body", }, cli.StringSliceFlag{ Name: "node", Usage: "create template only on the specified node id or host ip; repeat to specify multiple nodes", }, cli.BoolFlag{ Name: "allow-internet-access", Usage: "set allowInternetAccess on the network config for the template request", }, cli.StringSliceFlag{ Name: "allow-out-cidr", Usage: "append an allowed egress CIDR to cube_network_config; repeat the flag to specify multiple CIDRs", }, cli.StringSliceFlag{ Name: "deny-out-cidr", Usage: "append a denied egress CIDR to cube_network_config; repeat the flag to specify multiple CIDRs", }, cli.BoolFlag{ Name: "json", Usage: "print raw json response", }, }, Action: func(c *cli.Context) error { filePath := c.String("file") if filePath == "" && c.NArg() > 0 { filePath = c.Args().First() } if filePath == "" { return errors.New("file is required") } reqBytes, err := getParams(filePath) if err != nil { return err } req := &types.CreateCubeSandboxReq{} if err = jsoniter.Unmarshal(reqBytes, req); err != nil { return err } if req.Request == nil { req.Request = &types.Request{} } req.RequestID = uuid.New().String() if req.Annotations == nil { req.Annotations = map[string]string{} } req.Annotations[constants.CubeAnnotationsAppSnapshotCreate] = "true" version := c.String("version") if version != "" { req.Annotations[constants.CubeAnnotationAppSnapshotVersion] = version req.Annotations[constants.CubeAnnotationAppSnapshotTemplateVersion] = version } if snapshotDir := c.String("snapshot-dir"); snapshotDir != "" { req.SnapshotDir = snapshotDir } if instanceType := c.String("instance-type"); instanceType != "" { req.InstanceType = instanceType } if scope := c.StringSlice("node"); len(scope) > 0 { req.DistributionScope = scope } req.CubeNetworkConfig = mergeCubeNetworkConfigFlags(c, req.CubeNetworkConfig) serverList = getServerAddrs(c) if len(serverList) == 0 { log.Printf("no server addr\n") return errors.New("no server addr") } port = c.GlobalString("port") host := serverList[rand.Int()%len(serverList)] url := fmt.Sprintf("http://%s/cube/template", net.JoinHostPort(host, port)) body, err := jsoniter.Marshal(req) if err != nil { return err } rsp := &templateResponse{} if err = doHttpReq(c, url, http.MethodPost, req.RequestID, bytes.NewBuffer(body), rsp); err != nil { log.Printf("template create request err. %s. RequestId: %s\n", err.Error(), req.RequestID) return err } if rsp.Ret == nil { return errors.New("empty response") } if rsp.Ret.RetCode != 200 { log.Printf("template create failed. %s. RequestId: %s\n", rsp.Ret.RetMsg, req.RequestID) return errors.New(rsp.Ret.RetMsg) } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } printTemplateSummary(rsp) return nil }, }
View Source
var TemplateCreateFromImageCommand = cli.Command{ Name: "create-from-image", Usage: "build ext4 rootfs from OCI image and create template asynchronously", Flags: []cli.Flag{ cli.StringFlag{Name: "image", Usage: "source OCI image reference"}, cli.StringFlag{Name: "alias", Usage: "human-readable stable alias for the template (e.g. my-app); sandboxes can reference the template by this alias instead of the generated template ID; valid: [a-z0-9-], max 64 chars"}, cli.StringFlag{Name: "writable-layer-size", Usage: "immutable writable layer size, e.g. 20Gi"}, cli.StringSliceFlag{Name: "expose-port", Usage: "container port to expose for the template; repeat the flag to specify multiple ports"}, cli.StringFlag{Name: "instance-type", Value: "cubebox", Usage: "instance type"}, cli.StringFlag{Name: "network-type", Value: "tap", Usage: "network type"}, cli.StringSliceFlag{Name: "node", Usage: "create template only on the specified node id or host ip; repeat to specify multiple nodes"}, cli.BoolFlag{Name: "allow-internet-access", Usage: "set allowInternetAccess on the network config for the generated template request"}, cli.StringSliceFlag{Name: "allow-out-cidr", Usage: "append an allowed egress CIDR to cube_network_config; repeat the flag to specify multiple CIDRs"}, cli.StringSliceFlag{Name: "deny-out-cidr", Usage: "append a denied egress CIDR to cube_network_config; repeat the flag to specify multiple CIDRs"}, cli.StringFlag{Name: "registry-username", Usage: "registry username"}, cli.StringFlag{Name: "registry-password", Usage: "registry password"}, cli.BoolFlag{Name: "enable-ivshmem", Usage: "boot the template build sandbox with ivshmem enabled"}, cli.StringSliceFlag{Name: "cmd", Usage: "override container ENTRYPOINT (command); repeat for multiple elements, e.g. --cmd /bin/sh --cmd -c"}, cli.StringSliceFlag{Name: "arg", Usage: "override container CMD (args); repeat for multiple elements"}, cli.StringSliceFlag{Name: "env", Usage: "set environment variable, KEY=VALUE format; repeat for multiple envs"}, cli.StringSliceFlag{Name: "dns", Usage: "set container DNS nameserver; repeat for multiple servers"}, cli.IntFlag{Name: "probe", Usage: "enable HTTP GET probe on the specified port (e.g. --probe 9000); sets timeout_ms=30000, period_ms=500"}, cli.StringFlag{Name: "probe-path", Value: "/health", Usage: "HTTP path for the readiness probe (default: /health); only effective when --probe is set"}, cli.IntFlag{Name: "cpu", Value: 2000, Usage: "CPU millicores for the template container (default: 2000, i.e. 2 cores)"}, cli.IntFlag{Name: "memory", Value: 2000, Usage: "Memory for the template container in MB (default: 2000 MB)"}, cli.BoolTFlag{Name: "with-cube-ca", Usage: "bake the CubeEgress root CA at /etc/cube/ca/cube-root-ca.crt into the template rootfs so sandboxes trust CubeEgress's MITM. Pass --with-cube-ca=false to skip (default: true)"}, cli.BoolFlag{Name: "detach, no-wait", Usage: "submit and exit immediately instead of watching the job to completion"}, cli.DurationFlag{Name: "interval", Value: defaultWatchInterval, Usage: "poll interval while watching the job"}, cli.BoolFlag{Name: "json", Usage: "print raw json response"}, }, Action: func(c *cli.Context) error { if c.String("image") == "" { return errors.New("image is required") } if c.String("writable-layer-size") == "" { return errors.New("writable-layer-size is required") } serverList = getServerAddrs(c) if len(serverList) == 0 { return errors.New("no server addr") } port = c.GlobalString("port") host := serverList[rand.Int()%len(serverList)] exposedPorts, err := parseExposePortFlags(c.StringSlice("expose-port")) if err != nil { return err } containerOverrides, err := parseContainerOverrides(c) if err != nil { return err } req := &types.CreateTemplateFromImageReq{ Request: &types.Request{RequestID: uuid.New().String()}, SourceImageRef: c.String("image"), Alias: c.String("alias"), WritableLayerSize: c.String("writable-layer-size"), DistributionScope: c.StringSlice("node"), ExposedPorts: exposedPorts, InstanceType: c.String("instance-type"), NetworkType: c.String("network-type"), RegistryUsername: c.String("registry-username"), RegistryPassword: c.String("registry-password"), ContainerOverrides: containerOverrides, } withCubeCA := c.BoolT("with-cube-ca") req.WithCubeCA = &withCubeCA applyCreateFromImageIvshmemFlag(c, req) req.CubeNetworkConfig, err = mergeCreateFromImageCubeNetworkConfigFlags(c, req.CubeNetworkConfig) if err != nil { return err } body, err := jsoniter.Marshal(req) if err != nil { return err } url := fmt.Sprintf("http://%s/cube/template/from-image", net.JoinHostPort(host, port)) rsp := &templateImageJobResponse{} if err := doHttpReq(c, url, http.MethodPost, req.RequestID, bytes.NewBuffer(body), rsp); err != nil { return err } if rsp.Ret == nil { return errors.New("empty response") } if rsp.Ret.RetCode != 200 { return errors.New(rsp.Ret.RetMsg) } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } if detachRequested(c) || rsp.Job == nil { printTemplateImageJob(rsp.Job) return nil } log.Printf("submitted template image job: job_id=%s template_id=%s\n", rsp.Job.JobID, rsp.Job.TemplateID) return runImageJobWatch(c, rsp.Job.JobID) }, }
View Source
var TemplateDeleteCommand = cli.Command{ Name: "delete", Usage: "delete template metadata and node replicas", ArgsUsage: "<template-id>", Flags: []cli.Flag{ cli.StringFlag{ Name: "template-id", Usage: "template id to delete", }, }, Action: func(c *cli.Context) error { templateID := resolveTemplateID(c) if templateID == "" { return errors.New("template-id is required") } serverList = getServerAddrs(c) if len(serverList) == 0 { return errors.New("no server addr") } port = c.GlobalString("port") requestID := uuid.New().String() host := serverList[rand.Int()%len(serverList)] url := fmt.Sprintf("http://%s/cube/template", net.JoinHostPort(host, port)) req := &templateDeleteRequest{ RequestID: requestID, TemplateID: templateID, } body, err := jsoniter.Marshal(req) if err != nil { return err } rsp := &templateResponse{} if err := doHttpReq(c, url, http.MethodDelete, requestID, bytes.NewBuffer(body), rsp); err != nil { log.Printf("template delete request err. %s. RequestId: %s\n", err.Error(), requestID) return err } if rsp.Ret == nil { return errors.New("empty response") } if rsp.Ret.RetCode != 200 { log.Printf("template delete failed. %s. RequestId: %s\n", rsp.Ret.RetMsg, requestID) return errors.New(rsp.Ret.RetMsg) } log.Printf("template deleted: %s\n", templateID) return nil }, }
View Source
var TemplateInfoCommand = cli.Command{ Name: "info", Usage: "show template metadata and node replicas", ArgsUsage: "<template-id>", Flags: []cli.Flag{ cli.StringFlag{ Name: "template-id", Usage: "template id to query", }, cli.BoolFlag{ Name: "include-request", Usage: "include the stored create request in the response", }, cli.BoolFlag{ Name: "json", Usage: "print raw json response", }, }, Action: func(c *cli.Context) error { templateID := resolveTemplateID(c) if templateID == "" { return errors.New("template-id is required") } serverList = getServerAddrs(c) if len(serverList) == 0 { log.Printf("no server addr\n") return errors.New("no server addr") } port = c.GlobalString("port") requestID := uuid.New().String() host := serverList[rand.Int()%len(serverList)] url := fmt.Sprintf("http://%s/cube/template?template_id=%s", net.JoinHostPort(host, port), templateID) if c.Bool("include-request") { url += "&include_request=true" } rsp := &templateResponse{} if err := doHttpReq(c, url, http.MethodGet, requestID, nil, rsp); err != nil { log.Printf("template info request err. %s. RequestId: %s\n", err.Error(), requestID) return err } if rsp.Ret == nil { return errors.New("empty response") } if rsp.Ret.RetCode != 200 { log.Printf("template info failed. %s. RequestId: %s\n", rsp.Ret.RetMsg, requestID) return errors.New(rsp.Ret.RetMsg) } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } printTemplateSummary(rsp) return nil }, }
View Source
var TemplateListCommand = cli.Command{ Name: "list", Aliases: []string{"ls"}, Usage: "list templates", Flags: []cli.Flag{ cli.BoolFlag{ Name: "json", Usage: "print raw json response", }, cli.StringFlag{ Name: "output,o", Usage: "output format, set to wide for more columns", }, }, Action: func(c *cli.Context) error { serverList = getServerAddrs(c) if len(serverList) == 0 { log.Printf("no server addr\n") return errors.New("no server addr") } port = c.GlobalString("port") requestID := uuid.New().String() host := serverList[rand.Int()%len(serverList)] url := fmt.Sprintf("http://%s/cube/template", net.JoinHostPort(host, port)) rsp := &templateListResponse{} if err := doHttpReq(c, url, http.MethodGet, requestID, nil, rsp); err != nil { log.Printf("template list request err. %s. RequestId: %s\n", err.Error(), requestID) return err } if rsp.Ret == nil { return errors.New("empty response") } if rsp.Ret.RetCode != 200 { log.Printf("template list failed. %s. RequestId: %s\n", rsp.Ret.RetMsg, requestID) return errors.New(rsp.Ret.RetMsg) } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } wideOutput := strings.EqualFold(strings.TrimSpace(c.String("output")), "wide") w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0) tabHeader := "TEMPLATE_ID\tALIAS\tSTATUS\tJOB_ID\tCREATED_AT\tIMAGE_INFO" if wideOutput { tabHeader = "TEMPLATE_ID\tALIAS\tSTATUS\tJOB_ID\tLAST_ERROR\tCREATED_AT\tIMAGE_INFO" } fmt.Fprintln(w, tabHeader) for _, item := range rsp.Data { jobID := item.JobID if jobID == "" { jobID = "-" } alias := item.DisplayName if alias == "" { alias = "-" } if wideOutput { fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", item.TemplateID, alias, item.Status, jobID, item.LastError, item.CreatedAt, item.ImageInfo) continue } fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", item.TemplateID, alias, item.Status, jobID, item.CreatedAt, item.ImageInfo) } return w.Flush() }, }
View Source
var TemplateRedoCommand = cli.Command{ Name: "redo", Usage: "redo a template on all, specific, or failed nodes", ArgsUsage: "<template-id>", Flags: []cli.Flag{ cli.StringFlag{Name: "template-id", Usage: "template id to redo"}, cli.StringSliceFlag{Name: "node", Usage: "redo only the specified node id or host ip; repeat to specify multiple nodes"}, cli.BoolFlag{Name: "failed-only", Usage: "redo only failed nodes"}, cli.BoolFlag{Name: "wait", Usage: "deprecated: redo now waits by default; use --detach to opt out"}, cli.BoolFlag{Name: "detach, no-wait", Usage: "submit and exit immediately instead of watching the redo job to completion"}, cli.DurationFlag{Name: "interval", Value: defaultWatchInterval, Usage: "poll interval while watching the job"}, cli.BoolFlag{Name: "json", Usage: "print raw json response"}, }, Action: func(c *cli.Context) error { templateID := resolveTemplateID(c) if templateID == "" { return errors.New("template-id is required") } serverList = getServerAddrs(c) if len(serverList) == 0 { return errors.New("no server addr") } port = c.GlobalString("port") host := serverList[rand.Int()%len(serverList)] req := &types.RedoTemplateFromImageReq{ Request: &types.Request{RequestID: uuid.New().String()}, TemplateID: templateID, DistributionScope: c.StringSlice("node"), FailedOnly: c.Bool("failed-only"), Wait: c.Bool("wait"), } body, err := jsoniter.Marshal(req) if err != nil { return err } url := fmt.Sprintf("http://%s/cube/template/redo", net.JoinHostPort(host, port)) rsp := &templateImageJobResponse{} if err := doHttpReq(c, url, http.MethodPost, req.RequestID, bytes.NewBuffer(body), rsp); err != nil { return err } if rsp.Ret == nil { return errors.New("empty response") } if rsp.Ret.RetCode != 200 { return errors.New(rsp.Ret.RetMsg) } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } if detachRequested(c) || rsp.Job == nil { printTemplateImageJob(rsp.Job) return nil } log.Printf("submitted redo job: job_id=%s template_id=%s\n", rsp.Job.JobID, rsp.Job.TemplateID) return runImageJobWatch(c, rsp.Job.JobID) }, }
View Source
var TemplateRenderCommand = cli.Command{ Name: "render", Usage: "preview the effective sandbox request for a template", Flags: []cli.Flag{ cli.StringFlag{ Name: "file, f", Usage: "sandbox create request json file used as preview input", }, cli.StringFlag{ Name: "template-id", Usage: "template id to preview; overrides the request file annotation", }, cli.BoolFlag{ Name: "json", Usage: "print raw json response", }, }, Action: func(c *cli.Context) error { req := &types.CreateCubeSandboxReq{} filePath := c.String("file") if filePath == "" && c.NArg() > 0 { filePath = c.Args().First() } if filePath != "" { reqBytes, err := getParams(filePath) if err != nil { return err } if err = jsoniter.Unmarshal(reqBytes, req); err != nil { return err } } if req.Request == nil { req.Request = &types.Request{} } req.RequestID = uuid.New().String() if req.Annotations == nil { req.Annotations = map[string]string{} } if templateID := c.String("template-id"); templateID != "" { req.Annotations[constants.CubeAnnotationAppSnapshotTemplateID] = templateID } if req.Annotations[constants.CubeAnnotationAppSnapshotTemplateID] == "" { return errors.New("template-id is required, either in request file or flag") } if constants.GetAppSnapshotVersion(req.Annotations) == "" { constants.SetAppSnapshotVersion(req.Annotations, "v2") } serverList = getServerAddrs(c) if len(serverList) == 0 { log.Printf("no server addr\n") return errors.New("no server addr") } port = c.GlobalString("port") host := serverList[rand.Int()%len(serverList)] url := fmt.Sprintf("http://%s/cube/sandbox/preview", net.JoinHostPort(host, port)) body, err := jsoniter.Marshal(req) if err != nil { return err } rsp := &sandboxPreviewResponse{} if err = doHttpReq(c, url, http.MethodPost, req.RequestID, bytes.NewBuffer(body), rsp); err != nil { log.Printf("template render request err. %s. RequestId: %s\n", err.Error(), req.RequestID) return err } if rsp.Ret == nil { return errors.New("empty response") } if rsp.Ret.RetCode != 200 { log.Printf("template render failed. %s. RequestId: %s\n", rsp.Ret.RetMsg, req.RequestID) return errors.New(rsp.Ret.RetMsg) } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } printSandboxPreviewSummary(rsp) return nil }, }
View Source
var TemplateStatusCommand = cli.Command{ Name: "status", Usage: "show create-from-image job status", Flags: []cli.Flag{ cli.StringFlag{Name: "job-id", Usage: "template image job id"}, cli.BoolFlag{Name: "json", Usage: "print raw json response"}, }, Action: func(c *cli.Context) error { jobID := c.String("job-id") if jobID == "" { return errors.New("job-id is required") } rsp, err := fetchTemplateImageJob(c, jobID) if err != nil { return err } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } printTemplateImageJob(rsp.Job) return nil }, }
View Source
var TemplateWatchCommand = cli.Command{ Name: "watch", Usage: "watch create-from-image job progress until completion", Flags: []cli.Flag{ cli.StringFlag{Name: "job-id", Usage: "template image job id"}, cli.DurationFlag{Name: "interval", Value: 2 * time.Second, Usage: "poll interval"}, cli.BoolFlag{Name: "json", Usage: "print final raw json response"}, }, Action: func(c *cli.Context) error { jobID := c.String("job-id") if jobID == "" { return errors.New("job-id is required") } return runImageJobWatch(c, jobID) }, }
View Source
var VolumeCommand = cli.Command{ Name: "volume", Aliases: []string{"vol"}, Usage: "manage volumes (CubeMaster /cube/volume)", Subcommands: cli.Commands{ VolumeListCommand, VolumeGetCommand, VolumeDeleteCommand, }, }
View Source
var VolumeDeleteCommand = cli.Command{ Name: "delete", Aliases: []string{"rm", "remove"}, Usage: "delete a volume by id", Flags: []cli.Flag{ cli.StringFlag{Name: "volume-id", Usage: "volume id to delete"}, cli.BoolFlag{Name: "json", Usage: "print json response"}, }, Action: func(c *cli.Context) error { volumeID := resolveVolumeID(c) if volumeID == "" { return errors.New("volume id is required (positional arg or --volume-id)") } endpoint := "/cube/volume/" + url.PathEscape(volumeID) rsp := &volumeDeleteResponse{} if err := doVolumeReq(c, http.MethodDelete, endpoint, uuid.NewString(), nil, rsp); err != nil { return err } if err := ensureVolumeSuccessRet(rsp.Ret); err != nil { return err } if c.Bool("json") { commands.PrintAsJSON(rsp) return nil } fmt.Printf("deleted volume: %s\n", rsp.VolumeID) return nil }, }
View Source
var VolumeGetCommand = cli.Command{ Name: "get", Aliases: []string{"info", "describe"}, Usage: "get a volume by id (omits token and private_data)", Flags: []cli.Flag{ cli.StringFlag{Name: "volume-id", Usage: "volume id to query"}, cli.BoolFlag{Name: "json", Usage: "print json (without sensitive fields)"}, }, Action: func(c *cli.Context) error { volumeID := resolveVolumeID(c) if volumeID == "" { return errors.New("volume id is required (positional arg or --volume-id)") } endpoint := "/cube/volume/" + url.PathEscape(volumeID) rsp := &volumeGetResponse{} if err := doVolumeReq(c, http.MethodGet, endpoint, uuid.NewString(), nil, rsp); err != nil { return err } if err := ensureVolumeSuccessRet(rsp.Ret); err != nil { return err } view := volumeGetViewResponse{Ret: rsp.Ret} if rsp.Volume != nil { v := toVolumeView(rsp.Volume) view.Volume = &v } if c.Bool("json") { commands.PrintAsJSON(view) return nil } if view.Volume == nil { return errors.New("empty volume in response") } printVolumeInfo(view.Volume) return nil }, }
View Source
var VolumeListCommand = cli.Command{ Name: "list", Aliases: []string{"ls"}, Usage: "list volumes (omits token and private_data)", Flags: []cli.Flag{ cli.BoolFlag{Name: "json", Usage: "print json (without sensitive fields)"}, }, Action: func(c *cli.Context) error { rsp := &volumeListResponse{} if err := doVolumeReq(c, http.MethodGet, "/cube/volume", uuid.NewString(), nil, rsp); err != nil { return err } if err := ensureVolumeSuccessRet(rsp.Ret); err != nil { return err } view := volumeListViewResponse{ Ret: rsp.Ret, Volumes: make([]volumeView, 0, len(rsp.Volumes)), } for i := range rsp.Volumes { view.Volumes = append(view.Volumes, toVolumeView(&rsp.Volumes[i])) } if c.Bool("json") { commands.PrintAsJSON(view) return nil } printVolumeList(view.Volumes) return nil }, }
Functions ¶
func GoWithWaitGroup ¶
func HandleCrash ¶
func HandleCrash(additionalHandlers ...func(panicError interface{}))
func WithRecover ¶
func WithRecover(handler func(), panicHandlers ...func(panicError interface{}))
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.