diff --git a/pkg/kubectl/cmd/autoscale.go b/pkg/kubectl/cmd/autoscale.go index eb0054dfbf2..b9282e86dbd 100644 --- a/pkg/kubectl/cmd/autoscale.go +++ b/pkg/kubectl/cmd/autoscale.go @@ -24,7 +24,6 @@ import ( autoscalingv1 "k8s.io/api/autoscaling/v1" "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/runtime/schema" autoscalingv1client "k8s.io/client-go/kubernetes/typed/autoscaling/v1" "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/kubectl" @@ -33,6 +32,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" ) @@ -73,7 +73,7 @@ type AutoscaleOptions struct { namespace string dryRun bool builder *resource.Builder - canBeAutoscaled func(kind schema.GroupKind) error + canBeAutoscaled polymorphichelpers.CanBeAutoscaledFunc generatorFunc func(string, *meta.RESTMapping) (kubectl.StructuredGenerator, error) HPAClient autoscalingv1client.HorizontalPodAutoscalersGetter @@ -132,7 +132,7 @@ func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args o.dryRun = cmdutil.GetFlagBool(cmd, "dry-run") o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag) o.builder = f.NewBuilder() - o.canBeAutoscaled = f.CanBeAutoscaled + o.canBeAutoscaled = polymorphichelpers.CanBeAutoscaledFn o.args = args o.RecordFlags.Complete(f.Command(cmd, false)) diff --git a/pkg/kubectl/cmd/expose.go b/pkg/kubectl/cmd/expose.go index daecc86ca09..e4e7e7490dd 100644 --- a/pkg/kubectl/cmd/expose.go +++ b/pkg/kubectl/cmd/expose.go @@ -37,6 +37,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" ) @@ -94,7 +95,7 @@ type ExposeServiceOptions struct { CanBeExposed func(kind schema.GroupKind) error ClientForMapping func(*meta.RESTMapping) (resource.RESTClient, error) MapBasedSelectorForObject func(runtime.Object) (string, error) - PortsForObject func(runtime.Object) ([]string, error) + PortsForObject polymorphichelpers.PortsForObjectFunc ProtocolsForObject func(runtime.Object) (map[string]string, error) Namespace string @@ -193,7 +194,7 @@ func (o *ExposeServiceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) e o.CanBeExposed = f.CanBeExposed o.ClientForMapping = f.ClientForMapping o.MapBasedSelectorForObject = f.MapBasedSelectorForObject - o.PortsForObject = f.PortsForObject + o.PortsForObject = polymorphichelpers.PortsForObjectFn o.ProtocolsForObject = f.ProtocolsForObject o.Mapper, err = f.ToRESTMapper() if err != nil { diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 9af233887c1..bc549c455e1 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -82,8 +82,6 @@ type ClientAccessFactory interface { // new set-based selector is provided, an error is returned if the selector cannot be converted to a // map-based selector MapBasedSelectorForObject(object runtime.Object) (string, error) - // PortsForObject returns the ports associated with the provided object - PortsForObject(object runtime.Object) ([]string, error) // ProtocolsForObject returns the mapping associated with the provided object ProtocolsForObject(object runtime.Object) (map[string]string, error) @@ -111,8 +109,6 @@ type ClientAccessFactory interface { Generators(cmdName string) map[string]kubectl.Generator // Check whether the kind of resources could be exposed CanBeExposed(kind schema.GroupKind) error - // Check whether the kind of resources could be autoscaled - CanBeAutoscaled(kind schema.GroupKind) error } // ObjectMappingFactory holds the second level of factory methods. These functions depend upon ClientAccessFactory methods. @@ -178,16 +174,6 @@ func makePortsString(ports []api.ServicePort, useNodePort bool) string { return strings.Join(pieces, ",") } -func getPorts(spec api.PodSpec) []string { - result := []string{} - for _, container := range spec.Containers { - for _, port := range container.Ports { - result = append(result, strconv.Itoa(int(port.ContainerPort))) - } - } - return result -} - func getProtocols(spec api.PodSpec) map[string]string { result := make(map[string]string) for _, container := range spec.Containers { @@ -198,15 +184,6 @@ func getProtocols(spec api.PodSpec) map[string]string { return result } -// Extracts the ports exposed by a service from the given service spec. -func getServicePorts(spec api.ServiceSpec) []string { - result := []string{} - for _, servicePort := range spec.Ports { - result = append(result, strconv.Itoa(int(servicePort.Port))) - } - return result -} - // Extracts the protocols exposed by a service from the given service spec. func getServiceProtocols(spec api.ServiceSpec) map[string]string { result := make(map[string]string) diff --git a/pkg/kubectl/cmd/util/factory_client_access.go b/pkg/kubectl/cmd/util/factory_client_access.go index 9fb932d262c..1cffea783f3 100644 --- a/pkg/kubectl/cmd/util/factory_client_access.go +++ b/pkg/kubectl/cmd/util/factory_client_access.go @@ -160,24 +160,6 @@ func (f *ring0Factory) MapBasedSelectorForObject(object runtime.Object) (string, } } -func (f *ring0Factory) PortsForObject(object runtime.Object) ([]string, error) { - // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) - switch t := object.(type) { - case *api.ReplicationController: - return getPorts(t.Spec.Template.Spec), nil - case *api.Pod: - return getPorts(t.Spec), nil - case *api.Service: - return getServicePorts(t.Spec), nil - case *extensions.Deployment: - return getPorts(t.Spec.Template.Spec), nil - case *extensions.ReplicaSet: - return getPorts(t.Spec.Template.Spec), nil - default: - return nil, fmt.Errorf("cannot extract ports from %T", object) - } -} - func (f *ring0Factory) ProtocolsForObject(object runtime.Object) (map[string]string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) switch t := object.(type) { @@ -488,17 +470,6 @@ func (f *ring0Factory) CanBeExposed(kind schema.GroupKind) error { return nil } -func (f *ring0Factory) CanBeAutoscaled(kind schema.GroupKind) error { - switch kind { - case api.Kind("ReplicationController"), extensions.Kind("ReplicaSet"), - extensions.Kind("Deployment"), apps.Kind("Deployment"), apps.Kind("ReplicaSet"): - // nothing to do here - default: - return fmt.Errorf("cannot autoscale a %v", 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_test.go b/pkg/kubectl/cmd/util/factory_test.go index 8f9b120dde4..360091f46c7 100644 --- a/pkg/kubectl/cmd/util/factory_test.go +++ b/pkg/kubectl/cmd/util/factory_test.go @@ -28,37 +28,6 @@ import ( "k8s.io/kubernetes/pkg/kubectl/genericclioptions" ) -func TestPortsForObject(t *testing.T) { - f := NewFactory(genericclioptions.NewTestConfigFlags()) - - pod := &api.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Ports: []api.ContainerPort{ - { - ContainerPort: 101, - }, - }, - }, - }, - }, - } - - expected := sets.NewString("101") - ports, err := f.PortsForObject(pod) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - got := sets.NewString(ports...) - - if !expected.Equal(got) { - t.Fatalf("Ports mismatch! Expected %v, got %v", expected, got) - } -} - func TestProtocolsForObject(t *testing.T) { f := NewFactory(genericclioptions.NewTestConfigFlags()) diff --git a/pkg/kubectl/polymorphichelpers/BUILD b/pkg/kubectl/polymorphichelpers/BUILD index 1feaaf523b1..9848dc98c24 100644 --- a/pkg/kubectl/polymorphichelpers/BUILD +++ b/pkg/kubectl/polymorphichelpers/BUILD @@ -4,10 +4,12 @@ go_library( name = "go_default_library", srcs = [ "attachablepodforobject.go", + "canbeautoscaled.go", "helpers.go", "historyviewer.go", "interface.go", "logsforobject.go", + "portsforobject.go", "statusviewer.go", "updatepodspec.go", ], @@ -36,6 +38,7 @@ go_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/kubernetes:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", @@ -47,6 +50,7 @@ go_test( srcs = [ "helpers_test.go", "logsforobject_test.go", + "portsforobject_test.go", ], embed = [":go_default_library"], deps = [ @@ -63,6 +67,7 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/client-go/testing:go_default_library", ], diff --git a/pkg/kubectl/polymorphichelpers/canbeautoscaled.go b/pkg/kubectl/polymorphichelpers/canbeautoscaled.go new file mode 100644 index 00000000000..6009f2b5733 --- /dev/null +++ b/pkg/kubectl/polymorphichelpers/canbeautoscaled.go @@ -0,0 +1,37 @@ +/* +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" +) + +func canBeAutoscaled(kind schema.GroupKind) error { + switch kind { + case api.Kind("ReplicationController"), extensions.Kind("ReplicaSet"), + extensions.Kind("Deployment"), apps.Kind("Deployment"), apps.Kind("ReplicaSet"): + // nothing to do here + default: + return fmt.Errorf("cannot autoscale a %v", kind) + } + return nil +} diff --git a/pkg/kubectl/polymorphichelpers/interface.go b/pkg/kubectl/polymorphichelpers/interface.go index 3029730742e..bf0c230ce27 100644 --- a/pkg/kubectl/polymorphichelpers/interface.go +++ b/pkg/kubectl/polymorphichelpers/interface.go @@ -22,6 +22,7 @@ import ( "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/rest" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/kubectl" @@ -58,3 +59,15 @@ type UpdatePodSpecForObjectFunc func(obj runtime.Object, fn func(*v1.PodSpec) er // UpdatePodSpecForObjectFn gives a way to easily override the function for unit testing if needed var UpdatePodSpecForObjectFn UpdatePodSpecForObjectFunc = updatePodSpecForObject + +// PortsForObjectFunc returns the ports associated with the provided object +type PortsForObjectFunc func(object runtime.Object) ([]string, error) + +// PortsForObjectFn gives a way to easily override the function for unit testing if needed +var PortsForObjectFn PortsForObjectFunc = portsForObject + +// CanBeAutoscaledFunc checks whether the kind of resources could be autoscaled +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 diff --git a/pkg/kubectl/polymorphichelpers/portsforobject.go b/pkg/kubectl/polymorphichelpers/portsforobject.go new file mode 100644 index 00000000000..48318d30a98 --- /dev/null +++ b/pkg/kubectl/polymorphichelpers/portsforobject.go @@ -0,0 +1,62 @@ +/* +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" + "strconv" + + "k8s.io/apimachinery/pkg/runtime" + api "k8s.io/kubernetes/pkg/apis/core" + "k8s.io/kubernetes/pkg/apis/extensions" +) + +func portsForObject(object runtime.Object) ([]string, error) { + switch t := object.(type) { + case *api.ReplicationController: + return getPorts(t.Spec.Template.Spec), nil + case *api.Pod: + return getPorts(t.Spec), nil + case *api.Service: + return getServicePorts(t.Spec), nil + case *extensions.Deployment: + return getPorts(t.Spec.Template.Spec), nil + case *extensions.ReplicaSet: + return getPorts(t.Spec.Template.Spec), nil + default: + return nil, fmt.Errorf("cannot extract ports from %T", object) + } +} + +func getPorts(spec api.PodSpec) []string { + result := []string{} + for _, container := range spec.Containers { + for _, port := range container.Ports { + result = append(result, strconv.Itoa(int(port.ContainerPort))) + } + } + return result +} + +// Extracts the ports exposed by a service from the given service spec. +func getServicePorts(spec api.ServiceSpec) []string { + result := []string{} + for _, servicePort := range spec.Ports { + result = append(result, strconv.Itoa(int(servicePort.Port))) + } + return result +} diff --git a/pkg/kubectl/polymorphichelpers/portsforobject_test.go b/pkg/kubectl/polymorphichelpers/portsforobject_test.go new file mode 100644 index 00000000000..3bd05b1f803 --- /dev/null +++ b/pkg/kubectl/polymorphichelpers/portsforobject_test.go @@ -0,0 +1,54 @@ +/* +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" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" + api "k8s.io/kubernetes/pkg/apis/core" +) + +func TestPortsForObject(t *testing.T) { + pod := &api.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Ports: []api.ContainerPort{ + { + ContainerPort: 101, + }, + }, + }, + }, + }, + } + + expected := sets.NewString("101") + ports, err := portsForObject(pod) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + got := sets.NewString(ports...) + + if !expected.Equal(got) { + t.Fatalf("Ports mismatch! Expected %v, got %v", expected, got) + } +}