From 364afe4042fb70607f0ce1bee920cc588320996e Mon Sep 17 00:00:00 2001 From: Mike Dame Date: Wed, 29 Aug 2018 11:46:48 -0400 Subject: [PATCH 1/2] Support multiple versions in custom metrics client This changes the custom metrics client logic over to support multiple versions of the custom metrics API by checking discovery to find the appropriate versions. Fixes #68011 Co-authored-by: Solly Ross --- .../app/autoscaling.go | 13 +- staging/src/k8s.io/metrics/Godeps/Godeps.json | 16 ++ .../pkg/apis/custom_metrics/register.go | 1 + .../metrics/pkg/apis/custom_metrics/types.go | 16 ++ .../custom_metrics/v1beta1/generated.pb.go | 242 +++++++++++++++--- .../custom_metrics/v1beta1/generated.proto | 12 + .../apis/custom_metrics/v1beta1/register.go | 1 + .../pkg/apis/custom_metrics/v1beta1/types.go | 16 ++ .../v1beta1/zz_generated.conversion.go | 32 +++ .../v1beta1/zz_generated.deepcopy.go | 25 ++ .../v1beta2/zz_generated.conversion.go | 32 +++ .../custom_metrics/zz_generated.deepcopy.go | 25 ++ .../metrics/pkg/client/custom_metrics/BUILD | 27 +- .../pkg/client/custom_metrics/converter.go | 122 +++++++++ .../pkg/client/custom_metrics/discovery.go | 145 +++++++++++ .../pkg/client/custom_metrics/multi_client.go | 138 ++++++++++ .../pkg/client/custom_metrics/scheme/BUILD | 1 + .../client/custom_metrics/scheme/register.go | 6 + .../pkg/client/custom_metrics/util_test.go | 65 +++++ .../{client.go => versioned_client.go} | 197 +++++++------- test/e2e/instrumentation/monitoring/BUILD | 1 + .../monitoring/custom_metrics_stackdriver.go | 10 +- 22 files changed, 1007 insertions(+), 136 deletions(-) create mode 100644 staging/src/k8s.io/metrics/pkg/client/custom_metrics/converter.go create mode 100644 staging/src/k8s.io/metrics/pkg/client/custom_metrics/discovery.go create mode 100644 staging/src/k8s.io/metrics/pkg/client/custom_metrics/multi_client.go create mode 100644 staging/src/k8s.io/metrics/pkg/client/custom_metrics/util_test.go rename staging/src/k8s.io/metrics/pkg/client/custom_metrics/{client.go => versioned_client.go} (61%) diff --git a/cmd/kube-controller-manager/app/autoscaling.go b/cmd/kube-controller-manager/app/autoscaling.go index 76f80d9ae1..f53d65b05d 100644 --- a/cmd/kube-controller-manager/app/autoscaling.go +++ b/cmd/kube-controller-manager/app/autoscaling.go @@ -28,6 +28,7 @@ import ( "k8s.io/client-go/scale" "k8s.io/kubernetes/pkg/controller/podautoscaler" "k8s.io/kubernetes/pkg/controller/podautoscaler/metrics" + resourceclient "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1" "k8s.io/metrics/pkg/client/custom_metrics" "k8s.io/metrics/pkg/client/external_metrics" @@ -48,9 +49,19 @@ func startHPAController(ctx ControllerContext) (http.Handler, bool, error) { func startHPAControllerWithRESTClient(ctx ControllerContext) (http.Handler, bool, error) { clientConfig := ctx.ClientBuilder.ConfigOrDie("horizontal-pod-autoscaler") + hpaClient := ctx.ClientBuilder.ClientOrDie("horizontal-pod-autoscaler") + + apiVersionsGetter := custom_metrics.NewAvailableAPIsGetter(hpaClient.Discovery()) + // invalidate the discovery information roughly once per resync interval our API + // information is *at most* two resync intervals old. + go custom_metrics.PeriodicallyInvalidate( + apiVersionsGetter, + ctx.ComponentConfig.HPAController.HorizontalPodAutoscalerSyncPeriod.Duration, + ctx.Stop) + metricsClient := metrics.NewRESTMetricsClient( resourceclient.NewForConfigOrDie(clientConfig), - custom_metrics.NewForConfigOrDie(clientConfig), + custom_metrics.NewForConfig(clientConfig, ctx.RESTMapper, apiVersionsGetter), external_metrics.NewForConfigOrDie(clientConfig), ) return startHPAControllerWithMetricsClient(ctx, metricsClient) diff --git a/staging/src/k8s.io/metrics/Godeps/Godeps.json b/staging/src/k8s.io/metrics/Godeps/Godeps.json index 8a3032fd29..59a8e39a3d 100644 --- a/staging/src/k8s.io/metrics/Godeps/Godeps.json +++ b/staging/src/k8s.io/metrics/Godeps/Godeps.json @@ -90,6 +90,18 @@ "ImportPath": "github.com/peterbourgon/diskv", "Rev": "5f041e8faa004a95c88a202771f4cc3e991971e6" }, + { + "ImportPath": "github.com/pmezard/go-difflib/difflib", + "Rev": "d8ed2627bdf02c080bf22230dbb337003b7aba2d" + }, + { + "ImportPath": "github.com/stretchr/testify/assert", + "Rev": "c679ae2cc0cb27ec3293fea7e254e47386f05d69" + }, + { + "ImportPath": "github.com/stretchr/testify/require", + "Rev": "c679ae2cc0cb27ec3293fea7e254e47386f05d69" + }, { "ImportPath": "golang.org/x/crypto/ssh/terminal", "Rev": "de0752318171da717af4ce24d0a2e8626afaeb11" @@ -438,6 +450,10 @@ "ImportPath": "k8s.io/client-go/discovery", "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, + { + "ImportPath": "k8s.io/client-go/discovery/cached", + "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + }, { "ImportPath": "k8s.io/client-go/discovery/fake", "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" diff --git a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/register.go b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/register.go index 091fcc483d..a44b8b410c 100644 --- a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/register.go +++ b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/register.go @@ -46,6 +46,7 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &MetricValue{}, &MetricValueList{}, + &MetricListOptions{}, ) return nil } diff --git a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/types.go b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/types.go index b426c01995..a39d5c460d 100644 --- a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/types.go +++ b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/types.go @@ -71,6 +71,22 @@ type MetricValue struct { // for all objects matching the given label selector const AllObjects = "*" +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// MetricListOptions is used to select metrics by their label selectors +type MetricListOptions struct { + metav1.TypeMeta `json:",inline"` + + // A selector to restrict the list of returned objects by their labels. + // Defaults to everything. + // +optional + LabelSelector string `json:"labelSelector,omitempty" protobuf:"bytes,1,opt,name=labelSelector"` + + // A selector to restrict the list of returned metrics by their labels + // +optional + MetricLabelSelector string `json:"metricLabelSelector,omitempty" protobuf:"bytes,2,opt,name=metricLabelSelector"` +} + // NOTE: ObjectReference is copied from k8s.io/kubernetes/pkg/api/types.go. We // cannot depend on k8s.io/kubernetes/pkg/api because that creates cyclic // dependency between k8s.io/metrics and k8s.io/kubernetes. We cannot depend on diff --git a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/generated.pb.go b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/generated.pb.go index 3191ebd3de..704c8cc634 100644 --- a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/generated.pb.go +++ b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/generated.pb.go @@ -25,6 +25,7 @@ limitations under the License. k8s.io/kubernetes/vendor/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/generated.proto It has these top-level messages: + MetricListOptions MetricValue MetricValueList */ @@ -52,18 +53,49 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +func (m *MetricListOptions) Reset() { *m = MetricListOptions{} } +func (*MetricListOptions) ProtoMessage() {} +func (*MetricListOptions) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} } + func (m *MetricValue) Reset() { *m = MetricValue{} } func (*MetricValue) ProtoMessage() {} -func (*MetricValue) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} } +func (*MetricValue) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{1} } func (m *MetricValueList) Reset() { *m = MetricValueList{} } func (*MetricValueList) ProtoMessage() {} -func (*MetricValueList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{1} } +func (*MetricValueList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{2} } func init() { + proto.RegisterType((*MetricListOptions)(nil), "k8s.io.metrics.pkg.apis.custom_metrics.v1beta1.MetricListOptions") proto.RegisterType((*MetricValue)(nil), "k8s.io.metrics.pkg.apis.custom_metrics.v1beta1.MetricValue") proto.RegisterType((*MetricValueList)(nil), "k8s.io.metrics.pkg.apis.custom_metrics.v1beta1.MetricValueList") } +func (m *MetricListOptions) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MetricListOptions) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + dAtA[i] = 0xa + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(m.LabelSelector))) + i += copy(dAtA[i:], m.LabelSelector) + dAtA[i] = 0x12 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(m.MetricLabelSelector))) + i += copy(dAtA[i:], m.MetricLabelSelector) + return i, nil +} + func (m *MetricValue) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -190,6 +222,16 @@ func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return offset + 1 } +func (m *MetricListOptions) Size() (n int) { + var l int + _ = l + l = len(m.LabelSelector) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.MetricLabelSelector) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + func (m *MetricValue) Size() (n int) { var l int _ = l @@ -238,6 +280,17 @@ func sovGenerated(x uint64) (n int) { func sozGenerated(x uint64) (n int) { return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } +func (this *MetricListOptions) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&MetricListOptions{`, + `LabelSelector:` + fmt.Sprintf("%v", this.LabelSelector) + `,`, + `MetricLabelSelector:` + fmt.Sprintf("%v", this.MetricLabelSelector) + `,`, + `}`, + }, "") + return s +} func (this *MetricValue) String() string { if this == nil { return "nil" @@ -272,6 +325,114 @@ func valueToStringGenerated(v interface{}) string { pv := reflect.Indirect(rv).Interface() return fmt.Sprintf("*%v", pv) } +func (m *MetricListOptions) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MetricListOptions: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MetricListOptions: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LabelSelector", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LabelSelector = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MetricLabelSelector", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MetricLabelSelector = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *MetricValue) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -715,41 +876,44 @@ func init() { } var fileDescriptorGenerated = []byte{ - // 566 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x93, 0x4f, 0x6f, 0xd3, 0x3e, - 0x18, 0xc7, 0x9b, 0xf5, 0xd7, 0xfd, 0x3a, 0x8f, 0x69, 0x2c, 0x17, 0xa2, 0x1d, 0xd2, 0x6a, 0x5c, - 0x0a, 0xd2, 0x6c, 0x6d, 0x20, 0x84, 0xc4, 0x2d, 0xe2, 0x82, 0xb4, 0x81, 0xc8, 0x26, 0x26, 0xf1, - 0x47, 0xe0, 0x38, 0x4f, 0x53, 0xd3, 0x26, 0x8e, 0x6c, 0xa7, 0xd3, 0x6e, 0xbc, 0x04, 0x5e, 0x56, - 0x8f, 0xe3, 0xb6, 0x53, 0x45, 0x83, 0x78, 0x1f, 0x28, 0x89, 0xd3, 0x76, 0x1b, 0x7f, 0xb6, 0x5b, - 0x6c, 0x3f, 0xdf, 0x8f, 0xbf, 0xcf, 0xf7, 0x71, 0xd0, 0xc9, 0xf0, 0xa9, 0xc2, 0x5c, 0x90, 0x61, - 0x16, 0x80, 0x4c, 0x40, 0x83, 0x22, 0x63, 0x48, 0x42, 0x21, 0x89, 0x39, 0x88, 0x41, 0x4b, 0xce, - 0x14, 0x49, 0x87, 0x11, 0xa1, 0x29, 0x57, 0x84, 0x65, 0x4a, 0x8b, 0xf8, 0x63, 0xbd, 0x3f, 0xde, - 0x0b, 0x40, 0xd3, 0x3d, 0x12, 0x41, 0x02, 0x92, 0x6a, 0x08, 0x71, 0x2a, 0x85, 0x16, 0x36, 0xae, - 0xf4, 0xd8, 0xd4, 0xe1, 0x74, 0x18, 0xe1, 0x42, 0x8f, 0x2f, 0xeb, 0xb1, 0xd1, 0x6f, 0xef, 0x46, - 0x5c, 0x0f, 0xb2, 0x00, 0x33, 0x11, 0x93, 0x48, 0x44, 0x82, 0x94, 0x98, 0x20, 0xeb, 0x97, 0xab, - 0x72, 0x51, 0x7e, 0x55, 0xf8, 0xed, 0x1d, 0x63, 0x8f, 0xa6, 0x9c, 0x30, 0x21, 0x81, 0x8c, 0xaf, - 0x59, 0xd8, 0x7e, 0xbc, 0xa8, 0x89, 0x29, 0x1b, 0xf0, 0x04, 0xe4, 0x59, 0xdd, 0x07, 0x91, 0xa0, - 0x44, 0x26, 0x19, 0xdc, 0x4a, 0xa5, 0x8a, 0x38, 0xe8, 0xef, 0xee, 0x22, 0x7f, 0x52, 0xc9, 0x2c, - 0xd1, 0x3c, 0xbe, 0x7e, 0xcd, 0x93, 0x7f, 0x09, 0x14, 0x1b, 0x40, 0x4c, 0xaf, 0xea, 0x76, 0x7e, - 0x36, 0xd1, 0xfa, 0x61, 0x99, 0xdd, 0x1b, 0x3a, 0xca, 0xc0, 0xee, 0xa3, 0xcd, 0x10, 0x14, 0x93, - 0x3c, 0x80, 0xf0, 0x55, 0xf0, 0x19, 0x98, 0x76, 0xac, 0xae, 0xd5, 0x5b, 0xdf, 0xbf, 0x5f, 0x4f, - 0x80, 0xa6, 0x1c, 0x17, 0x11, 0xe1, 0xf1, 0x1e, 0xae, 0x2a, 0x7c, 0xe8, 0x83, 0x84, 0x84, 0x81, - 0x77, 0x6f, 0x32, 0xed, 0x34, 0xf2, 0x69, 0x67, 0xf3, 0xf9, 0x65, 0x86, 0x7f, 0x15, 0x6a, 0xef, - 0x23, 0x54, 0x8d, 0xec, 0x25, 0x8d, 0xc1, 0x59, 0xe9, 0x5a, 0xbd, 0x35, 0xcf, 0x36, 0x6a, 0x74, - 0x38, 0x3f, 0xf1, 0x97, 0xaa, 0xec, 0x77, 0x68, 0xad, 0x68, 0x45, 0x69, 0x1a, 0xa7, 0x4e, 0xb3, - 0x74, 0xf5, 0x70, 0xc9, 0xd5, 0xbc, 0xef, 0xc5, 0xe3, 0x28, 0xe2, 0x2d, 0x7c, 0x1e, 0xf3, 0x18, - 0xbc, 0x2d, 0x83, 0x5f, 0x3b, 0xae, 0x21, 0xfe, 0x82, 0x67, 0x3f, 0x40, 0xab, 0xa7, 0x3c, 0x09, - 0xc5, 0xa9, 0xf3, 0x5f, 0xd7, 0xea, 0x35, 0xbd, 0xad, 0x7c, 0xda, 0xd9, 0x38, 0x29, 0x77, 0x8e, - 0x80, 0x89, 0x24, 0x54, 0xbe, 0x29, 0xb0, 0x8f, 0x50, 0x6b, 0x5c, 0x84, 0xe5, 0xb4, 0x4a, 0x0f, - 0xf8, 0x6f, 0x1e, 0x70, 0xfd, 0x30, 0xf0, 0xeb, 0x8c, 0x26, 0x9a, 0xeb, 0x33, 0x6f, 0xc3, 0xf8, - 0x68, 0x95, 0x89, 0xfb, 0x15, 0xcb, 0xfe, 0x80, 0xda, 0x0a, 0x46, 0xc0, 0xb4, 0x90, 0xce, 0x6a, - 0xc9, 0x7d, 0x74, 0xb3, 0xde, 0x0e, 0x68, 0x00, 0xa3, 0x23, 0x23, 0xf5, 0xee, 0xe4, 0xd3, 0x4e, - 0xbb, 0x5e, 0xf9, 0x73, 0xe4, 0xce, 0x37, 0x0b, 0x6d, 0x2e, 0xcd, 0xf9, 0x80, 0x2b, 0x6d, 0xbf, - 0x47, 0xed, 0x02, 0x12, 0x52, 0x4d, 0xcd, 0x90, 0xf1, 0x0d, 0xaf, 0xe4, 0x4a, 0x1f, 0x82, 0xa6, - 0xde, 0x5d, 0xd3, 0x4a, 0xbb, 0xde, 0xf1, 0xe7, 0x44, 0xfb, 0x13, 0x6a, 0x71, 0x0d, 0xb1, 0x72, - 0x56, 0xba, 0xcd, 0xde, 0xfa, 0xfe, 0xb3, 0x5b, 0xfe, 0xc1, 0x78, 0xc9, 0xed, 0x22, 0xb2, 0x17, - 0x05, 0xd1, 0xaf, 0xc0, 0xde, 0xee, 0x64, 0xe6, 0x36, 0xce, 0x67, 0x6e, 0xe3, 0x62, 0xe6, 0x36, - 0xbe, 0xe4, 0xae, 0x35, 0xc9, 0x5d, 0xeb, 0x3c, 0x77, 0xad, 0x8b, 0xdc, 0xb5, 0xbe, 0xe7, 0xae, - 0xf5, 0xf5, 0x87, 0xdb, 0x78, 0xfb, 0xbf, 0x01, 0xfe, 0x0a, 0x00, 0x00, 0xff, 0xff, 0xdb, 0xed, - 0x64, 0xfc, 0x9c, 0x04, 0x00, 0x00, + // 616 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x94, 0x4f, 0x4f, 0x14, 0x3f, + 0x1c, 0xc6, 0x77, 0xd8, 0xdf, 0xf2, 0x5b, 0x8a, 0x04, 0x19, 0x62, 0xdc, 0x60, 0x32, 0x90, 0xf5, + 0x82, 0x26, 0xb4, 0x01, 0x8d, 0x31, 0xe1, 0x36, 0xf1, 0x62, 0xc2, 0x4a, 0x1c, 0x88, 0x24, 0xfe, + 0x89, 0x76, 0x3a, 0x5f, 0x96, 0xba, 0x3b, 0xd3, 0x49, 0xdb, 0x59, 0xc2, 0xcd, 0x97, 0xe0, 0x3b, + 0xf0, 0xed, 0x70, 0xc4, 0x1b, 0x27, 0x22, 0x63, 0x7c, 0x1f, 0x66, 0x3a, 0x9d, 0xfd, 0xc3, 0xa2, + 0xc2, 0x6d, 0xa7, 0x7d, 0x9e, 0x4f, 0x9f, 0x7e, 0x9f, 0x66, 0xd1, 0x41, 0xef, 0xb9, 0xc2, 0x5c, + 0x90, 0x5e, 0x16, 0x82, 0x4c, 0x40, 0x83, 0x22, 0x03, 0x48, 0x22, 0x21, 0x89, 0xdd, 0x88, 0x41, + 0x4b, 0xce, 0x14, 0x49, 0x7b, 0x5d, 0x42, 0x53, 0xae, 0x08, 0xcb, 0x94, 0x16, 0xf1, 0xc7, 0x6a, + 0x7d, 0xb0, 0x19, 0x82, 0xa6, 0x9b, 0xa4, 0x0b, 0x09, 0x48, 0xaa, 0x21, 0xc2, 0xa9, 0x14, 0x5a, + 0xb8, 0xb8, 0xf4, 0x63, 0xab, 0xc3, 0x69, 0xaf, 0x8b, 0x0b, 0x3f, 0x9e, 0xf4, 0x63, 0xeb, 0x5f, + 0xd9, 0xe8, 0x72, 0x7d, 0x94, 0x85, 0x98, 0x89, 0x98, 0x74, 0x45, 0x57, 0x10, 0x83, 0x09, 0xb3, + 0x43, 0xf3, 0x65, 0x3e, 0xcc, 0xaf, 0x12, 0xbf, 0xd2, 0xb6, 0xf1, 0x68, 0xca, 0x09, 0x13, 0x12, + 0xc8, 0x60, 0x2a, 0xc2, 0xca, 0xd3, 0x91, 0x26, 0xa6, 0xec, 0x88, 0x27, 0x20, 0x4f, 0xaa, 0x7b, + 0x10, 0x09, 0x4a, 0x64, 0x92, 0xc1, 0xad, 0x5c, 0xaa, 0x18, 0x07, 0xbd, 0xee, 0x2c, 0xf2, 0x27, + 0x97, 0xcc, 0x12, 0xcd, 0xe3, 0xe9, 0x63, 0x9e, 0xfd, 0xcb, 0xa0, 0xd8, 0x11, 0xc4, 0xf4, 0xaa, + 0xaf, 0xfd, 0xcd, 0x41, 0x4b, 0x1d, 0x33, 0xbb, 0x1d, 0xae, 0xf4, 0x6e, 0xaa, 0xb9, 0x48, 0x94, + 0xbb, 0x8d, 0x16, 0xfa, 0x34, 0x84, 0xfe, 0x1e, 0xf4, 0x81, 0x69, 0x21, 0x5b, 0xce, 0x9a, 0xb3, + 0x3e, 0xe7, 0xdf, 0x3b, 0xbd, 0x58, 0xad, 0xe5, 0x17, 0xab, 0x0b, 0x3b, 0xe3, 0x9b, 0xc1, 0xa4, + 0xd6, 0xed, 0xa0, 0xe5, 0xb2, 0x8d, 0x09, 0x55, 0x6b, 0xc6, 0x20, 0x1e, 0x58, 0xc4, 0x72, 0x67, + 0x5a, 0x12, 0x5c, 0xe7, 0x6b, 0xff, 0xaa, 0xa3, 0xf9, 0x52, 0xfc, 0x86, 0xf6, 0x33, 0x70, 0x0f, + 0xd1, 0x62, 0x04, 0x8a, 0x49, 0x1e, 0x42, 0xb4, 0x1b, 0x7e, 0x06, 0xa6, 0x4d, 0xba, 0xf9, 0xad, + 0x87, 0xd5, 0x1b, 0xa1, 0x29, 0xc7, 0x45, 0x89, 0x78, 0xb0, 0x89, 0x4b, 0x45, 0x00, 0x87, 0x20, + 0x21, 0x61, 0xe0, 0xdf, 0xb7, 0xe7, 0x2f, 0xbe, 0x98, 0x64, 0x04, 0x57, 0xa1, 0xee, 0x16, 0x42, + 0x65, 0x9c, 0x57, 0x34, 0x06, 0x9b, 0xde, 0xb5, 0x6e, 0xd4, 0x19, 0xee, 0x04, 0x63, 0x2a, 0xf7, + 0x1d, 0x9a, 0x2b, 0x86, 0xad, 0x34, 0x8d, 0xd3, 0x56, 0xdd, 0xa4, 0x7a, 0x3c, 0x96, 0x6a, 0xd8, + 0xcc, 0xe8, 0xf9, 0x16, 0x0f, 0xa0, 0xc8, 0xb9, 0xcf, 0x63, 0xf0, 0x97, 0x2c, 0x7e, 0x6e, 0xbf, + 0x82, 0x04, 0x23, 0x9e, 0xfb, 0x08, 0xcd, 0x1e, 0xf3, 0x24, 0x12, 0xc7, 0xad, 0xff, 0xd6, 0x9c, + 0xf5, 0xba, 0xbf, 0x54, 0x34, 0x71, 0x60, 0x56, 0xf6, 0x80, 0x89, 0x24, 0x52, 0x81, 0x15, 0xb8, + 0x7b, 0xa8, 0x31, 0x28, 0x86, 0xd5, 0x6a, 0x98, 0x0c, 0xf8, 0x6f, 0x19, 0x70, 0xf5, 0x74, 0xf1, + 0xeb, 0x8c, 0x26, 0x9a, 0xeb, 0x13, 0x7f, 0xc1, 0xe6, 0x68, 0x98, 0x89, 0x07, 0x25, 0xcb, 0xfd, + 0x80, 0x9a, 0xaa, 0x2a, 0x73, 0xd6, 0x70, 0x9f, 0xdc, 0xec, 0x6e, 0x13, 0x7d, 0xfa, 0x77, 0xf2, + 0x8b, 0xd5, 0xe6, 0xb0, 0xf2, 0x21, 0xb2, 0xfd, 0xdd, 0x41, 0x8b, 0x63, 0x3d, 0x17, 0xcf, 0xd1, + 0x7d, 0x8f, 0x9a, 0x05, 0x24, 0xa2, 0x9a, 0xda, 0x92, 0xf1, 0x0d, 0x8f, 0xe4, 0x4a, 0x77, 0x40, + 0x53, 0xff, 0xae, 0xbd, 0x4a, 0xb3, 0x5a, 0x09, 0x86, 0x44, 0xf7, 0x13, 0x6a, 0x70, 0x0d, 0xb1, + 0x6a, 0xcd, 0xac, 0xd5, 0xd7, 0xe7, 0xb7, 0xb6, 0x6f, 0xf9, 0x1f, 0x83, 0xc7, 0xd2, 0x8e, 0x46, + 0xf6, 0xb2, 0x20, 0x06, 0x25, 0xd8, 0xdf, 0x38, 0xbd, 0xf4, 0x6a, 0x67, 0x97, 0x5e, 0xed, 0xfc, + 0xd2, 0xab, 0x7d, 0xc9, 0x3d, 0xe7, 0x34, 0xf7, 0x9c, 0xb3, 0xdc, 0x73, 0xce, 0x73, 0xcf, 0xf9, + 0x91, 0x7b, 0xce, 0xd7, 0x9f, 0x5e, 0xed, 0xed, 0xff, 0x16, 0xf8, 0x3b, 0x00, 0x00, 0xff, 0xff, + 0xf5, 0x23, 0xb5, 0xdc, 0x3e, 0x05, 0x00, 0x00, } diff --git a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/generated.proto b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/generated.proto index 76b8995056..9d4d259679 100644 --- a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/generated.proto +++ b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/generated.proto @@ -30,6 +30,18 @@ import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto"; // Package-wide variables from generator "generated". option go_package = "v1beta1"; +// MetricListOptions is used to select metrics by their label selectors +message MetricListOptions { + // A selector to restrict the list of returned objects by their labels. + // Defaults to everything. + // +optional + optional string labelSelector = 1; + + // A selector to restrict the list of returned metrics by their labels + // +optional + optional string metricLabelSelector = 2; +} + // a metric value for some object message MetricValue { // a reference to the described object diff --git a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/register.go b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/register.go index 42fd2b92f0..f1b43e9cb6 100644 --- a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/register.go +++ b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/register.go @@ -43,6 +43,7 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &MetricValue{}, &MetricValueList{}, + &MetricListOptions{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/types.go b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/types.go index 8f5901896a..aa97053ca6 100644 --- a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/types.go +++ b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/types.go @@ -65,6 +65,22 @@ type MetricValue struct { Selector *metav1.LabelSelector `json:"selector" protobuf:"bytes,6,opt,name=selector"` } +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// MetricListOptions is used to select metrics by their label selectors +type MetricListOptions struct { + metav1.TypeMeta `json:",inline"` + + // A selector to restrict the list of returned objects by their labels. + // Defaults to everything. + // +optional + LabelSelector string `json:"labelSelector,omitempty" protobuf:"bytes,1,opt,name=labelSelector"` + + // A selector to restrict the list of returned metrics by their labels + // +optional + MetricLabelSelector string `json:"metricLabelSelector,omitempty" protobuf:"bytes,2,opt,name=metricLabelSelector"` +} + // allObjects is a wildcard used to select metrics // for all objects matching the given label selector const AllObjects = "*" diff --git a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/zz_generated.conversion.go b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/zz_generated.conversion.go index 5122f74d9d..d3c587b8b6 100644 --- a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/zz_generated.conversion.go +++ b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/zz_generated.conversion.go @@ -35,6 +35,16 @@ func init() { // RegisterConversions adds conversion functions to the given scheme. // Public to allow building arbitrary schemes. func RegisterConversions(s *runtime.Scheme) error { + if err := s.AddGeneratedConversionFunc((*MetricListOptions)(nil), (*custommetrics.MetricListOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_MetricListOptions_To_custom_metrics_MetricListOptions(a.(*MetricListOptions), b.(*custommetrics.MetricListOptions), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*custommetrics.MetricListOptions)(nil), (*MetricListOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_custom_metrics_MetricListOptions_To_v1beta1_MetricListOptions(a.(*custommetrics.MetricListOptions), b.(*MetricListOptions), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*MetricValue)(nil), (*custommetrics.MetricValue)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta1_MetricValue_To_custom_metrics_MetricValue(a.(*MetricValue), b.(*custommetrics.MetricValue), scope) }); err != nil { @@ -68,6 +78,28 @@ func RegisterConversions(s *runtime.Scheme) error { return nil } +func autoConvert_v1beta1_MetricListOptions_To_custom_metrics_MetricListOptions(in *MetricListOptions, out *custommetrics.MetricListOptions, s conversion.Scope) error { + out.LabelSelector = in.LabelSelector + out.MetricLabelSelector = in.MetricLabelSelector + return nil +} + +// Convert_v1beta1_MetricListOptions_To_custom_metrics_MetricListOptions is an autogenerated conversion function. +func Convert_v1beta1_MetricListOptions_To_custom_metrics_MetricListOptions(in *MetricListOptions, out *custommetrics.MetricListOptions, s conversion.Scope) error { + return autoConvert_v1beta1_MetricListOptions_To_custom_metrics_MetricListOptions(in, out, s) +} + +func autoConvert_custom_metrics_MetricListOptions_To_v1beta1_MetricListOptions(in *custommetrics.MetricListOptions, out *MetricListOptions, s conversion.Scope) error { + out.LabelSelector = in.LabelSelector + out.MetricLabelSelector = in.MetricLabelSelector + return nil +} + +// Convert_custom_metrics_MetricListOptions_To_v1beta1_MetricListOptions is an autogenerated conversion function. +func Convert_custom_metrics_MetricListOptions_To_v1beta1_MetricListOptions(in *custommetrics.MetricListOptions, out *MetricListOptions, s conversion.Scope) error { + return autoConvert_custom_metrics_MetricListOptions_To_v1beta1_MetricListOptions(in, out, s) +} + func autoConvert_v1beta1_MetricValue_To_custom_metrics_MetricValue(in *MetricValue, out *custommetrics.MetricValue, s conversion.Scope) error { // TODO: Inefficient conversion - can we improve it? if err := s.Convert(&in.DescribedObject, &out.DescribedObject, 0); err != nil { diff --git a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/zz_generated.deepcopy.go b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/zz_generated.deepcopy.go index c3e6a2614a..4d2e11c725 100644 --- a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1/zz_generated.deepcopy.go @@ -25,6 +25,31 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MetricListOptions) DeepCopyInto(out *MetricListOptions) { + *out = *in + out.TypeMeta = in.TypeMeta + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricListOptions. +func (in *MetricListOptions) DeepCopy() *MetricListOptions { + if in == nil { + return nil + } + out := new(MetricListOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MetricListOptions) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MetricValue) DeepCopyInto(out *MetricValue) { *out = *in diff --git a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta2/zz_generated.conversion.go b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta2/zz_generated.conversion.go index bb38f4cb31..6a12d03444 100644 --- a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta2/zz_generated.conversion.go +++ b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta2/zz_generated.conversion.go @@ -46,6 +46,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*MetricListOptions)(nil), (*custommetrics.MetricListOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_MetricListOptions_To_custom_metrics_MetricListOptions(a.(*MetricListOptions), b.(*custommetrics.MetricListOptions), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*custommetrics.MetricListOptions)(nil), (*MetricListOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_custom_metrics_MetricListOptions_To_v1beta2_MetricListOptions(a.(*custommetrics.MetricListOptions), b.(*MetricListOptions), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*MetricValue)(nil), (*custommetrics.MetricValue)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_MetricValue_To_custom_metrics_MetricValue(a.(*MetricValue), b.(*custommetrics.MetricValue), scope) }); err != nil { @@ -91,6 +101,28 @@ func Convert_custom_metrics_MetricIdentifier_To_v1beta2_MetricIdentifier(in *cus return autoConvert_custom_metrics_MetricIdentifier_To_v1beta2_MetricIdentifier(in, out, s) } +func autoConvert_v1beta2_MetricListOptions_To_custom_metrics_MetricListOptions(in *MetricListOptions, out *custommetrics.MetricListOptions, s conversion.Scope) error { + out.LabelSelector = in.LabelSelector + out.MetricLabelSelector = in.MetricLabelSelector + return nil +} + +// Convert_v1beta2_MetricListOptions_To_custom_metrics_MetricListOptions is an autogenerated conversion function. +func Convert_v1beta2_MetricListOptions_To_custom_metrics_MetricListOptions(in *MetricListOptions, out *custommetrics.MetricListOptions, s conversion.Scope) error { + return autoConvert_v1beta2_MetricListOptions_To_custom_metrics_MetricListOptions(in, out, s) +} + +func autoConvert_custom_metrics_MetricListOptions_To_v1beta2_MetricListOptions(in *custommetrics.MetricListOptions, out *MetricListOptions, s conversion.Scope) error { + out.LabelSelector = in.LabelSelector + out.MetricLabelSelector = in.MetricLabelSelector + return nil +} + +// Convert_custom_metrics_MetricListOptions_To_v1beta2_MetricListOptions is an autogenerated conversion function. +func Convert_custom_metrics_MetricListOptions_To_v1beta2_MetricListOptions(in *custommetrics.MetricListOptions, out *MetricListOptions, s conversion.Scope) error { + return autoConvert_custom_metrics_MetricListOptions_To_v1beta2_MetricListOptions(in, out, s) +} + func autoConvert_v1beta2_MetricValue_To_custom_metrics_MetricValue(in *MetricValue, out *custommetrics.MetricValue, s conversion.Scope) error { // TODO: Inefficient conversion - can we improve it? if err := s.Convert(&in.DescribedObject, &out.DescribedObject, 0); err != nil { diff --git a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/zz_generated.deepcopy.go b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/zz_generated.deepcopy.go index 0c110bbf51..a80a69130f 100644 --- a/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/zz_generated.deepcopy.go @@ -46,6 +46,31 @@ func (in *MetricIdentifier) DeepCopy() *MetricIdentifier { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MetricListOptions) DeepCopyInto(out *MetricListOptions) { + *out = *in + out.TypeMeta = in.TypeMeta + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricListOptions. +func (in *MetricListOptions) DeepCopy() *MetricListOptions { + if in == nil { + return nil + } + out := new(MetricListOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MetricListOptions) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MetricValue) DeepCopyInto(out *MetricValue) { *out = *in diff --git a/staging/src/k8s.io/metrics/pkg/client/custom_metrics/BUILD b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/BUILD index 262d485c93..90c00c9919 100644 --- a/staging/src/k8s.io/metrics/pkg/client/custom_metrics/BUILD +++ b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/BUILD @@ -1,21 +1,29 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", srcs = [ - "client.go", + "converter.go", + "discovery.go", "interfaces.go", + "multi_client.go", + "versioned_client.go", ], importmap = "k8s.io/kubernetes/vendor/k8s.io/metrics/pkg/client/custom_metrics", importpath = "k8s.io/metrics/pkg/client/custom_metrics", visibility = ["//visibility:public"], deps = [ "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", + "//staging/src/k8s.io/client-go/discovery:go_default_library", "//staging/src/k8s.io/client-go/rest:go_default_library", "//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library", + "//staging/src/k8s.io/metrics/pkg/apis/custom_metrics:go_default_library", + "//staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1:go_default_library", "//staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta2:go_default_library", "//staging/src/k8s.io/metrics/pkg/client/custom_metrics/scheme:go_default_library", ], @@ -38,3 +46,18 @@ filegroup( tags = ["automanaged"], visibility = ["//visibility:public"], ) + +go_test( + name = "go_default_test", + srcs = ["util_test.go"], + embed = [":go_default_library"], + deps = [ + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//staging/src/k8s.io/metrics/pkg/apis/custom_metrics:go_default_library", + "//staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1:go_default_library", + "//staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta2:go_default_library", + "//vendor/github.com/stretchr/testify/require:go_default_library", + ], +) diff --git a/staging/src/k8s.io/metrics/pkg/client/custom_metrics/converter.go b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/converter.go new file mode 100644 index 0000000000..d83d467020 --- /dev/null +++ b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/converter.go @@ -0,0 +1,122 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 custom_metrics + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/client-go/rest" + + cmint "k8s.io/metrics/pkg/apis/custom_metrics" + cmv1beta1 "k8s.io/metrics/pkg/apis/custom_metrics/v1beta1" + cmv1beta2 "k8s.io/metrics/pkg/apis/custom_metrics/v1beta2" + "k8s.io/metrics/pkg/client/custom_metrics/scheme" +) + +var ( + // MetricVersions is the set of metric versions accepted by the converter. + MetricVersions = []schema.GroupVersion{ + cmv1beta2.SchemeGroupVersion, + cmv1beta1.SchemeGroupVersion, + cmint.SchemeGroupVersion, + } +) + +// MetricConverter knows how to convert between external MetricValue versions. +type MetricConverter struct { + scheme *runtime.Scheme + codecs serializer.CodecFactory + internalVersioner runtime.GroupVersioner +} + +// NewMetricConverter creates a MetricConverter which knows how to convert objects +// between different versions of the custom metrics api. +func NewMetricConverter() *MetricConverter { + return &MetricConverter{ + scheme: scheme.Scheme, + codecs: serializer.NewCodecFactory(scheme.Scheme), + internalVersioner: runtime.NewMultiGroupVersioner( + scheme.SchemeGroupVersion, + schema.GroupKind{Group: cmint.GroupName, Kind: ""}, + schema.GroupKind{Group: cmv1beta1.GroupName, Kind: ""}, + schema.GroupKind{Group: cmv1beta2.GroupName, Kind: ""}, + ), + } +} + +// Scheme returns the scheme used by this metric converter. +func (c *MetricConverter) Scheme() *runtime.Scheme { + return c.scheme +} + +// Codecs returns the codecs used by this metric converter +func (c *MetricConverter) Codecs() serializer.CodecFactory { + return c.codecs +} + +// ConvertListOptionsToVersion converts converts a set of MetricListOptions +// to the provided GroupVersion. +func (c *MetricConverter) ConvertListOptionsToVersion(opts *cmint.MetricListOptions, version schema.GroupVersion) (runtime.Object, error) { + paramObj, err := c.UnsafeConvertToVersionVia(opts, version) + if err != nil { + return nil, err + } + return paramObj, nil +} + +// ConvertResultToVersion converts a Result to the provided GroupVersion +func (c *MetricConverter) ConvertResultToVersion(res rest.Result, gv schema.GroupVersion) (runtime.Object, error) { + if err := res.Error(); err != nil { + return nil, err + } + + metricBytes, err := res.Raw() + if err != nil { + return nil, err + } + + decoder := c.codecs.UniversalDecoder(MetricVersions...) + rawMetricObj, err := runtime.Decode(decoder, metricBytes) + if err != nil { + return nil, err + } + + metricObj, err := c.UnsafeConvertToVersionVia(rawMetricObj, gv) + if err != nil { + return nil, err + } + return metricObj, nil +} + +// unsafeConvertToVersionVia is like Scheme.UnsafeConvertToVersion, but it does so via an internal version first. +// We use it here to work with the v1beta2 client internally, while preserving backwards compatibility for existing custom metrics adapters +func (c *MetricConverter) UnsafeConvertToVersionVia(obj runtime.Object, externalVersion schema.GroupVersion) (runtime.Object, error) { + objInt, err := c.scheme.UnsafeConvertToVersion(obj, schema.GroupVersion{Group: externalVersion.Group, Version: runtime.APIVersionInternal}) + if err != nil { + return nil, fmt.Errorf("failed to convert the given object to the internal version: %v", err) + } + + objExt, err := c.scheme.UnsafeConvertToVersion(objInt, externalVersion) + if err != nil { + return nil, fmt.Errorf("failed to convert the given object back to the external version: %v", err) + } + + return objExt, err +} diff --git a/staging/src/k8s.io/metrics/pkg/client/custom_metrics/discovery.go b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/discovery.go new file mode 100644 index 0000000000..7c9f8951bb --- /dev/null +++ b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/discovery.go @@ -0,0 +1,145 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 custom_metrics + +import ( + "fmt" + "sync" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" + + cmint "k8s.io/metrics/pkg/apis/custom_metrics" +) + +var ( + // metricVersionsToGV is the map of string group-versions + // accepted by the converter to group-version objects (so + // we don't have to re-parse) + metricVersionsToGV map[string]schema.GroupVersion +) + +func init() { + metricVersionsToGV = make(map[string]schema.GroupVersion) + for _, ver := range MetricVersions { + metricVersionsToGV[ver.String()] = ver + } +} + +// NewAvailableAPIsGetter creates an AvailableAPIsGetter that checks discovery +// to find the available versions of the custom metrics api. +func NewAvailableAPIsGetter(client discovery.DiscoveryInterface) AvailableAPIsGetter { + return &apiVersionsFromDiscovery{ + client: client, + } +} + +// apiVersionsFromDiscovery caches a preferred version of the custom metrics api. +type apiVersionsFromDiscovery struct { + client discovery.DiscoveryInterface + + // just cache the group directly since the discovery interface doesn't yet allow + // asking for a single API group's versions. + prefVersion *schema.GroupVersion + mu sync.RWMutex +} + +// fetchVersions fetches the versions, but doesn't try to invalidate on cache misses. +func (d *apiVersionsFromDiscovery) fetchVersions() (*metav1.APIGroup, error) { + // TODO(directxman12): amend the discovery interface to ask for a particular group (/apis/foo) + groups, err := d.client.ServerGroups() + if err != nil { + return nil, err + } + + // Determine the preferred version on the server by first finding the custom metrics group + var apiGroup *metav1.APIGroup + for _, group := range groups.Groups { + if group.Name == cmint.GroupName { + apiGroup = &group + break + } + } + + if apiGroup == nil { + return nil, fmt.Errorf("no custom metrics API (%s) registered", cmint.GroupName) + } + + return apiGroup, nil +} + +// chooseVersion sets a preferred version of the custom metrics api based on available versions. +func (d *apiVersionsFromDiscovery) chooseVersion(apiGroup *metav1.APIGroup) (schema.GroupVersion, error) { + var preferredVersion *schema.GroupVersion + if gv, present := metricVersionsToGV[apiGroup.PreferredVersion.GroupVersion]; present && len(apiGroup.PreferredVersion.GroupVersion) != 0 { + preferredVersion = &gv + } else { + for _, version := range apiGroup.Versions { + if gv, present := metricVersionsToGV[version.GroupVersion]; present { + preferredVersion = &gv + break + } + } + } + + if preferredVersion == nil { + return schema.GroupVersion{}, fmt.Errorf("no known available metric versions found") + } + return *preferredVersion, nil +} + +// PreferredVersion returns the current preferred version of the custom metrics api. +// If none is specified, it will use the first known one. +func (d *apiVersionsFromDiscovery) PreferredVersion() (schema.GroupVersion, error) { + d.mu.RLock() + if d.prefVersion != nil { + // if we've already got one, proceed with that + defer d.mu.RUnlock() + return *d.prefVersion, nil + } + d.mu.RUnlock() + + d.mu.Lock() + defer d.mu.Unlock() + + // double check, someone might have beaten us to it + if d.prefVersion != nil { + return *d.prefVersion, nil + } + + // populate our cache + groupInfo, err := d.fetchVersions() + if err != nil { + return schema.GroupVersion{}, err + } + prefVersion, err := d.chooseVersion(groupInfo) + if err != nil { + return schema.GroupVersion{}, err + } + + d.prefVersion = &prefVersion + return *d.prefVersion, nil +} + +// Invalidate refreshes the preferred version information. +func (d *apiVersionsFromDiscovery) Invalidate() { + d.mu.Lock() + defer d.mu.Unlock() + + d.prefVersion = nil +} diff --git a/staging/src/k8s.io/metrics/pkg/client/custom_metrics/multi_client.go b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/multi_client.go new file mode 100644 index 0000000000..cbc91a9d26 --- /dev/null +++ b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/multi_client.go @@ -0,0 +1,138 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 custom_metrics + +import ( + "sync" + "time" + + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/rest" + + "k8s.io/metrics/pkg/apis/custom_metrics/v1beta2" +) + +// AvailableAPIsGetter knows how to fetch and cache the preferred custom metrics API version, +// and invalidate that cache when asked. +type AvailableAPIsGetter interface { + PreferredVersion() (schema.GroupVersion, error) + Invalidate() +} + +// PeriodicallyInvalidate periodically invalidates the preferred version cache until +// told to stop. +func PeriodicallyInvalidate(cache AvailableAPIsGetter, interval time.Duration, stopCh <-chan struct{}) { + ticker := time.NewTicker(interval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + cache.Invalidate() + case <-stopCh: + break + } + } +} + +// NewForConfig creates a new custom metrics client which delegates to a client which +// uses the preferred api version. +func NewForConfig(baseConfig *rest.Config, mapper meta.RESTMapper, availableAPIs AvailableAPIsGetter) CustomMetricsClient { + return &multiClient{ + clients: make(map[schema.GroupVersion]CustomMetricsClient), + availableAPIs: availableAPIs, + + newClient: func(ver schema.GroupVersion) (CustomMetricsClient, error) { + return NewForVersionForConfig(rest.CopyConfig(baseConfig), mapper, ver) + }, + } +} + +// multiClient is a CustomMetricsClient that can work with *any* metrics API version. +type multiClient struct { + newClient func(schema.GroupVersion) (CustomMetricsClient, error) + clients map[schema.GroupVersion]CustomMetricsClient + availableAPIs AvailableAPIsGetter + mu sync.RWMutex +} + +// getPreferredClient returns a custom metrics client of the preferred api version. +func (c *multiClient) getPreferredClient() (CustomMetricsClient, error) { + pref, err := c.availableAPIs.PreferredVersion() + if err != nil { + return nil, err + } + + c.mu.RLock() + client, present := c.clients[pref] + c.mu.RUnlock() + if present { + return client, nil + } + + c.mu.Lock() + defer c.mu.Unlock() + client, err = c.newClient(pref) + if err != nil { + return nil, err + } + c.clients[pref] = client + + return client, nil +} + +func (c *multiClient) RootScopedMetrics() MetricsInterface { + return &multiClientInterface{clients: c} +} + +func (c *multiClient) NamespacedMetrics(namespace string) MetricsInterface { + return &multiClientInterface{ + clients: c, + namespace: &namespace, + } +} + +type multiClientInterface struct { + clients *multiClient + namespace *string +} + +func (m *multiClientInterface) GetForObject(groupKind schema.GroupKind, name string, metricName string, metricSelector labels.Selector) (*v1beta2.MetricValue, error) { + client, err := m.clients.getPreferredClient() + if err != nil { + return nil, err + } + if m.namespace == nil { + return client.RootScopedMetrics().GetForObject(groupKind, name, metricName, metricSelector) + } else { + return client.NamespacedMetrics(*m.namespace).GetForObject(groupKind, name, metricName, metricSelector) + } +} + +func (m *multiClientInterface) GetForObjects(groupKind schema.GroupKind, selector labels.Selector, metricName string, metricSelector labels.Selector) (*v1beta2.MetricValueList, error) { + client, err := m.clients.getPreferredClient() + if err != nil { + return nil, err + } + if m.namespace == nil { + return client.RootScopedMetrics().GetForObjects(groupKind, selector, metricName, metricSelector) + } else { + return client.NamespacedMetrics(*m.namespace).GetForObjects(groupKind, selector, metricName, metricSelector) + } +} diff --git a/staging/src/k8s.io/metrics/pkg/client/custom_metrics/scheme/BUILD b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/scheme/BUILD index 42e136a6ed..85e6e80a57 100644 --- a/staging/src/k8s.io/metrics/pkg/client/custom_metrics/scheme/BUILD +++ b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/scheme/BUILD @@ -11,6 +11,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", + "//staging/src/k8s.io/metrics/pkg/apis/custom_metrics:go_default_library", "//staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1:go_default_library", "//staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta2:go_default_library", ], diff --git a/staging/src/k8s.io/metrics/pkg/client/custom_metrics/scheme/register.go b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/scheme/register.go index 314415517a..9875f42f9c 100644 --- a/staging/src/k8s.io/metrics/pkg/client/custom_metrics/scheme/register.go +++ b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/scheme/register.go @@ -21,10 +21,15 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" serializer "k8s.io/apimachinery/pkg/runtime/serializer" + cmint "k8s.io/metrics/pkg/apis/custom_metrics" cmv1beta1 "k8s.io/metrics/pkg/apis/custom_metrics/v1beta1" cmv1beta2 "k8s.io/metrics/pkg/apis/custom_metrics/v1beta2" ) +const GroupName = cmv1beta1.GroupName + +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} + var Scheme = runtime.NewScheme() var Codecs = serializer.NewCodecFactory(Scheme) var ParameterCodec = runtime.NewParameterCodec(Scheme) @@ -49,6 +54,7 @@ func init() { // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. func AddToScheme(scheme *runtime.Scheme) { + cmint.AddToScheme(scheme) cmv1beta1.AddToScheme(scheme) cmv1beta2.AddToScheme(scheme) } diff --git a/staging/src/k8s.io/metrics/pkg/client/custom_metrics/util_test.go b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/util_test.go new file mode 100644 index 0000000000..5d5dd12ef9 --- /dev/null +++ b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/util_test.go @@ -0,0 +1,65 @@ +/* +Copyright 2018 The Kubernetes Authors. + +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 custom_metrics + +import ( + "testing" + + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + + cmint "k8s.io/metrics/pkg/apis/custom_metrics" + cmv1beta1 "k8s.io/metrics/pkg/apis/custom_metrics/v1beta1" + cmv1beta2 "k8s.io/metrics/pkg/apis/custom_metrics/v1beta2" +) + +func TestMetricConverter(t *testing.T) { + testCases := []struct { + name string + group schema.GroupVersion + expected runtime.Object + }{ + { + name: "Use custom metrics v1beta2", + group: cmv1beta2.SchemeGroupVersion, + expected: &cmv1beta2.MetricListOptions{ + TypeMeta: metav1.TypeMeta{Kind: "MetricListOptions", APIVersion: cmv1beta2.SchemeGroupVersion.String()}, + MetricLabelSelector: "foo", + }, + }, + { + name: "Use custom metrics v1beta1", + group: cmv1beta1.SchemeGroupVersion, + expected: &cmv1beta1.MetricListOptions{ + TypeMeta: metav1.TypeMeta{Kind: "MetricListOptions", APIVersion: cmv1beta1.SchemeGroupVersion.String()}, + MetricLabelSelector: "foo", + }, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + metricConverter := NewMetricConverter() + opts := &cmint.MetricListOptions{MetricLabelSelector: "foo"} + res, err := metricConverter.ConvertListOptionsToVersion(opts, test.group) + require.NoError(t, err) + require.Equal(t, test.expected, res) + }) + } +} diff --git a/staging/src/k8s.io/metrics/pkg/client/custom_metrics/client.go b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/versioned_client.go similarity index 61% rename from staging/src/k8s.io/metrics/pkg/client/custom_metrics/client.go rename to staging/src/k8s.io/metrics/pkg/client/custom_metrics/versioned_client.go index d399ca059c..f26785bf8e 100644 --- a/staging/src/k8s.io/metrics/pkg/client/custom_metrics/client.go +++ b/staging/src/k8s.io/metrics/pkg/client/custom_metrics/versioned_client.go @@ -25,22 +25,35 @@ import ( serializer "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/client-go/rest" "k8s.io/client-go/util/flowcontrol" + + cmint "k8s.io/metrics/pkg/apis/custom_metrics" + "k8s.io/metrics/pkg/apis/custom_metrics/v1beta1" "k8s.io/metrics/pkg/apis/custom_metrics/v1beta2" "k8s.io/metrics/pkg/client/custom_metrics/scheme" ) +var ( + codecs = serializer.NewCodecFactory(scheme.Scheme) + versionConverter = NewMetricConverter() +) + type customMetricsClient struct { - client rest.Interface - mapper meta.RESTMapper + client rest.Interface + version schema.GroupVersion + mapper meta.RESTMapper } -func New(client rest.Interface) CustomMetricsClient { +// NewForVersion returns a new CustomMetricsClient for a particular api version. +func NewForVersion(client rest.Interface, mapper meta.RESTMapper, version schema.GroupVersion) CustomMetricsClient { return &customMetricsClient{ - client: client, + client: client, + version: version, + mapper: mapper, } } -func NewForConfig(c *rest.Config) (CustomMetricsClient, error) { +// NewForVersionForConfig returns a new CustomMetricsClient for a particular api version and base configuration. +func NewForVersionForConfig(c *rest.Config, mapper meta.RESTMapper, version schema.GroupVersion) (CustomMetricsClient, error) { configShallowCopy := *c if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) @@ -49,7 +62,7 @@ func NewForConfig(c *rest.Config) (CustomMetricsClient, error) { if configShallowCopy.UserAgent == "" { configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent() } - configShallowCopy.GroupVersion = &v1beta2.SchemeGroupVersion + configShallowCopy.GroupVersion = &version configShallowCopy.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} client, err := rest.RESTClientFor(&configShallowCopy) @@ -57,24 +70,7 @@ func NewForConfig(c *rest.Config) (CustomMetricsClient, error) { return nil, err } - return New(client), nil -} - -func NewForConfigOrDie(c *rest.Config) CustomMetricsClient { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// NewForMapper constructs the client with a RESTMapper, which allows more -// accurate translation from GroupVersionKind to GroupVersionResource. -func NewForMapper(client rest.Interface, mapper meta.RESTMapper) CustomMetricsClient { - return &customMetricsClient{ - client: client, - mapper: mapper, - } + return NewForVersion(client, mapper, version), nil } func (c *customMetricsClient) RootScopedMetrics() MetricsInterface { @@ -88,16 +84,8 @@ func (c *customMetricsClient) NamespacedMetrics(namespace string) MetricsInterfa } } +// qualResourceForKind returns the string format of a qualified group resource for the specified GroupKind func (c *customMetricsClient) qualResourceForKind(groupKind schema.GroupKind) (string, error) { - if c.mapper == nil { - // the version doesn't matter - gvk := groupKind.WithVersion("") - gvr, _ := meta.UnsafeGuessKindToResource(gvk) - gr := gvr.GroupResource() - return gr.String(), nil - } - - // use the mapper if it's available mapping, err := c.mapper.RESTMapping(groupKind) if err != nil { return "", fmt.Errorf("unable to map kind %s to resource: %v", groupKind.String(), err) @@ -112,21 +100,26 @@ type rootScopedMetrics struct { } func (m *rootScopedMetrics) getForNamespace(namespace string, metricName string, metricSelector labels.Selector) (*v1beta2.MetricValue, error) { - res := &v1beta2.MetricValueList{} - err := m.client.client.Get(). - Resource("metrics"). - Namespace(namespace). - Name(metricName). - VersionedParams(&v1beta2.MetricListOptions{ - MetricLabelSelector: metricSelector.String(), - }, scheme.ParameterCodec). - Do(). - Into(res) - + params, err := versionConverter.ConvertListOptionsToVersion(&cmint.MetricListOptions{ + MetricLabelSelector: metricSelector.String(), + }, m.client.version) if err != nil { return nil, err } + result := m.client.client.Get(). + Resource("metrics"). + Namespace(namespace). + Name(metricName). + VersionedParams(params, scheme.ParameterCodec). + Do() + + metricObj, err := versionConverter.ConvertResultToVersion(result, v1beta2.SchemeGroupVersion) + if err != nil { + return nil, err + } + + res := metricObj.(*v1beta2.MetricValueList) if len(res.Items) != 1 { return nil, fmt.Errorf("the custom metrics API server returned %v results when we asked for exactly one", len(res.Items)) } @@ -145,21 +138,26 @@ func (m *rootScopedMetrics) GetForObject(groupKind schema.GroupKind, name string return nil, err } - res := &v1beta2.MetricValueList{} - err = m.client.client.Get(). - Resource(resourceName). - Name(name). - SubResource(metricName). - VersionedParams(&v1beta2.MetricListOptions{ - MetricLabelSelector: metricSelector.String(), - }, scheme.ParameterCodec). - Do(). - Into(res) - + params, err := versionConverter.ConvertListOptionsToVersion(&cmint.MetricListOptions{ + MetricLabelSelector: metricSelector.String(), + }, m.client.version) if err != nil { return nil, err } + result := m.client.client.Get(). + Resource(resourceName). + Name(name). + SubResource(metricName). + VersionedParams(params, scheme.ParameterCodec). + Do() + + metricObj, err := versionConverter.ConvertResultToVersion(result, v1beta2.SchemeGroupVersion) + if err != nil { + return nil, err + } + + res := metricObj.(*v1beta2.MetricValueList) if len(res.Items) != 1 { return nil, fmt.Errorf("the custom metrics API server returned %v results when we asked for exactly one", len(res.Items)) } @@ -178,22 +176,27 @@ func (m *rootScopedMetrics) GetForObjects(groupKind schema.GroupKind, selector l return nil, err } - res := &v1beta2.MetricValueList{} - err = m.client.client.Get(). - Resource(resourceName). - Name(v1beta2.AllObjects). - SubResource(metricName). - VersionedParams(&v1beta2.MetricListOptions{ - LabelSelector: selector.String(), - MetricLabelSelector: metricSelector.String(), - }, scheme.ParameterCodec). - Do(). - Into(res) - + params, err := versionConverter.ConvertListOptionsToVersion(&cmint.MetricListOptions{ + LabelSelector: selector.String(), + MetricLabelSelector: metricSelector.String(), + }, m.client.version) if err != nil { return nil, err } + result := m.client.client.Get(). + Resource(resourceName). + Name(v1beta1.AllObjects). + SubResource(metricName). + VersionedParams(params, scheme.ParameterCodec). + Do() + + metricObj, err := versionConverter.ConvertResultToVersion(result, v1beta2.SchemeGroupVersion) + if err != nil { + return nil, err + } + + res := metricObj.(*v1beta2.MetricValueList) return res, nil } @@ -208,22 +211,27 @@ func (m *namespacedMetrics) GetForObject(groupKind schema.GroupKind, name string return nil, err } - res := &v1beta2.MetricValueList{} - err = m.client.client.Get(). - Resource(resourceName). - Namespace(m.namespace). - Name(name). - SubResource(metricName). - VersionedParams(&v1beta2.MetricListOptions{ - MetricLabelSelector: metricSelector.String(), - }, scheme.ParameterCodec). - Do(). - Into(res) - + params, err := versionConverter.ConvertListOptionsToVersion(&cmint.MetricListOptions{ + MetricLabelSelector: metricSelector.String(), + }, m.client.version) if err != nil { return nil, err } + result := m.client.client.Get(). + Resource(resourceName). + Namespace(m.namespace). + Name(name). + SubResource(metricName). + VersionedParams(params, scheme.ParameterCodec). + Do() + + metricObj, err := versionConverter.ConvertResultToVersion(result, v1beta2.SchemeGroupVersion) + if err != nil { + return nil, err + } + + res := metricObj.(*v1beta2.MetricValueList) if len(res.Items) != 1 { return nil, fmt.Errorf("the custom metrics API server returned %v results when we asked for exactly one", len(res.Items)) } @@ -237,22 +245,27 @@ func (m *namespacedMetrics) GetForObjects(groupKind schema.GroupKind, selector l return nil, err } - res := &v1beta2.MetricValueList{} - err = m.client.client.Get(). - Resource(resourceName). - Namespace(m.namespace). - Name(v1beta2.AllObjects). - SubResource(metricName). - VersionedParams(&v1beta2.MetricListOptions{ - LabelSelector: selector.String(), - MetricLabelSelector: metricSelector.String(), - }, scheme.ParameterCodec). - Do(). - Into(res) - + params, err := versionConverter.ConvertListOptionsToVersion(&cmint.MetricListOptions{ + LabelSelector: selector.String(), + MetricLabelSelector: metricSelector.String(), + }, m.client.version) if err != nil { return nil, err } + result := m.client.client.Get(). + Resource(resourceName). + Namespace(m.namespace). + Name(v1beta1.AllObjects). + SubResource(metricName). + VersionedParams(params, scheme.ParameterCodec). + Do() + + metricObj, err := versionConverter.ConvertResultToVersion(result, v1beta2.SchemeGroupVersion) + if err != nil { + return nil, err + } + + res := metricObj.(*v1beta2.MetricValueList) return res, nil } diff --git a/test/e2e/instrumentation/monitoring/BUILD b/test/e2e/instrumentation/monitoring/BUILD index a712842964..9ccc20d813 100644 --- a/test/e2e/instrumentation/monitoring/BUILD +++ b/test/e2e/instrumentation/monitoring/BUILD @@ -29,6 +29,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/selection:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/client-go/discovery:go_default_library", + "//staging/src/k8s.io/client-go/discovery/cached:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library", "//staging/src/k8s.io/metrics/pkg/client/custom_metrics:go_default_library", "//staging/src/k8s.io/metrics/pkg/client/external_metrics:go_default_library", diff --git a/test/e2e/instrumentation/monitoring/custom_metrics_stackdriver.go b/test/e2e/instrumentation/monitoring/custom_metrics_stackdriver.go index 6f586b6219..36bed14bc6 100644 --- a/test/e2e/instrumentation/monitoring/custom_metrics_stackdriver.go +++ b/test/e2e/instrumentation/monitoring/custom_metrics_stackdriver.go @@ -29,11 +29,13 @@ import ( gcm "google.golang.org/api/monitoring/v3" "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/selection" "k8s.io/client-go/discovery" "k8s.io/kubernetes/test/e2e/framework" + cmv1beta1 "k8s.io/metrics/pkg/apis/custom_metrics/v1beta1" customclient "k8s.io/metrics/pkg/client/custom_metrics" externalclient "k8s.io/metrics/pkg/client/external_metrics" ) @@ -57,8 +59,10 @@ var _ = instrumentation.SIGDescribe("Stackdriver Monitoring", func() { if err != nil { framework.Failf("Failed to load config: %s", err) } - customMetricsClient := customclient.NewForConfigOrDie(config) discoveryClient := discovery.NewDiscoveryClientForConfigOrDie(config) + apiVersionsGetter := customclient.NewAvailableAPIsGetter(discoveryClient) + restMapper := meta.NewDefaultRESTMapper([]schema.GroupVersion{cmv1beta1.SchemeGroupVersion}) + customMetricsClient := customclient.NewForConfig(config, restMapper, apiVersionsGetter) testCustomMetrics(f, kubeClient, customMetricsClient, discoveryClient, AdapterForOldResourceModel) }) @@ -68,8 +72,10 @@ var _ = instrumentation.SIGDescribe("Stackdriver Monitoring", func() { if err != nil { framework.Failf("Failed to load config: %s", err) } - customMetricsClient := customclient.NewForConfigOrDie(config) discoveryClient := discovery.NewDiscoveryClientForConfigOrDie(config) + apiVersionsGetter := customclient.NewAvailableAPIsGetter(discoveryClient) + restMapper := meta.NewDefaultRESTMapper([]schema.GroupVersion{cmv1beta1.SchemeGroupVersion}) + customMetricsClient := customclient.NewForConfig(config, restMapper, apiVersionsGetter) testCustomMetrics(f, kubeClient, customMetricsClient, discoveryClient, AdapterForNewResourceModel) }) From 76bd48b1405b2356428615a870b4e871b1d3d259 Mon Sep 17 00:00:00 2001 From: Solly Ross Date: Wed, 5 Sep 2018 18:46:50 -0400 Subject: [PATCH 2/2] Fix up potentially empty fields in HPA v2beta2 There were a few cases where fields that should have been marked optional were not. This was causing weird validation issues, but is now fixed. --- api/openapi-spec/swagger.json | 5 ----- api/swagger-spec/autoscaling_v2beta2.json | 5 ----- .../autoscaling/v2beta2/definitions.html | 6 +++--- .../api/autoscaling/v2beta2/generated.proto | 6 ++++++ .../k8s.io/api/autoscaling/v2beta2/types.go | 19 ++++++++++++------- staging/src/k8s.io/metrics/Godeps/Godeps.json | 4 ---- test/e2e/instrumentation/monitoring/BUILD | 3 ++- 7 files changed, 23 insertions(+), 25 deletions(-) diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 53d1c1cc53..2f52c9eb9b 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -79039,11 +79039,6 @@ }, "io.k8s.api.autoscaling.v2beta2.MetricValueStatus": { "description": "MetricValueStatus holds the current value for a metric", - "required": [ - "value", - "averageValue", - "averageUtilization" - ], "properties": { "averageUtilization": { "description": "currentAverageUtilization is the current value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods.", diff --git a/api/swagger-spec/autoscaling_v2beta2.json b/api/swagger-spec/autoscaling_v2beta2.json index 82faa15867..626fbb33c0 100644 --- a/api/swagger-spec/autoscaling_v2beta2.json +++ b/api/swagger-spec/autoscaling_v2beta2.json @@ -1840,11 +1840,6 @@ "v2beta2.MetricValueStatus": { "id": "v2beta2.MetricValueStatus", "description": "MetricValueStatus holds the current value for a metric", - "required": [ - "value", - "averageValue", - "averageUtilization" - ], "properties": { "value": { "type": "string", diff --git a/docs/api-reference/autoscaling/v2beta2/definitions.html b/docs/api-reference/autoscaling/v2beta2/definitions.html index 9264640854..28559ba353 100755 --- a/docs/api-reference/autoscaling/v2beta2/definitions.html +++ b/docs/api-reference/autoscaling/v2beta2/definitions.html @@ -1002,21 +1002,21 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }

