mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 18:53:35 +00:00
Fresh dep ensure
This commit is contained in:
111
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/BUILD
generated
vendored
Normal file
111
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/BUILD
generated
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"autoscale.go",
|
||||
"clusterrolebinding.go",
|
||||
"configmap.go",
|
||||
"deployment.go",
|
||||
"env_file.go",
|
||||
"generator.go",
|
||||
"namespace.go",
|
||||
"pdb.go",
|
||||
"priorityclass.go",
|
||||
"quota.go",
|
||||
"rolebinding.go",
|
||||
"run.go",
|
||||
"secret.go",
|
||||
"secret_for_docker_registry.go",
|
||||
"secret_for_tls.go",
|
||||
"service.go",
|
||||
"service_basic.go",
|
||||
"serviceaccount.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/generate/versioned",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/kubectl/generate:go_default_library",
|
||||
"//pkg/kubectl/util:go_default_library",
|
||||
"//pkg/kubectl/util/hash:go_default_library",
|
||||
"//staging/src/k8s.io/api/apps/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/apps/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/autoscaling/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/batch/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/batch/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/batch/v2alpha1:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/policy/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/rbac/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/scheduling/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/discovery:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"autoscale_test.go",
|
||||
"clusterrolebinding_test.go",
|
||||
"configmap_test.go",
|
||||
"deployment_test.go",
|
||||
"env_file_test.go",
|
||||
"namespace_test.go",
|
||||
"pdb_test.go",
|
||||
"priorityclass_test.go",
|
||||
"quota_test.go",
|
||||
"rolebinding_test.go",
|
||||
"run_test.go",
|
||||
"secret_for_docker_registry_test.go",
|
||||
"secret_for_tls_test.go",
|
||||
"secret_test.go",
|
||||
"service_basic_test.go",
|
||||
"service_test.go",
|
||||
"serviceaccount_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/kubectl/generate:go_default_library",
|
||||
"//staging/src/k8s.io/api/apps/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/apps/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/autoscaling/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/batch/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/batch/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/batch/v2alpha1:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/policy/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/rbac/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/api/scheduling/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/util/testing:go_default_library",
|
||||
"//vendor/k8s.io/utils/pointer:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
86
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/autoscale.go
generated
vendored
Normal file
86
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/autoscale.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
Copyright 2015 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"
|
||||
|
||||
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubectl/generate"
|
||||
)
|
||||
|
||||
// HorizontalPodAutoscalerGeneratorV1 supports stable generation of a horizontal pod autoscaler.
|
||||
type HorizontalPodAutoscalerGeneratorV1 struct {
|
||||
Name string
|
||||
ScaleRefKind string
|
||||
ScaleRefName string
|
||||
ScaleRefAPIVersion string
|
||||
MinReplicas int32
|
||||
MaxReplicas int32
|
||||
CPUPercent int32
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction.
|
||||
var _ generate.StructuredGenerator = &HorizontalPodAutoscalerGeneratorV1{}
|
||||
|
||||
// StructuredGenerate outputs a horizontal pod autoscaler object using the configured fields.
|
||||
func (s *HorizontalPodAutoscalerGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scaler := autoscalingv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: s.Name,
|
||||
},
|
||||
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
|
||||
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
|
||||
Kind: s.ScaleRefKind,
|
||||
Name: s.ScaleRefName,
|
||||
APIVersion: s.ScaleRefAPIVersion,
|
||||
},
|
||||
MaxReplicas: s.MaxReplicas,
|
||||
},
|
||||
}
|
||||
|
||||
if s.MinReplicas > 0 {
|
||||
v := int32(s.MinReplicas)
|
||||
scaler.Spec.MinReplicas = &v
|
||||
}
|
||||
if s.CPUPercent >= 0 {
|
||||
c := int32(s.CPUPercent)
|
||||
scaler.Spec.TargetCPUUtilizationPercentage = &c
|
||||
}
|
||||
|
||||
return &scaler, nil
|
||||
}
|
||||
|
||||
// validate check if the caller has set the right fields.
|
||||
func (s HorizontalPodAutoscalerGeneratorV1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if s.MaxReplicas < 1 {
|
||||
return fmt.Errorf("'max' is a required parameter and must be at least 1")
|
||||
}
|
||||
if s.MinReplicas > s.MaxReplicas {
|
||||
return fmt.Errorf("'max' must be greater than or equal to 'min'")
|
||||
}
|
||||
return nil
|
||||
}
|
130
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/autoscale_test.go
generated
vendored
Normal file
130
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/autoscale_test.go
generated
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
func TestHPAGenerate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
HPAName string
|
||||
scaleRefKind string
|
||||
scaleRefName string
|
||||
scaleRefAPIVersion string
|
||||
minReplicas int32
|
||||
maxReplicas int32
|
||||
CPUPercent int32
|
||||
expected *autoscalingv1.HorizontalPodAutoscaler
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid case",
|
||||
HPAName: "foo",
|
||||
minReplicas: 1,
|
||||
maxReplicas: 10,
|
||||
CPUPercent: 80,
|
||||
scaleRefKind: "kind",
|
||||
scaleRefName: "name",
|
||||
scaleRefAPIVersion: "apiVersion",
|
||||
expected: &autoscalingv1.HorizontalPodAutoscaler{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
|
||||
TargetCPUUtilizationPercentage: utilpointer.Int32Ptr(80),
|
||||
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
|
||||
Kind: "kind",
|
||||
Name: "name",
|
||||
APIVersion: "apiVersion",
|
||||
},
|
||||
MaxReplicas: int32(10),
|
||||
MinReplicas: utilpointer.Int32Ptr(1),
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "'name' is a required parameter",
|
||||
scaleRefKind: "kind",
|
||||
scaleRefName: "name",
|
||||
scaleRefAPIVersion: "apiVersion",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "'max' is a required parameter",
|
||||
HPAName: "foo",
|
||||
scaleRefKind: "kind",
|
||||
scaleRefName: "name",
|
||||
scaleRefAPIVersion: "apiVersion",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "'max' must be greater than or equal to 'min'",
|
||||
HPAName: "foo",
|
||||
minReplicas: 10,
|
||||
maxReplicas: 1,
|
||||
scaleRefKind: "kind",
|
||||
scaleRefName: "name",
|
||||
scaleRefAPIVersion: "apiVersion",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "'max' must be at least 1",
|
||||
HPAName: "foo",
|
||||
minReplicas: 1,
|
||||
maxReplicas: -10,
|
||||
scaleRefKind: "kind",
|
||||
scaleRefName: "name",
|
||||
scaleRefAPIVersion: "apiVersion",
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
generator := HorizontalPodAutoscalerGeneratorV1{
|
||||
Name: tt.HPAName,
|
||||
ScaleRefKind: tt.scaleRefKind,
|
||||
ScaleRefName: tt.scaleRefName,
|
||||
ScaleRefAPIVersion: tt.scaleRefAPIVersion,
|
||||
MinReplicas: tt.minReplicas,
|
||||
MaxReplicas: tt.maxReplicas,
|
||||
CPUPercent: tt.CPUPercent,
|
||||
}
|
||||
obj, err := generator.StructuredGenerate()
|
||||
if tt.expectErr && err != nil {
|
||||
return
|
||||
}
|
||||
if !tt.expectErr && err != nil {
|
||||
t.Errorf("[%s] unexpected error: %v", tt.name, err)
|
||||
}
|
||||
if tt.expectErr && err == nil {
|
||||
t.Errorf("[%s] expect error, got nil", tt.name)
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*autoscalingv1.HorizontalPodAutoscaler), tt.expected) {
|
||||
t.Errorf("[%s] want:\n%#v\ngot:\n%#v", tt.name, tt.expected, obj.(*autoscalingv1.HorizontalPodAutoscaler))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
159
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/clusterrolebinding.go
generated
vendored
Normal file
159
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/clusterrolebinding.go
generated
vendored
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"strings"
|
||||
|
||||
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/kubectl/generate"
|
||||
)
|
||||
|
||||
// ClusterRoleBindingGeneratorV1 supports stable generation of a clusterRoleBinding.
|
||||
type ClusterRoleBindingGeneratorV1 struct {
|
||||
// Name of clusterRoleBinding (required)
|
||||
Name string
|
||||
// ClusterRole for the clusterRoleBinding (required)
|
||||
ClusterRole string
|
||||
// Users to derive the clusterRoleBinding from (optional)
|
||||
Users []string
|
||||
// Groups to derive the clusterRoleBinding from (optional)
|
||||
Groups []string
|
||||
// ServiceAccounts to derive the clusterRoleBinding from in namespace:name format(optional)
|
||||
ServiceAccounts []string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter injection.
|
||||
var _ generate.Generator = &ClusterRoleBindingGeneratorV1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction.
|
||||
var _ generate.StructuredGenerator = &ClusterRoleBindingGeneratorV1{}
|
||||
|
||||
// Generate returns a clusterRoleBinding using the specified parameters.
|
||||
func (s ClusterRoleBindingGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := generate.ValidateParams(s.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &ClusterRoleBindingGeneratorV1{}
|
||||
userStrings, found := genericParams["user"]
|
||||
if found {
|
||||
fromFileArray, isArray := userStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", userStrings)
|
||||
}
|
||||
delegate.Users = fromFileArray
|
||||
delete(genericParams, "user")
|
||||
}
|
||||
groupStrings, found := genericParams["group"]
|
||||
if found {
|
||||
fromLiteralArray, isArray := groupStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", groupStrings)
|
||||
}
|
||||
delegate.Groups = fromLiteralArray
|
||||
delete(genericParams, "group")
|
||||
}
|
||||
saStrings, found := genericParams["serviceaccount"]
|
||||
if found {
|
||||
fromLiteralArray, isArray := saStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", saStrings)
|
||||
}
|
||||
delegate.ServiceAccounts = fromLiteralArray
|
||||
delete(genericParams, "serviceaccount")
|
||||
}
|
||||
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
|
||||
}
|
||||
delegate.Name = params["name"]
|
||||
delegate.ClusterRole = params["clusterrole"]
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern.
|
||||
func (s ClusterRoleBindingGeneratorV1) ParamNames() []generate.GeneratorParam {
|
||||
return []generate.GeneratorParam{
|
||||
{Name: "name", Required: true},
|
||||
{Name: "clusterrole", Required: false},
|
||||
{Name: "user", Required: false},
|
||||
{Name: "group", Required: false},
|
||||
{Name: "serviceaccount", Required: false},
|
||||
}
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a clusterRoleBinding object using the configured fields.
|
||||
func (s ClusterRoleBindingGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clusterRoleBinding := &rbacv1beta1.ClusterRoleBinding{}
|
||||
clusterRoleBinding.Name = s.Name
|
||||
clusterRoleBinding.RoleRef = rbacv1beta1.RoleRef{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: "ClusterRole",
|
||||
Name: s.ClusterRole,
|
||||
}
|
||||
for _, user := range sets.NewString(s.Users...).List() {
|
||||
clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbacv1beta1.Subject{
|
||||
Kind: rbacv1beta1.UserKind,
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Name: user,
|
||||
})
|
||||
}
|
||||
for _, group := range sets.NewString(s.Groups...).List() {
|
||||
clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbacv1beta1.Subject{
|
||||
Kind: rbacv1beta1.GroupKind,
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Name: group,
|
||||
})
|
||||
}
|
||||
for _, sa := range sets.NewString(s.ServiceAccounts...).List() {
|
||||
tokens := strings.Split(sa, ":")
|
||||
if len(tokens) != 2 || tokens[0] == "" || tokens[1] == "" {
|
||||
return nil, fmt.Errorf("serviceaccount must be <namespace>:<name>")
|
||||
}
|
||||
clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, rbacv1beta1.Subject{
|
||||
Kind: rbacv1beta1.ServiceAccountKind,
|
||||
APIGroup: "",
|
||||
Namespace: tokens[0],
|
||||
Name: tokens[1],
|
||||
})
|
||||
}
|
||||
|
||||
return clusterRoleBinding, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation.
|
||||
func (s ClusterRoleBindingGeneratorV1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if len(s.ClusterRole) == 0 {
|
||||
return fmt.Errorf("clusterrole must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
231
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/clusterrolebinding_test.go
generated
vendored
Normal file
231
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/clusterrolebinding_test.go
generated
vendored
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestClusterRoleBindingGenerate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params map[string]interface{}
|
||||
expected *rbacv1beta1.ClusterRoleBinding
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid case 1",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"clusterrole": "admin",
|
||||
"user": []string{"user"},
|
||||
"group": []string{"group"},
|
||||
"serviceaccount": []string{"ns1:name1"},
|
||||
},
|
||||
expected: &rbacv1beta1.ClusterRoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
RoleRef: rbacv1beta1.RoleRef{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: "ClusterRole",
|
||||
Name: "admin",
|
||||
},
|
||||
Subjects: []rbacv1beta1.Subject{
|
||||
{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: rbacv1beta1.UserKind,
|
||||
Name: "user",
|
||||
},
|
||||
{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: rbacv1beta1.GroupKind,
|
||||
Name: "group",
|
||||
},
|
||||
{
|
||||
Kind: rbacv1beta1.ServiceAccountKind,
|
||||
APIGroup: "",
|
||||
Namespace: "ns1",
|
||||
Name: "name1",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid case 2",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"clusterrole": "admin",
|
||||
"user": []string{"user1", "user2"},
|
||||
"group": []string{"group1", "group2"},
|
||||
"serviceaccount": []string{"ns1:name1", "ns2:name2"},
|
||||
},
|
||||
expected: &rbacv1beta1.ClusterRoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
RoleRef: rbacv1beta1.RoleRef{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: "ClusterRole",
|
||||
Name: "admin",
|
||||
},
|
||||
Subjects: []rbacv1beta1.Subject{
|
||||
{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: rbacv1beta1.UserKind,
|
||||
Name: "user1",
|
||||
},
|
||||
{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: rbacv1beta1.UserKind,
|
||||
Name: "user2",
|
||||
},
|
||||
{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: rbacv1beta1.GroupKind,
|
||||
Name: "group1",
|
||||
},
|
||||
{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: rbacv1beta1.GroupKind,
|
||||
Name: "group2",
|
||||
},
|
||||
{
|
||||
Kind: rbacv1beta1.ServiceAccountKind,
|
||||
APIGroup: "",
|
||||
Namespace: "ns1",
|
||||
Name: "name1",
|
||||
},
|
||||
{
|
||||
Kind: rbacv1beta1.ServiceAccountKind,
|
||||
APIGroup: "",
|
||||
Namespace: "ns2",
|
||||
Name: "name2",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid case 3",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"clusterrole": "admin",
|
||||
},
|
||||
expected: &rbacv1beta1.ClusterRoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
RoleRef: rbacv1beta1.RoleRef{
|
||||
APIGroup: rbacv1beta1.GroupName,
|
||||
Kind: "ClusterRole",
|
||||
Name: "admin",
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid serviceaccount, expected format: <namespace:name>",
|
||||
params: map[string]interface{}{
|
||||
"name": "role",
|
||||
"clusterrole": "admin",
|
||||
"user": []string{"user"},
|
||||
"group": []string{"group"},
|
||||
"serviceaccount": []string{"ns1-name1"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "name must be specified",
|
||||
params: map[string]interface{}{
|
||||
"name": "",
|
||||
"clusterrole": "admin",
|
||||
"user": []string{"user"},
|
||||
"group": []string{"group"},
|
||||
"serviceaccount": []string{"ns1:name1"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "clusterrole must be specified",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"clusterrole": "",
|
||||
"user": []string{"user"},
|
||||
"group": []string{"group"},
|
||||
"serviceaccount": []string{"ns1:name1"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "expected user []string",
|
||||
params: map[string]interface{}{
|
||||
"name": "role",
|
||||
"clusterrole": "admin",
|
||||
"user": "user",
|
||||
"group": []string{"group"},
|
||||
"serviceaccount": []string{"ns1:name1"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "expected group []string",
|
||||
params: map[string]interface{}{
|
||||
"name": "role",
|
||||
"clusterrole": "admin",
|
||||
"user": []string{"user"},
|
||||
"group": "group",
|
||||
"serviceaccount": []string{"ns1:name1"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "expected serviceaccount []string",
|
||||
params: map[string]interface{}{
|
||||
"name": "role",
|
||||
"clusterrole": "admin",
|
||||
"user": []string{"user"},
|
||||
"group": []string{"group"},
|
||||
"serviceaccount": "ns1",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
generator := ClusterRoleBindingGeneratorV1{}
|
||||
for i, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
obj, err := generator.Generate(tt.params)
|
||||
if !tt.expectErr && err != nil {
|
||||
t.Errorf("[%d] unexpected error: %v", i, err)
|
||||
}
|
||||
if tt.expectErr && err != nil {
|
||||
return
|
||||
}
|
||||
if tt.expectErr && err == nil {
|
||||
t.Errorf("[%s] expect error, got nil", tt.name)
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*rbacv1beta1.ClusterRoleBinding), tt.expected) {
|
||||
t.Errorf("\n[%s] want:\n%#v\ngot:\n%#v", tt.name, tt.expected, obj.(*rbacv1beta1.ClusterRoleBinding))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
298
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/configmap.go
generated
vendored
Normal file
298
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/configmap.go
generated
vendored
Normal file
@ -0,0 +1,298 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/kubernetes/pkg/kubectl/generate"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/hash"
|
||||
)
|
||||
|
||||
// ConfigMapGeneratorV1 supports stable generation of a configMap.
|
||||
type ConfigMapGeneratorV1 struct {
|
||||
// Name of configMap (required)
|
||||
Name string
|
||||
// Type of configMap (optional)
|
||||
Type string
|
||||
// FileSources to derive the configMap from (optional)
|
||||
FileSources []string
|
||||
// LiteralSources to derive the configMap from (optional)
|
||||
LiteralSources []string
|
||||
// EnvFileSource to derive the configMap from (optional)
|
||||
EnvFileSource string
|
||||
// AppendHash; if true, derive a hash from the ConfigMap and append it to the name
|
||||
AppendHash bool
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter injection.
|
||||
var _ generate.Generator = &ConfigMapGeneratorV1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction.
|
||||
var _ generate.StructuredGenerator = &ConfigMapGeneratorV1{}
|
||||
|
||||
// Generate returns a configMap using the specified parameters.
|
||||
func (s ConfigMapGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := generate.ValidateParams(s.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &ConfigMapGeneratorV1{}
|
||||
fromFileStrings, found := genericParams["from-file"]
|
||||
if found {
|
||||
fromFileArray, isArray := fromFileStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", fromFileStrings)
|
||||
}
|
||||
delegate.FileSources = fromFileArray
|
||||
delete(genericParams, "from-file")
|
||||
}
|
||||
fromLiteralStrings, found := genericParams["from-literal"]
|
||||
if found {
|
||||
fromLiteralArray, isArray := fromLiteralStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", fromLiteralStrings)
|
||||
}
|
||||
delegate.LiteralSources = fromLiteralArray
|
||||
delete(genericParams, "from-literal")
|
||||
}
|
||||
fromEnvFileString, found := genericParams["from-env-file"]
|
||||
if found {
|
||||
fromEnvFile, isString := fromEnvFileString.(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, found :%v", fromEnvFileString)
|
||||
}
|
||||
delegate.EnvFileSource = fromEnvFile
|
||||
delete(genericParams, "from-env-file")
|
||||
}
|
||||
hashParam, found := genericParams["append-hash"]
|
||||
if found {
|
||||
hashBool, isBool := hashParam.(bool)
|
||||
if !isBool {
|
||||
return nil, fmt.Errorf("expected bool, found :%v", hashParam)
|
||||
}
|
||||
delegate.AppendHash = hashBool
|
||||
delete(genericParams, "append-hash")
|
||||
}
|
||||
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
|
||||
}
|
||||
delegate.Name = params["name"]
|
||||
delegate.Type = params["type"]
|
||||
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern.
|
||||
func (s ConfigMapGeneratorV1) ParamNames() []generate.GeneratorParam {
|
||||
return []generate.GeneratorParam{
|
||||
{Name: "name", Required: true},
|
||||
{Name: "type", Required: false},
|
||||
{Name: "from-file", Required: false},
|
||||
{Name: "from-literal", Required: false},
|
||||
{Name: "from-env-file", Required: false},
|
||||
{Name: "force", Required: false},
|
||||
{Name: "hash", Required: false},
|
||||
}
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a configMap object using the configured fields.
|
||||
func (s ConfigMapGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configMap := &v1.ConfigMap{}
|
||||
configMap.Name = s.Name
|
||||
configMap.Data = map[string]string{}
|
||||
configMap.BinaryData = map[string][]byte{}
|
||||
if len(s.FileSources) > 0 {
|
||||
if err := handleConfigMapFromFileSources(configMap, s.FileSources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(s.LiteralSources) > 0 {
|
||||
if err := handleConfigMapFromLiteralSources(configMap, s.LiteralSources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(s.EnvFileSource) > 0 {
|
||||
if err := handleConfigMapFromEnvFileSource(configMap, s.EnvFileSource); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if s.AppendHash {
|
||||
h, err := hash.ConfigMapHash(configMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configMap.Name = fmt.Sprintf("%s-%s", configMap.Name, h)
|
||||
}
|
||||
return configMap, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation.
|
||||
func (s ConfigMapGeneratorV1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if len(s.EnvFileSource) > 0 && (len(s.FileSources) > 0 || len(s.LiteralSources) > 0) {
|
||||
return fmt.Errorf("from-env-file cannot be combined with from-file or from-literal")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleConfigMapFromLiteralSources adds the specified literal source
|
||||
// information into the provided configMap.
|
||||
func handleConfigMapFromLiteralSources(configMap *v1.ConfigMap, literalSources []string) error {
|
||||
for _, literalSource := range literalSources {
|
||||
keyName, value, err := util.ParseLiteralSource(literalSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = addKeyFromLiteralToConfigMap(configMap, keyName, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleConfigMapFromFileSources adds the specified file source information
|
||||
// into the provided configMap
|
||||
func handleConfigMapFromFileSources(configMap *v1.ConfigMap, fileSources []string) error {
|
||||
for _, fileSource := range fileSources {
|
||||
keyName, filePath, err := util.ParseFileSource(fileSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *os.PathError:
|
||||
return fmt.Errorf("error reading %s: %v", filePath, err.Err)
|
||||
default:
|
||||
return fmt.Errorf("error reading %s: %v", filePath, err)
|
||||
}
|
||||
}
|
||||
if info.IsDir() {
|
||||
if strings.Contains(fileSource, "=") {
|
||||
return fmt.Errorf("cannot give a key name for a directory path.")
|
||||
}
|
||||
fileList, err := ioutil.ReadDir(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing files in %s: %v", filePath, err)
|
||||
}
|
||||
for _, item := range fileList {
|
||||
itemPath := path.Join(filePath, item.Name())
|
||||
if item.Mode().IsRegular() {
|
||||
keyName = item.Name()
|
||||
err = addKeyFromFileToConfigMap(configMap, keyName, itemPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := addKeyFromFileToConfigMap(configMap, keyName, filePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleConfigMapFromEnvFileSource adds the specified env file source information
|
||||
// into the provided configMap
|
||||
func handleConfigMapFromEnvFileSource(configMap *v1.ConfigMap, envFileSource string) error {
|
||||
info, err := os.Stat(envFileSource)
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *os.PathError:
|
||||
return fmt.Errorf("error reading %s: %v", envFileSource, err.Err)
|
||||
default:
|
||||
return fmt.Errorf("error reading %s: %v", envFileSource, err)
|
||||
}
|
||||
}
|
||||
if info.IsDir() {
|
||||
return fmt.Errorf("env config file cannot be a directory")
|
||||
}
|
||||
|
||||
return addFromEnvFile(envFileSource, func(key, value string) error {
|
||||
return addKeyFromLiteralToConfigMap(configMap, key, value)
|
||||
})
|
||||
}
|
||||
|
||||
// addKeyFromFileToConfigMap adds a key with the given name to a ConfigMap, populating
|
||||
// the value with the content of the given file path, or returns an error.
|
||||
func addKeyFromFileToConfigMap(configMap *v1.ConfigMap, keyName, filePath string) error {
|
||||
data, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if utf8.Valid(data) {
|
||||
return addKeyFromLiteralToConfigMap(configMap, keyName, string(data))
|
||||
}
|
||||
|
||||
err = validateNewConfigMap(configMap, keyName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configMap.BinaryData[keyName] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// addKeyFromLiteralToConfigMap adds the given key and data to the given config map,
|
||||
// returning an error if the key is not valid or if the key already exists.
|
||||
func addKeyFromLiteralToConfigMap(configMap *v1.ConfigMap, keyName, data string) error {
|
||||
err := validateNewConfigMap(configMap, keyName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configMap.Data[keyName] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateNewConfigMap(configMap *v1.ConfigMap, keyName string) error {
|
||||
// Note, the rules for ConfigMap keys are the exact same as the ones for SecretKeys.
|
||||
if errs := validation.IsConfigMapKey(keyName); len(errs) != 0 {
|
||||
return fmt.Errorf("%q is not a valid key name for a ConfigMap: %s", keyName, strings.Join(errs, ";"))
|
||||
}
|
||||
|
||||
if _, exists := configMap.Data[keyName]; exists {
|
||||
return fmt.Errorf("cannot add key %s, another key by that name already exists in data: %v", keyName, configMap.Data)
|
||||
}
|
||||
if _, exists := configMap.BinaryData[keyName]; exists {
|
||||
return fmt.Errorf("cannot add key %s, another key by that name already exists in binaryData: %v", keyName, configMap.BinaryData)
|
||||
}
|
||||
return nil
|
||||
}
|
416
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/configmap_test.go
generated
vendored
Normal file
416
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/configmap_test.go
generated
vendored
Normal file
@ -0,0 +1,416 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestConfigMapGenerate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setup func(t *testing.T, params map[string]interface{}) func()
|
||||
params map[string]interface{}
|
||||
expected *v1.ConfigMap
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "test1",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: map[string]string{},
|
||||
BinaryData: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test2",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-867km9574f",
|
||||
},
|
||||
Data: map[string]string{},
|
||||
BinaryData: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test3",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"type": "my-type",
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: map[string]string{},
|
||||
BinaryData: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test4",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"type": "my-type",
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-867km9574f",
|
||||
},
|
||||
Data: map[string]string{},
|
||||
BinaryData: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test5",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-literal": []string{"key1=value1", "key2=value2"},
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
},
|
||||
BinaryData: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test6",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-literal": []string{"key1=value1", "key2=value2"},
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-gcb75dd9gb",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
},
|
||||
BinaryData: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test7",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-literal": []string{"key1value1"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test8",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-file": []string{"key1=/file=2"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test9",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-file": []string{"key1==value"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test10",
|
||||
setup: setupBinaryFile([]byte{0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64}),
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-file": []string{"foo1"},
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: map[string]string{"foo1": "hello world"},
|
||||
BinaryData: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test11",
|
||||
setup: setupBinaryFile([]byte{0xff, 0xfd}),
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-file": []string{"foo1"},
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: map[string]string{},
|
||||
BinaryData: map[string][]byte{"foo1": {0xff, 0xfd}},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test12",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-literal": []string{"key1==value1"},
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"key1": "=value1",
|
||||
},
|
||||
BinaryData: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test13",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-literal": []string{"key1==value1"},
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-bdgk9ttt7m",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"key1": "=value1",
|
||||
},
|
||||
BinaryData: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test14",
|
||||
setup: setupEnvFile("key1=value1", "#", "", "key2=value2"),
|
||||
params: map[string]interface{}{
|
||||
"name": "valid_env",
|
||||
"from-env-file": "file.env",
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "valid_env",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
},
|
||||
BinaryData: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test15",
|
||||
setup: setupEnvFile("key1=value1", "#", "", "key2=value2"),
|
||||
params: map[string]interface{}{
|
||||
"name": "valid_env",
|
||||
"from-env-file": "file.env",
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "valid_env-2cgh8552ch",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
},
|
||||
BinaryData: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test16",
|
||||
setup: func() func(t *testing.T, params map[string]interface{}) func() {
|
||||
os.Setenv("g_key1", "1")
|
||||
os.Setenv("g_key2", "2")
|
||||
return setupEnvFile("g_key1", "g_key2=")
|
||||
}(),
|
||||
params: map[string]interface{}{
|
||||
"name": "getenv",
|
||||
"from-env-file": "file.env",
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "getenv",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"g_key1": "1",
|
||||
"g_key2": "",
|
||||
},
|
||||
BinaryData: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test17",
|
||||
setup: func() func(t *testing.T, params map[string]interface{}) func() {
|
||||
os.Setenv("g_key1", "1")
|
||||
os.Setenv("g_key2", "2")
|
||||
return setupEnvFile("g_key1", "g_key2=")
|
||||
}(),
|
||||
params: map[string]interface{}{
|
||||
"name": "getenv",
|
||||
"from-env-file": "file.env",
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "getenv-b4hh92hgdk",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"g_key1": "1",
|
||||
"g_key2": "",
|
||||
},
|
||||
BinaryData: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test18",
|
||||
params: map[string]interface{}{
|
||||
"name": "too_many_args",
|
||||
"from-literal": []string{"key1=value1"},
|
||||
"from-env-file": "file.env",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{name: "test19",
|
||||
setup: setupEnvFile("key#1=value1"),
|
||||
params: map[string]interface{}{
|
||||
"name": "invalid_key",
|
||||
"from-env-file": "file.env",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test20",
|
||||
setup: setupEnvFile(" key1= value1"),
|
||||
params: map[string]interface{}{
|
||||
"name": "with_spaces",
|
||||
"from-env-file": "file.env",
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "with_spaces",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"key1": " value1",
|
||||
},
|
||||
BinaryData: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test21",
|
||||
setup: setupEnvFile(" key1= value1"),
|
||||
params: map[string]interface{}{
|
||||
"name": "with_spaces",
|
||||
"from-env-file": "file.env",
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "with_spaces-bfc558b4ct",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"key1": " value1",
|
||||
},
|
||||
BinaryData: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
generator := ConfigMapGeneratorV1{}
|
||||
for i, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.setup != nil {
|
||||
if teardown := tt.setup(t, tt.params); teardown != nil {
|
||||
defer teardown()
|
||||
}
|
||||
}
|
||||
obj, err := generator.Generate(tt.params)
|
||||
if !tt.expectErr && err != nil {
|
||||
t.Errorf("case %d, unexpected error: %v", i, err)
|
||||
}
|
||||
if tt.expectErr && err != nil {
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*v1.ConfigMap), tt.expected) {
|
||||
t.Errorf("\ncase %d, expected:\n%#v\nsaw:\n%#v", i, tt.expected, obj.(*v1.ConfigMap))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setupEnvFile(lines ...string) func(*testing.T, map[string]interface{}) func() {
|
||||
return func(t *testing.T, params map[string]interface{}) func() {
|
||||
f, err := ioutil.TempFile("", "cme")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
for _, l := range lines {
|
||||
f.WriteString(l)
|
||||
f.WriteString("\r\n")
|
||||
}
|
||||
f.Close()
|
||||
params["from-env-file"] = f.Name()
|
||||
return func() {
|
||||
os.Remove(f.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setupBinaryFile(data []byte) func(*testing.T, map[string]interface{}) func() {
|
||||
return func(t *testing.T, params map[string]interface{}) func() {
|
||||
tmp, _ := ioutil.TempDir("", "")
|
||||
f := tmp + "/foo1"
|
||||
ioutil.WriteFile(f, data, 0644)
|
||||
params["from-file"] = []string{f}
|
||||
return func() {
|
||||
os.Remove(f)
|
||||
}
|
||||
}
|
||||
}
|
180
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/deployment.go
generated
vendored
Normal file
180
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/deployment.go
generated
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||
"k8s.io/api/core/v1"
|
||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubectl/generate"
|
||||
)
|
||||
|
||||
// BaseDeploymentGenerator implements the common functionality of
|
||||
// DeploymentBasicGeneratorV1, DeploymentBasicAppsGeneratorV1Beta1 and DeploymentBasicAppsGeneratorV1. To reduce
|
||||
// confusion, it's best to keep this struct in the same file as those
|
||||
// generators.
|
||||
type BaseDeploymentGenerator struct {
|
||||
Name string
|
||||
Images []string
|
||||
}
|
||||
|
||||
// validate: check if the caller has forgotten to set one of our fields.
|
||||
func (b BaseDeploymentGenerator) validate() error {
|
||||
if len(b.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if len(b.Images) == 0 {
|
||||
return fmt.Errorf("at least one image must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// structuredGenerate: determine the fields of a deployment. The struct that
|
||||
// embeds BaseDeploymentGenerator should assemble these pieces into a
|
||||
// runtime.Object.
|
||||
func (b BaseDeploymentGenerator) structuredGenerate() (
|
||||
podSpec v1.PodSpec,
|
||||
labels map[string]string,
|
||||
selector metav1.LabelSelector,
|
||||
err error,
|
||||
) {
|
||||
err = b.validate()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
podSpec = buildPodSpec(b.Images)
|
||||
labels = map[string]string{}
|
||||
labels["app"] = b.Name
|
||||
selector = metav1.LabelSelector{MatchLabels: labels}
|
||||
return
|
||||
}
|
||||
|
||||
// buildPodSpec: parse the image strings and assemble them into the Containers
|
||||
// of a PodSpec. This is all you need to create the PodSpec for a deployment.
|
||||
func buildPodSpec(images []string) v1.PodSpec {
|
||||
podSpec := v1.PodSpec{Containers: []v1.Container{}}
|
||||
for _, imageString := range images {
|
||||
// Retain just the image name
|
||||
imageSplit := strings.Split(imageString, "/")
|
||||
name := imageSplit[len(imageSplit)-1]
|
||||
// Remove any tag or hash
|
||||
if strings.Contains(name, ":") {
|
||||
name = strings.Split(name, ":")[0]
|
||||
}
|
||||
if strings.Contains(name, "@") {
|
||||
name = strings.Split(name, "@")[0]
|
||||
}
|
||||
podSpec.Containers = append(podSpec.Containers, v1.Container{Name: name, Image: imageString})
|
||||
}
|
||||
return podSpec
|
||||
}
|
||||
|
||||
// DeploymentBasicGeneratorV1 supports stable generation of a deployment
|
||||
type DeploymentBasicGeneratorV1 struct {
|
||||
BaseDeploymentGenerator
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ generate.StructuredGenerator = &DeploymentBasicGeneratorV1{}
|
||||
|
||||
// StructuredGenerate outputs a deployment object using the configured fields
|
||||
func (s *DeploymentBasicGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
podSpec, labels, selector, err := s.structuredGenerate()
|
||||
one := int32(1)
|
||||
return &extensionsv1beta1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: s.Name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: extensionsv1beta1.DeploymentSpec{
|
||||
Replicas: &one,
|
||||
Selector: &selector,
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: podSpec,
|
||||
},
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
// DeploymentBasicAppsGeneratorV1Beta1 supports stable generation of a deployment under apps/v1beta1 endpoint
|
||||
type DeploymentBasicAppsGeneratorV1Beta1 struct {
|
||||
BaseDeploymentGenerator
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ generate.StructuredGenerator = &DeploymentBasicAppsGeneratorV1Beta1{}
|
||||
|
||||
// StructuredGenerate outputs a deployment object using the configured fields
|
||||
func (s *DeploymentBasicAppsGeneratorV1Beta1) StructuredGenerate() (runtime.Object, error) {
|
||||
podSpec, labels, selector, err := s.structuredGenerate()
|
||||
one := int32(1)
|
||||
return &appsv1beta1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: s.Name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: appsv1beta1.DeploymentSpec{
|
||||
Replicas: &one,
|
||||
Selector: &selector,
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: podSpec,
|
||||
},
|
||||
},
|
||||
}, err
|
||||
}
|
||||
|
||||
// DeploymentBasicAppsGeneratorV1 supports stable generation of a deployment under apps/v1 endpoint
|
||||
type DeploymentBasicAppsGeneratorV1 struct {
|
||||
BaseDeploymentGenerator
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ generate.StructuredGenerator = &DeploymentBasicAppsGeneratorV1{}
|
||||
|
||||
// StructuredGenerate outputs a deployment object using the configured fields
|
||||
func (s *DeploymentBasicAppsGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
podSpec, labels, selector, err := s.structuredGenerate()
|
||||
one := int32(1)
|
||||
return &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: s.Name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Replicas: &one,
|
||||
Selector: &selector,
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: podSpec,
|
||||
},
|
||||
},
|
||||
}, err
|
||||
}
|
105
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/deployment_test.go
generated
vendored
Normal file
105
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/deployment_test.go
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestDeploymentBasicGenerate(t *testing.T) {
|
||||
one := int32(1)
|
||||
tests := []struct {
|
||||
name string
|
||||
deploymentName string
|
||||
images []string
|
||||
expected *appsv1.Deployment
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "deployment name and images ok",
|
||||
deploymentName: "images-name-ok",
|
||||
images: []string{"nn/image1", "registry/nn/image2", "nn/image3:tag", "nn/image4@digest", "nn/image5@sha256:digest"},
|
||||
expected: &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "images-name-ok",
|
||||
Labels: map[string]string{"app": "images-name-ok"},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Replicas: &one,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"app": "images-name-ok"},
|
||||
},
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{"app": "images-name-ok"},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "image1", Image: "nn/image1"},
|
||||
{Name: "image2", Image: "registry/nn/image2"},
|
||||
{Name: "image3", Image: "nn/image3:tag"},
|
||||
{Name: "image4", Image: "nn/image4@digest"},
|
||||
{Name: "image5", Image: "nn/image5@sha256:digest"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "empty images",
|
||||
deploymentName: "images-empty",
|
||||
images: []string{},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "no images",
|
||||
deploymentName: "images-missing",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "no deployment name and images",
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
generator := &DeploymentBasicAppsGeneratorV1{
|
||||
BaseDeploymentGenerator{
|
||||
Name: tt.deploymentName,
|
||||
Images: tt.images,
|
||||
},
|
||||
}
|
||||
obj, err := generator.StructuredGenerate()
|
||||
if !tt.expectErr && err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if tt.expectErr && err != nil {
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*appsv1.Deployment), tt.expected) {
|
||||
t.Errorf("test: %v\nexpected:\n%#v\nsaw:\n%#v", tt.name, tt.expected, obj.(*appsv1.Deployment))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
103
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/env_file.go
generated
vendored
Normal file
103
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/env_file.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
)
|
||||
|
||||
var utf8bom = []byte{0xEF, 0xBB, 0xBF}
|
||||
|
||||
// proccessEnvFileLine returns a blank key if the line is empty or a comment.
|
||||
// The value will be retrieved from the environment if necessary.
|
||||
func proccessEnvFileLine(line []byte, filePath string,
|
||||
currentLine int) (key, value string, err error) {
|
||||
|
||||
if !utf8.Valid(line) {
|
||||
return ``, ``, fmt.Errorf("env file %s contains invalid utf8 bytes at line %d: %v",
|
||||
filePath, currentLine+1, line)
|
||||
}
|
||||
|
||||
// We trim UTF8 BOM from the first line of the file but no others
|
||||
if currentLine == 0 {
|
||||
line = bytes.TrimPrefix(line, utf8bom)
|
||||
}
|
||||
|
||||
// trim the line from all leading whitespace first
|
||||
line = bytes.TrimLeftFunc(line, unicode.IsSpace)
|
||||
|
||||
// If the line is empty or a comment, we return a blank key/value pair.
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
return ``, ``, nil
|
||||
}
|
||||
|
||||
data := strings.SplitN(string(line), "=", 2)
|
||||
key = data[0]
|
||||
if errs := validation.IsEnvVarName(key); len(errs) != 0 {
|
||||
return ``, ``, fmt.Errorf("%q is not a valid key name: %s", key, strings.Join(errs, ";"))
|
||||
}
|
||||
|
||||
if len(data) == 2 {
|
||||
value = data[1]
|
||||
} else {
|
||||
// No value (no `=` in the line) is a signal to obtain the value
|
||||
// from the environment.
|
||||
value = os.Getenv(key)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// addFromEnvFile processes an env file allows a generic addTo to handle the
|
||||
// collection of key value pairs or returns an error.
|
||||
func addFromEnvFile(filePath string, addTo func(key, value string) error) error {
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
currentLine := 0
|
||||
for scanner.Scan() {
|
||||
// Process the current line, retrieving a key/value pair if
|
||||
// possible.
|
||||
scannedBytes := scanner.Bytes()
|
||||
key, value, err := proccessEnvFileLine(scannedBytes, filePath, currentLine)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentLine++
|
||||
|
||||
if len(key) == 0 {
|
||||
// no key means line was empty or a comment
|
||||
continue
|
||||
}
|
||||
|
||||
if err = addTo(key, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
105
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/env_file_test.go
generated
vendored
Normal file
105
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/env_file_test.go
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test the cases of proccessEnvFileLine that can be run without touching the
|
||||
// environment.
|
||||
func Test_processEnvFileLine(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
line []byte
|
||||
currentLine int
|
||||
expectedKey string
|
||||
expectedValue string
|
||||
expectedErr string
|
||||
}{
|
||||
{"the utf8bom is trimmed on the first line",
|
||||
append(utf8bom, 'a', '=', 'c'), 0, "a", "c", ""},
|
||||
|
||||
{"the utf8bom is NOT trimmed on the second line",
|
||||
append(utf8bom, 'a', '=', 'c'), 1, "", "", "not a valid key name"},
|
||||
|
||||
{"no key is returned on a comment line",
|
||||
[]byte{' ', '#', 'c'}, 0, "", "", ""},
|
||||
|
||||
{"no key is returned on a blank line",
|
||||
[]byte{' ', ' ', '\t'}, 0, "", "", ""},
|
||||
|
||||
{"key is returned even with no value",
|
||||
[]byte{' ', 'x', '='}, 0, "x", "", ""},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
key, value, err := proccessEnvFileLine(tt.line, `filename`, tt.currentLine)
|
||||
t.Logf("Testing that %s.", tt.name)
|
||||
if tt.expectedKey != key {
|
||||
t.Errorf("\texpected key %q, received %q", tt.expectedKey, key)
|
||||
}
|
||||
if tt.expectedValue != value {
|
||||
t.Errorf("\texpected value %q, received %q", tt.expectedValue, value)
|
||||
}
|
||||
if len(tt.expectedErr) == 0 {
|
||||
if err != nil {
|
||||
t.Errorf("\tunexpected err %v", err)
|
||||
}
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), tt.expectedErr) {
|
||||
t.Errorf("\terr %v doesn't match expected %q", err, tt.expectedErr)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// proccessEnvFileLine needs to fetch the value from the environment if no
|
||||
// equals sign is provided.
|
||||
// For example:
|
||||
//
|
||||
// my_key1=alpha
|
||||
// my_key2=beta
|
||||
// my_key3
|
||||
//
|
||||
// In this file, my_key3 must be fetched from the environment.
|
||||
// Test this capability.
|
||||
func Test_processEnvFileLine_readEnvironment(t *testing.T) {
|
||||
const realKey = "k8s_test_env_file_key"
|
||||
const realValue = `my_value`
|
||||
|
||||
// Just in case, these two lines ensure the environment is restored to
|
||||
// its original state.
|
||||
original := os.Getenv(realKey)
|
||||
defer func() { os.Setenv(realKey, original) }()
|
||||
|
||||
os.Setenv(realKey, `my_value`)
|
||||
|
||||
key, value, err := proccessEnvFileLine([]byte(realKey), `filename`, 3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if key != realKey {
|
||||
t.Errorf(`expected key %q, received %q`, realKey, key)
|
||||
}
|
||||
if value != realValue {
|
||||
t.Errorf(`expected value %q, received %q`, realValue, value)
|
||||
}
|
||||
}
|
235
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/generator.go
generated
vendored
Normal file
235
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/generator.go
generated
vendored
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
batchv1beta1 "k8s.io/api/batch/v1beta1"
|
||||
batchv2alpha1 "k8s.io/api/batch/v2alpha1"
|
||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/kubernetes/pkg/kubectl/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.
|
||||
RunV1GeneratorName = "run/v1"
|
||||
RunPodV1GeneratorName = "run-pod/v1"
|
||||
ServiceV1GeneratorName = "service/v1"
|
||||
ServiceV2GeneratorName = "service/v2"
|
||||
ServiceNodePortGeneratorV1Name = "service-nodeport/v1"
|
||||
ServiceClusterIPGeneratorV1Name = "service-clusterip/v1"
|
||||
ServiceLoadBalancerGeneratorV1Name = "service-loadbalancer/v1"
|
||||
ServiceExternalNameGeneratorV1Name = "service-externalname/v1"
|
||||
ServiceAccountV1GeneratorName = "serviceaccount/v1"
|
||||
HorizontalPodAutoscalerV1GeneratorName = "horizontalpodautoscaler/v1"
|
||||
DeploymentV1Beta1GeneratorName = "deployment/v1beta1"
|
||||
DeploymentAppsV1Beta1GeneratorName = "deployment/apps.v1beta1"
|
||||
DeploymentAppsV1GeneratorName = "deployment/apps.v1"
|
||||
DeploymentBasicV1Beta1GeneratorName = "deployment-basic/v1beta1"
|
||||
DeploymentBasicAppsV1Beta1GeneratorName = "deployment-basic/apps.v1beta1"
|
||||
DeploymentBasicAppsV1GeneratorName = "deployment-basic/apps.v1"
|
||||
JobV1GeneratorName = "job/v1"
|
||||
CronJobV2Alpha1GeneratorName = "cronjob/v2alpha1"
|
||||
CronJobV1Beta1GeneratorName = "cronjob/v1beta1"
|
||||
NamespaceV1GeneratorName = "namespace/v1"
|
||||
ResourceQuotaV1GeneratorName = "resourcequotas/v1"
|
||||
SecretV1GeneratorName = "secret/v1"
|
||||
SecretForDockerRegistryV1GeneratorName = "secret-for-docker-registry/v1"
|
||||
SecretForTLSV1GeneratorName = "secret-for-tls/v1"
|
||||
ConfigMapV1GeneratorName = "configmap/v1"
|
||||
ClusterRoleBindingV1GeneratorName = "clusterrolebinding.rbac.authorization.k8s.io/v1alpha1"
|
||||
RoleBindingV1GeneratorName = "rolebinding.rbac.authorization.k8s.io/v1alpha1"
|
||||
PodDisruptionBudgetV1GeneratorName = "poddisruptionbudget/v1beta1"
|
||||
PodDisruptionBudgetV2GeneratorName = "poddisruptionbudget/v1beta1/v2"
|
||||
PriorityClassV1Alpha1GeneratorName = "priorityclass/v1alpha1"
|
||||
)
|
||||
|
||||
// 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{
|
||||
ServiceV1GeneratorName: ServiceGeneratorV1{},
|
||||
ServiceV2GeneratorName: ServiceGeneratorV2{},
|
||||
}
|
||||
case "service-clusterip":
|
||||
generator = map[string]generate.Generator{
|
||||
ServiceClusterIPGeneratorV1Name: ServiceClusterIPGeneratorV1{},
|
||||
}
|
||||
case "service-nodeport":
|
||||
generator = map[string]generate.Generator{
|
||||
ServiceNodePortGeneratorV1Name: ServiceNodePortGeneratorV1{},
|
||||
}
|
||||
case "service-loadbalancer":
|
||||
generator = map[string]generate.Generator{
|
||||
ServiceLoadBalancerGeneratorV1Name: ServiceLoadBalancerGeneratorV1{},
|
||||
}
|
||||
case "deployment":
|
||||
// Create Deployment has only StructuredGenerators and no
|
||||
// param-based Generators.
|
||||
// The StructuredGenerators are as follows (as of 2018-03-16):
|
||||
// DeploymentBasicV1Beta1GeneratorName -> DeploymentBasicGeneratorV1
|
||||
// DeploymentBasicAppsV1Beta1GeneratorName -> DeploymentBasicAppsGeneratorV1Beta1
|
||||
// DeploymentBasicAppsV1GeneratorName -> DeploymentBasicAppsGeneratorV1
|
||||
generator = map[string]generate.Generator{}
|
||||
case "run":
|
||||
generator = map[string]generate.Generator{
|
||||
RunV1GeneratorName: BasicReplicationController{},
|
||||
RunPodV1GeneratorName: BasicPod{},
|
||||
DeploymentV1Beta1GeneratorName: DeploymentV1Beta1{},
|
||||
DeploymentAppsV1Beta1GeneratorName: DeploymentAppsV1Beta1{},
|
||||
DeploymentAppsV1GeneratorName: DeploymentAppsV1{},
|
||||
JobV1GeneratorName: JobV1{},
|
||||
CronJobV2Alpha1GeneratorName: CronJobV2Alpha1{},
|
||||
CronJobV1Beta1GeneratorName: CronJobV1Beta1{},
|
||||
}
|
||||
case "namespace":
|
||||
generator = map[string]generate.Generator{
|
||||
NamespaceV1GeneratorName: NamespaceGeneratorV1{},
|
||||
}
|
||||
case "quota":
|
||||
generator = map[string]generate.Generator{
|
||||
ResourceQuotaV1GeneratorName: ResourceQuotaGeneratorV1{},
|
||||
}
|
||||
case "secret":
|
||||
generator = map[string]generate.Generator{
|
||||
SecretV1GeneratorName: SecretGeneratorV1{},
|
||||
}
|
||||
case "secret-for-docker-registry":
|
||||
generator = map[string]generate.Generator{
|
||||
SecretForDockerRegistryV1GeneratorName: SecretForDockerRegistryGeneratorV1{},
|
||||
}
|
||||
case "secret-for-tls":
|
||||
generator = map[string]generate.Generator{
|
||||
SecretForTLSV1GeneratorName: SecretForTLSGeneratorV1{},
|
||||
}
|
||||
}
|
||||
|
||||
return generator
|
||||
}
|
||||
|
||||
// FallbackGeneratorNameIfNecessary returns the name of the old generator
|
||||
// if server does not support new generator. Otherwise, the
|
||||
// generator string is returned unchanged.
|
||||
//
|
||||
// If the generator name is changed, print a warning message to let the user
|
||||
// know.
|
||||
func FallbackGeneratorNameIfNecessary(
|
||||
generatorName string,
|
||||
discoveryClient discovery.DiscoveryInterface,
|
||||
cmdErr io.Writer,
|
||||
) (string, error) {
|
||||
switch generatorName {
|
||||
case DeploymentAppsV1GeneratorName:
|
||||
hasResource, err := HasResource(discoveryClient, appsv1.SchemeGroupVersion.WithResource("deployments"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !hasResource {
|
||||
return FallbackGeneratorNameIfNecessary(DeploymentAppsV1Beta1GeneratorName, discoveryClient, cmdErr)
|
||||
}
|
||||
case DeploymentAppsV1Beta1GeneratorName:
|
||||
hasResource, err := HasResource(discoveryClient, appsv1beta1.SchemeGroupVersion.WithResource("deployments"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !hasResource {
|
||||
return FallbackGeneratorNameIfNecessary(DeploymentV1Beta1GeneratorName, discoveryClient, cmdErr)
|
||||
}
|
||||
case DeploymentV1Beta1GeneratorName:
|
||||
hasResource, err := HasResource(discoveryClient, extensionsv1beta1.SchemeGroupVersion.WithResource("deployments"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !hasResource {
|
||||
return RunV1GeneratorName, nil
|
||||
}
|
||||
case DeploymentBasicAppsV1GeneratorName:
|
||||
hasResource, err := HasResource(discoveryClient, appsv1.SchemeGroupVersion.WithResource("deployments"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !hasResource {
|
||||
return FallbackGeneratorNameIfNecessary(DeploymentBasicAppsV1Beta1GeneratorName, discoveryClient, cmdErr)
|
||||
}
|
||||
case DeploymentBasicAppsV1Beta1GeneratorName:
|
||||
hasResource, err := HasResource(discoveryClient, appsv1beta1.SchemeGroupVersion.WithResource("deployments"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !hasResource {
|
||||
return DeploymentBasicV1Beta1GeneratorName, nil
|
||||
}
|
||||
case JobV1GeneratorName:
|
||||
hasResource, err := HasResource(discoveryClient, batchv1.SchemeGroupVersion.WithResource("jobs"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !hasResource {
|
||||
return RunPodV1GeneratorName, nil
|
||||
}
|
||||
case CronJobV1Beta1GeneratorName:
|
||||
hasResource, err := HasResource(discoveryClient, batchv1beta1.SchemeGroupVersion.WithResource("cronjobs"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !hasResource {
|
||||
return FallbackGeneratorNameIfNecessary(CronJobV2Alpha1GeneratorName, discoveryClient, cmdErr)
|
||||
}
|
||||
case CronJobV2Alpha1GeneratorName:
|
||||
hasResource, err := HasResource(discoveryClient, batchv2alpha1.SchemeGroupVersion.WithResource("cronjobs"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !hasResource {
|
||||
return JobV1GeneratorName, nil
|
||||
}
|
||||
}
|
||||
return generatorName, nil
|
||||
}
|
||||
|
||||
func HasResource(client discovery.DiscoveryInterface, resource schema.GroupVersionResource) (bool, error) {
|
||||
resources, err := client.ServerResourcesForGroupVersion(resource.GroupVersion().String())
|
||||
if apierrors.IsNotFound(err) {
|
||||
// entire group is missing
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
// other errors error
|
||||
return false, fmt.Errorf("failed to discover supported resources: %v", err)
|
||||
}
|
||||
for _, serverResource := range resources.APIResources {
|
||||
if serverResource.Name == resource.Resource {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
80
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/namespace.go
generated
vendored
Normal file
80
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/namespace.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
Copyright 2015 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"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubectl/generate"
|
||||
)
|
||||
|
||||
// NamespaceGeneratorV1 supports stable generation of a namespace
|
||||
type NamespaceGeneratorV1 struct {
|
||||
// Name of namespace
|
||||
Name string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter injection
|
||||
var _ generate.Generator = &NamespaceGeneratorV1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ generate.StructuredGenerator = &NamespaceGeneratorV1{}
|
||||
|
||||
// Generate returns a namespace using the specified parameters
|
||||
func (g NamespaceGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := generate.ValidateParams(g.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
}
|
||||
delegate := &NamespaceGeneratorV1{Name: params["name"]}
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern
|
||||
func (g NamespaceGeneratorV1) ParamNames() []generate.GeneratorParam {
|
||||
return []generate.GeneratorParam{
|
||||
{Name: "name", Required: true},
|
||||
}
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a namespace object using the configured fields
|
||||
func (g *NamespaceGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := g.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
namespace := &v1.Namespace{}
|
||||
namespace.Name = g.Name
|
||||
return namespace, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation
|
||||
func (g *NamespaceGeneratorV1) validate() error {
|
||||
if len(g.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
108
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/namespace_test.go
generated
vendored
Normal file
108
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/namespace_test.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
Copyright 2015 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"
|
||||
)
|
||||
|
||||
func TestNamespaceGenerate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params map[string]interface{}
|
||||
expected *v1.Namespace
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "test1",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
},
|
||||
expected: &v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test2",
|
||||
params: map[string]interface{}{},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test3",
|
||||
params: map[string]interface{}{
|
||||
"name": 1,
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test4",
|
||||
params: map[string]interface{}{
|
||||
"name": "",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test5",
|
||||
params: map[string]interface{}{
|
||||
"name": nil,
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test6",
|
||||
params: map[string]interface{}{
|
||||
"name_wrong_key": "some_value",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test7",
|
||||
params: map[string]interface{}{
|
||||
"NAME": "some_value",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
generator := NamespaceGeneratorV1{}
|
||||
for index, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
obj, err := generator.Generate(tt.params)
|
||||
switch {
|
||||
case tt.expectErr && err != nil:
|
||||
return // loop, since there's no output to check
|
||||
case tt.expectErr && err == nil:
|
||||
t.Errorf("%v: expected error and didn't get one", index)
|
||||
return // loop, no expected output object
|
||||
case !tt.expectErr && err != nil:
|
||||
t.Errorf("%v: unexpected error %v", index, err)
|
||||
return // loop, no output object
|
||||
case !tt.expectErr && err == nil:
|
||||
// do nothing and drop through
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*v1.Namespace), tt.expected) {
|
||||
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", tt.expected, obj.(*v1.Namespace))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
214
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/pdb.go
generated
vendored
Normal file
214
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/pdb.go
generated
vendored
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
policy "k8s.io/api/policy/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/kubernetes/pkg/kubectl/generate"
|
||||
)
|
||||
|
||||
// PodDisruptionBudgetV1Generator supports stable generation of a pod disruption budget.
|
||||
type PodDisruptionBudgetV1Generator struct {
|
||||
Name string
|
||||
MinAvailable string
|
||||
Selector string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction.
|
||||
var _ generate.StructuredGenerator = &PodDisruptionBudgetV1Generator{}
|
||||
|
||||
func (PodDisruptionBudgetV1Generator) ParamNames() []generate.GeneratorParam {
|
||||
return []generate.GeneratorParam{
|
||||
{Name: "name", Required: true},
|
||||
{Name: "min-available", Required: false},
|
||||
{Name: "selector", Required: true},
|
||||
}
|
||||
}
|
||||
|
||||
func (s PodDisruptionBudgetV1Generator) Generate(params map[string]interface{}) (runtime.Object, error) {
|
||||
err := generate.ValidateParams(s.ParamNames(), params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name, isString := params["name"].(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, found %T for 'name'", params["name"])
|
||||
}
|
||||
minAvailable, isString := params["min-available"].(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, found %T for 'min-available'", params["min-available"])
|
||||
}
|
||||
selector, isString := params["selector"].(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, found %T for 'selector'", params["selector"])
|
||||
}
|
||||
delegate := &PodDisruptionBudgetV1Generator{Name: name, MinAvailable: minAvailable, Selector: selector}
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a pod disruption budget object using the configured fields.
|
||||
func (s *PodDisruptionBudgetV1Generator) StructuredGenerate() (runtime.Object, error) {
|
||||
if len(s.MinAvailable) == 0 {
|
||||
// defaulting behavior seen in Kubernetes 1.6 and below.
|
||||
s.MinAvailable = "1"
|
||||
}
|
||||
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selector, err := metav1.ParseToLabelSelector(s.Selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
minAvailable := intstr.Parse(s.MinAvailable)
|
||||
return &policy.PodDisruptionBudget{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: s.Name,
|
||||
},
|
||||
Spec: policy.PodDisruptionBudgetSpec{
|
||||
MinAvailable: &minAvailable,
|
||||
Selector: selector,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation.
|
||||
func (s *PodDisruptionBudgetV1Generator) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if len(s.Selector) == 0 {
|
||||
return fmt.Errorf("a selector must be specified")
|
||||
}
|
||||
if len(s.MinAvailable) == 0 {
|
||||
return fmt.Errorf("the minimum number of available pods required must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PodDisruptionBudgetV2Generator supports stable generation of a pod disruption budget.
|
||||
type PodDisruptionBudgetV2Generator struct {
|
||||
Name string
|
||||
MinAvailable string
|
||||
MaxUnavailable string
|
||||
Selector string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction.
|
||||
var _ generate.StructuredGenerator = &PodDisruptionBudgetV2Generator{}
|
||||
|
||||
func (PodDisruptionBudgetV2Generator) ParamNames() []generate.GeneratorParam {
|
||||
return []generate.GeneratorParam{
|
||||
{Name: "name", Required: true},
|
||||
{Name: "min-available", Required: false},
|
||||
{Name: "max-unavailable", Required: false},
|
||||
{Name: "selector", Required: true},
|
||||
}
|
||||
}
|
||||
|
||||
func (s PodDisruptionBudgetV2Generator) Generate(params map[string]interface{}) (runtime.Object, error) {
|
||||
err := generate.ValidateParams(s.ParamNames(), params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, isString := params["name"].(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, found %T for 'name'", params["name"])
|
||||
}
|
||||
|
||||
minAvailable, isString := params["min-available"].(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, found %T for 'min-available'", params["min-available"])
|
||||
}
|
||||
|
||||
maxUnavailable, isString := params["max-unavailable"].(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, found %T for 'max-unavailable'", params["max-unavailable"])
|
||||
}
|
||||
|
||||
selector, isString := params["selector"].(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, found %T for 'selector'", params["selector"])
|
||||
}
|
||||
delegate := &PodDisruptionBudgetV2Generator{Name: name, MinAvailable: minAvailable, MaxUnavailable: maxUnavailable, Selector: selector}
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a pod disruption budget object using the configured fields.
|
||||
func (s *PodDisruptionBudgetV2Generator) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selector, err := metav1.ParseToLabelSelector(s.Selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(s.MaxUnavailable) > 0 {
|
||||
maxUnavailable := intstr.Parse(s.MaxUnavailable)
|
||||
return &policy.PodDisruptionBudget{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: s.Name,
|
||||
},
|
||||
Spec: policy.PodDisruptionBudgetSpec{
|
||||
MaxUnavailable: &maxUnavailable,
|
||||
Selector: selector,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
if len(s.MinAvailable) > 0 {
|
||||
minAvailable := intstr.Parse(s.MinAvailable)
|
||||
return &policy.PodDisruptionBudget{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: s.Name,
|
||||
},
|
||||
Spec: policy.PodDisruptionBudgetSpec{
|
||||
MinAvailable: &minAvailable,
|
||||
Selector: selector,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation.
|
||||
func (s *PodDisruptionBudgetV2Generator) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if len(s.Selector) == 0 {
|
||||
return fmt.Errorf("a selector must be specified")
|
||||
}
|
||||
if len(s.MaxUnavailable) == 0 && len(s.MinAvailable) == 0 {
|
||||
return fmt.Errorf("one of min-available or max-unavailable must be specified")
|
||||
}
|
||||
if len(s.MaxUnavailable) > 0 && len(s.MinAvailable) > 0 {
|
||||
return fmt.Errorf("min-available and max-unavailable cannot be both specified")
|
||||
}
|
||||
return nil
|
||||
}
|
368
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/pdb_test.go
generated
vendored
Normal file
368
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/pdb_test.go
generated
vendored
Normal file
@ -0,0 +1,368 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
policy "k8s.io/api/policy/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
func TestPodDisruptionBudgetV1Generate(t *testing.T) {
|
||||
name := "foo"
|
||||
minAvailable := "5"
|
||||
minAvailableIS := intstr.Parse(minAvailable)
|
||||
defaultMinAvailableIS := intstr.Parse("1")
|
||||
selector := "app=foo"
|
||||
labelSelector, err := metav1.ParseToLabelSelector(selector)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
params map[string]interface{}
|
||||
expectErrMsg string
|
||||
expectPDB *policy.PodDisruptionBudget
|
||||
}{
|
||||
{
|
||||
name: "test-valid-use",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": minAvailable,
|
||||
"selector": selector,
|
||||
},
|
||||
expectPDB: &policy.PodDisruptionBudget{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: policy.PodDisruptionBudgetSpec{
|
||||
MinAvailable: &minAvailableIS,
|
||||
Selector: labelSelector,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "test-missing-name-param",
|
||||
params: map[string]interface{}{
|
||||
"min-available": minAvailable,
|
||||
"selector": selector,
|
||||
},
|
||||
expectErrMsg: "Parameter: name is required",
|
||||
},
|
||||
{
|
||||
name: "test-blank-name-param",
|
||||
params: map[string]interface{}{
|
||||
"name": "",
|
||||
"min-available": minAvailable,
|
||||
"selector": selector,
|
||||
},
|
||||
expectErrMsg: "Parameter: name is required",
|
||||
},
|
||||
{
|
||||
name: "test-invalid-name-param",
|
||||
params: map[string]interface{}{
|
||||
"name": 1,
|
||||
"min-available": minAvailable,
|
||||
"selector": selector,
|
||||
},
|
||||
expectErrMsg: "expected string, found int for 'name'",
|
||||
},
|
||||
{
|
||||
name: "test-missing-min-available-param",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"selector": selector,
|
||||
},
|
||||
expectErrMsg: "expected string, found <nil> for 'min-available'",
|
||||
},
|
||||
{
|
||||
name: "test-blank-min-available-param",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": "",
|
||||
"selector": selector,
|
||||
},
|
||||
expectPDB: &policy.PodDisruptionBudget{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: policy.PodDisruptionBudgetSpec{
|
||||
MinAvailable: &defaultMinAvailableIS,
|
||||
Selector: labelSelector,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "test-invalid-min-available-param",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": 1,
|
||||
"selector": selector,
|
||||
},
|
||||
expectErrMsg: "expected string, found int for 'min-available'",
|
||||
},
|
||||
{
|
||||
name: "test-missing-selector-param",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": minAvailable,
|
||||
},
|
||||
expectErrMsg: "Parameter: selector is required",
|
||||
},
|
||||
{
|
||||
name: "test-blank-selector-param",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": minAvailable,
|
||||
"selector": "",
|
||||
},
|
||||
expectErrMsg: "Parameter: selector is required",
|
||||
},
|
||||
{
|
||||
name: "test-invalid-selector-param",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": minAvailable,
|
||||
"selector": 1,
|
||||
},
|
||||
expectErrMsg: "expected string, found int for 'selector'",
|
||||
},
|
||||
}
|
||||
|
||||
generator := PodDisruptionBudgetV1Generator{}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
obj, err := generator.Generate(tt.params)
|
||||
switch {
|
||||
case tt.expectErrMsg != "" && err != nil:
|
||||
if err.Error() != tt.expectErrMsg {
|
||||
t.Errorf("test '%s': expect error '%s', but saw '%s'", tt.name, tt.expectErrMsg, err.Error())
|
||||
}
|
||||
return
|
||||
case tt.expectErrMsg != "" && err == nil:
|
||||
t.Errorf("test '%s': expected error '%s' and didn't get one", tt.name, tt.expectErrMsg)
|
||||
return
|
||||
case tt.expectErrMsg == "" && err != nil:
|
||||
t.Errorf("test '%s': unexpected error %s", tt.name, err.Error())
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*policy.PodDisruptionBudget), tt.expectPDB) {
|
||||
t.Errorf("test '%s': expected:\n%#v\nsaw:\n%#v", tt.name, tt.expectPDB, obj.(*policy.PodDisruptionBudget))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodDisruptionBudgetV2Generate(t *testing.T) {
|
||||
name := "foo"
|
||||
minAvailable := "1"
|
||||
minAvailableIS := intstr.Parse(minAvailable)
|
||||
maxUnavailable := "5%"
|
||||
maxUnavailableIS := intstr.Parse(maxUnavailable)
|
||||
selector := "app=foo"
|
||||
labelSelector, err := metav1.ParseToLabelSelector(selector)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
params map[string]interface{}
|
||||
expectErrMsg string
|
||||
expectPDB *policy.PodDisruptionBudget
|
||||
}{
|
||||
{
|
||||
name: "test-valid-min-available",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": minAvailable,
|
||||
"max-unavailable": "",
|
||||
"selector": selector,
|
||||
},
|
||||
expectPDB: &policy.PodDisruptionBudget{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: policy.PodDisruptionBudgetSpec{
|
||||
MinAvailable: &minAvailableIS,
|
||||
Selector: labelSelector,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "test-valid-max-available",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": "",
|
||||
"max-unavailable": maxUnavailable,
|
||||
"selector": selector,
|
||||
},
|
||||
expectPDB: &policy.PodDisruptionBudget{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: policy.PodDisruptionBudgetSpec{
|
||||
MaxUnavailable: &maxUnavailableIS,
|
||||
Selector: labelSelector,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "test-missing-name-param",
|
||||
params: map[string]interface{}{
|
||||
"min-available": "",
|
||||
"max-unavailable": "",
|
||||
"selector": selector,
|
||||
},
|
||||
expectErrMsg: "Parameter: name is required",
|
||||
},
|
||||
{
|
||||
name: "test-blank-name-param",
|
||||
params: map[string]interface{}{
|
||||
"name": "",
|
||||
"min-available": "",
|
||||
"max-unavailable": "",
|
||||
"selector": selector,
|
||||
},
|
||||
expectErrMsg: "Parameter: name is required",
|
||||
},
|
||||
{
|
||||
name: "test-invalid-name-param",
|
||||
params: map[string]interface{}{
|
||||
"name": 1,
|
||||
"min-available": "",
|
||||
"max-unavailable": "",
|
||||
"selector": selector,
|
||||
},
|
||||
expectErrMsg: "expected string, found int for 'name'",
|
||||
},
|
||||
{
|
||||
name: "test-missing-min-available-param",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"max-unavailable": "",
|
||||
"selector": selector,
|
||||
},
|
||||
expectErrMsg: "expected string, found <nil> for 'min-available'",
|
||||
},
|
||||
{
|
||||
name: "test-invalid-min-available-param",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": 1,
|
||||
"max-unavailable": "",
|
||||
"selector": selector,
|
||||
},
|
||||
expectErrMsg: "expected string, found int for 'min-available'",
|
||||
},
|
||||
{
|
||||
name: "test-missing-max-available-param",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": "",
|
||||
"selector": selector,
|
||||
},
|
||||
expectErrMsg: "expected string, found <nil> for 'max-unavailable'",
|
||||
},
|
||||
{
|
||||
name: "test-invalid-max-available-param",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": "",
|
||||
"max-unavailable": 1,
|
||||
"selector": selector,
|
||||
},
|
||||
expectErrMsg: "expected string, found int for 'max-unavailable'",
|
||||
},
|
||||
{
|
||||
name: "test-blank-min-available-max-unavailable-param",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": "",
|
||||
"max-unavailable": "",
|
||||
"selector": selector,
|
||||
},
|
||||
expectErrMsg: "one of min-available or max-unavailable must be specified",
|
||||
},
|
||||
{
|
||||
name: "test-min-available-max-unavailable-param",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": minAvailable,
|
||||
"max-unavailable": maxUnavailable,
|
||||
"selector": selector,
|
||||
},
|
||||
expectErrMsg: "min-available and max-unavailable cannot be both specified",
|
||||
},
|
||||
{
|
||||
name: "test-missing-selector-param",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": "",
|
||||
"max-unavailable": "",
|
||||
},
|
||||
expectErrMsg: "Parameter: selector is required",
|
||||
},
|
||||
{
|
||||
name: "test-blank-selector-param",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": "",
|
||||
"max-unavailable": "",
|
||||
"selector": "",
|
||||
},
|
||||
expectErrMsg: "Parameter: selector is required",
|
||||
},
|
||||
{
|
||||
name: "test-invalid-selector-param",
|
||||
params: map[string]interface{}{
|
||||
"name": name,
|
||||
"min-available": "",
|
||||
"max-unavailable": "",
|
||||
"selector": 1,
|
||||
},
|
||||
expectErrMsg: "expected string, found int for 'selector'",
|
||||
},
|
||||
}
|
||||
|
||||
generator := PodDisruptionBudgetV2Generator{}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
obj, err := generator.Generate(tt.params)
|
||||
switch {
|
||||
case tt.expectErrMsg != "" && err != nil:
|
||||
if err.Error() != tt.expectErrMsg {
|
||||
t.Errorf("test '%s': expect error '%s', but saw '%s'", tt.name, tt.expectErrMsg, err.Error())
|
||||
}
|
||||
return
|
||||
case tt.expectErrMsg != "" && err == nil:
|
||||
t.Errorf("test '%s': expected error '%s' and didn't get one", tt.name, tt.expectErrMsg)
|
||||
return
|
||||
case tt.expectErrMsg == "" && err != nil:
|
||||
t.Errorf("test '%s': unexpected error %s", tt.name, err.Error())
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*policy.PodDisruptionBudget), tt.expectPDB) {
|
||||
t.Errorf("test '%s': expected:\n%#v\nsaw:\n%#v", tt.name, tt.expectPDB, obj.(*policy.PodDisruptionBudget))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
86
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/priorityclass.go
generated
vendored
Normal file
86
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/priorityclass.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
scheduling "k8s.io/api/scheduling/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubectl/generate"
|
||||
)
|
||||
|
||||
// PriorityClassV1Generator supports stable generation of a priorityClass.
|
||||
type PriorityClassV1Generator struct {
|
||||
Name string
|
||||
Value int32
|
||||
GlobalDefault bool
|
||||
Description string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction.
|
||||
var _ generate.StructuredGenerator = &PriorityClassV1Generator{}
|
||||
|
||||
func (PriorityClassV1Generator) ParamNames() []generate.GeneratorParam {
|
||||
return []generate.GeneratorParam{
|
||||
{Name: "name", Required: true},
|
||||
{Name: "value", Required: true},
|
||||
{Name: "global-default", Required: false},
|
||||
{Name: "description", Required: false},
|
||||
}
|
||||
}
|
||||
|
||||
func (s PriorityClassV1Generator) Generate(params map[string]interface{}) (runtime.Object, error) {
|
||||
if err := generate.ValidateParams(s.ParamNames(), params); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, found := params["name"].(string)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("expected string, saw %v for 'name'", name)
|
||||
}
|
||||
|
||||
value, found := params["value"].(int32)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("expected int32, found %v", value)
|
||||
}
|
||||
|
||||
globalDefault, found := params["global-default"].(bool)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("expected bool, found %v", globalDefault)
|
||||
}
|
||||
|
||||
description, found := params["description"].(string)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("expected string, found %v", description)
|
||||
}
|
||||
delegate := &PriorityClassV1Generator{Name: name, Value: value, GlobalDefault: globalDefault, Description: description}
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a priorityClass object using the configured fields.
|
||||
func (s *PriorityClassV1Generator) StructuredGenerate() (runtime.Object, error) {
|
||||
return &scheduling.PriorityClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: s.Name,
|
||||
},
|
||||
Value: s.Value,
|
||||
GlobalDefault: s.GlobalDefault,
|
||||
Description: s.Description,
|
||||
}, nil
|
||||
}
|
96
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/priorityclass_test.go
generated
vendored
Normal file
96
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/priorityclass_test.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
scheduling "k8s.io/api/scheduling/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPriorityClassV1Generator(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params map[string]interface{}
|
||||
expected *scheduling.PriorityClass
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "test valid case",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"value": int32(1000),
|
||||
"global-default": false,
|
||||
"description": "high priority class",
|
||||
},
|
||||
expected: &scheduling.PriorityClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Value: int32(1000),
|
||||
GlobalDefault: false,
|
||||
Description: "high priority class",
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test valid case that as default priority",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"value": int32(1000),
|
||||
"global-default": true,
|
||||
"description": "high priority class",
|
||||
},
|
||||
expected: &scheduling.PriorityClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Value: int32(1000),
|
||||
GlobalDefault: true,
|
||||
Description: "high priority class",
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test missing required param",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"global-default": true,
|
||||
"description": "high priority class",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
generator := PriorityClassV1Generator{}
|
||||
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("%s: unexpected error: %v", tt.name, err)
|
||||
}
|
||||
if tt.expectErr && err != nil {
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*scheduling.PriorityClass), tt.expected) {
|
||||
t.Errorf("%s:\nexpected:\n%#v\nsaw:\n%#v", tt.name, tt.expected, obj.(*scheduling.PriorityClass))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
126
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/quota.go
generated
vendored
Normal file
126
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/quota.go
generated
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubectl/generate"
|
||||
)
|
||||
|
||||
// ResourceQuotaGeneratorV1 supports stable generation of a resource quota
|
||||
type ResourceQuotaGeneratorV1 struct {
|
||||
// The name of a quota object.
|
||||
Name string
|
||||
|
||||
// The hard resource limit string before parsing.
|
||||
Hard string
|
||||
|
||||
// The scopes of a quota object before parsing.
|
||||
Scopes string
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern
|
||||
func (g ResourceQuotaGeneratorV1) ParamNames() []generate.GeneratorParam {
|
||||
return []generate.GeneratorParam{
|
||||
{Name: "name", Required: true},
|
||||
{Name: "hard", Required: true},
|
||||
{Name: "scopes", Required: false},
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter injection
|
||||
var _ generate.Generator = &ResourceQuotaGeneratorV1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ generate.StructuredGenerator = &ResourceQuotaGeneratorV1{}
|
||||
|
||||
func (g ResourceQuotaGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := generate.ValidateParams(g.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
delegate := &ResourceQuotaGeneratorV1{}
|
||||
delegate.Name = params["name"]
|
||||
delegate.Hard = params["hard"]
|
||||
delegate.Scopes = params["scopes"]
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a ResourceQuota object using the configured fields
|
||||
func (g *ResourceQuotaGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := g.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceList, err := populateResourceListV1(g.Hard)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scopes, err := parseScopes(g.Scopes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceQuota := &v1.ResourceQuota{}
|
||||
resourceQuota.Name = g.Name
|
||||
resourceQuota.Spec.Hard = resourceList
|
||||
resourceQuota.Spec.Scopes = scopes
|
||||
return resourceQuota, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation
|
||||
func (r *ResourceQuotaGeneratorV1) validate() error {
|
||||
if len(r.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseScopes(spec string) ([]v1.ResourceQuotaScope, error) {
|
||||
// empty input gets a nil response to preserve generator test expected behaviors
|
||||
if spec == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
scopes := strings.Split(spec, ",")
|
||||
result := make([]v1.ResourceQuotaScope, 0, len(scopes))
|
||||
for _, scope := range scopes {
|
||||
// intentionally do not verify the scope against the valid scope list. This is done by the apiserver anyway.
|
||||
|
||||
if scope == "" {
|
||||
return nil, fmt.Errorf("invalid resource quota scope \"\"")
|
||||
}
|
||||
|
||||
result = append(result, v1.ResourceQuotaScope(scope))
|
||||
}
|
||||
return result, nil
|
||||
}
|
123
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/quota_test.go
generated
vendored
Normal file
123
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/quota_test.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestQuotaGenerate(t *testing.T) {
|
||||
hard := "cpu=10,memory=5G,pods=10,services=7"
|
||||
resourceQuotaSpecList, err := populateResourceListV1(hard)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
params map[string]interface{}
|
||||
expected *v1.ResourceQuota
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "test-valid-use",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"hard": hard,
|
||||
},
|
||||
expected: &v1.ResourceQuota{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: v1.ResourceQuotaSpec{Hard: resourceQuotaSpecList},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test-missing-required-param",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test-valid-scopes",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"hard": hard,
|
||||
"scopes": "BestEffort,NotTerminating",
|
||||
},
|
||||
expected: &v1.ResourceQuota{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: v1.ResourceQuotaSpec{
|
||||
Hard: resourceQuotaSpecList,
|
||||
Scopes: []v1.ResourceQuotaScope{
|
||||
v1.ResourceQuotaScopeBestEffort,
|
||||
v1.ResourceQuotaScopeNotTerminating,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test-empty-scopes",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"hard": hard,
|
||||
"scopes": "",
|
||||
},
|
||||
expected: &v1.ResourceQuota{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: v1.ResourceQuotaSpec{Hard: resourceQuotaSpecList},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test-invalid-scopes",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"hard": hard,
|
||||
"scopes": "abc,",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
generator := ResourceQuotaGeneratorV1{}
|
||||
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("%s: unexpected error: %v", tt.name, err)
|
||||
}
|
||||
if tt.expectErr && err != nil {
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*v1.ResourceQuota), tt.expected) {
|
||||
t.Errorf("%s:\nexpected:\n%#v\nsaw:\n%#v", tt.name, tt.expected, obj.(*v1.ResourceQuota))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
174
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/rolebinding.go
generated
vendored
Normal file
174
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/rolebinding.go
generated
vendored
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"strings"
|
||||
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/kubectl/generate"
|
||||
)
|
||||
|
||||
// RoleBindingGeneratorV1 supports stable generation of a roleBinding.
|
||||
type RoleBindingGeneratorV1 struct {
|
||||
// Name of roleBinding (required)
|
||||
Name string
|
||||
// ClusterRole for the roleBinding
|
||||
ClusterRole string
|
||||
// Role for the roleBinding
|
||||
Role string
|
||||
// Users to derive the roleBinding from (optional)
|
||||
Users []string
|
||||
// Groups to derive the roleBinding from (optional)
|
||||
Groups []string
|
||||
// ServiceAccounts to derive the roleBinding from in namespace:name format(optional)
|
||||
ServiceAccounts []string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter injection.
|
||||
var _ generate.Generator = &RoleBindingGeneratorV1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction.
|
||||
var _ generate.StructuredGenerator = &RoleBindingGeneratorV1{}
|
||||
|
||||
// Generate returns a roleBinding using the specified parameters.
|
||||
func (s RoleBindingGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := generate.ValidateParams(s.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &RoleBindingGeneratorV1{}
|
||||
userStrings, found := genericParams["user"]
|
||||
if found {
|
||||
fromFileArray, isArray := userStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", userStrings)
|
||||
}
|
||||
delegate.Users = fromFileArray
|
||||
delete(genericParams, "user")
|
||||
}
|
||||
groupStrings, found := genericParams["group"]
|
||||
if found {
|
||||
fromLiteralArray, isArray := groupStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", groupStrings)
|
||||
}
|
||||
delegate.Groups = fromLiteralArray
|
||||
delete(genericParams, "group")
|
||||
}
|
||||
saStrings, found := genericParams["serviceaccount"]
|
||||
if found {
|
||||
fromLiteralArray, isArray := saStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", saStrings)
|
||||
}
|
||||
delegate.ServiceAccounts = fromLiteralArray
|
||||
delete(genericParams, "serviceaccount")
|
||||
}
|
||||
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
|
||||
}
|
||||
delegate.Name = params["name"]
|
||||
delegate.ClusterRole = params["clusterrole"]
|
||||
delegate.Role = params["role"]
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern.
|
||||
func (s RoleBindingGeneratorV1) ParamNames() []generate.GeneratorParam {
|
||||
return []generate.GeneratorParam{
|
||||
{Name: "name", Required: true},
|
||||
{Name: "clusterrole", Required: false},
|
||||
{Name: "role", Required: false},
|
||||
{Name: "user", Required: false},
|
||||
{Name: "group", Required: false},
|
||||
{Name: "serviceaccount", Required: false},
|
||||
}
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a roleBinding object using the configured fields.
|
||||
func (s RoleBindingGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
roleBinding := &rbacv1.RoleBinding{}
|
||||
roleBinding.Name = s.Name
|
||||
|
||||
switch {
|
||||
case len(s.Role) > 0:
|
||||
roleBinding.RoleRef = rbacv1.RoleRef{
|
||||
APIGroup: rbacv1.GroupName,
|
||||
Kind: "Role",
|
||||
Name: s.Role,
|
||||
}
|
||||
case len(s.ClusterRole) > 0:
|
||||
roleBinding.RoleRef = rbacv1.RoleRef{
|
||||
APIGroup: rbacv1.GroupName,
|
||||
Kind: "ClusterRole",
|
||||
Name: s.ClusterRole,
|
||||
}
|
||||
}
|
||||
|
||||
for _, user := range sets.NewString(s.Users...).List() {
|
||||
roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{
|
||||
Kind: rbacv1.UserKind,
|
||||
APIGroup: rbacv1.GroupName,
|
||||
Name: user,
|
||||
})
|
||||
}
|
||||
for _, group := range sets.NewString(s.Groups...).List() {
|
||||
roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{
|
||||
Kind: rbacv1.GroupKind,
|
||||
APIGroup: rbacv1.GroupName,
|
||||
Name: group,
|
||||
})
|
||||
}
|
||||
for _, sa := range sets.NewString(s.ServiceAccounts...).List() {
|
||||
tokens := strings.Split(sa, ":")
|
||||
if len(tokens) != 2 || tokens[1] == "" {
|
||||
return nil, fmt.Errorf("serviceaccount must be <namespace>:<name>")
|
||||
}
|
||||
roleBinding.Subjects = append(roleBinding.Subjects, rbacv1.Subject{
|
||||
Kind: rbacv1.ServiceAccountKind,
|
||||
APIGroup: "",
|
||||
Namespace: tokens[0],
|
||||
Name: tokens[1],
|
||||
})
|
||||
}
|
||||
|
||||
return roleBinding, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation.
|
||||
func (s RoleBindingGeneratorV1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if (len(s.ClusterRole) == 0) == (len(s.Role) == 0) {
|
||||
return fmt.Errorf("exactly one of clusterrole or role must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
144
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/rolebinding_test.go
generated
vendored
Normal file
144
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/rolebinding_test.go
generated
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestRoleBindingGenerate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params map[string]interface{}
|
||||
expectErrMsg string
|
||||
expectBinding *rbac.RoleBinding
|
||||
}{
|
||||
{
|
||||
name: "test-missing-name",
|
||||
params: map[string]interface{}{
|
||||
"role": "fake-role",
|
||||
"groups": []string{"fake-group"},
|
||||
"serviceaccount": []string{"fake-namespace:fake-account"},
|
||||
},
|
||||
expectErrMsg: "Parameter: name is required",
|
||||
},
|
||||
{
|
||||
name: "test-missing-role-and-clusterrole",
|
||||
params: map[string]interface{}{
|
||||
"name": "fake-binding",
|
||||
"group": []string{"fake-group"},
|
||||
"serviceaccount": []string{"fake-namespace:fake-account"},
|
||||
},
|
||||
expectErrMsg: "exactly one of clusterrole or role must be specified",
|
||||
},
|
||||
{
|
||||
name: "test-both-role-and-clusterrole-provided",
|
||||
params: map[string]interface{}{
|
||||
"name": "fake-binding",
|
||||
"role": "fake-role",
|
||||
"clusterrole": "fake-clusterrole",
|
||||
"group": []string{"fake-group"},
|
||||
"serviceaccount": []string{"fake-namespace:fake-account"},
|
||||
},
|
||||
expectErrMsg: "exactly one of clusterrole or role must be specified",
|
||||
},
|
||||
{
|
||||
name: "test-invalid-parameter-type",
|
||||
params: map[string]interface{}{
|
||||
"name": "fake-binding",
|
||||
"role": []string{"fake-role"},
|
||||
"group": []string{"fake-group"},
|
||||
"serviceaccount": []string{"fake-namespace:fake-account"},
|
||||
},
|
||||
expectErrMsg: "expected string, saw [fake-role] for 'role'",
|
||||
},
|
||||
{
|
||||
name: "test-invalid-serviceaccount",
|
||||
params: map[string]interface{}{
|
||||
"name": "fake-binding",
|
||||
"role": "fake-role",
|
||||
"group": []string{"fake-group"},
|
||||
"serviceaccount": []string{"fake-account"},
|
||||
},
|
||||
expectErrMsg: "serviceaccount must be <namespace>:<name>",
|
||||
},
|
||||
{
|
||||
name: "test-valid-case",
|
||||
params: map[string]interface{}{
|
||||
"name": "fake-binding",
|
||||
"role": "fake-role",
|
||||
"user": []string{"fake-user"},
|
||||
"group": []string{"fake-group"},
|
||||
"serviceaccount": []string{"fake-namespace:fake-account"},
|
||||
},
|
||||
expectBinding: &rbac.RoleBinding{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "fake-binding",
|
||||
},
|
||||
RoleRef: rbac.RoleRef{
|
||||
APIGroup: rbac.GroupName,
|
||||
Kind: "Role",
|
||||
Name: "fake-role",
|
||||
},
|
||||
Subjects: []rbac.Subject{
|
||||
{
|
||||
Kind: rbac.UserKind,
|
||||
APIGroup: "rbac.authorization.k8s.io",
|
||||
Name: "fake-user",
|
||||
},
|
||||
{
|
||||
Kind: rbac.GroupKind,
|
||||
APIGroup: "rbac.authorization.k8s.io",
|
||||
Name: "fake-group",
|
||||
},
|
||||
{
|
||||
Kind: rbac.ServiceAccountKind,
|
||||
Namespace: "fake-namespace",
|
||||
Name: "fake-account",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
generator := RoleBindingGeneratorV1{}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
obj, err := generator.Generate(tt.params)
|
||||
switch {
|
||||
case tt.expectErrMsg != "" && err != nil:
|
||||
if err.Error() != tt.expectErrMsg {
|
||||
t.Errorf("test '%s': expect error '%s', but saw '%s'", tt.name, tt.expectErrMsg, err.Error())
|
||||
}
|
||||
return
|
||||
case tt.expectErrMsg != "" && err == nil:
|
||||
t.Errorf("test '%s': expected error '%s' and didn't get one", tt.name, tt.expectErrMsg)
|
||||
return
|
||||
case tt.expectErrMsg == "" && err != nil:
|
||||
t.Errorf("test '%s': unexpected error %s", tt.name, err.Error())
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*rbac.RoleBinding), tt.expectBinding) {
|
||||
t.Errorf("test '%s': expected:\n%#v\nsaw:\n%#v", tt.name, tt.expectBinding, obj.(*rbac.RoleBinding))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
1022
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/run.go
generated
vendored
Normal file
1022
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/run.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1249
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/run_test.go
generated
vendored
Normal file
1249
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/run_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
272
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/secret.go
generated
vendored
Normal file
272
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/secret.go
generated
vendored
Normal file
@ -0,0 +1,272 @@
|
||||
/*
|
||||
Copyright 2015 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"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/kubernetes/pkg/kubectl/generate"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/hash"
|
||||
)
|
||||
|
||||
// SecretGeneratorV1 supports stable generation of an opaque secret
|
||||
type SecretGeneratorV1 struct {
|
||||
// Name of secret (required)
|
||||
Name string
|
||||
// Type of secret (optional)
|
||||
Type string
|
||||
// FileSources to derive the secret from (optional)
|
||||
FileSources []string
|
||||
// LiteralSources to derive the secret from (optional)
|
||||
LiteralSources []string
|
||||
// EnvFileSource to derive the secret from (optional)
|
||||
EnvFileSource string
|
||||
// AppendHash; if true, derive a hash from the Secret data and type and append it to the name
|
||||
AppendHash bool
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter injection
|
||||
var _ generate.Generator = &SecretGeneratorV1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ generate.StructuredGenerator = &SecretGeneratorV1{}
|
||||
|
||||
// Generate returns a secret using the specified parameters
|
||||
func (s SecretGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := generate.ValidateParams(s.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &SecretGeneratorV1{}
|
||||
fromFileStrings, found := genericParams["from-file"]
|
||||
if found {
|
||||
fromFileArray, isArray := fromFileStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", fromFileStrings)
|
||||
}
|
||||
delegate.FileSources = fromFileArray
|
||||
delete(genericParams, "from-file")
|
||||
}
|
||||
fromLiteralStrings, found := genericParams["from-literal"]
|
||||
if found {
|
||||
fromLiteralArray, isArray := fromLiteralStrings.([]string)
|
||||
if !isArray {
|
||||
return nil, fmt.Errorf("expected []string, found :%v", fromLiteralStrings)
|
||||
}
|
||||
delegate.LiteralSources = fromLiteralArray
|
||||
delete(genericParams, "from-literal")
|
||||
}
|
||||
fromEnvFileString, found := genericParams["from-env-file"]
|
||||
if found {
|
||||
fromEnvFile, isString := fromEnvFileString.(string)
|
||||
if !isString {
|
||||
return nil, fmt.Errorf("expected string, found :%v", fromEnvFileString)
|
||||
}
|
||||
delegate.EnvFileSource = fromEnvFile
|
||||
delete(genericParams, "from-env-file")
|
||||
}
|
||||
|
||||
hashParam, found := genericParams["append-hash"]
|
||||
if found {
|
||||
hashBool, isBool := hashParam.(bool)
|
||||
if !isBool {
|
||||
return nil, fmt.Errorf("expected bool, found :%v", hashParam)
|
||||
}
|
||||
delegate.AppendHash = hashBool
|
||||
delete(genericParams, "append-hash")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
delegate.Name = params["name"]
|
||||
delegate.Type = params["type"]
|
||||
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern
|
||||
func (s SecretGeneratorV1) ParamNames() []generate.GeneratorParam {
|
||||
return []generate.GeneratorParam{
|
||||
{Name: "name", Required: true},
|
||||
{Name: "type", Required: false},
|
||||
{Name: "from-file", Required: false},
|
||||
{Name: "from-literal", Required: false},
|
||||
{Name: "from-env-file", Required: false},
|
||||
{Name: "force", Required: false},
|
||||
{Name: "append-hash", Required: false},
|
||||
}
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a secret object using the configured fields
|
||||
func (s SecretGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret := &v1.Secret{}
|
||||
secret.SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("Secret"))
|
||||
secret.Name = s.Name
|
||||
secret.Data = map[string][]byte{}
|
||||
if len(s.Type) > 0 {
|
||||
secret.Type = v1.SecretType(s.Type)
|
||||
}
|
||||
if len(s.FileSources) > 0 {
|
||||
if err := handleFromFileSources(secret, s.FileSources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(s.LiteralSources) > 0 {
|
||||
if err := handleFromLiteralSources(secret, s.LiteralSources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(s.EnvFileSource) > 0 {
|
||||
if err := handleFromEnvFileSource(secret, s.EnvFileSource); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if s.AppendHash {
|
||||
h, err := hash.SecretHash(secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret.Name = fmt.Sprintf("%s-%s", secret.Name, h)
|
||||
}
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation
|
||||
func (s SecretGeneratorV1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if len(s.EnvFileSource) > 0 && (len(s.FileSources) > 0 || len(s.LiteralSources) > 0) {
|
||||
return fmt.Errorf("from-env-file cannot be combined with from-file or from-literal")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleFromLiteralSources adds the specified literal source information into the provided secret
|
||||
func handleFromLiteralSources(secret *v1.Secret, literalSources []string) error {
|
||||
for _, literalSource := range literalSources {
|
||||
keyName, value, err := util.ParseLiteralSource(literalSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = addKeyFromLiteralToSecret(secret, keyName, []byte(value)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleFromFileSources adds the specified file source information into the provided secret
|
||||
func handleFromFileSources(secret *v1.Secret, fileSources []string) error {
|
||||
for _, fileSource := range fileSources {
|
||||
keyName, filePath, err := util.ParseFileSource(fileSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *os.PathError:
|
||||
return fmt.Errorf("error reading %s: %v", filePath, err.Err)
|
||||
default:
|
||||
return fmt.Errorf("error reading %s: %v", filePath, err)
|
||||
}
|
||||
}
|
||||
if info.IsDir() {
|
||||
if strings.Contains(fileSource, "=") {
|
||||
return fmt.Errorf("cannot give a key name for a directory path")
|
||||
}
|
||||
fileList, err := ioutil.ReadDir(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing files in %s: %v", filePath, err)
|
||||
}
|
||||
for _, item := range fileList {
|
||||
itemPath := path.Join(filePath, item.Name())
|
||||
if item.Mode().IsRegular() {
|
||||
keyName = item.Name()
|
||||
if err = addKeyFromFileToSecret(secret, keyName, itemPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := addKeyFromFileToSecret(secret, keyName, filePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleFromEnvFileSource adds the specified env file source information
|
||||
// into the provided secret
|
||||
func handleFromEnvFileSource(secret *v1.Secret, envFileSource string) error {
|
||||
info, err := os.Stat(envFileSource)
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *os.PathError:
|
||||
return fmt.Errorf("error reading %s: %v", envFileSource, err.Err)
|
||||
default:
|
||||
return fmt.Errorf("error reading %s: %v", envFileSource, err)
|
||||
}
|
||||
}
|
||||
if info.IsDir() {
|
||||
return fmt.Errorf("env secret file cannot be a directory")
|
||||
}
|
||||
|
||||
return addFromEnvFile(envFileSource, func(key, value string) error {
|
||||
return addKeyFromLiteralToSecret(secret, key, []byte(value))
|
||||
})
|
||||
}
|
||||
|
||||
func addKeyFromFileToSecret(secret *v1.Secret, keyName, filePath string) error {
|
||||
data, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return addKeyFromLiteralToSecret(secret, keyName, data)
|
||||
}
|
||||
|
||||
func addKeyFromLiteralToSecret(secret *v1.Secret, keyName string, data []byte) error {
|
||||
if errs := validation.IsConfigMapKey(keyName); len(errs) != 0 {
|
||||
return fmt.Errorf("%q is not a valid key name for a Secret: %s", keyName, strings.Join(errs, ";"))
|
||||
}
|
||||
|
||||
if _, entryExists := secret.Data[keyName]; entryExists {
|
||||
return fmt.Errorf("cannot add key %s, another key by that name already exists: %v", keyName, secret.Data)
|
||||
}
|
||||
secret.Data[keyName] = data
|
||||
return nil
|
||||
}
|
181
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/secret_for_docker_registry.go
generated
vendored
Normal file
181
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/secret_for_docker_registry.go
generated
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
Copyright 2015 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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubectl/generate"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/hash"
|
||||
)
|
||||
|
||||
// SecretForDockerRegistryGeneratorV1 supports stable generation of a docker registry secret
|
||||
type SecretForDockerRegistryGeneratorV1 struct {
|
||||
// Name of secret (required)
|
||||
Name string
|
||||
// FileSources to derive the secret from (optional)
|
||||
FileSources []string
|
||||
// Username for registry (required)
|
||||
Username string
|
||||
// Email for registry (optional)
|
||||
Email string
|
||||
// Password for registry (required)
|
||||
Password string
|
||||
// Server for registry (required)
|
||||
Server string
|
||||
// AppendHash; if true, derive a hash from the Secret and append it to the name
|
||||
AppendHash bool
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter injection
|
||||
var _ generate.Generator = &SecretForDockerRegistryGeneratorV1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ generate.StructuredGenerator = &SecretForDockerRegistryGeneratorV1{}
|
||||
|
||||
// Generate returns a secret using the specified parameters
|
||||
func (s SecretForDockerRegistryGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := generate.ValidateParams(s.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &SecretForDockerRegistryGeneratorV1{}
|
||||
hashParam, found := genericParams["append-hash"]
|
||||
if found {
|
||||
hashBool, isBool := hashParam.(bool)
|
||||
if !isBool {
|
||||
return nil, fmt.Errorf("expected bool, found :%v", hashParam)
|
||||
}
|
||||
delegate.AppendHash = hashBool
|
||||
delete(genericParams, "append-hash")
|
||||
}
|
||||
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
|
||||
}
|
||||
delegate.Name = params["name"]
|
||||
delegate.Username = params["docker-username"]
|
||||
delegate.Email = params["docker-email"]
|
||||
delegate.Password = params["docker-password"]
|
||||
delegate.Server = params["docker-server"]
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a secret object using the configured fields
|
||||
func (s SecretForDockerRegistryGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret := &v1.Secret{}
|
||||
secret.Name = s.Name
|
||||
secret.Type = v1.SecretTypeDockerConfigJson
|
||||
secret.Data = map[string][]byte{}
|
||||
if len(s.FileSources) > 0 {
|
||||
if err := handleFromFileSources(secret, s.FileSources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(s.FileSources) == 0 {
|
||||
dockercfgJSONContent, err := handleDockerCfgJSONContent(s.Username, s.Password, s.Email, s.Server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret.Data[v1.DockerConfigJsonKey] = dockercfgJSONContent
|
||||
}
|
||||
if s.AppendHash {
|
||||
h, err := hash.SecretHash(secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret.Name = fmt.Sprintf("%s-%s", secret.Name, h)
|
||||
}
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern
|
||||
func (s SecretForDockerRegistryGeneratorV1) ParamNames() []generate.GeneratorParam {
|
||||
return []generate.GeneratorParam{
|
||||
{Name: "name", Required: true},
|
||||
{Name: "from-file", Required: false},
|
||||
{Name: "docker-username", Required: true},
|
||||
{Name: "docker-email", Required: false},
|
||||
{Name: "docker-password", Required: true},
|
||||
{Name: "docker-server", Required: true},
|
||||
{Name: "append-hash", Required: false},
|
||||
}
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation
|
||||
func (s SecretForDockerRegistryGeneratorV1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
|
||||
if len(s.FileSources) == 0 {
|
||||
if len(s.Username) == 0 {
|
||||
return fmt.Errorf("username must be specified")
|
||||
}
|
||||
if len(s.Password) == 0 {
|
||||
return fmt.Errorf("password must be specified")
|
||||
}
|
||||
if len(s.Server) == 0 {
|
||||
return fmt.Errorf("server must be specified")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleDockerCfgJSONContent serializes a ~/.docker/config.json file
|
||||
func handleDockerCfgJSONContent(username, password, email, server string) ([]byte, error) {
|
||||
dockercfgAuth := DockerConfigEntry{
|
||||
Username: username,
|
||||
Password: password,
|
||||
Email: email,
|
||||
}
|
||||
|
||||
dockerCfgJSON := DockerConfigJSON{
|
||||
Auths: map[string]DockerConfigEntry{server: dockercfgAuth},
|
||||
}
|
||||
|
||||
return json.Marshal(dockerCfgJSON)
|
||||
}
|
||||
|
||||
// DockerConfigJSON represents a local docker auth config file
|
||||
// for pulling images.
|
||||
type DockerConfigJSON struct {
|
||||
Auths DockerConfig `json:"auths"`
|
||||
// +optional
|
||||
HttpHeaders map[string]string `json:"HttpHeaders,omitempty"`
|
||||
}
|
||||
|
||||
// DockerConfig represents the config file used by the docker CLI.
|
||||
// This config that represents the credentials that should be used
|
||||
// when pulling images from specific image repositories.
|
||||
type DockerConfig map[string]DockerConfigEntry
|
||||
|
||||
type DockerConfigEntry struct {
|
||||
Username string
|
||||
Password string
|
||||
Email string
|
||||
}
|
131
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/secret_for_docker_registry_test.go
generated
vendored
Normal file
131
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/secret_for_docker_registry_test.go
generated
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
Copyright 2015 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"
|
||||
)
|
||||
|
||||
func TestSecretForDockerRegistryGenerate(t *testing.T) {
|
||||
username, password, email, server := "test-user", "test-password", "test-user@example.org", "https://index.docker.io/v1/"
|
||||
secretData, err := handleDockerCfgJSONContent(username, password, email, server)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
secretDataNoEmail, err := handleDockerCfgJSONContent(username, password, "", server)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
params map[string]interface{}
|
||||
expected *v1.Secret
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "test-valid-use",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"docker-server": server,
|
||||
"docker-username": username,
|
||||
"docker-password": password,
|
||||
"docker-email": email,
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
v1.DockerConfigJsonKey: secretData,
|
||||
},
|
||||
Type: v1.SecretTypeDockerConfigJson,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test-valid-use-append-hash",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"docker-server": server,
|
||||
"docker-username": username,
|
||||
"docker-password": password,
|
||||
"docker-email": email,
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-7566tc6mgc",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
v1.DockerConfigJsonKey: secretData,
|
||||
},
|
||||
Type: v1.SecretTypeDockerConfigJson,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test-valid-use-no-email",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"docker-server": server,
|
||||
"docker-username": username,
|
||||
"docker-password": password,
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
v1.DockerConfigJsonKey: secretDataNoEmail,
|
||||
},
|
||||
Type: v1.SecretTypeDockerConfigJson,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test-missing-required-param",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"docker-server": server,
|
||||
"docker-password": password,
|
||||
"docker-email": email,
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
generator := SecretForDockerRegistryGeneratorV1{}
|
||||
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.Secret), tt.expected) {
|
||||
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", tt.expected, obj.(*v1.Secret))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
146
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/secret_for_tls.go
generated
vendored
Normal file
146
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/secret_for_tls.go
generated
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
Copyright 2015 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 (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubectl/generate"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/hash"
|
||||
)
|
||||
|
||||
// SecretForTLSGeneratorV1 supports stable generation of a TLS secret.
|
||||
type SecretForTLSGeneratorV1 struct {
|
||||
// Name is the name of this TLS secret.
|
||||
Name string
|
||||
// Key is the path to the user's private key.
|
||||
Key string
|
||||
// Cert is the path to the user's public key certificate.
|
||||
Cert string
|
||||
// AppendHash; if true, derive a hash from the Secret and append it to the name
|
||||
AppendHash bool
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameter injection
|
||||
var _ generate.Generator = &SecretForTLSGeneratorV1{}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ generate.StructuredGenerator = &SecretForTLSGeneratorV1{}
|
||||
|
||||
// Generate returns a secret using the specified parameters
|
||||
func (s SecretForTLSGeneratorV1) Generate(genericParams map[string]interface{}) (runtime.Object, error) {
|
||||
err := generate.ValidateParams(s.ParamNames(), genericParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &SecretForTLSGeneratorV1{}
|
||||
hashParam, found := genericParams["append-hash"]
|
||||
if found {
|
||||
hashBool, isBool := hashParam.(bool)
|
||||
if !isBool {
|
||||
return nil, fmt.Errorf("expected bool, found :%v", hashParam)
|
||||
}
|
||||
delegate.AppendHash = hashBool
|
||||
delete(genericParams, "append-hash")
|
||||
}
|
||||
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
|
||||
}
|
||||
delegate.Name = params["name"]
|
||||
delegate.Key = params["key"]
|
||||
delegate.Cert = params["cert"]
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// StructuredGenerate outputs a secret object using the configured fields
|
||||
func (s SecretForTLSGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := s.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsCrt, err := readFile(s.Cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsKey, err := readFile(s.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := tls.X509KeyPair(tlsCrt, tlsKey); err != nil {
|
||||
return nil, fmt.Errorf("failed to load key pair %v", err)
|
||||
}
|
||||
// TODO: Add more validation.
|
||||
// 1. If the certificate contains intermediates, it is a valid chain.
|
||||
// 2. Format etc.
|
||||
|
||||
secret := &v1.Secret{}
|
||||
secret.Name = s.Name
|
||||
secret.Type = v1.SecretTypeTLS
|
||||
secret.Data = map[string][]byte{}
|
||||
secret.Data[v1.TLSCertKey] = []byte(tlsCrt)
|
||||
secret.Data[v1.TLSPrivateKeyKey] = []byte(tlsKey)
|
||||
if s.AppendHash {
|
||||
h, err := hash.SecretHash(secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret.Name = fmt.Sprintf("%s-%s", secret.Name, h)
|
||||
}
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
// readFile just reads a file into a byte array.
|
||||
func readFile(file string) ([]byte, error) {
|
||||
b, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return []byte{}, fmt.Errorf("Cannot read file %v, %v", file, err)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// ParamNames returns the set of supported input parameters when using the parameter injection generator pattern
|
||||
func (s SecretForTLSGeneratorV1) ParamNames() []generate.GeneratorParam {
|
||||
return []generate.GeneratorParam{
|
||||
{Name: "name", Required: true},
|
||||
{Name: "key", Required: true},
|
||||
{Name: "cert", Required: true},
|
||||
{Name: "append-hash", Required: false},
|
||||
}
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation
|
||||
func (s SecretForTLSGeneratorV1) validate() error {
|
||||
// TODO: This is not strictly necessary. We can generate a self signed cert
|
||||
// if no key/cert is given. The only requirement is that we either get both
|
||||
// or none. See test/e2e/ingress_utils for self signed cert generation.
|
||||
if len(s.Key) == 0 {
|
||||
return fmt.Errorf("key must be specified")
|
||||
}
|
||||
if len(s.Cert) == 0 {
|
||||
return fmt.Errorf("certificate must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
233
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/secret_for_tls_test.go
generated
vendored
Normal file
233
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/secret_for_tls_test.go
generated
vendored
Normal file
@ -0,0 +1,233 @@
|
||||
/*
|
||||
Copyright 2015 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"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
)
|
||||
|
||||
var rsaCertPEM = `-----BEGIN CERTIFICATE-----
|
||||
MIIB0zCCAX2gAwIBAgIJAI/M7BYjwB+uMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
|
||||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||
aWRnaXRzIFB0eSBMdGQwHhcNMTIwOTEyMjE1MjAyWhcNMTUwOTEyMjE1MjAyWjBF
|
||||
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
|
||||
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANLJ
|
||||
hPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wok/4xIA+ui35/MmNa
|
||||
rtNuC+BdZ1tMuVCPFZcCAwEAAaNQME4wHQYDVR0OBBYEFJvKs8RfJaXTH08W+SGv
|
||||
zQyKn0H8MB8GA1UdIwQYMBaAFJvKs8RfJaXTH08W+SGvzQyKn0H8MAwGA1UdEwQF
|
||||
MAMBAf8wDQYJKoZIhvcNAQEFBQADQQBJlffJHybjDGxRMqaRmDhX0+6v02TUKZsW
|
||||
r5QuVbpQhH6u+0UgcW0jp9QwpxoPTLTWGXEWBBBurxFwiCBhkQ+V
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
var rsaKeyPEM = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo
|
||||
k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G
|
||||
6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N
|
||||
MQIhAPW+eyZo7ay3lMz1V01WVjNKK9QSn1MJlb06h/LuYv9FAiEA25WPedKgVyCW
|
||||
SmUwbPw8fnTcpqDWE3yTO3vKcebqMSsCIBF3UmVue8YU3jybC3NxuXq3wNm34R8T
|
||||
xVLHwDXh/6NJAiEAl2oHGGLz64BuAfjKrqwz7qMYr9HCLIe/YsoWq/olzScCIQDi
|
||||
D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`
|
||||
|
||||
const mismatchRSAKeyPEM = `-----BEGIN PRIVATE KEY-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC/665h55hWD4V2
|
||||
kiQ+B/G9NNfBw69eBibEhI9vWkPUyn36GO2r3HPtRE63wBfFpV486ns9DoZnnAYE
|
||||
JaGjVNCCqS5tQyMBWp843o66KBrEgBpuddChigvyul33FhD1ImFnN+Vy0ajOJ+1/
|
||||
Zai28zBXWbxCWEbqz7s8e2UsPlBd0Caj4gcd32yD2BwiHqzB8odToWRUT7l+pS8R
|
||||
qA1BruQvtjEIrcoWVlE170ZYe7+Apm96A+WvtVRkozPynxHF8SuEiw4hAh0lXR6b
|
||||
4zZz4tZVV8ev2HpffveV/68GiCyeFDbglqd4sZ/Iga/rwu7bVY/BzFApHwu2hmmV
|
||||
XLnaa3uVAgMBAAECggEAG+kvnCdtPR7Wvw6z3J2VJ3oW4qQNzfPBEZVhssUC1mB4
|
||||
f7W+Yt8VsOzdMdXq3yCUmvFS6OdC3rCPI21Bm5pLFKV8DgHUhm7idwfO4/3PHsKu
|
||||
lV/m7odAA5Xc8oEwCCZu2e8EHHWnQgwGex+SsMCfSCTRvyhNb/qz9TDQ3uVVFL9e
|
||||
9a4OKqZl/GlRspJSuXhy+RSVulw9NjeX1VRjIbhqpdXAmQNXgShA+gZSQh8T/tgv
|
||||
XQYsMtg+FUDvcunJQf4OW5BY7IenYBV/GvsnJU8L7oD0wjNSAwe/iLKqV/NpYhre
|
||||
QR4DsGnmoRYlUlHdHFTTJpReDjWm+vH3T756yDdFAQKBgQD2/sP5dM/aEW7Z1TgS
|
||||
TG4ts1t8Rhe9escHxKZQR81dfOxBeCJMBDm6ySfR8rvyUM4VsogxBL/RhRQXsjJM
|
||||
7wN08MhdiXG0J5yy/oNo8W6euD8m8Mk1UmqcZjSgV4vA7zQkvkr6DRJdybKsT9mE
|
||||
jouEwev8sceS6iBpPw/+Ws8z1QKBgQDG6uYHMfMcS844xKQQWhargdN2XBzeG6TV
|
||||
YXfNFstNpD84d9zIbpG/AKJF8fKrseUhXkJhkDjFGJTriD3QQsntOFaDOrHMnveV
|
||||
zGzvC4OTFUUFHe0SVJ0HuLf8YCHoZ+DXEeCKCN6zBXnUue+bt3NvLOf2yN5o9kYx
|
||||
SIa8O1vIwQKBgEdONXWG65qg/ceVbqKZvhUjen3eHmxtTZhIhVsX34nlzq73567a
|
||||
aXArMnvB/9Bs05IgAIFmRZpPOQW+RBdByVWxTabzTwgbh3mFUJqzWKQpvNGZIf1q
|
||||
1axhNUA1BfulEwCojyyxKWQ6HoLwanOCU3T4JxDEokEfpku8EPn1bWwhAoGAAN8A
|
||||
eOGYHfSbB5ac3VF3rfKYmXkXy0U1uJV/r888vq9Mc5PazKnnS33WOBYyKNxTk4zV
|
||||
H5ZBGWPdKxbipmnUdox7nIGCS9IaZXaKt5VGUzuRnM8fvafPNDxz2dAV9e2Wh3qV
|
||||
kCUvzHrmqK7TxMvN3pvEvEju6GjDr+2QYXylD0ECgYAGK5r+y+EhtKkYFLeYReUt
|
||||
znvSsWq+JCQH/cmtZLaVOldCaMRL625hSl3XPPcMIHE14xi3d4njoXWzvzPcg8L6
|
||||
vNXk3GiNldACS+vwk4CwEqe5YlZRm5doD07wIdsg2zRlnKsnXNM152OwgmcchDul
|
||||
rLTt0TTazzwBCgCD0Jkoqg==
|
||||
-----END PRIVATE KEY-----`
|
||||
|
||||
func tearDown(tmpDir string) {
|
||||
err := os.RemoveAll(tmpDir)
|
||||
if err != nil {
|
||||
fmt.Printf("Error in cleaning up test: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func write(path, contents string, t *testing.T) {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create %v.", path)
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = f.WriteString(contents)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write to %v.", path)
|
||||
}
|
||||
}
|
||||
|
||||
func writeKeyPair(tmpDirPath, key, cert string, t *testing.T) (keyPath, certPath string) {
|
||||
keyPath = path.Join(tmpDirPath, "tls.key")
|
||||
certPath = path.Join(tmpDirPath, "tls.cert")
|
||||
write(keyPath, key, t)
|
||||
write(certPath, cert, t)
|
||||
return
|
||||
}
|
||||
|
||||
func TestSecretForTLSGenerate(t *testing.T) {
|
||||
invalidCertTmpDir := utiltesting.MkTmpdirOrDie("tls-test")
|
||||
defer tearDown(invalidCertTmpDir)
|
||||
invalidKeyPath, invalidCertPath := writeKeyPair(invalidCertTmpDir, "test", "test", t)
|
||||
|
||||
validCertTmpDir := utiltesting.MkTmpdirOrDie("tls-test")
|
||||
defer tearDown(validCertTmpDir)
|
||||
validKeyPath, validCertPath := writeKeyPair(validCertTmpDir, rsaKeyPEM, rsaCertPEM, t)
|
||||
|
||||
mismatchCertTmpDir := utiltesting.MkTmpdirOrDie("tls-mismatch-test")
|
||||
defer tearDown(mismatchCertTmpDir)
|
||||
mismatchKeyPath, mismatchCertPath := writeKeyPair(mismatchCertTmpDir, mismatchRSAKeyPEM, rsaCertPEM, t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
params map[string]interface{}
|
||||
expected *v1.Secret
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "test-valid-tls-secret",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"key": validKeyPath,
|
||||
"cert": validCertPath,
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
v1.TLSCertKey: []byte(rsaCertPEM),
|
||||
v1.TLSPrivateKeyKey: []byte(rsaKeyPEM),
|
||||
},
|
||||
Type: v1.SecretTypeTLS,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test-valid-tls-secret-append-hash",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"key": validKeyPath,
|
||||
"cert": validCertPath,
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-272h6tt825",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
v1.TLSCertKey: []byte(rsaCertPEM),
|
||||
v1.TLSPrivateKeyKey: []byte(rsaKeyPEM),
|
||||
},
|
||||
Type: v1.SecretTypeTLS,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test-invalid-key-pair",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"key": invalidKeyPath,
|
||||
"cert": invalidCertPath,
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
v1.TLSCertKey: []byte("test"),
|
||||
v1.TLSPrivateKeyKey: []byte("test"),
|
||||
},
|
||||
Type: v1.SecretTypeTLS,
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test-mismatched-key-pair",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"key": mismatchKeyPath,
|
||||
"cert": mismatchCertPath,
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
v1.TLSCertKey: []byte(rsaCertPEM),
|
||||
v1.TLSPrivateKeyKey: []byte(mismatchRSAKeyPEM),
|
||||
},
|
||||
Type: v1.SecretTypeTLS,
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test-missing-required-param",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"key": "/tmp/foo.key",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
generator := SecretForTLSGeneratorV1{}
|
||||
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.Secret), tt.expected) {
|
||||
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", tt.expected, obj.(*v1.Secret))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
371
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/secret_test.go
generated
vendored
Normal file
371
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/secret_test.go
generated
vendored
Normal file
@ -0,0 +1,371 @@
|
||||
/*
|
||||
Copyright 2015 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 (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestSecretGenerate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setup func(t *testing.T, params map[string]interface{}) func()
|
||||
params map[string]interface{}
|
||||
expected *v1.Secret
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "test1",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
// this is ok because we know exactly how we want to be serialized
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.String(), Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test2",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
// this is ok because we know exactly how we want to be serialized
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.String(), Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-949tdgdkgg",
|
||||
},
|
||||
Data: map[string][]byte{},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test3",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"type": "my-type",
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
// this is ok because we know exactly how we want to be serialized
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.String(), Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: map[string][]byte{},
|
||||
Type: "my-type",
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test4",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"type": "my-type",
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
// this is ok because we know exactly how we want to be serialized
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.String(), Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-dg474f9t76",
|
||||
},
|
||||
Data: map[string][]byte{},
|
||||
Type: "my-type",
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test5",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-literal": []string{"key1=value1", "key2=value2"},
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
// this is ok because we know exactly how we want to be serialized
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.String(), Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"key1": []byte("value1"),
|
||||
"key2": []byte("value2"),
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test6",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-literal": []string{"key1=value1", "key2=value2"},
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
// this is ok because we know exactly how we want to be serialized
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.String(), Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-tf72c228m4",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"key1": []byte("value1"),
|
||||
"key2": []byte("value2"),
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test7",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-literal": []string{"key1value1"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test8",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-file": []string{"key1=/file=2"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test9",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-file": []string{"key1==value"},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test10",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-literal": []string{"key1==value1"},
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
// this is ok because we know exactly how we want to be serialized
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.String(), Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"key1": []byte("=value1"),
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test11",
|
||||
params: map[string]interface{}{
|
||||
"name": "foo",
|
||||
"from-literal": []string{"key1==value1"},
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
// this is ok because we know exactly how we want to be serialized
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.String(), Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-fdcc8tkhh5",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"key1": []byte("=value1"),
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test12",
|
||||
setup: setupEnvFile("key1=value1", "#", "", "key2=value2"),
|
||||
params: map[string]interface{}{
|
||||
"name": "valid_env",
|
||||
"from-env-file": "file.env",
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
// this is ok because we know exactly how we want to be serialized
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.String(), Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "valid_env",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"key1": []byte("value1"),
|
||||
"key2": []byte("value2"),
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test13",
|
||||
setup: setupEnvFile("key1=value1", "#", "", "key2=value2"),
|
||||
params: map[string]interface{}{
|
||||
"name": "valid_env",
|
||||
"from-env-file": "file.env",
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
// this is ok because we know exactly how we want to be serialized
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.String(), Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "valid_env-bkb2m2965h",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"key1": []byte("value1"),
|
||||
"key2": []byte("value2"),
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test14",
|
||||
setup: func() func(t *testing.T, params map[string]interface{}) func() {
|
||||
os.Setenv("g_key1", "1")
|
||||
os.Setenv("g_key2", "2")
|
||||
return setupEnvFile("g_key1", "g_key2=")
|
||||
}(),
|
||||
params: map[string]interface{}{
|
||||
"name": "getenv",
|
||||
"from-env-file": "file.env",
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
// this is ok because we know exactly how we want to be serialized
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.String(), Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "getenv",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"g_key1": []byte("1"),
|
||||
"g_key2": []byte(""),
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test15",
|
||||
setup: func() func(t *testing.T, params map[string]interface{}) func() {
|
||||
os.Setenv("g_key1", "1")
|
||||
os.Setenv("g_key2", "2")
|
||||
return setupEnvFile("g_key1", "g_key2=")
|
||||
}(),
|
||||
params: map[string]interface{}{
|
||||
"name": "getenv",
|
||||
"from-env-file": "file.env",
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
// this is ok because we know exactly how we want to be serialized
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.String(), Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "getenv-m7kg2khdb4",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"g_key1": []byte("1"),
|
||||
"g_key2": []byte(""),
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test16",
|
||||
params: map[string]interface{}{
|
||||
"name": "too_many_args",
|
||||
"from-literal": []string{"key1=value1"},
|
||||
"from-env-file": "file.env",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test17",
|
||||
setup: setupEnvFile("key#1=value1"),
|
||||
params: map[string]interface{}{
|
||||
"name": "invalid_key",
|
||||
"from-env-file": "file.env",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "test18",
|
||||
setup: setupEnvFile(" key1= value1"),
|
||||
params: map[string]interface{}{
|
||||
"name": "with_spaces",
|
||||
"from-env-file": "file.env",
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
// this is ok because we know exactly how we want to be serialized
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.String(), Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "with_spaces",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"key1": []byte(" value1"),
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "test19",
|
||||
setup: setupEnvFile(" key1= value1"),
|
||||
params: map[string]interface{}{
|
||||
"name": "with_spaces",
|
||||
"from-env-file": "file.env",
|
||||
"append-hash": true,
|
||||
},
|
||||
expected: &v1.Secret{
|
||||
// this is ok because we know exactly how we want to be serialized
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: v1.SchemeGroupVersion.String(), Kind: "Secret"},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "with_spaces-4488d5b57d",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"key1": []byte(" value1"),
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
generator := SecretGeneratorV1{}
|
||||
for i, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.setup != nil {
|
||||
if teardown := tt.setup(t, tt.params); teardown != nil {
|
||||
defer teardown()
|
||||
}
|
||||
}
|
||||
obj, err := generator.Generate(tt.params)
|
||||
if !tt.expectErr && err != nil {
|
||||
t.Errorf("case %d, unexpected error: %v", i, err)
|
||||
return
|
||||
}
|
||||
if tt.expectErr && err != nil {
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*v1.Secret), tt.expected) {
|
||||
t.Errorf("\ncase %d, expected:\n%#v\nsaw:\n%#v", i, tt.expected, obj.(*v1.Secret))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
240
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/service.go
generated
vendored
Normal file
240
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/service.go
generated
vendored
Normal file
@ -0,0 +1,240 @@
|
||||
/*
|
||||
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/kubernetes/pkg/kubectl/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.FromInt(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.FromInt(int(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
|
||||
}
|
260
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/service_basic.go
generated
vendored
Normal file
260
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/service_basic.go
generated
vendored
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package 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/apimachinery/pkg/util/validation"
|
||||
"k8s.io/kubernetes/pkg/kubectl/generate"
|
||||
)
|
||||
|
||||
type ServiceCommonGeneratorV1 struct {
|
||||
Name string
|
||||
TCP []string
|
||||
Type v1.ServiceType
|
||||
ClusterIP string
|
||||
NodePort int
|
||||
ExternalName string
|
||||
}
|
||||
|
||||
type ServiceClusterIPGeneratorV1 struct {
|
||||
ServiceCommonGeneratorV1
|
||||
}
|
||||
|
||||
type ServiceNodePortGeneratorV1 struct {
|
||||
ServiceCommonGeneratorV1
|
||||
}
|
||||
|
||||
type ServiceLoadBalancerGeneratorV1 struct {
|
||||
ServiceCommonGeneratorV1
|
||||
}
|
||||
|
||||
// TODO: is this really necessary?
|
||||
type ServiceExternalNameGeneratorV1 struct {
|
||||
ServiceCommonGeneratorV1
|
||||
}
|
||||
|
||||
func (ServiceClusterIPGeneratorV1) ParamNames() []generate.GeneratorParam {
|
||||
return []generate.GeneratorParam{
|
||||
{Name: "name", Required: true},
|
||||
{Name: "tcp", Required: true},
|
||||
{Name: "clusterip", Required: false},
|
||||
}
|
||||
}
|
||||
func (ServiceNodePortGeneratorV1) ParamNames() []generate.GeneratorParam {
|
||||
return []generate.GeneratorParam{
|
||||
{Name: "name", Required: true},
|
||||
{Name: "tcp", Required: true},
|
||||
{Name: "nodeport", Required: true},
|
||||
}
|
||||
}
|
||||
func (ServiceLoadBalancerGeneratorV1) ParamNames() []generate.GeneratorParam {
|
||||
return []generate.GeneratorParam{
|
||||
{Name: "name", Required: true},
|
||||
{Name: "tcp", Required: true},
|
||||
}
|
||||
}
|
||||
|
||||
func (ServiceExternalNameGeneratorV1) ParamNames() []generate.GeneratorParam {
|
||||
return []generate.GeneratorParam{
|
||||
{Name: "name", Required: true},
|
||||
{Name: "externalname", Required: true},
|
||||
}
|
||||
}
|
||||
|
||||
func parsePorts(portString string) (int32, intstr.IntOrString, error) {
|
||||
portStringSlice := strings.Split(portString, ":")
|
||||
|
||||
port, err := strconv.Atoi(portStringSlice[0])
|
||||
if err != nil {
|
||||
return 0, intstr.FromInt(0), err
|
||||
}
|
||||
|
||||
if errs := validation.IsValidPortNum(port); len(errs) != 0 {
|
||||
return 0, intstr.FromInt(0), fmt.Errorf(strings.Join(errs, ","))
|
||||
}
|
||||
|
||||
if len(portStringSlice) == 1 {
|
||||
return int32(port), intstr.FromInt(int(port)), nil
|
||||
}
|
||||
|
||||
var targetPort intstr.IntOrString
|
||||
if portNum, err := strconv.Atoi(portStringSlice[1]); err != nil {
|
||||
if errs := validation.IsValidPortName(portStringSlice[1]); len(errs) != 0 {
|
||||
return 0, intstr.FromInt(0), fmt.Errorf(strings.Join(errs, ","))
|
||||
}
|
||||
targetPort = intstr.FromString(portStringSlice[1])
|
||||
} else {
|
||||
if errs := validation.IsValidPortNum(portNum); len(errs) != 0 {
|
||||
return 0, intstr.FromInt(0), fmt.Errorf(strings.Join(errs, ","))
|
||||
}
|
||||
targetPort = intstr.FromInt(portNum)
|
||||
}
|
||||
return int32(port), targetPort, nil
|
||||
}
|
||||
|
||||
func (s ServiceCommonGeneratorV1) GenerateCommon(params map[string]interface{}) error {
|
||||
name, isString := params["name"].(string)
|
||||
if !isString {
|
||||
return fmt.Errorf("expected string, saw %v for 'name'", name)
|
||||
}
|
||||
tcpStrings, isArray := params["tcp"].([]string)
|
||||
if !isArray {
|
||||
return fmt.Errorf("expected []string, found :%v", tcpStrings)
|
||||
}
|
||||
clusterip, isString := params["clusterip"].(string)
|
||||
if !isString {
|
||||
return fmt.Errorf("expected string, saw %v for 'clusterip'", clusterip)
|
||||
}
|
||||
externalname, isString := params["externalname"].(string)
|
||||
if !isString {
|
||||
return fmt.Errorf("expected string, saw %v for 'externalname'", externalname)
|
||||
}
|
||||
s.Name = name
|
||||
s.TCP = tcpStrings
|
||||
s.ClusterIP = clusterip
|
||||
s.ExternalName = externalname
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s ServiceLoadBalancerGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
|
||||
err := generate.ValidateParams(s.ParamNames(), params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &ServiceCommonGeneratorV1{Type: v1.ServiceTypeLoadBalancer, ClusterIP: ""}
|
||||
err = delegate.GenerateCommon(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
func (s ServiceNodePortGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
|
||||
err := generate.ValidateParams(s.ParamNames(), params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &ServiceCommonGeneratorV1{Type: v1.ServiceTypeNodePort, ClusterIP: ""}
|
||||
err = delegate.GenerateCommon(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
func (s ServiceClusterIPGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
|
||||
err := generate.ValidateParams(s.ParamNames(), params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &ServiceCommonGeneratorV1{Type: v1.ServiceTypeClusterIP, ClusterIP: ""}
|
||||
err = delegate.GenerateCommon(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
func (s ServiceExternalNameGeneratorV1) Generate(params map[string]interface{}) (runtime.Object, error) {
|
||||
err := generate.ValidateParams(s.ParamNames(), params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delegate := &ServiceCommonGeneratorV1{Type: v1.ServiceTypeExternalName, ClusterIP: ""}
|
||||
err = delegate.GenerateCommon(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return delegate.StructuredGenerate()
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation
|
||||
// TODO(xiangpengzhao): validate ports are identity mapped for headless service when we enforce that in validation.validateServicePort.
|
||||
func (s ServiceCommonGeneratorV1) validate() error {
|
||||
if len(s.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
if len(s.Type) == 0 {
|
||||
return fmt.Errorf("type must be specified")
|
||||
}
|
||||
if s.ClusterIP == v1.ClusterIPNone && s.Type != v1.ServiceTypeClusterIP {
|
||||
return fmt.Errorf("ClusterIP=None can only be used with ClusterIP service type")
|
||||
}
|
||||
if s.ClusterIP != v1.ClusterIPNone && len(s.TCP) == 0 && s.Type != v1.ServiceTypeExternalName {
|
||||
return fmt.Errorf("at least one tcp port specifier must be provided")
|
||||
}
|
||||
if s.Type == v1.ServiceTypeExternalName {
|
||||
if errs := validation.IsDNS1123Subdomain(s.ExternalName); len(errs) != 0 {
|
||||
return fmt.Errorf("invalid service external name %s", s.ExternalName)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s ServiceCommonGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
err := s.validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ports := []v1.ServicePort{}
|
||||
for _, tcpString := range s.TCP {
|
||||
port, targetPort, err := parsePorts(tcpString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
portName := strings.Replace(tcpString, ":", "-", -1)
|
||||
ports = append(ports, v1.ServicePort{
|
||||
Name: portName,
|
||||
Port: port,
|
||||
TargetPort: targetPort,
|
||||
Protocol: v1.Protocol("TCP"),
|
||||
NodePort: int32(s.NodePort),
|
||||
})
|
||||
}
|
||||
|
||||
// setup default label and selector
|
||||
labels := map[string]string{}
|
||||
labels["app"] = s.Name
|
||||
selector := map[string]string{}
|
||||
selector["app"] = s.Name
|
||||
|
||||
service := v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: s.Name,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: v1.ServiceSpec{
|
||||
Type: v1.ServiceType(s.Type),
|
||||
Selector: selector,
|
||||
Ports: ports,
|
||||
ExternalName: s.ExternalName,
|
||||
},
|
||||
}
|
||||
if len(s.ClusterIP) > 0 {
|
||||
service.Spec.ClusterIP = s.ClusterIP
|
||||
}
|
||||
return &service, nil
|
||||
}
|
321
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/service_basic_test.go
generated
vendored
Normal file
321
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/service_basic_test.go
generated
vendored
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
func TestServiceBasicGenerate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
serviceType v1.ServiceType
|
||||
tcp []string
|
||||
clusterip string
|
||||
expected *v1.Service
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "clusterip-ok",
|
||||
tcp: []string{"456", "321:908"},
|
||||
clusterip: "",
|
||||
serviceType: v1.ServiceTypeClusterIP,
|
||||
expected: &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "clusterip-ok",
|
||||
Labels: map[string]string{"app": "clusterip-ok"},
|
||||
},
|
||||
Spec: v1.ServiceSpec{Type: "ClusterIP",
|
||||
Ports: []v1.ServicePort{{Name: "456", Protocol: "TCP", Port: 456, TargetPort: intstr.IntOrString{Type: 0, IntVal: 456, StrVal: ""}, NodePort: 0},
|
||||
{Name: "321-908", Protocol: "TCP", Port: 321, TargetPort: intstr.IntOrString{Type: 0, IntVal: 908, StrVal: ""}, NodePort: 0}},
|
||||
Selector: map[string]string{"app": "clusterip-ok"},
|
||||
ClusterIP: "", ExternalIPs: []string(nil), LoadBalancerIP: ""},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "clusterip-missing",
|
||||
serviceType: v1.ServiceTypeClusterIP,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "clusterip-none-wrong-type",
|
||||
tcp: []string{},
|
||||
clusterip: "None",
|
||||
serviceType: v1.ServiceTypeNodePort,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "clusterip-none-ok",
|
||||
tcp: []string{},
|
||||
clusterip: "None",
|
||||
serviceType: v1.ServiceTypeClusterIP,
|
||||
expected: &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "clusterip-none-ok",
|
||||
Labels: map[string]string{"app": "clusterip-none-ok"},
|
||||
},
|
||||
Spec: v1.ServiceSpec{Type: "ClusterIP",
|
||||
Ports: []v1.ServicePort{},
|
||||
Selector: map[string]string{"app": "clusterip-none-ok"},
|
||||
ClusterIP: "None", ExternalIPs: []string(nil), LoadBalancerIP: ""},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "clusterip-none-and-port-mapping",
|
||||
tcp: []string{"456:9898"},
|
||||
clusterip: "None",
|
||||
serviceType: v1.ServiceTypeClusterIP,
|
||||
expected: &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "clusterip-none-and-port-mapping",
|
||||
Labels: map[string]string{"app": "clusterip-none-and-port-mapping"},
|
||||
},
|
||||
Spec: v1.ServiceSpec{Type: "ClusterIP",
|
||||
Ports: []v1.ServicePort{{Name: "456-9898", Protocol: "TCP", Port: 456, TargetPort: intstr.IntOrString{Type: 0, IntVal: 9898, StrVal: ""}, NodePort: 0}},
|
||||
Selector: map[string]string{"app": "clusterip-none-and-port-mapping"},
|
||||
ClusterIP: "None", ExternalIPs: []string(nil), LoadBalancerIP: ""},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "loadbalancer-ok",
|
||||
tcp: []string{"456:9898"},
|
||||
clusterip: "",
|
||||
serviceType: v1.ServiceTypeLoadBalancer,
|
||||
expected: &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "loadbalancer-ok",
|
||||
Labels: map[string]string{"app": "loadbalancer-ok"},
|
||||
},
|
||||
Spec: v1.ServiceSpec{Type: "LoadBalancer",
|
||||
Ports: []v1.ServicePort{{Name: "456-9898", Protocol: "TCP", Port: 456, TargetPort: intstr.IntOrString{Type: 0, IntVal: 9898, StrVal: ""}, NodePort: 0}},
|
||||
Selector: map[string]string{"app": "loadbalancer-ok"},
|
||||
ClusterIP: "", ExternalIPs: []string(nil), LoadBalancerIP: ""},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid-port",
|
||||
tcp: []string{"65536"},
|
||||
clusterip: "None",
|
||||
serviceType: v1.ServiceTypeClusterIP,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid-port-mapping",
|
||||
tcp: []string{"8080:-abc"},
|
||||
clusterip: "None",
|
||||
serviceType: v1.ServiceTypeClusterIP,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
generator := ServiceCommonGeneratorV1{
|
||||
Name: test.name,
|
||||
TCP: test.tcp,
|
||||
Type: test.serviceType,
|
||||
ClusterIP: test.clusterip,
|
||||
}
|
||||
obj, err := generator.StructuredGenerate()
|
||||
if !test.expectErr && err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if test.expectErr && err != nil {
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*v1.Service), test.expected) {
|
||||
t.Errorf("test: %v\nexpected:\n%#v\nsaw:\n%#v", test.name, test.expected, obj.(*v1.Service))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePorts(t *testing.T) {
|
||||
tests := []struct {
|
||||
portString string
|
||||
expectPort int32
|
||||
expectTargetPort intstr.IntOrString
|
||||
expectErr string
|
||||
}{
|
||||
{
|
||||
portString: "3232",
|
||||
expectPort: 3232,
|
||||
expectTargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 3232},
|
||||
},
|
||||
{
|
||||
portString: "1:65535",
|
||||
expectPort: 1,
|
||||
expectTargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 65535},
|
||||
},
|
||||
{
|
||||
portString: "-5:1234",
|
||||
expectPort: 0,
|
||||
expectTargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 0},
|
||||
expectErr: "must be between 1 and 65535, inclusive",
|
||||
},
|
||||
{
|
||||
portString: "5:65536",
|
||||
expectPort: 0,
|
||||
expectTargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 0},
|
||||
expectErr: "must be between 1 and 65535, inclusive",
|
||||
},
|
||||
{
|
||||
portString: "test-5:443",
|
||||
expectPort: 0,
|
||||
expectTargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 0},
|
||||
expectErr: "invalid syntax",
|
||||
},
|
||||
{
|
||||
portString: "5:test-443",
|
||||
expectPort: 5,
|
||||
expectTargetPort: intstr.IntOrString{Type: intstr.String, StrVal: "test-443"},
|
||||
},
|
||||
{
|
||||
portString: "5:test*443",
|
||||
expectPort: 0,
|
||||
expectTargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 0},
|
||||
expectErr: "must contain only alpha-numeric characters (a-z, 0-9), and hyphens (-)",
|
||||
},
|
||||
{
|
||||
portString: "5:",
|
||||
expectPort: 0,
|
||||
expectTargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 0},
|
||||
expectErr: "must contain at least one letter or number (a-z, 0-9)",
|
||||
},
|
||||
{
|
||||
portString: "5:test--443",
|
||||
expectPort: 0,
|
||||
expectTargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 0},
|
||||
expectErr: "must not contain consecutive hyphens",
|
||||
},
|
||||
{
|
||||
portString: "5:test443-",
|
||||
expectPort: 0,
|
||||
expectTargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 0},
|
||||
expectErr: "must not begin or end with a hyphen",
|
||||
},
|
||||
{
|
||||
portString: "3232:1234:4567",
|
||||
expectPort: 3232,
|
||||
expectTargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 1234},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.portString, func(t *testing.T) {
|
||||
port, targetPort, err := parsePorts(test.portString)
|
||||
if len(test.expectErr) != 0 {
|
||||
if !strings.Contains(err.Error(), test.expectErr) {
|
||||
t.Errorf("parse ports string: %s. Expected err: %s, Got err: %v.", test.portString, test.expectErr, err)
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(targetPort, test.expectTargetPort) || port != test.expectPort {
|
||||
t.Errorf("parse ports string: %s. Expected port:%d, targetPort:%v, Got port:%d, targetPort:%v.", test.portString, test.expectPort, test.expectTargetPort, port, targetPort)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateServiceCommonGeneratorV1(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
s ServiceCommonGeneratorV1
|
||||
expectErr string
|
||||
}{
|
||||
{
|
||||
name: "validate-ok",
|
||||
s: ServiceCommonGeneratorV1{
|
||||
Name: "validate-ok",
|
||||
Type: v1.ServiceTypeClusterIP,
|
||||
TCP: []string{"123", "234:1234"},
|
||||
ClusterIP: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Name-none",
|
||||
s: ServiceCommonGeneratorV1{
|
||||
Type: v1.ServiceTypeClusterIP,
|
||||
TCP: []string{"123", "234:1234"},
|
||||
ClusterIP: "",
|
||||
},
|
||||
expectErr: "name must be specified",
|
||||
},
|
||||
{
|
||||
name: "Type-none",
|
||||
s: ServiceCommonGeneratorV1{
|
||||
Name: "validate-ok",
|
||||
TCP: []string{"123", "234:1234"},
|
||||
ClusterIP: "",
|
||||
},
|
||||
expectErr: "type must be specified",
|
||||
},
|
||||
{
|
||||
name: "invalid-ClusterIPNone",
|
||||
s: ServiceCommonGeneratorV1{
|
||||
Name: "validate-ok",
|
||||
Type: v1.ServiceTypeNodePort,
|
||||
TCP: []string{"123", "234:1234"},
|
||||
ClusterIP: v1.ClusterIPNone,
|
||||
},
|
||||
expectErr: "ClusterIP=None can only be used with ClusterIP service type",
|
||||
},
|
||||
{
|
||||
name: "TCP-none",
|
||||
s: ServiceCommonGeneratorV1{
|
||||
Name: "validate-ok",
|
||||
Type: v1.ServiceTypeClusterIP,
|
||||
ClusterIP: "",
|
||||
},
|
||||
expectErr: "at least one tcp port specifier must be provided",
|
||||
},
|
||||
{
|
||||
name: "invalid-ExternalName",
|
||||
s: ServiceCommonGeneratorV1{
|
||||
Name: "validate-ok",
|
||||
Type: v1.ServiceTypeExternalName,
|
||||
TCP: []string{"123", "234:1234"},
|
||||
ClusterIP: "",
|
||||
ExternalName: "@oi:test",
|
||||
},
|
||||
expectErr: "invalid service external name",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := test.s.validate()
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), test.expectErr) {
|
||||
t.Errorf("validate:%s Expected err: %s, Got err: %v", test.name, test.expectErr, err)
|
||||
}
|
||||
}
|
||||
if err == nil && len(test.expectErr) != 0 {
|
||||
t.Errorf("validate:%s Expected success, Got err: %v", test.name, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
963
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/service_test.go
generated
vendored
Normal file
963
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/service_test.go
generated
vendored
Normal file
@ -0,0 +1,963 @@
|
||||
/*
|
||||
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/kubernetes/pkg/kubectl/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.FromInt(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.FromInt(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.FromInt(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.FromInt(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.FromInt(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.FromInt(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.FromInt(1234),
|
||||
},
|
||||
{
|
||||
Name: "port-2",
|
||||
Port: 443,
|
||||
Protocol: v1.ProtocolUDP,
|
||||
TargetPort: intstr.FromInt(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.FromInt(80),
|
||||
},
|
||||
{
|
||||
Name: "port-2",
|
||||
Port: 443,
|
||||
Protocol: v1.ProtocolTCP,
|
||||
TargetPort: intstr.FromInt(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.FromInt(80),
|
||||
},
|
||||
{
|
||||
Name: "port-2",
|
||||
Port: 8080,
|
||||
Protocol: v1.ProtocolUDP,
|
||||
TargetPort: intstr.FromInt(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.FromInt(80),
|
||||
},
|
||||
{
|
||||
Name: "port-2",
|
||||
Port: 8080,
|
||||
Protocol: v1.ProtocolUDP,
|
||||
TargetPort: intstr.FromInt(8080),
|
||||
},
|
||||
{
|
||||
Name: "port-3",
|
||||
Port: 8081,
|
||||
Protocol: v1.ProtocolTCP,
|
||||
TargetPort: intstr.FromInt(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.FromInt(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.FromInt(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.FromInt(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.FromInt(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.FromInt(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.FromInt(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.FromInt(80),
|
||||
},
|
||||
{
|
||||
Name: "port-2",
|
||||
Port: 443,
|
||||
Protocol: v1.ProtocolSCTP,
|
||||
TargetPort: intstr.FromInt(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.FromInt(80),
|
||||
},
|
||||
{
|
||||
Name: "port-2",
|
||||
Port: 8080,
|
||||
Protocol: v1.ProtocolSCTP,
|
||||
TargetPort: intstr.FromInt(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.FromInt(80),
|
||||
},
|
||||
{
|
||||
Name: "port-2",
|
||||
Port: 8080,
|
||||
Protocol: v1.ProtocolUDP,
|
||||
TargetPort: intstr.FromInt(8080),
|
||||
},
|
||||
{
|
||||
Name: "port-3",
|
||||
Port: 8081,
|
||||
Protocol: v1.ProtocolTCP,
|
||||
TargetPort: intstr.FromInt(8081),
|
||||
},
|
||||
{
|
||||
Name: "port-4",
|
||||
Port: 8082,
|
||||
Protocol: v1.ProtocolSCTP,
|
||||
TargetPort: intstr.FromInt(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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
52
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/serviceaccount.go
generated
vendored
Normal file
52
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/serviceaccount.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubectl/generate"
|
||||
)
|
||||
|
||||
// ServiceAccountGeneratorV1 supports stable generation of a service account
|
||||
type ServiceAccountGeneratorV1 struct {
|
||||
// Name of service account
|
||||
Name string
|
||||
}
|
||||
|
||||
// Ensure it supports the generator pattern that uses parameters specified during construction
|
||||
var _ generate.StructuredGenerator = &ServiceAccountGeneratorV1{}
|
||||
|
||||
// StructuredGenerate outputs a service account object using the configured fields
|
||||
func (g *ServiceAccountGeneratorV1) StructuredGenerate() (runtime.Object, error) {
|
||||
if err := g.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serviceAccount := &v1.ServiceAccount{}
|
||||
serviceAccount.Name = g.Name
|
||||
return serviceAccount, nil
|
||||
}
|
||||
|
||||
// validate validates required fields are set to support structured generation
|
||||
func (g *ServiceAccountGeneratorV1) validate() error {
|
||||
if len(g.Name) == 0 {
|
||||
return fmt.Errorf("name must be specified")
|
||||
}
|
||||
return nil
|
||||
}
|
63
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/serviceaccount_test.go
generated
vendored
Normal file
63
vendor/k8s.io/kubernetes/pkg/kubectl/generate/versioned/serviceaccount_test.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package versioned
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestServiceAccountGenerate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
expected *v1.ServiceAccount
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "foo",
|
||||
expected: &v1.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
generator := ServiceAccountGeneratorV1{
|
||||
Name: tt.name,
|
||||
}
|
||||
obj, err := generator.StructuredGenerate()
|
||||
if !tt.expectErr && err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if tt.expectErr && err != nil {
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(obj.(*v1.ServiceAccount), tt.expected) {
|
||||
t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", tt.expected, obj.(*v1.ServiceAccount))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user