diff --git a/cli/cmd/create/create.go b/cli/cmd/create/create.go index 11bd6b744..ce661e16d 100644 --- a/cli/cmd/create/create.go +++ b/cli/cmd/create/create.go @@ -133,11 +133,13 @@ func (c *Create) ToService(args []string) (*riov1.Service, error) { spec.Options = stringers.ParseDNSOptions(c.DNSOption...) - cpus, err := stringers.ParseQuantity(c.Cpus) - if err != nil { - return nil, err + if c.Cpus != "" { + cpus, err := stringers.ParseQuantity(c.Cpus) + if err != nil { + return nil, err + } + spec.CPUs = &cpus } - spec.CPUs = &cpus service := riov1.NewService("", c.N_Name, riov1.Service{ ObjectMeta: metav1.ObjectMeta{ diff --git a/cli/cmd/edit/edit.go b/cli/cmd/edit/edit.go index 708d99833..f6461126d 100644 --- a/cli/cmd/edit/edit.go +++ b/cli/cmd/edit/edit.go @@ -5,13 +5,21 @@ import ( "encoding/json" "fmt" "os" + "strings" + + "github.com/rancher/wrangler/pkg/gvk" + name2 "github.com/rancher/wrangler/pkg/name" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "github.com/rancher/rio/cli/cmd/inspect" "github.com/rancher/rio/cli/pkg/clicontext" "github.com/rancher/rio/cli/pkg/lookup" clitypes "github.com/rancher/rio/cli/pkg/types" "github.com/sirupsen/logrus" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/kubernetes/pkg/kubectl/cmd/util/editor" "sigs.k8s.io/yaml" ) @@ -44,47 +52,100 @@ func (edit *Edit) Run(ctx *clicontext.CLIContext) error { return edit.edit(ctx) } -func (edit *Edit) edit(ctx *clicontext.CLIContext) error { - if len(ctx.CLI.Args()) != 1 { - return fmt.Errorf("exactly one Name (not name) arguement is required for raw edit") +func (edit *Edit) updateObject(ctx *clicontext.CLIContext, data map[string]interface{}, name string, types ...string) (map[string]interface{}, error) { + _, origObject, err := edit.getObject(ctx, true, name, types...) + if err != nil { + return nil, err } - var types []string - for _, t := range inspect.InspectTypes { - if t == clitypes.AppType { - continue - } - types = append(types, t) + origMdMap := getMdMap(origObject) + newMdMap := getMdMap(data) + + for _, key := range []string{"labels", "annotations"} { + origMdMap[key] = newMdMap[key] + } + + delete(data, "metadata") + for k, v := range data { + origObject[k] = v + } + + return origObject, nil +} + +func getMdMap(obj map[string]interface{}) map[string]interface{} { + md, ok := obj["metadata"] + if !ok { + return map[string]interface{}{} + } + + mdMap, ok := md.(map[string]interface{}) + if !ok { + return map[string]interface{}{} } + + return mdMap +} + +func (edit *Edit) getObject(ctx *clicontext.CLIContext, raw bool, name string, types ...string) (clitypes.Resource, map[string]interface{}, error) { r, err := lookup.Lookup(ctx, ctx.CLI.Args()[0], types...) if err != nil { - return err + return clitypes.Resource{}, nil, err } data, err := json.Marshal(r.Object) if err != nil { - return err + return clitypes.Resource{}, nil, err } var dataMap map[string]interface{} if err := json.Unmarshal(data, &dataMap); err != nil { - return err + return clitypes.Resource{}, nil, err + } + + if raw { + return r, dataMap, nil } - modifiedMap := make(map[string]interface{}) - if !edit.Raw { - newMeta := map[string]interface{}{} - if meta, ok := dataMap["metadata"].(map[string]interface{}); ok { - newMeta["labels"] = meta["labels"] - newMeta["annotations"] = meta["annotations"] - } - modifiedMap["metadata"] = newMeta - modifiedMap["spec"] = dataMap["spec"] - if dataMap["data"] != nil { - modifiedMap["data"] = dataMap["data"] + newMdMap := map[string]interface{}{} + mdMap := getMdMap(dataMap) + for _, key := range []string{"labels", "annotations"} { + if val, ok := mdMap[key]; ok { + newMdMap[key] = val } + } + if len(newMdMap) == 0 { + delete(dataMap, "metadata") } else { - modifiedMap = dataMap + dataMap["metadata"] = newMdMap + } + + delete(dataMap, "kind") + delete(dataMap, "apiVersion") + delete(dataMap, "status") + return r, dataMap, nil +} + +func (edit *Edit) edit(ctx *clicontext.CLIContext) error { + if len(ctx.CLI.Args()) != 1 { + return fmt.Errorf("exactly one Name (not name) arguement is required for raw edit") + } + + var ( + name = ctx.CLI.Args()[0] + types []string + ) + + for _, t := range inspect.InspectTypes { + if t == clitypes.AppType { + continue + } + types = append(types, t) + } + + r, modifiedMap, err := edit.getObject(ctx, edit.Raw, name, types...) + if err != nil { + return err } m, err := json.Marshal(modifiedMap) @@ -97,33 +158,39 @@ func (edit *Edit) edit(ctx *clicontext.CLIContext) error { } updated, err := Loop(nil, str, func(content []byte) error { - return ctx.UpdateResource(r, func(obj runtime.Object) error { - if !edit.Raw { - m := make(map[string]interface{}) - if err := yaml.Unmarshal(content, &m); err != nil { - return err - } - newMeta := dataMap["metadata"].(map[string]interface{}) - if meta, ok := m["metadata"].(map[string]interface{}); ok { - newMeta["labels"] = meta["labels"] - newMeta["annotations"] = meta["annotations"] - } - dataMap["spec"] = m["spec"] - if m["data"] != nil { - dataMap["data"] = m["data"] - } - - content, err = json.Marshal(dataMap) - if err != nil { - return err - } - } + m := make(map[string]interface{}) + if err := yaml.Unmarshal(content, &m); err != nil { + return err + } - if err := yaml.Unmarshal(content, &obj); err != nil { + if !edit.Raw { + m, err = edit.updateObject(ctx, m, name, types...) + if err != nil { return err } - return nil - }) + } + + obj := &unstructured.Unstructured{ + Object: m, + } + + gvk, err := gvk.Get(obj) + if err != nil { + return err + } + gvr := schema.GroupVersionResource{ + Group: gvk.Group, + Version: gvk.Version, + Resource: strings.ToLower(name2.GuessPluralName(gvk.Kind)), + } + + c, err := dynamic.NewForConfig(ctx.RestConfig) + if err != nil { + return err + } + + _, err = c.Resource(gvr).Namespace(obj.GetNamespace()).Update(obj, v1.UpdateOptions{}) + return err }) if err != nil { return err @@ -174,52 +241,3 @@ func Loop(prefix, input []byte, update updateFunc) (bool, error) { return true, nil } - -// -//func (edit *Edit) update(ctx *clicontext.CLIContext, format string, obj *types.Resource, self string, content []byte) error { -// if obj.Type == clitypes.NamespaceType { -// return up.Run(ctx, content, obj.Name, true, edit.Prompt, nil, "") -// } -// -// if obj.Type == clitypes.ConfigType { -// return config.RunUpdate(ctx, obj.Name, content, nil) -// } -// -// parsed, err := url.Parse(self) -// if err != nil { -// return err -// } -// -// q := parsed.Query() -// q.Set("_edited", "true") -// q.Set("_replace", "true") -// parsed.RawQuery = q.Encode() -// -// req, err := http.NewRequest(http.MethodPut, parsed.String(), bytes.NewReader(content)) -// if err != nil { -// return err -// } -// -// wc, err := ctx.ProjectClient() -// if err != nil { -// return err -// } -// -// wc.Ops.SetupRequest(req) -// req.Header.Set("Content-Type", format) -// -// resp, err := wc.Ops.Client.Do(req) -// if err != nil { -// return err -// } -// defer func() { -// io.Copy(ioutil.Discard, resp.Body) -// resp.Body.Close() -// }() -// -// if resp.StatusCode >= 300 { -// return clientbase.NewAPIError(resp, parsed.String()) -// } -// -// return nil -//} diff --git a/cli/cmd/inspect/inspect.go b/cli/cmd/inspect/inspect.go index 22a26b7c5..a07bdf075 100644 --- a/cli/cmd/inspect/inspect.go +++ b/cli/cmd/inspect/inspect.go @@ -13,8 +13,8 @@ import ( var ( InspectTypes = []string{ - clitypes.AppType, clitypes.ServiceType, + clitypes.AppType, clitypes.ConfigType, clitypes.NamespaceType, clitypes.RouterType, @@ -54,14 +54,8 @@ func (i *Inspect) Run(ctx *clicontext.CLIContext) error { for _, arg := range ctx.CLI.Args() { if strings.Contains(arg, ":") { types = []string{clitypes.ServiceType} - } else { - for i, t := range types { - if t == clitypes.ServiceType { - types = append(types[0:i], types[i+1:]...) - break - } - } } + r, err := lookup.Lookup(ctx, arg, types...) if err != nil { return err diff --git a/cli/cmd/install/install.go b/cli/cmd/install/install.go index d0a8e3761..4e9946619 100644 --- a/cli/cmd/install/install.go +++ b/cli/cmd/install/install.go @@ -200,7 +200,6 @@ func (i *Install) Run(ctx *clicontext.CLIContext) error { fmt.Println("ClusterDomain is reachable. Run `rio info` to get more info.") } - fmt.Printf("Please make sure all the system pods are actually running. Run `kubectl get po -n %s` to get more detail.\n", info.Status.SystemNamespace) fmt.Println("Controller logs are available from `rio systemlogs`") fmt.Println("") fmt.Println("Welcome to Rio!") diff --git a/cli/cmd/ps/ps_services.go b/cli/cmd/ps/ps_services.go index 75d2a617b..c06abc2cb 100644 --- a/cli/cmd/ps/ps_services.go +++ b/cli/cmd/ps/ps_services.go @@ -30,8 +30,9 @@ func (p *Ps) apps(ctx *clicontext.CLIContext) error { for _, v := range appObjs { app := v.(*riov1.App) appDatas[app.Namespace+"/"+app.Name] = tables.AppData{ - App: app, - Revisions: map[string]*riov1.Service{}, + ObjectMeta: app.ObjectMeta, + App: app, + Revisions: map[string]*riov1.Service{}, } } diff --git a/cli/cmd/revision/revision.go b/cli/cmd/revision/revision.go index 731976b3c..0b0a765e3 100644 --- a/cli/cmd/revision/revision.go +++ b/cli/cmd/revision/revision.go @@ -2,6 +2,7 @@ package revision import ( "fmt" + "sort" "github.com/rancher/rio/cli/pkg/clicontext" "github.com/rancher/rio/cli/pkg/stack" @@ -110,6 +111,10 @@ func Revisions(ctx *clicontext.CLIContext) error { } } + sort.Slice(output, func(i, j int) bool { + return output[i].Service.CreationTimestamp.After(output[j].Service.CreationTimestamp.Time) + }) + writer := tables.NewService(ctx) defer writer.TableWriter().Close() for _, obj := range output { diff --git a/cli/cmd/scale/scale.go b/cli/cmd/scale/scale.go index fa4bf2a68..bc8029cea 100644 --- a/cli/cmd/scale/scale.go +++ b/cli/cmd/scale/scale.go @@ -26,13 +26,20 @@ func (s *Scale) Run(ctx *clicontext.CLIContext) error { if strings.ContainsRune(scaleStr, '-') { min, max := kv.Split(scaleStr, "-") - minScale, _ := strconv.Atoi(min) - maxScale, _ := strconv.Atoi(max) + minScale, err := strconv.Atoi(min) + if err != nil { + return err + } + maxScale, err := strconv.Atoi(max) + if err != nil { + return err + } if service.Spec.AutoscaleConfig.Concurrency == nil { service.Spec.AutoscaleConfig.Concurrency = &[]int{10}[0] } service.Spec.AutoscaleConfig.MinScale = &minScale service.Spec.AutoscaleConfig.MaxScale = &maxScale + service.Spec.Scale = 0 } else { scale, err := strconv.Atoi(scaleStr) if err != nil { diff --git a/cli/pkg/tables/apps.go b/cli/pkg/tables/apps.go index 13a2dc46b..9927dd7d8 100644 --- a/cli/pkg/tables/apps.go +++ b/cli/pkg/tables/apps.go @@ -40,8 +40,8 @@ func NewApp(cfg Config) TableWriter { {"Name", "{{stackScopedName .Obj.App.Namespace .Obj.App.Name ``}}"}, {"CREATED", "{{.Obj.App.CreationTimestamp | ago}}"}, {"ENDPOINT", "{{.Obj.App.Status.Endpoints | array}}"}, - {"REVISIONS", "{{revisions .Obj.App.Spec.Revisions}}"}, - {"SCALE", "{{appScale .Obj.App.Spec.Revisions}}"}, + {"REVISIONS", "{{revisions .Obj}}"}, + {"SCALE", "{{appScale .Obj}}"}, {"WEIGHT", "{{weightVersioned .Obj.App}}"}, {"DETAIL", "{{detail .Obj}}"}, }, cfg) @@ -57,10 +57,10 @@ func NewApp(cfg Config) TableWriter { } } -func revisions(obj interface{}) (result []string) { - revs := obj.([]riov1.Revision) - for _, rev := range revs { - if rev.AdjustedWeight == 0 && rev.Scale == 0 { +func revisions(app *riov1.App) (result []string) { + for _, rev := range app.Spec.Revisions { + revStatus := app.Status.RevisionWeight[rev.Version] + if revStatus.Weight == 0 { continue } result = append(result, rev.Version) @@ -69,25 +69,25 @@ func revisions(obj interface{}) (result []string) { return } -func revisionsByVersion(obj interface{}) map[string]riov1.Revision { +func revisionsByVersion(app *riov1.App) map[string]riov1.Revision { result := map[string]riov1.Revision{} - for _, rev := range obj.([]riov1.Revision) { + for _, rev := range app.Spec.Revisions { result[rev.Version] = rev } return result } func formatRevisions(obj interface{}) (string, error) { - revs := revisions(obj) + revs := revisions(obj.(*AppData).App) return strings.Join(revs, ","), nil } func formatAppScale(obj interface{}) (string, error) { var ( - revMap = revisionsByVersion(obj) + revMap = revisionsByVersion(obj.(*AppData).App) result []string ) - for _, version := range revisions(obj) { + for _, version := range revisions(obj.(*AppData).App) { rev := revMap[version] scale, err := FormatScale(rev.Scale, rev.ScaleStatus, nil) if err != nil { @@ -104,7 +104,7 @@ func formatWeightGraph(obj interface{}) (string, error) { result []string ) - for _, version := range revisions(app.Spec.Revisions) { + for _, version := range revisions(app) { weight := app.Status.RevisionWeight[version].Weight ret := fmt.Sprintf("%v%%", weight) result = append(result, ret) @@ -130,8 +130,8 @@ func formatAppDetail(obj interface{}) (string, error) { appData := obj.(*AppData) buffer := strings.Builder{} - versions := revisionsByVersion(appData.App.Spec.Revisions) - for _, name := range revisions(appData.App.Spec.Revisions) { + versions := revisionsByVersion(appData.App) + for _, name := range revisions(appData.App) { svc, ok := appData.Revisions[name] if !ok { continue diff --git a/cli/pkg/tables/builds.go b/cli/pkg/tables/builds.go index 4c8f19294..c1a252573 100644 --- a/cli/pkg/tables/builds.go +++ b/cli/pkg/tables/builds.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/knative/build/pkg/apis/build/v1alpha1" - "github.com/rancher/rio/cli/pkg/table" ) diff --git a/cli/pkg/tables/types.go b/cli/pkg/tables/types.go index d3c1c9137..498e64da4 100644 --- a/cli/pkg/tables/types.go +++ b/cli/pkg/tables/types.go @@ -36,7 +36,8 @@ func (t *tableWriter) Write(objs []runtime.Object) (err error) { sort.Slice(objs, func(i, j int) bool { leftMeta, _ := meta.Accessor(objs[i]) rightMeta, _ := meta.Accessor(objs[j]) - return leftMeta.GetNamespace()+"/"+leftMeta.GetName() < rightMeta.GetNamespace()+"/"+rightMeta.GetName() + leftCreated := leftMeta.GetCreationTimestamp() + return leftCreated.After(rightMeta.GetCreationTimestamp().Time) }) defer func() {