From 24b04c583803400f1b9fb5d1b0fa64c681119460 Mon Sep 17 00:00:00 2001 From: Elijah Oyekunle Date: Thu, 29 Aug 2019 11:33:57 +0100 Subject: [PATCH] Support 'Scale' subresource for CRDs (#4195) * support scale subresource for crds * add omitempty for 'scalable' field * trigger build * make scalable property optional * refactor scalable method * move scalableResources implementation to backend * update backend tests with the 'scalable' property --- i18n/messages.fr.xlf | 22 +++++--- i18n/messages.ja.xlf | 22 +++++--- i18n/messages.ko.xlf | 23 +++++--- i18n/messages.xlf | 21 +++++--- i18n/messages.zh.xlf | 22 +++++--- src/app/backend/api/types.go | 23 +++++++- src/app/backend/handler/apihandler.go | 10 +++- .../customresourcedefinition/detail.go | 18 +++++-- .../customresourcedefinition/objects.go | 19 +++---- .../resource/deployment/detail_test.go | 5 +- .../backend/resource/deployment/list_test.go | 5 +- .../resource/replicaset/common_test.go | 2 +- .../resource/replicaset/detail_test.go | 6 +-- .../backend/resource/replicaset/list_test.go | 6 +-- .../replicationcontroller/list_test.go | 15 ++++-- .../backend/resource/statefulset/list_test.go | 5 +- src/app/backend/scaling/scale.go | 19 +++++-- .../actionbars/scaledefault/component.ts | 4 ++ .../actionbars/scaledefault/template.html | 2 +- .../components/list/column/menu/component.ts | 9 +--- .../resourcelist/crdobject/template.html | 4 +- .../common/dialogs/scaleresource/dialog.ts | 6 ++- .../frontend/common/services/global/verber.ts | 6 ++- src/app/frontend/crd/detail/template.html | 5 ++ src/app/frontend/crd/routing.ts | 54 +++++++++++++++---- src/app/frontend/typings/backendapi.d.ts | 2 + 26 files changed, 246 insertions(+), 89 deletions(-) diff --git a/i18n/messages.fr.xlf b/i18n/messages.fr.xlf index 59c816824..e1a580496 100644 --- a/i18n/messages.fr.xlf +++ b/i18n/messages.fr.xlf @@ -1879,7 +1879,7 @@ ../src/app/frontend/crd/detail/template.html - 62 + 67 @@ -2470,12 +2470,20 @@ 33 + + Subresources + Subresources + + ../src/app/frontend/crd/detail/template.html + 43 + + Accepted Names Accepted Names ../src/app/frontend/crd/detail/template.html - 46 + 51 @@ -2483,7 +2491,7 @@ Plural ../src/app/frontend/crd/detail/template.html - 52 + 57 @@ -2491,7 +2499,7 @@ Singular ../src/app/frontend/crd/detail/template.html - 57 + 62 @@ -2499,7 +2507,7 @@ List Kind ../src/app/frontend/crd/detail/template.html - 67 + 72 @@ -2507,7 +2515,7 @@ Short Names ../src/app/frontend/crd/detail/template.html - 72 + 77 @@ -2515,7 +2523,7 @@ Categories ../src/app/frontend/crd/detail/template.html - 77 + 82 diff --git a/i18n/messages.ja.xlf b/i18n/messages.ja.xlf index 58c3d6b10..37d824bea 100644 --- a/i18n/messages.ja.xlf +++ b/i18n/messages.ja.xlf @@ -1291,7 +1291,7 @@ ../src/app/frontend/crd/detail/template.html - 62 + 67 @@ -2869,12 +2869,20 @@ 33 + + Subresources + Subresources + + ../src/app/frontend/crd/detail/template.html + 43 + + Accepted Names 容認された名前 ../src/app/frontend/crd/detail/template.html - 46 + 51 @@ -2882,7 +2890,7 @@ 複数 ../src/app/frontend/crd/detail/template.html - 52 + 57 @@ -2890,7 +2898,7 @@ 単数 ../src/app/frontend/crd/detail/template.html - 57 + 62 @@ -2898,7 +2906,7 @@ 種類一覧 ../src/app/frontend/crd/detail/template.html - 67 + 72 @@ -2906,7 +2914,7 @@ 省略名 ../src/app/frontend/crd/detail/template.html - 72 + 77 @@ -2914,7 +2922,7 @@ カテゴリー ../src/app/frontend/crd/detail/template.html - 77 + 82 diff --git a/i18n/messages.ko.xlf b/i18n/messages.ko.xlf index 9cd79db18..df944e89e 100644 --- a/i18n/messages.ko.xlf +++ b/i18n/messages.ko.xlf @@ -1475,7 +1475,7 @@ ../src/app/frontend/crd/detail/template.html - 62 + 67 @@ -2912,12 +2912,20 @@ 33 + + Subresources + Subresources + + ../src/app/frontend/crd/detail/template.html + 43 + + Accepted Names Accepted Names ../src/app/frontend/crd/detail/template.html - 46 + 51 @@ -2925,7 +2933,7 @@ 복수 ../src/app/frontend/crd/detail/template.html - 52 + 57 @@ -2933,7 +2941,7 @@ 단수 ../src/app/frontend/crd/detail/template.html - 57 + 62 @@ -2941,7 +2949,7 @@ 종류 리스트 ../src/app/frontend/crd/detail/template.html - 67 + 72 @@ -2949,7 +2957,7 @@ 단축 이름 ../src/app/frontend/crd/detail/template.html - 72 + 77 @@ -2957,7 +2965,7 @@ 카테고리 ../src/app/frontend/crd/detail/template.html - 77 + 82 @@ -3254,6 +3262,7 @@ open_in_new 더 배우기 + ../src/app/frontend/create/from/form/template.html 54 diff --git a/i18n/messages.xlf b/i18n/messages.xlf index d8828ca0e..6f9c62b49 100644 --- a/i18n/messages.xlf +++ b/i18n/messages.xlf @@ -1374,7 +1374,7 @@ ../src/app/frontend/crd/detail/template.html - 62 + 67 @@ -2619,46 +2619,53 @@ 33 + + Subresources + + ../src/app/frontend/crd/detail/template.html + 43 + + Accepted Names ../src/app/frontend/crd/detail/template.html - 46 + 51 Plural ../src/app/frontend/crd/detail/template.html - 52 + 57 Singular ../src/app/frontend/crd/detail/template.html - 57 + 62 List Kind ../src/app/frontend/crd/detail/template.html - 67 + 72 Short Names ../src/app/frontend/crd/detail/template.html - 72 + 77 Categories ../src/app/frontend/crd/detail/template.html - 77 + 82 diff --git a/i18n/messages.zh.xlf b/i18n/messages.zh.xlf index 7139be7f7..be4801d6a 100644 --- a/i18n/messages.zh.xlf +++ b/i18n/messages.zh.xlf @@ -1483,7 +1483,7 @@ ../src/app/frontend/crd/detail/template.html - 62 + 67 @@ -2930,12 +2930,20 @@ 33 + + Subresources + Subresources + + ../src/app/frontend/crd/detail/template.html + 43 + + Accepted Names Accepted Names ../src/app/frontend/crd/detail/template.html - 46 + 51 @@ -2943,7 +2951,7 @@ Plural ../src/app/frontend/crd/detail/template.html - 52 + 57 @@ -2951,7 +2959,7 @@ Singular ../src/app/frontend/crd/detail/template.html - 57 + 62 @@ -2959,7 +2967,7 @@ List Kind ../src/app/frontend/crd/detail/template.html - 67 + 72 @@ -2967,7 +2975,7 @@ Short Names ../src/app/frontend/crd/detail/template.html - 72 + 77 @@ -2975,7 +2983,7 @@ Categories ../src/app/frontend/crd/detail/template.html - 77 + 82 diff --git a/src/app/backend/api/types.go b/src/app/backend/api/types.go index cc214bbad..7617ec4f5 100644 --- a/src/app/backend/api/types.go +++ b/src/app/backend/api/types.go @@ -75,6 +75,9 @@ type TypeMeta struct { // In smalllettercase. // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds Kind ResourceKind `json:"kind,omitempty"` + + // Scalable represents whether or not an object is scalable. + Scalable bool `json:"scalable,omitempty"` } // ListMeta describes list of objects, i.e. holds information about pagination options set for the list. @@ -99,7 +102,8 @@ func NewObjectMeta(k8SObjectMeta metaV1.ObjectMeta) ObjectMeta { // NewTypeMeta creates new type mete for the resource kind. func NewTypeMeta(kind ResourceKind) TypeMeta { return TypeMeta{ - Kind: kind, + Kind: kind, + Scalable: kind.Scalable(), } } @@ -136,6 +140,23 @@ const ( ResourceKindEndpoint = "endpoint" ) +func (k ResourceKind) Scalable() bool { + scalable := []ResourceKind{ + ResourceKindDeployment, + ResourceKindReplicaSet, + ResourceKindReplicationController, + ResourceKindStatefulSet, + } + + for _, kind := range scalable { + if k == kind { + return true + } + } + + return false +} + // ClientType represents type of client that is used to perform generic operations on resources. // Different resources belong to different client, i.e. Deployments belongs to extension client // and StatefulSets to apps client. diff --git a/src/app/backend/handler/apihandler.go b/src/app/backend/handler/apihandler.go index 3e9eeb8b1..47073b90c 100644 --- a/src/app/backend/handler/apihandler.go +++ b/src/app/backend/handler/apihandler.go @@ -268,10 +268,18 @@ func CreateHTTPAPIHandler(iManager integration.IntegrationManager, cManager clie apiV1Ws.PUT("/scale/{kind}/{namespace}/{name}/"). To(apiHandler.handleScaleResource). Writes(scaling.ReplicaCounts{})) + apiV1Ws.Route( + apiV1Ws.PUT("/scale/{kind}/{name}/"). + To(apiHandler.handleScaleResource). + Writes(scaling.ReplicaCounts{})) apiV1Ws.Route( apiV1Ws.GET("/scale/{kind}/{namespace}/{name}"). To(apiHandler.handleGetReplicaCount). Writes(scaling.ReplicaCounts{})) + apiV1Ws.Route( + apiV1Ws.GET("/scale/{kind}/{name}"). + To(apiHandler.handleGetReplicaCount). + Writes(scaling.ReplicaCounts{})) apiV1Ws.Route( apiV1Ws.GET("/daemonset"). @@ -550,7 +558,7 @@ func CreateHTTPAPIHandler(iManager integration.IntegrationManager, cManager clie apiV1Ws.Route( apiV1Ws.GET("/crd/{namespace}/{crd}/{object}"). To(apiHandler.handleGetCustomResourceObjectDetail). - Writes(customresourcedefinition.CustomResourceObject{})) + Writes(customresourcedefinition.CustomResourceObjectDetail{})) apiV1Ws.Route( apiV1Ws.GET("/crd/{namespace}/{crd}/{object}/event"). diff --git a/src/app/backend/resource/customresourcedefinition/detail.go b/src/app/backend/resource/customresourcedefinition/detail.go index c0fc47ecd..905c32d14 100644 --- a/src/app/backend/resource/customresourcedefinition/detail.go +++ b/src/app/backend/resource/customresourcedefinition/detail.go @@ -27,9 +27,10 @@ import ( type CustomResourceDefinitionDetail struct { CustomResourceDefinition `json:",inline"` - Versions []CustomResourceDefinitionVersion `json:"versions,omitempty"` - Conditions []common.Condition `json:"conditions"` - Objects CustomResourceObjectList `json:"objects"` + Versions []CustomResourceDefinitionVersion `json:"versions,omitempty"` + Conditions []common.Condition `json:"conditions"` + Objects CustomResourceObjectList `json:"objects"` + Subresources []string `json:"subresources"` // List of non-critical errors, that occurred during resource retrieval. Errors []error `json:"errors"` @@ -61,11 +62,22 @@ func GetCustomResourceDefinitionDetail(client apiextensionsclientset.Interface, } func toCustomResourceDefinitionDetail(crd *apiextensions.CustomResourceDefinition, objects CustomResourceObjectList, nonCriticalErrors []error) *CustomResourceDefinitionDetail { + subresources := []string{} + if crd.Spec.Subresources != nil { + if crd.Spec.Subresources.Scale != nil { + subresources = append(subresources, "Scale") + } + if crd.Spec.Subresources.Status != nil { + subresources = append(subresources, "Status") + } + } + return &CustomResourceDefinitionDetail{ CustomResourceDefinition: toCustomResourceDefinition(crd), Versions: getCRDVersions(crd), Conditions: getCRDConditions(crd), Objects: objects, + Subresources: subresources, Errors: nonCriticalErrors, } } diff --git a/src/app/backend/resource/customresourcedefinition/objects.go b/src/app/backend/resource/customresourcedefinition/objects.go index dee208cfb..5f4e460b6 100644 --- a/src/app/backend/resource/customresourcedefinition/objects.go +++ b/src/app/backend/resource/customresourcedefinition/objects.go @@ -29,8 +29,8 @@ import ( // CustomResourceObject represents a custom resource object. type CustomResourceObject struct { - TypeMeta metav1.TypeMeta `json:"typeMeta"` - ObjectMeta metav1.ObjectMeta `json:"objectMeta"` + TypeMeta api.TypeMeta `json:"typeMeta"` + ObjectMeta api.ObjectMeta `json:"objectMeta"` } func (r *CustomResourceObject) UnmarshalJSON(data []byte) error { @@ -44,8 +44,8 @@ func (r *CustomResourceObject) UnmarshalJSON(data []byte) error { return err } - r.TypeMeta = tempStruct.TypeMeta - r.ObjectMeta = tempStruct.ObjectMeta + r.TypeMeta = api.NewTypeMeta(api.ResourceKind(tempStruct.TypeMeta.Kind)) + r.ObjectMeta = api.NewObjectMeta(tempStruct.ObjectMeta) return nil } @@ -125,7 +125,7 @@ func GetCustomResourceObjectList(client apiextensionsclientset.Interface, config list.ListMeta = api.ListMeta{TotalItems: filteredTotal} for i := range list.Items { - replaceCRDObjectKind(&list.Items[i], customResourceDefinition.Name) + toCRDObject(&list.Items[i], customResourceDefinition) } return list, nil @@ -165,12 +165,13 @@ func GetCustomResourceObjectDetail(client apiextensionsclientset.Interface, name } detail.Errors = nonCriticalErrors - replaceCRDObjectKind(&detail.CustomResourceObject, customResourceDefinition.Name) + toCRDObject(&detail.CustomResourceObject, customResourceDefinition) return detail, nil } -// replaceCRDObjectKind sets the object kind to the full name of the CRD. +// toCRDObject sets the object kind to the full name of the CRD. // E.g. changes "Foo" to "foos.samplecontroller.k8s.io" -func replaceCRDObjectKind(object *CustomResourceObject, kind string) { - object.TypeMeta.Kind = kind +func toCRDObject(object *CustomResourceObject, crd *apiextensions.CustomResourceDefinition) { + object.TypeMeta.Kind = api.ResourceKind(crd.Name) + object.TypeMeta.Scalable = crd.Spec.Subresources != nil && crd.Spec.Subresources.Scale != nil } diff --git a/src/app/backend/resource/deployment/detail_test.go b/src/app/backend/resource/deployment/detail_test.go index 7a0147a80..ce695c3b0 100644 --- a/src/app/backend/resource/deployment/detail_test.go +++ b/src/app/backend/resource/deployment/detail_test.go @@ -111,7 +111,10 @@ func TestGetDeploymentDetail(t *testing.T) { Namespace: "ns-1", Labels: map[string]string{"foo": "bar"}, }, - TypeMeta: api.TypeMeta{Kind: api.ResourceKindDeployment}, + TypeMeta: api.TypeMeta{ + Kind: api.ResourceKindDeployment, + Scalable: true, + }, Pods: common.PodInfo{ Desired: &desired, Current: 4, diff --git a/src/app/backend/resource/deployment/list_test.go b/src/app/backend/resource/deployment/list_test.go index 7655f1150..ab401e13a 100644 --- a/src/app/backend/resource/deployment/list_test.go +++ b/src/app/backend/resource/deployment/list_test.go @@ -114,7 +114,10 @@ func TestGetDeploymentListFromChannels(t *testing.T) { Labels: map[string]string{"key": "value"}, CreationTimestamp: metaV1.Unix(111, 222), }, - TypeMeta: api.TypeMeta{Kind: api.ResourceKindDeployment}, + TypeMeta: api.TypeMeta{ + Kind: api.ResourceKindDeployment, + Scalable: true, + }, Pods: common.PodInfo{ Current: 7, Desired: getReplicasPointer(21), diff --git a/src/app/backend/resource/replicaset/common_test.go b/src/app/backend/resource/replicaset/common_test.go index 173335884..7492eaa6c 100644 --- a/src/app/backend/resource/replicaset/common_test.go +++ b/src/app/backend/resource/replicaset/common_test.go @@ -35,7 +35,7 @@ func TestToReplicaSet(t *testing.T) { &common.PodInfo{Running: 1, Warnings: []common.Event{}}, ReplicaSet{ ObjectMeta: api.ObjectMeta{Name: "replica-set"}, - TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicaSet}, + TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicaSet, Scalable: true}, Pods: common.PodInfo{Running: 1, Warnings: []common.Event{}}, }, }, diff --git a/src/app/backend/resource/replicaset/detail_test.go b/src/app/backend/resource/replicaset/detail_test.go index adc54fb2a..56f748ca2 100644 --- a/src/app/backend/resource/replicaset/detail_test.go +++ b/src/app/backend/resource/replicaset/detail_test.go @@ -54,7 +54,7 @@ func TestGetReplicaSetDetail(t *testing.T) { ReplicaSet: ReplicaSet{ ObjectMeta: api.ObjectMeta{Name: "rs-1", Namespace: "ns-1", Labels: map[string]string{"app": "test"}}, - TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicaSet}, + TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicaSet, Scalable: true}, Pods: common.PodInfo{ Warnings: []common.Event{}, Desired: &replicas, @@ -118,7 +118,7 @@ func TestToReplicaSetDetail(t *testing.T) { horizontalpodautoscaler.HorizontalPodAutoscalerList{}, ReplicaSetDetail{ ReplicaSet: ReplicaSet{ - TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicaSet}, + TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicaSet, Scalable: true}, }, Errors: []error{}, }, @@ -136,7 +136,7 @@ func TestToReplicaSetDetail(t *testing.T) { ReplicaSetDetail{ ReplicaSet: ReplicaSet{ ObjectMeta: api.ObjectMeta{Name: "replica-set"}, - TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicaSet}, + TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicaSet, Scalable: true}, }, HorizontalPodAutoscalerList: horizontalpodautoscaler.HorizontalPodAutoscalerList{ HorizontalPodAutoscalers: []horizontalpodautoscaler.HorizontalPodAutoscaler{{ diff --git a/src/app/backend/resource/replicaset/list_test.go b/src/app/backend/resource/replicaset/list_test.go index cc99ca43b..ec73ceac6 100644 --- a/src/app/backend/resource/replicaset/list_test.go +++ b/src/app/backend/resource/replicaset/list_test.go @@ -144,7 +144,7 @@ func TestGetReplicaSetListFromChannels(t *testing.T) { Labels: map[string]string{"key": "value"}, CreationTimestamp: metaV1.Unix(111, 222), }, - TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicaSet}, + TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicaSet, Scalable: true}, Pods: common.PodInfo{ Current: 7, Desired: &replicas, @@ -234,7 +234,7 @@ func TestToReplicaSetList(t *testing.T) { ReplicaSets: []ReplicaSet{ { ObjectMeta: api.ObjectMeta{Name: "replica-set", Namespace: "ns-1"}, - TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicaSet}, + TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicaSet, Scalable: true}, Pods: common.PodInfo{ Warnings: []common.Event{}, Desired: &replicas, @@ -285,7 +285,7 @@ func TestGetReplicaSetList(t *testing.T) { Name: "rs-1", Labels: map[string]string{}, }, - TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicaSet}, + TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicaSet, Scalable: true}, Pods: common.PodInfo{ Desired: &replicas, Warnings: make([]common.Event, 0), diff --git a/src/app/backend/resource/replicationcontroller/list_test.go b/src/app/backend/resource/replicationcontroller/list_test.go index aa6e4d3d0..a541e1e87 100644 --- a/src/app/backend/resource/replicationcontroller/list_test.go +++ b/src/app/backend/resource/replicationcontroller/list_test.go @@ -177,7 +177,10 @@ func TestToReplicationControllerList(t *testing.T) { Namespace: "namespace-1", UID: "uid-1", }, - TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicationController}, + TypeMeta: api.TypeMeta{ + Kind: api.ResourceKindReplicationController, + Scalable: true, + }, ContainerImages: []string{"my-container-image-1"}, Pods: common.PodInfo{ Desired: &replicas, @@ -193,7 +196,10 @@ func TestToReplicationControllerList(t *testing.T) { Namespace: "namespace-2", UID: "uid-2", }, - TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicationController}, + TypeMeta: api.TypeMeta{ + Kind: api.ResourceKindReplicationController, + Scalable: true, + }, ContainerImages: []string{"my-container-image-2"}, Pods: common.PodInfo{ Desired: &replicas, @@ -245,7 +251,10 @@ func TestGetReplicationControllerList(t *testing.T) { Name: "rc-1", Labels: map[string]string{}, }, - TypeMeta: api.TypeMeta{Kind: api.ResourceKindReplicationController}, + TypeMeta: api.TypeMeta{ + Kind: api.ResourceKindReplicationController, + Scalable: true, + }, Pods: common.PodInfo{ Desired: &replicas, Warnings: make([]common.Event, 0), diff --git a/src/app/backend/resource/statefulset/list_test.go b/src/app/backend/resource/statefulset/list_test.go index 3aa9919e4..29cfcdd8f 100644 --- a/src/app/backend/resource/statefulset/list_test.go +++ b/src/app/backend/resource/statefulset/list_test.go @@ -148,7 +148,10 @@ func TestGetStatefulSetListFromChannels(t *testing.T) { Labels: map[string]string{"key": "value"}, CreationTimestamp: metaV1.Unix(111, 222), }, - TypeMeta: api.TypeMeta{Kind: api.ResourceKindStatefulSet}, + TypeMeta: api.TypeMeta{ + Kind: api.ResourceKindStatefulSet, + Scalable: true, + }, Pods: common.PodInfo{ Current: 7, Desired: getReplicasPointer(21), diff --git a/src/app/backend/scaling/scale.go b/src/app/backend/scaling/scale.go index 7114fe394..98790092f 100644 --- a/src/app/backend/scaling/scale.go +++ b/src/app/backend/scaling/scale.go @@ -17,6 +17,7 @@ package scaling import ( "strconv" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/discovery" "k8s.io/client-go/discovery/cached/memory" "k8s.io/client-go/dynamic" @@ -40,7 +41,8 @@ func GetScaleSpec(cfg *rest.Config, kind, namespace, name string) (*ReplicaCount return nil, err } - res, err := sc.Scales(namespace).Get(appsv1beta2.Resource(kind), name) + gr := getGroupResource(kind) + res, err := sc.Scales(namespace).Get(gr, name) if err != nil { return nil, err } @@ -60,7 +62,8 @@ func ScaleResource(cfg *rest.Config, kind, namespace, name, count string) (*Repl return nil, err } - res, err := sc.Scales(namespace).Get(appsv1beta2.Resource(kind), name) + gr := getGroupResource(kind) + res, err := sc.Scales(namespace).Get(gr, name) if err != nil { return nil, err } @@ -72,7 +75,7 @@ func ScaleResource(cfg *rest.Config, kind, namespace, name, count string) (*Repl res.Spec.Replicas = int32(c) - res, err = sc.Scales(namespace).Update(appsv1beta2.Resource(kind), res) + res, err = sc.Scales(namespace).Update(gr, res) if err != nil { return nil, err } @@ -107,3 +110,13 @@ func getScaleGetter(cfg *rest.Config) (scale.ScalesGetter, error) { return scale.New(restClient, drm, dynamic.LegacyAPIPathResolverFunc, resolver), nil } + +func getGroupResource(kind string) schema.GroupResource { + gr := schema.ParseGroupResource(kind) + + if gr.Group != "" && gr.Resource != "" { + return gr + } else { + return appsv1beta2.Resource(kind) + } +} diff --git a/src/app/frontend/common/components/actionbars/scaledefault/component.ts b/src/app/frontend/common/components/actionbars/scaledefault/component.ts index 301fc9e52..c7c7dcffb 100644 --- a/src/app/frontend/common/components/actionbars/scaledefault/component.ts +++ b/src/app/frontend/common/components/actionbars/scaledefault/component.ts @@ -49,4 +49,8 @@ export class ScaleDefaultActionbar implements OnInit, OnDestroy { this._unsubscribe.next(); this._unsubscribe.complete(); } + + scalable(): boolean { + return this.resourceMeta.typeMeta.scalable; + } } diff --git a/src/app/frontend/common/components/actionbars/scaledefault/template.html b/src/app/frontend/common/components/actionbars/scaledefault/template.html index ebd16de73..0bf5fc530 100644 --- a/src/app/frontend/common/components/actionbars/scaledefault/template.html +++ b/src/app/frontend/common/components/actionbars/scaledefault/template.html @@ -16,7 +16,7 @@ limitations under the License.
- diff --git a/src/app/frontend/common/components/list/column/menu/component.ts b/src/app/frontend/common/components/list/column/menu/component.ts index bd1712b8f..5b073a48b 100644 --- a/src/app/frontend/common/components/list/column/menu/component.ts +++ b/src/app/frontend/common/components/list/column/menu/component.ts @@ -31,13 +31,6 @@ const loggableResources: string[] = [ Resource.statefulSet, ]; -const scalableResources: string[] = [ - Resource.deployment, - Resource.replicaSet, - Resource.replicationController, - Resource.statefulSet, -]; - const pinnableResources: string[] = [Resource.crdFull]; const executableResources: string[] = [Resource.pod]; @@ -97,7 +90,7 @@ export class MenuComponent implements ActionColumn { } isScaleEnabled(): boolean { - return scalableResources.includes(this.typeMeta.kind); + return this.typeMeta.scalable; } onScale(): void { diff --git a/src/app/frontend/common/components/resourcelist/crdobject/template.html b/src/app/frontend/common/components/resourcelist/crdobject/template.html index 423eddcff..cacf9957f 100644 --- a/src/app/frontend/common/components/resourcelist/crdobject/template.html +++ b/src/app/frontend/common/components/resourcelist/crdobject/template.html @@ -65,9 +65,9 @@ limitations under the License. - + + [resource]="object"> diff --git a/src/app/frontend/common/dialogs/scaleresource/dialog.ts b/src/app/frontend/common/dialogs/scaleresource/dialog.ts index 24a20f85d..7bd9ebcb9 100644 --- a/src/app/frontend/common/dialogs/scaleresource/dialog.ts +++ b/src/app/frontend/common/dialogs/scaleresource/dialog.ts @@ -34,7 +34,11 @@ export class ScaleResourceDialog implements OnInit { ) {} ngOnInit(): void { - const url = `api/v1/scale/${this.data.typeMeta.kind}/${this.data.objectMeta.namespace}/${this.data.objectMeta.name}/`; + const url = + `api/v1/scale/${this.data.typeMeta.kind}` + + (this.data.objectMeta.namespace ? `/${this.data.objectMeta.namespace}` : '') + + `/${this.data.objectMeta.name}/`; + this.http_ .get(url) .toPromise() diff --git a/src/app/frontend/common/services/global/verber.ts b/src/app/frontend/common/services/global/verber.ts index 6e029f177..5952c65b7 100644 --- a/src/app/frontend/common/services/global/verber.ts +++ b/src/app/frontend/common/services/global/verber.ts @@ -72,7 +72,11 @@ export class VerberService { .afterClosed() .subscribe(result => { if (Number.isInteger(result)) { - const url = `api/v1/scale/${typeMeta.kind}/${objectMeta.namespace}/${objectMeta.name}/`; + const url = + `api/v1/scale/${typeMeta.kind}` + + (objectMeta.namespace ? `/${objectMeta.namespace}` : '') + + `/${objectMeta.name}/`; + this.http_ .put(url, result, { params: { diff --git a/src/app/frontend/crd/detail/template.html b/src/app/frontend/crd/detail/template.html index 3530c2654..3926ed039 100644 --- a/src/app/frontend/crd/detail/template.html +++ b/src/app/frontend/crd/detail/template.html @@ -38,6 +38,11 @@ limitations under the License. i18n>Group
{{crd?.group}}
+ +
Subresources
+
{{crd.subresources.join(", ")}}
+
diff --git a/src/app/frontend/crd/routing.ts b/src/app/frontend/crd/routing.ts index 58026935f..685767191 100644 --- a/src/app/frontend/crd/routing.ts +++ b/src/app/frontend/crd/routing.ts @@ -17,31 +17,64 @@ import {Route, RouterModule} from '@angular/router'; import {CRDDetailComponent} from './detail/component'; import {CRDListComponent} from './list/component'; -import {PIN_DEFAULT_ACTIONBAR} from '../common/components/actionbars/routing'; +import {DEFAULT_ACTIONBAR, PIN_DEFAULT_ACTIONBAR} from '../common/components/actionbars/routing'; import {CRDObjectDetailComponent} from './crdobject/component'; +import {SCALE_DEFAULT_ACTIONBAR} from '../common/components/actionbars/routing'; const CRD_LIST_ROUTE: Route = { path: '', - component: CRDListComponent, - data: {breadcrumb: 'Custom Resource Definitions'}, + children: [ + { + path: '', + component: CRDListComponent, + data: {breadcrumb: 'Custom Resource Definitions'}, + }, + DEFAULT_ACTIONBAR, + ], }; const CRD_DETAIL_ROUTE: Route = { - path: ':crdName', - component: CRDDetailComponent, - data: {breadcrumb: '{{ crdName }}', parent: CRD_LIST_ROUTE}, + path: '', + children: [ + { + path: ':crdName', + component: CRDDetailComponent, + data: {breadcrumb: '{{ crdName }}', parent: CRD_LIST_ROUTE.children[0]}, + }, + PIN_DEFAULT_ACTIONBAR, + ], }; const CRD_NAMESPACED_OBJECT_DETAIL_ROUTE: Route = { path: ':crdName/:namespace/:objectName', - component: CRDObjectDetailComponent, - data: {breadcrumb: '{{ objectName }}', routeParamsCount: 2, parent: CRD_DETAIL_ROUTE}, + children: [ + { + path: '', + component: CRDObjectDetailComponent, + data: { + breadcrumb: '{{ objectName }}', + routeParamsCount: 2, + parent: CRD_DETAIL_ROUTE.children[0], + }, + }, + SCALE_DEFAULT_ACTIONBAR, + ], }; const CRD_CLUSTER_OBJECT_DETAIL_ROUTE: Route = { path: ':crdName/:objectName', - component: CRDObjectDetailComponent, - data: {breadcrumb: '{{ objectName }}', routeParamsCount: 1, parent: CRD_DETAIL_ROUTE}, + children: [ + { + path: '', + component: CRDObjectDetailComponent, + data: { + breadcrumb: '{{ objectName }}', + routeParamsCount: 1, + parent: CRD_DETAIL_ROUTE.children[0], + }, + }, + SCALE_DEFAULT_ACTIONBAR, + ], }; @NgModule({ @@ -51,7 +84,6 @@ const CRD_CLUSTER_OBJECT_DETAIL_ROUTE: Route = { CRD_DETAIL_ROUTE, CRD_NAMESPACED_OBJECT_DETAIL_ROUTE, CRD_CLUSTER_OBJECT_DETAIL_ROUTE, - PIN_DEFAULT_ACTIONBAR, ]), ], }) diff --git a/src/app/frontend/typings/backendapi.d.ts b/src/app/frontend/typings/backendapi.d.ts index 64e8d928f..020863864 100644 --- a/src/app/frontend/typings/backendapi.d.ts +++ b/src/app/frontend/typings/backendapi.d.ts @@ -17,6 +17,7 @@ import {KdError} from '@api/frontendapi'; export interface TypeMeta { kind: string; + scalable?: boolean; } export interface ListMeta { @@ -459,6 +460,7 @@ export interface CRDDetail extends ResourceDetail { versions: CRDVersion[]; objects: CRDObjectList; conditions: Condition[]; + subresources: string[]; } export interface CRDObjectDetail extends ResourceDetail {} -- GitLab