value

value is the current value of the metric (as a quantity).

-

true

+

false

string

averageValue

averageValue is the current value of the average of the metric across all relevant pods (as a quantity)

-

true

+

false

string

averageUtilization

currentAverageUtilization is the current value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods.

-

true

+

false

integer (int32)

diff --git a/staging/src/k8s.io/api/autoscaling/v2beta2/generated.proto b/staging/src/k8s.io/api/autoscaling/v2beta2/generated.proto index 38f099ae74..b4e4c95a3b 100644 --- a/staging/src/k8s.io/api/autoscaling/v2beta2/generated.proto +++ b/staging/src/k8s.io/api/autoscaling/v2beta2/generated.proto @@ -259,31 +259,37 @@ message MetricTarget { optional string type = 1; // value is the target value of the metric (as a quantity). + // +optional optional k8s.io.apimachinery.pkg.api.resource.Quantity value = 2; // averageValue is the target value of the average of the // metric across all relevant pods (as a quantity) + // +optional optional k8s.io.apimachinery.pkg.api.resource.Quantity averageValue = 3; // averageUtilization is the target value of the average of the // resource metric across all relevant pods, represented as a percentage of // the requested value of the resource for the pods. // Currently only valid for Resource metric source type + // +optional optional int32 averageUtilization = 4; } // MetricValueStatus holds the current value for a metric message MetricValueStatus { // value is the current value of the metric (as a quantity). + // +optional optional k8s.io.apimachinery.pkg.api.resource.Quantity value = 1; // averageValue is the current value of the average of the // metric across all relevant pods (as a quantity) + // +optional optional k8s.io.apimachinery.pkg.api.resource.Quantity averageValue = 2; // currentAverageUtilization is the current value of the average of the // resource metric across all relevant pods, represented as a percentage of // the requested value of the resource for the pods. + // +optional optional int32 averageUtilization = 3; } diff --git a/staging/src/k8s.io/api/autoscaling/v2beta2/types.go b/staging/src/k8s.io/api/autoscaling/v2beta2/types.go index e873971f3f..2d33795374 100644 --- a/staging/src/k8s.io/api/autoscaling/v2beta2/types.go +++ b/staging/src/k8s.io/api/autoscaling/v2beta2/types.go @@ -200,16 +200,18 @@ type MetricTarget struct { // type represents whether the metric type is Utilization, Value, or AverageValue Type MetricTargetType `json:"type" protobuf:"bytes,1,name=type"` // value is the target value of the metric (as a quantity). - Value *resource.Quantity `json:"value,omitempty" protobuf:"bytes,2,name=value"` + // +optional + Value *resource.Quantity `json:"value,omitempty" protobuf:"bytes,2,opt,name=value"` // averageValue is the target value of the average of the // metric across all relevant pods (as a quantity) - AverageValue *resource.Quantity `json:"averageValue,omitempty" protobuf:"bytes,3,name=averageValue"` - + // +optional + AverageValue *resource.Quantity `json:"averageValue,omitempty" protobuf:"bytes,3,opt,name=averageValue"` // averageUtilization is the target value of the average of the // resource metric across all relevant pods, represented as a percentage of // the requested value of the resource for the pods. // Currently only valid for Resource metric source type - AverageUtilization *int32 `json:"averageUtilization,omitempty" protobuf:"bytes,4,name=averageUtilization"` + // +optional + AverageUtilization *int32 `json:"averageUtilization,omitempty" protobuf:"bytes,4,opt,name=averageUtilization"` } // MetricTargetType specifies the type of metric being targeted, and should be either @@ -364,14 +366,17 @@ type ExternalMetricStatus struct { // MetricValueStatus holds the current value for a metric type MetricValueStatus struct { // value is the current value of the metric (as a quantity). - Value *resource.Quantity `json:"value" protobuf:"bytes,1,name=value"` + // +optional + Value *resource.Quantity `json:"value,omitempty" protobuf:"bytes,1,opt,name=value"` // averageValue is the current value of the average of the // metric across all relevant pods (as a quantity) - AverageValue *resource.Quantity `json:"averageValue" protobuf:"bytes,2,name=averageValue"` + // +optional + AverageValue *resource.Quantity `json:"averageValue,omitempty" protobuf:"bytes,2,opt,name=averageValue"` // currentAverageUtilization is the current value of the average of the // resource metric across all relevant pods, represented as a percentage of // the requested value of the resource for the pods. - AverageUtilization *int32 `json:"averageUtilization" protobuf:"bytes,3,name=averageUtilization"` + // +optional + AverageUtilization *int32 `json:"averageUtilization,omitempty" protobuf:"bytes,3,opt,name=averageUtilization"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/staging/src/k8s.io/metrics/Godeps/Godeps.json b/staging/src/k8s.io/metrics/Godeps/Godeps.json index 59a8e39a3d..5a2e5ba459 100644 --- a/staging/src/k8s.io/metrics/Godeps/Godeps.json +++ b/staging/src/k8s.io/metrics/Godeps/Godeps.json @@ -450,10 +450,6 @@ "ImportPath": "k8s.io/client-go/discovery", "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, - { - "ImportPath": "k8s.io/client-go/discovery/cached", - "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - }, { "ImportPath": "k8s.io/client-go/discovery/fake", "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" diff --git a/test/e2e/instrumentation/monitoring/BUILD b/test/e2e/instrumentation/monitoring/BUILD index 9ccc20d813..8f647a964c 100644 --- a/test/e2e/instrumentation/monitoring/BUILD +++ b/test/e2e/instrumentation/monitoring/BUILD @@ -22,6 +22,7 @@ go_library( "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/extensions/v1beta1:go_default_library", "//staging/src/k8s.io/api/rbac/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library", @@ -29,8 +30,8 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/selection:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/client-go/discovery:go_default_library", - "//staging/src/k8s.io/client-go/discovery/cached:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library", + "//staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1:go_default_library", "//staging/src/k8s.io/metrics/pkg/client/custom_metrics:go_default_library", "//staging/src/k8s.io/metrics/pkg/client/external_metrics:go_default_library", "//test/e2e/common:go_default_library",