mirror of
https://github.com/kubernetes/kubectl.git
synced 2026-05-28 04:35:50 -04:00
Drop generators entirely from kubectl
Signed-off-by: Maciej Szulik <soltysh@gmail.com> Kubernetes-commit: 2e7089b46420f6d98cc67086ba6b0c1f364798d1
This commit is contained in:
parent
a174022b42
commit
3fe5e3bbf1
10 changed files with 35 additions and 2716 deletions
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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 <resource>=<value>", resourceStatement)
|
||||
}
|
||||
resourceName := v1.ResourceName(parts[0])
|
||||
resourceQuantity, err := apiresource.ParseQuantity(parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result[resourceName] = resourceQuantity
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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 <resourceName1>=<value1>,<resourceName1>=<value2>
|
||||
// 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 <resource>=<value>", 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
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue