diff --git a/cli/cmd/delete.go b/cli/cmd/delete.go index 80c40ff7c7..89ee3669d1 100644 --- a/cli/cmd/delete.go +++ b/cli/cmd/delete.go @@ -21,6 +21,7 @@ import ( "github.com/spf13/cobra" + "github.com/cortexlabs/cortex/pkg/lib/console" "github.com/cortexlabs/cortex/pkg/lib/errors" "github.com/cortexlabs/cortex/pkg/lib/json" s "github.com/cortexlabs/cortex/pkg/lib/strings" @@ -65,6 +66,6 @@ var deleteCmd = &cobra.Command{ if err != nil { errors.Exit(err, "/delete", "response", string(httpResponse)) } - fmt.Println(deleteResponse.Message) + fmt.Println("\n" + console.Bold(deleteResponse.Message)) }, } diff --git a/cli/cmd/deploy.go b/cli/cmd/deploy.go index 53351dcb99..ce4ae151df 100644 --- a/cli/cmd/deploy.go +++ b/cli/cmd/deploy.go @@ -21,6 +21,7 @@ import ( "github.com/spf13/cobra" + "github.com/cortexlabs/cortex/pkg/lib/console" "github.com/cortexlabs/cortex/pkg/lib/errors" "github.com/cortexlabs/cortex/pkg/lib/json" s "github.com/cortexlabs/cortex/pkg/lib/strings" @@ -77,5 +78,5 @@ func deploy(force bool, ignoreCache bool) { errors.Exit(err, "/deploy", "response", string(response)) } - fmt.Println(deployResponse.Message) + fmt.Println("\n" + console.Bold(deployResponse.Message)) } diff --git a/cli/cmd/get.go b/cli/cmd/get.go index 94d5efd794..347aaee651 100644 --- a/cli/cmd/get.go +++ b/cli/cmd/get.go @@ -26,11 +26,11 @@ import ( "github.com/cortexlabs/yaml" "github.com/spf13/cobra" + "github.com/cortexlabs/cortex/pkg/lib/console" "github.com/cortexlabs/cortex/pkg/lib/errors" "github.com/cortexlabs/cortex/pkg/lib/json" "github.com/cortexlabs/cortex/pkg/lib/msgpack" "github.com/cortexlabs/cortex/pkg/lib/sets/strset" - "github.com/cortexlabs/cortex/pkg/lib/slices" s "github.com/cortexlabs/cortex/pkg/lib/strings" "github.com/cortexlabs/cortex/pkg/lib/table" libtime "github.com/cortexlabs/cortex/pkg/lib/time" @@ -126,7 +126,7 @@ func getDeploymentsResponse() (string, error) { } if len(resourcesRes.Deployments) == 0 { - return "No deployments found", nil + return "no deployments found", nil } rows := make([][]interface{}, len(resourcesRes.Deployments)) @@ -140,9 +140,9 @@ func getDeploymentsResponse() (string, error) { t := table.Table{ Headers: []table.Header{ - {Title: "NAME", MaxWidth: 32}, - {Title: "STATUS", MaxWidth: 21}, - {Title: "LAST UPDATE"}, + {Title: "name", MaxWidth: 32}, + {Title: "status", MaxWidth: 21}, + {Title: "last updated"}, }, Rows: rows, } @@ -198,19 +198,19 @@ func resourceByNameStr(resourceName string, resourcesRes *schema.GetResourcesRes func resourcesByTypeStr(resourceType resource.Type, resourcesRes *schema.GetResourcesResponse) (string, error) { switch resourceType { case resource.PythonPackageType: - return pythonPackagesStr(resourcesRes.DataStatuses, resourcesRes.Context, false), nil + return pythonPackagesStr(resourcesRes.DataStatuses, resourcesRes.Context), nil case resource.RawColumnType: - return rawColumnsStr(resourcesRes.DataStatuses, resourcesRes.Context, false), nil + return rawColumnsStr(resourcesRes.DataStatuses, resourcesRes.Context), nil case resource.AggregateType: - return aggregatesStr(resourcesRes.DataStatuses, resourcesRes.Context, false), nil + return aggregatesStr(resourcesRes.DataStatuses, resourcesRes.Context), nil case resource.TransformedColumnType: - return transformedColumnsStr(resourcesRes.DataStatuses, resourcesRes.Context, false), nil + return transformedColumnsStr(resourcesRes.DataStatuses, resourcesRes.Context), nil case resource.TrainingDatasetType: - return trainingDataStr(resourcesRes.DataStatuses, resourcesRes.Context, false), nil + return trainingDataStr(resourcesRes.DataStatuses, resourcesRes.Context), nil case resource.ModelType: - return modelsStr(resourcesRes.DataStatuses, resourcesRes.Context, false), nil + return modelsStr(resourcesRes.DataStatuses, resourcesRes.Context), nil case resource.APIType: - return apisStr(resourcesRes.APIGroupStatuses, false), nil + return apisStr(resourcesRes.APIGroupStatuses), nil default: return "", resource.ErrorInvalidType(resourceType.String()) } @@ -240,27 +240,18 @@ func resourceByNameAndTypeStr(resourceName string, resourceType resource.Type, r func allResourcesStr(resourcesRes *schema.GetResourcesResponse) string { ctx := resourcesRes.Context - showTitle := false - if slices.AreNGreaterThanZero(2, - len(ctx.PythonPackages), len(ctx.RawColumns), len(ctx.Aggregates), - len(ctx.TransformedColumns), len(ctx.Models), len(ctx.Models), // Models occurs twice because of training datasets - len(resourcesRes.APIGroupStatuses), - ) { - showTitle = true - } - out := "" - out += pythonPackagesStr(resourcesRes.DataStatuses, ctx, showTitle) - out += rawColumnsStr(resourcesRes.DataStatuses, ctx, showTitle) - out += aggregatesStr(resourcesRes.DataStatuses, ctx, showTitle) - out += transformedColumnsStr(resourcesRes.DataStatuses, ctx, showTitle) - out += trainingDataStr(resourcesRes.DataStatuses, ctx, showTitle) - out += modelsStr(resourcesRes.DataStatuses, ctx, showTitle) - out += apisStr(resourcesRes.APIGroupStatuses, showTitle) + out += pythonPackagesStr(resourcesRes.DataStatuses, ctx) + out += rawColumnsStr(resourcesRes.DataStatuses, ctx) + out += aggregatesStr(resourcesRes.DataStatuses, ctx) + out += transformedColumnsStr(resourcesRes.DataStatuses, ctx) + out += trainingDataStr(resourcesRes.DataStatuses, ctx) + out += modelsStr(resourcesRes.DataStatuses, ctx) + out += apisStr(resourcesRes.APIGroupStatuses) return out } -func pythonPackagesStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Context, showTitle bool) string { +func pythonPackagesStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Context) string { if len(ctx.PythonPackages) == 0 { return "" } @@ -270,14 +261,10 @@ func pythonPackagesStr(dataStatuses map[string]*resource.DataStatus, ctx *contex resources = append(resources, pythonPackage) } - title := "\n" - if showTitle { - title = titleStr("Python Packages") - } - return title + dataResourceTable(resources, dataStatuses) + "\n" + return "\n" + dataResourceTable(resources, dataStatuses, resource.PythonPackageType) + "\n" } -func rawColumnsStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Context, showTitle bool) string { +func rawColumnsStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Context) string { if len(ctx.RawColumns) == 0 { return "" } @@ -287,14 +274,10 @@ func rawColumnsStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Co resources = append(resources, rawColumn) } - title := "\n" - if showTitle { - title = titleStr("Raw Columns") - } - return title + dataResourceTable(resources, dataStatuses) + "\n" + return "\n" + dataResourceTable(resources, dataStatuses, resource.RawColumnType) + "\n" } -func aggregatesStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Context, showTitle bool) string { +func aggregatesStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Context) string { if len(ctx.Aggregates) == 0 { return "" } @@ -304,14 +287,10 @@ func aggregatesStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Co resources = append(resources, aggregate) } - title := "\n" - if showTitle { - title = titleStr("Aggregates") - } - return title + dataResourceTable(resources, dataStatuses) + "\n" + return "\n" + dataResourceTable(resources, dataStatuses, resource.AggregateType) + "\n" } -func transformedColumnsStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Context, showTitle bool) string { +func transformedColumnsStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Context) string { if len(ctx.TransformedColumns) == 0 { return "" } @@ -321,14 +300,10 @@ func transformedColumnsStr(dataStatuses map[string]*resource.DataStatus, ctx *co resources = append(resources, transformedColumn) } - title := "\n" - if showTitle { - title = titleStr("Transformed Columns") - } - return title + dataResourceTable(resources, dataStatuses) + "\n" + return "\n" + dataResourceTable(resources, dataStatuses, resource.TransformedColumnType) + "\n" } -func trainingDataStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Context, showTitle bool) string { +func trainingDataStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Context) string { if len(ctx.Models) == 0 { return "" } @@ -338,14 +313,10 @@ func trainingDataStr(dataStatuses map[string]*resource.DataStatus, ctx *context. resources = append(resources, model.Dataset) } - title := "\n" - if showTitle { - title = titleStr("Training Datasets") - } - return title + dataResourceTable(resources, dataStatuses) + "\n" + return "\n" + dataResourceTable(resources, dataStatuses, resource.TrainingDatasetType) + "\n" } -func modelsStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Context, showTitle bool) string { +func modelsStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Context) string { if len(ctx.Models) == 0 { return "" } @@ -355,23 +326,15 @@ func modelsStr(dataStatuses map[string]*resource.DataStatus, ctx *context.Contex resources = append(resources, model) } - title := "\n" - if showTitle { - title = titleStr("Models") - } - return title + dataResourceTable(resources, dataStatuses) + "\n" + return "\n" + dataResourceTable(resources, dataStatuses, resource.ModelType) + "\n" } -func apisStr(apiGroupStatuses map[string]*resource.APIGroupStatus, showTitle bool) string { +func apisStr(apiGroupStatuses map[string]*resource.APIGroupStatus) string { if len(apiGroupStatuses) == 0 { return "" } - title := "\n" - if showTitle { - title = titleStr("APIs") - } - return title + apiResourceTable(apiGroupStatuses) + return "\n" + apiResourceTable(apiGroupStatuses) } func describePythonPackage(name string, resourcesRes *schema.GetResourcesResponse) (string, error) { @@ -390,7 +353,7 @@ func describeRawColumn(name string, resourcesRes *schema.GetResourcesResponse) ( } dataStatus := resourcesRes.DataStatuses[rawColumn.GetID()] out := dataStatusSummary(dataStatus) - out += titleStr("Configuration") + rawColumn.UserConfigStr() + out += "\n" + titleStr("configuration") + rawColumn.UserConfigStr() return out, nil } @@ -422,7 +385,7 @@ func describeAggregate(name string, resourcesRes *schema.GetResourcesResponse) ( out += valueStr(obj) } - out += titleStr("Configuration") + aggregate.UserConfigStr() + out += "\n" + titleStr("configuration") + aggregate.UserConfigStr() return out, nil } @@ -433,7 +396,7 @@ func describeTransformedColumn(name string, resourcesRes *schema.GetResourcesRes } dataStatus := resourcesRes.DataStatuses[transformedColumn.ID] out := dataStatusSummary(dataStatus) - out += titleStr("Configuration") + transformedColumn.UserConfigStr() + out += "\n" + titleStr("configuration") + transformedColumn.UserConfigStr() return out, nil } @@ -453,7 +416,7 @@ func describeModel(name string, resourcesRes *schema.GetResourcesResponse) (stri } dataStatus := resourcesRes.DataStatuses[model.ID] out := dataStatusSummary(dataStatus) - out += titleStr("Configuration") + model.UserConfigStr() + out += "\n" + titleStr("configuration") + model.UserConfigStr() return out, nil } @@ -478,42 +441,44 @@ func describeAPI(name string, resourcesRes *schema.GetResourcesResponse, flagVer updatedAt = groupStatus.ActiveStatus.Start } - var staleComputeStr = "" - if groupStatus.ReadyStaleCompute != 0 { - hasStr := "has" - if groupStatus.ReadyStaleCompute > 1 { - hasStr = "have" - } - staleComputeStr = fmt.Sprintf(" (%s %s previous compute)", s.Int32(groupStatus.ReadyStaleCompute), hasStr) + row := []interface{}{ + groupStatus.Message(), + s.Int32(groupStatus.Requested), + s.Int32(groupStatus.Available()), + s.Int32(groupStatus.ReadyUpdated), + s.Int32(groupStatus.ReadyStaleCompute), + s.Int32(groupStatus.ReadyStaleModel), + s.Int32(groupStatus.FailedUpdated), + } + + headers := []table.Header{ + {Title: "status"}, + {Title: "requested"}, + {Title: "available"}, + {Title: "up-to-date"}, + {Title: "stale compute", Hidden: groupStatus.ReadyStaleCompute == 0}, + {Title: "stale model", Hidden: groupStatus.ReadyStaleModel == 0}, + {Title: "failed", Hidden: groupStatus.FailedUpdated == 0}, } apiEndpoint := urls.Join(resourcesRes.APIsBaseURL, anyAPIStatus.Path) - out := "\nURL: " + apiEndpoint + "\n" - out += fmt.Sprintf("cURL: curl -k -X POST -H \"Content-Type: application/json\" %s -d @samples.json\n", apiEndpoint) - out += "\n" - out += fmt.Sprintf("Status: %s\n", groupStatus.Message()) - out += fmt.Sprintf("Available replicas: %s\n", s.Int32(groupStatus.Available())) - out += fmt.Sprintf(" - Current model: %s%s\n", s.Int32(groupStatus.UpToDate()), staleComputeStr) - out += fmt.Sprintf(" - Previous model: %s\n", s.Int32(groupStatus.ReadyStaleModel)) - out += fmt.Sprintf("Requested replicas: %s\n", s.Int32(groupStatus.Requested)) - out += fmt.Sprintf("Failed replicas: %s\n", s.Int32(groupStatus.FailedUpdated)) + out := "\n" + console.Bold("url: ") + apiEndpoint + "\n" + out += fmt.Sprintf("%s curl -k -X POST -H \"Content-Type: application/json\" %s -d @samples.json\n\n", console.Bold("curl:"), apiEndpoint) + out += fmt.Sprintf(console.Bold("updated at:")+" %s\n", libtime.LocalTimestamp(updatedAt)) + out += "\n" - out += fmt.Sprintf("Updated at: %s", libtime.LocalTimestamp(updatedAt)) + t := table.Table{ + Headers: headers, + Rows: [][]interface{}{row}, + } + out += table.MustFormat(t) if !flagVerbose { return out, nil } - out += "\n" - - if api != nil { - out += titleStr("Configuration") + api.UserConfigStr() - } - - out += titleStr("Model Input Signature") - - out += describeModelInput(groupStatus, apiEndpoint) + out += "\n\n" + describeModelInput(groupStatus, apiEndpoint) if modelName, ok := yaml.ExtractAtSymbolText(api.Model); ok { model := ctx.Models[modelName] @@ -532,7 +497,11 @@ func describeAPI(name string, resourcesRes *schema.GetResourcesResponse, flagVer } sort.Strings(samplePlaceholderFields) samplesPlaceholderStr := `{ "samples": [ { ` + strings.Join(samplePlaceholderFields, ", ") + " } ] }" - out += "\n\nPayload: " + samplesPlaceholderStr + out += "\n\n" + console.Bold("payload: ") + samplesPlaceholderStr + } + + if api != nil { + out += "\n" + titleStr("configuration") + api.UserConfigStr() } return out, nil @@ -540,12 +509,12 @@ func describeAPI(name string, resourcesRes *schema.GetResourcesResponse, flagVer func describeModelInput(groupStatus *resource.APIGroupStatus, apiEndpoint string) string { if groupStatus.Available() == 0 { - return "Waiting for API to be ready" + return "waiting for api to be ready" } modelInput, err := getModelInput(urls.Join(apiEndpoint, "signature")) if err != nil { - return "Waiting for API to be ready" + return "waiting for api to be ready" } rows := make([][]interface{}, len(modelInput.Signature)) @@ -569,9 +538,9 @@ func describeModelInput(groupStatus *resource.APIGroupStatus, apiEndpoint string t := table.Table{ Headers: []table.Header{ - {Title: "FEATURE", MaxWidth: 32}, - {Title: "TYPE", MaxWidth: 10}, - {Title: "SHAPE", MaxWidth: 20}, + {Title: "model input", MaxWidth: 32}, + {Title: "type", MaxWidth: 10}, + {Title: "shape", MaxWidth: 20}, }, Rows: rows, } @@ -600,15 +569,26 @@ func getModelInput(infoAPIPath string) (*schema.ModelInput, error) { } func dataStatusSummary(dataStatus *resource.DataStatus) string { - out := titleStr("Summary") - out += "Status: " + dataStatus.Message() + "\n" - out += "Workload started at: " + libtime.LocalTimestamp(dataStatus.Start) + "\n" - out += "Workload ended at: " + libtime.LocalTimestamp(dataStatus.End) + "\n" - return out + headers := []table.Header{ + {Title: "status"}, + {Title: "start"}, + {Title: "end"}, + } + row := []interface{}{ + dataStatus.Message(), + libtime.LocalTimestamp(dataStatus.Start), + libtime.LocalTimestamp(dataStatus.End), + } + + t := table.Table{ + Headers: headers, + Rows: [][]interface{}{row}, + } + return "\n" + table.MustFormat(t) } func valueStr(value interface{}) string { - return titleStr("Value") + s.Obj(value) + "\n" + return titleStr("value") + s.Obj(value) + "\n" } func strMapToStr(strings map[string]string) string { @@ -627,7 +607,7 @@ func strMapToStr(strings map[string]string) string { return out } -func dataResourceTable(resources []context.Resource, dataStatuses map[string]*resource.DataStatus) string { +func dataResourceTable(resources []context.Resource, dataStatuses map[string]*resource.DataStatus, resourceType resource.Type) string { rows := make([][]interface{}, len(resources)) for rowNum, res := range resources { dataStatus := dataStatuses[res.GetID()] @@ -638,11 +618,16 @@ func dataResourceTable(resources []context.Resource, dataStatuses map[string]*re } } + title := resourceType.UserFacing() + if resourceType == resource.PythonPackageType { + title = resourceType.UserFacingPlural() + } + t := table.Table{ Headers: []table.Header{ - {Title: "NAME", MaxWidth: 32}, - {Title: "STATUS", MaxWidth: 21}, - {Title: "AGE"}, + {Title: title, MaxWidth: 32}, + {Title: "status", MaxWidth: 21}, + {Title: "age"}, }, Rows: rows, } @@ -652,6 +637,8 @@ func dataResourceTable(resources []context.Resource, dataStatuses map[string]*re func apiResourceTable(apiGroupStatuses map[string]*resource.APIGroupStatus) string { rows := make([][]interface{}, 0, len(apiGroupStatuses)) + + totalFailed := 0 for name, groupStatus := range apiGroupStatuses { if groupStatus.Requested == 0 { continue @@ -665,21 +652,23 @@ func apiResourceTable(apiGroupStatuses map[string]*resource.APIGroupStatus) stri rows = append(rows, []interface{}{ name, groupStatus.Available(), - groupStatus.UpToDate(), + groupStatus.ReadyUpdated, groupStatus.Requested, groupStatus.FailedUpdated, libtime.Since(updatedAt), }) + + totalFailed += int(groupStatus.FailedUpdated) } t := table.Table{ Headers: []table.Header{ - {Title: "NAME"}, - {Title: "AVAILABLE"}, - {Title: "UP-TO-DATE"}, - {Title: "REQUESTED"}, - {Title: "FAILED"}, - {Title: "LAST UPDATE"}, + {Title: resource.APIType.UserFacing()}, + {Title: "available"}, + {Title: "up-to-date"}, + {Title: "requested"}, + {Title: "failed", Hidden: totalFailed == 0}, + {Title: "last update"}, }, Rows: rows, } @@ -688,10 +677,7 @@ func apiResourceTable(apiGroupStatuses map[string]*resource.APIGroupStatus) stri } func titleStr(title string) string { - titleLength := len(title) - top := strings.Repeat("-", titleLength) - bottom := strings.Repeat("-", titleLength) - return "\n" + top + "\n" + title + "\n" + bottom + "\n\n" + return "\n" + console.Bold(title) + "\n" } func resourceStatusesStr(resourcesRes *schema.GetResourcesResponse) string { @@ -699,37 +685,37 @@ func resourceStatusesStr(resourcesRes *schema.GetResourcesResponse) string { var titles, values []string if len(ctx.PythonPackages) != 0 { - titles = append(titles, "Python Packages") + titles = append(titles, resource.PythonPackageType.UserFacingPlural()) values = append(values, pythonPackageStatusesStr(resourcesRes.DataStatuses, resourcesRes.Context)) } if len(ctx.RawColumns) != 0 { - titles = append(titles, "Raw Columns") + titles = append(titles, resource.RawColumnType.UserFacingPlural()) values = append(values, rawColumnStatusesStr(resourcesRes.DataStatuses, resourcesRes.Context)) } if len(ctx.Aggregates) != 0 { - titles = append(titles, "Aggregates") + titles = append(titles, resource.AggregateType.UserFacingPlural()) values = append(values, aggregateStatusesStr(resourcesRes.DataStatuses, resourcesRes.Context)) } if len(ctx.TransformedColumns) != 0 { - titles = append(titles, "Transformed Columns") + titles = append(titles, resource.TransformedColumnType.UserFacingPlural()) values = append(values, transformedColumnStatusesStr(resourcesRes.DataStatuses, resourcesRes.Context)) } if len(ctx.Models) != 0 { - titles = append(titles, "Training Datasets") + titles = append(titles, resource.TrainingDatasetType.UserFacingPlural()) values = append(values, trainingDatasetStatusesStr(resourcesRes.DataStatuses, resourcesRes.Context)) } if len(ctx.Models) != 0 { - titles = append(titles, "Models") + titles = append(titles, resource.ModelType.UserFacingPlural()) values = append(values, modelStatusesStr(resourcesRes.DataStatuses, resourcesRes.Context)) } if len(resourcesRes.APIGroupStatuses) != 0 { - titles = append(titles, "APIs") + titles = append(titles, resource.APIType.UserFacingPlural()) values = append(values, apiStatusesStr(resourcesRes.APIGroupStatuses)) } @@ -739,7 +725,10 @@ func resourceStatusesStr(resourcesRes *schema.GetResourcesResponse) string { for i, title := range titles { paddingWidth := maxTitleLen - len(title) + 3 padding := strings.Repeat(" ", paddingWidth) - out += title + ":" + padding + values[i] + "\n" + out += title + ":" + padding + values[i] + if i != len(titles)-1 { + out += "\n" + } } return out diff --git a/go.mod b/go.mod index 902fd7445a..2cdec0955a 100644 --- a/go.mod +++ b/go.mod @@ -21,9 +21,11 @@ require ( github.com/aws/aws-sdk-go v1.20.20 github.com/cortexlabs/yaml v0.0.0-20190626164117-202ab3a3d475 github.com/davecgh/go-spew v1.1.1 + github.com/fatih/color v1.7.0 github.com/gorilla/mux v1.7.3 github.com/gorilla/websocket v1.4.0 github.com/imdario/mergo v0.3.7 // indirect + github.com/mattn/go-colorable v0.1.2 // indirect github.com/mitchellh/go-homedir v1.1.0 github.com/pkg/errors v0.8.1 github.com/spf13/cobra v0.0.5 diff --git a/go.sum b/go.sum index 36fb0e3dc2..70796a6886 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,8 @@ github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11 github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415 h1:WSBJMqJbLxsn+bTCPyPYZfqHdJmc8MK4wrBjMft6BAM= github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -53,6 +55,10 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE= github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -110,6 +116,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FY golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/pkg/lib/console/format.go b/pkg/lib/console/format.go new file mode 100644 index 0000000000..cba2d8f56e --- /dev/null +++ b/pkg/lib/console/format.go @@ -0,0 +1,29 @@ +/* +Copyright 2019 Cortex Labs, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package console + +import ( + "github.com/fatih/color" +) + +var ( + bold = color.New(color.Bold).SprintFunc() +) + +func Bold(a ...interface{}) string { + return bold(a...) +} diff --git a/pkg/lib/table/table.go b/pkg/lib/table/table.go index a3e6f19331..82a307876d 100644 --- a/pkg/lib/table/table.go +++ b/pkg/lib/table/table.go @@ -21,6 +21,7 @@ import ( "sort" "strings" + "github.com/cortexlabs/cortex/pkg/lib/console" "github.com/cortexlabs/cortex/pkg/lib/errors" "github.com/cortexlabs/cortex/pkg/lib/slices" s "github.com/cortexlabs/cortex/pkg/lib/strings" @@ -36,6 +37,7 @@ type Header struct { Title string MaxWidth int // Max width of the text (not including spacing). Items that are longer will be truncated to less than MaxWidth to fit the elipses. If 0 is provided, it defaults to no max. MinWidth int // Min width of the text (not including spacing) + Hidden bool } func validate(t Table) error { @@ -116,24 +118,30 @@ func Format(t Table) (string, error) { var headerStr string for colNum, header := range t.Headers { - headerStr += header.Title + if header.Hidden { + continue + } + headerStr += console.Bold(header.Title) if colNum != lastColIndex { headerStr += strings.Repeat(" ", maxColWidths[colNum]+t.Spacing-len(header.Title)) } } - elipses := "..." + ellipses := "..." rowStrs := make([]string, len(rows)) for rowNum, row := range rows { var rowStr string for colNum, val := range row { + if t.Headers[colNum].Hidden { + continue + } if len(val) > maxColWidths[colNum] { val = val[0:maxColWidths[colNum]] - // Ensure at least one space after elipses - for len(val)+len(elipses) > maxColWidths[colNum]+t.Spacing-1 { + // Ensure at least one space after ellipses + for len(val)+len(ellipses) > maxColWidths[colNum]+t.Spacing-1 { val = val[0 : len(val)-1] } - val += elipses + val += ellipses } rowStr += val if colNum != lastColIndex { diff --git a/pkg/operator/api/resource/status.go b/pkg/operator/api/resource/status.go index 3725eb8b57..bea3d8cb2d 100644 --- a/pkg/operator/api/resource/status.go +++ b/pkg/operator/api/resource/status.go @@ -76,11 +76,6 @@ func (grc *GroupedReplicaCounts) Available() int32 { return grc.ReadyUpdated + grc.ReadyStaleModel + grc.ReadyStaleCompute } -// Number of replicas with the up-to-date model (includes stale compute) -func (grc *GroupedReplicaCounts) UpToDate() int32 { - return grc.ReadyUpdated + grc.ReadyStaleCompute -} - func (status *DataStatus) GetCode() StatusCode { return status.Code } diff --git a/pkg/operator/api/resource/type.go b/pkg/operator/api/resource/type.go index aa509e9cc8..1d3e2a2d1b 100644 --- a/pkg/operator/api/resource/type.go +++ b/pkg/operator/api/resource/type.go @@ -81,6 +81,44 @@ var ( "python_packages", } + userFacing = []string{ + "unknown", + "deployment", + "raw column", + "transformed column", + "aggregate", + "api", + "model", + "environment", + "aggregator", + "transformer", + "estimator", + "template", + "embed", + "training dataset", + "constant", + "python package", + } + + userFacingPlural = []string{ + "unknowns", + "deployments", + "raw columns", + "transformed columns", + "aggregates", + "apis", + "models", + "environments", + "aggregators", + "transformers", + "estimators", + "templates", + "embeds", + "training datasets", + "constants", + "python packages", + } + typeAcronyms = map[string]Type{ "rc": RawColumnType, "rcs": RawColumnType, @@ -170,6 +208,14 @@ func (t Type) Plural() string { return typePlurals[t] } +func (t Type) UserFacing() string { + return userFacing[t] +} + +func (t Type) UserFacingPlural() string { + return userFacingPlural[t] +} + // MarshalText satisfies TextMarshaler func (t Type) MarshalText() ([]byte, error) { return []byte(t.String()), nil