diff --git a/cmd/kube-controller-manager/app/BUILD b/cmd/kube-controller-manager/app/BUILD index 6bcd3baa947..d5dcfcc0031 100644 --- a/cmd/kube-controller-manager/app/BUILD +++ b/cmd/kube-controller-manager/app/BUILD @@ -124,6 +124,7 @@ go_library( "//vendor/k8s.io/client-go/kubernetes:go_default_library", "//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", + "//vendor/k8s.io/client-go/scale:go_default_library", "//vendor/k8s.io/client-go/tools/clientcmd:go_default_library", "//vendor/k8s.io/client-go/tools/leaderelection:go_default_library", "//vendor/k8s.io/client-go/tools/leaderelection/resourcelock:go_default_library", diff --git a/cmd/kube-controller-manager/app/autoscaling.go b/cmd/kube-controller-manager/app/autoscaling.go index 5af911d7adc..827d767b14b 100644 --- a/cmd/kube-controller-manager/app/autoscaling.go +++ b/cmd/kube-controller-manager/app/autoscaling.go @@ -21,7 +21,12 @@ limitations under the License. package app import ( + apimeta "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" + discocache "k8s.io/client-go/discovery/cached" // Saturday Night Fever + "k8s.io/client-go/dynamic" + "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_generated/clientset/typed/metrics/v1beta1" @@ -63,16 +68,33 @@ func startHPAControllerWithLegacyClient(ctx ControllerContext) (bool, error) { } func startHPAControllerWithMetricsClient(ctx ControllerContext, metricsClient metrics.MetricsClient) (bool, error) { + hpaClientGoClient := ctx.ClientBuilder.ClientGoClientOrDie("horizontal-pod-autoscaler") hpaClient := ctx.ClientBuilder.ClientOrDie("horizontal-pod-autoscaler") + hpaClientConfig := ctx.ClientBuilder.ConfigOrDie("horizontal-pod-autoscaler") + + // TODO: we need something like deferred discovery REST mapper that calls invalidate + // on cache misses. + cachedDiscovery := discocache.NewMemCacheClient(hpaClientGoClient.Discovery()) + restMapper := discovery.NewDeferredDiscoveryRESTMapper(cachedDiscovery, apimeta.InterfacesForUnstructured) + restMapper.Reset() + // we don't use cached discovery because DiscoveryScaleKindResolver does its own caching, + // so we want to re-fetch every time when we actually ask for it + scaleKindResolver := scale.NewDiscoveryScaleKindResolver(hpaClientGoClient.Discovery()) + scaleClient, err := scale.NewForConfig(hpaClientConfig, restMapper, dynamic.LegacyAPIPathResolverFunc, scaleKindResolver) + if err != nil { + return false, err + } + replicaCalc := podautoscaler.NewReplicaCalculator( metricsClient, hpaClient.Core(), ctx.Options.HorizontalPodAutoscalerTolerance, ) go podautoscaler.NewHorizontalController( - ctx.ClientBuilder.ClientGoClientOrDie("horizontal-pod-autoscaler").Core(), - hpaClient.Extensions(), + hpaClientGoClient.Core(), + scaleClient, hpaClient.Autoscaling(), + restMapper, replicaCalc, ctx.InformerFactory.Autoscaling().V1().HorizontalPodAutoscalers(), ctx.Options.HorizontalPodAutoscalerSyncPeriod.Duration, diff --git a/federation/pkg/kubefed/init/init_test.go b/federation/pkg/kubefed/init/init_test.go index 6db8e70110a..bc5cf61ea7e 100644 --- a/federation/pkg/kubefed/init/init_test.go +++ b/federation/pkg/kubefed/init/init_test.go @@ -1184,7 +1184,7 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na tf.ClientConfig = kubefedtesting.DefaultClientConfig() tf.TmpDir = tmpDirPath tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/federation/pkg/kubefed/join_test.go b/federation/pkg/kubefed/join_test.go index b7c36fd2bfb..e55312e3995 100644 --- a/federation/pkg/kubefed/join_test.go +++ b/federation/pkg/kubefed/join_test.go @@ -231,7 +231,7 @@ func testJoinFederationFactory(clusterName, secretName, server string, isRBACAPI codec := testapi.Federation.Codec() ns := dynamic.ContentConfig().NegotiatedSerializer tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -365,7 +365,7 @@ func fakeJoinHostFactory(clusterName, clusterCtx, secretName, server, token, dns ns := dynamic.ContentConfig().NegotiatedSerializer tf.ClientConfig = kubefedtesting.DefaultClientConfig() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -526,7 +526,7 @@ func fakeJoinTargetClusterFactory(clusterName, clusterCtx, dnsProvider, tmpDirPa tf.TmpDir = tmpDirPath tf.ClientConfig = kubefedtesting.DefaultClientConfig() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m, r := req.URL.Path, req.Method, isRBACAPIAvailable; { diff --git a/federation/pkg/kubefed/unjoin_test.go b/federation/pkg/kubefed/unjoin_test.go index ae8509b2fd6..3ff19a1de06 100644 --- a/federation/pkg/kubefed/unjoin_test.go +++ b/federation/pkg/kubefed/unjoin_test.go @@ -185,9 +185,8 @@ func testUnjoinFederationFactory(name, server, secret string) cmdutil.Factory { tf.ClientConfig = kubefedtesting.DefaultClientConfig() ns := serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: runtime.NewCodec(f.JSONEncoder(), legacyscheme.Codecs.UniversalDecoder(fedv1beta1.SchemeGroupVersion))}) tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie("federation").GroupVersion, NegotiatedSerializer: ns, - GroupName: "federation", Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { case strings.HasPrefix(p, urlPrefix): @@ -242,7 +241,7 @@ func fakeUnjoinHostFactory(clusterName string) cmdutil.Factory { ns := dynamic.ContentConfig().NegotiatedSerializer tf.ClientConfig = kubefedtesting.DefaultClientConfig() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/hack/.golint_failures b/hack/.golint_failures index 3b4b1cea0a1..0d6a492b5dc 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -713,6 +713,12 @@ staging/src/k8s.io/client-go/plugin/pkg/auth/authenticator/token/oidc/testing staging/src/k8s.io/client-go/plugin/pkg/client/auth/oidc staging/src/k8s.io/client-go/rest staging/src/k8s.io/client-go/rest/fake +staging/src/k8s.io/client-go/scale +staging/src/k8s.io/client-go/scale/fake +staging/src/k8s.io/client-go/scale/scheme +staging/src/k8s.io/client-go/scale/scheme/autoscalingv1 +staging/src/k8s.io/client-go/scale/scheme/extensionsint +staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1 staging/src/k8s.io/client-go/testing staging/src/k8s.io/client-go/tools/cache staging/src/k8s.io/client-go/tools/cache/testing diff --git a/hack/make-rules/test.sh b/hack/make-rules/test.sh index 151060ed8b0..c7b5a1481a8 100755 --- a/hack/make-rules/test.sh +++ b/hack/make-rules/test.sh @@ -289,7 +289,7 @@ runTests() { ${KUBE_RACE} ${KUBE_TIMEOUT} "${@}" \ "${testargs[@]:+${testargs[@]}}" \ | tee ${junit_filename_prefix:+"${junit_filename_prefix}.stdout"} \ - | grep "${go_test_grep_pattern}" && rc=$? || rc=$? + | grep --binary-files=text "${go_test_grep_pattern}" && rc=$? || rc=$? produceJUnitXMLReport "${junit_filename_prefix}" return ${rc} fi diff --git a/pkg/api/testing/serialization_test.go b/pkg/api/testing/serialization_test.go index cc97b76e246..4106f0209a6 100644 --- a/pkg/api/testing/serialization_test.go +++ b/pkg/api/testing/serialization_test.go @@ -104,6 +104,15 @@ func TestSetControllerConversion(t *testing.T) { fuzzInternalObject(t, extGroup.InternalGroupVersion(), rs, rand.Int63()) + // explicitly set the selector to something that is convertible to old-style selectors + // (since normally we'll fuzz the selectors with things that aren't convertible) + rs.Spec.Selector = &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "foo": "bar", + "baz": "quux", + }, + } + t.Logf("rs._internal.extensions -> rs.v1beta1.extensions") data, err := runtime.Encode(extGroup.Codec(), rs) if err != nil { diff --git a/pkg/apis/extensions/fuzzer/BUILD b/pkg/apis/extensions/fuzzer/BUILD index 4f4bf9cce0f..712d0f4e681 100644 --- a/pkg/apis/extensions/fuzzer/BUILD +++ b/pkg/apis/extensions/fuzzer/BUILD @@ -12,7 +12,6 @@ go_library( deps = [ "//pkg/apis/extensions:go_default_library", "//vendor/github.com/google/gofuzz:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", ], diff --git a/pkg/apis/extensions/fuzzer/fuzzer.go b/pkg/apis/extensions/fuzzer/fuzzer.go index f8f3b3d374a..006d04f9f9c 100644 --- a/pkg/apis/extensions/fuzzer/fuzzer.go +++ b/pkg/apis/extensions/fuzzer/fuzzer.go @@ -21,7 +21,6 @@ import ( fuzz "github.com/google/gofuzz" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/kubernetes/pkg/apis/extensions" @@ -83,25 +82,6 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { } psp.FSGroup.Rule = fsGroupRules[c.Rand.Intn(len(fsGroupRules))] }, - func(s *extensions.Scale, c fuzz.Continue) { - c.FuzzNoCustom(s) // fuzz self without calling this function again - // TODO: Implement a fuzzer to generate valid keys, values and operators for - // selector requirements. - if s.Status.Selector != nil { - s.Status.Selector = &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "testlabelkey": "testlabelval", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "testkey", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"val1", "val2", "val3"}, - }, - }, - } - } - }, func(j *extensions.DaemonSetSpec, c fuzz.Continue) { c.FuzzNoCustom(j) // fuzz self without calling this function again rhl := int32(c.Rand.Int31()) diff --git a/pkg/controller/podautoscaler/BUILD b/pkg/controller/podautoscaler/BUILD index 2231e891453..b0fa8efaa38 100644 --- a/pkg/controller/podautoscaler/BUILD +++ b/pkg/controller/podautoscaler/BUILD @@ -24,9 +24,9 @@ go_library( "//vendor/k8s.io/api/autoscaling/v1:go_default_library", "//vendor/k8s.io/api/autoscaling/v2beta1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", - "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", @@ -39,8 +39,8 @@ go_library( "//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library", "//vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v1:go_default_library", "//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library", - "//vendor/k8s.io/client-go/kubernetes/typed/extensions/v1beta1:go_default_library", "//vendor/k8s.io/client-go/listers/autoscaling/v1:go_default_library", + "//vendor/k8s.io/client-go/scale:go_default_library", "//vendor/k8s.io/client-go/tools/cache:go_default_library", "//vendor/k8s.io/client-go/tools/record:go_default_library", "//vendor/k8s.io/client-go/util/workqueue:go_default_library", @@ -58,6 +58,7 @@ go_test( importpath = "k8s.io/kubernetes/pkg/controller/podautoscaler", library = ":go_default_library", deps = [ + "//pkg/api/install:go_default_library", "//pkg/api/legacyscheme:go_default_library", "//pkg/apis/autoscaling:go_default_library", "//pkg/apis/autoscaling/install:go_default_library", @@ -69,15 +70,16 @@ go_test( "//vendor/k8s.io/api/autoscaling/v1:go_default_library", "//vendor/k8s.io/api/autoscaling/v2beta1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", - "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/client-go/informers:go_default_library", "//vendor/k8s.io/client-go/kubernetes/fake:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", + "//vendor/k8s.io/client-go/scale/fake:go_default_library", "//vendor/k8s.io/client-go/testing:go_default_library", "//vendor/k8s.io/heapster/metrics/api/v1/types:go_default_library", "//vendor/k8s.io/metrics/pkg/apis/custom_metrics/v1beta1:go_default_library", diff --git a/pkg/controller/podautoscaler/horizontal.go b/pkg/controller/podautoscaler/horizontal.go index 24c596c585c..b714aa077e4 100644 --- a/pkg/controller/podautoscaler/horizontal.go +++ b/pkg/controller/podautoscaler/horizontal.go @@ -25,9 +25,9 @@ import ( autoscalingv1 "k8s.io/api/autoscaling/v1" autoscalingv2 "k8s.io/api/autoscaling/v2beta1" "k8s.io/api/core/v1" - extensions "k8s.io/api/extensions/v1beta1" apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" + apimeta "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -39,8 +39,8 @@ import ( "k8s.io/client-go/kubernetes/scheme" autoscalingclient "k8s.io/client-go/kubernetes/typed/autoscaling/v1" v1core "k8s.io/client-go/kubernetes/typed/core/v1" - extensionsclient "k8s.io/client-go/kubernetes/typed/extensions/v1beta1" autoscalinglisters "k8s.io/client-go/listers/autoscaling/v1" + scaleclient "k8s.io/client-go/scale" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" "k8s.io/client-go/util/workqueue" @@ -57,8 +57,9 @@ var ( // in the system with the actual deployments/replication controllers they // control. type HorizontalController struct { - scaleNamespacer extensionsclient.ScalesGetter + scaleNamespacer scaleclient.ScalesGetter hpaNamespacer autoscalingclient.HorizontalPodAutoscalersGetter + mapper apimeta.RESTMapper replicaCalc *ReplicaCalculator eventRecorder record.EventRecorder @@ -78,8 +79,9 @@ type HorizontalController struct { // NewHorizontalController creates a new HorizontalController. func NewHorizontalController( evtNamespacer v1core.EventsGetter, - scaleNamespacer extensionsclient.ScalesGetter, + scaleNamespacer scaleclient.ScalesGetter, hpaNamespacer autoscalingclient.HorizontalPodAutoscalersGetter, + mapper apimeta.RESTMapper, replicaCalc *ReplicaCalculator, hpaInformer autoscalinginformers.HorizontalPodAutoscalerInformer, resyncPeriod time.Duration, @@ -100,7 +102,8 @@ func NewHorizontalController( hpaNamespacer: hpaNamespacer, upscaleForbiddenWindow: upscaleForbiddenWindow, downscaleForbiddenWindow: downscaleForbiddenWindow, - queue: workqueue.NewNamedRateLimitingQueue(NewDefaultHPARateLimiter(resyncPeriod), "horizontalpodautoscaler"), + queue: workqueue.NewNamedRateLimitingQueue(NewDefaultHPARateLimiter(resyncPeriod), "horizontalpodautoscaler"), + mapper: mapper, } hpaInformer.Informer().AddEventHandlerWithResyncPeriod( @@ -190,7 +193,7 @@ func (a *HorizontalController) processNextWorkItem() bool { // Computes the desired number of replicas for the metric specifications listed in the HPA, returning the maximum // of the computed replica counts, a description of the associated metric, and the statuses of all metrics // computed. -func (a *HorizontalController) computeReplicasForMetrics(hpa *autoscalingv2.HorizontalPodAutoscaler, scale *extensions.Scale, +func (a *HorizontalController) computeReplicasForMetrics(hpa *autoscalingv2.HorizontalPodAutoscaler, scale *autoscalingv1.Scale, metricSpecs []autoscalingv2.MetricSpec) (replicas int32, metric string, statuses []autoscalingv2.MetricStatus, timestamp time.Time, err error) { currentReplicas := scale.Status.Replicas @@ -198,21 +201,14 @@ func (a *HorizontalController) computeReplicasForMetrics(hpa *autoscalingv2.Hori statuses = make([]autoscalingv2.MetricStatus, len(metricSpecs)) for i, metricSpec := range metricSpecs { - if len(scale.Status.Selector) == 0 && len(scale.Status.TargetSelector) == 0 { + if scale.Status.Selector == "" { errMsg := "selector is required" a.eventRecorder.Event(hpa, v1.EventTypeWarning, "SelectorRequired", errMsg) setCondition(hpa, autoscalingv2.ScalingActive, v1.ConditionFalse, "InvalidSelector", "the HPA target's scale is missing a selector") return 0, "", nil, time.Time{}, fmt.Errorf(errMsg) } - var selector labels.Selector - var err error - if len(scale.Status.Selector) > 0 { - selector = labels.SelectorFromSet(labels.Set(scale.Status.Selector)) - err = nil - } else { - selector, err = labels.Parse(scale.Status.TargetSelector) - } + selector, err := labels.Parse(scale.Status.Selector) if err != nil { errMsg := fmt.Sprintf("couldn't convert selector into a corresponding internal selector object: %v", err) a.eventRecorder.Event(hpa, v1.EventTypeWarning, "InvalidSelector", errMsg) @@ -350,7 +346,28 @@ func (a *HorizontalController) reconcileAutoscaler(hpav1Shared *autoscalingv1.Ho reference := fmt.Sprintf("%s/%s/%s", hpa.Spec.ScaleTargetRef.Kind, hpa.Namespace, hpa.Spec.ScaleTargetRef.Name) - scale, err := a.scaleNamespacer.Scales(hpa.Namespace).Get(hpa.Spec.ScaleTargetRef.Kind, hpa.Spec.ScaleTargetRef.Name) + targetGV, err := schema.ParseGroupVersion(hpa.Spec.ScaleTargetRef.APIVersion) + if err != nil { + a.eventRecorder.Event(hpa, v1.EventTypeWarning, "FailedGetScale", err.Error()) + setCondition(hpa, autoscalingv2.AbleToScale, v1.ConditionFalse, "FailedGetScale", "the HPA controller was unable to get the target's current scale: %v", err) + a.updateStatusIfNeeded(hpaStatusOriginal, hpa) + return fmt.Errorf("invalid API version in scale target reference: %v", err) + } + + targetGK := schema.GroupKind{ + Group: targetGV.Group, + Kind: hpa.Spec.ScaleTargetRef.Kind, + } + + mappings, err := a.mapper.RESTMappings(targetGK) + if err != nil { + a.eventRecorder.Event(hpa, v1.EventTypeWarning, "FailedGetScale", err.Error()) + setCondition(hpa, autoscalingv2.AbleToScale, v1.ConditionFalse, "FailedGetScale", "the HPA controller was unable to get the target's current scale: %v", err) + a.updateStatusIfNeeded(hpaStatusOriginal, hpa) + return fmt.Errorf("unable to determine resource for scale target reference: %v", err) + } + + scale, targetGR, err := a.scaleForResourceMappings(hpa.Namespace, hpa.Spec.ScaleTargetRef.Name, mappings) if err != nil { a.eventRecorder.Event(hpa, v1.EventTypeWarning, "FailedGetScale", err.Error()) setCondition(hpa, autoscalingv2.AbleToScale, v1.ConditionFalse, "FailedGetScale", "the HPA controller was unable to get the target's current scale: %v", err) @@ -480,7 +497,7 @@ func (a *HorizontalController) reconcileAutoscaler(hpav1Shared *autoscalingv1.Ho if rescale { scale.Spec.Replicas = desiredReplicas - _, err = a.scaleNamespacer.Scales(hpa.Namespace).Update(hpa.Spec.ScaleTargetRef.Kind, scale) + _, err = a.scaleNamespacer.Scales(hpa.Namespace).Update(targetGR, scale) if err != nil { a.eventRecorder.Eventf(hpa, v1.EventTypeWarning, "FailedRescale", "New size: %d; reason: %s; error: %v", desiredReplicas, rescaleReason, err.Error()) setCondition(hpa, autoscalingv2.AbleToScale, v1.ConditionFalse, "FailedUpdateScale", "the HPA controller was unable to update the target scale: %v", err) @@ -527,6 +544,35 @@ func (a *HorizontalController) shouldScale(hpa *autoscalingv2.HorizontalPodAutos return false } +// scaleForResourceMappings attempts to fetch the scale for the +// resource with the given name and namespace, trying each RESTMapping +// in turn until a working one is found. If none work, the first error +// is returned. It returns both the scale, as well as the group-resource from +// the working mapping. +func (a *HorizontalController) scaleForResourceMappings(namespace, name string, mappings []*apimeta.RESTMapping) (*autoscalingv1.Scale, schema.GroupResource, error) { + var firstErr error + for i, mapping := range mappings { + targetGR := mapping.GroupVersionKind.GroupVersion().WithResource(mapping.Resource).GroupResource() + scale, err := a.scaleNamespacer.Scales(namespace).Get(targetGR, name) + if err == nil { + return scale, targetGR, nil + } + + // if this is the first error, remember it, + // then go on and try other mappings until we find a good one + if i == 0 { + firstErr = err + } + } + + // make sure we handle an empty set of mappings + if firstErr == nil { + firstErr = fmt.Errorf("unrecognized resource") + } + + return nil, schema.GroupResource{}, firstErr +} + // setCurrentReplicasInStatus sets the current replica count in the status of the HPA. func (a *HorizontalController) setCurrentReplicasInStatus(hpa *autoscalingv2.HorizontalPodAutoscaler, currentReplicas int32) { a.setStatus(hpa, currentReplicas, hpa.Status.DesiredReplicas, hpa.Status.CurrentMetrics, false) diff --git a/pkg/controller/podautoscaler/horizontal_test.go b/pkg/controller/podautoscaler/horizontal_test.go index 2de54888482..ac840d61bab 100644 --- a/pkg/controller/podautoscaler/horizontal_test.go +++ b/pkg/controller/podautoscaler/horizontal_test.go @@ -27,15 +27,16 @@ import ( autoscalingv1 "k8s.io/api/autoscaling/v1" autoscalingv2 "k8s.io/api/autoscaling/v2beta1" "k8s.io/api/core/v1" - extensions "k8s.io/api/extensions/v1beta1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" clientfake "k8s.io/client-go/kubernetes/fake" + scalefake "k8s.io/client-go/scale/fake" core "k8s.io/client-go/testing" "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/apis/autoscaling" @@ -121,6 +122,7 @@ type testCase struct { testClient *fake.Clientset testMetricsClient *metricsfake.Clientset testCMClient *cmfake.FakeCustomMetricsClient + testScaleClient *scalefake.FakeScaleClient } // Needs to be called under a lock. @@ -144,12 +146,12 @@ func init() { scaleUpLimitFactor = 8 } -func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfake.Clientset, *cmfake.FakeCustomMetricsClient) { +func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfake.Clientset, *cmfake.FakeCustomMetricsClient, *scalefake.FakeScaleClient) { namespace := "test-namespace" hpaName := "test-hpa" podNamePrefix := "test-pod" - // TODO: also test with TargetSelector - selector := map[string]string{"name": podNamePrefix} + labelSet := map[string]string{"name": podNamePrefix} + selector := labels.SelectorFromSet(labelSet).String() tc.Lock() @@ -161,13 +163,11 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa tc.computeCPUCurrent() } - // TODO(madhusudancs): HPA only supports resources in extensions/v1beta1 right now. Add - // tests for "v1" replicationcontrollers when HPA adds support for cross-group scale. if tc.resource == nil { tc.resource = &fakeResource{ name: "test-rc", - apiVersion: "extensions/v1beta1", - kind: "replicationcontrollers", + apiVersion: "v1", + kind: "ReplicationController", } } tc.Unlock() @@ -239,66 +239,6 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa return true, objv1, nil }) - fakeClient.AddReactor("get", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { - tc.Lock() - defer tc.Unlock() - - obj := &extensions.Scale{ - ObjectMeta: metav1.ObjectMeta{ - Name: tc.resource.name, - Namespace: namespace, - }, - Spec: extensions.ScaleSpec{ - Replicas: tc.initialReplicas, - }, - Status: extensions.ScaleStatus{ - Replicas: tc.initialReplicas, - Selector: selector, - }, - } - return true, obj, nil - }) - - fakeClient.AddReactor("get", "deployments", func(action core.Action) (handled bool, ret runtime.Object, err error) { - tc.Lock() - defer tc.Unlock() - - obj := &extensions.Scale{ - ObjectMeta: metav1.ObjectMeta{ - Name: tc.resource.name, - Namespace: namespace, - }, - Spec: extensions.ScaleSpec{ - Replicas: tc.initialReplicas, - }, - Status: extensions.ScaleStatus{ - Replicas: tc.initialReplicas, - Selector: selector, - }, - } - return true, obj, nil - }) - - fakeClient.AddReactor("get", "replicasets", func(action core.Action) (handled bool, ret runtime.Object, err error) { - tc.Lock() - defer tc.Unlock() - - obj := &extensions.Scale{ - ObjectMeta: metav1.ObjectMeta{ - Name: tc.resource.name, - Namespace: namespace, - }, - Spec: extensions.ScaleSpec{ - Replicas: tc.initialReplicas, - }, - Status: extensions.ScaleStatus{ - Replicas: tc.initialReplicas, - Selector: selector, - }, - } - return true, obj, nil - }) - fakeClient.AddReactor("list", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) { tc.Lock() defer tc.Unlock() @@ -344,39 +284,6 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa return true, obj, nil }) - fakeClient.AddReactor("update", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { - tc.Lock() - defer tc.Unlock() - - obj := action.(core.UpdateAction).GetObject().(*extensions.Scale) - replicas := action.(core.UpdateAction).GetObject().(*extensions.Scale).Spec.Replicas - assert.Equal(t, tc.desiredReplicas, replicas, "the replica count of the RC should be as expected") - tc.scaleUpdated = true - return true, obj, nil - }) - - fakeClient.AddReactor("update", "deployments", func(action core.Action) (handled bool, ret runtime.Object, err error) { - tc.Lock() - defer tc.Unlock() - - obj := action.(core.UpdateAction).GetObject().(*extensions.Scale) - replicas := action.(core.UpdateAction).GetObject().(*extensions.Scale).Spec.Replicas - assert.Equal(t, tc.desiredReplicas, replicas, "the replica count of the deployment should be as expected") - tc.scaleUpdated = true - return true, obj, nil - }) - - fakeClient.AddReactor("update", "replicasets", func(action core.Action) (handled bool, ret runtime.Object, err error) { - tc.Lock() - defer tc.Unlock() - - obj := action.(core.UpdateAction).GetObject().(*extensions.Scale) - replicas := action.(core.UpdateAction).GetObject().(*extensions.Scale).Spec.Replicas - assert.Equal(t, tc.desiredReplicas, replicas, "the replica count of the replicaset should be as expected") - tc.scaleUpdated = true - return true, obj, nil - }) - fakeClient.AddReactor("update", "horizontalpodautoscalers", func(action core.Action) (handled bool, ret runtime.Object, err error) { tc.Lock() defer tc.Unlock() @@ -386,8 +293,9 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa assert.Equal(t, hpaName, obj.Name, "the HPA name should be as expected") assert.Equal(t, tc.desiredReplicas, obj.Status.DesiredReplicas, "the desired replica count reported in the object status should be as expected") if tc.verifyCPUCurrent { - assert.NotNil(t, obj.Status.CurrentCPUUtilizationPercentage, "the reported CPU utilization percentage should be non-nil") - assert.Equal(t, tc.CPUCurrent, *obj.Status.CurrentCPUUtilizationPercentage, "the report CPU utilization percentage should be as expected") + if assert.NotNil(t, obj.Status.CurrentCPUUtilizationPercentage, "the reported CPU utilization percentage should be non-nil") { + assert.Equal(t, tc.CPUCurrent, *obj.Status.CurrentCPUUtilizationPercentage, "the report CPU utilization percentage should be as expected") + } } var actualConditions []autoscalingv1.HorizontalPodAutoscalerCondition if err := json.Unmarshal([]byte(obj.ObjectMeta.Annotations[autoscaling.HorizontalPodAutoscalerConditionsAnnotation]), &actualConditions); err != nil { @@ -411,6 +319,100 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa return true, obj, nil }) + fakeScaleClient := &scalefake.FakeScaleClient{} + fakeScaleClient.AddReactor("get", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { + tc.Lock() + defer tc.Unlock() + + obj := &autoscalingv1.Scale{ + ObjectMeta: metav1.ObjectMeta{ + Name: tc.resource.name, + Namespace: namespace, + }, + Spec: autoscalingv1.ScaleSpec{ + Replicas: tc.initialReplicas, + }, + Status: autoscalingv1.ScaleStatus{ + Replicas: tc.initialReplicas, + Selector: selector, + }, + } + return true, obj, nil + }) + + fakeScaleClient.AddReactor("get", "deployments", func(action core.Action) (handled bool, ret runtime.Object, err error) { + tc.Lock() + defer tc.Unlock() + + obj := &autoscalingv1.Scale{ + ObjectMeta: metav1.ObjectMeta{ + Name: tc.resource.name, + Namespace: namespace, + }, + Spec: autoscalingv1.ScaleSpec{ + Replicas: tc.initialReplicas, + }, + Status: autoscalingv1.ScaleStatus{ + Replicas: tc.initialReplicas, + Selector: selector, + }, + } + return true, obj, nil + }) + + fakeScaleClient.AddReactor("get", "replicasets", func(action core.Action) (handled bool, ret runtime.Object, err error) { + tc.Lock() + defer tc.Unlock() + + obj := &autoscalingv1.Scale{ + ObjectMeta: metav1.ObjectMeta{ + Name: tc.resource.name, + Namespace: namespace, + }, + Spec: autoscalingv1.ScaleSpec{ + Replicas: tc.initialReplicas, + }, + Status: autoscalingv1.ScaleStatus{ + Replicas: tc.initialReplicas, + Selector: selector, + }, + } + return true, obj, nil + }) + + fakeScaleClient.AddReactor("update", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { + tc.Lock() + defer tc.Unlock() + + obj := action.(core.UpdateAction).GetObject().(*autoscalingv1.Scale) + replicas := action.(core.UpdateAction).GetObject().(*autoscalingv1.Scale).Spec.Replicas + assert.Equal(t, tc.desiredReplicas, replicas, "the replica count of the RC should be as expected") + tc.scaleUpdated = true + return true, obj, nil + }) + + fakeScaleClient.AddReactor("update", "deployments", func(action core.Action) (handled bool, ret runtime.Object, err error) { + tc.Lock() + defer tc.Unlock() + + obj := action.(core.UpdateAction).GetObject().(*autoscalingv1.Scale) + replicas := action.(core.UpdateAction).GetObject().(*autoscalingv1.Scale).Spec.Replicas + assert.Equal(t, tc.desiredReplicas, replicas, "the replica count of the deployment should be as expected") + tc.scaleUpdated = true + return true, obj, nil + }) + + fakeScaleClient.AddReactor("update", "replicasets", func(action core.Action) (handled bool, ret runtime.Object, err error) { + tc.Lock() + defer tc.Unlock() + + obj := action.(core.UpdateAction).GetObject().(*autoscalingv1.Scale) + replicas := action.(core.UpdateAction).GetObject().(*autoscalingv1.Scale).Spec.Replicas + assert.Equal(t, tc.desiredReplicas, replicas, "the replica count of the replicaset should be as expected") + tc.scaleUpdated = true + return true, obj, nil + }) + fakeWatch := watch.NewFake() fakeClient.AddWatchReactor("*", core.DefaultWatchReactor(fakeWatch, nil)) @@ -427,7 +429,7 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("%s-%d", podNamePrefix, i), Namespace: namespace, - Labels: selector, + Labels: labelSet, }, Timestamp: metav1.Time{Time: time.Now()}, Containers: []metricsapi.ContainerMetrics{ @@ -522,7 +524,7 @@ func (tc *testCase) prepareTestClient(t *testing.T) (*fake.Clientset, *metricsfa return true, metrics, nil }) - return fakeClient, fakeMetricsClient, fakeCMClient + return fakeClient, fakeMetricsClient, fakeCMClient, fakeScaleClient } func (tc *testCase) verifyResults(t *testing.T) { @@ -537,7 +539,7 @@ func (tc *testCase) verifyResults(t *testing.T) { } func (tc *testCase) setupController(t *testing.T) (*HorizontalController, informers.SharedInformerFactory) { - testClient, testMetricsClient, testCMClient := tc.prepareTestClient(t) + testClient, testMetricsClient, testCMClient, testScaleClient := tc.prepareTestClient(t) if tc.testClient != nil { testClient = tc.testClient } @@ -547,6 +549,9 @@ func (tc *testCase) setupController(t *testing.T) (*HorizontalController, inform if tc.testCMClient != nil { testCMClient = tc.testCMClient } + if tc.testScaleClient != nil { + testScaleClient = tc.testScaleClient + } metricsClient := metrics.NewRESTMetricsClient( testMetricsClient.MetricsV1beta1(), testCMClient, @@ -587,8 +592,9 @@ func (tc *testCase) setupController(t *testing.T) (*HorizontalController, inform hpaController := NewHorizontalController( eventClient.Core(), - testClient.Extensions(), + testScaleClient, testClient.Autoscaling(), + legacyscheme.Registry.RESTMapper(), replicaCalc, informerFactory.Autoscaling().V1().HorizontalPodAutoscalers(), controller.NoResyncPeriodFunc(), @@ -692,7 +698,7 @@ func TestScaleUpDeployment(t *testing.T) { resource: &fakeResource{ name: "test-dep", apiVersion: "extensions/v1beta1", - kind: "deployments", + kind: "Deployment", }, } tc.runTest(t) @@ -712,7 +718,7 @@ func TestScaleUpReplicaSet(t *testing.T) { resource: &fakeResource{ name: "test-replicaset", apiVersion: "extensions/v1beta1", - kind: "replicasets", + kind: "ReplicaSet", }, } tc.runTest(t) @@ -1267,18 +1273,18 @@ func TestConditionInvalidSelectorMissing(t *testing.T) { }, } - testClient, _, _ := tc.prepareTestClient(t) - tc.testClient = testClient + _, _, _, testScaleClient := tc.prepareTestClient(t) + tc.testScaleClient = testScaleClient - testClient.PrependReactor("get", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { - obj := &extensions.Scale{ + testScaleClient.PrependReactor("get", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { + obj := &autoscalingv1.Scale{ ObjectMeta: metav1.ObjectMeta{ Name: tc.resource.name, }, - Spec: extensions.ScaleSpec{ + Spec: autoscalingv1.ScaleSpec{ Replicas: tc.initialReplicas, }, - Status: extensions.ScaleStatus{ + Status: autoscalingv1.ScaleStatus{ Replicas: tc.initialReplicas, }, } @@ -1312,20 +1318,20 @@ func TestConditionInvalidSelectorUnparsable(t *testing.T) { }, } - testClient, _, _ := tc.prepareTestClient(t) - tc.testClient = testClient + _, _, _, testScaleClient := tc.prepareTestClient(t) + tc.testScaleClient = testScaleClient - testClient.PrependReactor("get", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { - obj := &extensions.Scale{ + testScaleClient.PrependReactor("get", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { + obj := &autoscalingv1.Scale{ ObjectMeta: metav1.ObjectMeta{ Name: tc.resource.name, }, - Spec: extensions.ScaleSpec{ + Spec: autoscalingv1.ScaleSpec{ Replicas: tc.initialReplicas, }, - Status: extensions.ScaleStatus{ - Replicas: tc.initialReplicas, - TargetSelector: "cheddar cheese", + Status: autoscalingv1.ScaleStatus{ + Replicas: tc.initialReplicas, + Selector: "cheddar cheese", }, } return true, obj, nil @@ -1373,7 +1379,7 @@ func TestConditionFailedGetMetrics(t *testing.T) { reportedCPURequests: []resource.Quantity{resource.MustParse("0.1"), resource.MustParse("0.1"), resource.MustParse("0.1")}, useMetricsAPI: true, } - _, testMetricsClient, testCMClient := tc.prepareTestClient(t) + _, testMetricsClient, testCMClient, _ := tc.prepareTestClient(t) tc.testMetricsClient = testMetricsClient tc.testCMClient = testCMClient @@ -1446,11 +1452,11 @@ func TestConditionFailedGetScale(t *testing.T) { }, } - testClient, _, _ := tc.prepareTestClient(t) - tc.testClient = testClient + _, _, _, testScaleClient := tc.prepareTestClient(t) + tc.testScaleClient = testScaleClient - testClient.PrependReactor("get", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { - return true, &extensions.Scale{}, fmt.Errorf("something went wrong") + testScaleClient.PrependReactor("get", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { + return true, &autoscalingv1.Scale{}, fmt.Errorf("something went wrong") }) tc.runTest(t) @@ -1473,11 +1479,11 @@ func TestConditionFailedUpdateScale(t *testing.T) { }), } - testClient, _, _ := tc.prepareTestClient(t) - tc.testClient = testClient + _, _, _, testScaleClient := tc.prepareTestClient(t) + tc.testScaleClient = testScaleClient - testClient.PrependReactor("update", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { - return true, &extensions.Scale{}, fmt.Errorf("something went wrong") + testScaleClient.PrependReactor("update", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { + return true, &autoscalingv1.Scale{}, fmt.Errorf("something went wrong") }) tc.runTest(t) @@ -1659,7 +1665,7 @@ func TestAvoidUncessaryUpdates(t *testing.T) { reportedPodReadiness: []v1.ConditionStatus{v1.ConditionTrue, v1.ConditionFalse, v1.ConditionFalse}, useMetricsAPI: true, } - testClient, _, _ := tc.prepareTestClient(t) + testClient, _, _, _ := tc.prepareTestClient(t) tc.testClient = testClient var savedHPA *autoscalingv1.HorizontalPodAutoscaler testClient.PrependReactor("list", "horizontalpodautoscalers", func(action core.Action) (handled bool, ret runtime.Object, err error) { diff --git a/pkg/controller/podautoscaler/legacy_horizontal_test.go b/pkg/controller/podautoscaler/legacy_horizontal_test.go index b94ce82c6c2..72eeade2114 100644 --- a/pkg/controller/podautoscaler/legacy_horizontal_test.go +++ b/pkg/controller/podautoscaler/legacy_horizontal_test.go @@ -30,16 +30,18 @@ import ( autoscalingv1 "k8s.io/api/autoscaling/v1" autoscalingv2 "k8s.io/api/autoscaling/v2beta1" "k8s.io/api/core/v1" - extensions "k8s.io/api/extensions/v1beta1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" clientfake "k8s.io/client-go/kubernetes/fake" restclient "k8s.io/client-go/rest" + scalefake "k8s.io/client-go/scale/fake" core "k8s.io/client-go/testing" + "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller/podautoscaler/metrics" @@ -48,6 +50,7 @@ import ( "github.com/stretchr/testify/assert" + _ "k8s.io/kubernetes/pkg/api/install" _ "k8s.io/kubernetes/pkg/apis/autoscaling/install" _ "k8s.io/kubernetes/pkg/apis/extensions/install" ) @@ -114,12 +117,12 @@ func (tc *legacyTestCase) computeCPUCurrent() { tc.CPUCurrent = int32(100 * reported / requested) } -func (tc *legacyTestCase) prepareTestClient(t *testing.T) *fake.Clientset { +func (tc *legacyTestCase) prepareTestClient(t *testing.T) (*fake.Clientset, *scalefake.FakeScaleClient) { namespace := "test-namespace" hpaName := "test-hpa" podNamePrefix := "test-pod" - // TODO: also test with TargetSelector - selector := map[string]string{"name": podNamePrefix} + labelSet := map[string]string{"name": podNamePrefix} + selector := labels.SelectorFromSet(labelSet).String() tc.Lock() @@ -131,13 +134,11 @@ func (tc *legacyTestCase) prepareTestClient(t *testing.T) *fake.Clientset { tc.computeCPUCurrent() } - // TODO(madhusudancs): HPA only supports resources in extensions/v1beta1 right now. Add - // tests for "v1" replicationcontrollers when HPA adds support for cross-group scale. if tc.resource == nil { tc.resource = &fakeResource{ name: "test-rc", - apiVersion: "extensions/v1beta1", - kind: "replicationcontrollers", + apiVersion: "v1", + kind: "ReplicationController", } } tc.Unlock() @@ -208,66 +209,6 @@ func (tc *legacyTestCase) prepareTestClient(t *testing.T) *fake.Clientset { return true, objv1, nil }) - fakeClient.AddReactor("get", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { - tc.Lock() - defer tc.Unlock() - - obj := &extensions.Scale{ - ObjectMeta: metav1.ObjectMeta{ - Name: tc.resource.name, - Namespace: namespace, - }, - Spec: extensions.ScaleSpec{ - Replicas: tc.initialReplicas, - }, - Status: extensions.ScaleStatus{ - Replicas: tc.initialReplicas, - Selector: selector, - }, - } - return true, obj, nil - }) - - fakeClient.AddReactor("get", "deployments", func(action core.Action) (handled bool, ret runtime.Object, err error) { - tc.Lock() - defer tc.Unlock() - - obj := &extensions.Scale{ - ObjectMeta: metav1.ObjectMeta{ - Name: tc.resource.name, - Namespace: namespace, - }, - Spec: extensions.ScaleSpec{ - Replicas: tc.initialReplicas, - }, - Status: extensions.ScaleStatus{ - Replicas: tc.initialReplicas, - Selector: selector, - }, - } - return true, obj, nil - }) - - fakeClient.AddReactor("get", "replicasets", func(action core.Action) (handled bool, ret runtime.Object, err error) { - tc.Lock() - defer tc.Unlock() - - obj := &extensions.Scale{ - ObjectMeta: metav1.ObjectMeta{ - Name: tc.resource.name, - Namespace: namespace, - }, - Spec: extensions.ScaleSpec{ - Replicas: tc.initialReplicas, - }, - Status: extensions.ScaleStatus{ - Replicas: tc.initialReplicas, - Selector: selector, - }, - } - return true, obj, nil - }) - fakeClient.AddReactor("list", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) { tc.Lock() defer tc.Unlock() @@ -386,39 +327,6 @@ func (tc *legacyTestCase) prepareTestClient(t *testing.T) *fake.Clientset { return true, newFakeResponseWrapper(heapsterRawMemResponse), nil }) - fakeClient.AddReactor("update", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { - tc.Lock() - defer tc.Unlock() - - obj := action.(core.UpdateAction).GetObject().(*extensions.Scale) - replicas := action.(core.UpdateAction).GetObject().(*extensions.Scale).Spec.Replicas - assert.Equal(t, tc.desiredReplicas, replicas, "the replica count of the RC should be as expected") - tc.scaleUpdated = true - return true, obj, nil - }) - - fakeClient.AddReactor("update", "deployments", func(action core.Action) (handled bool, ret runtime.Object, err error) { - tc.Lock() - defer tc.Unlock() - - obj := action.(core.UpdateAction).GetObject().(*extensions.Scale) - replicas := action.(core.UpdateAction).GetObject().(*extensions.Scale).Spec.Replicas - assert.Equal(t, tc.desiredReplicas, replicas, "the replica count of the deployment should be as expected") - tc.scaleUpdated = true - return true, obj, nil - }) - - fakeClient.AddReactor("update", "replicasets", func(action core.Action) (handled bool, ret runtime.Object, err error) { - tc.Lock() - defer tc.Unlock() - - obj := action.(core.UpdateAction).GetObject().(*extensions.Scale) - replicas := action.(core.UpdateAction).GetObject().(*extensions.Scale).Spec.Replicas - assert.Equal(t, tc.desiredReplicas, replicas, "the replica count of the replicaset should be as expected") - tc.scaleUpdated = true - return true, obj, nil - }) - fakeClient.AddReactor("update", "horizontalpodautoscalers", func(action core.Action) (handled bool, ret runtime.Object, err error) { tc.Lock() defer tc.Unlock() @@ -428,8 +336,9 @@ func (tc *legacyTestCase) prepareTestClient(t *testing.T) *fake.Clientset { assert.Equal(t, hpaName, obj.Name, "the HPA name should be as expected") assert.Equal(t, tc.desiredReplicas, obj.Status.DesiredReplicas, "the desired replica count reported in the object status should be as expected") if tc.verifyCPUCurrent { - assert.NotNil(t, obj.Status.CurrentCPUUtilizationPercentage, "the reported CPU utilization percentage should be non-nil") - assert.Equal(t, tc.CPUCurrent, *obj.Status.CurrentCPUUtilizationPercentage, "the report CPU utilization percentage should be as expected") + if assert.NotNil(t, obj.Status.CurrentCPUUtilizationPercentage, "the reported CPU utilization percentage should be non-nil") { + assert.Equal(t, tc.CPUCurrent, *obj.Status.CurrentCPUUtilizationPercentage, "the report CPU utilization percentage should be as expected") + } } tc.statusUpdated = true // Every time we reconcile HPA object we are updating status. @@ -437,10 +346,104 @@ func (tc *legacyTestCase) prepareTestClient(t *testing.T) *fake.Clientset { return true, obj, nil }) + fakeScaleClient := &scalefake.FakeScaleClient{} + fakeScaleClient.AddReactor("get", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { + tc.Lock() + defer tc.Unlock() + + obj := &autoscalingv1.Scale{ + ObjectMeta: metav1.ObjectMeta{ + Name: tc.resource.name, + Namespace: namespace, + }, + Spec: autoscalingv1.ScaleSpec{ + Replicas: tc.initialReplicas, + }, + Status: autoscalingv1.ScaleStatus{ + Replicas: tc.initialReplicas, + Selector: selector, + }, + } + return true, obj, nil + }) + + fakeScaleClient.AddReactor("get", "deployments", func(action core.Action) (handled bool, ret runtime.Object, err error) { + tc.Lock() + defer tc.Unlock() + + obj := &autoscalingv1.Scale{ + ObjectMeta: metav1.ObjectMeta{ + Name: tc.resource.name, + Namespace: namespace, + }, + Spec: autoscalingv1.ScaleSpec{ + Replicas: tc.initialReplicas, + }, + Status: autoscalingv1.ScaleStatus{ + Replicas: tc.initialReplicas, + Selector: selector, + }, + } + return true, obj, nil + }) + + fakeScaleClient.AddReactor("get", "replicasets", func(action core.Action) (handled bool, ret runtime.Object, err error) { + tc.Lock() + defer tc.Unlock() + + obj := &autoscalingv1.Scale{ + ObjectMeta: metav1.ObjectMeta{ + Name: tc.resource.name, + Namespace: namespace, + }, + Spec: autoscalingv1.ScaleSpec{ + Replicas: tc.initialReplicas, + }, + Status: autoscalingv1.ScaleStatus{ + Replicas: tc.initialReplicas, + Selector: selector, + }, + } + return true, obj, nil + }) + + fakeScaleClient.AddReactor("update", "replicationcontrollers", func(action core.Action) (handled bool, ret runtime.Object, err error) { + tc.Lock() + defer tc.Unlock() + + obj := action.(core.UpdateAction).GetObject().(*autoscalingv1.Scale) + replicas := action.(core.UpdateAction).GetObject().(*autoscalingv1.Scale).Spec.Replicas + assert.Equal(t, tc.desiredReplicas, replicas, "the replica count of the RC should be as expected") + tc.scaleUpdated = true + return true, obj, nil + }) + + fakeScaleClient.AddReactor("update", "deployments", func(action core.Action) (handled bool, ret runtime.Object, err error) { + tc.Lock() + defer tc.Unlock() + + obj := action.(core.UpdateAction).GetObject().(*autoscalingv1.Scale) + replicas := action.(core.UpdateAction).GetObject().(*autoscalingv1.Scale).Spec.Replicas + assert.Equal(t, tc.desiredReplicas, replicas, "the replica count of the deployment should be as expected") + tc.scaleUpdated = true + return true, obj, nil + }) + + fakeScaleClient.AddReactor("update", "replicasets", func(action core.Action) (handled bool, ret runtime.Object, err error) { + tc.Lock() + defer tc.Unlock() + + obj := action.(core.UpdateAction).GetObject().(*autoscalingv1.Scale) + replicas := action.(core.UpdateAction).GetObject().(*autoscalingv1.Scale).Spec.Replicas + assert.Equal(t, tc.desiredReplicas, replicas, "the replica count of the replicaset should be as expected") + tc.scaleUpdated = true + return true, obj, nil + }) + fakeWatch := watch.NewFake() fakeClient.AddWatchReactor("*", core.DefaultWatchReactor(fakeWatch, nil)) - return fakeClient + return fakeClient, fakeScaleClient } func (tc *legacyTestCase) verifyResults(t *testing.T) { @@ -455,7 +458,7 @@ func (tc *legacyTestCase) verifyResults(t *testing.T) { } func (tc *legacyTestCase) runTest(t *testing.T) { - testClient := tc.prepareTestClient(t) + testClient, testScaleClient := tc.prepareTestClient(t) metricsClient := metrics.NewHeapsterMetricsClient(testClient, metrics.DefaultHeapsterNamespace, metrics.DefaultHeapsterScheme, metrics.DefaultHeapsterService, metrics.DefaultHeapsterPort) eventClient := &clientfake.Clientset{} @@ -493,8 +496,9 @@ func (tc *legacyTestCase) runTest(t *testing.T) { hpaController := NewHorizontalController( eventClient.Core(), - testClient.Extensions(), + testScaleClient, testClient.Autoscaling(), + legacyscheme.Registry.RESTMapper(), replicaCalc, informerFactory.Autoscaling().V1().HorizontalPodAutoscalers(), controller.NoResyncPeriodFunc(), @@ -584,7 +588,7 @@ func LegacyTestScaleUpDeployment(t *testing.T) { resource: &fakeResource{ name: "test-dep", apiVersion: "extensions/v1beta1", - kind: "deployments", + kind: "Deployment", }, } tc.runTest(t) @@ -604,7 +608,7 @@ func LegacyTestScaleUpReplicaSet(t *testing.T) { resource: &fakeResource{ name: "test-replicaset", apiVersion: "extensions/v1beta1", - kind: "replicasets", + kind: "ReplicaSet", }, } tc.runTest(t) diff --git a/pkg/kubectl/cmd/annotate_test.go b/pkg/kubectl/cmd/annotate_test.go index 3b9e2aa085d..7396d9a4936 100644 --- a/pkg/kubectl/cmd/annotate_test.go +++ b/pkg/kubectl/cmd/annotate_test.go @@ -452,7 +452,7 @@ func TestAnnotateObject(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch req.Method { @@ -503,7 +503,7 @@ func TestAnnotateObjectFromFile(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch req.Method { @@ -552,7 +552,7 @@ func TestAnnotateObjectFromFile(t *testing.T) { func TestAnnotateLocal(t *testing.T) { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) @@ -585,7 +585,7 @@ func TestAnnotateMultipleObjects(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch req.Method { diff --git a/pkg/kubectl/cmd/apply_test.go b/pkg/kubectl/cmd/apply_test.go index 9cceac1a273..fc98f462f9b 100644 --- a/pkg/kubectl/cmd/apply_test.go +++ b/pkg/kubectl/cmd/apply_test.go @@ -302,7 +302,7 @@ func TestRunApplyViewLastApplied(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: schema.GroupVersion{Version: "v1"}, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -358,7 +358,7 @@ func TestApplyObjectWithoutAnnotation(t *testing.T) { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: schema.GroupVersion{Version: "v1"}, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -403,7 +403,7 @@ func TestApplyObject(t *testing.T) { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: schema.GroupVersion{Version: "v1"}, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -460,7 +460,7 @@ func TestApplyObjectOutput(t *testing.T) { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.Printer = &printers.YAMLPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: schema.GroupVersion{Version: "v1"}, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -505,7 +505,7 @@ func TestApplyRetry(t *testing.T) { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: schema.GroupVersion{Version: "v1"}, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -559,7 +559,7 @@ func TestApplyNonExistObject(t *testing.T) { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: schema.GroupVersion{Version: "v1"}, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -610,7 +610,7 @@ func testApplyMultipleObjects(t *testing.T, asList bool) { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -688,7 +688,7 @@ func TestApplyNULLPreservation(t *testing.T) { f, tf, _, _ := cmdtesting.NewTestFactory() tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -757,7 +757,7 @@ func TestUnstructuredApply(t *testing.T) { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: schema.GroupVersion{Version: "v1"}, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -821,7 +821,7 @@ func TestUnstructuredIdempotentApply(t *testing.T) { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: schema.GroupVersion{Version: "v1"}, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -946,7 +946,7 @@ func TestRunApplySetLastApplied(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: schema.GroupVersion{Version: "v1"}, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -1035,7 +1035,7 @@ func TestForceApply(t *testing.T) { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: schema.GroupVersion{Version: "v1"}, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/attach_test.go b/pkg/kubectl/cmd/attach_test.go index f406b4fb069..04dcd0aedf2 100644 --- a/pkg/kubectl/cmd/attach_test.go +++ b/pkg/kubectl/cmd/attach_test.go @@ -140,7 +140,7 @@ func TestPodAndContainerAttach(t *testing.T) { for _, test := range tests { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { if test.obj != nil { @@ -217,7 +217,7 @@ func TestAttach(t *testing.T) { for _, test := range tests { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -304,7 +304,7 @@ func TestAttachWarnings(t *testing.T) { for _, test := range tests { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/auth/cani_test.go b/pkg/kubectl/cmd/auth/cani_test.go index 0bc060b4644..5411dafdde9 100644 --- a/pkg/kubectl/cmd/auth/cani_test.go +++ b/pkg/kubectl/cmd/auth/cani_test.go @@ -122,7 +122,7 @@ func TestRunAccessCheck(t *testing.T) { f, tf, _, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { expectPath := "/apis/authorization.k8s.io/v1/selfsubjectaccessreviews" diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index f46146d5086..d59ea8d64ab 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -163,7 +163,7 @@ func Example_printReplicationControllerWithNamespace() { printersinternal.AddHandlers(p) tf.Printer = p tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: nil, } @@ -216,7 +216,7 @@ func Example_printMultiContainersReplicationControllerWithWide() { printersinternal.AddHandlers(p) tf.Printer = p tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: nil, } @@ -270,7 +270,7 @@ func Example_printReplicationController() { printersinternal.AddHandlers(p) tf.Printer = p tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: nil, } @@ -325,7 +325,7 @@ func Example_printPodWithWideFormat() { printersinternal.AddHandlers(p) tf.Printer = p tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: nil, } @@ -368,7 +368,7 @@ func Example_printPodWithShowLabels() { printersinternal.AddHandlers(p) tf.Printer = p tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: nil, } @@ -505,7 +505,7 @@ func Example_printPodHideTerminated() { printersinternal.AddHandlers(p) tf.Printer = p tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: nil, } @@ -541,7 +541,7 @@ func Example_printPodShowAll() { printersinternal.AddHandlers(p) tf.Printer = p tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: nil, } @@ -570,7 +570,7 @@ func Example_printServiceWithNamespacesAndLabels() { printersinternal.AddHandlers(p) tf.Printer = p tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: nil, } diff --git a/pkg/kubectl/cmd/create_clusterrolebinding_test.go b/pkg/kubectl/cmd/create_clusterrolebinding_test.go index 26eccd1c531..cf43eff2ae8 100644 --- a/pkg/kubectl/cmd/create_clusterrolebinding_test.go +++ b/pkg/kubectl/cmd/create_clusterrolebinding_test.go @@ -30,6 +30,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" restclient "k8s.io/client-go/rest" "k8s.io/client-go/rest/fake" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/legacyscheme" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" ) @@ -77,7 +78,7 @@ func TestCreateClusterRoleBinding(t *testing.T) { tf.Printer = &testPrinter{} tf.Client = &ClusterRoleBindingRESTClient{ RESTClient: &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/create_configmap_test.go b/pkg/kubectl/cmd/create_configmap_test.go index e8c898ad9ca..f0ed75cda73 100644 --- a/pkg/kubectl/cmd/create_configmap_test.go +++ b/pkg/kubectl/cmd/create_configmap_test.go @@ -33,7 +33,7 @@ func TestCreateConfigMap(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/create_deployment_test.go b/pkg/kubectl/cmd/create_deployment_test.go index 348a5ec2a55..59263ffe67e 100644 --- a/pkg/kubectl/cmd/create_deployment_test.go +++ b/pkg/kubectl/cmd/create_deployment_test.go @@ -26,6 +26,7 @@ import ( restclient "k8s.io/client-go/rest" "k8s.io/client-go/rest/fake" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/kubectl" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" @@ -76,7 +77,7 @@ func TestCreateDeployment(t *testing.T) { depName := "jonny-dep" f, tf, _, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { return &http.Response{ @@ -105,7 +106,7 @@ func TestCreateDeploymentNoImage(t *testing.T) { depName := "jonny-dep" f, tf, _, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { return &http.Response{ diff --git a/pkg/kubectl/cmd/create_namespace_test.go b/pkg/kubectl/cmd/create_namespace_test.go index a4bed218391..d16dc2e2ce8 100644 --- a/pkg/kubectl/cmd/create_namespace_test.go +++ b/pkg/kubectl/cmd/create_namespace_test.go @@ -33,7 +33,7 @@ func TestCreateNamespace(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/create_pdb_test.go b/pkg/kubectl/cmd/create_pdb_test.go index 11f1387ece6..ac21f09f72a 100644 --- a/pkg/kubectl/cmd/create_pdb_test.go +++ b/pkg/kubectl/cmd/create_pdb_test.go @@ -24,6 +24,7 @@ import ( restclient "k8s.io/client-go/rest" "k8s.io/client-go/rest/fake" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/legacyscheme" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" ) @@ -32,7 +33,7 @@ func TestCreatePdb(t *testing.T) { pdbName := "my-pdb" f, tf, _, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { return &http.Response{ diff --git a/pkg/kubectl/cmd/create_quota_test.go b/pkg/kubectl/cmd/create_quota_test.go index 6f34d81f400..0a4e8c900c4 100644 --- a/pkg/kubectl/cmd/create_quota_test.go +++ b/pkg/kubectl/cmd/create_quota_test.go @@ -33,7 +33,7 @@ func TestCreateQuota(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/create_rolebinding_test.go b/pkg/kubectl/cmd/create_rolebinding_test.go index 5586623da9e..bf85e7febb0 100644 --- a/pkg/kubectl/cmd/create_rolebinding_test.go +++ b/pkg/kubectl/cmd/create_rolebinding_test.go @@ -29,6 +29,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" restclient "k8s.io/client-go/rest" "k8s.io/client-go/rest/fake" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/apis/rbac" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" @@ -75,7 +76,7 @@ func TestCreateRoleBinding(t *testing.T) { tf.Printer = &testPrinter{} tf.Client = &RoleBindingRESTClient{ RESTClient: &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/create_secret_test.go b/pkg/kubectl/cmd/create_secret_test.go index 8b25ea0c265..11970790eaf 100644 --- a/pkg/kubectl/cmd/create_secret_test.go +++ b/pkg/kubectl/cmd/create_secret_test.go @@ -38,7 +38,7 @@ func TestCreateSecretGeneric(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -69,7 +69,7 @@ func TestCreateSecretDockerRegistry(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/create_service_test.go b/pkg/kubectl/cmd/create_service_test.go index aa38d11b3c2..810be786f01 100644 --- a/pkg/kubectl/cmd/create_service_test.go +++ b/pkg/kubectl/cmd/create_service_test.go @@ -33,7 +33,7 @@ func TestCreateService(t *testing.T) { f, tf, codec, negSer := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: negSer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -63,7 +63,7 @@ func TestCreateServiceNodePort(t *testing.T) { f, tf, codec, negSer := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: negSer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -93,7 +93,7 @@ func TestCreateServiceExternalName(t *testing.T) { f, tf, codec, negSer := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: negSer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/create_serviceaccount_test.go b/pkg/kubectl/cmd/create_serviceaccount_test.go index 170fe3401cc..64431e3e2b8 100644 --- a/pkg/kubectl/cmd/create_serviceaccount_test.go +++ b/pkg/kubectl/cmd/create_serviceaccount_test.go @@ -33,7 +33,7 @@ func TestCreateServiceAccount(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/create_test.go b/pkg/kubectl/cmd/create_test.go index c21e1f313ed..819d7d35ffb 100644 --- a/pkg/kubectl/cmd/create_test.go +++ b/pkg/kubectl/cmd/create_test.go @@ -22,6 +22,7 @@ import ( "testing" "k8s.io/client-go/rest/fake" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/legacyscheme" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" ) @@ -46,7 +47,7 @@ func TestCreateObject(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -80,7 +81,7 @@ func TestCreateMultipleObject(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -118,7 +119,7 @@ func TestCreateDirectory(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/delete_test.go b/pkg/kubectl/cmd/delete_test.go index b3bb0895db0..e7c82f1e266 100644 --- a/pkg/kubectl/cmd/delete_test.go +++ b/pkg/kubectl/cmd/delete_test.go @@ -58,7 +58,7 @@ func TestDeleteObjectByTuple(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -123,7 +123,7 @@ func TestOrphanDependentsInDeleteObject(t *testing.T) { tf.Printer = &testPrinter{} var expectedOrphanDependents *bool tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m, b := req.URL.Path, req.Method, req.Body; { @@ -172,7 +172,7 @@ func TestDeleteNamedObject(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -223,7 +223,7 @@ func TestDeleteObject(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -282,7 +282,7 @@ func TestDeleteObjectGraceZero(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { t.Logf("got request %s %s", req.Method, req.URL.Path) @@ -332,7 +332,7 @@ func TestDeleteObjectNotFound(t *testing.T) { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -370,7 +370,7 @@ func TestDeleteObjectIgnoreNotFound(t *testing.T) { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -408,7 +408,7 @@ func TestDeleteAllNotFound(t *testing.T) { tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -458,7 +458,7 @@ func TestDeleteAllIgnoreNotFound(t *testing.T) { tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -495,7 +495,7 @@ func TestDeleteMultipleObject(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -531,7 +531,7 @@ func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -576,7 +576,7 @@ func TestDeleteMultipleResourcesWithTheSameName(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -615,7 +615,7 @@ func TestDeleteDirectory(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -648,7 +648,7 @@ func TestDeleteMultipleSelector(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/describe_test.go b/pkg/kubectl/cmd/describe_test.go index 77076521e66..c40e88bafdf 100644 --- a/pkg/kubectl/cmd/describe_test.go +++ b/pkg/kubectl/cmd/describe_test.go @@ -23,6 +23,7 @@ import ( "testing" "k8s.io/client-go/rest/fake" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/legacyscheme" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" ) @@ -33,7 +34,7 @@ func TestDescribeUnknownSchemaObject(t *testing.T) { f, tf, codec, _ := cmdtesting.NewTestFactory() tf.Describer = d tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, cmdtesting.NewInternalType("", "", "foo"))}, } @@ -58,7 +59,7 @@ func TestDescribeUnknownNamespacedSchemaObject(t *testing.T) { f, tf, codec, _ := cmdtesting.NewTestFactory() tf.Describer = d tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, cmdtesting.NewInternalNamespacedType("", "", "foo", "non-default"))}, } @@ -83,7 +84,7 @@ func TestDescribeObject(t *testing.T) { d := &testDescriber{Output: "test output"} tf.Describer = d tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -117,7 +118,7 @@ func TestDescribeListObjects(t *testing.T) { d := &testDescriber{Output: "test output"} tf.Describer = d tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, } @@ -138,7 +139,7 @@ func TestDescribeObjectShowEvents(t *testing.T) { d := &testDescriber{Output: "test output"} tf.Describer = d tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, } @@ -160,7 +161,7 @@ func TestDescribeObjectSkipEvents(t *testing.T) { d := &testDescriber{Output: "test output"} tf.Describer = d tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, } diff --git a/pkg/kubectl/cmd/drain_test.go b/pkg/kubectl/cmd/drain_test.go index d701df65667..5d277055f47 100644 --- a/pkg/kubectl/cmd/drain_test.go +++ b/pkg/kubectl/cmd/drain_test.go @@ -154,7 +154,7 @@ func TestCordon(t *testing.T) { new_node := &v1.Node{} updated := false tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { m := &MyReq{req} @@ -597,7 +597,7 @@ func TestDrain(t *testing.T) { evicted := false f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { m := &MyReq{req} diff --git a/pkg/kubectl/cmd/edit_test.go b/pkg/kubectl/cmd/edit_test.go index 04e5520545c..86792766935 100644 --- a/pkg/kubectl/cmd/edit_test.go +++ b/pkg/kubectl/cmd/edit_test.go @@ -36,6 +36,7 @@ import ( "k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/rest/fake" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/legacyscheme" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" @@ -217,7 +218,7 @@ func TestEdit(t *testing.T) { versionedAPIPath = "/apis/" + mapping.GroupVersionKind.Group + "/" + mapping.GroupVersionKind.Version } return &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, VersionedAPIPath: versionedAPIPath, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(reqResp), diff --git a/pkg/kubectl/cmd/exec_test.go b/pkg/kubectl/cmd/exec_test.go index ec527899413..3eb751167c3 100644 --- a/pkg/kubectl/cmd/exec_test.go +++ b/pkg/kubectl/cmd/exec_test.go @@ -131,7 +131,7 @@ func TestPodAndContainer(t *testing.T) { for _, test := range tests { f, tf, _, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { return nil, nil }), } @@ -186,7 +186,7 @@ func TestExec(t *testing.T) { for _, test := range tests { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/expose_test.go b/pkg/kubectl/cmd/expose_test.go index 5bb4a1b101d..e2f62be3d56 100644 --- a/pkg/kubectl/cmd/expose_test.go +++ b/pkg/kubectl/cmd/expose_test.go @@ -463,7 +463,7 @@ func TestRunExposeService(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Printer = &printers.JSONPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/get_test.go b/pkg/kubectl/cmd/get_test.go index 9571fae1808..624d5721c06 100644 --- a/pkg/kubectl/cmd/get_test.go +++ b/pkg/kubectl/cmd/get_test.go @@ -126,7 +126,7 @@ func TestGetUnknownSchemaObject(t *testing.T) { _, _, codec, _ := cmdtesting.NewTestFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, cmdtesting.NewInternalType("", "", "foo"))}, } @@ -171,7 +171,7 @@ func TestGetSchemaObject(t *testing.T) { codec := testapi.Default.Codec() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &api.ReplicationController{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})}, } @@ -197,7 +197,7 @@ func TestGetObjectsWithOpenAPIOutputFormatPresent(t *testing.T) { // for Pod type. tf.OpenAPISchemaFunc = testOpenAPISchemaData tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, } @@ -251,7 +251,7 @@ func TestGetObjects(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, } @@ -303,7 +303,7 @@ func TestGetObjectsFiltered(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{GenericPrinter: test.genericPrinter} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, test.resp)}, } @@ -344,7 +344,7 @@ func TestGetObjectIgnoreNotFound(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{GenericPrinter: true} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -397,7 +397,7 @@ func TestGetSortedObjects(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, } @@ -456,7 +456,7 @@ func TestGetObjectsIdentifiedByFile(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{GenericPrinter: true} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, } @@ -483,7 +483,7 @@ func TestGetListObjects(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, } @@ -524,7 +524,7 @@ func TestGetAllListObjects(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, } @@ -554,7 +554,7 @@ func TestGetListComponentStatus(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, statuses)}, } @@ -596,7 +596,7 @@ func TestGetMixedGenericObjects(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{GenericPrinter: true} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch req.URL.Path { @@ -660,7 +660,7 @@ func TestGetMultipleTypeObjects(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch req.URL.Path { @@ -699,7 +699,7 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{GenericPrinter: true} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch req.URL.Path { @@ -764,7 +764,7 @@ func TestGetMultipleTypeObjectsWithSelector(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { if req.URL.Query().Get(metav1.LabelSelectorQueryParam(legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion.String())) != "a=b" { @@ -816,7 +816,7 @@ func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch req.URL.Path { @@ -853,7 +853,7 @@ func TestGetByFormatForcesFlag(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{GenericPrinter: true} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, } @@ -954,7 +954,7 @@ func TestWatchSelector(t *testing.T) { }, } tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { if req.URL.Query().Get(metav1.LabelSelectorQueryParam(legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion.String())) != "a=b" { @@ -998,7 +998,7 @@ func TestWatchResource(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch req.URL.Path { @@ -1040,7 +1040,7 @@ func TestWatchResourceIdentifiedByFile(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch req.URL.Path { @@ -1083,7 +1083,7 @@ func TestWatchOnlyResource(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch req.URL.Path { @@ -1131,7 +1131,7 @@ func TestWatchOnlyList(t *testing.T) { }, } tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch req.URL.Path { diff --git a/pkg/kubectl/cmd/label_test.go b/pkg/kubectl/cmd/label_test.go index a2a42fe18b1..ee591d459fc 100644 --- a/pkg/kubectl/cmd/label_test.go +++ b/pkg/kubectl/cmd/label_test.go @@ -358,7 +358,7 @@ func TestLabelForResourceFromFile(t *testing.T) { pods, _, _ := testData() f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch req.Method { @@ -409,7 +409,7 @@ func TestLabelForResourceFromFile(t *testing.T) { func TestLabelLocal(t *testing.T) { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) @@ -443,7 +443,7 @@ func TestLabelMultipleObjects(t *testing.T) { pods, _, _ := testData() f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch req.Method { diff --git a/pkg/kubectl/cmd/logs_test.go b/pkg/kubectl/cmd/logs_test.go index cd237082fa8..65c06bcbdc8 100644 --- a/pkg/kubectl/cmd/logs_test.go +++ b/pkg/kubectl/cmd/logs_test.go @@ -52,7 +52,7 @@ func TestLog(t *testing.T) { logContent := "test log content" f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/patch_test.go b/pkg/kubectl/cmd/patch_test.go index dfa77c271e9..0b46aed160b 100644 --- a/pkg/kubectl/cmd/patch_test.go +++ b/pkg/kubectl/cmd/patch_test.go @@ -23,6 +23,7 @@ import ( "testing" "k8s.io/client-go/rest/fake" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/legacyscheme" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" "k8s.io/kubernetes/pkg/printers" @@ -33,7 +34,7 @@ func TestPatchObject(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -72,7 +73,7 @@ func TestPatchObjectFromFile(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -107,7 +108,7 @@ func TestPatchNoop(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -153,7 +154,7 @@ func TestPatchObjectFromFileOutput(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &printers.YAMLPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/portforward_test.go b/pkg/kubectl/cmd/portforward_test.go index 4898365f951..dfae45575a0 100644 --- a/pkg/kubectl/cmd/portforward_test.go +++ b/pkg/kubectl/cmd/portforward_test.go @@ -70,7 +70,7 @@ func testPortForward(t *testing.T, flags map[string]string, args []string) { var err error f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/replace_test.go b/pkg/kubectl/cmd/replace_test.go index 8b2eb2ab9d6..86a87e02c9d 100644 --- a/pkg/kubectl/cmd/replace_test.go +++ b/pkg/kubectl/cmd/replace_test.go @@ -35,7 +35,7 @@ func TestReplaceObject(t *testing.T) { tf.Printer = &testPrinter{} deleted := false tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -92,7 +92,7 @@ func TestReplaceMultipleObject(t *testing.T) { redisMasterDeleted := false frontendDeleted := false tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -161,7 +161,7 @@ func TestReplaceDirectory(t *testing.T) { tf.Printer = &testPrinter{} created := map[string]bool{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -217,7 +217,7 @@ func TestForceReplaceObjectNotFound(t *testing.T) { f, tf, codec, _ := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.UnstructuredClient = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: unstructuredSerializer, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { diff --git a/pkg/kubectl/cmd/run_test.go b/pkg/kubectl/cmd/run_test.go index c51016dc54e..a9360c744e5 100644 --- a/pkg/kubectl/cmd/run_test.go +++ b/pkg/kubectl/cmd/run_test.go @@ -164,7 +164,7 @@ func TestRunArgsFollowDashRules(t *testing.T) { for _, test := range tests { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { if req.URL.Path == "/namespaces/test/replicationcontrollers" { @@ -288,7 +288,7 @@ func TestGenerateService(t *testing.T) { tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion}} tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -429,7 +429,7 @@ func TestRunValidations(t *testing.T) { f, tf, codec, ns := cmdtesting.NewTestFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, cmdtesting.NewInternalType("", "", ""))}, } diff --git a/pkg/kubectl/cmd/set/BUILD b/pkg/kubectl/cmd/set/BUILD index c00e787e457..19d946148b2 100644 --- a/pkg/kubectl/cmd/set/BUILD +++ b/pkg/kubectl/cmd/set/BUILD @@ -74,6 +74,7 @@ go_test( "//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/rest/fake:go_default_library", ], diff --git a/pkg/kubectl/cmd/set/set_env_test.go b/pkg/kubectl/cmd/set/set_env_test.go index 11942947296..ea71128c990 100644 --- a/pkg/kubectl/cmd/set/set_env_test.go +++ b/pkg/kubectl/cmd/set/set_env_test.go @@ -36,7 +36,7 @@ import ( func TestSetEnvLocal(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) diff --git a/pkg/kubectl/cmd/set/set_image_test.go b/pkg/kubectl/cmd/set/set_image_test.go index c445a612fa0..63c15e92f37 100644 --- a/pkg/kubectl/cmd/set/set_image_test.go +++ b/pkg/kubectl/cmd/set/set_image_test.go @@ -35,7 +35,7 @@ import ( func TestImageLocal(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) diff --git a/pkg/kubectl/cmd/set/set_resources_test.go b/pkg/kubectl/cmd/set/set_resources_test.go index e3e9def3e90..5cff6c25a24 100644 --- a/pkg/kubectl/cmd/set/set_resources_test.go +++ b/pkg/kubectl/cmd/set/set_resources_test.go @@ -35,7 +35,7 @@ import ( func TestResourcesLocal(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) diff --git a/pkg/kubectl/cmd/set/set_selector_test.go b/pkg/kubectl/cmd/set/set_selector_test.go index cde6b1d4f9b..3b45e4da2bb 100644 --- a/pkg/kubectl/cmd/set/set_selector_test.go +++ b/pkg/kubectl/cmd/set/set_selector_test.go @@ -318,7 +318,7 @@ func TestGetResourcesAndSelector(t *testing.T) { func TestSelectorTest(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) diff --git a/pkg/kubectl/cmd/set/set_serviceaccount_test.go b/pkg/kubectl/cmd/set/set_serviceaccount_test.go index 7ecdf988144..2330083b844 100644 --- a/pkg/kubectl/cmd/set/set_serviceaccount_test.go +++ b/pkg/kubectl/cmd/set/set_serviceaccount_test.go @@ -29,6 +29,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/rest/fake" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/legacyscheme" @@ -65,7 +66,7 @@ func TestServiceAccountLocal(t *testing.T) { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) return nil, nil @@ -155,7 +156,7 @@ func TestServiceAccountRemote(t *testing.T) { tf.Namespace = "test" tf.CategoryExpander = resource.LegacyCategoryExpander tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(input.apiGroup).GroupVersion, NegotiatedSerializer: testapi.Default.NegotiatedSerializer(), Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", tf.Namespace, input.args[1]) @@ -179,7 +180,6 @@ func TestServiceAccountRemote(t *testing.T) { } }), VersionedAPIPath: path.Join(input.apiPrefix, groupVersion.String()), - GroupName: input.apiGroup, } out := new(bytes.Buffer) cmd := NewCmdServiceAccount(f, out, out) @@ -207,7 +207,7 @@ func TestServiceAccountValidation(t *testing.T) { for _, input := range inputs { f, tf, _, _ := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: schema.GroupVersion{Version: "v1"}, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) return nil, nil diff --git a/pkg/kubectl/cmd/taint_test.go b/pkg/kubectl/cmd/taint_test.go index 61fe9c62aa5..cc2b43e7035 100644 --- a/pkg/kubectl/cmd/taint_test.go +++ b/pkg/kubectl/cmd/taint_test.go @@ -29,6 +29,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/client-go/rest/fake" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/legacyscheme" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" @@ -242,7 +243,7 @@ func TestTaint(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { m := &MyReq{req} diff --git a/pkg/kubectl/cmd/top_node_test.go b/pkg/kubectl/cmd/top_node_test.go index 47d6e8f1dd8..b7da9ae493d 100644 --- a/pkg/kubectl/cmd/top_node_test.go +++ b/pkg/kubectl/cmd/top_node_test.go @@ -46,7 +46,7 @@ func TestTopNodeAllMetrics(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -92,7 +92,7 @@ func TestTopNodeAllMetricsCustomDefaults(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -148,7 +148,7 @@ func TestTopNodeWithNameMetrics(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -208,7 +208,7 @@ func TestTopNodeWithLabelSelectorMetrics(t *testing.T) { f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m, q := req.URL.Path, req.Method, req.URL.RawQuery; { diff --git a/pkg/kubectl/cmd/top_pod_test.go b/pkg/kubectl/cmd/top_pod_test.go index 5b24e176205..43d0666855a 100644 --- a/pkg/kubectl/cmd/top_pod_test.go +++ b/pkg/kubectl/cmd/top_pod_test.go @@ -29,6 +29,7 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/rest/fake" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/legacyscheme" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" metricsapi "k8s.io/metrics/pkg/apis/metrics/v1alpha1" @@ -118,7 +119,7 @@ func TestTopPod(t *testing.T) { f, tf, _, ns := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m, q := req.URL.Path, req.Method, req.URL.RawQuery; { @@ -254,7 +255,7 @@ func TestTopPodCustomDefaults(t *testing.T) { f, tf, _, ns := cmdtesting.NewAPIFactory() tf.Printer = &testPrinter{} tf.Client = &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: ns, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m, q := req.URL.Path, req.Method, req.URL.RawQuery; { diff --git a/pkg/kubectl/resource/builder_test.go b/pkg/kubectl/resource/builder_test.go index b5587cc8844..08b80d15d02 100644 --- a/pkg/kubectl/resource/builder_test.go +++ b/pkg/kubectl/resource/builder_test.go @@ -72,7 +72,7 @@ func fakeClient() ClientMapper { func fakeClientWith(testName string, t *testing.T, data map[string]string) ClientMapper { return ClientMapperFunc(func(*meta.RESTMapping) (RESTClient, error) { return &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: testapi.Default.NegotiatedSerializer(), Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { p := req.URL.Path diff --git a/pkg/kubectl/resource/helper_test.go b/pkg/kubectl/resource/helper_test.go index 17428fcd8b2..db3d7d57e92 100644 --- a/pkg/kubectl/resource/helper_test.go +++ b/pkg/kubectl/resource/helper_test.go @@ -104,7 +104,7 @@ func TestHelperDelete(t *testing.T) { } for _, test := range tests { client := &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: testapi.Default.NegotiatedSerializer(), Resp: test.Resp, Err: test.HttpErr, @@ -195,7 +195,7 @@ func TestHelperCreate(t *testing.T) { } for i, test := range tests { client := &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: testapi.Default.NegotiatedSerializer(), Resp: test.Resp, Err: test.HttpErr, @@ -276,7 +276,7 @@ func TestHelperGet(t *testing.T) { } for _, test := range tests { client := &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: testapi.Default.NegotiatedSerializer(), Resp: test.Resp, Err: test.HttpErr, @@ -350,7 +350,7 @@ func TestHelperList(t *testing.T) { } for _, test := range tests { client := &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: testapi.Default.NegotiatedSerializer(), Resp: test.Resp, Err: test.HttpErr, @@ -484,7 +484,7 @@ func TestHelperReplace(t *testing.T) { } for i, test := range tests { client := &fake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: testapi.Default.NegotiatedSerializer(), Client: test.HTTPClient, Resp: test.Resp, diff --git a/pkg/kubectl/rolling_updater_test.go b/pkg/kubectl/rolling_updater_test.go index 15d15a8307a..b30c1183403 100644 --- a/pkg/kubectl/rolling_updater_test.go +++ b/pkg/kubectl/rolling_updater_test.go @@ -1469,7 +1469,7 @@ func TestUpdateRcWithRetries(t *testing.T) { {StatusCode: 200, Header: header, Body: objBody(codec, rc)}, } fakeClient := &manualfake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: testapi.Default.NegotiatedSerializer(), Client: manualfake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { switch p, m := req.URL.Path, req.Method; { @@ -1562,7 +1562,7 @@ func TestAddDeploymentHash(t *testing.T) { seen := sets.String{} updatedRc := false fakeClient := &manualfake.RESTClient{ - APIRegistry: legacyscheme.Registry, + GroupVersion: legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, NegotiatedSerializer: testapi.Default.NegotiatedSerializer(), Client: manualfake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { header := http.Header{} diff --git a/staging/BUILD b/staging/BUILD index d1f33145250..356f6d566a3 100644 --- a/staging/BUILD +++ b/staging/BUILD @@ -167,6 +167,7 @@ filegroup( "//staging/src/k8s.io/client-go/plugin/pkg/auth/authenticator/token/oidc/testing:all-srcs", "//staging/src/k8s.io/client-go/plugin/pkg/client/auth:all-srcs", "//staging/src/k8s.io/client-go/rest:all-srcs", + "//staging/src/k8s.io/client-go/scale:all-srcs", "//staging/src/k8s.io/client-go/testing:all-srcs", "//staging/src/k8s.io/client-go/third_party/forked/golang/template:all-srcs", "//staging/src/k8s.io/client-go/tools/auth:all-srcs", diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/fuzzer/fuzzer.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/fuzzer/fuzzer.go index e5f5bd4c796..27dfb3d9e90 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/fuzzer/fuzzer.go +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/fuzzer/fuzzer.go @@ -18,7 +18,10 @@ package fuzzer import ( "fmt" + "math/rand" + "sort" "strconv" + "strings" "github.com/google/gofuzz" @@ -96,7 +99,80 @@ func genericFuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} { } } +// taken from gofuzz internals for RandString +type charRange struct { + first, last rune +} + +func (c *charRange) choose(r *rand.Rand) rune { + count := int64(c.last - c.first + 1) + ch := c.first + rune(r.Int63n(count)) + + return ch +} + +// randomLabelPart produces a valid random label value or name-part +// of a label key. +func randomLabelPart(c fuzz.Continue, canBeEmpty bool) string { + validStartEnd := []charRange{{'0', '9'}, {'a', 'z'}, {'A', 'Z'}} + validMiddle := []charRange{{'0', '9'}, {'a', 'z'}, {'A', 'Z'}, + {'.', '.'}, {'-', '-'}, {'_', '_'}} + + partLen := c.Rand.Intn(64) // len is [0, 63] + if !canBeEmpty { + partLen = c.Rand.Intn(63) + 1 // len is [1, 63] + } + + runes := make([]rune, partLen) + if partLen == 0 { + return string(runes) + } + + runes[0] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand) + for i := range runes[1:] { + runes[i+1] = validMiddle[c.Rand.Intn(len(validMiddle))].choose(c.Rand) + } + runes[len(runes)-1] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand) + + return string(runes) +} + +func randomDNSLabel(c fuzz.Continue) string { + validStartEnd := []charRange{{'0', '9'}, {'a', 'z'}} + validMiddle := []charRange{{'0', '9'}, {'a', 'z'}, {'-', '-'}} + + partLen := c.Rand.Intn(63) + 1 // len is [1, 63] + runes := make([]rune, partLen) + + runes[0] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand) + for i := range runes[1:] { + runes[i+1] = validMiddle[c.Rand.Intn(len(validMiddle))].choose(c.Rand) + } + runes[len(runes)-1] = validStartEnd[c.Rand.Intn(len(validStartEnd))].choose(c.Rand) + + return string(runes) +} + +func randomLabelKey(c fuzz.Continue) string { + namePart := randomLabelPart(c, false) + prefixPart := "" + + usePrefix := c.RandBool() + if usePrefix { + // we can fit, with dots, at most 3 labels in the 253 allotted characters + prefixPartsLen := c.Rand.Intn(2) + 1 + prefixParts := make([]string, prefixPartsLen) + for i := range prefixParts { + prefixParts[i] = randomDNSLabel(c) + } + prefixPart = strings.Join(prefixParts, ".") + "/" + } + + return prefixPart + namePart +} + func v1FuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} { + return []interface{}{ func(j *metav1.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their @@ -120,6 +196,57 @@ func v1FuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} { j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10) j.SelfLink = c.RandString() }, + func(j *metav1.LabelSelector, c fuzz.Continue) { + c.FuzzNoCustom(j) + // we can't have an entirely empty selector, so force + // use of MatchExpression if necessary + if len(j.MatchLabels) == 0 && len(j.MatchExpressions) == 0 { + j.MatchExpressions = make([]metav1.LabelSelectorRequirement, c.Rand.Intn(2)+1) + } + + if j.MatchLabels != nil { + fuzzedMatchLabels := make(map[string]string, len(j.MatchLabels)) + for i := 0; i < len(j.MatchLabels); i++ { + fuzzedMatchLabels[randomLabelKey(c)] = randomLabelPart(c, true) + } + j.MatchLabels = fuzzedMatchLabels + } + + validOperators := []metav1.LabelSelectorOperator{ + metav1.LabelSelectorOpIn, + metav1.LabelSelectorOpNotIn, + metav1.LabelSelectorOpExists, + metav1.LabelSelectorOpDoesNotExist, + } + + if j.MatchExpressions != nil { + // NB: the label selector parser code sorts match expressions by key, and sorts the values, + // so we need to make sure ours are sorted as well here to preserve round-trip comparision. + // In practice, not sorting doesn't hurt anything... + + for i := range j.MatchExpressions { + req := metav1.LabelSelectorRequirement{} + c.Fuzz(&req) + req.Key = randomLabelKey(c) + req.Operator = validOperators[c.Rand.Intn(len(validOperators))] + if req.Operator == metav1.LabelSelectorOpIn || req.Operator == metav1.LabelSelectorOpNotIn { + if len(req.Values) == 0 { + // we must have some values here, so randomly choose a short length + req.Values = make([]string, c.Rand.Intn(2)+1) + } + for i := range req.Values { + req.Values[i] = randomLabelPart(c, true) + } + sort.Strings(req.Values) + } else { + req.Values = nil + } + j.MatchExpressions[i] = req + } + + sort.Slice(j.MatchExpressions, func(a, b int) bool { return j.MatchExpressions[a].Key < j.MatchExpressions[b].Key }) + } + }, } } diff --git a/staging/src/k8s.io/client-go/Godeps/Godeps.json b/staging/src/k8s.io/client-go/Godeps/Godeps.json index e2b8325db7f..dbf237dc825 100644 --- a/staging/src/k8s.io/client-go/Godeps/Godeps.json +++ b/staging/src/k8s.io/client-go/Godeps/Godeps.json @@ -490,14 +490,34 @@ "ImportPath": "k8s.io/apimachinery/pkg/api/resource", "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, + { + "ImportPath": "k8s.io/apimachinery/pkg/api/testing", + "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + }, + { + "ImportPath": "k8s.io/apimachinery/pkg/api/testing/fuzzer", + "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + }, + { + "ImportPath": "k8s.io/apimachinery/pkg/api/testing/roundtrip", + "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + }, { "ImportPath": "k8s.io/apimachinery/pkg/apimachinery", "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, + { + "ImportPath": "k8s.io/apimachinery/pkg/apimachinery/announced", + "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + }, { "ImportPath": "k8s.io/apimachinery/pkg/apimachinery/registered", "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, + { + "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/fuzzer", + "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + }, { "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/internalversion", "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" diff --git a/staging/src/k8s.io/client-go/discovery/fake/discovery.go b/staging/src/k8s.io/client-go/discovery/fake/discovery.go index 13ccebfa74f..984a0ba1ec6 100644 --- a/staging/src/k8s.io/client-go/discovery/fake/discovery.go +++ b/staging/src/k8s.io/client-go/discovery/fake/discovery.go @@ -68,7 +68,44 @@ func (c *FakeDiscovery) ServerPreferredNamespacedResources() ([]*metav1.APIResou } func (c *FakeDiscovery) ServerGroups() (*metav1.APIGroupList, error) { - return nil, nil + action := testing.ActionImpl{ + Verb: "get", + Resource: schema.GroupVersionResource{Resource: "group"}, + } + c.Invokes(action, nil) + + groups := map[string]*metav1.APIGroup{} + + for _, res := range c.Resources { + gv, err := schema.ParseGroupVersion(res.GroupVersion) + if err != nil { + return nil, err + } + group := groups[gv.Group] + if group == nil { + group = &metav1.APIGroup{ + Name: gv.Group, + PreferredVersion: metav1.GroupVersionForDiscovery{ + GroupVersion: res.GroupVersion, + Version: gv.Version, + }, + } + groups[gv.Group] = group + } + + group.Versions = append(group.Versions, metav1.GroupVersionForDiscovery{ + GroupVersion: res.GroupVersion, + Version: gv.Version, + }) + } + + list := &metav1.APIGroupList{} + for _, apiGroup := range groups { + list.Groups = append(list.Groups, *apiGroup) + } + + return list, nil + } func (c *FakeDiscovery) ServerVersion() (*version.Info, error) { diff --git a/staging/src/k8s.io/client-go/rest/fake/BUILD b/staging/src/k8s.io/client-go/rest/fake/BUILD index 524701f1ec0..e511b207a6b 100644 --- a/staging/src/k8s.io/client-go/rest/fake/BUILD +++ b/staging/src/k8s.io/client-go/rest/fake/BUILD @@ -10,7 +10,6 @@ go_library( srcs = ["fake.go"], importpath = "k8s.io/client-go/rest/fake", deps = [ - "//vendor/k8s.io/apimachinery/pkg/apimachinery/registered:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", diff --git a/staging/src/k8s.io/client-go/rest/fake/fake.go b/staging/src/k8s.io/client-go/rest/fake/fake.go index 2e1bc55a985..db2c01c71fd 100644 --- a/staging/src/k8s.io/client-go/rest/fake/fake.go +++ b/staging/src/k8s.io/client-go/rest/fake/fake.go @@ -22,7 +22,6 @@ import ( "net/http" "net/url" - "k8s.io/apimachinery/pkg/apimachinery/registered" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" @@ -46,8 +45,7 @@ func (f roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) { type RESTClient struct { Client *http.Client NegotiatedSerializer runtime.NegotiatedSerializer - GroupName string - APIRegistry *registered.APIRegistrationManager + GroupVersion schema.GroupVersion VersionedAPIPath string Req *http.Request @@ -80,7 +78,7 @@ func (c *RESTClient) Verb(verb string) *restclient.Request { } func (c *RESTClient) APIVersion() schema.GroupVersion { - return c.APIRegistry.GroupOrDie("").GroupVersion + return c.GroupVersion } func (c *RESTClient) GetRateLimiter() flowcontrol.RateLimiter { @@ -89,22 +87,20 @@ func (c *RESTClient) GetRateLimiter() flowcontrol.RateLimiter { func (c *RESTClient) request(verb string) *restclient.Request { config := restclient.ContentConfig{ - ContentType: runtime.ContentTypeJSON, - // TODO this was hardcoded before, but it doesn't look right - GroupVersion: &c.APIRegistry.GroupOrDie("").GroupVersion, + ContentType: runtime.ContentTypeJSON, + GroupVersion: &c.GroupVersion, NegotiatedSerializer: c.NegotiatedSerializer, } ns := c.NegotiatedSerializer info, _ := runtime.SerializerInfoForMediaType(ns.SupportedMediaTypes(), runtime.ContentTypeJSON) internalVersion := schema.GroupVersion{ - Group: c.APIRegistry.GroupOrDie(c.GroupName).GroupVersion.Group, + Group: c.GroupVersion.Group, Version: runtime.APIVersionInternal, } - internalVersion.Version = runtime.APIVersionInternal serializers := restclient.Serializers{ // TODO this was hardcoded before, but it doesn't look right - Encoder: ns.EncoderForVersion(info.Serializer, c.APIRegistry.GroupOrDie("").GroupVersion), + Encoder: ns.EncoderForVersion(info.Serializer, c.GroupVersion), Decoder: ns.DecoderToVersion(info.Serializer, internalVersion), } if info.StreamSerializer != nil { diff --git a/staging/src/k8s.io/client-go/rest/url_utils.go b/staging/src/k8s.io/client-go/rest/url_utils.go index 14f94650a9e..a56d1838d8f 100644 --- a/staging/src/k8s.io/client-go/rest/url_utils.go +++ b/staging/src/k8s.io/client-go/rest/url_utils.go @@ -56,6 +56,14 @@ func DefaultServerURL(host, apiPath string, groupVersion schema.GroupVersion, de // hostURL.Path should be blank. // // versionedAPIPath, a path relative to baseURL.Path, points to a versioned API base + versionedAPIPath := DefaultVersionedAPIPath(apiPath, groupVersion) + + return hostURL, versionedAPIPath, nil +} + +// DefaultVersionedAPIPathFor constructs the default path for the given group version, assuming the given +// API path, following the standard conventions of the Kubernetes API. +func DefaultVersionedAPIPath(apiPath string, groupVersion schema.GroupVersion) string { versionedAPIPath := path.Join("/", apiPath) // Add the version to the end of the path @@ -64,10 +72,9 @@ func DefaultServerURL(host, apiPath string, groupVersion schema.GroupVersion, de } else { versionedAPIPath = path.Join(versionedAPIPath, groupVersion.Version) - } - return hostURL, versionedAPIPath, nil + return versionedAPIPath } // defaultServerUrlFor is shared between IsConfigTransportTLS and RESTClientFor. It diff --git a/staging/src/k8s.io/client-go/scale/BUILD b/staging/src/k8s.io/client-go/scale/BUILD new file mode 100644 index 00000000000..e3b3cac1a11 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/BUILD @@ -0,0 +1,73 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "client.go", + "doc.go", + "interfaces.go", + "util.go", + ], + importpath = "k8s.io/client-go/scale", + visibility = ["//visibility:public"], + deps = [ + "//vendor/k8s.io/api/autoscaling/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", + "//vendor/k8s.io/client-go/discovery:go_default_library", + "//vendor/k8s.io/client-go/dynamic:go_default_library", + "//vendor/k8s.io/client-go/rest:go_default_library", + "//vendor/k8s.io/client-go/scale/scheme:go_default_library", + "//vendor/k8s.io/client-go/scale/scheme/autoscalingv1:go_default_library", + "//vendor/k8s.io/client-go/scale/scheme/extensionsint:go_default_library", + "//vendor/k8s.io/client-go/scale/scheme/extensionsv1beta1:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = [ + "client_test.go", + "roundtrip_test.go", + ], + importpath = "k8s.io/client-go/scale", + library = ":go_default_library", + deps = [ + "//vendor/github.com/stretchr/testify/assert:go_default_library", + "//vendor/k8s.io/api/apps/v1beta2:go_default_library", + "//vendor/k8s.io/api/autoscaling/v1:go_default_library", + "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/testing/roundtrip:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", + "//vendor/k8s.io/client-go/discovery:go_default_library", + "//vendor/k8s.io/client-go/discovery/fake:go_default_library", + "//vendor/k8s.io/client-go/dynamic:go_default_library", + "//vendor/k8s.io/client-go/rest/fake:go_default_library", + "//vendor/k8s.io/client-go/testing:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [ + ":package-srcs", + "//staging/src/k8s.io/client-go/scale/fake:all-srcs", + "//staging/src/k8s.io/client-go/scale/scheme:all-srcs", + ], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/staging/src/k8s.io/client-go/scale/client.go b/staging/src/k8s.io/client-go/scale/client.go new file mode 100644 index 00000000000..3f85197a0b6 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/client.go @@ -0,0 +1,197 @@ +/* +Copyright 2017 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 scale + +import ( + "fmt" + + autoscaling "k8s.io/api/autoscaling/v1" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/client-go/dynamic" + restclient "k8s.io/client-go/rest" +) + +var scaleConverter = NewScaleConverter() +var codecs = serializer.NewCodecFactory(scaleConverter.Scheme()) + +// restInterfaceProvider turns a restclient.Config into a restclient.Interface. +// It's overridable for the purposes of testing. +type restInterfaceProvider func(*restclient.Config) (restclient.Interface, error) + +// scaleClient is an implementation of ScalesGetter +// which makes use of a RESTMapper and a generic REST +// client to support an discoverable resource. +// It behaves somewhat similarly to the dynamic ClientPool, +// but is more specifically scoped to Scale. +type scaleClient struct { + mapper meta.RESTMapper + + apiPathResolverFunc dynamic.APIPathResolverFunc + scaleKindResolver ScaleKindResolver + clientBase restclient.Interface +} + +// NewForConfig creates a new ScalesGetter which resolves kinds +// to resources using the given RESTMapper, and API paths using +// the given dynamic.APIPathResolverFunc. +func NewForConfig(cfg *restclient.Config, mapper meta.RESTMapper, resolver dynamic.APIPathResolverFunc, scaleKindResolver ScaleKindResolver) (ScalesGetter, error) { + // so that the RESTClientFor doesn't complain + cfg.GroupVersion = &schema.GroupVersion{} + + cfg.NegotiatedSerializer = serializer.DirectCodecFactory{ + CodecFactory: codecs, + } + if len(cfg.UserAgent) == 0 { + cfg.UserAgent = restclient.DefaultKubernetesUserAgent() + } + + client, err := restclient.RESTClientFor(cfg) + if err != nil { + return nil, err + } + + return New(client, mapper, resolver, scaleKindResolver), nil +} + +// New creates a new ScalesGetter using the given client to make requests. +// The GroupVersion on the client is ignored. +func New(baseClient restclient.Interface, mapper meta.RESTMapper, resolver dynamic.APIPathResolverFunc, scaleKindResolver ScaleKindResolver) ScalesGetter { + return &scaleClient{ + mapper: mapper, + + apiPathResolverFunc: resolver, + scaleKindResolver: scaleKindResolver, + clientBase: baseClient, + } +} + +// pathAndVersionFor returns the appropriate base path and the associated full GroupVersionResource +// for the given GroupResource +func (c *scaleClient) pathAndVersionFor(resource schema.GroupResource) (string, schema.GroupVersionResource, error) { + gvr, err := c.mapper.ResourceFor(resource.WithVersion("")) + if err != nil { + return "", gvr, fmt.Errorf("unable to get full preferred group-version-resource for %s: %v", resource.String(), err) + } + + groupVer := gvr.GroupVersion() + + // we need to set the API path based on GroupVersion (defaulting to the legacy path if none is set) + // TODO: we "cheat" here since the API path really only depends on group ATM, but this should + // *probably* take GroupVersionResource and not GroupVersionKind. + apiPath := c.apiPathResolverFunc(groupVer.WithKind("")) + if apiPath == "" { + apiPath = "/api" + } + + path := restclient.DefaultVersionedAPIPath(apiPath, groupVer) + + return path, gvr, nil +} + +// namespacedScaleClient is an ScaleInterface for fetching +// Scales in a given namespace. +type namespacedScaleClient struct { + client *scaleClient + namespace string +} + +func (c *scaleClient) Scales(namespace string) ScaleInterface { + return &namespacedScaleClient{ + client: c, + namespace: namespace, + } +} + +func (c *namespacedScaleClient) Get(resource schema.GroupResource, name string) (*autoscaling.Scale, error) { + // Currently, a /scale endpoint can return different scale types. + // Until we have support for the alternative API representations proposal, + // we need to deal with accepting different API versions. + // In practice, this is autoscaling/v1.Scale and extensions/v1beta1.Scale + + path, gvr, err := c.client.pathAndVersionFor(resource) + if err != nil { + return nil, fmt.Errorf("unable to get client for %s: %v", resource.String(), err) + } + + rawObj, err := c.client.clientBase.Get(). + AbsPath(path). + Namespace(c.namespace). + Resource(gvr.Resource). + Name(name). + SubResource("scale"). + Do(). + Get() + + if err != nil { + return nil, err + } + + // convert whatever this is to autoscaling/v1.Scale + scaleObj, err := scaleConverter.ConvertToVersion(rawObj, autoscaling.SchemeGroupVersion) + if err != nil { + return nil, fmt.Errorf("received an object from a /scale endpoint which was not convertible to autoscaling Scale: %v", err) + } + + return scaleObj.(*autoscaling.Scale), nil +} + +func (c *namespacedScaleClient) Update(resource schema.GroupResource, scale *autoscaling.Scale) (*autoscaling.Scale, error) { + path, gvr, err := c.client.pathAndVersionFor(resource) + if err != nil { + return nil, fmt.Errorf("unable to get client for %s: %v", resource.String(), err) + } + + // Currently, a /scale endpoint can receive and return different scale types. + // Until we hvae support for the alternative API representations proposal, + // we need to deal with sending and accepting differnet API versions. + + // figure out what scale we actually need here + desiredGVK, err := c.client.scaleKindResolver.ScaleForResource(gvr) + if err != nil { + return nil, fmt.Errorf("could not find proper group-version for scale subresource of %s: %v", gvr.String(), err) + } + + // convert this to whatever this endpoint wants + scaleUpdate, err := scaleConverter.ConvertToVersion(scale, desiredGVK.GroupVersion()) + if err != nil { + return nil, fmt.Errorf("could not convert scale update to internal Scale: %v", err) + } + + rawObj, err := c.client.clientBase.Put(). + AbsPath(path). + Namespace(c.namespace). + Resource(gvr.Resource). + Name(scale.Name). + SubResource("scale"). + Body(scaleUpdate). + Do(). + Get() + + if err != nil { + return nil, fmt.Errorf("could not fetch the scale for %s %s: %v", resource.String(), scale.Name, err) + } + + // convert whatever this is back to autoscaling/v1.Scale + scaleObj, err := scaleConverter.ConvertToVersion(rawObj, autoscaling.SchemeGroupVersion) + if err != nil { + return nil, fmt.Errorf("received an object from a /scale endpoint which was not convertible to autoscaling Scale: %v", err) + } + + return scaleObj.(*autoscaling.Scale), err +} diff --git a/staging/src/k8s.io/client-go/scale/client_test.go b/staging/src/k8s.io/client-go/scale/client_test.go new file mode 100644 index 00000000000..a10429c0d36 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/client_test.go @@ -0,0 +1,246 @@ +/* +Copyright 2017 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 scale + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/client-go/discovery" + fakedisco "k8s.io/client-go/discovery/fake" + "k8s.io/client-go/dynamic" + fakerest "k8s.io/client-go/rest/fake" + + "github.com/stretchr/testify/assert" + appsv1beta2 "k8s.io/api/apps/v1beta2" + autoscalingv1 "k8s.io/api/autoscaling/v1" + corev1 "k8s.io/api/core/v1" + extv1beta1 "k8s.io/api/extensions/v1beta1" + apimeta "k8s.io/apimachinery/pkg/api/meta" + coretesting "k8s.io/client-go/testing" +) + +func bytesBody(bodyBytes []byte) io.ReadCloser { + return ioutil.NopCloser(bytes.NewReader(bodyBytes)) +} + +func defaultHeaders() http.Header { + header := http.Header{} + header.Set("Content-Type", runtime.ContentTypeJSON) + return header +} + +func fakeScaleClient(t *testing.T) (ScalesGetter, []schema.GroupResource) { + fakeDiscoveryClient := &fakedisco.FakeDiscovery{Fake: &coretesting.Fake{}} + fakeDiscoveryClient.Resources = []*metav1.APIResourceList{ + { + GroupVersion: corev1.SchemeGroupVersion.String(), + APIResources: []metav1.APIResource{ + {Name: "pods", Namespaced: true, Kind: "Pod"}, + {Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"}, + {Name: "replicationcontrollers/scale", Namespaced: true, Kind: "Scale", Group: "autoscaling", Version: "v1"}, + }, + }, + { + GroupVersion: extv1beta1.SchemeGroupVersion.String(), + APIResources: []metav1.APIResource{ + {Name: "replicasets", Namespaced: true, Kind: "ReplicaSet"}, + {Name: "replicasets/scale", Namespaced: true, Kind: "Scale"}, + }, + }, + { + GroupVersion: appsv1beta2.SchemeGroupVersion.String(), + APIResources: []metav1.APIResource{ + {Name: "deployments", Namespaced: true, Kind: "Deployment"}, + {Name: "deployments/scale", Namespaced: true, Kind: "Scale", Group: "autoscaling", Version: "v1"}, + }, + }, + // test a resource that doesn't exist anywere to make sure we're not accidentally depending + // on a static RESTMapper anywhere. + { + GroupVersion: "cheese.testing.k8s.io/v27alpha15", + APIResources: []metav1.APIResource{ + {Name: "cheddars", Namespaced: true, Kind: "Cheddar"}, + {Name: "cheddars/scale", Namespaced: true, Kind: "Scale", Group: "extensions", Version: "v1beta1"}, + }, + }, + } + + restMapperRes, err := discovery.GetAPIGroupResources(fakeDiscoveryClient) + if err != nil { + t.Fatalf("unexpected error while constructing resource list from fake discovery client: %v") + } + restMapper := discovery.NewRESTMapper(restMapperRes, apimeta.InterfacesForUnstructured) + + autoscalingScale := &autoscalingv1.Scale{ + TypeMeta: metav1.TypeMeta{ + Kind: "Scale", + APIVersion: autoscalingv1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: autoscalingv1.ScaleSpec{Replicas: 10}, + Status: autoscalingv1.ScaleStatus{ + Replicas: 10, + Selector: "foo=bar", + }, + } + extScale := &extv1beta1.Scale{ + TypeMeta: metav1.TypeMeta{ + Kind: "Scale", + APIVersion: extv1beta1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: extv1beta1.ScaleSpec{Replicas: 10}, + Status: extv1beta1.ScaleStatus{ + Replicas: 10, + TargetSelector: "foo=bar", + }, + } + + resourcePaths := map[string]runtime.Object{ + "/api/v1/namespaces/default/replicationcontrollers/foo/scale": autoscalingScale, + "/apis/extensions/v1beta1/namespaces/default/replicasets/foo/scale": extScale, + "/apis/apps/v1beta2/namespaces/default/deployments/foo/scale": autoscalingScale, + "/apis/cheese.testing.k8s.io/v27alpha15/namespaces/default/cheddars/foo/scale": extScale, + } + + fakeReqHandler := func(req *http.Request) (*http.Response, error) { + scale, isScalePath := resourcePaths[req.URL.Path] + if !isScalePath { + return nil, fmt.Errorf("unexpected request for URL %q with method %q", req.URL.String(), req.Method) + } + + switch req.Method { + case "GET": + res, err := json.Marshal(scale) + if err != nil { + return nil, err + } + return &http.Response{StatusCode: 200, Header: defaultHeaders(), Body: bytesBody(res)}, nil + case "PUT": + decoder := codecs.UniversalDeserializer() + body, err := ioutil.ReadAll(req.Body) + if err != nil { + return nil, err + } + newScale, newScaleGVK, err := decoder.Decode(body, nil, nil) + if err != nil { + return nil, fmt.Errorf("unexpected request body: %v", err) + } + if *newScaleGVK != scale.GetObjectKind().GroupVersionKind() { + return nil, fmt.Errorf("unexpected scale API version %s (expected %s)", newScaleGVK.String(), scale.GetObjectKind().GroupVersionKind().String()) + } + res, err := json.Marshal(newScale) + if err != nil { + return nil, err + } + return &http.Response{StatusCode: 200, Header: defaultHeaders(), Body: bytesBody(res)}, nil + default: + return nil, fmt.Errorf("unexpected request for URL %q with method %q", req.URL.String(), req.Method) + } + } + + fakeClient := &fakerest.RESTClient{ + Client: fakerest.CreateHTTPClient(fakeReqHandler), + NegotiatedSerializer: serializer.DirectCodecFactory{ + CodecFactory: codecs, + }, + GroupVersion: schema.GroupVersion{}, + VersionedAPIPath: "/not/a/real/path", + } + + resolver := NewDiscoveryScaleKindResolver(fakeDiscoveryClient) + client := New(fakeClient, restMapper, dynamic.LegacyAPIPathResolverFunc, resolver) + + groupResources := []schema.GroupResource{ + {Group: corev1.GroupName, Resource: "replicationcontroller"}, + {Group: extv1beta1.GroupName, Resource: "replicaset"}, + {Group: appsv1beta2.GroupName, Resource: "deployment"}, + {Group: "cheese.testing.k8s.io", Resource: "cheddar"}, + } + + return client, groupResources +} + +func TestGetScale(t *testing.T) { + scaleClient, groupResources := fakeScaleClient(t) + expectedScale := &autoscalingv1.Scale{ + TypeMeta: metav1.TypeMeta{ + Kind: "Scale", + APIVersion: autoscalingv1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: autoscalingv1.ScaleSpec{Replicas: 10}, + Status: autoscalingv1.ScaleStatus{ + Replicas: 10, + Selector: "foo=bar", + }, + } + + for _, groupResource := range groupResources { + scale, err := scaleClient.Scales("default").Get(groupResource, "foo") + if !assert.NoError(t, err, "should have been able to fetch a scale for %s", groupResource.String()) { + continue + } + assert.NotNil(t, scale, "should have returned a non-nil scale for %s", groupResource.String()) + + assert.Equal(t, expectedScale, scale, "should have returned the expected scale for %s", groupResource.String()) + } +} + +func TestUpdateScale(t *testing.T) { + scaleClient, groupResources := fakeScaleClient(t) + expectedScale := &autoscalingv1.Scale{ + TypeMeta: metav1.TypeMeta{ + Kind: "Scale", + APIVersion: autoscalingv1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: autoscalingv1.ScaleSpec{Replicas: 10}, + Status: autoscalingv1.ScaleStatus{ + Replicas: 10, + Selector: "foo=bar", + }, + } + + for _, groupResource := range groupResources { + scale, err := scaleClient.Scales("default").Update(groupResource, expectedScale) + if !assert.NoError(t, err, "should have been able to fetch a scale for %s", groupResource.String()) { + continue + } + assert.NotNil(t, scale, "should have returned a non-nil scale for %s", groupResource.String()) + + assert.Equal(t, expectedScale, scale, "should have returned the expected scale for %s", groupResource.String()) + } +} diff --git a/staging/src/k8s.io/client-go/scale/doc.go b/staging/src/k8s.io/client-go/scale/doc.go new file mode 100644 index 00000000000..59fd39146ca --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2017 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 scale provides a polymorphic scale client capable of fetching +// and updating Scale for any resource which implements the `scale` subresource, +// as long as that subresource operates on a version of scale convertable to +// autoscaling.Scale. +package scale diff --git a/staging/src/k8s.io/client-go/scale/fake/BUILD b/staging/src/k8s.io/client-go/scale/fake/BUILD new file mode 100644 index 00000000000..8c9374305e2 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/fake/BUILD @@ -0,0 +1,28 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["client.go"], + importpath = "k8s.io/client-go/scale/fake", + visibility = ["//visibility:public"], + deps = [ + "//vendor/k8s.io/api/autoscaling/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/client-go/scale:go_default_library", + "//vendor/k8s.io/client-go/testing:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/staging/src/k8s.io/client-go/scale/fake/client.go b/staging/src/k8s.io/client-go/scale/fake/client.go new file mode 100644 index 00000000000..1736680f14d --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/fake/client.go @@ -0,0 +1,67 @@ +/* +Copyright 2017 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 fake provides a fake client interface to arbitrary Kubernetes +// APIs that exposes common high level operations and exposes common +// metadata. +package fake + +import ( + autoscalingapi "k8s.io/api/autoscaling/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/scale" + "k8s.io/client-go/testing" +) + +// FakeScaleClient provides a fake implementation of scale.ScalesGetter. +type FakeScaleClient struct { + testing.Fake +} + +func (f *FakeScaleClient) Scales(namespace string) scale.ScaleInterface { + return &fakeNamespacedScaleClient{ + namespace: namespace, + fake: &f.Fake, + } +} + +type fakeNamespacedScaleClient struct { + namespace string + fake *testing.Fake +} + +func (f *fakeNamespacedScaleClient) Get(resource schema.GroupResource, name string) (*autoscalingapi.Scale, error) { + obj, err := f.fake. + Invokes(testing.NewGetSubresourceAction(resource.WithVersion(""), f.namespace, "scale", name), &autoscalingapi.Scale{}) + + if err != nil { + return nil, err + } + + return obj.(*autoscalingapi.Scale), err +} + +func (f *fakeNamespacedScaleClient) Update(resource schema.GroupResource, scale *autoscalingapi.Scale) (*autoscalingapi.Scale, error) { + obj, err := f.fake. + Invokes(testing.NewUpdateSubresourceAction(resource.WithVersion(""), f.namespace, "scale", scale), &autoscalingapi.Scale{}) + + if err != nil { + return nil, err + } + + return obj.(*autoscalingapi.Scale), err + +} diff --git a/staging/src/k8s.io/client-go/scale/interfaces.go b/staging/src/k8s.io/client-go/scale/interfaces.go new file mode 100644 index 00000000000..4668c7417d1 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/interfaces.go @@ -0,0 +1,39 @@ +/* +Copyright 2017 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 scale + +import ( + autoscalingapi "k8s.io/api/autoscaling/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// ScalesGetter can produce a ScaleInterface +// for a particular namespace. +type ScalesGetter interface { + Scales(namespace string) ScaleInterface +} + +// ScaleInterface can fetch and update scales for +// resources in a particular namespace which implement +// the scale subresource. +type ScaleInterface interface { + // Get fetches the scale of the given scalable resource. + Get(resource schema.GroupResource, name string) (*autoscalingapi.Scale, error) + + // Update updates the scale of the the given scalable resource. + Update(resource schema.GroupResource, scale *autoscalingapi.Scale) (*autoscalingapi.Scale, error) +} diff --git a/staging/src/k8s.io/client-go/scale/roundtrip_test.go b/staging/src/k8s.io/client-go/scale/roundtrip_test.go new file mode 100644 index 00000000000..2ea28875700 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/roundtrip_test.go @@ -0,0 +1,34 @@ +/* +Copyright 2017 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 scale + +import ( + "testing" + + "k8s.io/apimachinery/pkg/api/testing/roundtrip" +) + +// NB: this can't be in the scheme package, because importing' +// scheme/autoscalingv1 from scheme causes a depedency loop from +// conversions + +func TestRoundTrip(t *testing.T) { + scheme := NewScaleConverter().Scheme() + // we don't actually need any custom fuzzer funcs ATM -- the defaults + // will do just fine + roundtrip.RoundTripTestForScheme(t, scheme, nil) +} diff --git a/staging/src/k8s.io/client-go/scale/scheme/BUILD b/staging/src/k8s.io/client-go/scale/scheme/BUILD new file mode 100644 index 00000000000..effa9141927 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/BUILD @@ -0,0 +1,39 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "doc.go", + "register.go", + "types.go", + "zz_generated.deepcopy.go", + ], + importpath = "k8s.io/client-go/scale/scheme", + visibility = ["//visibility:public"], + deps = [ + "//vendor/k8s.io/api/autoscaling/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [ + ":package-srcs", + "//staging/src/k8s.io/client-go/scale/scheme/autoscalingv1:all-srcs", + "//staging/src/k8s.io/client-go/scale/scheme/extensionsint:all-srcs", + "//staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1:all-srcs", + ], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/staging/src/k8s.io/client-go/scale/scheme/autoscalingv1/BUILD b/staging/src/k8s.io/client-go/scale/scheme/autoscalingv1/BUILD new file mode 100644 index 00000000000..646a6fdf8e7 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/autoscalingv1/BUILD @@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "conversion.go", + "doc.go", + "register.go", + "zz_generated.conversion.go", + ], + importpath = "k8s.io/client-go/scale/scheme/autoscalingv1", + visibility = ["//visibility:public"], + deps = [ + "//vendor/k8s.io/api/autoscaling/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/client-go/scale/scheme:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/staging/src/k8s.io/client-go/scale/scheme/autoscalingv1/conversion.go b/staging/src/k8s.io/client-go/scale/scheme/autoscalingv1/conversion.go new file mode 100644 index 00000000000..a775bcc225b --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/autoscalingv1/conversion.go @@ -0,0 +1,69 @@ +/* +Copyright 2017 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 autoscalingv1 + +import ( + "fmt" + + v1 "k8s.io/api/autoscaling/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/conversion" + "k8s.io/apimachinery/pkg/runtime" + scheme "k8s.io/client-go/scale/scheme" +) + +// addConversions registers conversions between the internal version +// of Scale and supported external versions of Scale. +func addConversionFuncs(scheme *runtime.Scheme) error { + err := scheme.AddConversionFuncs( + Convert_scheme_ScaleStatus_To_v1_ScaleStatus, + Convert_v1_ScaleStatus_To_scheme_ScaleStatus, + ) + if err != nil { + return err + } + + return nil +} + +func Convert_scheme_ScaleStatus_To_v1_ScaleStatus(in *scheme.ScaleStatus, out *v1.ScaleStatus, s conversion.Scope) error { + out.Replicas = in.Replicas + out.Selector = "" + if in.Selector != nil { + selector, err := metav1.LabelSelectorAsSelector(in.Selector) + if err != nil { + return fmt.Errorf("invalid label selector: %v", err) + } + out.Selector = selector.String() + } + + return nil +} + +func Convert_v1_ScaleStatus_To_scheme_ScaleStatus(in *v1.ScaleStatus, out *scheme.ScaleStatus, s conversion.Scope) error { + out.Replicas = in.Replicas + if in.Selector != "" { + labelSelector, err := metav1.ParseToLabelSelector(in.Selector) + if err != nil { + out.Selector = nil + return fmt.Errorf("failed to parse target selector: %v", err) + } + out.Selector = labelSelector + } + + return nil +} diff --git a/staging/src/k8s.io/client-go/scale/scheme/autoscalingv1/doc.go b/staging/src/k8s.io/client-go/scale/scheme/autoscalingv1/doc.go new file mode 100644 index 00000000000..aff53e4c37b --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/autoscalingv1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2017 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. +*/ + +// +k8s:conversion-gen=k8s.io/kubernetes/vendor/k8s.io/client-go/scale/scheme +// +k8s:conversion-gen-external-types=../../../../../k8s.io/api/autoscaling/v1 + +package autoscalingv1 // import "k8s.io/client-go/scale/scheme/autoscalingv1" diff --git a/staging/src/k8s.io/client-go/scale/scheme/autoscalingv1/register.go b/staging/src/k8s.io/client-go/scale/scheme/autoscalingv1/register.go new file mode 100644 index 00000000000..b15701c4ff7 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/autoscalingv1/register.go @@ -0,0 +1,45 @@ +/* +Copyright 2017 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 autoscalingv1 + +import ( + autoscalingapiv1 "k8s.io/api/autoscaling/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the group name use in this package +const GroupName = autoscalingapiv1.GroupName + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + localSchemeBuilder = &autoscalingapiv1.SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addConversionFuncs) +} diff --git a/staging/src/k8s.io/client-go/scale/scheme/autoscalingv1/zz_generated.conversion.go b/staging/src/k8s.io/client-go/scale/scheme/autoscalingv1/zz_generated.conversion.go new file mode 100644 index 00000000000..1eaa0d18027 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/autoscalingv1/zz_generated.conversion.go @@ -0,0 +1,109 @@ +// +build !ignore_autogenerated + +/* +Copyright 2017 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. +*/ + +// This file was autogenerated by conversion-gen. Do not edit it manually! + +package autoscalingv1 + +import ( + v1 "k8s.io/api/autoscaling/v1" + conversion "k8s.io/apimachinery/pkg/conversion" + runtime "k8s.io/apimachinery/pkg/runtime" + scheme "k8s.io/client-go/scale/scheme" +) + +func init() { + localSchemeBuilder.Register(RegisterConversions) +} + +// RegisterConversions adds conversion functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterConversions(scheme *runtime.Scheme) error { + return scheme.AddGeneratedConversionFuncs( + Convert_v1_Scale_To_scheme_Scale, + Convert_scheme_Scale_To_v1_Scale, + Convert_v1_ScaleSpec_To_scheme_ScaleSpec, + Convert_scheme_ScaleSpec_To_v1_ScaleSpec, + Convert_v1_ScaleStatus_To_scheme_ScaleStatus, + Convert_scheme_ScaleStatus_To_v1_ScaleStatus, + ) +} + +func autoConvert_v1_Scale_To_scheme_Scale(in *v1.Scale, out *scheme.Scale, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1_ScaleSpec_To_scheme_ScaleSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1_ScaleStatus_To_scheme_ScaleStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1_Scale_To_scheme_Scale is an autogenerated conversion function. +func Convert_v1_Scale_To_scheme_Scale(in *v1.Scale, out *scheme.Scale, s conversion.Scope) error { + return autoConvert_v1_Scale_To_scheme_Scale(in, out, s) +} + +func autoConvert_scheme_Scale_To_v1_Scale(in *scheme.Scale, out *v1.Scale, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_scheme_ScaleSpec_To_v1_ScaleSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_scheme_ScaleStatus_To_v1_ScaleStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_scheme_Scale_To_v1_Scale is an autogenerated conversion function. +func Convert_scheme_Scale_To_v1_Scale(in *scheme.Scale, out *v1.Scale, s conversion.Scope) error { + return autoConvert_scheme_Scale_To_v1_Scale(in, out, s) +} + +func autoConvert_v1_ScaleSpec_To_scheme_ScaleSpec(in *v1.ScaleSpec, out *scheme.ScaleSpec, s conversion.Scope) error { + out.Replicas = in.Replicas + return nil +} + +// Convert_v1_ScaleSpec_To_scheme_ScaleSpec is an autogenerated conversion function. +func Convert_v1_ScaleSpec_To_scheme_ScaleSpec(in *v1.ScaleSpec, out *scheme.ScaleSpec, s conversion.Scope) error { + return autoConvert_v1_ScaleSpec_To_scheme_ScaleSpec(in, out, s) +} + +func autoConvert_scheme_ScaleSpec_To_v1_ScaleSpec(in *scheme.ScaleSpec, out *v1.ScaleSpec, s conversion.Scope) error { + out.Replicas = in.Replicas + return nil +} + +// Convert_scheme_ScaleSpec_To_v1_ScaleSpec is an autogenerated conversion function. +func Convert_scheme_ScaleSpec_To_v1_ScaleSpec(in *scheme.ScaleSpec, out *v1.ScaleSpec, s conversion.Scope) error { + return autoConvert_scheme_ScaleSpec_To_v1_ScaleSpec(in, out, s) +} + +func autoConvert_v1_ScaleStatus_To_scheme_ScaleStatus(in *v1.ScaleStatus, out *scheme.ScaleStatus, s conversion.Scope) error { + out.Replicas = in.Replicas + // WARNING: in.Selector requires manual conversion: inconvertible types (string vs *k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector) + return nil +} + +func autoConvert_scheme_ScaleStatus_To_v1_ScaleStatus(in *scheme.ScaleStatus, out *v1.ScaleStatus, s conversion.Scope) error { + out.Replicas = in.Replicas + // WARNING: in.Selector requires manual conversion: inconvertible types (*k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector vs string) + return nil +} diff --git a/staging/src/k8s.io/client-go/scale/scheme/doc.go b/staging/src/k8s.io/client-go/scale/scheme/doc.go new file mode 100644 index 00000000000..8a050ce47b4 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/doc.go @@ -0,0 +1,22 @@ +/* +Copyright 2017 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. +*/ + +// +k8s:deepcopy-gen=package,register + +// Package scheme contains a runtime.Scheme to be used for serializing +// and deserializing different versions of Scale, and for converting +// in between them. +package scheme diff --git a/staging/src/k8s.io/client-go/scale/scheme/extensionsint/BUILD b/staging/src/k8s.io/client-go/scale/scheme/extensionsint/BUILD new file mode 100644 index 00000000000..6174a88b145 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/extensionsint/BUILD @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "doc.go", + "register.go", + ], + importpath = "k8s.io/client-go/scale/scheme/extensionsint", + visibility = ["//visibility:public"], + deps = [ + "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/client-go/scale/scheme:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/staging/src/k8s.io/client-go/scale/scheme/extensionsint/doc.go b/staging/src/k8s.io/client-go/scale/scheme/extensionsint/doc.go new file mode 100644 index 00000000000..cc92bf18824 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/extensionsint/doc.go @@ -0,0 +1,22 @@ +/* +Copyright 2017 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 extensionsint contains the necessary scaffolding of the +// internal version of extensions as required by conversion logic. +// It doesn't have any of its own types -- it's just necessary to +// get the expected behavoir out of runtime.Scheme.ConvertToVersion +// and associated methods. +package extensionsint diff --git a/staging/src/k8s.io/client-go/scale/scheme/extensionsint/register.go b/staging/src/k8s.io/client-go/scale/scheme/extensionsint/register.go new file mode 100644 index 00000000000..5a96ac56140 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/extensionsint/register.go @@ -0,0 +1,53 @@ +/* +Copyright 2016 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 extensionsint + +import ( + extensionsv1beta1 "k8s.io/api/extensions/v1beta1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + scalescheme "k8s.io/client-go/scale/scheme" +) + +// GroupName is the group name use in this package +const GroupName = extensionsv1beta1.GroupName + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} + +// Kind takes an unqualified kind and returns a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &scalescheme.Scale{}, + ) + return nil +} diff --git a/staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1/BUILD b/staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1/BUILD new file mode 100644 index 00000000000..4c992c7d113 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1/BUILD @@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "conversion.go", + "doc.go", + "register.go", + "zz_generated.conversion.go", + ], + importpath = "k8s.io/client-go/scale/scheme/extensionsv1beta1", + visibility = ["//visibility:public"], + deps = [ + "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/client-go/scale/scheme:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1/conversion.go b/staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1/conversion.go new file mode 100644 index 00000000000..1b6b9e61037 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1/conversion.go @@ -0,0 +1,87 @@ +/* +Copyright 2017 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 extensionsv1beta1 + +import ( + "fmt" + + v1beta1 "k8s.io/api/extensions/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/conversion" + "k8s.io/apimachinery/pkg/runtime" + scheme "k8s.io/client-go/scale/scheme" +) + +// addConversions registers conversions between the internal version +// of Scale and supported external versions of Scale. +func addConversionFuncs(scheme *runtime.Scheme) error { + err := scheme.AddConversionFuncs( + Convert_scheme_ScaleStatus_To_v1beta1_ScaleStatus, + Convert_v1beta1_ScaleStatus_To_scheme_ScaleStatus, + ) + if err != nil { + return err + } + + return nil +} +func Convert_scheme_ScaleStatus_To_v1beta1_ScaleStatus(in *scheme.ScaleStatus, out *v1beta1.ScaleStatus, s conversion.Scope) error { + out.Replicas = in.Replicas + out.Selector = nil + out.TargetSelector = "" + if in.Selector != nil { + if in.Selector.MatchExpressions == nil || len(in.Selector.MatchExpressions) == 0 { + out.Selector = in.Selector.MatchLabels + } + + selector, err := metav1.LabelSelectorAsSelector(in.Selector) + if err != nil { + return fmt.Errorf("invalid label selector: %v", err) + } + out.TargetSelector = selector.String() + } + + return nil +} + +func Convert_v1beta1_ScaleStatus_To_scheme_ScaleStatus(in *v1beta1.ScaleStatus, out *scheme.ScaleStatus, s conversion.Scope) error { + out.Replicas = in.Replicas + + // Normally when 2 fields map to the same internal value we favor the old field, since + // old clients can't be expected to know about new fields but clients that know about the + // new field can be expected to know about the old field (though that's not quite true, due + // to kubectl apply). However, these fields are readonly, so any non-nil value should work. + if in.TargetSelector != "" { + labelSelector, err := metav1.ParseToLabelSelector(in.TargetSelector) + if err != nil { + out.Selector = nil + return fmt.Errorf("failed to parse target selector: %v", err) + } + out.Selector = labelSelector + } else if in.Selector != nil { + out.Selector = new(metav1.LabelSelector) + selector := make(map[string]string) + for key, val := range in.Selector { + selector[key] = val + } + out.Selector.MatchLabels = selector + } else { + out.Selector = nil + } + + return nil +} diff --git a/staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1/doc.go b/staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1/doc.go new file mode 100644 index 00000000000..40d0fc0e959 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2017 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. +*/ + +// +k8s:conversion-gen=k8s.io/kubernetes/vendor/k8s.io/client-go/scale/scheme +// +k8s:conversion-gen-external-types=../../../../../k8s.io/api/extensions/v1beta1 + +package extensionsv1beta1 // import "k8s.io/client-go/scale/scheme/extensionsv1beta1" diff --git a/staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1/register.go b/staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1/register.go new file mode 100644 index 00000000000..aed1174e023 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1/register.go @@ -0,0 +1,45 @@ +/* +Copyright 2017 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 extensionsv1beta1 + +import ( + extensionsapiv1beta1 "k8s.io/api/extensions/v1beta1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the group name use in this package +const GroupName = extensionsapiv1beta1.GroupName + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + localSchemeBuilder = &extensionsapiv1beta1.SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addConversionFuncs) +} diff --git a/staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1/zz_generated.conversion.go b/staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1/zz_generated.conversion.go new file mode 100644 index 00000000000..848cb8d2aa3 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/extensionsv1beta1/zz_generated.conversion.go @@ -0,0 +1,110 @@ +// +build !ignore_autogenerated + +/* +Copyright 2017 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. +*/ + +// This file was autogenerated by conversion-gen. Do not edit it manually! + +package extensionsv1beta1 + +import ( + v1beta1 "k8s.io/api/extensions/v1beta1" + conversion "k8s.io/apimachinery/pkg/conversion" + runtime "k8s.io/apimachinery/pkg/runtime" + scheme "k8s.io/client-go/scale/scheme" +) + +func init() { + localSchemeBuilder.Register(RegisterConversions) +} + +// RegisterConversions adds conversion functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterConversions(scheme *runtime.Scheme) error { + return scheme.AddGeneratedConversionFuncs( + Convert_v1beta1_Scale_To_scheme_Scale, + Convert_scheme_Scale_To_v1beta1_Scale, + Convert_v1beta1_ScaleSpec_To_scheme_ScaleSpec, + Convert_scheme_ScaleSpec_To_v1beta1_ScaleSpec, + Convert_v1beta1_ScaleStatus_To_scheme_ScaleStatus, + Convert_scheme_ScaleStatus_To_v1beta1_ScaleStatus, + ) +} + +func autoConvert_v1beta1_Scale_To_scheme_Scale(in *v1beta1.Scale, out *scheme.Scale, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta1_ScaleSpec_To_scheme_ScaleSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta1_ScaleStatus_To_scheme_ScaleStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_Scale_To_scheme_Scale is an autogenerated conversion function. +func Convert_v1beta1_Scale_To_scheme_Scale(in *v1beta1.Scale, out *scheme.Scale, s conversion.Scope) error { + return autoConvert_v1beta1_Scale_To_scheme_Scale(in, out, s) +} + +func autoConvert_scheme_Scale_To_v1beta1_Scale(in *scheme.Scale, out *v1beta1.Scale, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_scheme_ScaleSpec_To_v1beta1_ScaleSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_scheme_ScaleStatus_To_v1beta1_ScaleStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_scheme_Scale_To_v1beta1_Scale is an autogenerated conversion function. +func Convert_scheme_Scale_To_v1beta1_Scale(in *scheme.Scale, out *v1beta1.Scale, s conversion.Scope) error { + return autoConvert_scheme_Scale_To_v1beta1_Scale(in, out, s) +} + +func autoConvert_v1beta1_ScaleSpec_To_scheme_ScaleSpec(in *v1beta1.ScaleSpec, out *scheme.ScaleSpec, s conversion.Scope) error { + out.Replicas = in.Replicas + return nil +} + +// Convert_v1beta1_ScaleSpec_To_scheme_ScaleSpec is an autogenerated conversion function. +func Convert_v1beta1_ScaleSpec_To_scheme_ScaleSpec(in *v1beta1.ScaleSpec, out *scheme.ScaleSpec, s conversion.Scope) error { + return autoConvert_v1beta1_ScaleSpec_To_scheme_ScaleSpec(in, out, s) +} + +func autoConvert_scheme_ScaleSpec_To_v1beta1_ScaleSpec(in *scheme.ScaleSpec, out *v1beta1.ScaleSpec, s conversion.Scope) error { + out.Replicas = in.Replicas + return nil +} + +// Convert_scheme_ScaleSpec_To_v1beta1_ScaleSpec is an autogenerated conversion function. +func Convert_scheme_ScaleSpec_To_v1beta1_ScaleSpec(in *scheme.ScaleSpec, out *v1beta1.ScaleSpec, s conversion.Scope) error { + return autoConvert_scheme_ScaleSpec_To_v1beta1_ScaleSpec(in, out, s) +} + +func autoConvert_v1beta1_ScaleStatus_To_scheme_ScaleStatus(in *v1beta1.ScaleStatus, out *scheme.ScaleStatus, s conversion.Scope) error { + out.Replicas = in.Replicas + // WARNING: in.Selector requires manual conversion: inconvertible types (map[string]string vs *k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector) + // WARNING: in.TargetSelector requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_scheme_ScaleStatus_To_v1beta1_ScaleStatus(in *scheme.ScaleStatus, out *v1beta1.ScaleStatus, s conversion.Scope) error { + out.Replicas = in.Replicas + // WARNING: in.Selector requires manual conversion: inconvertible types (*k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector vs map[string]string) + return nil +} diff --git a/staging/src/k8s.io/client-go/scale/scheme/register.go b/staging/src/k8s.io/client-go/scale/scheme/register.go new file mode 100644 index 00000000000..7e6decfff5f --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/register.go @@ -0,0 +1,52 @@ +/* +Copyright 2016 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 scheme + +import ( + autoscalingv1 "k8s.io/api/autoscaling/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the group name use in this package +const GroupName = autoscalingv1.GroupName + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} + +// Kind takes an unqualified kind and returns a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &Scale{}, + ) + return nil +} diff --git a/staging/src/k8s.io/client-go/scale/scheme/types.go b/staging/src/k8s.io/client-go/scale/scheme/types.go new file mode 100644 index 00000000000..13aec2b3c3c --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/types.go @@ -0,0 +1,60 @@ +/* +Copyright 2017 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 scheme + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// This file contains our own "internal" version of scale that we use for conversions, +// since we can't use the main Kubernetes internal versions. + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Scale represents a scaling request for a resource. +type Scale struct { + metav1.TypeMeta + // Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata. + // +optional + metav1.ObjectMeta + + // defines the behavior of the scale. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status. + // +optional + Spec ScaleSpec + + // current status of the scale. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status. Read-only. + // +optional + Status ScaleStatus +} + +// ScaleSpec describes the attributes of a scale subresource. +type ScaleSpec struct { + // desired number of instances for the scaled object. + // +optional + Replicas int32 +} + +// ScaleStatus represents the current status of a scale subresource. +type ScaleStatus struct { + // actual number of observed instances of the scaled object. + Replicas int32 + + // label query over pods that should match the replicas count. + // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors + // +optional + Selector *metav1.LabelSelector +} diff --git a/staging/src/k8s.io/client-go/scale/scheme/zz_generated.deepcopy.go b/staging/src/k8s.io/client-go/scale/scheme/zz_generated.deepcopy.go new file mode 100644 index 00000000000..ec9f5f95ed1 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/scheme/zz_generated.deepcopy.go @@ -0,0 +1,123 @@ +// +build !ignore_autogenerated + +/* +Copyright 2017 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. +*/ + +// This file was autogenerated by deepcopy-gen. Do not edit it manually! + +package scheme + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + conversion "k8s.io/apimachinery/pkg/conversion" + runtime "k8s.io/apimachinery/pkg/runtime" + reflect "reflect" +) + +func init() { + SchemeBuilder.Register(RegisterDeepCopies) +} + +// RegisterDeepCopies adds deep-copy functions to the given scheme. Public +// to allow building arbitrary schemes. +// +// Deprecated: deepcopy registration will go away when static deepcopy is fully implemented. +func RegisterDeepCopies(scheme *runtime.Scheme) error { + return scheme.AddGeneratedDeepCopyFuncs( + conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { + in.(*Scale).DeepCopyInto(out.(*Scale)) + return nil + }, InType: reflect.TypeOf(&Scale{})}, + conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { + in.(*ScaleSpec).DeepCopyInto(out.(*ScaleSpec)) + return nil + }, InType: reflect.TypeOf(&ScaleSpec{})}, + conversion.GeneratedDeepCopyFunc{Fn: func(in interface{}, out interface{}, c *conversion.Cloner) error { + in.(*ScaleStatus).DeepCopyInto(out.(*ScaleStatus)) + return nil + }, InType: reflect.TypeOf(&ScaleStatus{})}, + ) +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Scale) DeepCopyInto(out *Scale) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Scale. +func (in *Scale) DeepCopy() *Scale { + if in == nil { + return nil + } + out := new(Scale) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Scale) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } else { + return nil + } +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScaleSpec) DeepCopyInto(out *ScaleSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScaleSpec. +func (in *ScaleSpec) DeepCopy() *ScaleSpec { + if in == nil { + return nil + } + out := new(ScaleSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScaleStatus) DeepCopyInto(out *ScaleStatus) { + *out = *in + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + if *in == nil { + *out = nil + } else { + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScaleStatus. +func (in *ScaleStatus) DeepCopy() *ScaleStatus { + if in == nil { + return nil + } + out := new(ScaleStatus) + in.DeepCopyInto(out) + return out +} diff --git a/staging/src/k8s.io/client-go/scale/util.go b/staging/src/k8s.io/client-go/scale/util.go new file mode 100644 index 00000000000..f5de4bc9a33 --- /dev/null +++ b/staging/src/k8s.io/client-go/scale/util.go @@ -0,0 +1,159 @@ +/* +Copyright 2017 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 scale + +import ( + "fmt" + "strings" + "sync" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" + scalescheme "k8s.io/client-go/scale/scheme" + scaleautoscaling "k8s.io/client-go/scale/scheme/autoscalingv1" + scaleextint "k8s.io/client-go/scale/scheme/extensionsint" + scaleext "k8s.io/client-go/scale/scheme/extensionsv1beta1" +) + +// ScaleKindResolver knows about the relationship between +// resources and the GroupVersionKind of their scale subresources. +type ScaleKindResolver interface { + // ScaleForResource returns the GroupVersionKind of the + // scale subresource for the given GroupVersionResource. + ScaleForResource(resource schema.GroupVersionResource) (scaleVersion schema.GroupVersionKind, err error) +} + +// discoveryScaleResolver is a ScaleKindResolver that uses +// a DiscoveryInterface to associate resources with their +// scale-kinds +type discoveryScaleResolver struct { + discoveryClient discovery.ServerResourcesInterface +} + +func (r *discoveryScaleResolver) ScaleForResource(inputRes schema.GroupVersionResource) (scaleVersion schema.GroupVersionKind, err error) { + groupVerResources, err := r.discoveryClient.ServerResourcesForGroupVersion(inputRes.GroupVersion().String()) + if err != nil { + return schema.GroupVersionKind{}, fmt.Errorf("unable to fetch discovery information for %s: %v", inputRes.String(), err) + } + + for _, resource := range groupVerResources.APIResources { + resourceParts := strings.SplitN(resource.Name, "/", 2) + if len(resourceParts) != 2 || resourceParts[0] != inputRes.Resource || resourceParts[1] != "scale" { + // skip non-scale resources, or scales for resources that we're not looking for + continue + } + + scaleGV := inputRes.GroupVersion() + if resource.Group != "" && resource.Version != "" { + scaleGV = schema.GroupVersion{ + Group: resource.Group, + Version: resource.Version, + } + } + + return scaleGV.WithKind(resource.Kind), nil + } + + return schema.GroupVersionKind{}, fmt.Errorf("could not find scale subresource for %s in discovery information", inputRes.String()) +} + +// cachedScaleKindResolver is a ScaleKindResolver that caches results +// from another ScaleKindResolver, re-fetching on cache misses. +type cachedScaleKindResolver struct { + base ScaleKindResolver + + cache map[schema.GroupVersionResource]schema.GroupVersionKind + mu sync.RWMutex +} + +func (r *cachedScaleKindResolver) ScaleForResource(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) { + r.mu.RLock() + gvk, isCached := r.cache[resource] + r.mu.RUnlock() + if isCached { + return gvk, nil + } + + // we could have multiple fetches of the same resources, but that's probably + // better than limiting to only one reader at once (mu.Mutex), + // or blocking checks for other resources while we fetch + // (mu.Lock before fetch). + gvk, err := r.base.ScaleForResource(resource) + if err != nil { + return schema.GroupVersionKind{}, err + } + + r.mu.Lock() + defer r.mu.Unlock() + r.cache[resource] = gvk + + return gvk, nil +} + +// NewDiscoveryScaleKindResolver creates a new ScaleKindResolver which uses information from the given +// disovery client to resolve the correct Scale GroupVersionKind for different resources. +func NewDiscoveryScaleKindResolver(client discovery.ServerResourcesInterface) ScaleKindResolver { + base := &discoveryScaleResolver{ + discoveryClient: client, + } + + return &cachedScaleKindResolver{ + base: base, + cache: make(map[schema.GroupVersionResource]schema.GroupVersionKind), + } +} + +// ScaleConverter knows how to convert between external scale versions. +type ScaleConverter struct { + scheme *runtime.Scheme + internalVersioner runtime.GroupVersioner +} + +// NewScaleConverter creates a new ScaleConverter for converting between +// Scales in autoscaling/v1 and extensions/v1beta1. +func NewScaleConverter() *ScaleConverter { + scheme := runtime.NewScheme() + scaleautoscaling.AddToScheme(scheme) + scalescheme.AddToScheme(scheme) + scaleext.AddToScheme(scheme) + scaleextint.AddToScheme(scheme) + + return &ScaleConverter{ + scheme: scheme, + internalVersioner: runtime.NewMultiGroupVersioner( + scalescheme.SchemeGroupVersion, + schema.GroupKind{Group: scaleext.GroupName, Kind: "Scale"}, + schema.GroupKind{Group: scaleautoscaling.GroupName, Kind: "Scale"}, + ), + } +} + +// Scheme returns the scheme used by this scale converter. +func (c *ScaleConverter) Scheme() *runtime.Scheme { + return c.scheme +} + +// ConvertToVersion converts the given *external* input object to the given output *external* output group-version. +func (c *ScaleConverter) ConvertToVersion(in runtime.Object, outVersion schema.GroupVersion) (runtime.Object, error) { + scaleInt, err := c.scheme.ConvertToVersion(in, c.internalVersioner) + if err != nil { + return nil, err + } + + return c.scheme.ConvertToVersion(scaleInt, outVersion) +}