diff --git a/pkg/cmd/expose/expose_test.go b/pkg/cmd/expose/expose_test.go index a1d32d9a5..cf8968e93 100644 --- a/pkg/cmd/expose/expose_test.go +++ b/pkg/cmd/expose/expose_test.go @@ -337,7 +337,7 @@ func TestRunExposeService(t *testing.T) { input: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, }, - flags: map[string]string{"selector": "svc=frompod", "port": "90", "labels": "svc=frompod", "generator": "service/v2"}, + flags: map[string]string{"selector": "svc=frompod", "port": "90", "labels": "svc=frompod"}, output: &corev1.Service{ ObjectMeta: metav1.ObjectMeta{Name: "a-name-that-is-toooo-big-for-a-service-because-it-can-only-handle-63-characters"[:63], Namespace: "", Labels: map[string]string{"svc": "frompod"}}, Spec: corev1.ServiceSpec{ @@ -379,7 +379,7 @@ func TestRunExposeService(t *testing.T) { }, }, }, - flags: map[string]string{"selector": "svc=fromfoo", "generator": "service/v2", "name": "fromfoo", "dry-run": "client"}, + flags: map[string]string{"selector": "svc=fromfoo", "name": "fromfoo", "dry-run": "client"}, output: &corev1.Service{ ObjectMeta: metav1.ObjectMeta{Name: "fromfoo", Namespace: "", Labels: map[string]string{"svc": "multiport"}}, Spec: corev1.ServiceSpec{ @@ -438,7 +438,7 @@ func TestRunExposeService(t *testing.T) { }, }, }, - flags: map[string]string{"selector": "svc=fromfoo", "generator": "service/v2", "name": "fromfoo", "dry-run": "client"}, + flags: map[string]string{"selector": "svc=fromfoo", "name": "fromfoo", "dry-run": "client"}, output: &corev1.Service{ ObjectMeta: metav1.ObjectMeta{Name: "fromfoo", Namespace: "", Labels: map[string]string{"svc": "multiport"}}, Spec: corev1.ServiceSpec{ diff --git a/pkg/cmd/set/set_resources.go b/pkg/cmd/set/set_resources.go index 4bcb3390c..c6ea07927 100644 --- a/pkg/cmd/set/set_resources.go +++ b/pkg/cmd/set/set_resources.go @@ -18,11 +18,12 @@ package set import ( "fmt" + "strings" "github.com/spf13/cobra" - "k8s.io/klog/v2" v1 "k8s.io/api/core/v1" + apiresource "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" utilerrors "k8s.io/apimachinery/pkg/util/errors" @@ -31,8 +32,8 @@ import ( "k8s.io/cli-runtime/pkg/printers" "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/tools/clientcmd" + "k8s.io/klog/v2" cmdutil "k8s.io/kubectl/pkg/cmd/util" - generateversioned "k8s.io/kubectl/pkg/generate/versioned" "k8s.io/kubectl/pkg/polymorphichelpers" "k8s.io/kubectl/pkg/scheme" "k8s.io/kubectl/pkg/util/i18n" @@ -211,10 +212,17 @@ func (o *SetResourcesOptions) Validate() error { return fmt.Errorf("you must specify an update to requests or limits (in the form of --requests/--limits)") } - o.ResourceRequirements, err = generateversioned.HandleResourceRequirementsV1(map[string]string{"limits": o.Limits, "requests": o.Requests}) + o.ResourceRequirements = v1.ResourceRequirements{} + limits, err := parseResourceList(o.Limits) if err != nil { return err } + o.ResourceRequirements.Limits = limits + requests, err := parseResourceList(o.Requests) + if err != nil { + return err + } + o.ResourceRequirements.Requests = requests return nil } @@ -300,3 +308,24 @@ func (o *SetResourcesOptions) Run() error { } return utilerrors.NewAggregate(allErrs) } + +func parseResourceList(spec string) (v1.ResourceList, error) { + if spec == "" { + return nil, nil + } + + result := v1.ResourceList{} + for resourceStatement := range strings.SplitSeq(spec, ",") { + parts := strings.Split(resourceStatement, "=") + if len(parts) != 2 { + return nil, fmt.Errorf("invalid argument syntax %v, expected =", resourceStatement) + } + resourceName := v1.ResourceName(parts[0]) + resourceQuantity, err := apiresource.ParseQuantity(parts[1]) + if err != nil { + return nil, err + } + result[resourceName] = resourceQuantity + } + return result, nil +} diff --git a/pkg/cmd/util/helpers.go b/pkg/cmd/util/helpers.go index c9872d2d0..3d16c8974 100644 --- a/pkg/cmd/util/helpers.go +++ b/pkg/cmd/util/helpers.go @@ -906,15 +906,6 @@ func scaleClient(restClientGetter genericclioptions.RESTClientGetter) (scale.Sca return scale.New(restClient, mapper, dynamic.LegacyAPIPathResolverFunc, resolver), nil } -func Warning(cmdErr io.Writer, newGeneratorName, oldGeneratorName string) { - fmt.Fprintf(cmdErr, "WARNING: New generator %q specified, "+ - "but it isn't available. "+ - "Falling back to %q.\n", - newGeneratorName, - oldGeneratorName, - ) -} - // Difference removes any elements of subArray from fullArray and returns the result func Difference(fullArray []string, subArray []string) []string { exclude := make(map[string]bool, len(subArray)) diff --git a/pkg/generate/generate.go b/pkg/generate/generate.go deleted file mode 100644 index 4087c2196..000000000 --- a/pkg/generate/generate.go +++ /dev/null @@ -1,207 +0,0 @@ -/* -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 generate - -import ( - "fmt" - "reflect" - "strconv" - "strings" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - "k8s.io/apimachinery/pkg/runtime" - utilerrors "k8s.io/apimachinery/pkg/util/errors" -) - -// GeneratorFunc returns the generators for the provided command -type GeneratorFunc func(cmdName string) map[string]Generator - -// GeneratorParam is a parameter for a generator -// TODO: facilitate structured json generator input schemes -type GeneratorParam struct { - Name string - Required bool -} - -// Generator is an interface for things that can generate API objects from input -// parameters. One example is the "expose" generator that is capable of exposing -// new replication controllers and services, among other things. -type Generator interface { - // Generate creates an API object given a set of parameters - Generate(params map[string]interface{}) (runtime.Object, error) - // ParamNames returns the list of parameters that this generator uses - ParamNames() []GeneratorParam -} - -// StructuredGenerator is an interface for things that can generate API objects not using parameter injection -type StructuredGenerator interface { - // StructuredGenerator creates an API object using pre-configured parameters - StructuredGenerate() (runtime.Object, error) -} - -func IsZero(i interface{}) bool { - if i == nil { - return true - } - return reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) -} - -// ValidateParams ensures that all required params are present in the params map -func ValidateParams(paramSpec []GeneratorParam, params map[string]interface{}) error { - allErrs := []error{} - for ix := range paramSpec { - if paramSpec[ix].Required { - value, found := params[paramSpec[ix].Name] - if !found || IsZero(value) { - allErrs = append(allErrs, fmt.Errorf("Parameter: %s is required", paramSpec[ix].Name)) - } - } - } - return utilerrors.NewAggregate(allErrs) -} - -// AnnotateFlags annotates all flags that are used by generators. -func AnnotateFlags(cmd *cobra.Command, generators map[string]Generator) { - // Iterate over all generators and mark any flags used by them. - for name, generator := range generators { - generatorParams := map[string]struct{}{} - for _, param := range generator.ParamNames() { - generatorParams[param.Name] = struct{}{} - } - - cmd.Flags().VisitAll(func(flag *pflag.Flag) { - if _, found := generatorParams[flag.Name]; !found { - // This flag is not used by the current generator - // so skip it. - return - } - if flag.Annotations == nil { - flag.Annotations = map[string][]string{} - } - if annotations := flag.Annotations["generator"]; annotations == nil { - flag.Annotations["generator"] = []string{} - } - flag.Annotations["generator"] = append(flag.Annotations["generator"], name) - }) - } -} - -// EnsureFlagsValid ensures that no invalid flags are being used against a -func EnsureFlagsValid(cmd *cobra.Command, generators map[string]Generator, generatorInUse string) error { - AnnotateFlags(cmd, generators) - - allErrs := []error{} - cmd.Flags().VisitAll(func(flag *pflag.Flag) { - // If the flag hasn't changed, don't validate it. - if !flag.Changed { - return - } - // Look into the flag annotations for the generators that can use it. - if annotations := flag.Annotations["generator"]; len(annotations) > 0 { - annotationMap := map[string]struct{}{} - for _, ann := range annotations { - annotationMap[ann] = struct{}{} - } - // If the current generator is not annotated, then this flag shouldn't - // be used with it. - if _, found := annotationMap[generatorInUse]; !found { - allErrs = append(allErrs, fmt.Errorf("cannot use --%s with --generator=%s", flag.Name, generatorInUse)) - } - } - }) - return utilerrors.NewAggregate(allErrs) -} - -// MakeParams is a utility that creates generator parameters from a command line -func MakeParams(cmd *cobra.Command, params []GeneratorParam) map[string]interface{} { - result := map[string]interface{}{} - for ix := range params { - f := cmd.Flags().Lookup(params[ix].Name) - if f != nil { - result[params[ix].Name] = f.Value.String() - } - } - return result -} - -func MakeProtocols(protocols map[string]string) string { - out := []string{} - for key, value := range protocols { - out = append(out, fmt.Sprintf("%s/%s", key, value)) - } - return strings.Join(out, ",") -} - -func ParseProtocols(protocols interface{}) (map[string]string, error) { - protocolsString, isString := protocols.(string) - if !isString { - return nil, fmt.Errorf("expected string, found %v", protocols) - } - if len(protocolsString) == 0 { - return nil, fmt.Errorf("no protocols passed") - } - portProtocolMap := map[string]string{} - protocolsSlice := strings.Split(protocolsString, ",") - for ix := range protocolsSlice { - portProtocol := strings.Split(protocolsSlice[ix], "/") - if len(portProtocol) != 2 { - return nil, fmt.Errorf("unexpected port protocol mapping: %s", protocolsSlice[ix]) - } - if len(portProtocol[0]) == 0 { - return nil, fmt.Errorf("unexpected empty port") - } - if len(portProtocol[1]) == 0 { - return nil, fmt.Errorf("unexpected empty protocol") - } - portProtocolMap[portProtocol[0]] = portProtocol[1] - } - return portProtocolMap, nil -} - -// ParseLabels turns a string representation of a label set into a map[string]string -func ParseLabels(labelSpec interface{}) (map[string]string, error) { - labelString, isString := labelSpec.(string) - if !isString { - return nil, fmt.Errorf("expected string, found %v", labelSpec) - } - if len(labelString) == 0 { - return nil, fmt.Errorf("no label spec passed") - } - labels := map[string]string{} - labelSpecs := strings.Split(labelString, ",") - for ix := range labelSpecs { - labelSpec := strings.Split(labelSpecs[ix], "=") - if len(labelSpec) != 2 { - return nil, fmt.Errorf("unexpected label spec: %s", labelSpecs[ix]) - } - if len(labelSpec[0]) == 0 { - return nil, fmt.Errorf("unexpected empty label key") - } - labels[labelSpec[0]] = labelSpec[1] - } - return labels, nil -} - -func GetBool(params map[string]string, key string, defValue bool) (bool, error) { - if val, found := params[key]; !found { - return defValue, nil - } else { - return strconv.ParseBool(val) - } -} diff --git a/pkg/generate/generate_test.go b/pkg/generate/generate_test.go deleted file mode 100644 index f4d181499..000000000 --- a/pkg/generate/generate_test.go +++ /dev/null @@ -1,444 +0,0 @@ -/* -Copyright 2014 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 generate - -import ( - "fmt" - "reflect" - "strings" - "testing" - - "github.com/spf13/cobra" -) - -type TestStruct struct { - val int -} - -func TestIsZero(t *testing.T) { - tests := []struct { - name string - val interface{} - expectZero bool - }{ - { - name: "test1", - val: "", - expectZero: true, - }, - { - name: "test2", - val: nil, - expectZero: true, - }, - { - name: "test3", - val: 0, - expectZero: true, - }, - { - name: "test4", - val: TestStruct{}, - expectZero: true, - }, - { - name: "test5", - val: "foo", - expectZero: false, - }, - { - name: "test6", - val: 1, - expectZero: false, - }, - { - name: "test7", - val: TestStruct{val: 2}, - expectZero: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - output := IsZero(tt.val) - if output != tt.expectZero { - t.Errorf("expected: %v, saw %v", tt.expectZero, output) - } - }) - } -} - -func TestValidateParams(t *testing.T) { - tests := []struct { - name string - paramSpec []GeneratorParam - params map[string]interface{} - valid bool - }{ - { - name: "test1", - paramSpec: []GeneratorParam{}, - params: map[string]interface{}{}, - valid: true, - }, - { - name: "test2", - paramSpec: []GeneratorParam{ - {Name: "foo"}, - }, - params: map[string]interface{}{}, - valid: true, - }, - { - name: "test3", - paramSpec: []GeneratorParam{ - {Name: "foo", Required: true}, - }, - params: map[string]interface{}{ - "foo": "bar", - }, - valid: true, - }, - { - name: "test4", - paramSpec: []GeneratorParam{ - {Name: "foo", Required: true}, - }, - params: map[string]interface{}{ - "baz": "blah", - "foo": "bar", - }, - valid: true, - }, - { - name: "test5", - paramSpec: []GeneratorParam{ - {Name: "foo", Required: true}, - {Name: "baz", Required: true}, - }, - params: map[string]interface{}{ - "baz": "blah", - "foo": "bar", - }, - valid: true, - }, - { - name: "test6", - paramSpec: []GeneratorParam{ - {Name: "foo", Required: true}, - {Name: "baz", Required: true}, - }, - params: map[string]interface{}{ - "foo": "bar", - }, - valid: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := ValidateParams(tt.paramSpec, tt.params) - if tt.valid && err != nil { - t.Errorf("unexpected error: %v", err) - } - if !tt.valid && err == nil { - t.Errorf("unexpected non-error") - } - }) - } -} - -func TestMakeParams(t *testing.T) { - cmd := &cobra.Command{} - cmd.Flags().String("foo", "bar", "") - cmd.Flags().String("baz", "", "") - cmd.Flags().Set("baz", "blah") - - paramSpec := []GeneratorParam{ - {Name: "foo", Required: true}, - {Name: "baz", Required: true}, - } - expected := map[string]interface{}{ - "foo": "bar", - "baz": "blah", - } - params := MakeParams(cmd, paramSpec) - if !reflect.DeepEqual(params, expected) { - t.Errorf("\nexpected:\n%v\nsaw:\n%v", expected, params) - } -} - -func TestGetBool(t *testing.T) { - testCases := []struct { - name string - parameters map[string]string - key string - defaultValue bool - expected bool - expectError bool - }{ - { - name: "found key in parameters, default value is different from key value", - parameters: map[string]string{ - "foo": "false", - }, - key: "foo", - defaultValue: false, - expected: false, - expectError: false, - }, - { - name: "found key in parameters, default value is same with key value", - parameters: map[string]string{ - "foo": "true", - }, - key: "foo", - defaultValue: true, - expected: true, - expectError: false, - }, - { - name: "key not found in parameters, default value is true", - parameters: map[string]string{ - "foo": "true", - "far": "false", - }, - key: "bar", - defaultValue: true, - expected: true, - expectError: false, - }, - { - name: "key not found in parameters, default value is false", - parameters: map[string]string{ - "foo": "true", - "far": "false", - }, - key: "bar", - defaultValue: false, - expected: false, - expectError: false, - }, - { - name: "parameters is empty", - parameters: map[string]string{}, - key: "foo", - defaultValue: true, - expected: true, - expectError: false, - }, - { - name: "parameters key is not a valid bool value", - parameters: map[string]string{ - "foo": "error", - }, - key: "foo", - defaultValue: true, - expected: false, - expectError: true, - }, - } - for _, tt := range testCases { - t.Run(tt.name, func(t *testing.T) { - got, err := GetBool(tt.parameters, tt.key, tt.defaultValue) - if err != nil && !tt.expectError { - t.Errorf("%s: unexpected error: %v", tt.name, err) - } - if err == nil && tt.expectError { - t.Errorf("%s: expect error, got nil", tt.name) - } - if got != tt.expected { - t.Errorf("%s: expect %v, got %v", tt.name, tt.expected, got) - } - }) - } -} - -func makeLabels(labels map[string]string) string { - out := []string{} - for key, value := range labels { - out = append(out, fmt.Sprintf("%s=%s", key, value)) - } - return strings.Join(out, ",") -} - -func TestMakeParseLabels(t *testing.T) { - successCases := []struct { - name string - labels map[string]string - expected map[string]string - }{ - { - name: "test1", - labels: map[string]string{ - "foo": "false", - }, - expected: map[string]string{ - "foo": "false", - }, - }, - { - name: "test2", - labels: map[string]string{ - "foo": "true", - "bar": "123", - }, - expected: map[string]string{ - "foo": "true", - "bar": "123", - }, - }, - } - for _, tt := range successCases { - t.Run(tt.name, func(t *testing.T) { - labelString := makeLabels(tt.labels) - got, err := ParseLabels(labelString) - if err != nil { - t.Errorf("unexpected error :%v", err) - } - if !reflect.DeepEqual(tt.expected, got) { - t.Errorf("\nexpected:\n%v\ngot:\n%v", tt.expected, got) - } - }) - } - - errorCases := []struct { - name string - labels interface{} - }{ - { - name: "non-string", - labels: 123, - }, - { - name: "empty string", - labels: "", - }, - { - name: "error format", - labels: "abc=456;bcd=789", - }, - { - name: "error format", - labels: "abc=456.bcd=789", - }, - { - name: "error format", - labels: "abc,789", - }, - { - name: "error format", - labels: "abc", - }, - { - name: "error format", - labels: "=abc", - }, - } - for _, test := range errorCases { - _, err := ParseLabels(test.labels) - if err == nil { - t.Errorf("labels %s expect error, reason: %s, got nil", test.labels, test.name) - } - } -} - -func TestMakeParseProtocols(t *testing.T) { - successCases := []struct { - name string - protocols map[string]string - expected map[string]string - }{ - { - name: "test1", - protocols: map[string]string{ - "101": "TCP", - }, - expected: map[string]string{ - "101": "TCP", - }, - }, - { - name: "test2", - protocols: map[string]string{ - "102": "UDP", - "101": "TCP", - "103": "SCTP", - }, - expected: map[string]string{ - "102": "UDP", - "101": "TCP", - "103": "SCTP", - }, - }, - } - for _, tt := range successCases { - t.Run(tt.name, func(t *testing.T) { - protocolString := MakeProtocols(tt.protocols) - got, err := ParseProtocols(protocolString) - if err != nil { - t.Errorf("unexpected error :%v", err) - } - if !reflect.DeepEqual(tt.expected, got) { - t.Errorf("\nexpected:\n%v\ngot:\n%v", tt.expected, got) - } - }) - } - - errorCases := []struct { - name string - protocols interface{} - }{ - { - name: "non-string", - protocols: 123, - }, - { - name: "empty string", - protocols: "", - }, - { - name: "error format", - protocols: "123/TCP;456/UDP", - }, - { - name: "error format", - protocols: "123/TCP.456/UDP", - }, - { - name: "error format", - protocols: "123=456", - }, - { - name: "error format", - protocols: "123", - }, - { - name: "error format", - protocols: "123=", - }, - { - name: "error format", - protocols: "=TCP", - }, - } - for _, test := range errorCases { - _, err := ParseProtocols(test.protocols) - if err == nil { - t.Errorf("protocols %s expect error, reason: %s, got nil", test.protocols, test.name) - } - } -} diff --git a/pkg/generate/versioned/generator.go b/pkg/generate/versioned/generator.go deleted file mode 100644 index b36f631e6..000000000 --- a/pkg/generate/versioned/generator.go +++ /dev/null @@ -1,49 +0,0 @@ -/* -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 versioned - -import ( - "k8s.io/kubectl/pkg/generate" -) - -// GeneratorFn gives a way to easily override the function for unit testing if needed -var GeneratorFn generate.GeneratorFunc = DefaultGenerators - -const ( - // TODO(sig-cli): Enforce consistent naming for generators here. - // See discussion in https://github.com/kubernetes/kubernetes/issues/46237 - // before you add any more. - RunPodV1GeneratorName = "run-pod/v1" - ServiceV2GeneratorName = "service/v2" -) - -// DefaultGenerators returns the set of default generators for use in Factory instances -func DefaultGenerators(cmdName string) map[string]generate.Generator { - var generator map[string]generate.Generator - switch cmdName { - case "expose": - generator = map[string]generate.Generator{ - ServiceV2GeneratorName: ServiceGeneratorV2{}, - } - case "run": - generator = map[string]generate.Generator{ - RunPodV1GeneratorName: BasicPod{}, - } - } - - return generator -} diff --git a/pkg/generate/versioned/run.go b/pkg/generate/versioned/run.go deleted file mode 100644 index b264c5260..000000000 --- a/pkg/generate/versioned/run.go +++ /dev/null @@ -1,379 +0,0 @@ -/* -Copyright 2014 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 versioned - -import ( - "fmt" - "strconv" - "strings" - - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/validation" - cmdutil "k8s.io/kubectl/pkg/cmd/util" - "k8s.io/kubectl/pkg/generate" -) - -// getLabels returns map of labels. -func getLabels(params map[string]string, name string) (map[string]string, error) { - labelString, found := params["labels"] - var labels map[string]string - var err error - if found && len(labelString) > 0 { - labels, err = generate.ParseLabels(labelString) - if err != nil { - return nil, err - } - } else { - labels = map[string]string{ - "run": name, - } - } - return labels, nil -} - -// getName returns the name of newly created resource. -func getName(params map[string]string) (string, error) { - name, found := params["name"] - if !found || len(name) == 0 { - name, found = params["default-name"] - if !found || len(name) == 0 { - return "", fmt.Errorf("'name' is a required parameter") - } - } - return name, nil -} - -// getParams returns map of generic parameters. -func getParams(genericParams map[string]interface{}) (map[string]string, error) { - params := map[string]string{} - for key, value := range genericParams { - strVal, isString := value.(string) - if !isString { - return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key) - } - params[key] = strVal - } - return params, nil -} - -// getArgs returns arguments for the container command. -func getArgs(genericParams map[string]interface{}) ([]string, error) { - args := []string{} - val, found := genericParams["args"] - if found { - var isArray bool - args, isArray = val.([]string) - if !isArray { - return nil, fmt.Errorf("expected []string, found: %v", val) - } - delete(genericParams, "args") - } - return args, nil -} - -// getAnnotations returns map of annotations. -func getAnnotations(genericParams map[string]interface{}) (map[string]string, error) { - annotationStrings, ok := genericParams["annotations"] - if !ok { - return nil, nil - } - - annotationStringArray, ok := annotationStrings.([]string) - if !ok { - return nil, fmt.Errorf("expected []string, found: %v", annotationStrings) - } - - annotations, _, err := cmdutil.ParsePairs(annotationStringArray, "annotations", false) - if err != nil { - return nil, err - } - - delete(genericParams, "annotations") - return annotations, nil -} - -// getEnvs returns environment variables. -func getEnvs(genericParams map[string]interface{}) ([]v1.EnvVar, error) { - var envs []v1.EnvVar - envStrings, found := genericParams["env"] - if found { - if envStringArray, isArray := envStrings.([]string); isArray { - var err error - envs, err = parseEnvs(envStringArray) - if err != nil { - return nil, err - } - delete(genericParams, "env") - } else { - return nil, fmt.Errorf("expected []string, found: %v", envStrings) - } - } - return envs, nil -} - -// populateResourceListV1 takes strings of form =,= -// and returns ResourceList. -func populateResourceListV1(spec string) (v1.ResourceList, error) { - // empty input gets a nil response to preserve generator test expected behaviors - if spec == "" { - return nil, nil - } - - result := v1.ResourceList{} - resourceStatements := strings.Split(spec, ",") - for _, resourceStatement := range resourceStatements { - parts := strings.Split(resourceStatement, "=") - if len(parts) != 2 { - return nil, fmt.Errorf("Invalid argument syntax %v, expected =", resourceStatement) - } - resourceName := v1.ResourceName(parts[0]) - resourceQuantity, err := resource.ParseQuantity(parts[1]) - if err != nil { - return nil, err - } - result[resourceName] = resourceQuantity - } - return result, nil -} - -// HandleResourceRequirementsV1 parses the limits and requests parameters if specified -// and returns ResourceRequirements. -func HandleResourceRequirementsV1(params map[string]string) (v1.ResourceRequirements, error) { - result := v1.ResourceRequirements{} - limits, err := populateResourceListV1(params["limits"]) - if err != nil { - return result, err - } - result.Limits = limits - requests, err := populateResourceListV1(params["requests"]) - if err != nil { - return result, err - } - result.Requests = requests - return result, nil -} - -// updatePodContainers updates PodSpec.Containers with passed parameters. -func updatePodContainers(params map[string]string, args []string, envs []v1.EnvVar, imagePullPolicy v1.PullPolicy, podSpec *v1.PodSpec) error { - if len(args) > 0 { - command, err := generate.GetBool(params, "command", false) - if err != nil { - return err - } - if command { - podSpec.Containers[0].Command = args - } else { - podSpec.Containers[0].Args = args - } - } - - if len(envs) > 0 { - podSpec.Containers[0].Env = envs - } - - if len(imagePullPolicy) > 0 { - // imagePullPolicy should be valid here since we have verified it before. - podSpec.Containers[0].ImagePullPolicy = imagePullPolicy - } - return nil -} - -// updatePodContainers updates PodSpec.Containers.Ports with passed parameters. -func updatePodPorts(params map[string]string, podSpec *v1.PodSpec) (err error) { - port := -1 - hostPort := -1 - if len(params["port"]) > 0 { - port, err = strconv.Atoi(params["port"]) - if err != nil { - return err - } - } - - if len(params["hostport"]) > 0 { - hostPort, err = strconv.Atoi(params["hostport"]) - if err != nil { - return err - } - if hostPort > 0 && port < 0 { - return fmt.Errorf("--hostport requires --port to be specified") - } - } - - // Don't include the port if it was not specified. - if len(params["port"]) > 0 { - podSpec.Containers[0].Ports = []v1.ContainerPort{ - { - ContainerPort: int32(port), - }, - } - if hostPort > 0 { - podSpec.Containers[0].Ports[0].HostPort = int32(hostPort) - } - } - return nil -} - -type BasicPod struct{} - -func (BasicPod) ParamNames() []generate.GeneratorParam { - return []generate.GeneratorParam{ - {Name: "labels", Required: false}, - {Name: "annotations", Required: false}, - {Name: "default-name", Required: false}, - {Name: "name", Required: true}, - {Name: "image", Required: true}, - {Name: "image-pull-policy", Required: false}, - {Name: "port", Required: false}, - {Name: "hostport", Required: false}, - {Name: "stdin", Required: false}, - {Name: "leave-stdin-open", Required: false}, - {Name: "tty", Required: false}, - {Name: "restart", Required: false}, - {Name: "command", Required: false}, - {Name: "args", Required: false}, - {Name: "env", Required: false}, - {Name: "requests", Required: false}, - {Name: "limits", Required: false}, - {Name: "serviceaccount", Required: false}, - {Name: "privileged", Required: false}, - } -} - -func (BasicPod) Generate(genericParams map[string]interface{}) (runtime.Object, error) { - args, err := getArgs(genericParams) - if err != nil { - return nil, err - } - - envs, err := getEnvs(genericParams) - if err != nil { - return nil, err - } - - annotations, err := getAnnotations(genericParams) - if err != nil { - return nil, err - } - - params, err := getParams(genericParams) - if err != nil { - return nil, err - } - - name, err := getName(params) - if err != nil { - return nil, err - } - - labels, err := getLabels(params, name) - if err != nil { - return nil, err - } - - stdin, err := generate.GetBool(params, "stdin", false) - if err != nil { - return nil, err - } - leaveStdinOpen, err := generate.GetBool(params, "leave-stdin-open", false) - if err != nil { - return nil, err - } - - tty, err := generate.GetBool(params, "tty", false) - if err != nil { - return nil, err - } - - resourceRequirements, err := HandleResourceRequirementsV1(params) - if err != nil { - return nil, err - } - - restartPolicy := v1.RestartPolicy(params["restart"]) - if len(restartPolicy) == 0 { - restartPolicy = v1.RestartPolicyAlways - } - - privileged, err := generate.GetBool(params, "privileged", false) - if err != nil { - return nil, err - } - var securityContext *v1.SecurityContext - if privileged { - securityContext = &v1.SecurityContext{ - Privileged: &privileged, - } - } - - pod := v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: labels, - Annotations: annotations, - }, - Spec: v1.PodSpec{ - ServiceAccountName: params["serviceaccount"], - Containers: []v1.Container{ - { - Name: name, - Image: params["image"], - Stdin: stdin, - StdinOnce: !leaveStdinOpen && stdin, - TTY: tty, - Resources: resourceRequirements, - SecurityContext: securityContext, - }, - }, - DNSPolicy: v1.DNSClusterFirst, - RestartPolicy: restartPolicy, - }, - } - imagePullPolicy := v1.PullPolicy(params["image-pull-policy"]) - if err = updatePodContainers(params, args, envs, imagePullPolicy, &pod.Spec); err != nil { - return nil, err - } - - if err := updatePodPorts(params, &pod.Spec); err != nil { - return nil, err - } - return &pod, nil -} - -// parseEnvs converts string into EnvVar objects. -func parseEnvs(envArray []string) ([]v1.EnvVar, error) { - envs := make([]v1.EnvVar, 0, len(envArray)) - for _, env := range envArray { - pos := strings.Index(env, "=") - if pos == -1 { - return nil, fmt.Errorf("invalid env: %v", env) - } - name := env[:pos] - value := env[pos+1:] - if len(name) == 0 { - return nil, fmt.Errorf("invalid env: %v", env) - } - if len(validation.IsEnvVarName(name)) != 0 { - return nil, fmt.Errorf("invalid env: %v", env) - } - envVar := v1.EnvVar{Name: name, Value: value} - envs = append(envs, envVar) - } - return envs, nil -} diff --git a/pkg/generate/versioned/run_test.go b/pkg/generate/versioned/run_test.go deleted file mode 100644 index e69025cee..000000000 --- a/pkg/generate/versioned/run_test.go +++ /dev/null @@ -1,419 +0,0 @@ -/* -Copyright 2014 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 versioned - -import ( - "reflect" - "testing" - - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestGeneratePod(t *testing.T) { - tests := []struct { - name string - params map[string]interface{} - expected *v1.Pod - expectErr bool - }{ - { - name: "test1", - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "port": "", - }, - expected: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"run": "foo"}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Name: "foo", - Image: "someimage", - }, - }, - DNSPolicy: v1.DNSClusterFirst, - RestartPolicy: v1.RestartPolicyAlways, - }, - }, - }, - { - name: "test2", - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "env": []string{"a", "c"}, - }, - - expected: nil, - expectErr: true, - }, - { - name: "test3", - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "image-pull-policy": "Always", - "env": []string{"a=b", "c=d"}, - }, - expected: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"run": "foo"}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Name: "foo", - Image: "someimage", - ImagePullPolicy: v1.PullAlways, - Env: []v1.EnvVar{ - { - Name: "a", - Value: "b", - }, - { - Name: "c", - Value: "d", - }, - }, - }, - }, - DNSPolicy: v1.DNSClusterFirst, - RestartPolicy: v1.RestartPolicyAlways, - }, - }, - }, - { - name: "test4", - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "port": "80", - }, - expected: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"run": "foo"}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Name: "foo", - Image: "someimage", - Ports: []v1.ContainerPort{ - { - ContainerPort: 80, - }, - }, - }, - }, - DNSPolicy: v1.DNSClusterFirst, - RestartPolicy: v1.RestartPolicyAlways, - }, - }, - }, - { - name: "test5", - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "port": "80", - "hostport": "80", - }, - expected: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"run": "foo"}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Name: "foo", - Image: "someimage", - Ports: []v1.ContainerPort{ - { - ContainerPort: 80, - HostPort: 80, - }, - }, - }, - }, - DNSPolicy: v1.DNSClusterFirst, - RestartPolicy: v1.RestartPolicyAlways, - }, - }, - }, - { - name: "test6", - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "hostport": "80", - }, - expected: nil, - expectErr: true, - }, - { - name: "test7", - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "labels": "foo=bar,baz=blah", - }, - expected: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "bar", "baz": "blah"}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Name: "foo", - Image: "someimage", - }, - }, - DNSPolicy: v1.DNSClusterFirst, - RestartPolicy: v1.RestartPolicyAlways, - }, - }, - }, - { - name: "test8", - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "labels": "foo=bar,baz=blah", - "stdin": "true", - }, - expected: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "bar", "baz": "blah"}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Name: "foo", - Image: "someimage", - Stdin: true, - StdinOnce: true, - }, - }, - DNSPolicy: v1.DNSClusterFirst, - RestartPolicy: v1.RestartPolicyAlways, - }, - }, - }, - { - name: "test9", - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "labels": "foo=bar,baz=blah", - "stdin": "true", - "leave-stdin-open": "true", - }, - expected: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "bar", "baz": "blah"}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Name: "foo", - Image: "someimage", - Stdin: true, - StdinOnce: false, - }, - }, - DNSPolicy: v1.DNSClusterFirst, - RestartPolicy: v1.RestartPolicyAlways, - }, - }, - }, - { - name: "test10: privileged mode", - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "privileged": "true", - }, - expected: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"run": "foo"}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Name: "foo", - Image: "someimage", - SecurityContext: securityContextWithPrivilege(true), - }, - }, - DNSPolicy: v1.DNSClusterFirst, - RestartPolicy: v1.RestartPolicyAlways, - }, - }, - }, - { - name: "test11: check annotations", - params: map[string]interface{}{ - "name": "foo", - "image": "someimage", - "replicas": "1", - "labels": "foo=bar,baz=blah", - "annotations": []string{"foo=bar1", "baz=blah1"}, - }, - expected: &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Labels: map[string]string{"foo": "bar", "baz": "blah"}, - Annotations: map[string]string{"foo": "bar1", "baz": "blah1"}, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Name: "foo", - Image: "someimage", - }, - }, - DNSPolicy: v1.DNSClusterFirst, - RestartPolicy: v1.RestartPolicyAlways, - }, - }, - }, - } - generator := BasicPod{} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - obj, err := generator.Generate(tt.params) - if !tt.expectErr && err != nil { - t.Errorf("unexpected error: %v", err) - } - if tt.expectErr && err != nil { - return - } - if !reflect.DeepEqual(obj.(*v1.Pod), tt.expected) { - t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", tt.expected, obj.(*v1.Pod)) - } - }) - } -} - -func TestParseEnv(t *testing.T) { - tests := []struct { - name string - envArray []string - expected []v1.EnvVar - expectErr bool - test string - }{ - { - name: "test1", - envArray: []string{ - "THIS_ENV=isOK", - "this.dotted.env=isOKToo", - "HAS_COMMAS=foo,bar", - "HAS_EQUALS=jJnro54iUu75xNy==", - }, - expected: []v1.EnvVar{ - { - Name: "THIS_ENV", - Value: "isOK", - }, - { - Name: "this.dotted.env", - Value: "isOKToo", - }, - { - Name: "HAS_COMMAS", - Value: "foo,bar", - }, - { - Name: "HAS_EQUALS", - Value: "jJnro54iUu75xNy==", - }, - }, - expectErr: false, - test: "test case 1", - }, - { - name: "test2", - envArray: []string{ - "WITH_OUT_EQUALS", - }, - expected: []v1.EnvVar{}, - expectErr: true, - test: "test case 2", - }, - { - name: "test3", - envArray: []string{ - "WITH_OUT_VALUES=", - }, - expected: []v1.EnvVar{ - { - Name: "WITH_OUT_VALUES", - Value: "", - }, - }, - expectErr: false, - test: "test case 3", - }, - { - name: "test4", - envArray: []string{ - "=WITH_OUT_NAME", - }, - expected: []v1.EnvVar{}, - expectErr: true, - test: "test case 4", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - envs, err := parseEnvs(tt.envArray) - if !tt.expectErr && err != nil { - t.Errorf("unexpected error: %v (%s)", err, tt.test) - } - if tt.expectErr && err != nil { - return - } - if !reflect.DeepEqual(envs, tt.expected) { - t.Errorf("\nexpected:\n%#v\nsaw:\n%#v (%s)", tt.expected, envs, tt.test) - } - }) - } -} - -func securityContextWithPrivilege(privileged bool) *v1.SecurityContext { - return &v1.SecurityContext{ - Privileged: &privileged, - } -} diff --git a/pkg/generate/versioned/service.go b/pkg/generate/versioned/service.go deleted file mode 100644 index 68b6557a6..000000000 --- a/pkg/generate/versioned/service.go +++ /dev/null @@ -1,240 +0,0 @@ -/* -Copyright 2014 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 versioned - -import ( - "fmt" - "strconv" - "strings" - - "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/kubectl/pkg/generate" -) - -// The only difference between ServiceGeneratorV1 and V2 is that the service port is named "default" in V1, while it is left unnamed in V2. -type ServiceGeneratorV1 struct{} - -func (ServiceGeneratorV1) ParamNames() []generate.GeneratorParam { - return paramNames() -} - -func (ServiceGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) { - params["port-name"] = "default" - return generateService(params) -} - -type ServiceGeneratorV2 struct{} - -func (ServiceGeneratorV2) ParamNames() []generate.GeneratorParam { - return paramNames() -} - -func (ServiceGeneratorV2) Generate(params map[string]interface{}) (runtime.Object, error) { - return generateService(params) -} - -func paramNames() []generate.GeneratorParam { - return []generate.GeneratorParam{ - {Name: "default-name", Required: true}, - {Name: "name", Required: false}, - {Name: "selector", Required: true}, - // port will be used if a user specifies --port OR the exposed object - // has one port - {Name: "port", Required: false}, - // ports will be used iff a user doesn't specify --port AND the - // exposed object has multiple ports - {Name: "ports", Required: false}, - {Name: "labels", Required: false}, - {Name: "external-ip", Required: false}, - {Name: "load-balancer-ip", Required: false}, - {Name: "type", Required: false}, - {Name: "protocol", Required: false}, - // protocols will be used to keep port-protocol mapping derived from - // exposed object - {Name: "protocols", Required: false}, - {Name: "container-port", Required: false}, // alias of target-port - {Name: "target-port", Required: false}, - {Name: "port-name", Required: false}, - {Name: "session-affinity", Required: false}, - {Name: "cluster-ip", Required: false}, - } -} - -func generateService(genericParams map[string]interface{}) (runtime.Object, error) { - params := map[string]string{} - for key, value := range genericParams { - strVal, isString := value.(string) - if !isString { - return nil, fmt.Errorf("expected string, saw %v for '%s'", value, key) - } - params[key] = strVal - } - selectorString, found := params["selector"] - if !found || len(selectorString) == 0 { - return nil, fmt.Errorf("'selector' is a required parameter") - } - selector, err := generate.ParseLabels(selectorString) - if err != nil { - return nil, err - } - - labelsString, found := params["labels"] - var labels map[string]string - if found && len(labelsString) > 0 { - labels, err = generate.ParseLabels(labelsString) - if err != nil { - return nil, err - } - } - - name, found := params["name"] - if !found || len(name) == 0 { - name, found = params["default-name"] - if !found || len(name) == 0 { - return nil, fmt.Errorf("'name' is a required parameter") - } - } - - isHeadlessService := params["cluster-ip"] == "None" - - ports := []v1.ServicePort{} - servicePortName, found := params["port-name"] - if !found { - // Leave the port unnamed. - servicePortName = "" - } - - protocolsString, found := params["protocols"] - var portProtocolMap map[string]string - if found && len(protocolsString) > 0 { - portProtocolMap, err = generate.ParseProtocols(protocolsString) - if err != nil { - return nil, err - } - } - // ports takes precedence over port since it will be - // specified only when the user hasn't specified a port - // via --port and the exposed object has multiple ports. - var portString string - if portString, found = params["ports"]; !found { - portString, found = params["port"] - if !found && !isHeadlessService { - return nil, fmt.Errorf("'ports' or 'port' is a required parameter") - } - } - - if portString != "" { - portStringSlice := strings.Split(portString, ",") - for i, stillPortString := range portStringSlice { - port, err := strconv.Atoi(stillPortString) - if err != nil { - return nil, err - } - name := servicePortName - // If we are going to assign multiple ports to a service, we need to - // generate a different name for each one. - if len(portStringSlice) > 1 { - name = fmt.Sprintf("port-%d", i+1) - } - protocol := params["protocol"] - - switch { - case len(protocol) == 0 && len(portProtocolMap) == 0: - // Default to TCP, what the flag was doing previously. - protocol = "TCP" - case len(protocol) > 0 && len(portProtocolMap) > 0: - // User has specified the --protocol while exposing a multiprotocol resource - // We should stomp multiple protocols with the one specified ie. do nothing - case len(protocol) == 0 && len(portProtocolMap) > 0: - // no --protocol and we expose a multiprotocol resource - protocol = "TCP" // have the default so we can stay sane - if exposeProtocol, found := portProtocolMap[stillPortString]; found { - protocol = exposeProtocol - } - } - ports = append(ports, v1.ServicePort{ - Name: name, - Port: int32(port), - Protocol: v1.Protocol(protocol), - }) - } - } - - service := v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: labels, - }, - Spec: v1.ServiceSpec{ - Selector: selector, - Ports: ports, - }, - } - targetPortString := params["target-port"] - if len(targetPortString) == 0 { - targetPortString = params["container-port"] - } - if len(targetPortString) > 0 { - var targetPort intstr.IntOrString - if portNum, err := strconv.Atoi(targetPortString); err != nil { - targetPort = intstr.FromString(targetPortString) - } else { - targetPort = intstr.FromInt32(int32(portNum)) - } - // Use the same target-port for every port - for i := range service.Spec.Ports { - service.Spec.Ports[i].TargetPort = targetPort - } - } else { - // If --target-port or --container-port haven't been specified, this - // should be the same as Port - for i := range service.Spec.Ports { - port := service.Spec.Ports[i].Port - service.Spec.Ports[i].TargetPort = intstr.FromInt32(port) - } - } - if len(params["external-ip"]) > 0 { - service.Spec.ExternalIPs = []string{params["external-ip"]} - } - if len(params["type"]) != 0 { - service.Spec.Type = v1.ServiceType(params["type"]) - } - if service.Spec.Type == v1.ServiceTypeLoadBalancer { - service.Spec.LoadBalancerIP = params["load-balancer-ip"] - } - if len(params["session-affinity"]) != 0 { - switch v1.ServiceAffinity(params["session-affinity"]) { - case v1.ServiceAffinityNone: - service.Spec.SessionAffinity = v1.ServiceAffinityNone - case v1.ServiceAffinityClientIP: - service.Spec.SessionAffinity = v1.ServiceAffinityClientIP - default: - return nil, fmt.Errorf("unknown session affinity: %s", params["session-affinity"]) - } - } - if len(params["cluster-ip"]) != 0 { - if params["cluster-ip"] == "None" { - service.Spec.ClusterIP = v1.ClusterIPNone - } else { - service.Spec.ClusterIP = params["cluster-ip"] - } - } - return &service, nil -} diff --git a/pkg/generate/versioned/service_test.go b/pkg/generate/versioned/service_test.go deleted file mode 100644 index 30438480e..000000000 --- a/pkg/generate/versioned/service_test.go +++ /dev/null @@ -1,963 +0,0 @@ -/* -Copyright 2014 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 versioned - -import ( - "reflect" - "testing" - - "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/kubectl/pkg/generate" -) - -func TestGenerateService(t *testing.T) { - tests := []struct { - name string - generator generate.Generator - params map[string]interface{} - expected v1.Service - }{ - { - name: "test1", - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "TCP", - "container-port": "1234", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Port: 80, - Protocol: "TCP", - TargetPort: intstr.FromInt32(1234), - }, - }, - }, - }, - }, - { - name: "test2", - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "UDP", - "container-port": "foobar", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Port: 80, - Protocol: "UDP", - TargetPort: intstr.FromString("foobar"), - }, - }, - }, - }, - }, - { - name: "test3", - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "labels": "key1=value1,key2=value2", - "name": "test", - "port": "80", - "protocol": "TCP", - "container-port": "1234", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Labels: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Port: 80, - Protocol: "TCP", - TargetPort: intstr.FromInt32(1234), - }, - }, - }, - }, - }, - { - name: "test4", - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "UDP", - "container-port": "foobar", - "external-ip": "1.2.3.4", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Port: 80, - Protocol: "UDP", - TargetPort: intstr.FromString("foobar"), - }, - }, - ExternalIPs: []string{"1.2.3.4"}, - }, - }, - }, - { - name: "test5", - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "UDP", - "container-port": "foobar", - "external-ip": "1.2.3.4", - "type": "LoadBalancer", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Port: 80, - Protocol: "UDP", - TargetPort: intstr.FromString("foobar"), - }, - }, - Type: v1.ServiceTypeLoadBalancer, - ExternalIPs: []string{"1.2.3.4"}, - }, - }, - }, - { - name: "test6", - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "UDP", - "container-port": "foobar", - "type": string(v1.ServiceTypeNodePort), - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Port: 80, - Protocol: "UDP", - TargetPort: intstr.FromString("foobar"), - }, - }, - Type: v1.ServiceTypeNodePort, - }, - }, - }, - { - name: "test7", - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "UDP", - "container-port": "foobar", - "create-external-load-balancer": "true", // ignored when type is present - "type": string(v1.ServiceTypeNodePort), - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Port: 80, - Protocol: "UDP", - TargetPort: intstr.FromString("foobar"), - }, - }, - Type: v1.ServiceTypeNodePort, - }, - }, - }, - { - name: "test8", - generator: ServiceGeneratorV1{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "TCP", - "container-port": "1234", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Name: "default", - Port: 80, - Protocol: "TCP", - TargetPort: intstr.FromInt32(1234), - }, - }, - }, - }, - }, - { - name: "test9", - generator: ServiceGeneratorV1{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "TCP", - "container-port": "1234", - "session-affinity": "ClientIP", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Name: "default", - Port: 80, - Protocol: "TCP", - TargetPort: intstr.FromInt32(1234), - }, - }, - SessionAffinity: v1.ServiceAffinityClientIP, - }, - }, - }, - { - name: "test10", - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "TCP", - "container-port": "1234", - "cluster-ip": "10.10.10.10", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Port: 80, - Protocol: "TCP", - TargetPort: intstr.FromInt32(1234), - }, - }, - ClusterIP: "10.10.10.10", - }, - }, - }, - { - name: "test11", - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "TCP", - "container-port": "1234", - "cluster-ip": "None", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Port: 80, - Protocol: "TCP", - TargetPort: intstr.FromInt32(1234), - }, - }, - ClusterIP: v1.ClusterIPNone, - }, - }, - }, - { - name: "test12", - generator: ServiceGeneratorV1{}, - params: map[string]interface{}{ - "selector": "foo=bar", - "name": "test", - "ports": "80,443", - "protocol": "TCP", - "container-port": "foobar", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - Ports: []v1.ServicePort{ - { - Name: "port-1", - Port: 80, - Protocol: v1.ProtocolTCP, - TargetPort: intstr.FromString("foobar"), - }, - { - Name: "port-2", - Port: 443, - Protocol: v1.ProtocolTCP, - TargetPort: intstr.FromString("foobar"), - }, - }, - }, - }, - }, - { - name: "test13", - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar", - "name": "test", - "ports": "80,443", - "protocol": "UDP", - "target-port": "1234", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - Ports: []v1.ServicePort{ - { - Name: "port-1", - Port: 80, - Protocol: v1.ProtocolUDP, - TargetPort: intstr.FromInt32(1234), - }, - { - Name: "port-2", - Port: 443, - Protocol: v1.ProtocolUDP, - TargetPort: intstr.FromInt32(1234), - }, - }, - }, - }, - }, - { - name: "test14", - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar", - "name": "test", - "ports": "80,443", - "protocol": "TCP", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - Ports: []v1.ServicePort{ - { - Name: "port-1", - Port: 80, - Protocol: v1.ProtocolTCP, - TargetPort: intstr.FromInt32(80), - }, - { - Name: "port-2", - Port: 443, - Protocol: v1.ProtocolTCP, - TargetPort: intstr.FromInt32(443), - }, - }, - }, - }, - }, - { - name: "test15", - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar", - "name": "test", - "ports": "80,8080", - "protocols": "8080/UDP", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - Ports: []v1.ServicePort{ - { - Name: "port-1", - Port: 80, - Protocol: v1.ProtocolTCP, - TargetPort: intstr.FromInt32(80), - }, - { - Name: "port-2", - Port: 8080, - Protocol: v1.ProtocolUDP, - TargetPort: intstr.FromInt32(8080), - }, - }, - }, - }, - }, - { - name: "test16", - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar", - "name": "test", - "ports": "80,8080,8081", - "protocols": "8080/UDP,8081/TCP", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - Ports: []v1.ServicePort{ - { - Name: "port-1", - Port: 80, - Protocol: v1.ProtocolTCP, - TargetPort: intstr.FromInt32(80), - }, - { - Name: "port-2", - Port: 8080, - Protocol: v1.ProtocolUDP, - TargetPort: intstr.FromInt32(8080), - }, - { - Name: "port-3", - Port: 8081, - Protocol: v1.ProtocolTCP, - TargetPort: intstr.FromInt32(8081), - }, - }, - }, - }, - }, - { - name: "test17", - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "protocol": "TCP", - "container-port": "1234", - "cluster-ip": "None", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{}, - ClusterIP: v1.ClusterIPNone, - }, - }, - }, - { - name: "test18", - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar", - "name": "test", - "cluster-ip": "None", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - Ports: []v1.ServicePort{}, - ClusterIP: v1.ClusterIPNone, - }, - }, - }, - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "SCTP", - "container-port": "1234", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Port: 80, - Protocol: "SCTP", - TargetPort: intstr.FromInt32(1234), - }, - }, - }, - }, - }, - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "labels": "key1=value1,key2=value2", - "name": "test", - "port": "80", - "protocol": "SCTP", - "container-port": "1234", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Labels: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Port: 80, - Protocol: "SCTP", - TargetPort: intstr.FromInt32(1234), - }, - }, - }, - }, - }, - { - generator: ServiceGeneratorV1{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "SCTP", - "container-port": "1234", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Name: "default", - Port: 80, - Protocol: "SCTP", - TargetPort: intstr.FromInt32(1234), - }, - }, - }, - }, - }, - { - generator: ServiceGeneratorV1{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "SCTP", - "container-port": "1234", - "session-affinity": "ClientIP", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Name: "default", - Port: 80, - Protocol: "SCTP", - TargetPort: intstr.FromInt32(1234), - }, - }, - SessionAffinity: v1.ServiceAffinityClientIP, - }, - }, - }, - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "SCTP", - "container-port": "1234", - "cluster-ip": "10.10.10.10", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Port: 80, - Protocol: "SCTP", - TargetPort: intstr.FromInt32(1234), - }, - }, - ClusterIP: "10.10.10.10", - }, - }, - }, - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "port": "80", - "protocol": "SCTP", - "container-port": "1234", - "cluster-ip": "None", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{ - { - Port: 80, - Protocol: "SCTP", - TargetPort: intstr.FromInt32(1234), - }, - }, - ClusterIP: v1.ClusterIPNone, - }, - }, - }, - { - generator: ServiceGeneratorV1{}, - params: map[string]interface{}{ - "selector": "foo=bar", - "name": "test", - "ports": "80,443", - "protocol": "SCTP", - "container-port": "foobar", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - Ports: []v1.ServicePort{ - { - Name: "port-1", - Port: 80, - Protocol: v1.ProtocolSCTP, - TargetPort: intstr.FromString("foobar"), - }, - { - Name: "port-2", - Port: 443, - Protocol: v1.ProtocolSCTP, - TargetPort: intstr.FromString("foobar"), - }, - }, - }, - }, - }, - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar", - "name": "test", - "ports": "80,443", - "protocol": "SCTP", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - Ports: []v1.ServicePort{ - { - Name: "port-1", - Port: 80, - Protocol: v1.ProtocolSCTP, - TargetPort: intstr.FromInt32(80), - }, - { - Name: "port-2", - Port: 443, - Protocol: v1.ProtocolSCTP, - TargetPort: intstr.FromInt32(443), - }, - }, - }, - }, - }, - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar", - "name": "test", - "ports": "80,8080", - "protocols": "8080/SCTP", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - Ports: []v1.ServicePort{ - { - Name: "port-1", - Port: 80, - Protocol: v1.ProtocolTCP, - TargetPort: intstr.FromInt32(80), - }, - { - Name: "port-2", - Port: 8080, - Protocol: v1.ProtocolSCTP, - TargetPort: intstr.FromInt32(8080), - }, - }, - }, - }, - }, - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar", - "name": "test", - "ports": "80,8080,8081,8082", - "protocols": "8080/UDP,8081/TCP,8082/SCTP", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - }, - Ports: []v1.ServicePort{ - { - Name: "port-1", - Port: 80, - Protocol: v1.ProtocolTCP, - TargetPort: intstr.FromInt32(80), - }, - { - Name: "port-2", - Port: 8080, - Protocol: v1.ProtocolUDP, - TargetPort: intstr.FromInt32(8080), - }, - { - Name: "port-3", - Port: 8081, - Protocol: v1.ProtocolTCP, - TargetPort: intstr.FromInt32(8081), - }, - { - Name: "port-4", - Port: 8082, - Protocol: v1.ProtocolSCTP, - TargetPort: intstr.FromInt32(8082), - }, - }, - }, - }, - }, - { - generator: ServiceGeneratorV2{}, - params: map[string]interface{}{ - "selector": "foo=bar,baz=blah", - "name": "test", - "protocol": "SCTP", - "container-port": "1234", - "cluster-ip": "None", - }, - expected: v1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - }, - Spec: v1.ServiceSpec{ - Selector: map[string]string{ - "foo": "bar", - "baz": "blah", - }, - Ports: []v1.ServicePort{}, - ClusterIP: v1.ClusterIPNone, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - obj, err := tt.generator.Generate(tt.params) - if !reflect.DeepEqual(obj, &tt.expected) { - t.Errorf("expected:\n%#v\ngot\n%#v\n", &tt.expected, obj) - } - if err != nil { - t.Errorf("unexpected error: %v", err) - } - }) - } -}