diff --git a/pkg/kubectl/cmd/expose.go b/pkg/kubectl/cmd/expose.go index ac18dbd8161..ba696073588 100644 --- a/pkg/kubectl/cmd/expose.go +++ b/pkg/kubectl/cmd/expose.go @@ -27,7 +27,6 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructuredscheme" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/client-go/dynamic" "k8s.io/kubernetes/pkg/api/legacyscheme" @@ -92,7 +91,7 @@ type ExposeServiceOptions struct { EnforceNamespace bool Generators func(string) map[string]kubectl.Generator - CanBeExposed func(kind schema.GroupKind) error + CanBeExposed polymorphichelpers.CanBeExposedFunc ClientForMapping func(*meta.RESTMapping) (resource.RESTClient, error) MapBasedSelectorForObject func(runtime.Object) (string, error) PortsForObject polymorphichelpers.PortsForObjectFunc @@ -191,7 +190,7 @@ func (o *ExposeServiceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) e o.Generators = f.Generators o.Builder = f.NewBuilder() - o.CanBeExposed = f.CanBeExposed + o.CanBeExposed = polymorphichelpers.CanBeExposedFn o.ClientForMapping = f.ClientForMapping o.MapBasedSelectorForObject = f.MapBasedSelectorForObject o.PortsForObject = polymorphichelpers.PortsForObjectFn diff --git a/pkg/kubectl/cmd/rollout/rollout_pause.go b/pkg/kubectl/cmd/rollout/rollout_pause.go index 65888248cd9..9460e70397c 100644 --- a/pkg/kubectl/cmd/rollout/rollout_pause.go +++ b/pkg/kubectl/cmd/rollout/rollout_pause.go @@ -30,6 +30,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" + "k8s.io/kubernetes/pkg/kubectl/polymorphichelpers" "k8s.io/kubernetes/pkg/kubectl/scheme" "k8s.io/kubernetes/pkg/kubectl/util/i18n" ) @@ -41,7 +42,7 @@ type PauseConfig struct { PrintFlags *genericclioptions.PrintFlags ToPrinter func(string) (printers.ResourcePrinter, error) - Pauser func(info *resource.Info) ([]byte, error) + Pauser polymorphichelpers.ObjectPauserFunc Infos []*resource.Info genericclioptions.IOStreams @@ -101,7 +102,7 @@ func (o *PauseConfig) CompletePause(f cmdutil.Factory, cmd *cobra.Command, args return cmdutil.UsageErrorf(cmd, "%s", cmd.Use) } - o.Pauser = f.Pauser + o.Pauser = polymorphichelpers.ObjectPauserFn cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { @@ -136,7 +137,7 @@ func (o *PauseConfig) CompletePause(f cmdutil.Factory, cmd *cobra.Command, args func (o PauseConfig) RunPause() error { allErrs := []error{} - for _, patch := range set.CalculatePatches(o.Infos, cmdutil.InternalVersionJSONEncoder(), o.Pauser) { + for _, patch := range set.CalculatePatches(o.Infos, cmdutil.InternalVersionJSONEncoder(), set.PatchFn(o.Pauser)) { info := patch.Info if patch.Err != nil { resourceString := info.Mapping.Resource.Resource diff --git a/pkg/kubectl/cmd/rollout/rollout_resume.go b/pkg/kubectl/cmd/rollout/rollout_resume.go index 16dde958d83..3208bc3c2d7 100644 --- a/pkg/kubectl/cmd/rollout/rollout_resume.go +++ b/pkg/kubectl/cmd/rollout/rollout_resume.go @@ -30,6 +30,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" + "k8s.io/kubernetes/pkg/kubectl/polymorphichelpers" "k8s.io/kubernetes/pkg/kubectl/scheme" "k8s.io/kubernetes/pkg/kubectl/util/i18n" ) @@ -41,7 +42,7 @@ type ResumeConfig struct { PrintFlags *genericclioptions.PrintFlags ToPrinter func(string) (printers.ResourcePrinter, error) - Resumer func(object *resource.Info) ([]byte, error) + Resumer polymorphichelpers.ObjectResumerFunc Infos []*resource.Info genericclioptions.IOStreams @@ -99,7 +100,7 @@ func (o *ResumeConfig) CompleteResume(f cmdutil.Factory, cmd *cobra.Command, arg return cmdutil.UsageErrorf(cmd, "%s", cmd.Use) } - o.Resumer = f.Resumer + o.Resumer = polymorphichelpers.ObjectResumerFn cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { @@ -140,7 +141,7 @@ func (o *ResumeConfig) CompleteResume(f cmdutil.Factory, cmd *cobra.Command, arg func (o ResumeConfig) RunResume() error { allErrs := []error{} - for _, patch := range set.CalculatePatches(o.Infos, cmdutil.InternalVersionJSONEncoder(), o.Resumer) { + for _, patch := range set.CalculatePatches(o.Infos, cmdutil.InternalVersionJSONEncoder(), set.PatchFn(o.Resumer)) { info := patch.Info if patch.Err != nil { diff --git a/pkg/kubectl/cmd/rollout/rollout_undo.go b/pkg/kubectl/cmd/rollout/rollout_undo.go index 6d5f4e28b09..46a66a0dd7b 100644 --- a/pkg/kubectl/cmd/rollout/rollout_undo.go +++ b/pkg/kubectl/cmd/rollout/rollout_undo.go @@ -20,6 +20,7 @@ import ( "io" "github.com/spf13/cobra" + "k8s.io/kubernetes/pkg/kubectl/polymorphichelpers" utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/kubernetes/pkg/api/legacyscheme" @@ -139,7 +140,7 @@ func (o *UndoOptions) CompleteUndo(f cmdutil.Factory, cmd *cobra.Command, out io if err != nil { return err } - rollbacker, err := f.Rollbacker(info.ResourceMapping()) + rollbacker, err := polymorphichelpers.RollbackerFn(f, info.ResourceMapping()) if err != nil { return err } diff --git a/pkg/kubectl/cmd/set/helper.go b/pkg/kubectl/cmd/set/helper.go index 4d267c28da2..42b1bb8a9e6 100644 --- a/pkg/kubectl/cmd/set/helper.go +++ b/pkg/kubectl/cmd/set/helper.go @@ -118,16 +118,16 @@ type Patch struct { Patch []byte } -// patchFn is a function type that accepts an info object and returns a byte slice. -// Implementations of patchFn should update the object and return it encoded. -type patchFn func(*resource.Info) ([]byte, error) +// PatchFn is a function type that accepts an info object and returns a byte slice. +// Implementations of PatchFn should update the object and return it encoded. +type PatchFn func(runtime.Object) ([]byte, error) // CalculatePatch calls the mutation function on the provided info object, and generates a strategic merge patch for // the changes in the object. Encoder must be able to encode the info into the appropriate destination type. // This function returns whether the mutation function made any change in the original object. -func CalculatePatch(patch *Patch, encoder runtime.Encoder, mutateFn patchFn) bool { +func CalculatePatch(patch *Patch, encoder runtime.Encoder, mutateFn PatchFn) bool { patch.Before, patch.Err = runtime.Encode(encoder, patch.Info.Object) - patch.After, patch.Err = mutateFn(patch.Info) + patch.After, patch.Err = mutateFn(patch.Info.Object) if patch.Err != nil { return true } @@ -141,7 +141,7 @@ func CalculatePatch(patch *Patch, encoder runtime.Encoder, mutateFn patchFn) boo // CalculatePatches calculates patches on each provided info object. If the provided mutateFn // makes no change in an object, the object is not included in the final list of patches. -func CalculatePatches(infos []*resource.Info, encoder runtime.Encoder, mutateFn patchFn) []*Patch { +func CalculatePatches(infos []*resource.Info, encoder runtime.Encoder, mutateFn PatchFn) []*Patch { var patches []*Patch for _, info := range infos { patch := &Patch{Info: info} diff --git a/pkg/kubectl/cmd/set/set_env.go b/pkg/kubectl/cmd/set/set_env.go index 87a83c7a10d..51d01e51294 100644 --- a/pkg/kubectl/cmd/set/set_env.go +++ b/pkg/kubectl/cmd/set/set_env.go @@ -26,6 +26,7 @@ import ( "github.com/spf13/cobra" "k8s.io/api/core/v1" + meta "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" utilerrors "k8s.io/apimachinery/pkg/util/errors" @@ -323,12 +324,53 @@ func (o *EnvOptions) RunEnv() error { if err != nil { return err } - patches := CalculatePatches(infos, scheme.DefaultJSONEncoder(), func(info *resource.Info) ([]byte, error) { - _, err := o.updatePodSpecForObject(info.Object, func(spec *v1.PodSpec) error { + patches := CalculatePatches(infos, scheme.DefaultJSONEncoder(), func(obj runtime.Object) ([]byte, error) { + _, err := o.updatePodSpecForObject(obj, func(spec *v1.PodSpec) error { resolutionErrorsEncountered := false containers, _ := selectContainers(spec.Containers, o.ContainerSelector) + objName, err := meta.NewAccessor().Name(obj) + if err != nil { + return err + } + + gvks, _, err := scheme.Scheme.ObjectKinds(obj) + if err != nil { + return err + } + objKind := obj.GetObjectKind().GroupVersionKind().Kind + if len(objKind) == 0 { + for _, gvk := range gvks { + if len(gvk.Kind) == 0 { + continue + } + if len(gvk.Version) == 0 || gvk.Version == runtime.APIVersionInternal { + continue + } + + objKind = gvk.Kind + break + } + } + if len(containers) == 0 { - fmt.Fprintf(o.ErrOut, "warning: %s/%s does not have any containers matching %q\n", info.Mapping.Resource, info.Name, o.ContainerSelector) + if gvks, _, err := scheme.Scheme.ObjectKinds(obj); err == nil { + objKind := obj.GetObjectKind().GroupVersionKind().Kind + if len(objKind) == 0 { + for _, gvk := range gvks { + if len(gvk.Kind) == 0 { + continue + } + if len(gvk.Version) == 0 || gvk.Version == runtime.APIVersionInternal { + continue + } + + objKind = gvk.Kind + break + } + } + + fmt.Fprintf(o.ErrOut, "warning: %s/%s does not have any containers matching %q\n", objKind, objName, o.ContainerSelector) + } return nil } for _, c := range containers { @@ -343,7 +385,7 @@ func (o *EnvOptions) RunEnv() error { resolveErrors := map[string][]string{} store := envutil.NewResourceStore() - fmt.Fprintf(o.Out, "# %s %s, container %s\n", info.Mapping.Resource, info.Name, c.Name) + fmt.Fprintf(o.Out, "# %s %s, container %s\n", objKind, objName, c.Name) for _, env := range c.Env { // Print the simple value if env.ValueFrom == nil { @@ -357,7 +399,7 @@ func (o *EnvOptions) RunEnv() error { continue } - value, err := envutil.GetEnvVarRefValue(o.clientset, o.namespace, store, env.ValueFrom, info.Object, c) + value, err := envutil.GetEnvVarRefValue(o.clientset, o.namespace, store, env.ValueFrom, obj, c) // Print the resolved value if err == nil { fmt.Fprintf(o.Out, "%s=%s\n", env.Name, value) @@ -390,7 +432,7 @@ func (o *EnvOptions) RunEnv() error { }) if err == nil { - return runtime.Encode(scheme.DefaultJSONEncoder(), info.Object) + return runtime.Encode(scheme.DefaultJSONEncoder(), obj) } return nil, err }) diff --git a/pkg/kubectl/cmd/set/set_image.go b/pkg/kubectl/cmd/set/set_image.go index 2e79c77b386..0f3269bb52d 100644 --- a/pkg/kubectl/cmd/set/set_image.go +++ b/pkg/kubectl/cmd/set/set_image.go @@ -210,9 +210,9 @@ func (o *SetImageOptions) Validate() error { func (o *SetImageOptions) Run() error { allErrs := []error{} - patches := CalculatePatches(o.Infos, scheme.DefaultJSONEncoder(), func(info *resource.Info) ([]byte, error) { + patches := CalculatePatches(o.Infos, scheme.DefaultJSONEncoder(), func(obj runtime.Object) ([]byte, error) { transformed := false - _, err := o.UpdatePodSpecForObject(info.Object, func(spec *v1.PodSpec) error { + _, err := o.UpdatePodSpecForObject(obj, func(spec *v1.PodSpec) error { for name, image := range o.ContainerImages { var ( containerFound bool @@ -255,11 +255,11 @@ func (o *SetImageOptions) Run() error { return nil, nil } // record this change (for rollout history) - if err := o.Recorder.Record(info.Object); err != nil { + if err := o.Recorder.Record(obj); err != nil { glog.V(4).Infof("error recording current command: %v", err) } - return runtime.Encode(scheme.DefaultJSONEncoder(), info.Object) + return runtime.Encode(scheme.DefaultJSONEncoder(), obj) }) for _, patch := range patches { diff --git a/pkg/kubectl/cmd/set/set_resources.go b/pkg/kubectl/cmd/set/set_resources.go index 1ff8ad40793..8a70ec56b4f 100644 --- a/pkg/kubectl/cmd/set/set_resources.go +++ b/pkg/kubectl/cmd/set/set_resources.go @@ -222,9 +222,9 @@ func (o *SetResourcesOptions) Validate() error { func (o *SetResourcesOptions) Run() error { allErrs := []error{} - patches := CalculatePatches(o.Infos, scheme.DefaultJSONEncoder(), func(info *resource.Info) ([]byte, error) { + patches := CalculatePatches(o.Infos, scheme.DefaultJSONEncoder(), func(obj runtime.Object) ([]byte, error) { transformed := false - _, err := o.UpdatePodSpecForObject(info.Object, func(spec *v1.PodSpec) error { + _, err := o.UpdatePodSpecForObject(obj, func(spec *v1.PodSpec) error { containers, _ := selectContainers(spec.Containers, o.ContainerSelector) if len(containers) != 0 { for i := range containers { @@ -255,11 +255,11 @@ func (o *SetResourcesOptions) Run() error { return nil, nil } // record this change (for rollout history) - if err := o.Recorder.Record(info.Object); err != nil { + if err := o.Recorder.Record(obj); err != nil { glog.V(4).Infof("error recording current command: %v", err) } - return runtime.Encode(scheme.DefaultJSONEncoder(), info.Object) + return runtime.Encode(scheme.DefaultJSONEncoder(), obj) }) for _, patch := range patches { diff --git a/pkg/kubectl/cmd/set/set_selector.go b/pkg/kubectl/cmd/set/set_selector.go index 7468af37e9b..54cfbacc1f7 100644 --- a/pkg/kubectl/cmd/set/set_selector.go +++ b/pkg/kubectl/cmd/set/set_selector.go @@ -201,7 +201,7 @@ func (o *SetSelectorOptions) RunSelector() error { return r.Visit(func(info *resource.Info, err error) error { patch := &Patch{Info: info} - CalculatePatch(patch, scheme.DefaultJSONEncoder(), func(info *resource.Info) ([]byte, error) { + CalculatePatch(patch, scheme.DefaultJSONEncoder(), func(obj runtime.Object) ([]byte, error) { selectErr := updateSelectorForObject(info.Object, *o.selector) if selectErr != nil { return nil, selectErr diff --git a/pkg/kubectl/cmd/set/set_serviceaccount.go b/pkg/kubectl/cmd/set/set_serviceaccount.go index fc39516bad5..9b40cf96a9a 100644 --- a/pkg/kubectl/cmd/set/set_serviceaccount.go +++ b/pkg/kubectl/cmd/set/set_serviceaccount.go @@ -173,8 +173,8 @@ func (o *SetServiceAccountOptions) Complete(f cmdutil.Factory, cmd *cobra.Comman // Run creates and applies the patch either locally or calling apiserver. func (o *SetServiceAccountOptions) Run() error { patchErrs := []error{} - patchFn := func(info *resource.Info) ([]byte, error) { - _, err := o.updatePodSpecForObject(info.Object, func(podSpec *v1.PodSpec) error { + patchFn := func(obj runtime.Object) ([]byte, error) { + _, err := o.updatePodSpecForObject(obj, func(podSpec *v1.PodSpec) error { podSpec.ServiceAccountName = o.serviceAccountName return nil }) @@ -182,11 +182,11 @@ func (o *SetServiceAccountOptions) Run() error { return nil, err } // record this change (for rollout history) - if err := o.Recorder.Record(info.Object); err != nil { + if err := o.Recorder.Record(obj); err != nil { glog.V(4).Infof("error recording current command: %v", err) } - return runtime.Encode(scheme.DefaultJSONEncoder(), info.Object) + return runtime.Encode(scheme.DefaultJSONEncoder(), obj) } patches := CalculatePatches(o.infos, scheme.DefaultJSONEncoder(), patchFn) diff --git a/pkg/kubectl/cmd/set/set_subject.go b/pkg/kubectl/cmd/set/set_subject.go index 10fd4031e10..9a0cc736d5c 100644 --- a/pkg/kubectl/cmd/set/set_subject.go +++ b/pkg/kubectl/cmd/set/set_subject.go @@ -194,7 +194,7 @@ func (o *SubjectOptions) Validate() error { } func (o *SubjectOptions) Run(fn updateSubjects) error { - patches := CalculatePatches(o.Infos, scheme.DefaultJSONEncoder(), func(info *resource.Info) ([]byte, error) { + patches := CalculatePatches(o.Infos, scheme.DefaultJSONEncoder(), func(obj runtime.Object) ([]byte, error) { subjects := []rbacv1.Subject{} for _, user := range sets.NewString(o.Users...).List() { subject := rbacv1.Subject{ @@ -227,10 +227,10 @@ func (o *SubjectOptions) Run(fn updateSubjects) error { subjects = append(subjects, subject) } - transformed, err := updateSubjectForObject(info.Object, subjects, fn) + transformed, err := updateSubjectForObject(obj, subjects, fn) if transformed && err == nil { // TODO: switch UpdatePodSpecForObject to work on v1.PodSpec - return runtime.Encode(scheme.DefaultJSONEncoder(), info.Object) + return runtime.Encode(scheme.DefaultJSONEncoder(), obj) } return nil, err }) diff --git a/pkg/kubectl/cmd/util/BUILD b/pkg/kubectl/cmd/util/BUILD index 41cba673fa6..4b83c6451f6 100644 --- a/pkg/kubectl/cmd/util/BUILD +++ b/pkg/kubectl/cmd/util/BUILD @@ -16,7 +16,6 @@ go_library( visibility = ["//build/visible_to:pkg_kubectl_cmd_util_CONSUMERS"], deps = [ "//pkg/api/legacyscheme:go_default_library", - "//pkg/apis/apps:go_default_library", "//pkg/apis/core:go_default_library", "//pkg/apis/extensions:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library", diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 931d0f10a9f..04e64f65ffa 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -86,23 +86,12 @@ type ClientAccessFactory interface { // SuggestedPodTemplateResources returns a list of resource types that declare a pod template SuggestedPodTemplateResources() []schema.GroupResource - // Pauser marks the object in the info as paused. Currently supported only for Deployments. - // Returns the patched object in bytes and any error that occurred during the encoding or - // in case the object is already paused. - Pauser(info *resource.Info) ([]byte, error) - // Resumer resumes a paused object inside the info. Currently supported only for Deployments. - // Returns the patched object in bytes and any error that occurred during the encoding or - // in case the object is already resumed. - Resumer(info *resource.Info) ([]byte, error) - // Returns the default namespace to use in cases where no // other namespace is specified and whether the namespace was // overridden. DefaultNamespace() (string, bool, error) // Generators returns the generators for the provided command Generators(cmdName string) map[string]kubectl.Generator - // Check whether the kind of resources could be exposed - CanBeExposed(kind schema.GroupKind) error } // ObjectMappingFactory holds the second level of factory methods. These functions depend upon ClientAccessFactory methods. @@ -116,9 +105,6 @@ type ObjectMappingFactory interface { // Returns a Describer for displaying the specified RESTMapping type or an error. Describer(mapping *meta.RESTMapping) (printers.Describer, error) - // Returns a Rollbacker for changing the rollback version of the specified RESTMapping type or an error - Rollbacker(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) - // Returns a schema that can validate objects stored on disk. Validator(validate bool) (validation.Schema, error) // OpenAPISchema returns the schema openapi schema definition diff --git a/pkg/kubectl/cmd/util/factory_client_access.go b/pkg/kubectl/cmd/util/factory_client_access.go index 765ca14e7b2..0477aa45ad3 100644 --- a/pkg/kubectl/cmd/util/factory_client_access.go +++ b/pkg/kubectl/cmd/util/factory_client_access.go @@ -19,7 +19,6 @@ limitations under the License. package util import ( - "errors" "fmt" "io" @@ -41,7 +40,6 @@ import ( restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/kubernetes/pkg/api/legacyscheme" - "k8s.io/kubernetes/pkg/apis/apps" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" @@ -182,32 +180,6 @@ func (f *ring0Factory) SuggestedPodTemplateResources() []schema.GroupResource { } } -func (f *ring0Factory) Pauser(info *resource.Info) ([]byte, error) { - switch obj := info.Object.(type) { - case *extensions.Deployment: - if obj.Spec.Paused { - return nil, errors.New("is already paused") - } - obj.Spec.Paused = true - return runtime.Encode(InternalVersionJSONEncoder(), info.Object) - default: - return nil, fmt.Errorf("pausing is not supported") - } -} - -func (f *ring0Factory) Resumer(info *resource.Info) ([]byte, error) { - switch obj := info.Object.(type) { - case *extensions.Deployment: - if !obj.Spec.Paused { - return nil, errors.New("is not paused") - } - obj.Spec.Paused = false - return runtime.Encode(InternalVersionJSONEncoder(), info.Object) - default: - return nil, fmt.Errorf("resuming is not supported") - } -} - func (f *ring0Factory) DefaultNamespace() (string, bool, error) { return f.clientGetter.ToRawKubeConfigLoader().Namespace() } @@ -422,17 +394,6 @@ func (f *ring0Factory) Generators(cmdName string) map[string]kubectl.Generator { return DefaultGenerators(cmdName) } -func (f *ring0Factory) CanBeExposed(kind schema.GroupKind) error { - switch kind { - case api.Kind("ReplicationController"), api.Kind("Service"), api.Kind("Pod"), - extensions.Kind("Deployment"), apps.Kind("Deployment"), extensions.Kind("ReplicaSet"), apps.Kind("ReplicaSet"): - // nothing to do here - default: - return fmt.Errorf("cannot expose a %s", kind) - } - return nil -} - // this method exists to help us find the points still relying on internal types. func InternalVersionDecoder() runtime.Decoder { return legacyscheme.Codecs.UniversalDecoder() diff --git a/pkg/kubectl/cmd/util/factory_object_mapping.go b/pkg/kubectl/cmd/util/factory_object_mapping.go index 496f6d10e6d..aa7105a1bd0 100644 --- a/pkg/kubectl/cmd/util/factory_object_mapping.go +++ b/pkg/kubectl/cmd/util/factory_object_mapping.go @@ -26,7 +26,6 @@ import ( "k8s.io/client-go/dynamic" restclient "k8s.io/client-go/rest" api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi" openapivalidation "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/validation" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" @@ -132,14 +131,6 @@ func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RES return printersinternal.GenericDescriberFor(mapping, dynamicClient, eventsClient), nil } -func (f *ring1Factory) Rollbacker(mapping *meta.RESTMapping) (kubectl.Rollbacker, error) { - external, err := f.clientAccessFactory.KubernetesClientSet() - if err != nil { - return nil, err - } - return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), external) -} - func (f *ring1Factory) Validator(validate bool) (validation.Schema, error) { if !validate { return validation.NullSchema{}, nil diff --git a/pkg/kubectl/cmd/util/factory_test.go b/pkg/kubectl/cmd/util/factory_test.go index 360091f46c7..910a8b2da67 100644 --- a/pkg/kubectl/cmd/util/factory_test.go +++ b/pkg/kubectl/cmd/util/factory_test.go @@ -21,7 +21,6 @@ import ( "testing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/sets" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/kubectl" @@ -66,33 +65,6 @@ func TestProtocolsForObject(t *testing.T) { } } -func TestCanBeExposed(t *testing.T) { - factory := NewFactory(genericclioptions.NewTestConfigFlags()) - tests := []struct { - kind schema.GroupKind - expectErr bool - }{ - { - kind: api.Kind("ReplicationController"), - expectErr: false, - }, - { - kind: api.Kind("Node"), - expectErr: true, - }, - } - - for _, test := range tests { - err := factory.CanBeExposed(test.kind) - if test.expectErr && err == nil { - t.Error("unexpected non-error") - } - if !test.expectErr && err != nil { - t.Errorf("unexpected error: %v", err) - } - } -} - func TestMakePortsString(t *testing.T) { tests := []struct { ports []api.ServicePort diff --git a/pkg/kubectl/polymorphichelpers/BUILD b/pkg/kubectl/polymorphichelpers/BUILD index 9848dc98c24..b50030a3c86 100644 --- a/pkg/kubectl/polymorphichelpers/BUILD +++ b/pkg/kubectl/polymorphichelpers/BUILD @@ -5,17 +5,22 @@ go_library( srcs = [ "attachablepodforobject.go", "canbeautoscaled.go", + "canbeexposed.go", "helpers.go", "historyviewer.go", "interface.go", "logsforobject.go", + "objectpauser.go", + "objectresumer.go", "portsforobject.go", + "rollbacker.go", "statusviewer.go", "updatepodspec.go", ], importpath = "k8s.io/kubernetes/pkg/kubectl/polymorphichelpers", visibility = ["//visibility:public"], deps = [ + "//pkg/api/legacyscheme:go_default_library", "//pkg/apis/apps:go_default_library", "//pkg/apis/batch:go_default_library", "//pkg/apis/core:go_default_library", @@ -36,6 +41,7 @@ go_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/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured: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", @@ -48,6 +54,7 @@ go_library( go_test( name = "go_default_test", srcs = [ + "canbeexposed_test.go", "helpers_test.go", "logsforobject_test.go", "portsforobject_test.go", diff --git a/pkg/kubectl/polymorphichelpers/canbeexposed.go b/pkg/kubectl/polymorphichelpers/canbeexposed.go new file mode 100644 index 00000000000..af4463fe999 --- /dev/null +++ b/pkg/kubectl/polymorphichelpers/canbeexposed.go @@ -0,0 +1,38 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package polymorphichelpers + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/kubernetes/pkg/apis/apps" + api "k8s.io/kubernetes/pkg/apis/core" + "k8s.io/kubernetes/pkg/apis/extensions" +) + +// Check whether the kind of resources could be exposed +func canBeExposed(kind schema.GroupKind) error { + switch kind { + case api.Kind("ReplicationController"), api.Kind("Service"), api.Kind("Pod"), + extensions.Kind("Deployment"), apps.Kind("Deployment"), extensions.Kind("ReplicaSet"), apps.Kind("ReplicaSet"): + // nothing to do here + default: + return fmt.Errorf("cannot expose a %s", kind) + } + return nil +} diff --git a/pkg/kubectl/polymorphichelpers/canbeexposed_test.go b/pkg/kubectl/polymorphichelpers/canbeexposed_test.go new file mode 100644 index 00000000000..b9e8d492dc2 --- /dev/null +++ b/pkg/kubectl/polymorphichelpers/canbeexposed_test.go @@ -0,0 +1,50 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package polymorphichelpers + +import ( + "testing" + + "k8s.io/apimachinery/pkg/runtime/schema" + api "k8s.io/kubernetes/pkg/apis/core" +) + +func TestCanBeExposed(t *testing.T) { + tests := []struct { + kind schema.GroupKind + expectErr bool + }{ + { + kind: api.Kind("ReplicationController"), + expectErr: false, + }, + { + kind: api.Kind("Node"), + expectErr: true, + }, + } + + for _, test := range tests { + err := canBeExposed(test.kind) + if test.expectErr && err == nil { + t.Error("unexpected non-error") + } + if !test.expectErr && err != nil { + t.Errorf("unexpected error: %v", err) + } + } +} diff --git a/pkg/kubectl/polymorphichelpers/interface.go b/pkg/kubectl/polymorphichelpers/interface.go index bf0c230ce27..e2ecab3c20f 100644 --- a/pkg/kubectl/polymorphichelpers/interface.go +++ b/pkg/kubectl/polymorphichelpers/interface.go @@ -71,3 +71,31 @@ type CanBeAutoscaledFunc func(kind schema.GroupKind) error // CanBeAutoscaledFn gives a way to easily override the function for unit testing if needed var CanBeAutoscaledFn CanBeAutoscaledFunc = canBeAutoscaled + +// CanBeExposedFunc is a function type that can tell you whether a given GroupKind is capable of being exposed +type CanBeExposedFunc func(kind schema.GroupKind) error + +// CanBeExposedFn gives a way to easily override the function for unit testing if needed +var CanBeExposedFn CanBeExposedFunc = canBeExposed + +// ObjectPauserFunc is a function type that marks the object in a given info as paused. +type ObjectPauserFunc func(runtime.Object) ([]byte, error) + +// ObjectPauserFn gives a way to easily override the function for unit testing if needed. +// Returns the patched object in bytes and any error that occurred during the encoding or +// in case the object is already paused. +var ObjectPauserFn ObjectPauserFunc = defaultObjectPauser + +// ObjectResumerFunc is a function type that marks the object in a given info as resumed. +type ObjectResumerFunc func(runtime.Object) ([]byte, error) + +// ObjectResumerFn gives a way to easily override the function for unit testing if needed. +// Returns the patched object in bytes and any error that occurred during the encoding or +// in case the object is already resumed. +var ObjectResumerFn ObjectResumerFunc = defaultObjectResumer + +// RollbackerFunc gives a way to change the rollback version of the specified RESTMapping type +type RollbackerFunc func(restClientGetter genericclioptions.RESTClientGetter, mapping *meta.RESTMapping) (kubectl.Rollbacker, error) + +// RollbackerFn gives a way to easily override the function for unit testing if needed +var RollbackerFn RollbackerFunc = rollbacker diff --git a/pkg/kubectl/polymorphichelpers/objectpauser.go b/pkg/kubectl/polymorphichelpers/objectpauser.go new file mode 100644 index 00000000000..f6fbde55893 --- /dev/null +++ b/pkg/kubectl/polymorphichelpers/objectpauser.go @@ -0,0 +1,46 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package polymorphichelpers + +import ( + "errors" + "fmt" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/kubernetes/pkg/api/legacyscheme" + "k8s.io/kubernetes/pkg/apis/extensions" +) + +// Currently only supports Deployments. +func defaultObjectPauser(obj runtime.Object) ([]byte, error) { + switch obj := obj.(type) { + case *extensions.Deployment: + if obj.Spec.Paused { + return nil, errors.New("is already paused") + } + obj.Spec.Paused = true + return runtime.Encode(internalVersionJSONEncoder(), obj) + default: + return nil, fmt.Errorf("pausing is not supported") + } +} + +func internalVersionJSONEncoder() runtime.Encoder { + encoder := legacyscheme.Codecs.LegacyCodec(legacyscheme.Scheme.PrioritizedVersionsAllGroups()...) + return unstructured.JSONFallbackEncoder{Encoder: encoder} +} diff --git a/pkg/kubectl/polymorphichelpers/objectresumer.go b/pkg/kubectl/polymorphichelpers/objectresumer.go new file mode 100644 index 00000000000..84d8dff91dc --- /dev/null +++ b/pkg/kubectl/polymorphichelpers/objectresumer.go @@ -0,0 +1,38 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package polymorphichelpers + +import ( + "errors" + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/kubernetes/pkg/apis/extensions" +) + +func defaultObjectResumer(obj runtime.Object) ([]byte, error) { + switch obj := obj.(type) { + case *extensions.Deployment: + if !obj.Spec.Paused { + return nil, errors.New("is not paused") + } + obj.Spec.Paused = false + return runtime.Encode(internalVersionJSONEncoder(), obj) + default: + return nil, fmt.Errorf("resuming is not supported") + } +} diff --git a/pkg/kubectl/polymorphichelpers/rollbacker.go b/pkg/kubectl/polymorphichelpers/rollbacker.go new file mode 100644 index 00000000000..f57f475c6b7 --- /dev/null +++ b/pkg/kubectl/polymorphichelpers/rollbacker.go @@ -0,0 +1,38 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package polymorphichelpers + +import ( + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/client-go/kubernetes" + "k8s.io/kubernetes/pkg/kubectl" + "k8s.io/kubernetes/pkg/kubectl/genericclioptions" +) + +// Returns a Rollbacker for changing the rollback version of the specified RESTMapping type or an error +func rollbacker(restClientGetter genericclioptions.RESTClientGetter, mapping *meta.RESTMapping) (kubectl.Rollbacker, error) { + clientConfig, err := restClientGetter.ToRESTConfig() + if err != nil { + return nil, err + } + external, err := kubernetes.NewForConfig(clientConfig) + if err != nil { + return nil, err + } + + return kubectl.RollbackerFor(mapping.GroupVersionKind.GroupKind(), external) +}