From 8fe8e4f1067c78c70c3a8a48a4b572cb99194e8b Mon Sep 17 00:00:00 2001 From: xilabao Date: Thu, 1 Jun 2017 16:48:02 +0800 Subject: [PATCH 01/38] fix parse pairs --- pkg/kubectl/cmd/annotate_test.go | 24 ++++++++++++++++++++++++ pkg/kubectl/cmd/label_test.go | 8 ++++++++ pkg/kubectl/cmd/util/helpers.go | 6 +++--- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/pkg/kubectl/cmd/annotate_test.go b/pkg/kubectl/cmd/annotate_test.go index 7f8a2e24538..e1b6ca7d6dd 100644 --- a/pkg/kubectl/cmd/annotate_test.go +++ b/pkg/kubectl/cmd/annotate_test.go @@ -156,6 +156,18 @@ func TestParseAnnotations(t *testing.T) { scenario: "incorrect annotation input (missing =value)", expectErr: true, }, + { + annotations: []string{"-"}, + expectedErr: "invalid annotation format: -", + scenario: "incorrect annotation input (missing key)", + expectErr: true, + }, + { + annotations: []string{"=bar"}, + expectedErr: "invalid annotation format: =bar", + scenario: "incorrect annotation input (missing key)", + expectErr: true, + }, } for _, test := range tests { annotations, remove, err := parseAnnotations(test.annotations) @@ -380,6 +392,18 @@ func TestAnnotateErrors(t *testing.T) { return strings.Contains(err.Error(), "at least one annotation update is required") }, }, + "wrong annotations": { + args: []string{"pods", "-"}, + errFn: func(err error) bool { + return strings.Contains(err.Error(), "at least one annotation update is required") + }, + }, + "wrong annotations 2": { + args: []string{"pods", "=bar"}, + errFn: func(err error) bool { + return strings.Contains(err.Error(), "at least one annotation update is required") + }, + }, "no resources remove annotations": { args: []string{"pods-"}, errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") }, diff --git a/pkg/kubectl/cmd/label_test.go b/pkg/kubectl/cmd/label_test.go index e92dda131bd..5140ca2bcf2 100644 --- a/pkg/kubectl/cmd/label_test.go +++ b/pkg/kubectl/cmd/label_test.go @@ -289,6 +289,14 @@ func TestLabelErrors(t *testing.T) { args: []string{"pods"}, errFn: func(err error) bool { return strings.Contains(err.Error(), "at least one label update is required") }, }, + "wrong labels": { + args: []string{"pods", "-"}, + errFn: func(err error) bool { return strings.Contains(err.Error(), "at least one label update is required") }, + }, + "wrong labels 2": { + args: []string{"pods", "=bar"}, + errFn: func(err error) bool { return strings.Contains(err.Error(), "at least one label update is required") }, + }, "no resources": { args: []string{"pods-"}, errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") }, diff --git a/pkg/kubectl/cmd/util/helpers.go b/pkg/kubectl/cmd/util/helpers.go index 867d5c55cea..fa8ccfa7dc5 100644 --- a/pkg/kubectl/cmd/util/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -620,7 +620,7 @@ func AddInclude3rdPartyVarFlags(cmd *cobra.Command, include3rdParty *bool) { func GetResourcesAndPairs(args []string, pairType string) (resources []string, pairArgs []string, err error) { foundPair := false for _, s := range args { - nonResource := strings.Contains(s, "=") || strings.HasSuffix(s, "-") + nonResource := (strings.Contains(s, "=") && s[0] != '=') || (strings.HasSuffix(s, "-") && s != "-") switch { case !foundPair && nonResource: foundPair = true @@ -646,7 +646,7 @@ func ParsePairs(pairArgs []string, pairType string, supportRemove bool) (newPair var invalidBuf bytes.Buffer var invalidBufNonEmpty bool for _, pairArg := range pairArgs { - if strings.Contains(pairArg, "=") { + if strings.Contains(pairArg, "=") && pairArg[0] != '=' { parts := strings.SplitN(pairArg, "=", 2) if len(parts) != 2 { if invalidBufNonEmpty { @@ -657,7 +657,7 @@ func ParsePairs(pairArgs []string, pairType string, supportRemove bool) (newPair } else { newPairs[parts[0]] = parts[1] } - } else if supportRemove && strings.HasSuffix(pairArg, "-") { + } else if supportRemove && strings.HasSuffix(pairArg, "-") && pairArg != "-" { removePairs = append(removePairs, pairArg[:len(pairArg)-1]) } else { if invalidBufNonEmpty { From 3e78e53c3dcf958631b6825c41b279288914ab4f Mon Sep 17 00:00:00 2001 From: zouyee Date: Tue, 27 Jun 2017 14:51:57 +0800 Subject: [PATCH 02/38] modify some mistake --- staging/src/k8s.io/client-go/discovery/discovery_client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/staging/src/k8s.io/client-go/discovery/discovery_client.go b/staging/src/k8s.io/client-go/discovery/discovery_client.go index 0ee46b863b4..d63b57ad96c 100644 --- a/staging/src/k8s.io/client-go/discovery/discovery_client.go +++ b/staging/src/k8s.io/client-go/discovery/discovery_client.go @@ -429,7 +429,7 @@ func NewDiscoveryClientForConfig(c *restclient.Config) (*DiscoveryClient, error) return &DiscoveryClient{restClient: client, LegacyPrefix: "/api"}, err } -// NewDiscoveryClientForConfig creates a new DiscoveryClient for the given config. If +// NewDiscoveryClientForConfigOrDie creates a new DiscoveryClient for the given config. If // there is an error, it panics. func NewDiscoveryClientForConfigOrDie(c *restclient.Config) *DiscoveryClient { client, err := NewDiscoveryClientForConfig(c) @@ -440,7 +440,7 @@ func NewDiscoveryClientForConfigOrDie(c *restclient.Config) *DiscoveryClient { } -// New creates a new DiscoveryClient for the given RESTClient. +// NewDiscoveryClient returns a new DiscoveryClient for the given RESTClient. func NewDiscoveryClient(c restclient.Interface) *DiscoveryClient { return &DiscoveryClient{restClient: c, LegacyPrefix: "/api"} } From 935a5c1eae8de1ddac8ad7e7729ed45620046410 Mon Sep 17 00:00:00 2001 From: zhangxiaoyu-zidif Date: Sun, 23 Jul 2017 01:59:53 +0800 Subject: [PATCH 03/38] fix f.Errorf --- pkg/kubectl/cmd/get_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/kubectl/cmd/get_test.go b/pkg/kubectl/cmd/get_test.go index e2c80d5f629..6532a7df1eb 100644 --- a/pkg/kubectl/cmd/get_test.go +++ b/pkg/kubectl/cmd/get_test.go @@ -142,7 +142,7 @@ func TestGetUnknownSchemaObject(t *testing.T) { expected := []runtime.Object{cmdtesting.NewInternalType("", "", "foo")} actual := tf.Printer.(*testPrinter).Objects if len(actual) != len(expected) { - t.Fatal(actual) + t.Fatalf("expected: %#v, but actual: %#v", expected, actual) } for i, obj := range actual { expectedJSON := runtime.EncodeOrDie(codec, expected[i]) @@ -158,7 +158,7 @@ func TestGetUnknownSchemaObject(t *testing.T) { } if !reflect.DeepEqual(expectedMap, actualMap) { - t.Errorf("unexpected object: \n%#v\n%#v", expectedMap, actualMap) + t.Errorf("expectedMap: %#v, but actualMap: %#v", expectedMap, actualMap) } } } From d717bf53b9ce5bd1c3f5fe54f74fce1b679e08e3 Mon Sep 17 00:00:00 2001 From: Ryan McNamara Date: Tue, 25 Jul 2017 13:45:07 -0700 Subject: [PATCH 04/38] DNS name error message improvement --- .../src/k8s.io/apimachinery/pkg/util/validation/validation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/staging/src/k8s.io/apimachinery/pkg/util/validation/validation.go b/staging/src/k8s.io/apimachinery/pkg/util/validation/validation.go index b1fcc570812..f5bab1deb06 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/validation/validation.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/validation/validation.go @@ -126,7 +126,7 @@ func IsDNS1123Subdomain(value string) []string { } const dns1035LabelFmt string = "[a-z]([-a-z0-9]*[a-z0-9])?" -const dns1035LabelErrMsg string = "a DNS-1035 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character" +const dns1035LabelErrMsg string = "a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character" const DNS1035LabelMaxLength int = 63 var dns1035LabelRegexp = regexp.MustCompile("^" + dns1035LabelFmt + "$") From 142f142ccc8eac64a8536d3b9be8770b4fc96eb7 Mon Sep 17 00:00:00 2001 From: zhangxiaoyu-zidif Date: Wed, 26 Jul 2017 10:20:08 +0800 Subject: [PATCH 05/38] change Errorf to Error when no printer format --- pkg/kubectl/cmd/get_test.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/pkg/kubectl/cmd/get_test.go b/pkg/kubectl/cmd/get_test.go index e2c80d5f629..2b2edec38d5 100644 --- a/pkg/kubectl/cmd/get_test.go +++ b/pkg/kubectl/cmd/get_test.go @@ -214,7 +214,7 @@ func TestGetObjectsWithOpenAPIOutputFormatPresent(t *testing.T) { verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") + t.Error("unexpected empty output") } } @@ -260,7 +260,7 @@ func TestGetObjects(t *testing.T) { verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") + t.Error("unexpected empty output") } } @@ -412,7 +412,7 @@ func TestGetSortedObjects(t *testing.T) { verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") + t.Error("unexpected empty output") } } @@ -466,7 +466,7 @@ func TestGetObjectsIdentifiedByFile(t *testing.T) { verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") + t.Error("unexpected empty output") } } @@ -495,7 +495,7 @@ func TestGetListObjects(t *testing.T) { verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") + t.Error("unexpected empty output") } } @@ -537,7 +537,7 @@ func TestGetAllListObjects(t *testing.T) { verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") + t.Error("unexpected empty output") } } @@ -566,7 +566,7 @@ func TestGetListComponentStatus(t *testing.T) { verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") + t.Error("unexpected empty output") } } @@ -605,7 +605,7 @@ func TestGetMultipleTypeObjects(t *testing.T) { verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") + t.Error("unexpected empty output") } } @@ -714,7 +714,7 @@ func TestGetMultipleTypeObjectsWithSelector(t *testing.T) { verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") + t.Error("unexpected empty output") } } @@ -759,7 +759,7 @@ func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) { verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") + t.Error("unexpected empty output") } } @@ -784,7 +784,7 @@ func TestGetByFormatForcesFlag(t *testing.T) { showAllFlag, _ := cmd.Flags().GetBool("show-all") if showAllFlag { - t.Errorf("expected showAll to not be true when getting resource") + t.Error("expected showAll to not be true when getting resource") } } @@ -904,7 +904,7 @@ func TestWatchSelector(t *testing.T) { verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") + t.Error("unexpected empty output") } } @@ -946,7 +946,7 @@ func TestWatchResource(t *testing.T) { verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") + t.Error("unexpected empty output") } } @@ -989,7 +989,7 @@ func TestWatchResourceIdentifiedByFile(t *testing.T) { verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") + t.Error("unexpected empty output") } } @@ -1031,7 +1031,7 @@ func TestWatchOnlyResource(t *testing.T) { verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") + t.Error("unexpected empty output") } } @@ -1077,7 +1077,7 @@ func TestWatchOnlyList(t *testing.T) { verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { - t.Errorf("unexpected empty output") + t.Error("unexpected empty output") } } From 17baaacb29247dbb6b04471f4bee831b0d617dc8 Mon Sep 17 00:00:00 2001 From: qingsenLi Date: Fri, 28 Jul 2017 03:11:50 +0800 Subject: [PATCH 06/38] fix the typo of intializing --- pkg/master/client_ca_hook.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/master/client_ca_hook.go b/pkg/master/client_ca_hook.go index e6d1b91001a..3a2780c92a7 100644 --- a/pkg/master/client_ca_hook.go +++ b/pkg/master/client_ca_hook.go @@ -46,7 +46,7 @@ func (h ClientCARegistrationHook) PostStartHook(hookContext genericapiserver.Pos return nil } - // intializing CAs is important so that aggregated API servers can come up with "normal" config. + // initializing CAs is important so that aggregated API servers can come up with "normal" config. // We've seen lagging etcd before, so we want to retry this a few times before we decide to crashloop // the API server on it. err := wait.Poll(1*time.Second, 30*time.Second, func() (done bool, err error) { @@ -62,7 +62,7 @@ func (h ClientCARegistrationHook) PostStartHook(hookContext genericapiserver.Pos return h.tryToWriteClientCAs(client) }) - // if we're never able to make it through intialization, kill the API server + // if we're never able to make it through initialization, kill the API server if err != nil { return fmt.Errorf("unable to initialize client CA configmap: %v", err) } From fa8d320a8dc57cef6058a4cb2832ba91556be622 Mon Sep 17 00:00:00 2001 From: Janet Kuo Date: Thu, 27 Jul 2017 14:51:17 -0700 Subject: [PATCH 07/38] Add conversion-gen between extensions and apps --- pkg/apis/apps/v1beta1/doc.go | 1 + pkg/apis/apps/v1beta2/doc.go | 1 + 2 files changed, 2 insertions(+) diff --git a/pkg/apis/apps/v1beta1/doc.go b/pkg/apis/apps/v1beta1/doc.go index 079bf6ef9b2..058dce37f1d 100644 --- a/pkg/apis/apps/v1beta1/doc.go +++ b/pkg/apis/apps/v1beta1/doc.go @@ -15,6 +15,7 @@ limitations under the License. */ // +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/apps +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/extensions // +k8s:conversion-gen-external-types=../../../../vendor/k8s.io/api/apps/v1beta1 // +k8s:defaulter-gen=TypeMeta // +k8s:defaulter-gen-input=../../../../vendor/k8s.io/api/apps/v1beta1 diff --git a/pkg/apis/apps/v1beta2/doc.go b/pkg/apis/apps/v1beta2/doc.go index 6cea002d3f5..9a0bb581994 100644 --- a/pkg/apis/apps/v1beta2/doc.go +++ b/pkg/apis/apps/v1beta2/doc.go @@ -15,6 +15,7 @@ limitations under the License. */ // +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/apps +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/extensions // +k8s:conversion-gen-external-types=../../../../vendor/k8s.io/api/apps/v1beta2 // +k8s:defaulter-gen=TypeMeta // +k8s:defaulter-gen-input=../../../../vendor/k8s.io/api/apps/v1beta2 From a5e29c8af2ce87f1baa166e1aa77054c4c539f70 Mon Sep 17 00:00:00 2001 From: Janet Kuo Date: Thu, 27 Jul 2017 14:53:13 -0700 Subject: [PATCH 08/38] Autogen --- .../apps/v1beta1/zz_generated.conversion.go | 372 ++++++++- .../apps/v1beta2/zz_generated.conversion.go | 728 ++++++++++++++++++ 2 files changed, 1091 insertions(+), 9 deletions(-) diff --git a/pkg/apis/apps/v1beta1/zz_generated.conversion.go b/pkg/apis/apps/v1beta1/zz_generated.conversion.go index 7da7c398cee..d5dfe18d5b4 100644 --- a/pkg/apis/apps/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/apps/v1beta1/zz_generated.conversion.go @@ -22,13 +22,14 @@ package v1beta1 import ( v1beta1 "k8s.io/api/apps/v1beta1" - core_v1 "k8s.io/api/core/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/api/core/v1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" api "k8s.io/kubernetes/pkg/api" api_v1 "k8s.io/kubernetes/pkg/api/v1" apps "k8s.io/kubernetes/pkg/apis/apps" + extensions "k8s.io/kubernetes/pkg/apis/extensions" unsafe "unsafe" ) @@ -44,8 +45,32 @@ func RegisterConversions(scheme *runtime.Scheme) error { Convert_apps_ControllerRevision_To_v1beta1_ControllerRevision, Convert_v1beta1_ControllerRevisionList_To_apps_ControllerRevisionList, Convert_apps_ControllerRevisionList_To_v1beta1_ControllerRevisionList, + Convert_v1beta1_Deployment_To_extensions_Deployment, + Convert_extensions_Deployment_To_v1beta1_Deployment, + Convert_v1beta1_DeploymentCondition_To_extensions_DeploymentCondition, + Convert_extensions_DeploymentCondition_To_v1beta1_DeploymentCondition, + Convert_v1beta1_DeploymentList_To_extensions_DeploymentList, + Convert_extensions_DeploymentList_To_v1beta1_DeploymentList, + Convert_v1beta1_DeploymentRollback_To_extensions_DeploymentRollback, + Convert_extensions_DeploymentRollback_To_v1beta1_DeploymentRollback, + Convert_v1beta1_DeploymentSpec_To_extensions_DeploymentSpec, + Convert_extensions_DeploymentSpec_To_v1beta1_DeploymentSpec, + Convert_v1beta1_DeploymentStatus_To_extensions_DeploymentStatus, + Convert_extensions_DeploymentStatus_To_v1beta1_DeploymentStatus, + Convert_v1beta1_DeploymentStrategy_To_extensions_DeploymentStrategy, + Convert_extensions_DeploymentStrategy_To_v1beta1_DeploymentStrategy, + Convert_v1beta1_RollbackConfig_To_extensions_RollbackConfig, + Convert_extensions_RollbackConfig_To_v1beta1_RollbackConfig, + Convert_v1beta1_RollingUpdateDeployment_To_extensions_RollingUpdateDeployment, + Convert_extensions_RollingUpdateDeployment_To_v1beta1_RollingUpdateDeployment, Convert_v1beta1_RollingUpdateStatefulSetStrategy_To_apps_RollingUpdateStatefulSetStrategy, Convert_apps_RollingUpdateStatefulSetStrategy_To_v1beta1_RollingUpdateStatefulSetStrategy, + Convert_v1beta1_Scale_To_extensions_Scale, + Convert_extensions_Scale_To_v1beta1_Scale, + Convert_v1beta1_ScaleSpec_To_extensions_ScaleSpec, + Convert_extensions_ScaleSpec_To_v1beta1_ScaleSpec, + Convert_v1beta1_ScaleStatus_To_extensions_ScaleStatus, + Convert_extensions_ScaleStatus_To_v1beta1_ScaleStatus, Convert_v1beta1_StatefulSet_To_apps_StatefulSet, Convert_apps_StatefulSet_To_v1beta1_StatefulSet, Convert_v1beta1_StatefulSetList_To_apps_StatefulSetList, @@ -129,8 +154,272 @@ func Convert_apps_ControllerRevisionList_To_v1beta1_ControllerRevisionList(in *a return autoConvert_apps_ControllerRevisionList_To_v1beta1_ControllerRevisionList(in, out, s) } +func autoConvert_v1beta1_Deployment_To_extensions_Deployment(in *v1beta1.Deployment, out *extensions.Deployment, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta1_DeploymentSpec_To_extensions_DeploymentSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta1_DeploymentStatus_To_extensions_DeploymentStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_Deployment_To_extensions_Deployment is an autogenerated conversion function. +func Convert_v1beta1_Deployment_To_extensions_Deployment(in *v1beta1.Deployment, out *extensions.Deployment, s conversion.Scope) error { + return autoConvert_v1beta1_Deployment_To_extensions_Deployment(in, out, s) +} + +func autoConvert_extensions_Deployment_To_v1beta1_Deployment(in *extensions.Deployment, out *v1beta1.Deployment, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_extensions_DeploymentSpec_To_v1beta1_DeploymentSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_extensions_DeploymentStatus_To_v1beta1_DeploymentStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_extensions_Deployment_To_v1beta1_Deployment is an autogenerated conversion function. +func Convert_extensions_Deployment_To_v1beta1_Deployment(in *extensions.Deployment, out *v1beta1.Deployment, s conversion.Scope) error { + return autoConvert_extensions_Deployment_To_v1beta1_Deployment(in, out, s) +} + +func autoConvert_v1beta1_DeploymentCondition_To_extensions_DeploymentCondition(in *v1beta1.DeploymentCondition, out *extensions.DeploymentCondition, s conversion.Scope) error { + out.Type = extensions.DeploymentConditionType(in.Type) + out.Status = api.ConditionStatus(in.Status) + out.LastUpdateTime = in.LastUpdateTime + out.LastTransitionTime = in.LastTransitionTime + out.Reason = in.Reason + out.Message = in.Message + return nil +} + +// Convert_v1beta1_DeploymentCondition_To_extensions_DeploymentCondition is an autogenerated conversion function. +func Convert_v1beta1_DeploymentCondition_To_extensions_DeploymentCondition(in *v1beta1.DeploymentCondition, out *extensions.DeploymentCondition, s conversion.Scope) error { + return autoConvert_v1beta1_DeploymentCondition_To_extensions_DeploymentCondition(in, out, s) +} + +func autoConvert_extensions_DeploymentCondition_To_v1beta1_DeploymentCondition(in *extensions.DeploymentCondition, out *v1beta1.DeploymentCondition, s conversion.Scope) error { + out.Type = v1beta1.DeploymentConditionType(in.Type) + out.Status = v1.ConditionStatus(in.Status) + out.LastUpdateTime = in.LastUpdateTime + out.LastTransitionTime = in.LastTransitionTime + out.Reason = in.Reason + out.Message = in.Message + return nil +} + +// Convert_extensions_DeploymentCondition_To_v1beta1_DeploymentCondition is an autogenerated conversion function. +func Convert_extensions_DeploymentCondition_To_v1beta1_DeploymentCondition(in *extensions.DeploymentCondition, out *v1beta1.DeploymentCondition, s conversion.Scope) error { + return autoConvert_extensions_DeploymentCondition_To_v1beta1_DeploymentCondition(in, out, s) +} + +func autoConvert_v1beta1_DeploymentList_To_extensions_DeploymentList(in *v1beta1.DeploymentList, out *extensions.DeploymentList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]extensions.Deployment, len(*in)) + for i := range *in { + if err := Convert_v1beta1_Deployment_To_extensions_Deployment(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta1_DeploymentList_To_extensions_DeploymentList is an autogenerated conversion function. +func Convert_v1beta1_DeploymentList_To_extensions_DeploymentList(in *v1beta1.DeploymentList, out *extensions.DeploymentList, s conversion.Scope) error { + return autoConvert_v1beta1_DeploymentList_To_extensions_DeploymentList(in, out, s) +} + +func autoConvert_extensions_DeploymentList_To_v1beta1_DeploymentList(in *extensions.DeploymentList, out *v1beta1.DeploymentList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta1.Deployment, len(*in)) + for i := range *in { + if err := Convert_extensions_Deployment_To_v1beta1_Deployment(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = make([]v1beta1.Deployment, 0) + } + return nil +} + +// Convert_extensions_DeploymentList_To_v1beta1_DeploymentList is an autogenerated conversion function. +func Convert_extensions_DeploymentList_To_v1beta1_DeploymentList(in *extensions.DeploymentList, out *v1beta1.DeploymentList, s conversion.Scope) error { + return autoConvert_extensions_DeploymentList_To_v1beta1_DeploymentList(in, out, s) +} + +func autoConvert_v1beta1_DeploymentRollback_To_extensions_DeploymentRollback(in *v1beta1.DeploymentRollback, out *extensions.DeploymentRollback, s conversion.Scope) error { + out.Name = in.Name + out.UpdatedAnnotations = *(*map[string]string)(unsafe.Pointer(&in.UpdatedAnnotations)) + if err := Convert_v1beta1_RollbackConfig_To_extensions_RollbackConfig(&in.RollbackTo, &out.RollbackTo, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_DeploymentRollback_To_extensions_DeploymentRollback is an autogenerated conversion function. +func Convert_v1beta1_DeploymentRollback_To_extensions_DeploymentRollback(in *v1beta1.DeploymentRollback, out *extensions.DeploymentRollback, s conversion.Scope) error { + return autoConvert_v1beta1_DeploymentRollback_To_extensions_DeploymentRollback(in, out, s) +} + +func autoConvert_extensions_DeploymentRollback_To_v1beta1_DeploymentRollback(in *extensions.DeploymentRollback, out *v1beta1.DeploymentRollback, s conversion.Scope) error { + out.Name = in.Name + out.UpdatedAnnotations = *(*map[string]string)(unsafe.Pointer(&in.UpdatedAnnotations)) + if err := Convert_extensions_RollbackConfig_To_v1beta1_RollbackConfig(&in.RollbackTo, &out.RollbackTo, s); err != nil { + return err + } + return nil +} + +// Convert_extensions_DeploymentRollback_To_v1beta1_DeploymentRollback is an autogenerated conversion function. +func Convert_extensions_DeploymentRollback_To_v1beta1_DeploymentRollback(in *extensions.DeploymentRollback, out *v1beta1.DeploymentRollback, s conversion.Scope) error { + return autoConvert_extensions_DeploymentRollback_To_v1beta1_DeploymentRollback(in, out, s) +} + +func autoConvert_v1beta1_DeploymentSpec_To_extensions_DeploymentSpec(in *v1beta1.DeploymentSpec, out *extensions.DeploymentSpec, s conversion.Scope) error { + if err := meta_v1.Convert_Pointer_int32_To_int32(&in.Replicas, &out.Replicas, s); err != nil { + return err + } + out.Selector = (*meta_v1.LabelSelector)(unsafe.Pointer(in.Selector)) + if err := api_v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err + } + if err := Convert_v1beta1_DeploymentStrategy_To_extensions_DeploymentStrategy(&in.Strategy, &out.Strategy, s); err != nil { + return err + } + out.MinReadySeconds = in.MinReadySeconds + out.RevisionHistoryLimit = (*int32)(unsafe.Pointer(in.RevisionHistoryLimit)) + out.Paused = in.Paused + out.RollbackTo = (*extensions.RollbackConfig)(unsafe.Pointer(in.RollbackTo)) + out.ProgressDeadlineSeconds = (*int32)(unsafe.Pointer(in.ProgressDeadlineSeconds)) + return nil +} + +func autoConvert_extensions_DeploymentSpec_To_v1beta1_DeploymentSpec(in *extensions.DeploymentSpec, out *v1beta1.DeploymentSpec, s conversion.Scope) error { + if err := meta_v1.Convert_int32_To_Pointer_int32(&in.Replicas, &out.Replicas, s); err != nil { + return err + } + out.Selector = (*meta_v1.LabelSelector)(unsafe.Pointer(in.Selector)) + if err := api_v1.Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err + } + if err := Convert_extensions_DeploymentStrategy_To_v1beta1_DeploymentStrategy(&in.Strategy, &out.Strategy, s); err != nil { + return err + } + out.MinReadySeconds = in.MinReadySeconds + out.RevisionHistoryLimit = (*int32)(unsafe.Pointer(in.RevisionHistoryLimit)) + out.Paused = in.Paused + out.RollbackTo = (*v1beta1.RollbackConfig)(unsafe.Pointer(in.RollbackTo)) + out.ProgressDeadlineSeconds = (*int32)(unsafe.Pointer(in.ProgressDeadlineSeconds)) + return nil +} + +func autoConvert_v1beta1_DeploymentStatus_To_extensions_DeploymentStatus(in *v1beta1.DeploymentStatus, out *extensions.DeploymentStatus, s conversion.Scope) error { + out.ObservedGeneration = in.ObservedGeneration + out.Replicas = in.Replicas + out.UpdatedReplicas = in.UpdatedReplicas + out.ReadyReplicas = in.ReadyReplicas + out.AvailableReplicas = in.AvailableReplicas + out.UnavailableReplicas = in.UnavailableReplicas + out.Conditions = *(*[]extensions.DeploymentCondition)(unsafe.Pointer(&in.Conditions)) + out.CollisionCount = (*int64)(unsafe.Pointer(in.CollisionCount)) + return nil +} + +// Convert_v1beta1_DeploymentStatus_To_extensions_DeploymentStatus is an autogenerated conversion function. +func Convert_v1beta1_DeploymentStatus_To_extensions_DeploymentStatus(in *v1beta1.DeploymentStatus, out *extensions.DeploymentStatus, s conversion.Scope) error { + return autoConvert_v1beta1_DeploymentStatus_To_extensions_DeploymentStatus(in, out, s) +} + +func autoConvert_extensions_DeploymentStatus_To_v1beta1_DeploymentStatus(in *extensions.DeploymentStatus, out *v1beta1.DeploymentStatus, s conversion.Scope) error { + out.ObservedGeneration = in.ObservedGeneration + out.Replicas = in.Replicas + out.UpdatedReplicas = in.UpdatedReplicas + out.ReadyReplicas = in.ReadyReplicas + out.AvailableReplicas = in.AvailableReplicas + out.UnavailableReplicas = in.UnavailableReplicas + out.Conditions = *(*[]v1beta1.DeploymentCondition)(unsafe.Pointer(&in.Conditions)) + out.CollisionCount = (*int64)(unsafe.Pointer(in.CollisionCount)) + return nil +} + +// Convert_extensions_DeploymentStatus_To_v1beta1_DeploymentStatus is an autogenerated conversion function. +func Convert_extensions_DeploymentStatus_To_v1beta1_DeploymentStatus(in *extensions.DeploymentStatus, out *v1beta1.DeploymentStatus, s conversion.Scope) error { + return autoConvert_extensions_DeploymentStatus_To_v1beta1_DeploymentStatus(in, out, s) +} + +func autoConvert_v1beta1_DeploymentStrategy_To_extensions_DeploymentStrategy(in *v1beta1.DeploymentStrategy, out *extensions.DeploymentStrategy, s conversion.Scope) error { + out.Type = extensions.DeploymentStrategyType(in.Type) + if in.RollingUpdate != nil { + in, out := &in.RollingUpdate, &out.RollingUpdate + *out = new(extensions.RollingUpdateDeployment) + if err := Convert_v1beta1_RollingUpdateDeployment_To_extensions_RollingUpdateDeployment(*in, *out, s); err != nil { + return err + } + } else { + out.RollingUpdate = nil + } + return nil +} + +func autoConvert_extensions_DeploymentStrategy_To_v1beta1_DeploymentStrategy(in *extensions.DeploymentStrategy, out *v1beta1.DeploymentStrategy, s conversion.Scope) error { + out.Type = v1beta1.DeploymentStrategyType(in.Type) + if in.RollingUpdate != nil { + in, out := &in.RollingUpdate, &out.RollingUpdate + *out = new(v1beta1.RollingUpdateDeployment) + if err := Convert_extensions_RollingUpdateDeployment_To_v1beta1_RollingUpdateDeployment(*in, *out, s); err != nil { + return err + } + } else { + out.RollingUpdate = nil + } + return nil +} + +func autoConvert_v1beta1_RollbackConfig_To_extensions_RollbackConfig(in *v1beta1.RollbackConfig, out *extensions.RollbackConfig, s conversion.Scope) error { + out.Revision = in.Revision + return nil +} + +// Convert_v1beta1_RollbackConfig_To_extensions_RollbackConfig is an autogenerated conversion function. +func Convert_v1beta1_RollbackConfig_To_extensions_RollbackConfig(in *v1beta1.RollbackConfig, out *extensions.RollbackConfig, s conversion.Scope) error { + return autoConvert_v1beta1_RollbackConfig_To_extensions_RollbackConfig(in, out, s) +} + +func autoConvert_extensions_RollbackConfig_To_v1beta1_RollbackConfig(in *extensions.RollbackConfig, out *v1beta1.RollbackConfig, s conversion.Scope) error { + out.Revision = in.Revision + return nil +} + +// Convert_extensions_RollbackConfig_To_v1beta1_RollbackConfig is an autogenerated conversion function. +func Convert_extensions_RollbackConfig_To_v1beta1_RollbackConfig(in *extensions.RollbackConfig, out *v1beta1.RollbackConfig, s conversion.Scope) error { + return autoConvert_extensions_RollbackConfig_To_v1beta1_RollbackConfig(in, out, s) +} + +func autoConvert_v1beta1_RollingUpdateDeployment_To_extensions_RollingUpdateDeployment(in *v1beta1.RollingUpdateDeployment, out *extensions.RollingUpdateDeployment, s conversion.Scope) error { + // WARNING: in.MaxUnavailable requires manual conversion: inconvertible types (*k8s.io/apimachinery/pkg/util/intstr.IntOrString vs k8s.io/apimachinery/pkg/util/intstr.IntOrString) + // WARNING: in.MaxSurge requires manual conversion: inconvertible types (*k8s.io/apimachinery/pkg/util/intstr.IntOrString vs k8s.io/apimachinery/pkg/util/intstr.IntOrString) + return nil +} + +func autoConvert_extensions_RollingUpdateDeployment_To_v1beta1_RollingUpdateDeployment(in *extensions.RollingUpdateDeployment, out *v1beta1.RollingUpdateDeployment, s conversion.Scope) error { + // WARNING: in.MaxUnavailable requires manual conversion: inconvertible types (k8s.io/apimachinery/pkg/util/intstr.IntOrString vs *k8s.io/apimachinery/pkg/util/intstr.IntOrString) + // WARNING: in.MaxSurge requires manual conversion: inconvertible types (k8s.io/apimachinery/pkg/util/intstr.IntOrString vs *k8s.io/apimachinery/pkg/util/intstr.IntOrString) + return nil +} + func autoConvert_v1beta1_RollingUpdateStatefulSetStrategy_To_apps_RollingUpdateStatefulSetStrategy(in *v1beta1.RollingUpdateStatefulSetStrategy, out *apps.RollingUpdateStatefulSetStrategy, s conversion.Scope) error { - if err := v1.Convert_Pointer_int32_To_int32(&in.Partition, &out.Partition, s); err != nil { + if err := meta_v1.Convert_Pointer_int32_To_int32(&in.Partition, &out.Partition, s); err != nil { return err } return nil @@ -142,7 +431,7 @@ func Convert_v1beta1_RollingUpdateStatefulSetStrategy_To_apps_RollingUpdateState } func autoConvert_apps_RollingUpdateStatefulSetStrategy_To_v1beta1_RollingUpdateStatefulSetStrategy(in *apps.RollingUpdateStatefulSetStrategy, out *v1beta1.RollingUpdateStatefulSetStrategy, s conversion.Scope) error { - if err := v1.Convert_int32_To_Pointer_int32(&in.Partition, &out.Partition, s); err != nil { + if err := meta_v1.Convert_int32_To_Pointer_int32(&in.Partition, &out.Partition, s); err != nil { return err } return nil @@ -153,6 +442,71 @@ func Convert_apps_RollingUpdateStatefulSetStrategy_To_v1beta1_RollingUpdateState return autoConvert_apps_RollingUpdateStatefulSetStrategy_To_v1beta1_RollingUpdateStatefulSetStrategy(in, out, s) } +func autoConvert_v1beta1_Scale_To_extensions_Scale(in *v1beta1.Scale, out *extensions.Scale, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta1_ScaleSpec_To_extensions_ScaleSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta1_ScaleStatus_To_extensions_ScaleStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta1_Scale_To_extensions_Scale is an autogenerated conversion function. +func Convert_v1beta1_Scale_To_extensions_Scale(in *v1beta1.Scale, out *extensions.Scale, s conversion.Scope) error { + return autoConvert_v1beta1_Scale_To_extensions_Scale(in, out, s) +} + +func autoConvert_extensions_Scale_To_v1beta1_Scale(in *extensions.Scale, out *v1beta1.Scale, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_extensions_ScaleSpec_To_v1beta1_ScaleSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_extensions_ScaleStatus_To_v1beta1_ScaleStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_extensions_Scale_To_v1beta1_Scale is an autogenerated conversion function. +func Convert_extensions_Scale_To_v1beta1_Scale(in *extensions.Scale, out *v1beta1.Scale, s conversion.Scope) error { + return autoConvert_extensions_Scale_To_v1beta1_Scale(in, out, s) +} + +func autoConvert_v1beta1_ScaleSpec_To_extensions_ScaleSpec(in *v1beta1.ScaleSpec, out *extensions.ScaleSpec, s conversion.Scope) error { + out.Replicas = in.Replicas + return nil +} + +// Convert_v1beta1_ScaleSpec_To_extensions_ScaleSpec is an autogenerated conversion function. +func Convert_v1beta1_ScaleSpec_To_extensions_ScaleSpec(in *v1beta1.ScaleSpec, out *extensions.ScaleSpec, s conversion.Scope) error { + return autoConvert_v1beta1_ScaleSpec_To_extensions_ScaleSpec(in, out, s) +} + +func autoConvert_extensions_ScaleSpec_To_v1beta1_ScaleSpec(in *extensions.ScaleSpec, out *v1beta1.ScaleSpec, s conversion.Scope) error { + out.Replicas = in.Replicas + return nil +} + +// Convert_extensions_ScaleSpec_To_v1beta1_ScaleSpec is an autogenerated conversion function. +func Convert_extensions_ScaleSpec_To_v1beta1_ScaleSpec(in *extensions.ScaleSpec, out *v1beta1.ScaleSpec, s conversion.Scope) error { + return autoConvert_extensions_ScaleSpec_To_v1beta1_ScaleSpec(in, out, s) +} + +func autoConvert_v1beta1_ScaleStatus_To_extensions_ScaleStatus(in *v1beta1.ScaleStatus, out *extensions.ScaleStatus, s conversion.Scope) error { + out.Replicas = in.Replicas + // WARNING: in.Selector requires manual conversion: inconvertible types (map[string]string vs *k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector) + // WARNING: in.TargetSelector requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_extensions_ScaleStatus_To_v1beta1_ScaleStatus(in *extensions.ScaleStatus, out *v1beta1.ScaleStatus, s conversion.Scope) error { + out.Replicas = in.Replicas + // WARNING: in.Selector requires manual conversion: inconvertible types (*k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector vs map[string]string) + return nil +} + func autoConvert_v1beta1_StatefulSet_To_apps_StatefulSet(in *v1beta1.StatefulSet, out *apps.StatefulSet, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_v1beta1_StatefulSetSpec_To_apps_StatefulSetSpec(&in.Spec, &out.Spec, s); err != nil { @@ -228,10 +582,10 @@ func Convert_apps_StatefulSetList_To_v1beta1_StatefulSetList(in *apps.StatefulSe } func autoConvert_v1beta1_StatefulSetSpec_To_apps_StatefulSetSpec(in *v1beta1.StatefulSetSpec, out *apps.StatefulSetSpec, s conversion.Scope) error { - if err := v1.Convert_Pointer_int32_To_int32(&in.Replicas, &out.Replicas, s); err != nil { + if err := meta_v1.Convert_Pointer_int32_To_int32(&in.Replicas, &out.Replicas, s); err != nil { return err } - out.Selector = (*v1.LabelSelector)(unsafe.Pointer(in.Selector)) + out.Selector = (*meta_v1.LabelSelector)(unsafe.Pointer(in.Selector)) if err := api_v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { return err } @@ -246,14 +600,14 @@ func autoConvert_v1beta1_StatefulSetSpec_To_apps_StatefulSetSpec(in *v1beta1.Sta } func autoConvert_apps_StatefulSetSpec_To_v1beta1_StatefulSetSpec(in *apps.StatefulSetSpec, out *v1beta1.StatefulSetSpec, s conversion.Scope) error { - if err := v1.Convert_int32_To_Pointer_int32(&in.Replicas, &out.Replicas, s); err != nil { + if err := meta_v1.Convert_int32_To_Pointer_int32(&in.Replicas, &out.Replicas, s); err != nil { return err } - out.Selector = (*v1.LabelSelector)(unsafe.Pointer(in.Selector)) + out.Selector = (*meta_v1.LabelSelector)(unsafe.Pointer(in.Selector)) if err := api_v1.Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { return err } - out.VolumeClaimTemplates = *(*[]core_v1.PersistentVolumeClaim)(unsafe.Pointer(&in.VolumeClaimTemplates)) + out.VolumeClaimTemplates = *(*[]v1.PersistentVolumeClaim)(unsafe.Pointer(&in.VolumeClaimTemplates)) out.ServiceName = in.ServiceName out.PodManagementPolicy = v1beta1.PodManagementPolicyType(in.PodManagementPolicy) if err := Convert_apps_StatefulSetUpdateStrategy_To_v1beta1_StatefulSetUpdateStrategy(&in.UpdateStrategy, &out.UpdateStrategy, s); err != nil { diff --git a/pkg/apis/apps/v1beta2/zz_generated.conversion.go b/pkg/apis/apps/v1beta2/zz_generated.conversion.go index 48b185a5ff1..23334b91a92 100644 --- a/pkg/apis/apps/v1beta2/zz_generated.conversion.go +++ b/pkg/apis/apps/v1beta2/zz_generated.conversion.go @@ -29,6 +29,7 @@ import ( api "k8s.io/kubernetes/pkg/api" api_v1 "k8s.io/kubernetes/pkg/api/v1" apps "k8s.io/kubernetes/pkg/apis/apps" + extensions "k8s.io/kubernetes/pkg/apis/extensions" unsafe "unsafe" ) @@ -40,8 +41,54 @@ func init() { // Public to allow building arbitrary schemes. func RegisterConversions(scheme *runtime.Scheme) error { return scheme.AddGeneratedConversionFuncs( + Convert_v1beta2_DaemonSet_To_extensions_DaemonSet, + Convert_extensions_DaemonSet_To_v1beta2_DaemonSet, + Convert_v1beta2_DaemonSetList_To_extensions_DaemonSetList, + Convert_extensions_DaemonSetList_To_v1beta2_DaemonSetList, + Convert_v1beta2_DaemonSetSpec_To_extensions_DaemonSetSpec, + Convert_extensions_DaemonSetSpec_To_v1beta2_DaemonSetSpec, + Convert_v1beta2_DaemonSetStatus_To_extensions_DaemonSetStatus, + Convert_extensions_DaemonSetStatus_To_v1beta2_DaemonSetStatus, + Convert_v1beta2_DaemonSetUpdateStrategy_To_extensions_DaemonSetUpdateStrategy, + Convert_extensions_DaemonSetUpdateStrategy_To_v1beta2_DaemonSetUpdateStrategy, + Convert_v1beta2_Deployment_To_extensions_Deployment, + Convert_extensions_Deployment_To_v1beta2_Deployment, + Convert_v1beta2_DeploymentCondition_To_extensions_DeploymentCondition, + Convert_extensions_DeploymentCondition_To_v1beta2_DeploymentCondition, + Convert_v1beta2_DeploymentList_To_extensions_DeploymentList, + Convert_extensions_DeploymentList_To_v1beta2_DeploymentList, + Convert_v1beta2_DeploymentRollback_To_extensions_DeploymentRollback, + Convert_extensions_DeploymentRollback_To_v1beta2_DeploymentRollback, + Convert_v1beta2_DeploymentSpec_To_extensions_DeploymentSpec, + Convert_extensions_DeploymentSpec_To_v1beta2_DeploymentSpec, + Convert_v1beta2_DeploymentStatus_To_extensions_DeploymentStatus, + Convert_extensions_DeploymentStatus_To_v1beta2_DeploymentStatus, + Convert_v1beta2_DeploymentStrategy_To_extensions_DeploymentStrategy, + Convert_extensions_DeploymentStrategy_To_v1beta2_DeploymentStrategy, + Convert_v1beta2_ReplicaSet_To_extensions_ReplicaSet, + Convert_extensions_ReplicaSet_To_v1beta2_ReplicaSet, + Convert_v1beta2_ReplicaSetCondition_To_extensions_ReplicaSetCondition, + Convert_extensions_ReplicaSetCondition_To_v1beta2_ReplicaSetCondition, + Convert_v1beta2_ReplicaSetList_To_extensions_ReplicaSetList, + Convert_extensions_ReplicaSetList_To_v1beta2_ReplicaSetList, + Convert_v1beta2_ReplicaSetSpec_To_extensions_ReplicaSetSpec, + Convert_extensions_ReplicaSetSpec_To_v1beta2_ReplicaSetSpec, + Convert_v1beta2_ReplicaSetStatus_To_extensions_ReplicaSetStatus, + Convert_extensions_ReplicaSetStatus_To_v1beta2_ReplicaSetStatus, + Convert_v1beta2_RollbackConfig_To_extensions_RollbackConfig, + Convert_extensions_RollbackConfig_To_v1beta2_RollbackConfig, + Convert_v1beta2_RollingUpdateDaemonSet_To_extensions_RollingUpdateDaemonSet, + Convert_extensions_RollingUpdateDaemonSet_To_v1beta2_RollingUpdateDaemonSet, + Convert_v1beta2_RollingUpdateDeployment_To_extensions_RollingUpdateDeployment, + Convert_extensions_RollingUpdateDeployment_To_v1beta2_RollingUpdateDeployment, Convert_v1beta2_RollingUpdateStatefulSetStrategy_To_apps_RollingUpdateStatefulSetStrategy, Convert_apps_RollingUpdateStatefulSetStrategy_To_v1beta2_RollingUpdateStatefulSetStrategy, + Convert_v1beta2_Scale_To_extensions_Scale, + Convert_extensions_Scale_To_v1beta2_Scale, + Convert_v1beta2_ScaleSpec_To_extensions_ScaleSpec, + Convert_extensions_ScaleSpec_To_v1beta2_ScaleSpec, + Convert_v1beta2_ScaleStatus_To_extensions_ScaleStatus, + Convert_extensions_ScaleStatus_To_v1beta2_ScaleStatus, Convert_v1beta2_StatefulSet_To_apps_StatefulSet, Convert_apps_StatefulSet_To_v1beta2_StatefulSet, Convert_v1beta2_StatefulSetList_To_apps_StatefulSetList, @@ -55,6 +102,622 @@ func RegisterConversions(scheme *runtime.Scheme) error { ) } +func autoConvert_v1beta2_DaemonSet_To_extensions_DaemonSet(in *v1beta2.DaemonSet, out *extensions.DaemonSet, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_DaemonSetSpec_To_extensions_DaemonSetSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta2_DaemonSetStatus_To_extensions_DaemonSetStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_DaemonSet_To_extensions_DaemonSet is an autogenerated conversion function. +func Convert_v1beta2_DaemonSet_To_extensions_DaemonSet(in *v1beta2.DaemonSet, out *extensions.DaemonSet, s conversion.Scope) error { + return autoConvert_v1beta2_DaemonSet_To_extensions_DaemonSet(in, out, s) +} + +func autoConvert_extensions_DaemonSet_To_v1beta2_DaemonSet(in *extensions.DaemonSet, out *v1beta2.DaemonSet, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_extensions_DaemonSetSpec_To_v1beta2_DaemonSetSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_extensions_DaemonSetStatus_To_v1beta2_DaemonSetStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_extensions_DaemonSet_To_v1beta2_DaemonSet is an autogenerated conversion function. +func Convert_extensions_DaemonSet_To_v1beta2_DaemonSet(in *extensions.DaemonSet, out *v1beta2.DaemonSet, s conversion.Scope) error { + return autoConvert_extensions_DaemonSet_To_v1beta2_DaemonSet(in, out, s) +} + +func autoConvert_v1beta2_DaemonSetList_To_extensions_DaemonSetList(in *v1beta2.DaemonSetList, out *extensions.DaemonSetList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]extensions.DaemonSet, len(*in)) + for i := range *in { + if err := Convert_v1beta2_DaemonSet_To_extensions_DaemonSet(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta2_DaemonSetList_To_extensions_DaemonSetList is an autogenerated conversion function. +func Convert_v1beta2_DaemonSetList_To_extensions_DaemonSetList(in *v1beta2.DaemonSetList, out *extensions.DaemonSetList, s conversion.Scope) error { + return autoConvert_v1beta2_DaemonSetList_To_extensions_DaemonSetList(in, out, s) +} + +func autoConvert_extensions_DaemonSetList_To_v1beta2_DaemonSetList(in *extensions.DaemonSetList, out *v1beta2.DaemonSetList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta2.DaemonSet, len(*in)) + for i := range *in { + if err := Convert_extensions_DaemonSet_To_v1beta2_DaemonSet(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = make([]v1beta2.DaemonSet, 0) + } + return nil +} + +// Convert_extensions_DaemonSetList_To_v1beta2_DaemonSetList is an autogenerated conversion function. +func Convert_extensions_DaemonSetList_To_v1beta2_DaemonSetList(in *extensions.DaemonSetList, out *v1beta2.DaemonSetList, s conversion.Scope) error { + return autoConvert_extensions_DaemonSetList_To_v1beta2_DaemonSetList(in, out, s) +} + +func autoConvert_v1beta2_DaemonSetSpec_To_extensions_DaemonSetSpec(in *v1beta2.DaemonSetSpec, out *extensions.DaemonSetSpec, s conversion.Scope) error { + out.Selector = (*v1.LabelSelector)(unsafe.Pointer(in.Selector)) + if err := api_v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err + } + if err := Convert_v1beta2_DaemonSetUpdateStrategy_To_extensions_DaemonSetUpdateStrategy(&in.UpdateStrategy, &out.UpdateStrategy, s); err != nil { + return err + } + out.MinReadySeconds = in.MinReadySeconds + out.TemplateGeneration = in.TemplateGeneration + out.RevisionHistoryLimit = (*int32)(unsafe.Pointer(in.RevisionHistoryLimit)) + return nil +} + +// Convert_v1beta2_DaemonSetSpec_To_extensions_DaemonSetSpec is an autogenerated conversion function. +func Convert_v1beta2_DaemonSetSpec_To_extensions_DaemonSetSpec(in *v1beta2.DaemonSetSpec, out *extensions.DaemonSetSpec, s conversion.Scope) error { + return autoConvert_v1beta2_DaemonSetSpec_To_extensions_DaemonSetSpec(in, out, s) +} + +func autoConvert_extensions_DaemonSetSpec_To_v1beta2_DaemonSetSpec(in *extensions.DaemonSetSpec, out *v1beta2.DaemonSetSpec, s conversion.Scope) error { + out.Selector = (*v1.LabelSelector)(unsafe.Pointer(in.Selector)) + if err := api_v1.Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err + } + if err := Convert_extensions_DaemonSetUpdateStrategy_To_v1beta2_DaemonSetUpdateStrategy(&in.UpdateStrategy, &out.UpdateStrategy, s); err != nil { + return err + } + out.MinReadySeconds = in.MinReadySeconds + out.TemplateGeneration = in.TemplateGeneration + out.RevisionHistoryLimit = (*int32)(unsafe.Pointer(in.RevisionHistoryLimit)) + return nil +} + +// Convert_extensions_DaemonSetSpec_To_v1beta2_DaemonSetSpec is an autogenerated conversion function. +func Convert_extensions_DaemonSetSpec_To_v1beta2_DaemonSetSpec(in *extensions.DaemonSetSpec, out *v1beta2.DaemonSetSpec, s conversion.Scope) error { + return autoConvert_extensions_DaemonSetSpec_To_v1beta2_DaemonSetSpec(in, out, s) +} + +func autoConvert_v1beta2_DaemonSetStatus_To_extensions_DaemonSetStatus(in *v1beta2.DaemonSetStatus, out *extensions.DaemonSetStatus, s conversion.Scope) error { + out.CurrentNumberScheduled = in.CurrentNumberScheduled + out.NumberMisscheduled = in.NumberMisscheduled + out.DesiredNumberScheduled = in.DesiredNumberScheduled + out.NumberReady = in.NumberReady + out.ObservedGeneration = in.ObservedGeneration + out.UpdatedNumberScheduled = in.UpdatedNumberScheduled + out.NumberAvailable = in.NumberAvailable + out.NumberUnavailable = in.NumberUnavailable + out.CollisionCount = (*int64)(unsafe.Pointer(in.CollisionCount)) + return nil +} + +// Convert_v1beta2_DaemonSetStatus_To_extensions_DaemonSetStatus is an autogenerated conversion function. +func Convert_v1beta2_DaemonSetStatus_To_extensions_DaemonSetStatus(in *v1beta2.DaemonSetStatus, out *extensions.DaemonSetStatus, s conversion.Scope) error { + return autoConvert_v1beta2_DaemonSetStatus_To_extensions_DaemonSetStatus(in, out, s) +} + +func autoConvert_extensions_DaemonSetStatus_To_v1beta2_DaemonSetStatus(in *extensions.DaemonSetStatus, out *v1beta2.DaemonSetStatus, s conversion.Scope) error { + out.CurrentNumberScheduled = in.CurrentNumberScheduled + out.NumberMisscheduled = in.NumberMisscheduled + out.DesiredNumberScheduled = in.DesiredNumberScheduled + out.NumberReady = in.NumberReady + out.ObservedGeneration = in.ObservedGeneration + out.UpdatedNumberScheduled = in.UpdatedNumberScheduled + out.NumberAvailable = in.NumberAvailable + out.NumberUnavailable = in.NumberUnavailable + out.CollisionCount = (*int64)(unsafe.Pointer(in.CollisionCount)) + return nil +} + +// Convert_extensions_DaemonSetStatus_To_v1beta2_DaemonSetStatus is an autogenerated conversion function. +func Convert_extensions_DaemonSetStatus_To_v1beta2_DaemonSetStatus(in *extensions.DaemonSetStatus, out *v1beta2.DaemonSetStatus, s conversion.Scope) error { + return autoConvert_extensions_DaemonSetStatus_To_v1beta2_DaemonSetStatus(in, out, s) +} + +func autoConvert_v1beta2_DaemonSetUpdateStrategy_To_extensions_DaemonSetUpdateStrategy(in *v1beta2.DaemonSetUpdateStrategy, out *extensions.DaemonSetUpdateStrategy, s conversion.Scope) error { + out.Type = extensions.DaemonSetUpdateStrategyType(in.Type) + if in.RollingUpdate != nil { + in, out := &in.RollingUpdate, &out.RollingUpdate + *out = new(extensions.RollingUpdateDaemonSet) + if err := Convert_v1beta2_RollingUpdateDaemonSet_To_extensions_RollingUpdateDaemonSet(*in, *out, s); err != nil { + return err + } + } else { + out.RollingUpdate = nil + } + return nil +} + +// Convert_v1beta2_DaemonSetUpdateStrategy_To_extensions_DaemonSetUpdateStrategy is an autogenerated conversion function. +func Convert_v1beta2_DaemonSetUpdateStrategy_To_extensions_DaemonSetUpdateStrategy(in *v1beta2.DaemonSetUpdateStrategy, out *extensions.DaemonSetUpdateStrategy, s conversion.Scope) error { + return autoConvert_v1beta2_DaemonSetUpdateStrategy_To_extensions_DaemonSetUpdateStrategy(in, out, s) +} + +func autoConvert_extensions_DaemonSetUpdateStrategy_To_v1beta2_DaemonSetUpdateStrategy(in *extensions.DaemonSetUpdateStrategy, out *v1beta2.DaemonSetUpdateStrategy, s conversion.Scope) error { + out.Type = v1beta2.DaemonSetUpdateStrategyType(in.Type) + if in.RollingUpdate != nil { + in, out := &in.RollingUpdate, &out.RollingUpdate + *out = new(v1beta2.RollingUpdateDaemonSet) + if err := Convert_extensions_RollingUpdateDaemonSet_To_v1beta2_RollingUpdateDaemonSet(*in, *out, s); err != nil { + return err + } + } else { + out.RollingUpdate = nil + } + return nil +} + +// Convert_extensions_DaemonSetUpdateStrategy_To_v1beta2_DaemonSetUpdateStrategy is an autogenerated conversion function. +func Convert_extensions_DaemonSetUpdateStrategy_To_v1beta2_DaemonSetUpdateStrategy(in *extensions.DaemonSetUpdateStrategy, out *v1beta2.DaemonSetUpdateStrategy, s conversion.Scope) error { + return autoConvert_extensions_DaemonSetUpdateStrategy_To_v1beta2_DaemonSetUpdateStrategy(in, out, s) +} + +func autoConvert_v1beta2_Deployment_To_extensions_Deployment(in *v1beta2.Deployment, out *extensions.Deployment, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_DeploymentSpec_To_extensions_DeploymentSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta2_DeploymentStatus_To_extensions_DeploymentStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_Deployment_To_extensions_Deployment is an autogenerated conversion function. +func Convert_v1beta2_Deployment_To_extensions_Deployment(in *v1beta2.Deployment, out *extensions.Deployment, s conversion.Scope) error { + return autoConvert_v1beta2_Deployment_To_extensions_Deployment(in, out, s) +} + +func autoConvert_extensions_Deployment_To_v1beta2_Deployment(in *extensions.Deployment, out *v1beta2.Deployment, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_extensions_DeploymentSpec_To_v1beta2_DeploymentSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_extensions_DeploymentStatus_To_v1beta2_DeploymentStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_extensions_Deployment_To_v1beta2_Deployment is an autogenerated conversion function. +func Convert_extensions_Deployment_To_v1beta2_Deployment(in *extensions.Deployment, out *v1beta2.Deployment, s conversion.Scope) error { + return autoConvert_extensions_Deployment_To_v1beta2_Deployment(in, out, s) +} + +func autoConvert_v1beta2_DeploymentCondition_To_extensions_DeploymentCondition(in *v1beta2.DeploymentCondition, out *extensions.DeploymentCondition, s conversion.Scope) error { + out.Type = extensions.DeploymentConditionType(in.Type) + out.Status = api.ConditionStatus(in.Status) + out.LastUpdateTime = in.LastUpdateTime + out.LastTransitionTime = in.LastTransitionTime + out.Reason = in.Reason + out.Message = in.Message + return nil +} + +// Convert_v1beta2_DeploymentCondition_To_extensions_DeploymentCondition is an autogenerated conversion function. +func Convert_v1beta2_DeploymentCondition_To_extensions_DeploymentCondition(in *v1beta2.DeploymentCondition, out *extensions.DeploymentCondition, s conversion.Scope) error { + return autoConvert_v1beta2_DeploymentCondition_To_extensions_DeploymentCondition(in, out, s) +} + +func autoConvert_extensions_DeploymentCondition_To_v1beta2_DeploymentCondition(in *extensions.DeploymentCondition, out *v1beta2.DeploymentCondition, s conversion.Scope) error { + out.Type = v1beta2.DeploymentConditionType(in.Type) + out.Status = core_v1.ConditionStatus(in.Status) + out.LastUpdateTime = in.LastUpdateTime + out.LastTransitionTime = in.LastTransitionTime + out.Reason = in.Reason + out.Message = in.Message + return nil +} + +// Convert_extensions_DeploymentCondition_To_v1beta2_DeploymentCondition is an autogenerated conversion function. +func Convert_extensions_DeploymentCondition_To_v1beta2_DeploymentCondition(in *extensions.DeploymentCondition, out *v1beta2.DeploymentCondition, s conversion.Scope) error { + return autoConvert_extensions_DeploymentCondition_To_v1beta2_DeploymentCondition(in, out, s) +} + +func autoConvert_v1beta2_DeploymentList_To_extensions_DeploymentList(in *v1beta2.DeploymentList, out *extensions.DeploymentList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]extensions.Deployment, len(*in)) + for i := range *in { + if err := Convert_v1beta2_Deployment_To_extensions_Deployment(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta2_DeploymentList_To_extensions_DeploymentList is an autogenerated conversion function. +func Convert_v1beta2_DeploymentList_To_extensions_DeploymentList(in *v1beta2.DeploymentList, out *extensions.DeploymentList, s conversion.Scope) error { + return autoConvert_v1beta2_DeploymentList_To_extensions_DeploymentList(in, out, s) +} + +func autoConvert_extensions_DeploymentList_To_v1beta2_DeploymentList(in *extensions.DeploymentList, out *v1beta2.DeploymentList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta2.Deployment, len(*in)) + for i := range *in { + if err := Convert_extensions_Deployment_To_v1beta2_Deployment(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = make([]v1beta2.Deployment, 0) + } + return nil +} + +// Convert_extensions_DeploymentList_To_v1beta2_DeploymentList is an autogenerated conversion function. +func Convert_extensions_DeploymentList_To_v1beta2_DeploymentList(in *extensions.DeploymentList, out *v1beta2.DeploymentList, s conversion.Scope) error { + return autoConvert_extensions_DeploymentList_To_v1beta2_DeploymentList(in, out, s) +} + +func autoConvert_v1beta2_DeploymentRollback_To_extensions_DeploymentRollback(in *v1beta2.DeploymentRollback, out *extensions.DeploymentRollback, s conversion.Scope) error { + out.Name = in.Name + out.UpdatedAnnotations = *(*map[string]string)(unsafe.Pointer(&in.UpdatedAnnotations)) + if err := Convert_v1beta2_RollbackConfig_To_extensions_RollbackConfig(&in.RollbackTo, &out.RollbackTo, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_DeploymentRollback_To_extensions_DeploymentRollback is an autogenerated conversion function. +func Convert_v1beta2_DeploymentRollback_To_extensions_DeploymentRollback(in *v1beta2.DeploymentRollback, out *extensions.DeploymentRollback, s conversion.Scope) error { + return autoConvert_v1beta2_DeploymentRollback_To_extensions_DeploymentRollback(in, out, s) +} + +func autoConvert_extensions_DeploymentRollback_To_v1beta2_DeploymentRollback(in *extensions.DeploymentRollback, out *v1beta2.DeploymentRollback, s conversion.Scope) error { + out.Name = in.Name + out.UpdatedAnnotations = *(*map[string]string)(unsafe.Pointer(&in.UpdatedAnnotations)) + if err := Convert_extensions_RollbackConfig_To_v1beta2_RollbackConfig(&in.RollbackTo, &out.RollbackTo, s); err != nil { + return err + } + return nil +} + +// Convert_extensions_DeploymentRollback_To_v1beta2_DeploymentRollback is an autogenerated conversion function. +func Convert_extensions_DeploymentRollback_To_v1beta2_DeploymentRollback(in *extensions.DeploymentRollback, out *v1beta2.DeploymentRollback, s conversion.Scope) error { + return autoConvert_extensions_DeploymentRollback_To_v1beta2_DeploymentRollback(in, out, s) +} + +func autoConvert_v1beta2_DeploymentSpec_To_extensions_DeploymentSpec(in *v1beta2.DeploymentSpec, out *extensions.DeploymentSpec, s conversion.Scope) error { + if err := v1.Convert_Pointer_int32_To_int32(&in.Replicas, &out.Replicas, s); err != nil { + return err + } + out.Selector = (*v1.LabelSelector)(unsafe.Pointer(in.Selector)) + if err := api_v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err + } + if err := Convert_v1beta2_DeploymentStrategy_To_extensions_DeploymentStrategy(&in.Strategy, &out.Strategy, s); err != nil { + return err + } + out.MinReadySeconds = in.MinReadySeconds + out.RevisionHistoryLimit = (*int32)(unsafe.Pointer(in.RevisionHistoryLimit)) + out.Paused = in.Paused + out.RollbackTo = (*extensions.RollbackConfig)(unsafe.Pointer(in.RollbackTo)) + out.ProgressDeadlineSeconds = (*int32)(unsafe.Pointer(in.ProgressDeadlineSeconds)) + return nil +} + +func autoConvert_extensions_DeploymentSpec_To_v1beta2_DeploymentSpec(in *extensions.DeploymentSpec, out *v1beta2.DeploymentSpec, s conversion.Scope) error { + if err := v1.Convert_int32_To_Pointer_int32(&in.Replicas, &out.Replicas, s); err != nil { + return err + } + out.Selector = (*v1.LabelSelector)(unsafe.Pointer(in.Selector)) + if err := api_v1.Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err + } + if err := Convert_extensions_DeploymentStrategy_To_v1beta2_DeploymentStrategy(&in.Strategy, &out.Strategy, s); err != nil { + return err + } + out.MinReadySeconds = in.MinReadySeconds + out.RevisionHistoryLimit = (*int32)(unsafe.Pointer(in.RevisionHistoryLimit)) + out.Paused = in.Paused + out.RollbackTo = (*v1beta2.RollbackConfig)(unsafe.Pointer(in.RollbackTo)) + out.ProgressDeadlineSeconds = (*int32)(unsafe.Pointer(in.ProgressDeadlineSeconds)) + return nil +} + +func autoConvert_v1beta2_DeploymentStatus_To_extensions_DeploymentStatus(in *v1beta2.DeploymentStatus, out *extensions.DeploymentStatus, s conversion.Scope) error { + out.ObservedGeneration = in.ObservedGeneration + out.Replicas = in.Replicas + out.UpdatedReplicas = in.UpdatedReplicas + out.ReadyReplicas = in.ReadyReplicas + out.AvailableReplicas = in.AvailableReplicas + out.UnavailableReplicas = in.UnavailableReplicas + out.Conditions = *(*[]extensions.DeploymentCondition)(unsafe.Pointer(&in.Conditions)) + out.CollisionCount = (*int64)(unsafe.Pointer(in.CollisionCount)) + return nil +} + +// Convert_v1beta2_DeploymentStatus_To_extensions_DeploymentStatus is an autogenerated conversion function. +func Convert_v1beta2_DeploymentStatus_To_extensions_DeploymentStatus(in *v1beta2.DeploymentStatus, out *extensions.DeploymentStatus, s conversion.Scope) error { + return autoConvert_v1beta2_DeploymentStatus_To_extensions_DeploymentStatus(in, out, s) +} + +func autoConvert_extensions_DeploymentStatus_To_v1beta2_DeploymentStatus(in *extensions.DeploymentStatus, out *v1beta2.DeploymentStatus, s conversion.Scope) error { + out.ObservedGeneration = in.ObservedGeneration + out.Replicas = in.Replicas + out.UpdatedReplicas = in.UpdatedReplicas + out.ReadyReplicas = in.ReadyReplicas + out.AvailableReplicas = in.AvailableReplicas + out.UnavailableReplicas = in.UnavailableReplicas + out.Conditions = *(*[]v1beta2.DeploymentCondition)(unsafe.Pointer(&in.Conditions)) + out.CollisionCount = (*int64)(unsafe.Pointer(in.CollisionCount)) + return nil +} + +// Convert_extensions_DeploymentStatus_To_v1beta2_DeploymentStatus is an autogenerated conversion function. +func Convert_extensions_DeploymentStatus_To_v1beta2_DeploymentStatus(in *extensions.DeploymentStatus, out *v1beta2.DeploymentStatus, s conversion.Scope) error { + return autoConvert_extensions_DeploymentStatus_To_v1beta2_DeploymentStatus(in, out, s) +} + +func autoConvert_v1beta2_DeploymentStrategy_To_extensions_DeploymentStrategy(in *v1beta2.DeploymentStrategy, out *extensions.DeploymentStrategy, s conversion.Scope) error { + out.Type = extensions.DeploymentStrategyType(in.Type) + if in.RollingUpdate != nil { + in, out := &in.RollingUpdate, &out.RollingUpdate + *out = new(extensions.RollingUpdateDeployment) + if err := Convert_v1beta2_RollingUpdateDeployment_To_extensions_RollingUpdateDeployment(*in, *out, s); err != nil { + return err + } + } else { + out.RollingUpdate = nil + } + return nil +} + +func autoConvert_extensions_DeploymentStrategy_To_v1beta2_DeploymentStrategy(in *extensions.DeploymentStrategy, out *v1beta2.DeploymentStrategy, s conversion.Scope) error { + out.Type = v1beta2.DeploymentStrategyType(in.Type) + if in.RollingUpdate != nil { + in, out := &in.RollingUpdate, &out.RollingUpdate + *out = new(v1beta2.RollingUpdateDeployment) + if err := Convert_extensions_RollingUpdateDeployment_To_v1beta2_RollingUpdateDeployment(*in, *out, s); err != nil { + return err + } + } else { + out.RollingUpdate = nil + } + return nil +} + +func autoConvert_v1beta2_ReplicaSet_To_extensions_ReplicaSet(in *v1beta2.ReplicaSet, out *extensions.ReplicaSet, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_ReplicaSetSpec_To_extensions_ReplicaSetSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta2_ReplicaSetStatus_To_extensions_ReplicaSetStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_ReplicaSet_To_extensions_ReplicaSet is an autogenerated conversion function. +func Convert_v1beta2_ReplicaSet_To_extensions_ReplicaSet(in *v1beta2.ReplicaSet, out *extensions.ReplicaSet, s conversion.Scope) error { + return autoConvert_v1beta2_ReplicaSet_To_extensions_ReplicaSet(in, out, s) +} + +func autoConvert_extensions_ReplicaSet_To_v1beta2_ReplicaSet(in *extensions.ReplicaSet, out *v1beta2.ReplicaSet, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_extensions_ReplicaSetSpec_To_v1beta2_ReplicaSetSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_extensions_ReplicaSetStatus_To_v1beta2_ReplicaSetStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_extensions_ReplicaSet_To_v1beta2_ReplicaSet is an autogenerated conversion function. +func Convert_extensions_ReplicaSet_To_v1beta2_ReplicaSet(in *extensions.ReplicaSet, out *v1beta2.ReplicaSet, s conversion.Scope) error { + return autoConvert_extensions_ReplicaSet_To_v1beta2_ReplicaSet(in, out, s) +} + +func autoConvert_v1beta2_ReplicaSetCondition_To_extensions_ReplicaSetCondition(in *v1beta2.ReplicaSetCondition, out *extensions.ReplicaSetCondition, s conversion.Scope) error { + out.Type = extensions.ReplicaSetConditionType(in.Type) + out.Status = api.ConditionStatus(in.Status) + out.LastTransitionTime = in.LastTransitionTime + out.Reason = in.Reason + out.Message = in.Message + return nil +} + +// Convert_v1beta2_ReplicaSetCondition_To_extensions_ReplicaSetCondition is an autogenerated conversion function. +func Convert_v1beta2_ReplicaSetCondition_To_extensions_ReplicaSetCondition(in *v1beta2.ReplicaSetCondition, out *extensions.ReplicaSetCondition, s conversion.Scope) error { + return autoConvert_v1beta2_ReplicaSetCondition_To_extensions_ReplicaSetCondition(in, out, s) +} + +func autoConvert_extensions_ReplicaSetCondition_To_v1beta2_ReplicaSetCondition(in *extensions.ReplicaSetCondition, out *v1beta2.ReplicaSetCondition, s conversion.Scope) error { + out.Type = v1beta2.ReplicaSetConditionType(in.Type) + out.Status = core_v1.ConditionStatus(in.Status) + out.LastTransitionTime = in.LastTransitionTime + out.Reason = in.Reason + out.Message = in.Message + return nil +} + +// Convert_extensions_ReplicaSetCondition_To_v1beta2_ReplicaSetCondition is an autogenerated conversion function. +func Convert_extensions_ReplicaSetCondition_To_v1beta2_ReplicaSetCondition(in *extensions.ReplicaSetCondition, out *v1beta2.ReplicaSetCondition, s conversion.Scope) error { + return autoConvert_extensions_ReplicaSetCondition_To_v1beta2_ReplicaSetCondition(in, out, s) +} + +func autoConvert_v1beta2_ReplicaSetList_To_extensions_ReplicaSetList(in *v1beta2.ReplicaSetList, out *extensions.ReplicaSetList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]extensions.ReplicaSet, len(*in)) + for i := range *in { + if err := Convert_v1beta2_ReplicaSet_To_extensions_ReplicaSet(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta2_ReplicaSetList_To_extensions_ReplicaSetList is an autogenerated conversion function. +func Convert_v1beta2_ReplicaSetList_To_extensions_ReplicaSetList(in *v1beta2.ReplicaSetList, out *extensions.ReplicaSetList, s conversion.Scope) error { + return autoConvert_v1beta2_ReplicaSetList_To_extensions_ReplicaSetList(in, out, s) +} + +func autoConvert_extensions_ReplicaSetList_To_v1beta2_ReplicaSetList(in *extensions.ReplicaSetList, out *v1beta2.ReplicaSetList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta2.ReplicaSet, len(*in)) + for i := range *in { + if err := Convert_extensions_ReplicaSet_To_v1beta2_ReplicaSet(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = make([]v1beta2.ReplicaSet, 0) + } + return nil +} + +// Convert_extensions_ReplicaSetList_To_v1beta2_ReplicaSetList is an autogenerated conversion function. +func Convert_extensions_ReplicaSetList_To_v1beta2_ReplicaSetList(in *extensions.ReplicaSetList, out *v1beta2.ReplicaSetList, s conversion.Scope) error { + return autoConvert_extensions_ReplicaSetList_To_v1beta2_ReplicaSetList(in, out, s) +} + +func autoConvert_v1beta2_ReplicaSetSpec_To_extensions_ReplicaSetSpec(in *v1beta2.ReplicaSetSpec, out *extensions.ReplicaSetSpec, s conversion.Scope) error { + if err := v1.Convert_Pointer_int32_To_int32(&in.Replicas, &out.Replicas, s); err != nil { + return err + } + out.MinReadySeconds = in.MinReadySeconds + out.Selector = (*v1.LabelSelector)(unsafe.Pointer(in.Selector)) + if err := api_v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err + } + return nil +} + +func autoConvert_extensions_ReplicaSetSpec_To_v1beta2_ReplicaSetSpec(in *extensions.ReplicaSetSpec, out *v1beta2.ReplicaSetSpec, s conversion.Scope) error { + if err := v1.Convert_int32_To_Pointer_int32(&in.Replicas, &out.Replicas, s); err != nil { + return err + } + out.MinReadySeconds = in.MinReadySeconds + out.Selector = (*v1.LabelSelector)(unsafe.Pointer(in.Selector)) + if err := api_v1.Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { + return err + } + return nil +} + +func autoConvert_v1beta2_ReplicaSetStatus_To_extensions_ReplicaSetStatus(in *v1beta2.ReplicaSetStatus, out *extensions.ReplicaSetStatus, s conversion.Scope) error { + out.Replicas = in.Replicas + out.FullyLabeledReplicas = in.FullyLabeledReplicas + out.ReadyReplicas = in.ReadyReplicas + out.AvailableReplicas = in.AvailableReplicas + out.ObservedGeneration = in.ObservedGeneration + out.Conditions = *(*[]extensions.ReplicaSetCondition)(unsafe.Pointer(&in.Conditions)) + return nil +} + +// Convert_v1beta2_ReplicaSetStatus_To_extensions_ReplicaSetStatus is an autogenerated conversion function. +func Convert_v1beta2_ReplicaSetStatus_To_extensions_ReplicaSetStatus(in *v1beta2.ReplicaSetStatus, out *extensions.ReplicaSetStatus, s conversion.Scope) error { + return autoConvert_v1beta2_ReplicaSetStatus_To_extensions_ReplicaSetStatus(in, out, s) +} + +func autoConvert_extensions_ReplicaSetStatus_To_v1beta2_ReplicaSetStatus(in *extensions.ReplicaSetStatus, out *v1beta2.ReplicaSetStatus, s conversion.Scope) error { + out.Replicas = in.Replicas + out.FullyLabeledReplicas = in.FullyLabeledReplicas + out.ReadyReplicas = in.ReadyReplicas + out.AvailableReplicas = in.AvailableReplicas + out.ObservedGeneration = in.ObservedGeneration + out.Conditions = *(*[]v1beta2.ReplicaSetCondition)(unsafe.Pointer(&in.Conditions)) + return nil +} + +// Convert_extensions_ReplicaSetStatus_To_v1beta2_ReplicaSetStatus is an autogenerated conversion function. +func Convert_extensions_ReplicaSetStatus_To_v1beta2_ReplicaSetStatus(in *extensions.ReplicaSetStatus, out *v1beta2.ReplicaSetStatus, s conversion.Scope) error { + return autoConvert_extensions_ReplicaSetStatus_To_v1beta2_ReplicaSetStatus(in, out, s) +} + +func autoConvert_v1beta2_RollbackConfig_To_extensions_RollbackConfig(in *v1beta2.RollbackConfig, out *extensions.RollbackConfig, s conversion.Scope) error { + out.Revision = in.Revision + return nil +} + +// Convert_v1beta2_RollbackConfig_To_extensions_RollbackConfig is an autogenerated conversion function. +func Convert_v1beta2_RollbackConfig_To_extensions_RollbackConfig(in *v1beta2.RollbackConfig, out *extensions.RollbackConfig, s conversion.Scope) error { + return autoConvert_v1beta2_RollbackConfig_To_extensions_RollbackConfig(in, out, s) +} + +func autoConvert_extensions_RollbackConfig_To_v1beta2_RollbackConfig(in *extensions.RollbackConfig, out *v1beta2.RollbackConfig, s conversion.Scope) error { + out.Revision = in.Revision + return nil +} + +// Convert_extensions_RollbackConfig_To_v1beta2_RollbackConfig is an autogenerated conversion function. +func Convert_extensions_RollbackConfig_To_v1beta2_RollbackConfig(in *extensions.RollbackConfig, out *v1beta2.RollbackConfig, s conversion.Scope) error { + return autoConvert_extensions_RollbackConfig_To_v1beta2_RollbackConfig(in, out, s) +} + +func autoConvert_v1beta2_RollingUpdateDaemonSet_To_extensions_RollingUpdateDaemonSet(in *v1beta2.RollingUpdateDaemonSet, out *extensions.RollingUpdateDaemonSet, s conversion.Scope) error { + // WARNING: in.MaxUnavailable requires manual conversion: inconvertible types (*k8s.io/apimachinery/pkg/util/intstr.IntOrString vs k8s.io/apimachinery/pkg/util/intstr.IntOrString) + return nil +} + +func autoConvert_extensions_RollingUpdateDaemonSet_To_v1beta2_RollingUpdateDaemonSet(in *extensions.RollingUpdateDaemonSet, out *v1beta2.RollingUpdateDaemonSet, s conversion.Scope) error { + // WARNING: in.MaxUnavailable requires manual conversion: inconvertible types (k8s.io/apimachinery/pkg/util/intstr.IntOrString vs *k8s.io/apimachinery/pkg/util/intstr.IntOrString) + return nil +} + +func autoConvert_v1beta2_RollingUpdateDeployment_To_extensions_RollingUpdateDeployment(in *v1beta2.RollingUpdateDeployment, out *extensions.RollingUpdateDeployment, s conversion.Scope) error { + // WARNING: in.MaxUnavailable requires manual conversion: inconvertible types (*k8s.io/apimachinery/pkg/util/intstr.IntOrString vs k8s.io/apimachinery/pkg/util/intstr.IntOrString) + // WARNING: in.MaxSurge requires manual conversion: inconvertible types (*k8s.io/apimachinery/pkg/util/intstr.IntOrString vs k8s.io/apimachinery/pkg/util/intstr.IntOrString) + return nil +} + +func autoConvert_extensions_RollingUpdateDeployment_To_v1beta2_RollingUpdateDeployment(in *extensions.RollingUpdateDeployment, out *v1beta2.RollingUpdateDeployment, s conversion.Scope) error { + // WARNING: in.MaxUnavailable requires manual conversion: inconvertible types (k8s.io/apimachinery/pkg/util/intstr.IntOrString vs *k8s.io/apimachinery/pkg/util/intstr.IntOrString) + // WARNING: in.MaxSurge requires manual conversion: inconvertible types (k8s.io/apimachinery/pkg/util/intstr.IntOrString vs *k8s.io/apimachinery/pkg/util/intstr.IntOrString) + return nil +} + func autoConvert_v1beta2_RollingUpdateStatefulSetStrategy_To_apps_RollingUpdateStatefulSetStrategy(in *v1beta2.RollingUpdateStatefulSetStrategy, out *apps.RollingUpdateStatefulSetStrategy, s conversion.Scope) error { if err := v1.Convert_Pointer_int32_To_int32(&in.Partition, &out.Partition, s); err != nil { return err @@ -79,6 +742,71 @@ func Convert_apps_RollingUpdateStatefulSetStrategy_To_v1beta2_RollingUpdateState return autoConvert_apps_RollingUpdateStatefulSetStrategy_To_v1beta2_RollingUpdateStatefulSetStrategy(in, out, s) } +func autoConvert_v1beta2_Scale_To_extensions_Scale(in *v1beta2.Scale, out *extensions.Scale, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_v1beta2_ScaleSpec_To_extensions_ScaleSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_v1beta2_ScaleStatus_To_extensions_ScaleStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_Scale_To_extensions_Scale is an autogenerated conversion function. +func Convert_v1beta2_Scale_To_extensions_Scale(in *v1beta2.Scale, out *extensions.Scale, s conversion.Scope) error { + return autoConvert_v1beta2_Scale_To_extensions_Scale(in, out, s) +} + +func autoConvert_extensions_Scale_To_v1beta2_Scale(in *extensions.Scale, out *v1beta2.Scale, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if err := Convert_extensions_ScaleSpec_To_v1beta2_ScaleSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + if err := Convert_extensions_ScaleStatus_To_v1beta2_ScaleStatus(&in.Status, &out.Status, s); err != nil { + return err + } + return nil +} + +// Convert_extensions_Scale_To_v1beta2_Scale is an autogenerated conversion function. +func Convert_extensions_Scale_To_v1beta2_Scale(in *extensions.Scale, out *v1beta2.Scale, s conversion.Scope) error { + return autoConvert_extensions_Scale_To_v1beta2_Scale(in, out, s) +} + +func autoConvert_v1beta2_ScaleSpec_To_extensions_ScaleSpec(in *v1beta2.ScaleSpec, out *extensions.ScaleSpec, s conversion.Scope) error { + out.Replicas = in.Replicas + return nil +} + +// Convert_v1beta2_ScaleSpec_To_extensions_ScaleSpec is an autogenerated conversion function. +func Convert_v1beta2_ScaleSpec_To_extensions_ScaleSpec(in *v1beta2.ScaleSpec, out *extensions.ScaleSpec, s conversion.Scope) error { + return autoConvert_v1beta2_ScaleSpec_To_extensions_ScaleSpec(in, out, s) +} + +func autoConvert_extensions_ScaleSpec_To_v1beta2_ScaleSpec(in *extensions.ScaleSpec, out *v1beta2.ScaleSpec, s conversion.Scope) error { + out.Replicas = in.Replicas + return nil +} + +// Convert_extensions_ScaleSpec_To_v1beta2_ScaleSpec is an autogenerated conversion function. +func Convert_extensions_ScaleSpec_To_v1beta2_ScaleSpec(in *extensions.ScaleSpec, out *v1beta2.ScaleSpec, s conversion.Scope) error { + return autoConvert_extensions_ScaleSpec_To_v1beta2_ScaleSpec(in, out, s) +} + +func autoConvert_v1beta2_ScaleStatus_To_extensions_ScaleStatus(in *v1beta2.ScaleStatus, out *extensions.ScaleStatus, s conversion.Scope) error { + out.Replicas = in.Replicas + // WARNING: in.Selector requires manual conversion: inconvertible types (map[string]string vs *k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector) + // WARNING: in.TargetSelector requires manual conversion: does not exist in peer-type + return nil +} + +func autoConvert_extensions_ScaleStatus_To_v1beta2_ScaleStatus(in *extensions.ScaleStatus, out *v1beta2.ScaleStatus, s conversion.Scope) error { + out.Replicas = in.Replicas + // WARNING: in.Selector requires manual conversion: inconvertible types (*k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector vs map[string]string) + return nil +} + func autoConvert_v1beta2_StatefulSet_To_apps_StatefulSet(in *v1beta2.StatefulSet, out *apps.StatefulSet, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_v1beta2_StatefulSetSpec_To_apps_StatefulSetSpec(&in.Spec, &out.Spec, s); err != nil { From 604dfb319775090f715fbf2270ded63ce7474f15 Mon Sep 17 00:00:00 2001 From: Timo Reimann Date: Sun, 18 Jun 2017 00:34:22 +0200 Subject: [PATCH 09/38] Relax restrictions on environment variable names. The POSIX standard restricts environment variable names to uppercase letters, digits, and the underscore character in shell contexts only. For generic application usage, it is stated that all other characters shall be tolerated. This change relaxes the rules to some degree. Namely, we stop requiring environment variable names to be strict C_IDENTIFIERS and start permitting lowercase, dot, and dash characters. Public container images using environment variable names beyond the shell-only context can benefit from this relaxation. Elasticsearch is one popular example. --- pkg/api/types.go | 2 +- pkg/api/validation/validation.go | 4 +- pkg/api/validation/validation_test.go | 47 +++++++++++++++---- pkg/kubectl/configmap_test.go | 2 +- pkg/kubectl/env_file.go | 2 +- pkg/kubectl/run.go | 2 +- pkg/kubectl/run_test.go | 5 ++ pkg/kubectl/secret_test.go | 2 +- pkg/kubelet/kubelet_pods.go | 4 +- pkg/kubelet/kubelet_pods_test.go | 12 ++--- .../pkg/util/validation/validation.go | 37 ++++++++++++--- 11 files changed, 88 insertions(+), 31 deletions(-) diff --git a/pkg/api/types.go b/pkg/api/types.go index 171bb1d828d..ef84c2794f4 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -1440,7 +1440,7 @@ type SecretKeySelector struct { // EnvFromSource represents the source of a set of ConfigMaps type EnvFromSource struct { - // An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER. + // An optional identifier to prepend to each key in the ConfigMap. // +optional Prefix string // The ConfigMap to select from. diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index f6bd510bfdd..d0f4eed9cee 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -1548,7 +1548,7 @@ func ValidateEnv(vars []api.EnvVar, fldPath *field.Path) field.ErrorList { if len(ev.Name) == 0 { allErrs = append(allErrs, field.Required(idxPath.Child("name"), "")) } else { - for _, msg := range validation.IsCIdentifier(ev.Name) { + for _, msg := range validation.IsEnvVarName(ev.Name) { allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), ev.Name, msg)) } } @@ -1637,7 +1637,7 @@ func ValidateEnvFrom(vars []api.EnvFromSource, fldPath *field.Path) field.ErrorL for i, ev := range vars { idxPath := fldPath.Index(i) if len(ev.Prefix) > 0 { - for _, msg := range validation.IsCIdentifier(ev.Prefix) { + for _, msg := range validation.IsEnvVarName(ev.Prefix) { allErrs = append(allErrs, field.Invalid(idxPath.Child("prefix"), ev.Prefix, msg)) } } diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index 6f0940d03e1..77908b03e5e 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -43,7 +43,7 @@ const ( maxLengthErrMsg = "must be no more than" namePartErrMsg = "name part must consist of" nameErrMsg = "a qualified name must consist of" - idErrMsg = "a valid C identifier must" + envVarNameErrMsg = "a valid environment variable name must consist of" ) func testVolume(name string, namespace string, spec api.PersistentVolumeSpec) *api.PersistentVolume { @@ -2575,6 +2575,8 @@ func TestValidateEnv(t *testing.T) { {Name: "ABC", Value: "value"}, {Name: "AbC_123", Value: "value"}, {Name: "abc", Value: ""}, + {Name: "a.b.c", Value: "value"}, + {Name: "a-b-c", Value: "value"}, { Name: "abc", ValueFrom: &api.EnvVarSource{ @@ -2676,9 +2678,24 @@ func TestValidateEnv(t *testing.T) { expectedError: "[0].name: Required value", }, { - name: "name not a C identifier", - envs: []api.EnvVar{{Name: "a.b.c"}}, - expectedError: `[0].name: Invalid value: "a.b.c": ` + idErrMsg, + name: "illegal character", + envs: []api.EnvVar{{Name: "a!b"}}, + expectedError: `[0].name: Invalid value: "a!b": ` + envVarNameErrMsg, + }, + { + name: "dot only", + envs: []api.EnvVar{{Name: "."}}, + expectedError: `[0].name: Invalid value: ".": must not be`, + }, + { + name: "double dots only", + envs: []api.EnvVar{{Name: ".."}}, + expectedError: `[0].name: Invalid value: "..": must not be`, + }, + { + name: "leading double dots", + envs: []api.EnvVar{{Name: "..abc"}}, + expectedError: `[0].name: Invalid value: "..abc": must not start with`, }, { name: "value and valueFrom specified", @@ -2897,6 +2914,12 @@ func TestValidateEnvFrom(t *testing.T) { LocalObjectReference: api.LocalObjectReference{Name: "abc"}, }, }, + { + Prefix: "a.b", + ConfigMapRef: &api.ConfigMapEnvSource{ + LocalObjectReference: api.LocalObjectReference{Name: "abc"}, + }, + }, { SecretRef: &api.SecretEnvSource{ LocalObjectReference: api.LocalObjectReference{Name: "abc"}, @@ -2908,6 +2931,12 @@ func TestValidateEnvFrom(t *testing.T) { LocalObjectReference: api.LocalObjectReference{Name: "abc"}, }, }, + { + Prefix: "a.b", + SecretRef: &api.SecretEnvSource{ + LocalObjectReference: api.LocalObjectReference{Name: "abc"}, + }, + }, } if errs := ValidateEnvFrom(successCase, field.NewPath("field")); len(errs) != 0 { t.Errorf("expected success: %v", errs) @@ -2942,12 +2971,12 @@ func TestValidateEnvFrom(t *testing.T) { name: "invalid prefix", envs: []api.EnvFromSource{ { - Prefix: "a.b", + Prefix: "a!b", ConfigMapRef: &api.ConfigMapEnvSource{ LocalObjectReference: api.LocalObjectReference{Name: "abc"}}, }, }, - expectedError: `field[0].prefix: Invalid value: "a.b": ` + idErrMsg, + expectedError: `field[0].prefix: Invalid value: "a!b": ` + envVarNameErrMsg, }, { name: "zero-length name", @@ -2973,12 +3002,12 @@ func TestValidateEnvFrom(t *testing.T) { name: "invalid prefix", envs: []api.EnvFromSource{ { - Prefix: "a.b", + Prefix: "a!b", SecretRef: &api.SecretEnvSource{ LocalObjectReference: api.LocalObjectReference{Name: "abc"}}, }, }, - expectedError: `field[0].prefix: Invalid value: "a.b": ` + idErrMsg, + expectedError: `field[0].prefix: Invalid value: "a!b": ` + envVarNameErrMsg, }, { name: "no refs", @@ -3374,7 +3403,7 @@ func TestValidateContainers(t *testing.T) { ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}, }, "invalid env var name": { - {Name: "abc", Image: "image", Env: []api.EnvVar{{Name: "ev.1"}}, ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}, + {Name: "abc", Image: "image", Env: []api.EnvVar{{Name: "ev!1"}}, ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}, }, "unknown volume name": { {Name: "abc", Image: "image", VolumeMounts: []api.VolumeMount{{Name: "anything", MountPath: "/foo"}}, diff --git a/pkg/kubectl/configmap_test.go b/pkg/kubectl/configmap_test.go index 5690c2e0f0a..b5c98b58f4c 100644 --- a/pkg/kubectl/configmap_test.go +++ b/pkg/kubectl/configmap_test.go @@ -157,7 +157,7 @@ func TestConfigMapGenerate(t *testing.T) { expectErr: true, }, { - setup: setupEnvFile("key.1=value1"), + setup: setupEnvFile("key#1=value1"), params: map[string]interface{}{ "name": "invalid_key", "from-env-file": "file.env", diff --git a/pkg/kubectl/env_file.go b/pkg/kubectl/env_file.go index ce24c710441..19598d5354a 100644 --- a/pkg/kubectl/env_file.go +++ b/pkg/kubectl/env_file.go @@ -55,7 +55,7 @@ func proccessEnvFileLine(line []byte, filePath string, data := strings.SplitN(string(line), "=", 2) key = data[0] - if errs := validation.IsCIdentifier(key); len(errs) != 0 { + if errs := validation.IsEnvVarName(key); len(errs) != 0 { return ``, ``, fmt.Errorf("%q is not a valid key name: %s", key, strings.Join(errs, ";")) } diff --git a/pkg/kubectl/run.go b/pkg/kubectl/run.go index e1f4549d62c..dbf9ba7d0c0 100644 --- a/pkg/kubectl/run.go +++ b/pkg/kubectl/run.go @@ -868,7 +868,7 @@ func parseEnvs(envArray []string) ([]v1.EnvVar, error) { if len(name) == 0 { return nil, fmt.Errorf("invalid env: %v", env) } - if len(validation.IsCIdentifier(name)) != 0 { + if len(validation.IsEnvVarName(name)) != 0 { return nil, fmt.Errorf("invalid env: %v", env) } envVar := v1.EnvVar{Name: name, Value: value} diff --git a/pkg/kubectl/run_test.go b/pkg/kubectl/run_test.go index 16a6e9423ab..114550ff3b2 100644 --- a/pkg/kubectl/run_test.go +++ b/pkg/kubectl/run_test.go @@ -1030,6 +1030,7 @@ func TestParseEnv(t *testing.T) { { envArray: []string{ "THIS_ENV=isOK", + "this.dotted.env=isOKToo", "HAS_COMMAS=foo,bar", "HAS_EQUALS=jJnro54iUu75xNy==", }, @@ -1038,6 +1039,10 @@ func TestParseEnv(t *testing.T) { Name: "THIS_ENV", Value: "isOK", }, + { + Name: "this.dotted.env", + Value: "isOKToo", + }, { Name: "HAS_COMMAS", Value: "foo,bar", diff --git a/pkg/kubectl/secret_test.go b/pkg/kubectl/secret_test.go index 309c7848d65..e7c1e7478be 100644 --- a/pkg/kubectl/secret_test.go +++ b/pkg/kubectl/secret_test.go @@ -157,7 +157,7 @@ func TestSecretGenerate(t *testing.T) { expectErr: true, }, { - setup: setupEnvFile("key.1=value1"), + setup: setupEnvFile("key#1=value1"), params: map[string]interface{}{ "name": "invalid_key", "from-env-file": "file.env", diff --git a/pkg/kubelet/kubelet_pods.go b/pkg/kubelet/kubelet_pods.go index a0651a680d9..ddc62038d03 100644 --- a/pkg/kubelet/kubelet_pods.go +++ b/pkg/kubelet/kubelet_pods.go @@ -472,7 +472,7 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container if len(envFrom.Prefix) > 0 { k = envFrom.Prefix + k } - if errMsgs := utilvalidation.IsCIdentifier(k); len(errMsgs) != 0 { + if errMsgs := utilvalidation.IsEnvVarName(k); len(errMsgs) != 0 { invalidKeys = append(invalidKeys, k) continue } @@ -507,7 +507,7 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container if len(envFrom.Prefix) > 0 { k = envFrom.Prefix + k } - if errMsgs := utilvalidation.IsCIdentifier(k); len(errMsgs) != 0 { + if errMsgs := utilvalidation.IsEnvVarName(k); len(errMsgs) != 0 { invalidKeys = append(invalidKeys, k) continue } diff --git a/pkg/kubelet/kubelet_pods_test.go b/pkg/kubelet/kubelet_pods_test.go index 16b8bf68b89..986de4fac8c 100644 --- a/pkg/kubelet/kubelet_pods_test.go +++ b/pkg/kubelet/kubelet_pods_test.go @@ -1219,14 +1219,14 @@ func TestMakeEnvironmentVariables(t *testing.T) { Name: "test-secret", }, Data: map[string][]byte{ - "1234": []byte("abc"), - "1z": []byte("abc"), - "key": []byte("value"), + "1234": []byte("abc"), + "1z": []byte("abc"), + "key.1": []byte("value"), }, }, expectedEnvs: []kubecontainer.EnvVar{ { - Name: "key", + Name: "key.1", Value: "value", }, }, @@ -1250,12 +1250,12 @@ func TestMakeEnvironmentVariables(t *testing.T) { Name: "test-secret", }, Data: map[string][]byte{ - "1234": []byte("abc"), + "1234.name": []byte("abc"), }, }, expectedEnvs: []kubecontainer.EnvVar{ { - Name: "p_1234", + Name: "p_1234.name", Value: "abc", }, }, diff --git a/staging/src/k8s.io/apimachinery/pkg/util/validation/validation.go b/staging/src/k8s.io/apimachinery/pkg/util/validation/validation.go index b1fcc570812..85ba8467bb8 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/validation/validation.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/validation/validation.go @@ -277,6 +277,22 @@ func IsHTTPHeaderName(value string) []string { return nil } +const envVarNameFmt = "[-._a-zA-Z][-._a-zA-Z0-9]*" +const envVarNameFmtErrMsg string = "a valid environment variable name must consist of alphabetic characters, digits, '_', '-', or '.', and must not start with a digit" + +var envVarNameRegexp = regexp.MustCompile("^" + envVarNameFmt + "$") + +// IsEnvVarName tests if a string is a valid environment variable name. +func IsEnvVarName(value string) []string { + var errs []string + if !envVarNameRegexp.MatchString(value) { + errs = append(errs, RegexError(envVarNameFmtErrMsg, envVarNameFmt, "my.env-name", "MY_ENV.NAME", "MyEnvName1")) + } + + errs = append(errs, hasChDirPrefix(value)...) + return errs +} + const configMapKeyFmt = `[-._a-zA-Z0-9]+` const configMapKeyErrMsg string = "a valid config key must consist of alphanumeric characters, '-', '_' or '.'" @@ -291,13 +307,7 @@ func IsConfigMapKey(value string) []string { if !configMapKeyRegexp.MatchString(value) { errs = append(errs, RegexError(configMapKeyErrMsg, configMapKeyFmt, "key.name", "KEY_NAME", "key-name")) } - if value == "." { - errs = append(errs, `must not be '.'`) - } else if value == ".." { - errs = append(errs, `must not be '..'`) - } else if strings.HasPrefix(value, "..") { - errs = append(errs, `must not start with '..'`) - } + errs = append(errs, hasChDirPrefix(value)...) return errs } @@ -341,3 +351,16 @@ func prefixEach(msgs []string, prefix string) []string { func InclusiveRangeError(lo, hi int) string { return fmt.Sprintf(`must be between %d and %d, inclusive`, lo, hi) } + +func hasChDirPrefix(value string) []string { + var errs []string + switch { + case value == ".": + errs = append(errs, `must not be '.'`) + case value == "..": + errs = append(errs, `must not be '..'`) + case strings.HasPrefix(value, ".."): + errs = append(errs, `must not start with '..'`) + } + return errs +} From 11e8f3a88dddcac5f83490eb44ff05a43e1ab113 Mon Sep 17 00:00:00 2001 From: FengyunPan Date: Sat, 29 Jul 2017 11:23:23 +0800 Subject: [PATCH 10/38] Filter duplicate ips or hostnames for ingress Fix issue: #48654 --- pkg/printers/internalversion/printers.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/printers/internalversion/printers.go b/pkg/printers/internalversion/printers.go index 32c80da9f7b..4d0834cb534 100644 --- a/pkg/printers/internalversion/printers.go +++ b/pkg/printers/internalversion/printers.go @@ -702,15 +702,16 @@ func printCronJobList(list *batch.CronJobList, options printers.PrintOptions) ([ // `wide` indicates whether the returned value is meant for --o=wide output. If not, it's clipped to 16 bytes. func loadBalancerStatusStringer(s api.LoadBalancerStatus, wide bool) string { ingress := s.Ingress - result := []string{} + result := sets.NewString() for i := range ingress { if ingress[i].IP != "" { - result = append(result, ingress[i].IP) + result.Insert(ingress[i].IP) } else if ingress[i].Hostname != "" { - result = append(result, ingress[i].Hostname) + result.Insert(ingress[i].Hostname) } } - r := strings.Join(result, ",") + + r := strings.Join(result.List(), ",") if !wide && len(r) > loadBalancerWidth { r = r[0:(loadBalancerWidth-3)] + "..." } From 376b5f8079556f88637739c2d30f5fc50ebdfda6 Mon Sep 17 00:00:00 2001 From: David Ashpole Date: Mon, 31 Jul 2017 10:40:13 -0700 Subject: [PATCH 11/38] ignore udp metrics in k8s --- pkg/kubelet/cadvisor/cadvisor_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/kubelet/cadvisor/cadvisor_linux.go b/pkg/kubelet/cadvisor/cadvisor_linux.go index cdbe96a27e4..751fed4341c 100644 --- a/pkg/kubelet/cadvisor/cadvisor_linux.go +++ b/pkg/kubelet/cadvisor/cadvisor_linux.go @@ -100,7 +100,7 @@ func New(address string, port uint, runtime string, rootPath string) (Interface, sysFs := sysfs.NewRealSysFs() // Create and start the cAdvisor container manager. - m, err := manager.New(memory.New(statsCacheDuration, nil), sysFs, maxHousekeepingInterval, allowDynamicHousekeeping, cadvisormetrics.MetricSet{cadvisormetrics.NetworkTcpUsageMetrics: struct{}{}}, http.DefaultClient) + m, err := manager.New(memory.New(statsCacheDuration, nil), sysFs, maxHousekeepingInterval, allowDynamicHousekeeping, cadvisormetrics.MetricSet{cadvisormetrics.NetworkTcpUsageMetrics: struct{}{}, cadvisormetrics.NetworkUdpUsageMetrics: struct{}{}}, http.DefaultClient) if err != nil { return nil, err } From 4c1879c171ec682e8571d5725c379e183564376c Mon Sep 17 00:00:00 2001 From: xiangpengzhao Date: Tue, 1 Aug 2017 20:13:05 +0800 Subject: [PATCH 12/38] Rename e2e sig framework files --- test/e2e/lifecycle/BUILD | 2 +- test/e2e/lifecycle/{sig.go => framework.go} | 0 test/e2e/node/BUILD | 2 +- test/e2e/node/{sig.go => framework.go} | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename test/e2e/lifecycle/{sig.go => framework.go} (100%) rename test/e2e/node/{sig.go => framework.go} (100%) diff --git a/test/e2e/lifecycle/BUILD b/test/e2e/lifecycle/BUILD index 348920c22a4..d39f01bc013 100644 --- a/test/e2e/lifecycle/BUILD +++ b/test/e2e/lifecycle/BUILD @@ -12,11 +12,11 @@ go_library( srcs = [ "addon_update.go", "cluster_upgrade.go", + "framework.go", "ha_master.go", "reboot.go", "resize_nodes.go", "restart.go", - "sig.go", ], tags = ["automanaged"], deps = [ diff --git a/test/e2e/lifecycle/sig.go b/test/e2e/lifecycle/framework.go similarity index 100% rename from test/e2e/lifecycle/sig.go rename to test/e2e/lifecycle/framework.go diff --git a/test/e2e/node/BUILD b/test/e2e/node/BUILD index d12ea9c1bba..12431fb8b82 100644 --- a/test/e2e/node/BUILD +++ b/test/e2e/node/BUILD @@ -11,11 +11,11 @@ go_library( name = "go_default_library", srcs = [ "apparmor.go", + "framework.go", "kubelet.go", "kubelet_perf.go", "nodeoutofdisk.go", "security_context.go", - "sig.go", ], tags = ["automanaged"], deps = [ diff --git a/test/e2e/node/sig.go b/test/e2e/node/framework.go similarity index 100% rename from test/e2e/node/sig.go rename to test/e2e/node/framework.go From aa1d47a3c61e59cfac32fd79d3e780352eaed154 Mon Sep 17 00:00:00 2001 From: Shimin Guo Date: Sat, 29 Jul 2017 09:29:23 +0000 Subject: [PATCH 13/38] Fix premature return --- .../pkg/util/strategicpatch/patch.go | 2 +- .../pkg/util/strategicpatch/patch_test.go | 78 ++++++++++++++++++- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go b/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go index 828df44351b..8884c738ed9 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go @@ -1138,7 +1138,7 @@ func mergePatchIntoOriginal(original, patch map[string]interface{}, t reflect.Ty return err } case !foundOriginal && !foundPatch: - return nil + continue } // Split all items into patch items and server-only items and then enforce the order. diff --git a/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch_test.go b/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch_test.go index 507c8cffa17..7f6372db6ad 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch_test.go @@ -5966,6 +5966,75 @@ retainKeysMergingList: retainKeysMergingList: - name: bar - name: foo +`), + }, + }, + { + Description: "delete and reorder in one list, reorder in another", + StrategicMergePatchRawTestCaseData: StrategicMergePatchRawTestCaseData{ + Original: []byte(` +mergingList: +- name: a + value: a +- name: b + value: b +mergeItemPtr: +- name: c + value: c +- name: d + value: d +`), + Current: []byte(` +mergingList: +- name: a + value: a +- name: b + value: b +mergeItemPtr: +- name: c + value: c +- name: d + value: d +`), + Modified: []byte(` +mergingList: +- name: b + value: b +mergeItemPtr: +- name: d + value: d +- name: c + value: c +`), + TwoWay: []byte(` +$setElementOrder/mergingList: +- name: b +$setElementOrder/mergeItemPtr: +- name: d +- name: c +mergingList: +- $patch: delete + name: a +`), + ThreeWay: []byte(` +$setElementOrder/mergingList: +- name: b +$setElementOrder/mergeItemPtr: +- name: d +- name: c +mergingList: +- $patch: delete + name: a +`), + Result: []byte(` +mergingList: +- name: b + value: b +mergeItemPtr: +- name: d + value: d +- name: c + value: c `), }, }, @@ -5993,9 +6062,12 @@ func TestStrategicMergePatch(t *testing.T) { testThreeWayPatch(t, c) } - for _, c := range strategicMergePatchRawTestCases { - testTwoWayPatchForRawTestCase(t, c) - testThreeWayPatchForRawTestCase(t, c) + // run multiple times to exercise different map traversal orders + for i := 0; i < 10; i++ { + for _, c := range strategicMergePatchRawTestCases { + testTwoWayPatchForRawTestCase(t, c) + testThreeWayPatchForRawTestCase(t, c) + } } } From 8ffc361564e68c8998630326fedd82c3ff415454 Mon Sep 17 00:00:00 2001 From: Jing Xu Date: Wed, 2 Aug 2017 14:35:39 -0700 Subject: [PATCH 14/38] AttachDisk should not call detach inside of Cinder volume provider This PR fixes #50038 which removes the detach call inside of AttachDisk. --- .../providers/openstack/openstack_volumes.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pkg/cloudprovider/providers/openstack/openstack_volumes.go b/pkg/cloudprovider/providers/openstack/openstack_volumes.go index 7f7b499bb77..f9a46140c81 100644 --- a/pkg/cloudprovider/providers/openstack/openstack_volumes.go +++ b/pkg/cloudprovider/providers/openstack/openstack_volumes.go @@ -230,11 +230,9 @@ func (os *OpenStack) AttachDisk(instanceID, volumeID string) (string, error) { glog.V(4).Infof("Disk %s is already attached to instance %s", volumeID, instanceID) return volume.ID, nil } - glog.V(2).Infof("Disk %s is attached to a different instance (%s), detaching", volumeID, volume.AttachedServerId) - err = os.DetachDisk(volume.AttachedServerId, volumeID) - if err != nil { - return "", err - } + errmsg := fmt.Sprintf("Disk %s is attached to a different instance (%s)", volumeID, volume.AttachedServerId) + glog.V(2).Infof(errmsg) + return "", errors.New(errmsg) } startTime := time.Now() From ce826dcd783333b048baaf9c23d25cb29d9f770d Mon Sep 17 00:00:00 2001 From: Klaus Ma Date: Fri, 4 Aug 2017 11:29:51 +0800 Subject: [PATCH 15/38] Removed un-used InodePressure condition. --- pkg/controller/daemon/daemon_controller_test.go | 2 -- staging/src/k8s.io/api/core/v1/types.go | 2 -- 2 files changed, 4 deletions(-) diff --git a/pkg/controller/daemon/daemon_controller_test.go b/pkg/controller/daemon/daemon_controller_test.go index 921cd6fc3dd..067ddd51ff4 100644 --- a/pkg/controller/daemon/daemon_controller_test.go +++ b/pkg/controller/daemon/daemon_controller_test.go @@ -1514,7 +1514,6 @@ func TestUpdateNode(t *testing.T) { {Type: v1.NodeMemoryPressure, Status: v1.ConditionFalse}, {Type: v1.NodeDiskPressure, Status: v1.ConditionFalse}, {Type: v1.NodeNetworkUnavailable, Status: v1.ConditionFalse}, - {Type: v1.NodeInodePressure, Status: v1.ConditionFalse}, } return node }(), @@ -1522,7 +1521,6 @@ func TestUpdateNode(t *testing.T) { node := newNode("node1", nil) node.Status.Conditions = []v1.NodeCondition{ {Type: v1.NodeOutOfDisk, Status: v1.ConditionTrue}, - {Type: v1.NodeInodePressure, Status: v1.ConditionFalse}, } return node }(), diff --git a/staging/src/k8s.io/api/core/v1/types.go b/staging/src/k8s.io/api/core/v1/types.go index c2c574e3446..35f9c416c0c 100644 --- a/staging/src/k8s.io/api/core/v1/types.go +++ b/staging/src/k8s.io/api/core/v1/types.go @@ -3510,8 +3510,6 @@ const ( NodeDiskPressure NodeConditionType = "DiskPressure" // NodeNetworkUnavailable means that network for the node is not correctly configured. NodeNetworkUnavailable NodeConditionType = "NetworkUnavailable" - // NodeInodePressure means the kubelet is under pressure due to insufficient available inodes. - NodeInodePressure NodeConditionType = "InodePressure" ) // NodeCondition contains condition information for a node. From f08f504a7ec5bc3f3f85b8cb372254d2b8766a1b Mon Sep 17 00:00:00 2001 From: Maciej Szulik Date: Thu, 29 Jun 2017 13:43:24 +0200 Subject: [PATCH 16/38] Fix swallowed errors in RS and deployment tests --- test/e2e/apimachinery/garbage_collector.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/e2e/apimachinery/garbage_collector.go b/test/e2e/apimachinery/garbage_collector.go index e12b3cb0674..b3dac90ed18 100644 --- a/test/e2e/apimachinery/garbage_collector.go +++ b/test/e2e/apimachinery/garbage_collector.go @@ -413,8 +413,8 @@ var _ = SIGDescribe("Garbage collector", func() { return len(rsList.Items) > 0, nil }) - if err == wait.ErrWaitTimeout { - err = fmt.Errorf("Failed to wait for the Deployment to create some ReplicaSet: %v", err) + if err != nil { + framework.Failf("Failed to wait for the Deployment to create some ReplicaSet: %v", err) } By("delete the deployment") @@ -464,8 +464,8 @@ var _ = SIGDescribe("Garbage collector", func() { return len(rsList.Items) > 0, nil }) - if err == wait.ErrWaitTimeout { - err = fmt.Errorf("Failed to wait for the Deployment to create some ReplicaSet: %v", err) + if err != nil { + framework.Failf("Failed to wait for the Deployment to create some ReplicaSet: %v", err) } By("delete the deployment") From 17f8c97fa0cd51c79c6b52ed101a89b96228edbd Mon Sep 17 00:00:00 2001 From: Maciej Szulik Date: Thu, 29 Jun 2017 13:42:50 +0200 Subject: [PATCH 17/38] Add e2e test for cronjob chained removal --- test/e2e/apimachinery/BUILD | 3 + test/e2e/apimachinery/garbage_collector.go | 125 ++++++++++++++++++++- 2 files changed, 124 insertions(+), 4 deletions(-) diff --git a/test/e2e/apimachinery/BUILD b/test/e2e/apimachinery/BUILD index 31ef898ed15..e9c7e7cadbb 100644 --- a/test/e2e/apimachinery/BUILD +++ b/test/e2e/apimachinery/BUILD @@ -33,6 +33,8 @@ go_library( "//vendor/github.com/onsi/ginkgo:go_default_library", "//vendor/github.com/onsi/gomega:go_default_library", "//vendor/k8s.io/api/admissionregistration/v1alpha1:go_default_library", + "//vendor/k8s.io/api/batch/v1:go_default_library", + "//vendor/k8s.io/api/batch/v2alpha1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", "//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library", @@ -43,6 +45,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1alpha1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", diff --git a/test/e2e/apimachinery/garbage_collector.go b/test/e2e/apimachinery/garbage_collector.go index b3dac90ed18..2942a0c9618 100644 --- a/test/e2e/apimachinery/garbage_collector.go +++ b/test/e2e/apimachinery/garbage_collector.go @@ -20,6 +20,8 @@ import ( "fmt" "time" + batchv1 "k8s.io/api/batch/v1" + batchv2alpha1 "k8s.io/api/batch/v2alpha1" "k8s.io/api/core/v1" v1beta1 "k8s.io/api/extensions/v1beta1" apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" @@ -28,6 +30,7 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apiserver/pkg/storage/names" @@ -46,6 +49,11 @@ func getForegroundOptions() *metav1.DeleteOptions { return &metav1.DeleteOptions{PropagationPolicy: &policy} } +func getBackgroundOptions() *metav1.DeleteOptions { + policy := metav1.DeletePropagationBackground + return &metav1.DeleteOptions{PropagationPolicy: &policy} +} + func getOrphanOptions() *metav1.DeleteOptions { var trueVar = true return &metav1.DeleteOptions{OrphanDependents: &trueVar} @@ -56,7 +64,11 @@ func getNonOrphanOptions() *metav1.DeleteOptions { return &metav1.DeleteOptions{OrphanDependents: &falseVar} } -var zero = int64(0) +var ( + zero = int64(0) + + CronJobGroupVersionResource = schema.GroupVersionResource{Group: batchv2alpha1.GroupName, Version: "v2alpha1", Resource: "cronjobs"} +) func getPodTemplateSpec(labels map[string]string) v1.PodTemplateSpec { return v1.PodTemplateSpec{ @@ -175,10 +187,10 @@ func newGCPod(name string) *v1.Pod { } } -// verifyRemainingObjects verifies if the number of the remaining replication +// verifyRemainingReplicationControllersPods verifies if the number of the remaining replication // controllers and pods are rcNum and podNum. It returns error if the // communication with the API server fails. -func verifyRemainingObjects(f *framework.Framework, clientSet clientset.Interface, rcNum, podNum int) (bool, error) { +func verifyRemainingReplicationControllersPods(f *framework.Framework, clientSet clientset.Interface, rcNum, podNum int) (bool, error) { rcClient := clientSet.Core().ReplicationControllers(f.Namespace.Name) pods, err := clientSet.Core().Pods(f.Namespace.Name).List(metav1.ListOptions{}) if err != nil { @@ -200,6 +212,42 @@ func verifyRemainingObjects(f *framework.Framework, clientSet clientset.Interfac return ret, nil } +// verifyRemainingCronJobsJobsPods verifies if the number of remaining cronjobs, +// jobs and pods. It returns error if the communication with the API server fails. +func verifyRemainingCronJobsJobsPods(f *framework.Framework, clientSet clientset.Interface, + cjNum, jobNum, podNum int) (bool, error) { + var ret = true + + cronJobs, err := f.ClientSet.BatchV2alpha1().CronJobs(f.Namespace.Name).List(metav1.ListOptions{}) + if err != nil { + return false, fmt.Errorf("Failed to list cronjobs: %v", err) + } + if len(cronJobs.Items) != cjNum { + ret = false + By(fmt.Sprintf("expected %d cronjobs, got %d cronjobs", cjNum, len(cronJobs.Items))) + } + + jobs, err := f.ClientSet.Batch().Jobs(f.Namespace.Name).List(metav1.ListOptions{}) + if err != nil { + return false, fmt.Errorf("Failed to list jobs: %v", err) + } + if len(jobs.Items) != jobNum { + ret = false + By(fmt.Sprintf("expected %d jobs, got %d jobs", jobNum, len(jobs.Items))) + } + + pods, err := f.ClientSet.Core().Pods(f.Namespace.Name).List(metav1.ListOptions{}) + if err != nil { + return false, fmt.Errorf("Failed to list pods: %v", err) + } + if len(pods.Items) != podNum { + ret = false + By(fmt.Sprintf("expected %d pods, got %d pods", podNum, len(pods.Items))) + } + + return ret, nil +} + func gatherMetrics(f *framework.Framework) { By("Gathering metrics") var summary framework.TestDataSummary @@ -217,6 +265,40 @@ func gatherMetrics(f *framework.Framework) { } } +func newCronJob(name, schedule string) *batchv2alpha1.CronJob { + parallelism := int32(1) + completions := int32(1) + return &batchv2alpha1.CronJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + TypeMeta: metav1.TypeMeta{ + Kind: "CronJob", + }, + Spec: batchv2alpha1.CronJobSpec{ + Schedule: schedule, + JobTemplate: batchv2alpha1.JobTemplateSpec{ + Spec: batchv1.JobSpec{ + Parallelism: ¶llelism, + Completions: &completions, + Template: v1.PodTemplateSpec{ + Spec: v1.PodSpec{ + RestartPolicy: v1.RestartPolicyOnFailure, + Containers: []v1.Container{ + { + Name: "c", + Image: "gcr.io/google_containers/busybox:1.24", + Command: []string{"sleep", "300"}, + }, + }, + }, + }, + }, + }, + }, + } +} + var _ = SIGDescribe("Garbage collector", func() { f := framework.NewDefaultFramework("gc") It("should delete pods created by rc when not orphaning", func() { @@ -259,7 +341,7 @@ var _ = SIGDescribe("Garbage collector", func() { By("wait for all pods to be garbage collected") // wait for the RCs and Pods to reach the expected numbers. if err := wait.Poll(5*time.Second, 60*time.Second, func() (bool, error) { - return verifyRemainingObjects(f, clientSet, 0, 0) + return verifyRemainingReplicationControllersPods(f, clientSet, 0, 0) }); err != nil { framework.Failf("failed to wait for all pods to be deleted: %v", err) remainingPods, err := podClient.List(metav1.ListOptions{}) @@ -849,4 +931,39 @@ var _ = SIGDescribe("Garbage collector", func() { } } }) + + It("should delete jobs and pods created by cronjob", func() { + framework.SkipIfMissingResource(f.ClientPool, CronJobGroupVersionResource, f.Namespace.Name) + + By("Create the cronjob") + cronJob := newCronJob("simple", "*/1 * * * ?") + cronJob, err := f.ClientSet.BatchV2alpha1().CronJobs(f.Namespace.Name).Create(cronJob) + Expect(err).NotTo(HaveOccurred()) + + By("Wait for the CronJob to create new Job") + err = wait.PollImmediate(500*time.Millisecond, 2*time.Minute, func() (bool, error) { + jobs, err := f.ClientSet.Batch().Jobs(f.Namespace.Name).List(metav1.ListOptions{}) + if err != nil { + return false, fmt.Errorf("Failed to list jobs: %v", err) + } + return len(jobs.Items) > 0, nil + }) + if err != nil { + framework.Failf("Failed to wait for the CronJob to create some Jobs: %v", err) + } + + By("Delete the cronjob") + if err := f.ClientSet.BatchV2alpha1().CronJobs(f.Namespace.Name).Delete(cronJob.Name, getBackgroundOptions()); err != nil { + framework.Failf("Failed to delete the CronJob: %v", err) + } + By("Verify if cronjob does not leave jobs nor pods behind") + err = wait.PollImmediate(500*time.Millisecond, 1*time.Minute, func() (bool, error) { + return verifyRemainingCronJobsJobsPods(f, f.ClientSet, 0, 0, 0) + }) + if err != nil { + framework.Failf("Failed to wait for all jobs and pods to be deleted: %v", err) + } + + gatherMetrics(f) + }) }) From 026a082a7ff2f94053c1cfb8382e8417e27fab84 Mon Sep 17 00:00:00 2001 From: Yang Guo Date: Mon, 31 Jul 2017 18:01:11 -0700 Subject: [PATCH 18/38] Add node e2e test for Docker's shared PID namespace --- test/e2e_node/BUILD | 5 +- test/e2e_node/docker_test.go | 73 +++++++++++++++++++ test/e2e_node/docker_util.go | 62 ++++++++++++++++ test/e2e_node/garbage_collector_test.go | 2 - .../e2e_node/jenkins/image-config-serial.yaml | 6 +- test/e2e_node/jenkins/image-config.yaml | 6 +- test/e2e_node/security_context_test.go | 20 +---- 7 files changed, 153 insertions(+), 21 deletions(-) create mode 100644 test/e2e_node/docker_test.go create mode 100644 test/e2e_node/docker_util.go diff --git a/test/e2e_node/BUILD b/test/e2e_node/BUILD index 57421ca310f..57c95b9c9c4 100644 --- a/test/e2e_node/BUILD +++ b/test/e2e_node/BUILD @@ -14,6 +14,7 @@ go_library( "benchmark_util.go", "container.go", "doc.go", + "docker_util.go", "gpus.go", "image_list.go", "node_problem_detector_linux.go", @@ -39,6 +40,8 @@ go_library( "//test/e2e/metrics:go_default_library", "//test/e2e/perftype:go_default_library", "//test/e2e_node/perftype:go_default_library", + "//vendor/github.com/blang/semver:go_default_library", + "//vendor/github.com/docker/docker/client:go_default_library", "//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/google/cadvisor/client/v2:go_default_library", "//vendor/github.com/google/cadvisor/info/v2:go_default_library", @@ -71,6 +74,7 @@ go_test( "critical_pod_test.go", "density_test.go", "disk_eviction_test.go", + "docker_test.go", "dockershim_checkpoint_test.go", "dynamic_kubelet_configuration_test.go", "e2e_node_suite_test.go", @@ -118,7 +122,6 @@ go_test( "//test/e2e_node/services:go_default_library", "//test/e2e_node/system:go_default_library", "//test/utils:go_default_library", - "//vendor/github.com/blang/semver:go_default_library", "//vendor/github.com/coreos/go-systemd/util:go_default_library", "//vendor/github.com/davecgh/go-spew/spew:go_default_library", "//vendor/github.com/golang/glog:go_default_library", diff --git a/test/e2e_node/docker_test.go b/test/e2e_node/docker_test.go new file mode 100644 index 00000000000..8c79604fb14 --- /dev/null +++ b/test/e2e_node/docker_test.go @@ -0,0 +1,73 @@ +/* +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 e2e_node + +import ( + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kubernetes/test/e2e/framework" + + . "github.com/onsi/ginkgo" +) + +var _ = framework.KubeDescribe("Docker features [Feature:Docker]", func() { + f := framework.NewDefaultFramework("docker-feature-test") + + BeforeEach(func() { + framework.RunIfContainerRuntimeIs("docker") + }) + + Context("when shared PID namespace is enabled", func() { + It("processes in different containers of the same pod should be able to see each other", func() { + // TODO(yguo0905): Change this test to run unless the runtime is + // Docker and its version is <1.13. + By("Check whether shared PID namespace is enabled.") + isEnabled, err := isSharedPIDNamespaceEnabled() + framework.ExpectNoError(err) + if !isEnabled { + framework.Skipf("Skipped because shared PID namespace is not enabled.") + } + + By("Create a pod with two containers.") + f.PodClient().CreateSync(&v1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "shared-pid-ns-test-pod"}, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "test-container-1", + Image: "gcr.io/google_containers/busybox:1.24", + Command: []string{"/bin/top"}, + }, + { + Name: "test-container-2", + Image: "gcr.io/google_containers/busybox:1.24", + Command: []string{"/bin/sleep"}, + Args: []string{"10000"}, + }, + }, + }, + }) + + By("Check if the process in one container is visible to the process in the other.") + pid1 := f.ExecCommandInContainer("shared-pid-ns-test-pod", "test-container-1", "/bin/pidof", "top") + pid2 := f.ExecCommandInContainer("shared-pid-ns-test-pod", "test-container-2", "/bin/pidof", "top") + if pid1 != pid2 { + framework.Failf("PIDs are not the same in different containers: test-container-1=%v, test-container-2=%v", pid1, pid2) + } + }) + }) +}) diff --git a/test/e2e_node/docker_util.go b/test/e2e_node/docker_util.go new file mode 100644 index 00000000000..b8bc2497083 --- /dev/null +++ b/test/e2e_node/docker_util.go @@ -0,0 +1,62 @@ +/* +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 e2e_node + +import ( + "context" + "fmt" + + "github.com/blang/semver" + "github.com/docker/docker/client" +) + +const ( + defaultDockerEndpoint = "unix:///var/run/docker.sock" +) + +// getDockerAPIVersion returns the Docker's API version. +func getDockerAPIVersion() (semver.Version, error) { + c, err := client.NewClient(defaultDockerEndpoint, "", nil, nil) + if err != nil { + return semver.Version{}, fmt.Errorf("failed to create docker client: %v", err) + } + version, err := c.ServerVersion(context.Background()) + if err != nil { + return semver.Version{}, fmt.Errorf("failed to get docker info: %v", err) + } + return semver.MustParse(version.APIVersion + ".0"), nil +} + +// isSharedPIDNamespaceEnabled returns true if the Docker version is 1.13.1+ +// (API version 1.26+), and false otherwise. +func isSharedPIDNamespaceEnabled() (bool, error) { + version, err := getDockerAPIVersion() + if err != nil { + return false, err + } + return version.GTE(semver.MustParse("1.26.0")), nil +} + +// isDockerNoNewPrivilegesSupported returns true if Docker version is 1.11+ +// (API version 1.23+), and false otherwise. +func isDockerNoNewPrivilegesSupported() (bool, error) { + version, err := getDockerAPIVersion() + if err != nil { + return false, err + } + return version.GTE(semver.MustParse("1.23.0")), nil +} diff --git a/test/e2e_node/garbage_collector_test.go b/test/e2e_node/garbage_collector_test.go index 12505349609..0bc4852ddd7 100644 --- a/test/e2e_node/garbage_collector_test.go +++ b/test/e2e_node/garbage_collector_test.go @@ -32,8 +32,6 @@ import ( ) const ( - defaultDockerEndpoint = "unix:///var/run/docker.sock" - //TODO (dashpole): Once dynamic config is possible, test different values for maxPerPodContainer and maxContainers // Currently using default values for maxPerPodContainer and maxTotalContainers maxPerPodContainer = 1 diff --git a/test/e2e_node/jenkins/image-config-serial.yaml b/test/e2e_node/jenkins/image-config-serial.yaml index 04991988a75..12da0f85f4e 100644 --- a/test/e2e_node/jenkins/image-config-serial.yaml +++ b/test/e2e_node/jenkins/image-config-serial.yaml @@ -12,7 +12,7 @@ images: containervm: image: e2e-node-containervm-v20161208-image # docker 1.11.2 project: kubernetes-node-e2e-images - gci: + cos-stable: image_regex: cos-stable-59-9460-64-0 # docker 1.11.2 project: cos-cloud metadata: "user-data= 1.11 thats when "no-new-privileges" was added - framework.Skipf("Skipping no_new_privs tests, docker version is < 1.11 it is %s", version.String()) + isSupported, err := isDockerNoNewPrivilegesSupported() + framework.ExpectNoError(err) + if !isSupported { + framework.Skipf("Skipping because no_new_privs is not supported in this docker") } } }) From a69697e6071c356d080b76aa3e8402271f2f056c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Sat, 5 Aug 2017 15:45:09 +0300 Subject: [PATCH 19/38] kubeadm: Add back labels for the Static Pod control plane --- cmd/kubeadm/app/phases/controlplane/BUILD | 1 + .../app/phases/controlplane/manifests.go | 3 ++ .../app/phases/controlplane/manifests_test.go | 40 ++++++++++++++----- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/cmd/kubeadm/app/phases/controlplane/BUILD b/cmd/kubeadm/app/phases/controlplane/BUILD index 4a3c9c8e1fb..97ca9e06e58 100644 --- a/cmd/kubeadm/app/phases/controlplane/BUILD +++ b/cmd/kubeadm/app/phases/controlplane/BUILD @@ -21,6 +21,7 @@ go_test( "//cmd/kubeadm/app/constants:go_default_library", "//pkg/util/version:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library", ], diff --git a/cmd/kubeadm/app/phases/controlplane/manifests.go b/cmd/kubeadm/app/phases/controlplane/manifests.go index 17e92ce64e5..ba861534468 100644 --- a/cmd/kubeadm/app/phases/controlplane/manifests.go +++ b/cmd/kubeadm/app/phases/controlplane/manifests.go @@ -166,6 +166,9 @@ func componentPod(container v1.Container, volumes []v1.Volume) v1.Pod { Name: container.Name, Namespace: metav1.NamespaceSystem, Annotations: map[string]string{kubetypes.CriticalPodAnnotationKey: ""}, + // The component and tier labels are useful for quickly identifying the control plane Pods when doing a .List() + // against Pods in the kube-system namespace. Can for example be used together with the WaitForPodsWithLabel function + Labels: map[string]string{"component": container.Name, "tier": "control-plane"}, }, Spec: v1.PodSpec{ Containers: []v1.Container{container}, diff --git a/cmd/kubeadm/app/phases/controlplane/manifests_test.go b/cmd/kubeadm/app/phases/controlplane/manifests_test.go index 25780b00c87..260dfc6b72a 100644 --- a/cmd/kubeadm/app/phases/controlplane/manifests_test.go +++ b/cmd/kubeadm/app/phases/controlplane/manifests_test.go @@ -26,6 +26,7 @@ import ( "testing" "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/yaml" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" @@ -179,22 +180,43 @@ func TestComponentProbe(t *testing.T) { func TestComponentPod(t *testing.T) { var tests = []struct { - n string + name string + expected v1.Pod }{ { - n: "foo", + name: "foo", + expected: v1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "kube-system", + Annotations: map[string]string{"scheduler.alpha.kubernetes.io/critical-pod": ""}, + Labels: map[string]string{"component": "foo", "tier": "control-plane"}, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "foo", + }, + }, + HostNetwork: true, + Volumes: []v1.Volume{}, + }, + }, }, } for _, rt := range tests { - c := v1.Container{Name: rt.n} - v := []v1.Volume{} - actual := componentPod(c, v) - if actual.ObjectMeta.Name != rt.n { + c := v1.Container{Name: rt.name} + actual := componentPod(c, []v1.Volume{}) + if !reflect.DeepEqual(rt.expected, actual) { t.Errorf( - "failed componentPod:\n\texpected: %s\n\t actual: %s", - rt.n, - actual.ObjectMeta.Name, + "failed componentPod:\n\texpected: %v\n\t actual: %v", + rt.expected, + actual, ) } } From 0bea0ca1d94745096548ce643dad80ed9c5b9504 Mon Sep 17 00:00:00 2001 From: Irfan Ur Rehman Date: Tue, 6 Dec 2016 00:29:40 +0530 Subject: [PATCH 20/38] [Federation] hpa controller --- federation/pkg/federatedtypes/BUILD | 10 +- federation/pkg/federatedtypes/deployment.go | 8 +- federation/pkg/federatedtypes/hpa.go | 927 ++++++++++++++++++ federation/pkg/federatedtypes/hpa_test.go | 262 +++++ federation/pkg/federatedtypes/replicaset.go | 10 +- federation/pkg/federatedtypes/scheduling.go | 36 +- .../federation-controller/sync/controller.go | 19 +- .../util/test/test_helper.go | 6 + 8 files changed, 1249 insertions(+), 29 deletions(-) create mode 100644 federation/pkg/federatedtypes/hpa.go create mode 100644 federation/pkg/federatedtypes/hpa_test.go diff --git a/federation/pkg/federatedtypes/BUILD b/federation/pkg/federatedtypes/BUILD index faa20fcda88..692c63eae15 100644 --- a/federation/pkg/federatedtypes/BUILD +++ b/federation/pkg/federatedtypes/BUILD @@ -10,11 +10,16 @@ load( go_test( name = "go_default_test", - srcs = ["scheduling_test.go"], + srcs = [ + "hpa_test.go", + "scheduling_test.go", + ], library = ":go_default_library", tags = ["automanaged"], deps = [ + "//federation/pkg/federation-controller/util/test:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library", + "//vendor/k8s.io/api/autoscaling/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", @@ -29,6 +34,7 @@ go_library( "configmap.go", "daemonset.go", "deployment.go", + "hpa.go", "namespace.go", "qualifiedname.go", "registry.go", @@ -48,12 +54,14 @@ go_library( "//pkg/api:go_default_library", "//pkg/controller/namespace/deletion:go_default_library", "//vendor/github.com/golang/glog:go_default_library", + "//vendor/k8s.io/api/autoscaling/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/client-go/dynamic:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", diff --git a/federation/pkg/federatedtypes/deployment.go b/federation/pkg/federatedtypes/deployment.go index af61f0f92bc..230501db0ef 100644 --- a/federation/pkg/federatedtypes/deployment.go +++ b/federation/pkg/federatedtypes/deployment.go @@ -40,16 +40,16 @@ func init() { } type DeploymentAdapter struct { - *schedulingAdapter + *replicaSchedulingAdapter client federationclientset.Interface } func NewDeploymentAdapter(client federationclientset.Interface, config *restclient.Config) FederatedTypeAdapter { - schedulingAdapter := schedulingAdapter{ + schedulingAdapter := replicaSchedulingAdapter{ preferencesAnnotationName: FedDeploymentPreferencesAnnotation, - updateStatusFunc: func(obj pkgruntime.Object, status interface{}) error { + updateStatusFunc: func(obj pkgruntime.Object, schedulingInfo interface{}) error { deployment := obj.(*extensionsv1.Deployment) - typedStatus := status.(ReplicaSchedulingStatus) + typedStatus := schedulingInfo.(*ReplicaSchedulingInfo).Status if typedStatus.Replicas != deployment.Status.Replicas || typedStatus.UpdatedReplicas != deployment.Status.UpdatedReplicas || typedStatus.ReadyReplicas != deployment.Status.ReadyReplicas || typedStatus.AvailableReplicas != deployment.Status.AvailableReplicas { deployment.Status = extensionsv1.DeploymentStatus{ diff --git a/federation/pkg/federatedtypes/hpa.go b/federation/pkg/federatedtypes/hpa.go new file mode 100644 index 00000000000..3479d7c4b3b --- /dev/null +++ b/federation/pkg/federatedtypes/hpa.go @@ -0,0 +1,927 @@ +/* +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 federatedtypes + +import ( + "time" + + "fmt" + autoscalingv1 "k8s.io/api/autoscaling/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + pkgruntime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/watch" + kubeclientset "k8s.io/client-go/kubernetes" + restclient "k8s.io/client-go/rest" + federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1" + federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" + fedutil "k8s.io/kubernetes/federation/pkg/federation-controller/util" +) + +const ( + HpaKind = "horizontalpodautoscaler" + HpaControllerName = "horizontalpodautoscalers" + // This is a tunable which does not change replica nums + // on an existing local hpa, before this timeout, if it + // did scale already (avoids thrashing of replicas around). + scaleForbiddenWindow = 5 * time.Minute + // This is used as the default min for hpa object submitted + // to federation, in a situation where the default is for + // some reason not present (Spec.MinReplicas == nil) + hpaMinReplicaDefault = int32(1) +) + +func init() { + RegisterFederatedType(HpaKind, HpaControllerName, []schema.GroupVersionResource{autoscalingv1.SchemeGroupVersion.WithResource(HpaControllerName)}, NewHpaAdapter) +} + +type HpaAdapter struct { + client federationclientset.Interface +} + +func NewHpaAdapter(client federationclientset.Interface, config *restclient.Config) FederatedTypeAdapter { + return &HpaAdapter{client: client} +} + +func (a *HpaAdapter) Kind() string { + return HpaKind +} + +func (a *HpaAdapter) ObjectType() pkgruntime.Object { + return &autoscalingv1.HorizontalPodAutoscaler{} +} + +func (a *HpaAdapter) IsExpectedType(obj interface{}) bool { + _, ok := obj.(*autoscalingv1.HorizontalPodAutoscaler) + return ok +} + +func (a *HpaAdapter) Copy(obj pkgruntime.Object) pkgruntime.Object { + hpa := obj.(*autoscalingv1.HorizontalPodAutoscaler) + return &autoscalingv1.HorizontalPodAutoscaler{ + ObjectMeta: fedutil.DeepCopyRelevantObjectMeta(hpa.ObjectMeta), + Spec: *fedutil.DeepCopyApiTypeOrPanic(&hpa.Spec).(*autoscalingv1.HorizontalPodAutoscalerSpec), + } +} + +func (a *HpaAdapter) Equivalent(obj1, obj2 pkgruntime.Object) bool { + return fedutil.ObjectMetaAndSpecEquivalent(obj1, obj2) +} + +func (a *HpaAdapter) QualifiedName(obj pkgruntime.Object) QualifiedName { + hpa := obj.(*autoscalingv1.HorizontalPodAutoscaler) + return QualifiedName{Namespace: hpa.Namespace, Name: hpa.Name} +} + +func (a *HpaAdapter) ObjectMeta(obj pkgruntime.Object) *metav1.ObjectMeta { + return &obj.(*autoscalingv1.HorizontalPodAutoscaler).ObjectMeta +} + +func (a *HpaAdapter) FedCreate(obj pkgruntime.Object) (pkgruntime.Object, error) { + hpa := obj.(*autoscalingv1.HorizontalPodAutoscaler) + return a.client.AutoscalingV1().HorizontalPodAutoscalers(hpa.Namespace).Create(hpa) +} + +func (a *HpaAdapter) FedDelete(qualifiedName QualifiedName, options *metav1.DeleteOptions) error { + return a.client.AutoscalingV1().HorizontalPodAutoscalers(qualifiedName.Namespace).Delete(qualifiedName.Name, options) +} + +func (a *HpaAdapter) FedGet(qualifiedName QualifiedName) (pkgruntime.Object, error) { + return a.client.AutoscalingV1().HorizontalPodAutoscalers(qualifiedName.Namespace).Get(qualifiedName.Name, metav1.GetOptions{}) +} + +func (a *HpaAdapter) FedList(namespace string, options metav1.ListOptions) (pkgruntime.Object, error) { + return a.client.AutoscalingV1().HorizontalPodAutoscalers(namespace).List(options) +} + +func (a *HpaAdapter) FedUpdate(obj pkgruntime.Object) (pkgruntime.Object, error) { + hpa := obj.(*autoscalingv1.HorizontalPodAutoscaler) + return a.client.AutoscalingV1().HorizontalPodAutoscalers(hpa.Namespace).Update(hpa) +} + +func (a *HpaAdapter) FedWatch(namespace string, options metav1.ListOptions) (watch.Interface, error) { + return a.client.AutoscalingV1().HorizontalPodAutoscalers(namespace).Watch(options) +} + +func (a *HpaAdapter) ClusterCreate(client kubeclientset.Interface, obj pkgruntime.Object) (pkgruntime.Object, error) { + hpa := obj.(*autoscalingv1.HorizontalPodAutoscaler) + return client.AutoscalingV1().HorizontalPodAutoscalers(hpa.Namespace).Create(hpa) +} + +func (a *HpaAdapter) ClusterDelete(client kubeclientset.Interface, qualifiedName QualifiedName, options *metav1.DeleteOptions) error { + return client.AutoscalingV1().HorizontalPodAutoscalers(qualifiedName.Namespace).Delete(qualifiedName.Name, options) +} + +func (a *HpaAdapter) ClusterGet(client kubeclientset.Interface, qualifiedName QualifiedName) (pkgruntime.Object, error) { + return client.AutoscalingV1().HorizontalPodAutoscalers(qualifiedName.Namespace).Get(qualifiedName.Name, metav1.GetOptions{}) +} + +func (a *HpaAdapter) ClusterList(client kubeclientset.Interface, namespace string, options metav1.ListOptions) (pkgruntime.Object, error) { + return client.AutoscalingV1().HorizontalPodAutoscalers(namespace).List(options) +} + +func (a *HpaAdapter) ClusterUpdate(client kubeclientset.Interface, obj pkgruntime.Object) (pkgruntime.Object, error) { + hpa := obj.(*autoscalingv1.HorizontalPodAutoscaler) + return client.AutoscalingV1().HorizontalPodAutoscalers(hpa.Namespace).Update(hpa) +} + +func (a *HpaAdapter) ClusterWatch(client kubeclientset.Interface, namespace string, options metav1.ListOptions) (watch.Interface, error) { + return client.AutoscalingV1().HorizontalPodAutoscalers(namespace).Watch(options) +} + +func (a *HpaAdapter) NewTestObject(namespace string) pkgruntime.Object { + var min int32 = 4 + var targetCPU int32 = 70 + return &autoscalingv1.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "test-hpa-", + Namespace: namespace, + }, + Spec: autoscalingv1.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{ + Kind: "replicaset", + Name: "myrs", + }, + MinReplicas: &min, + MaxReplicas: int32(10), + TargetCPUUtilizationPercentage: &targetCPU, + }, + } +} + +func (a *HpaAdapter) IsSchedulingAdapter() bool { + return true +} + +func (a *HpaAdapter) EquivalentIgnoringSchedule(obj1, obj2 pkgruntime.Object) bool { + hpa1 := obj1.(*autoscalingv1.HorizontalPodAutoscaler) + hpa2 := a.Copy(obj2).(*autoscalingv1.HorizontalPodAutoscaler) + if hpa1.Spec.MinReplicas == nil { + hpa2.Spec.MinReplicas = nil + } else if hpa2.Spec.MinReplicas == nil { + var r int32 = *hpa1.Spec.MinReplicas + hpa2.Spec.MinReplicas = &r + } else { + *hpa2.Spec.MinReplicas = *hpa1.Spec.MinReplicas + } + hpa2.Spec.MaxReplicas = hpa1.Spec.MaxReplicas + return fedutil.ObjectMetaAndSpecEquivalent(hpa1, hpa2) +} + +type replicaNums struct { + min int32 + max int32 +} + +type hpaFederatedStatus struct { + lastScaleTime *metav1.Time + // Indicates how many clusters have hpa/replicas. + // Used to average the cpu utilization which is + // reflected to the federation user. + count int32 + aggregateCPUUtilizationPercentage *int32 + currentReplicas int32 + desiredReplicas int32 +} + +type hpaSchedulingInfo struct { + scheduleState map[string]*replicaNums + fedStatus hpaFederatedStatus +} + +// List of cluster names. +type hpaLists struct { + // Stores names of those clusters which can offer min. + availableMin sets.String + // Stores names of those clusters which can offer max. + availableMax sets.String + // Stores names of those clusters which do not have hpa yet. + noHpa sets.String +} + +func (a *HpaAdapter) GetSchedule(obj pkgruntime.Object, key string, clusters []*federationapi.Cluster, informer fedutil.FederatedInformer) (interface{}, error) { + currentClusterObjs, err := getCurrentClusterObjs(informer, key, clusters) + if err != nil { + return nil, err + } + + // Initialise averaged cpu utilisation for this reconcile. + var ccup int32 = 0 + fedStatus := hpaFederatedStatus{ + aggregateCPUUtilizationPercentage: &ccup, + count: int32(0), + desiredReplicas: int32(0), + currentReplicas: int32(0), + } + fedHpa := obj.(*autoscalingv1.HorizontalPodAutoscaler) + // We assign the last known scale time here, which we update with + // the latest time from among all clusters in ScheduleObject() + if fedHpa.Status.LastScaleTime != nil { + t := metav1.NewTime(fedHpa.Status.LastScaleTime.Time) + fedStatus.lastScaleTime = &t + } + + return &hpaSchedulingInfo{ + scheduleState: getHpaScheduleState(obj, currentClusterObjs), + fedStatus: fedStatus, + }, nil +} + +func getCurrentClusterObjs(informer fedutil.FederatedInformer, key string, clusters []*federationapi.Cluster) (map[string]pkgruntime.Object, error) { + currentClusterObjs := make(map[string]pkgruntime.Object) + for _, cluster := range clusters { + clusterName := cluster.Name + clusterObj, found, err := informer.GetTargetStore().GetByKey(clusterName, key) + if err != nil { + return nil, err + } + currentClusterObjs[clusterName] = nil + if found { + currentClusterObjs[clusterName] = clusterObj.(pkgruntime.Object) + } + } + return currentClusterObjs, nil +} + +// The algorithm used for scheduling is briefed as below: +// +// 1. Find clusters which can offer max and min, if any (lists.availableMax and +// lists.availableMin) in one pass on all clusters. +// +// 2. Reduce the replicas (both min and max) if needed (situation when fedHpa +// has lesser replicas then all cluster local hpa replicas totalled together). +// In this step reduce first from those hpas which already have max (and min) +// reducible. Once such clusters are over and reduction still needed, reduce +// one at a time from all clusters, randomly. This step will ensure that the +// exceeding replicas in local hpas are reduced to match the fedHpa. +// This step would ideally be a noop in most cases because its rare that fedHpa +// would have lesser replicas then the cluster local total (probably when user +// forces update if fedHpa). +// +// 3. Distribute the replicas. In this step we have replicas to distribute (which +// are fed replicas exceeding the sum total of local cluster replicas). If clusters +// already have replicas, one replica from each cluster which can offer replicas +// (both for max and min) are also added to this replicas to distribute numbers (min +// and max). +// 3a. We first do a sub-pass to distribute to clusters which need replicas, considering +// those as clusters in crucial need of replicas. +// 3b. After previous sub-pass, if we still have replicas remaining, in the sub-pass +// we distribute to those clusters which do not yet have any hpa. +// 3c. After previous if we still have more to distribute, then we distribute to all +// clusters randomly, giving replica distribution count (rdc=total-fed-replicas/no-of-clusters) +// to each at a time. +// +// The above algorithm is run to first distribute max and then distribute min to those clusters +// which get max. +func getHpaScheduleState(fedObj pkgruntime.Object, currentObjs map[string]pkgruntime.Object) map[string]*replicaNums { + fedHpa := fedObj.(*autoscalingv1.HorizontalPodAutoscaler) + requestedMin := hpaMinReplicaDefault + if fedHpa.Spec.MinReplicas != nil { + requestedMin = *fedHpa.Spec.MinReplicas + } + requestedReplicas := replicaNums{ + min: requestedMin, + max: fedHpa.Spec.MaxReplicas, + } + // replica distribution count, per cluster + rdc := replicaNums{ + min: requestedReplicas.min / int32(len(currentObjs)), + max: requestedReplicas.max / int32(len(currentObjs)), + } + if rdc.min < 1 { + rdc.min = 1 + } + // TODO: Is there a better way? + // We need to cap the lowest limit of Max to 2, because in a + // situation like both min and max become 1 (same) for all clusters, + // no rebalancing would happen. + if rdc.max < 2 { + rdc.max = 2 + } + + // Pass 1: Analyse existing local hpa's if any. + // clusterLists holds the list of those clusters which can offer + // min and max replicas, to those which want them. + // For example new clusters joining the federation and/or + // those clusters which need to increase or reduce replicas + // beyond min/max limits. + // schedStatus currently have status of existing hpas. + // It will eventually have desired status for this reconcile. + clusterLists, currentReplicas, scheduleState := prepareForScheduling(currentObjs) + + remainingReplicas := replicaNums{ + min: requestedReplicas.min - currentReplicas.min, + max: requestedReplicas.max - currentReplicas.max, + } + + // Pass 2: reduction of replicas if needed ( situation that fedHpa updated replicas + // to lesser then existing). + // In this pass, we remain pessimistic and reduce one replica per cluster at a time. + if remainingReplicas.min < 0 { + excessMin := (remainingReplicas.min * int32(-1)) + remainingReplicas.min = reduceMinReplicas(excessMin, clusterLists.availableMin, scheduleState) + } + if remainingReplicas.max < 0 { + excessMax := (remainingReplicas.max * int32(-1)) + remainingReplicas.max = reduceMaxReplicas(excessMax, clusterLists.availableMax, scheduleState) + } + + toDistribute := replicaNums{ + min: remainingReplicas.min + int32(clusterLists.availableMin.Len()), + max: remainingReplicas.max + int32(clusterLists.availableMax.Len()), + } + + // Pass 3: Distribute Max and then Min. + // Here we first distribute max and then (in the next loop) + // distribute min into those clusters which already get the + // max fixed. + // In this process we might not meet the min limit and total of + // min limits might remain more then the requested federated min. + // This is partially because a min per cluster cannot be lesser + // then 1, but min could be requested as 1 at federation. + // Additionally we first increase replicas into those clusters + // which already have hpa's and are in a condition to increase. + // This will save cluster related resources for the user, such that + // if an already existing cluster can satisfy users request why send + // the workload to another. + // We then go ahead to give the replicas to those which do not + // have any hpa. In this pass however we try to ensure that all + // our Max are consumed in this reconcile. + distributeMaxReplicas(toDistribute.max, clusterLists, rdc, currentObjs, scheduleState) + + // We distribute min to those clusters which: + // 1 - can adjust min (our increase step would be only 1) + // 2 - which do not have this hpa and got max(increase step rdcMin) + // We might exhaust all min replicas here, with + // some clusters still needing them. We adjust this in finalise by + // assigning min replicas to 1 into those clusters which got max + // but min remains 0. + distributeMinReplicas(toDistribute.min, clusterLists, rdc, currentObjs, scheduleState) + + return finaliseScheduleState(scheduleState) +} + +func (a *HpaAdapter) ScheduleObject(cluster *federationapi.Cluster, clusterObj pkgruntime.Object, federationObjCopy pkgruntime.Object, schedulingInfo interface{}) (pkgruntime.Object, ScheduleAction, error) { + // Update federated status info + typedInfo := schedulingInfo.(*hpaSchedulingInfo) + if clusterObj != nil { + clusterHpa := clusterObj.(*autoscalingv1.HorizontalPodAutoscaler) + if clusterHpa.Status.CurrentCPUUtilizationPercentage != nil { + *typedInfo.fedStatus.aggregateCPUUtilizationPercentage += + (*clusterHpa.Status.CurrentCPUUtilizationPercentage * clusterHpa.Status.CurrentReplicas) + typedInfo.fedStatus.count += clusterHpa.Status.CurrentReplicas + } + if clusterHpa.Status.LastScaleTime != nil { + t := metav1.NewTime(clusterHpa.Status.LastScaleTime.Time) + if typedInfo.fedStatus.lastScaleTime != nil && + t.After(typedInfo.fedStatus.lastScaleTime.Time) { + typedInfo.fedStatus.lastScaleTime = &t + } + } + + typedInfo.fedStatus.currentReplicas += clusterHpa.Status.CurrentReplicas + typedInfo.fedStatus.desiredReplicas += clusterHpa.Status.DesiredReplicas + } + + // Update the cluster obj and the needed action on the cluster + clusterHpaState := typedInfo.scheduleState[cluster.Name] + desiredHpa := federationObjCopy.(*autoscalingv1.HorizontalPodAutoscaler) + if clusterHpaState != nil { + desiredHpa.Spec.MaxReplicas = clusterHpaState.max + if desiredHpa.Spec.MinReplicas == nil { + min := int32(0) + desiredHpa.Spec.MinReplicas = &min + } + *desiredHpa.Spec.MinReplicas = clusterHpaState.min + } + + var defaultAction ScheduleAction = "" + switch { + case clusterHpaState != nil && clusterObj != nil: + return desiredHpa, defaultAction, nil + case clusterHpaState != nil && clusterObj == nil: + return desiredHpa, ActionAdd, nil + case clusterHpaState == nil && clusterObj != nil: + return nil, ActionDelete, nil + } + return nil, defaultAction, nil +} + +func (a *HpaAdapter) UpdateFederatedStatus(obj pkgruntime.Object, schedulingInfo interface{}) error { + fedHpa := obj.(*autoscalingv1.HorizontalPodAutoscaler) + needUpdate, newFedHpaStatus := updateStatus(fedHpa, schedulingInfo.(*hpaSchedulingInfo).fedStatus) + if needUpdate { + fedHpa.Status = newFedHpaStatus + _, err := a.client.AutoscalingV1().HorizontalPodAutoscalers(fedHpa.Namespace).UpdateStatus(fedHpa) + if err != nil { + return fmt.Errorf("Error updating hpa: %s status in federation: %v", fedHpa.Name, err) + } + } + return nil +} + +func updateStatus(fedHpa *autoscalingv1.HorizontalPodAutoscaler, newStatus hpaFederatedStatus) (bool, autoscalingv1.HorizontalPodAutoscalerStatus) { + averageCPUUtilizationPercentage := int32(0) + // Average out the available current utilisation + if *newStatus.aggregateCPUUtilizationPercentage != 0 && newStatus.count != 0 { + averageCPUUtilizationPercentage = *newStatus.aggregateCPUUtilizationPercentage / newStatus.count + } + gen := fedHpa.Generation + newFedHpaStatus := autoscalingv1.HorizontalPodAutoscalerStatus{ObservedGeneration: &gen} + needUpdate := false + if (fedHpa.Status.CurrentCPUUtilizationPercentage == nil && + averageCPUUtilizationPercentage != 0) || + (fedHpa.Status.CurrentCPUUtilizationPercentage != nil && + averageCPUUtilizationPercentage != + *fedHpa.Status.CurrentCPUUtilizationPercentage) { + needUpdate = true + newFedHpaStatus.CurrentCPUUtilizationPercentage = &averageCPUUtilizationPercentage + } + if (fedHpa.Status.LastScaleTime == nil && newStatus.lastScaleTime != nil) || + (fedHpa.Status.LastScaleTime != nil && newStatus.lastScaleTime == nil) || + ((fedHpa.Status.LastScaleTime != nil && newStatus.lastScaleTime != nil) && + newStatus.lastScaleTime.After(fedHpa.Status.LastScaleTime.Time)) { + needUpdate = true + newFedHpaStatus.LastScaleTime = newStatus.lastScaleTime + } + if fedHpa.Status.DesiredReplicas != newStatus.desiredReplicas { + needUpdate = true + newFedHpaStatus.CurrentReplicas = newStatus.currentReplicas + } + if fedHpa.Status.CurrentReplicas != newStatus.currentReplicas { + needUpdate = true + newFedHpaStatus.DesiredReplicas = newStatus.desiredReplicas + } + return needUpdate, newFedHpaStatus +} + +// prepareForScheduling prepares the lists and totals from the +// existing objs. +// currentObjs has the list of all clusters, with obj as nil +// for those clusters which do not have hpa yet. +func prepareForScheduling(currentObjs map[string]pkgruntime.Object) (hpaLists, replicaNums, map[string]*replicaNums) { + lists := hpaLists{ + availableMax: sets.NewString(), + availableMin: sets.NewString(), + noHpa: sets.NewString(), + } + existingTotal := replicaNums{ + min: int32(0), + max: int32(0), + } + + scheduleState := make(map[string]*replicaNums) + for cluster, obj := range currentObjs { + if obj == nil { + lists.noHpa.Insert(cluster) + scheduleState[cluster] = nil + continue + } + + if maxReplicasReducible(obj) { + lists.availableMax.Insert(cluster) + } + if minReplicasReducible(obj) { + lists.availableMin.Insert(cluster) + } + + replicas := replicaNums{min: 0, max: 0} + scheduleState[cluster] = &replicas + if obj.(*autoscalingv1.HorizontalPodAutoscaler).Spec.MinReplicas != nil { + existingTotal.min += *obj.(*autoscalingv1.HorizontalPodAutoscaler).Spec.MinReplicas + replicas.min = *obj.(*autoscalingv1.HorizontalPodAutoscaler).Spec.MinReplicas + } + existingTotal.max += obj.(*autoscalingv1.HorizontalPodAutoscaler).Spec.MaxReplicas + replicas.max = obj.(*autoscalingv1.HorizontalPodAutoscaler).Spec.MaxReplicas + } + + return lists, existingTotal, scheduleState +} + +// Note: reduceMinReplicas and reduceMaxReplicas, look quite similar in flow +// and code, however there are subtle differences. They together can be made +// into 1 function with an arg governing the functionality difference and +// additional args (superset of args in both) as needed. Doing so however +// makes the logical flow quite less readable. They are thus left as 2 for +// readability. + +// reduceMinReplicas reduces the min replicas from existing clusters. +// At the end of the function excessMin should be 0 and the MinList +// and the scheduledReplicas properly updated in place. +func reduceMinReplicas(excessMin int32, availableMinList sets.String, scheduled map[string]*replicaNums) int32 { + if excessMin > 0 { + // first we try reducing from those clusters which already offer min + if availableMinList.Len() > 0 { + for _, cluster := range availableMinList.List() { + replicas := scheduled[cluster] + if replicas.min > 1 { + replicas.min-- + availableMinList.Delete(cluster) + excessMin-- + if excessMin <= 0 { + break + } + } + } + } + } + + // If we could not get needed replicas from already offered min above + // we abruptly start removing replicas from some/all clusters. + // Here we might make some min to 0 signalling that this hpa might be a + // candidate to be removed from this cluster altogether. + for excessMin > 0 { + for _, replicas := range scheduled { + if replicas != nil && + replicas.min > 0 { + replicas.min-- + excessMin-- + if excessMin <= 0 { + break + } + } + } + } + + return excessMin +} + +// reduceMaxReplicas reduces the max replicas from existing clusters. +// At the end of the function excessMax should be 0 and the MaxList +// and the scheduledReplicas properly updated in place. +func reduceMaxReplicas(excessMax int32, availableMaxList sets.String, scheduled map[string]*replicaNums) int32 { + if excessMax > 0 { + // first we try reducing from those clusters which already offer max + if availableMaxList.Len() > 0 { + for _, cluster := range availableMaxList.List() { + replicas := scheduled[cluster] + if replicas != nil && !((replicas.max - replicas.min) < 0) { + replicas.max-- + availableMaxList.Delete(cluster) + excessMax-- + if excessMax <= 0 { + break + } + } + } + } + } + // If we could not get needed replicas to reduce from already offered + // max above we abruptly start removing replicas from some/all clusters. + // Here we might make some max and min to 0, signalling that this hpa be + // removed from this cluster altogether + for excessMax > 0 { + for _, replicas := range scheduled { + if replicas != nil && + !((replicas.max - replicas.min) < 0) { + replicas.max-- + excessMax-- + if excessMax <= 0 { + break + } + } + } + } + + return excessMax +} + +// distributeMaxReplicas +// Takes input: +// toDistributeMax: number of replicas to distribute. +// lists: cluster name lists, which have clusters with available max, +// available min and those with no hpas yet. +// rdc: replicadistributioncount for max and min. +// currentObjs: list of current cluster hpas. +// scheduled: schedule state which will be updated in place. +func distributeMaxReplicas(toDistributeMax int32, lists hpaLists, rdc replicaNums, + currentObjs map[string]pkgruntime.Object, scheduled map[string]*replicaNums) int32 { + for cluster, replicas := range scheduled { + if toDistributeMax == 0 { + break + } + if replicas == nil { + continue + } + if maxReplicasNeeded(currentObjs[cluster]) { + replicas.max++ + if lists.availableMax.Len() > 0 { + popped, notEmpty := lists.availableMax.PopAny() + if notEmpty { + // Boundary checks have happened earlier in + // minReplicasReducible(). + scheduled[popped].max-- + } + } + // Any which ways utilise available map replicas + toDistributeMax-- + } + } + + // If we have new clusters where we can give our replicas, + // then give away all our replicas to the new clusters first. + if lists.noHpa.Len() > 0 { + for toDistributeMax > 0 { + for _, cluster := range lists.noHpa.UnsortedList() { + if scheduled[cluster] == nil { + scheduled[cluster] = &replicaNums{min: 0, max: 0} + } + replicas := scheduled[cluster] + // first give away max from clusters offering them + // this case especially helps getting hpa into newly joining + // clusters. + if lists.availableMax.Len() > 0 { + popped, notEmpty := lists.availableMax.PopAny() + if notEmpty { + // Boundary checks to reduce max have happened earlier in + // minReplicasReducible(). + replicas.max++ + scheduled[popped].max-- + toDistributeMax-- + continue + } + } + if toDistributeMax < rdc.max { + replicas.max += toDistributeMax + toDistributeMax = 0 + break + } + replicas.max += rdc.max + toDistributeMax -= rdc.max + } + } + } else { // we have no new clusters but if still have max replicas to distribute; + // just distribute all in current clusters. + for toDistributeMax > 0 { + for cluster, replicas := range scheduled { + if replicas == nil { + replicas = &replicaNums{min: 0, max: 0} + scheduled[cluster] = replicas + } + // First give away max from clusters offering them. + // This case especially helps getting hpa into newly joining + // clusters. + if lists.availableMax.Len() > 0 { + popped, notEmpty := lists.availableMax.PopAny() + if notEmpty { + // Boundary checks have happened earlier in + // minReplicasReducible(). + replicas.max++ + scheduled[popped].max-- + toDistributeMax-- + continue + } + } + if toDistributeMax < rdc.max { + replicas.max += toDistributeMax + toDistributeMax = 0 + break + } + replicas.max += rdc.max + toDistributeMax -= rdc.max + } + } + } + return toDistributeMax +} + +// distributeMinReplicas +// Takes input: +// toDistributeMin: number of replicas to distribute. +// lists: cluster name lists, which have clusters with available max, +// available min and those with no hpas yet. +// rdc: replicadistributioncount for max and min. +// currentObjs: list of current cluster hpas. +// scheduled: schedule state which will be updated in place. +func distributeMinReplicas(toDistributeMin int32, lists hpaLists, rdc replicaNums, + currentObjs map[string]pkgruntime.Object, scheduled map[string]*replicaNums) int32 { + for cluster, replicas := range scheduled { + if toDistributeMin == 0 { + break + } + // We have distriubted Max and thus scheduled might not be nil + // but probably current (what we got originally) is nil(no hpa) + if replicas == nil || currentObjs[cluster] == nil { + continue + } + if minReplicasIncreasable(currentObjs[cluster]) { + if lists.availableMin.Len() > 0 { + popped, notEmpty := lists.availableMin.PopAny() + if notEmpty { + // Boundary checks have happened earlier. + scheduled[popped].min-- + replicas.min++ + toDistributeMin-- + } + } + } + } + + if lists.noHpa.Len() > 0 { + // TODO: can this become an infinite loop? + for toDistributeMin > 0 { + for _, cluster := range lists.noHpa.UnsortedList() { + replicas := scheduled[cluster] + if replicas == nil { + // We did not get max here so this cluster + // remains without hpa + continue + } + var replicaNum int32 = 0 + if toDistributeMin < rdc.min { + replicaNum = toDistributeMin + } else { + replicaNum = rdc.min + } + if (replicas.max - replicaNum) < replicas.min { + // Cannot increase the min in this cluster + // as it will go beyond max + continue + } + if lists.availableMin.Len() > 0 { + popped, notEmpty := lists.availableMin.PopAny() + if notEmpty { + // Boundary checks have happened earlier. + scheduled[popped].min-- + replicas.min++ + toDistributeMin-- + continue + } + } + replicas.min += replicaNum + toDistributeMin -= replicaNum + } + } + } else { // we have no new clusters but if still have min replicas to distribute; + // just distribute all in current clusters. + for toDistributeMin > 0 { + for _, replicas := range scheduled { + if replicas == nil { + // We did not get max here so this cluster + // remains without hpa + continue + } + var replicaNum int32 = 0 + if toDistributeMin < rdc.min { + replicaNum = toDistributeMin + } else { + replicaNum = rdc.min + } + if (replicas.max - replicaNum) < replicas.min { + // Cannot increase the min in this cluster + // as it will go beyond max + continue + } + if lists.availableMin.Len() > 0 { + popped, notEmpty := lists.availableMin.PopAny() + if notEmpty { + // Boundary checks have happened earlier. + scheduled[popped].min-- + replicas.min++ + toDistributeMin-- + continue + } + } + replicas.min += replicaNum + toDistributeMin -= replicaNum + } + } + } + return toDistributeMin +} + +// finaliseScheduleState ensures that the minReplica count is made to 1 +// for those clusters which got max, but did not get min. This is because +// k8s hpa does not accept hpas with 0 min replicas. +// The replica num distribution can thus have more mins then fedHpa requested +// but its better then having all replicas go into one cluster (if fedHpa +// requested min=1 (which is the most usual case). +func finaliseScheduleState(scheduled map[string]*replicaNums) map[string]*replicaNums { + for _, replicas := range scheduled { + if (replicas != nil) && (replicas.min <= 0) && (replicas.max > 0) { + // Min total does not necessarily meet the federated min limit. + replicas.min = 1 + } + } + return scheduled +} + +// isPristine is used to determine if so far local controller has been +// able to really determine, what should be the desired replica number for +// this cluster. +// This is used to get hpas into those clusters which might join fresh, +// and so far other cluster hpas haven't really reached anywhere. +// TODO: There is a flaw here, that a just born object would also offer its +// replicas which can also lead to fast thrashing. +// The only better way is to either ensure that object creation time stamp is set +// and can be used authoritatively; or have another field on the local object +// which is mandatorily set on creation and can be used authoritatively. +// Should we abuse annotations again for this, or this can be a proper requirement? +func isPristine(hpa *autoscalingv1.HorizontalPodAutoscaler) bool { + if hpa.Status.LastScaleTime == nil && + hpa.Status.DesiredReplicas == 0 { + return true + } + return false +} + +// isScaleable tells if it already has been a reasonable amount of +// time since this hpa scaled. Its used to avoid fast thrashing. +func isScaleable(hpa *autoscalingv1.HorizontalPodAutoscaler) bool { + if hpa.Status.LastScaleTime == nil { + return false + } + t := hpa.Status.LastScaleTime.Add(scaleForbiddenWindow) + if t.After(time.Now()) { + return false + } + return true +} + +func maxReplicasReducible(obj pkgruntime.Object) bool { + hpa := obj.(*autoscalingv1.HorizontalPodAutoscaler) + if (hpa.Spec.MinReplicas != nil) && + (((hpa.Spec.MaxReplicas - 1) - *hpa.Spec.MinReplicas) < 0) { + return false + } + if isPristine(hpa) { + return true + } + if !isScaleable(hpa) { + return false + } + if (hpa.Status.DesiredReplicas < hpa.Status.CurrentReplicas) || + ((hpa.Status.DesiredReplicas == hpa.Status.CurrentReplicas) && + (hpa.Status.DesiredReplicas < hpa.Spec.MaxReplicas)) { + return true + } + return false +} + +// minReplicasReducible checks if this cluster (hpa) can offer replicas which are +// stuck here because of min limit. +// Its noteworthy, that min and max are adjusted separately, but if the replicas +// are not being used here, the max adjustment will lead it to become equal to min, +// but will not be able to scale down further and offer max to some other cluster +// which needs replicas. +func minReplicasReducible(obj pkgruntime.Object) bool { + hpa := obj.(*autoscalingv1.HorizontalPodAutoscaler) + if isPristine(hpa) && (hpa.Spec.MinReplicas != nil) && + (*hpa.Spec.MinReplicas > 1) && + (*hpa.Spec.MinReplicas <= hpa.Spec.MaxReplicas) { + return true + } + if !isScaleable(hpa) { + return false + } + if (hpa.Spec.MinReplicas != nil) && + (*hpa.Spec.MinReplicas > 1) && + (hpa.Status.DesiredReplicas == hpa.Status.CurrentReplicas) && + (hpa.Status.CurrentReplicas == *hpa.Spec.MinReplicas) { + return true + } + return false +} + +func maxReplicasNeeded(obj pkgruntime.Object) bool { + hpa := obj.(*autoscalingv1.HorizontalPodAutoscaler) + if !isScaleable(hpa) { + return false + } + + if (hpa.Status.CurrentReplicas == hpa.Status.DesiredReplicas) && + (hpa.Status.CurrentReplicas == hpa.Spec.MaxReplicas) { + return true + } + return false +} + +func minReplicasIncreasable(obj pkgruntime.Object) bool { + hpa := obj.(*autoscalingv1.HorizontalPodAutoscaler) + if !isScaleable(hpa) || + ((hpa.Spec.MinReplicas != nil) && + (*hpa.Spec.MinReplicas) >= hpa.Spec.MaxReplicas) { + return false + } + + if (hpa.Spec.MinReplicas != nil) && + (hpa.Status.DesiredReplicas > *hpa.Spec.MinReplicas) { + return true + } + return false +} diff --git a/federation/pkg/federatedtypes/hpa_test.go b/federation/pkg/federatedtypes/hpa_test.go new file mode 100644 index 00000000000..2168fb894f6 --- /dev/null +++ b/federation/pkg/federatedtypes/hpa_test.go @@ -0,0 +1,262 @@ +/* +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 federatedtypes + +import ( + "testing" + "time" + + autoscalingv1 "k8s.io/api/autoscaling/v1" + apiv1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + pkgruntime "k8s.io/apimachinery/pkg/runtime" + . "k8s.io/kubernetes/federation/pkg/federation-controller/util/test" + + "github.com/stretchr/testify/assert" +) + +type replicas struct { + min int32 + max int32 +} + +func TestGetHpaScheduleState(t *testing.T) { + defaultFedHpa := newHpaWithReplicas(NewInt32(1), NewInt32(70), 10) + testCases := map[string]struct { + fedHpa *autoscalingv1.HorizontalPodAutoscaler + localHpas map[string]pkgruntime.Object + expectedReplicas map[string]*replicas + }{ + "Distribiutes replicas randomly if no existing hpa in any local cluster": { + localHpas: func() map[string]pkgruntime.Object { + hpas := make(map[string]pkgruntime.Object) + hpas["c1"] = nil + hpas["c2"] = nil + return hpas + }(), + }, + "Cluster with no hpa gets replicas if other clusters have replicas": { + localHpas: func() map[string]pkgruntime.Object { + hpas := make(map[string]pkgruntime.Object) + hpas["c1"] = newHpaWithReplicas(NewInt32(1), NewInt32(70), 10) + hpas["c2"] = nil + return hpas + }(), + expectedReplicas: map[string]*replicas{ + "c1": { + min: int32(1), + max: int32(9), + }, + "c2": { + min: int32(1), + max: int32(1), + }, + }, + }, + "Cluster needing max replicas gets it if there is another cluster to offer max": { + localHpas: func() map[string]pkgruntime.Object { + hpa1 := newHpaWithReplicas(NewInt32(1), NewInt32(70), 7) + hpa1 = updateHpaStatus(hpa1, NewInt32(50), 5, 5, true) + hpa2 := newHpaWithReplicas(NewInt32(1), NewInt32(70), 1) + hpa2 = updateHpaStatus(hpa2, NewInt32(70), 1, 1, true) + // include third object to ensure, it does not break the test + hpa3 := newHpaWithReplicas(NewInt32(1), NewInt32(70), 2) + hpa3 = updateHpaStatus(hpa3, NewInt32(70), 1, 1, false) + hpas := make(map[string]pkgruntime.Object) + hpas["c1"] = hpa1 + hpas["c2"] = hpa2 + hpas["c3"] = hpa3 + return hpas + }(), + expectedReplicas: map[string]*replicas{ + "c1": { + min: int32(1), + max: int32(6), + }, + "c2": { + min: int32(1), + max: int32(2), + }, + "c3": { + min: int32(1), + max: int32(2), + }, + }, + }, + "Cluster needing max replicas does not get it if there is no cluster offerring max": { + localHpas: func() map[string]pkgruntime.Object { + hpa1 := newHpaWithReplicas(NewInt32(1), NewInt32(70), 9) + hpa1 = updateHpaStatus(hpa1, NewInt32(70), 9, 9, false) + hpa2 := newHpaWithReplicas(NewInt32(1), NewInt32(70), 1) + hpa2 = updateHpaStatus(hpa2, NewInt32(70), 1, 1, true) + hpas := make(map[string]pkgruntime.Object) + hpas["c1"] = hpa1 + hpas["c2"] = hpa2 + return hpas + }(), + expectedReplicas: map[string]*replicas{ + "c1": { + min: int32(1), + max: int32(9), + }, + "c2": { + min: int32(1), + max: int32(1), + }, + }, + }, + "Cluster which can increase min replicas gets to increase min if there is a cluster offering min": { + fedHpa: newHpaWithReplicas(NewInt32(4), NewInt32(70), 10), + localHpas: func() map[string]pkgruntime.Object { + hpa1 := newHpaWithReplicas(NewInt32(3), NewInt32(70), 6) + hpa1 = updateHpaStatus(hpa1, NewInt32(50), 3, 3, true) + hpa2 := newHpaWithReplicas(NewInt32(1), NewInt32(70), 4) + hpa2 = updateHpaStatus(hpa2, NewInt32(50), 3, 3, true) + hpas := make(map[string]pkgruntime.Object) + hpas["c1"] = hpa1 + hpas["c2"] = hpa2 + return hpas + }(), + expectedReplicas: map[string]*replicas{ + "c1": { + min: int32(2), + max: int32(6), + }, + "c2": { + min: int32(2), + max: int32(4), + }, + }, + }, + "Cluster which can increase min replicas does not increase if there are no clusters offering min": { + fedHpa: newHpaWithReplicas(NewInt32(4), NewInt32(70), 10), + localHpas: func() map[string]pkgruntime.Object { + hpa1 := newHpaWithReplicas(NewInt32(3), NewInt32(70), 6) + hpa1 = updateHpaStatus(hpa1, NewInt32(50), 4, 4, true) + hpa2 := newHpaWithReplicas(NewInt32(1), NewInt32(70), 4) + hpa2 = updateHpaStatus(hpa2, NewInt32(50), 3, 3, true) + hpas := make(map[string]pkgruntime.Object) + hpas["c1"] = hpa1 + hpas["c2"] = hpa2 + return hpas + }(), + expectedReplicas: map[string]*replicas{ + "c1": { + min: int32(3), + max: int32(6), + }, + "c2": { + min: int32(1), + max: int32(4), + }, + }, + }, + "Increasing replicas on fed object increases the same on clusters": { + // Existing total of local min, max = 1+1, 5+5 decreasing to below + fedHpa: newHpaWithReplicas(NewInt32(4), NewInt32(70), 14), + localHpas: func() map[string]pkgruntime.Object { + // does not matter if scaleability is true + hpas := make(map[string]pkgruntime.Object) + hpas["c1"] = newHpaWithReplicas(NewInt32(1), NewInt32(70), 5) + hpas["c2"] = newHpaWithReplicas(NewInt32(1), NewInt32(70), 5) + return hpas + }(), + // We dont know which cluster gets how many, but the resultant total should match + }, + "Decreasing replicas on fed object decreases the same on clusters": { + // Existing total of local min, max = 2+2, 8+8 decreasing to below + fedHpa: newHpaWithReplicas(NewInt32(3), NewInt32(70), 8), + localHpas: func() map[string]pkgruntime.Object { + // does not matter if scaleability is true + hpas := make(map[string]pkgruntime.Object) + hpas["c1"] = newHpaWithReplicas(NewInt32(2), NewInt32(70), 8) + hpas["c2"] = newHpaWithReplicas(NewInt32(2), NewInt32(70), 8) + return hpas + }(), + // We dont know which cluster gets how many, but the resultant total should match + }, + } + + for testName, testCase := range testCases { + t.Run(testName, func(t *testing.T) { + if testCase.fedHpa == nil { + testCase.fedHpa = defaultFedHpa + } + scheduledState := getHpaScheduleState(testCase.fedHpa, testCase.localHpas) + checkClusterConditions(t, testCase.fedHpa, scheduledState) + if testCase.expectedReplicas != nil { + for cluster, replicas := range testCase.expectedReplicas { + scheduledReplicas := scheduledState[cluster] + assert.Equal(t, replicas.min, scheduledReplicas.min) + assert.Equal(t, replicas.max, scheduledReplicas.max) + } + } + }) + } +} + +func updateHpaStatus(hpa *autoscalingv1.HorizontalPodAutoscaler, currentUtilisation *int32, current, desired int32, scaleable bool) *autoscalingv1.HorizontalPodAutoscaler { + hpa.Status.CurrentReplicas = current + hpa.Status.DesiredReplicas = desired + hpa.Status.CurrentCPUUtilizationPercentage = currentUtilisation + now := metav1.Now() + scaledTime := now + if scaleable { + // definitely more then 5 minutes ago + scaledTime = metav1.NewTime(now.Time.Add(-6 * time.Minute)) + } + hpa.Status.LastScaleTime = &scaledTime + return hpa +} + +func checkClusterConditions(t *testing.T, fedHpa *autoscalingv1.HorizontalPodAutoscaler, scheduled map[string]*replicaNums) { + minTotal := int32(0) + maxTotal := int32(0) + for _, replicas := range scheduled { + minTotal += replicas.min + maxTotal += replicas.max + } + + // - Total of max matches the fed max + assert.Equal(t, fedHpa.Spec.MaxReplicas, maxTotal) + // - Total of min is not less then fed min + assert.Condition(t, func() bool { + if *fedHpa.Spec.MinReplicas <= minTotal { + return true + } + return false + }) +} + +func newHpaWithReplicas(min, targetUtilisation *int32, max int32) *autoscalingv1.HorizontalPodAutoscaler { + return &autoscalingv1.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: "myhpa", + Namespace: apiv1.NamespaceDefault, + SelfLink: "/api/mylink", + }, + Spec: autoscalingv1.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{ + Kind: "HorizontalPodAutoscaler", + Name: "target-", + }, + MinReplicas: min, + MaxReplicas: max, + TargetCPUUtilizationPercentage: targetUtilisation, + }, + } +} diff --git a/federation/pkg/federatedtypes/replicaset.go b/federation/pkg/federatedtypes/replicaset.go index d439c7a361a..909b12b7d0a 100644 --- a/federation/pkg/federatedtypes/replicaset.go +++ b/federation/pkg/federatedtypes/replicaset.go @@ -40,16 +40,16 @@ func init() { } type ReplicaSetAdapter struct { - *schedulingAdapter + *replicaSchedulingAdapter client federationclientset.Interface } func NewReplicaSetAdapter(client federationclientset.Interface, config *restclient.Config) FederatedTypeAdapter { - schedulingAdapter := schedulingAdapter{ + replicaSchedulingAdapter := replicaSchedulingAdapter{ preferencesAnnotationName: FedReplicaSetPreferencesAnnotation, - updateStatusFunc: func(obj pkgruntime.Object, status interface{}) error { + updateStatusFunc: func(obj pkgruntime.Object, schedulingInfo interface{}) error { rs := obj.(*extensionsv1.ReplicaSet) - typedStatus := status.(ReplicaSchedulingStatus) + typedStatus := schedulingInfo.(*ReplicaSchedulingInfo).Status if typedStatus.Replicas != rs.Status.Replicas || typedStatus.FullyLabeledReplicas != rs.Status.FullyLabeledReplicas || typedStatus.ReadyReplicas != rs.Status.ReadyReplicas || typedStatus.AvailableReplicas != rs.Status.AvailableReplicas { rs.Status = extensionsv1.ReplicaSetStatus{ @@ -64,7 +64,7 @@ func NewReplicaSetAdapter(client federationclientset.Interface, config *restclie return nil }, } - return &ReplicaSetAdapter{&schedulingAdapter, client} + return &ReplicaSetAdapter{&replicaSchedulingAdapter, client} } func (a *ReplicaSetAdapter) Kind() string { diff --git a/federation/pkg/federatedtypes/scheduling.go b/federation/pkg/federatedtypes/scheduling.go index a6f9bcc9905..3cad44bd59e 100644 --- a/federation/pkg/federatedtypes/scheduling.go +++ b/federation/pkg/federatedtypes/scheduling.go @@ -37,6 +37,16 @@ import ( "github.com/golang/glog" ) +// ScheduleAction is used by the interface ScheduleObject of SchedulingAdapter +// to sync controller reconcile to convey the action type needed for the +// particular cluster local object in ScheduleObject +type ScheduleAction string + +const ( + ActionAdd = "add" + ActionDelete = "delete" +) + // ReplicaSchedulingStatus contains the status of the replica type objects (rs or deployment) // that are being scheduled into joined clusters. type ReplicaSchedulingStatus struct { @@ -58,26 +68,26 @@ type ReplicaSchedulingInfo struct { // federated type that requires more complex synchronization logic. type SchedulingAdapter interface { GetSchedule(obj pkgruntime.Object, key string, clusters []*federationapi.Cluster, informer fedutil.FederatedInformer) (interface{}, error) - ScheduleObject(cluster *federationapi.Cluster, clusterObj pkgruntime.Object, federationObjCopy pkgruntime.Object, schedulingInfo interface{}) (pkgruntime.Object, bool, error) - UpdateFederatedStatus(obj pkgruntime.Object, status interface{}) error + ScheduleObject(cluster *federationapi.Cluster, clusterObj pkgruntime.Object, federationObjCopy pkgruntime.Object, schedulingInfo interface{}) (pkgruntime.Object, ScheduleAction, error) + UpdateFederatedStatus(obj pkgruntime.Object, schedulingInfo interface{}) error // EquivalentIgnoringSchedule returns whether obj1 and obj2 are // equivalent ignoring differences due to scheduling. EquivalentIgnoringSchedule(obj1, obj2 pkgruntime.Object) bool } -// schedulingAdapter is meant to be embedded in other type adapters that require -// workload scheduling. -type schedulingAdapter struct { +// replicaSchedulingAdapter is meant to be embedded in other type adapters that require +// workload scheduling with actual pod replicas. +type replicaSchedulingAdapter struct { preferencesAnnotationName string updateStatusFunc func(pkgruntime.Object, interface{}) error } -func (a *schedulingAdapter) IsSchedulingAdapter() bool { +func (a *replicaSchedulingAdapter) IsSchedulingAdapter() bool { return true } -func (a *schedulingAdapter) GetSchedule(obj pkgruntime.Object, key string, clusters []*federationapi.Cluster, informer fedutil.FederatedInformer) (interface{}, error) { +func (a *replicaSchedulingAdapter) GetSchedule(obj pkgruntime.Object, key string, clusters []*federationapi.Cluster, informer fedutil.FederatedInformer) (interface{}, error) { var clusterNames []string for _, cluster := range clusters { clusterNames = append(clusterNames, cluster.Name) @@ -128,7 +138,7 @@ func (a *schedulingAdapter) GetSchedule(obj pkgruntime.Object, key string, clust }, nil } -func (a *schedulingAdapter) ScheduleObject(cluster *federationapi.Cluster, clusterObj pkgruntime.Object, federationObjCopy pkgruntime.Object, schedulingInfo interface{}) (pkgruntime.Object, bool, error) { +func (a *replicaSchedulingAdapter) ScheduleObject(cluster *federationapi.Cluster, clusterObj pkgruntime.Object, federationObjCopy pkgruntime.Object, schedulingInfo interface{}) (pkgruntime.Object, ScheduleAction, error) { typedSchedulingInfo := schedulingInfo.(*ReplicaSchedulingInfo) replicas, ok := typedSchedulingInfo.Schedule[cluster.Name] if !ok { @@ -152,11 +162,15 @@ func (a *schedulingAdapter) ScheduleObject(cluster *federationapi.Cluster, clust } } } - return federationObjCopy, replicas > 0, nil + var action ScheduleAction = "" + if replicas > 0 { + action = ActionAdd + } + return federationObjCopy, action, nil } -func (a *schedulingAdapter) UpdateFederatedStatus(obj pkgruntime.Object, status interface{}) error { - return a.updateStatusFunc(obj, status) +func (a *replicaSchedulingAdapter) UpdateFederatedStatus(obj pkgruntime.Object, schedulingInfo interface{}) error { + return a.updateStatusFunc(obj, schedulingInfo) } func schedule(planner *planner.Planner, obj pkgruntime.Object, key string, clusterNames []string, currentReplicasPerCluster map[string]int64, estimatedCapacity map[string]int64) map[string]int64 { diff --git a/federation/pkg/federation-controller/sync/controller.go b/federation/pkg/federation-controller/sync/controller.go index 800a1484f8a..41cee680fd3 100644 --- a/federation/pkg/federation-controller/sync/controller.go +++ b/federation/pkg/federation-controller/sync/controller.go @@ -490,8 +490,7 @@ func syncToClusters(clustersAccessor clustersAccessorFunc, operationsAccessor op if !ok { glog.Fatalf("Adapter for kind %q does not properly implement SchedulingAdapter.", kind) } - typedScheduleInfo := schedulingInfo.(*federatedtypes.ReplicaSchedulingInfo) - err = schedulingAdapter.UpdateFederatedStatus(obj, typedScheduleInfo.Status) + err = schedulingAdapter.UpdateFederatedStatus(obj, schedulingInfo) if err != nil { runtime.HandleError(fmt.Errorf("adapter.UpdateFinished() failed on adapter for %s %q: %v", kind, key, err)) return statusError @@ -548,7 +547,7 @@ func clusterOperations(adapter federatedtypes.FederatedTypeAdapter, selectedClus return nil, wrappedErr } - shouldCreateIfNeeded := true + var scheduleAction federatedtypes.ScheduleAction = federatedtypes.ActionAdd if adapter.IsSchedulingAdapter() { schedulingAdapter, ok := adapter.(federatedtypes.SchedulingAdapter) if !ok { @@ -559,7 +558,7 @@ func clusterOperations(adapter federatedtypes.FederatedTypeAdapter, selectedClus if clusterObj != nil { clusterTypedObj = clusterObj.(pkgruntime.Object) } - desiredObj, shouldCreateIfNeeded, err = schedulingAdapter.ScheduleObject(cluster, clusterTypedObj, desiredObj, schedulingInfo) + desiredObj, scheduleAction, err = schedulingAdapter.ScheduleObject(cluster, clusterTypedObj, desiredObj, schedulingInfo) if err != nil { runtime.HandleError(err) return nil, err @@ -568,11 +567,15 @@ func clusterOperations(adapter federatedtypes.FederatedTypeAdapter, selectedClus var operationType util.FederatedOperationType = "" if found { - clusterObj := clusterObj.(pkgruntime.Object) - if !adapter.Equivalent(desiredObj, clusterObj) { - operationType = util.OperationTypeUpdate + if scheduleAction == federatedtypes.ActionDelete { + operationType = util.OperationTypeDelete + } else { + clusterObj := clusterObj.(pkgruntime.Object) + if !adapter.Equivalent(desiredObj, clusterObj) { + operationType = util.OperationTypeUpdate + } } - } else if shouldCreateIfNeeded { + } else if scheduleAction == federatedtypes.ActionAdd { operationType = util.OperationTypeAdd } diff --git a/federation/pkg/federation-controller/util/test/test_helper.go b/federation/pkg/federation-controller/util/test/test_helper.go index de6307b80e4..534111d7424 100644 --- a/federation/pkg/federation-controller/util/test/test_helper.go +++ b/federation/pkg/federation-controller/util/test/test_helper.go @@ -446,3 +446,9 @@ func AssertHasFinalizer(t *testing.T, obj runtime.Object, finalizer string) { require.Nil(t, err) assert.True(t, hasFinalizer) } + +func NewInt32(val int32) *int32 { + p := new(int32) + *p = val + return p +} From a0cebcb559c5c0ab8a2e50b1ee11cc62f9ebb3a8 Mon Sep 17 00:00:00 2001 From: zhouhaibing089 Date: Mon, 9 Jan 2017 09:52:46 +0800 Subject: [PATCH 21/38] plugin/pkg/client/auth: add openstack auth provider --- .../src/k8s.io/client-go/Godeps/Godeps.json | 28 +++ .../client-go/plugin/pkg/client/auth/BUILD | 2 + .../plugin/pkg/client/auth/openstack/BUILD | 40 +++++ .../pkg/client/auth/openstack/openstack.go | 161 ++++++++++++++++++ .../client/auth/openstack/openstack_test.go | 116 +++++++++++++ .../plugin/pkg/client/auth/plugins.go | 1 + 6 files changed, 348 insertions(+) create mode 100644 staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/BUILD create mode 100644 staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/openstack.go create mode 100644 staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/openstack_test.go diff --git a/staging/src/k8s.io/client-go/Godeps/Godeps.json b/staging/src/k8s.io/client-go/Godeps/Godeps.json index 25134d60444..328e0a168e2 100644 --- a/staging/src/k8s.io/client-go/Godeps/Godeps.json +++ b/staging/src/k8s.io/client-go/Godeps/Godeps.json @@ -174,6 +174,34 @@ "ImportPath": "github.com/googleapis/gnostic/extensions", "Rev": "0c5108395e2debce0d731cf0287ddf7242066aba" }, + { + "ImportPath": "github.com/gophercloud/gophercloud", + "Rev": "ed590d9afe113c6107cd60717b196155e6579e78" + }, + { + "ImportPath": "github.com/gophercloud/gophercloud/openstack", + "Rev": "ed590d9afe113c6107cd60717b196155e6579e78" + }, + { + "ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants", + "Rev": "ed590d9afe113c6107cd60717b196155e6579e78" + }, + { + "ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens", + "Rev": "ed590d9afe113c6107cd60717b196155e6579e78" + }, + { + "ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens", + "Rev": "ed590d9afe113c6107cd60717b196155e6579e78" + }, + { + "ImportPath": "github.com/gophercloud/gophercloud/openstack/utils", + "Rev": "ed590d9afe113c6107cd60717b196155e6579e78" + }, + { + "ImportPath": "github.com/gophercloud/gophercloud/pagination", + "Rev": "ed590d9afe113c6107cd60717b196155e6579e78" + }, { "ImportPath": "github.com/gregjones/httpcache", "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" diff --git a/staging/src/k8s.io/client-go/plugin/pkg/client/auth/BUILD b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/BUILD index b0c9df62f56..4ced574ba93 100644 --- a/staging/src/k8s.io/client-go/plugin/pkg/client/auth/BUILD +++ b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/BUILD @@ -15,6 +15,7 @@ go_library( "//vendor/k8s.io/client-go/plugin/pkg/client/auth/azure:go_default_library", "//vendor/k8s.io/client-go/plugin/pkg/client/auth/gcp:go_default_library", "//vendor/k8s.io/client-go/plugin/pkg/client/auth/oidc:go_default_library", + "//vendor/k8s.io/client-go/plugin/pkg/client/auth/openstack:go_default_library", ], ) @@ -32,6 +33,7 @@ filegroup( "//staging/src/k8s.io/client-go/plugin/pkg/client/auth/azure:all-srcs", "//staging/src/k8s.io/client-go/plugin/pkg/client/auth/gcp:all-srcs", "//staging/src/k8s.io/client-go/plugin/pkg/client/auth/oidc:all-srcs", + "//staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack:all-srcs", ], tags = ["automanaged"], ) diff --git a/staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/BUILD b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/BUILD new file mode 100644 index 00000000000..b7802cc6eb4 --- /dev/null +++ b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/BUILD @@ -0,0 +1,40 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_test( + name = "go_default_test", + srcs = ["openstack_test.go"], + library = ":go_default_library", + tags = ["automanaged"], +) + +go_library( + name = "go_default_library", + srcs = ["openstack.go"], + tags = ["automanaged"], + deps = [ + "//vendor/github.com/golang/glog:go_default_library", + "//vendor/github.com/gophercloud/gophercloud/openstack:go_default_library", + "//vendor/k8s.io/client-go/rest:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) diff --git a/staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/openstack.go b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/openstack.go new file mode 100644 index 00000000000..9df94913122 --- /dev/null +++ b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/openstack.go @@ -0,0 +1,161 @@ +/* +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 openstack + +import ( + "fmt" + "net/http" + "sync" + "time" + + "github.com/golang/glog" + "github.com/gophercloud/gophercloud/openstack" + + restclient "k8s.io/client-go/rest" +) + +func init() { + if err := restclient.RegisterAuthProviderPlugin("openstack", newOpenstackAuthProvider); err != nil { + glog.Fatalf("Failed to register openstack auth plugin: %s", err) + } +} + +// DefaultTTLDuration is the time before a token gets expired. +const DefaultTTLDuration = 10 * time.Minute + +// openstackAuthProvider is an authprovider for openstack. this provider reads +// the environment variables to determine the client identity, and generates a +// token which will be inserted into the request header later. +type openstackAuthProvider struct { + ttl time.Duration + + tokenGetter TokenGetter +} + +// TokenGetter returns a bearer token that can be inserted into request. +type TokenGetter interface { + Token() (string, error) +} + +type tokenGetter struct{} + +// Token creates a token by authenticate with keystone. +func (*tokenGetter) Token() (string, error) { + options, err := openstack.AuthOptionsFromEnv() + if err != nil { + return "", fmt.Errorf("failed to read openstack env vars: %s", err) + } + client, err := openstack.AuthenticatedClient(options) + if err != nil { + return "", fmt.Errorf("authentication failed: %s", err) + } + return client.TokenID, nil +} + +// cachedGetter caches a token until it gets expired, after the expiration, it will +// generate another token and cache it. +type cachedGetter struct { + mutex sync.Mutex + tokenGetter TokenGetter + + token string + born time.Time + ttl time.Duration +} + +// Token returns the current available token, create a new one if expired. +func (c *cachedGetter) Token() (string, error) { + c.mutex.Lock() + defer c.mutex.Unlock() + + var err error + // no token or exceeds the TTL + if c.token == "" || time.Now().Sub(c.born) > c.ttl { + c.token, err = c.tokenGetter.Token() + if err != nil { + return "", fmt.Errorf("failed to get token: %s", err) + } + c.born = time.Now() + } + return c.token, nil +} + +// tokenRoundTripper implements the RoundTripper interface: adding the bearer token +// into the request header. +type tokenRoundTripper struct { + http.RoundTripper + + tokenGetter TokenGetter +} + +// RoundTrip adds the bearer token into the request. +func (t *tokenRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + // if the authorization header already present, use it. + if req.Header.Get("Authorization") != "" { + return t.RoundTripper.RoundTrip(req) + } + + token, err := t.tokenGetter.Token() + if err == nil { + req.Header.Set("Authorization", "Bearer "+token) + } else { + glog.V(4).Infof("failed to get token: %s", err) + } + + return t.RoundTripper.RoundTrip(req) +} + +// newOpenstackAuthProvider creates an auth provider which works with openstack +// environment. +func newOpenstackAuthProvider(clusterAddress string, config map[string]string, persister restclient.AuthProviderConfigPersister) (restclient.AuthProvider, error) { + var ttlDuration time.Duration + var err error + + ttl, found := config["ttl"] + if !found { + ttlDuration = DefaultTTLDuration + // persist to config + config["ttl"] = ttlDuration.String() + if err = persister.Persist(config); err != nil { + return nil, fmt.Errorf("failed to persist config: %s", err) + } + } else { + ttlDuration, err = time.ParseDuration(ttl) + if err != nil { + return nil, fmt.Errorf("failed to parse ttl config: %s", err) + } + } + + // TODO: read/persist client configuration(OS_XXX env vars) in config + + return &openstackAuthProvider{ + ttl: ttlDuration, + tokenGetter: &tokenGetter{}, + }, nil +} + +func (oap *openstackAuthProvider) WrapTransport(rt http.RoundTripper) http.RoundTripper { + return &tokenRoundTripper{ + RoundTripper: rt, + tokenGetter: &cachedGetter{ + tokenGetter: oap.tokenGetter, + ttl: oap.ttl, + }, + } +} + +func (oap *openstackAuthProvider) Login() error { return nil } diff --git a/staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/openstack_test.go b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/openstack_test.go new file mode 100644 index 00000000000..411bec70f7f --- /dev/null +++ b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/openstack/openstack_test.go @@ -0,0 +1,116 @@ +/* +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 openstack + +import ( + "math/rand" + "net/http" + "testing" + "time" +) + +// testTokenGetter is a simple random token getter. +type testTokenGetter struct{} + +const LetterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + +func RandStringBytes(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = LetterBytes[rand.Intn(len(LetterBytes))] + } + return string(b) +} + +func (*testTokenGetter) Token() (string, error) { + return RandStringBytes(32), nil +} + +// testRoundTripper is mocked roundtripper which responds with unauthorized when +// there is no authorization header, otherwise returns status ok. +type testRoundTripper struct{} + +func (trt *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + authHeader := req.Header.Get("Authorization") + if authHeader == "" || authHeader == "Bearer " { + return &http.Response{ + StatusCode: http.StatusUnauthorized, + }, nil + } + return &http.Response{StatusCode: http.StatusOK}, nil +} + +func TestOpenstackAuthProvider(t *testing.T) { + trt := &tokenRoundTripper{ + RoundTripper: &testRoundTripper{}, + } + + tests := []struct { + name string + ttl time.Duration + interval time.Duration + same bool + }{ + { + name: "normal", + ttl: 2 * time.Second, + interval: 1 * time.Second, + same: true, + }, + { + name: "expire", + ttl: 1 * time.Second, + interval: 2 * time.Second, + same: false, + }, + } + + for _, test := range tests { + trt.tokenGetter = &cachedGetter{ + tokenGetter: &testTokenGetter{}, + ttl: test.ttl, + } + + req, err := http.NewRequest(http.MethodPost, "https://test-api-server.com", nil) + if err != nil { + t.Errorf("failed to new request: %s", err) + } + trt.RoundTrip(req) + header := req.Header.Get("Authorization") + if header == "" { + t.Errorf("expect to see token in header, but is absent") + } + + time.Sleep(test.interval) + + req, err = http.NewRequest(http.MethodPost, "https://test-api-server.com", nil) + if err != nil { + t.Errorf("failed to new request: %s", err) + } + trt.RoundTrip(req) + newHeader := req.Header.Get("Authorization") + if newHeader == "" { + t.Errorf("expect to see token in header, but is absent") + } + + same := newHeader == header + if same != test.same { + t.Errorf("expect to get %t when compare header, but saw %t", test.same, same) + } + } + +} diff --git a/staging/src/k8s.io/client-go/plugin/pkg/client/auth/plugins.go b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/plugins.go index 0328fbd8e7f..42085d7ae15 100644 --- a/staging/src/k8s.io/client-go/plugin/pkg/client/auth/plugins.go +++ b/staging/src/k8s.io/client-go/plugin/pkg/client/auth/plugins.go @@ -21,4 +21,5 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth/azure" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" + _ "k8s.io/client-go/plugin/pkg/client/auth/openstack" ) From 1ff8e12a24278897837c83e9f3433154847ec2b5 Mon Sep 17 00:00:00 2001 From: FengyunPan Date: Sun, 6 Aug 2017 14:29:47 +0800 Subject: [PATCH 22/38] [OpenStack] Add more detail error message I get same simple error messages "Unable to initialize cinder client for region: RegionOne" from controller-manager, but I can not find the reason. We should add more detail message "err" into glog.Errorf. --- pkg/cloudprovider/providers/openstack/openstack_client.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/cloudprovider/providers/openstack/openstack_client.go b/pkg/cloudprovider/providers/openstack/openstack_client.go index 71b0078bfd0..916c4a09c56 100644 --- a/pkg/cloudprovider/providers/openstack/openstack_client.go +++ b/pkg/cloudprovider/providers/openstack/openstack_client.go @@ -28,7 +28,7 @@ func (os *OpenStack) NewNetworkV2() (*gophercloud.ServiceClient, error) { Region: os.region, }) if err != nil { - glog.Warningf("Failed to find network v2 endpoint: %v", err) + glog.Warningf("Failed to find network v2 endpoint for region %s: %v", os.region, err) return nil, err } return network, nil @@ -39,7 +39,7 @@ func (os *OpenStack) NewComputeV2() (*gophercloud.ServiceClient, error) { Region: os.region, }) if err != nil { - glog.Warningf("Failed to find compute v2 endpoint: %v", err) + glog.Warningf("Failed to find compute v2 endpoint for region %s: %v", os.region, err) return nil, err } return compute, nil @@ -50,7 +50,7 @@ func (os *OpenStack) NewBlockStorageV1() (*gophercloud.ServiceClient, error) { Region: os.region, }) if err != nil { - glog.Errorf("Unable to initialize cinder v1 client for region: %s", os.region) + glog.Errorf("Unable to initialize cinder v1 client for region %s: %v", os.region, err) return nil, err } return storage, nil @@ -61,7 +61,7 @@ func (os *OpenStack) NewBlockStorageV2() (*gophercloud.ServiceClient, error) { Region: os.region, }) if err != nil { - glog.Errorf("Unable to initialize cinder v2 client for region: %s", os.region) + glog.Errorf("Unable to initialize cinder v2 client for region %s: %v", os.region, err) return nil, err } return storage, nil From 48db05166a92414358757727b56d1f854d940e54 Mon Sep 17 00:00:00 2001 From: FengyunPan Date: Mon, 7 Aug 2017 17:11:40 +0800 Subject: [PATCH 23/38] Ignore the available volume when calling DetachDisk If use detachs the volume by nova in openstack env, volume becomes available. If nova instance is been deleted, nova will detach it automatically. So the "available" is fine since that means the volume is detached from instance already. --- .../providers/openstack/openstack_volumes.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pkg/cloudprovider/providers/openstack/openstack_volumes.go b/pkg/cloudprovider/providers/openstack/openstack_volumes.go index 7f7b499bb77..a5479a4f4b6 100644 --- a/pkg/cloudprovider/providers/openstack/openstack_volumes.go +++ b/pkg/cloudprovider/providers/openstack/openstack_volumes.go @@ -215,11 +215,7 @@ func (os *OpenStack) AttachDisk(instanceID, volumeID string) (string, error) { if err != nil { return "", err } - if volume.Status != VolumeAvailableStatus { - errmsg := fmt.Sprintf("volume %s status is %s, not %s, can not be attached to instance %s.", volume.Name, volume.Status, VolumeAvailableStatus, instanceID) - glog.Errorf(errmsg) - return "", errors.New(errmsg) - } + cClient, err := os.NewComputeV2() if err != nil { return "", err @@ -258,6 +254,12 @@ func (os *OpenStack) DetachDisk(instanceID, volumeID string) error { if err != nil { return err } + if volume.Status == VolumeAvailableStatus { + // "available" is fine since that means the volume is detached from instance already. + glog.V(2).Infof("volume: %s has been detached from compute: %s ", volume.ID, instanceID) + return nil + } + if volume.Status != VolumeInUseStatus { errmsg := fmt.Sprintf("can not detach volume %s, its status is %s.", volume.Name, volume.Status) glog.Errorf(errmsg) From 18ae1ba813f6ba8ff7b80ede4f6471d4c8b46a3b Mon Sep 17 00:00:00 2001 From: Klaus Ma Date: Mon, 7 Aug 2017 19:29:39 +0800 Subject: [PATCH 24/38] Handled taints on node in batch. --- pkg/controller/BUILD | 3 + pkg/controller/controller_utils.go | 123 ++++---- pkg/controller/controller_utils_test.go | 358 ++++++++++++++++++++++++ pkg/controller/node/controller_utils.go | 2 +- pkg/controller/node/node_controller.go | 4 +- test/e2e/framework/util.go | 2 +- 6 files changed, 440 insertions(+), 52 deletions(-) diff --git a/pkg/controller/BUILD b/pkg/controller/BUILD index 4d2d889d8ee..dc6dbfbac2b 100644 --- a/pkg/controller/BUILD +++ b/pkg/controller/BUILD @@ -18,7 +18,9 @@ go_test( tags = ["automanaged"], deps = [ "//pkg/api:go_default_library", + "//pkg/api/install:go_default_library", "//pkg/api/testapi:go_default_library", + "//pkg/controller/node/testutil:go_default_library", "//pkg/securitycontext:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", @@ -32,6 +34,7 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", + "//vendor/k8s.io/client-go/kubernetes/fake:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/tools/cache:go_default_library", "//vendor/k8s.io/client-go/tools/record:go_default_library", diff --git a/pkg/controller/controller_utils.go b/pkg/controller/controller_utils.go index 4a6d6863dab..4833b09cf1a 100644 --- a/pkg/controller/controller_utils.go +++ b/pkg/controller/controller_utils.go @@ -885,50 +885,11 @@ func (o ReplicaSetsBySizeNewer) Less(i, j int) bool { return *(o[i].Spec.Replicas) > *(o[j].Spec.Replicas) } -func AddOrUpdateTaintOnNode(c clientset.Interface, nodeName string, taint *v1.Taint) error { - firstTry := true - return clientretry.RetryOnConflict(UpdateTaintBackoff, func() error { - var err error - var oldNode *v1.Node - // First we try getting node from the API server cache, as it's cheaper. If it fails - // we get it from etcd to be sure to have fresh data. - if firstTry { - oldNode, err = c.Core().Nodes().Get(nodeName, metav1.GetOptions{ResourceVersion: "0"}) - firstTry = false - } else { - oldNode, err = c.Core().Nodes().Get(nodeName, metav1.GetOptions{}) - } - if err != nil { - return err - } - newNode, ok, err := taintutils.AddOrUpdateTaint(oldNode, taint) - if err != nil { - return fmt.Errorf("Failed to update taint annotation!") - } - if !ok { - return nil - } - return PatchNodeTaints(c, nodeName, oldNode, newNode) - }) -} - -// RemoveTaintOffNode is for cleaning up taints temporarily added to node, -// won't fail if target taint doesn't exist or has been removed. -// If passed a node it'll check if there's anything to be done, if taint is not present it won't issue -// any API calls. -func RemoveTaintOffNode(c clientset.Interface, nodeName string, taint *v1.Taint, node *v1.Node) error { - // Short circuit for limiting amount of API calls. - if node != nil { - match := false - for i := range node.Spec.Taints { - if node.Spec.Taints[i].MatchTaint(taint) { - match = true - break - } - } - if !match { - return nil - } +// AddOrUpdateTaintOnNode add taints to the node. If taint was added into node, it'll issue API calls +// to update nodes; otherwise, no API calls. Return error if any. +func AddOrUpdateTaintOnNode(c clientset.Interface, nodeName string, taints ...*v1.Taint) error { + if len(taints) == 0 { + return nil } firstTry := true return clientretry.RetryOnConflict(UpdateTaintBackoff, func() error { @@ -945,11 +906,77 @@ func RemoveTaintOffNode(c clientset.Interface, nodeName string, taint *v1.Taint, if err != nil { return err } - newNode, ok, err := taintutils.RemoveTaint(oldNode, taint) - if err != nil { - return fmt.Errorf("Failed to update taint annotation!") + + var newNode *v1.Node + oldNodeCopy := oldNode + updated := false + for _, taint := range taints { + curNewNode, ok, err := taintutils.AddOrUpdateTaint(oldNodeCopy, taint) + if err != nil { + return fmt.Errorf("Failed to update taint of node!") + } + updated = updated || ok + newNode = curNewNode + oldNodeCopy = curNewNode } - if !ok { + if !updated { + return nil + } + return PatchNodeTaints(c, nodeName, oldNode, newNode) + }) +} + +// RemoveTaintOffNode is for cleaning up taints temporarily added to node, +// won't fail if target taint doesn't exist or has been removed. +// If passed a node it'll check if there's anything to be done, if taint is not present it won't issue +// any API calls. +func RemoveTaintOffNode(c clientset.Interface, nodeName string, node *v1.Node, taints ...*v1.Taint) error { + if len(taints) == 0 { + return nil + } + // Short circuit for limiting amount of API calls. + if node != nil { + match := false + for _, taint := range taints { + if taintutils.TaintExists(node.Spec.Taints, taint) { + match = true + break + } + } + if !match { + return nil + } + } + + firstTry := true + return clientretry.RetryOnConflict(UpdateTaintBackoff, func() error { + var err error + var oldNode *v1.Node + // First we try getting node from the API server cache, as it's cheaper. If it fails + // we get it from etcd to be sure to have fresh data. + if firstTry { + oldNode, err = c.Core().Nodes().Get(nodeName, metav1.GetOptions{ResourceVersion: "0"}) + firstTry = false + } else { + oldNode, err = c.Core().Nodes().Get(nodeName, metav1.GetOptions{}) + } + if err != nil { + return err + } + + var newNode *v1.Node + oldNodeCopy := oldNode + updated := false + for _, taint := range taints { + curNewNode, ok, err := taintutils.RemoveTaint(oldNodeCopy, taint) + if err != nil { + return fmt.Errorf("Failed to remove taint of node!") + } + updated = updated || ok + newNode = curNewNode + oldNodeCopy = curNewNode + } + if !updated { return nil } return PatchNodeTaints(c, nodeName, oldNode, newNode) diff --git a/pkg/controller/controller_utils_test.go b/pkg/controller/controller_utils_test.go index 0e4fdd3fa21..7abc0af277e 100644 --- a/pkg/controller/controller_utils_test.go +++ b/pkg/controller/controller_utils_test.go @@ -37,12 +37,15 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/uuid" clientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/fake" restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" utiltesting "k8s.io/client-go/util/testing" "k8s.io/kubernetes/pkg/api" + _ "k8s.io/kubernetes/pkg/api/install" "k8s.io/kubernetes/pkg/api/testapi" + "k8s.io/kubernetes/pkg/controller/node/testutil" "k8s.io/kubernetes/pkg/securitycontext" ) @@ -479,3 +482,358 @@ func TestComputeHash(t *testing.T) { } } } + +func TestRemoveTaintOffNode(t *testing.T) { + tests := []struct { + name string + nodeHandler *testutil.FakeNodeHandler + nodeName string + taintsToRemove []*v1.Taint + expectedTaints []v1.Taint + requestCount int + }{ + { + name: "remove one taint from node", + nodeHandler: &testutil.FakeNodeHandler{ + Existing: []*v1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + Spec: v1.NodeSpec{ + Taints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + }, + }, + }, + }, + Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}), + }, + nodeName: "node1", + taintsToRemove: []*v1.Taint{ + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + }, + expectedTaints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + }, + requestCount: 4, + }, + { + name: "remove multiple taints from node", + nodeHandler: &testutil.FakeNodeHandler{ + Existing: []*v1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + Spec: v1.NodeSpec{ + Taints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + {Key: "key3", Value: "value3", Effect: "NoSchedule"}, + {Key: "key4", Value: "value4", Effect: "NoExecute"}, + }, + }, + }, + }, + Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}), + }, + nodeName: "node1", + taintsToRemove: []*v1.Taint{ + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + {Key: "key3", Value: "value3", Effect: "NoSchedule"}, + }, + expectedTaints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + {Key: "key4", Value: "value4", Effect: "NoExecute"}, + }, + requestCount: 4, + }, + { + name: "remove no-exist taints from node", + nodeHandler: &testutil.FakeNodeHandler{ + Existing: []*v1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + Spec: v1.NodeSpec{ + Taints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + }, + }, + }, + }, + Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}), + }, + nodeName: "node1", + taintsToRemove: []*v1.Taint{ + {Key: "key3", Value: "value3", Effect: "NoSchedule"}, + }, + expectedTaints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + }, + requestCount: 2, + }, + { + name: "remove taint from node without taints", + nodeHandler: &testutil.FakeNodeHandler{ + Existing: []*v1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + }, + }, + Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}), + }, + nodeName: "node1", + taintsToRemove: []*v1.Taint{ + {Key: "key3", Value: "value3", Effect: "NoSchedule"}, + }, + expectedTaints: nil, + requestCount: 2, + }, + { + name: "remove empty taint list from node without taints", + nodeHandler: &testutil.FakeNodeHandler{ + Existing: []*v1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + }, + }, + Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}), + }, + nodeName: "node1", + taintsToRemove: []*v1.Taint{}, + expectedTaints: nil, + requestCount: 2, + }, + { + name: "remove empty taint list from node", + nodeHandler: &testutil.FakeNodeHandler{ + Existing: []*v1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + Spec: v1.NodeSpec{ + Taints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + }, + }, + }, + }, + Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}), + }, + nodeName: "node1", + taintsToRemove: []*v1.Taint{}, + expectedTaints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + }, + requestCount: 2, + }, + } + for _, test := range tests { + node, _ := test.nodeHandler.Get(test.nodeName, metav1.GetOptions{}) + if err := RemoveTaintOffNode(test.nodeHandler, test.nodeName, node, test.taintsToRemove...); err != nil { + t.Errorf("%s: RemoveTaintOffNode() error = %v", test.name, err) + } + + node, _ = test.nodeHandler.Get(test.nodeName, metav1.GetOptions{}) + if !reflect.DeepEqual(node.Spec.Taints, test.expectedTaints) { + t.Errorf("%s: failed to remove taint off node: expected %+v, got %+v", + test.name, test.expectedTaints, node.Spec.Taints) + } + + if test.nodeHandler.RequestCount != test.requestCount { + t.Errorf("%s: unexpected request count: expected %+v, got %+v", + test.name, test.requestCount, test.nodeHandler.RequestCount) + } + } +} + +func TestAddOrUpdateTaintOnNode(t *testing.T) { + tests := []struct { + name string + nodeHandler *testutil.FakeNodeHandler + nodeName string + taintsToAdd []*v1.Taint + expectedTaints []v1.Taint + requestCount int + }{ + { + name: "add one taint on node", + nodeHandler: &testutil.FakeNodeHandler{ + Existing: []*v1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + Spec: v1.NodeSpec{ + Taints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + }, + }, + }, + }, + Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}), + }, + nodeName: "node1", + taintsToAdd: []*v1.Taint{ + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + }, + expectedTaints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + }, + requestCount: 3, + }, + { + name: "add multiple taints to node", + nodeHandler: &testutil.FakeNodeHandler{ + Existing: []*v1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + Spec: v1.NodeSpec{ + Taints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + }, + }, + }, + }, + Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}), + }, + nodeName: "node1", + taintsToAdd: []*v1.Taint{ + {Key: "key3", Value: "value3", Effect: "NoSchedule"}, + {Key: "key4", Value: "value4", Effect: "NoExecute"}, + }, + expectedTaints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + {Key: "key3", Value: "value3", Effect: "NoSchedule"}, + {Key: "key4", Value: "value4", Effect: "NoExecute"}, + }, + requestCount: 3, + }, + { + name: "add exist taints to node", + nodeHandler: &testutil.FakeNodeHandler{ + Existing: []*v1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + Spec: v1.NodeSpec{ + Taints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + }, + }, + }, + }, + Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}), + }, + nodeName: "node1", + taintsToAdd: []*v1.Taint{ + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + }, + expectedTaints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + }, + requestCount: 3, + }, + { + name: "add taint to node without taints", + nodeHandler: &testutil.FakeNodeHandler{ + Existing: []*v1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + }, + }, + Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}), + }, + nodeName: "node1", + taintsToAdd: []*v1.Taint{ + {Key: "key3", Value: "value3", Effect: "NoSchedule"}, + }, + expectedTaints: []v1.Taint{ + {Key: "key3", Value: "value3", Effect: "NoSchedule"}, + }, + requestCount: 3, + }, + { + name: "add empty taint list to node without taints", + nodeHandler: &testutil.FakeNodeHandler{ + Existing: []*v1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + }, + }, + Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}), + }, + nodeName: "node1", + taintsToAdd: []*v1.Taint{}, + expectedTaints: nil, + requestCount: 1, + }, + { + name: "add empty taint list to node", + nodeHandler: &testutil.FakeNodeHandler{ + Existing: []*v1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + Spec: v1.NodeSpec{ + Taints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + }, + }, + }, + }, + Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}), + }, + nodeName: "node1", + taintsToAdd: []*v1.Taint{}, + expectedTaints: []v1.Taint{ + {Key: "key1", Value: "value1", Effect: "NoSchedule"}, + {Key: "key2", Value: "value2", Effect: "NoExecute"}, + }, + requestCount: 1, + }, + } + for _, test := range tests { + if err := AddOrUpdateTaintOnNode(test.nodeHandler, test.nodeName, test.taintsToAdd...); err != nil { + t.Errorf("%s: AddOrUpdateTaintOnNode() error = %v", test.name, err) + } + + node, _ := test.nodeHandler.Get(test.nodeName, metav1.GetOptions{}) + if !reflect.DeepEqual(node.Spec.Taints, test.expectedTaints) { + t.Errorf("%s: failed to add taint to node: expected %+v, got %+v", + test.name, test.expectedTaints, node.Spec.Taints) + } + + if test.nodeHandler.RequestCount != test.requestCount { + t.Errorf("%s: unexpected request count: expected %+v, got %+v", + test.name, test.requestCount, test.nodeHandler.RequestCount) + } + } +} diff --git a/pkg/controller/node/controller_utils.go b/pkg/controller/node/controller_utils.go index 035dfdc6f9a..9b5666d5861 100644 --- a/pkg/controller/node/controller_utils.go +++ b/pkg/controller/node/controller_utils.go @@ -303,7 +303,7 @@ func swapNodeControllerTaint(kubeClient clientset.Interface, taintToAdd, taintTo } glog.V(4).Infof("Added %v Taint to Node %v", taintToAdd, node.Name) - err = controller.RemoveTaintOffNode(kubeClient, node.Name, taintToRemove, node) + err = controller.RemoveTaintOffNode(kubeClient, node.Name, node, taintToRemove) if err != nil { utilruntime.HandleError( fmt.Errorf( diff --git a/pkg/controller/node/node_controller.go b/pkg/controller/node/node_controller.go index 1df7d2d2c3e..5a2fa6f89f0 100644 --- a/pkg/controller/node/node_controller.go +++ b/pkg/controller/node/node_controller.go @@ -1072,12 +1072,12 @@ func (nc *NodeController) markNodeForTainting(node *v1.Node) bool { func (nc *NodeController) markNodeAsHealthy(node *v1.Node) (bool, error) { nc.evictorLock.Lock() defer nc.evictorLock.Unlock() - err := controller.RemoveTaintOffNode(nc.kubeClient, node.Name, UnreachableTaintTemplate, node) + err := controller.RemoveTaintOffNode(nc.kubeClient, node.Name, node, UnreachableTaintTemplate) if err != nil { glog.Errorf("Failed to remove taint from node %v: %v", node.Name, err) return false, err } - err = controller.RemoveTaintOffNode(nc.kubeClient, node.Name, NotReadyTaintTemplate, node) + err = controller.RemoveTaintOffNode(nc.kubeClient, node.Name, node, NotReadyTaintTemplate) if err != nil { glog.Errorf("Failed to remove taint from node %v: %v", node.Name, err) return false, err diff --git a/test/e2e/framework/util.go b/test/e2e/framework/util.go index 1a9130414b0..0b6455f83c3 100644 --- a/test/e2e/framework/util.go +++ b/test/e2e/framework/util.go @@ -2551,7 +2551,7 @@ func ExpectNodeHasLabel(c clientset.Interface, nodeName string, labelKey string, } func RemoveTaintOffNode(c clientset.Interface, nodeName string, taint v1.Taint) { - ExpectNoError(controller.RemoveTaintOffNode(c, nodeName, &taint, nil)) + ExpectNoError(controller.RemoveTaintOffNode(c, nodeName, nil, &taint)) VerifyThatTaintIsGone(c, nodeName, &taint) } From 53742560cdb1b2f4ba46eda0ad476087364b6224 Mon Sep 17 00:00:00 2001 From: FengyunPan Date: Mon, 7 Aug 2017 20:18:20 +0800 Subject: [PATCH 25/38] There is no need to split service key repeatedly --- pkg/controller/endpoint/endpoints_controller.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pkg/controller/endpoint/endpoints_controller.go b/pkg/controller/endpoint/endpoints_controller.go index fcc0991d412..f1cea8b39b0 100644 --- a/pkg/controller/endpoint/endpoints_controller.go +++ b/pkg/controller/endpoint/endpoints_controller.go @@ -328,12 +328,6 @@ func (e *EndpointController) syncService(key string) error { // service is deleted. However, if we're down at the time when // the service is deleted, we will miss that deletion, so this // doesn't completely solve the problem. See #6877. - namespace, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - utilruntime.HandleError(fmt.Errorf("Need to delete endpoint with key %q, but couldn't understand the key: %v", key, err)) - // Don't retry, as the key isn't going to magically become understandable. - return nil - } err = e.client.Core().Endpoints(namespace).Delete(name, nil) if err != nil && !errors.IsNotFound(err) { return err From ec397c4374f3e5d99f6ca8ee79892e1f0d9d8d38 Mon Sep 17 00:00:00 2001 From: deads2k Date: Thu, 3 Aug 2017 08:39:43 -0400 Subject: [PATCH 26/38] convert default predicates to use the default --- hack/.golint_failures | 10 ---- .../externaladmissionhookconfiguration/BUILD | 4 -- .../storage/storage.go | 3 +- .../strategy.go | 30 ----------- .../initializerconfiguration/BUILD | 4 -- .../storage/storage.go | 3 +- .../initializerconfiguration/strategy.go | 30 ----------- pkg/registry/apps/controllerrevision/BUILD | 6 --- .../controllerrevision/storage/storage.go | 3 +- .../apps/controllerrevision/strategy.go | 29 ----------- .../apps/controllerrevision/strategy_test.go | 51 ------------------- pkg/registry/apps/statefulset/BUILD | 4 -- .../apps/statefulset/storage/storage.go | 3 +- pkg/registry/apps/statefulset/strategy.go | 30 ----------- .../autoscaling/horizontalpodautoscaler/BUILD | 17 ------- .../storage/storage.go | 3 +- .../horizontalpodautoscaler/strategy.go | 26 ---------- .../horizontalpodautoscaler/strategy_test.go | 35 ------------- pkg/registry/certificates/certificates/BUILD | 4 -- .../certificates/storage/storage.go | 3 +- .../certificates/certificates/strategy.go | 27 ---------- pkg/registry/core/configmap/BUILD | 5 -- .../core/configmap/storage/storage.go | 3 +- pkg/registry/core/configmap/strategy.go | 29 ----------- pkg/registry/core/configmap/strategy_test.go | 10 ---- pkg/registry/core/endpoint/BUILD | 16 ------ pkg/registry/core/endpoint/storage/storage.go | 3 +- pkg/registry/core/endpoint/strategy.go | 30 ----------- pkg/registry/core/endpoint/strategy_test.go | 33 ------------ pkg/registry/core/limitrange/BUILD | 15 ------ .../core/limitrange/storage/storage.go | 3 +- pkg/registry/core/limitrange/strategy.go | 26 ---------- pkg/registry/core/limitrange/strategy_test.go | 33 ------------ pkg/registry/core/podtemplate/BUILD | 15 ------ .../core/podtemplate/storage/storage.go | 3 +- pkg/registry/core/podtemplate/strategy.go | 26 ---------- .../core/podtemplate/strategy_test.go | 33 ------------ pkg/registry/core/resourcequota/BUILD | 5 -- .../core/resourcequota/storage/storage.go | 3 +- pkg/registry/core/resourcequota/strategy.go | 29 ----------- .../core/resourcequota/strategy_test.go | 10 ---- pkg/registry/core/service/BUILD | 5 -- pkg/registry/core/service/storage/storage.go | 3 +- pkg/registry/core/service/strategy.go | 25 --------- pkg/registry/core/service/strategy_test.go | 10 ---- pkg/registry/core/serviceaccount/BUILD | 16 ------ .../core/serviceaccount/storage/storage.go | 3 +- pkg/registry/core/serviceaccount/strategy.go | 29 ----------- .../core/serviceaccount/strategy_test.go | 33 ------------ pkg/registry/extensions/daemonset/BUILD | 7 --- .../extensions/daemonset/storage/storage.go | 3 +- pkg/registry/extensions/daemonset/strategy.go | 31 ----------- .../extensions/daemonset/strategy_test.go | 12 ----- pkg/registry/extensions/deployment/BUILD | 6 --- .../extensions/deployment/storage/storage.go | 3 +- .../extensions/deployment/strategy.go | 31 ----------- .../extensions/deployment/strategy_test.go | 11 ---- pkg/registry/extensions/ingress/BUILD | 6 --- .../extensions/ingress/storage/storage.go | 3 +- pkg/registry/extensions/ingress/strategy.go | 31 ----------- .../extensions/ingress/strategy_test.go | 11 ---- pkg/registry/extensions/networkpolicy/BUILD | 4 -- .../networkpolicy/storage/storage.go | 3 +- .../extensions/networkpolicy/strategy.go | 30 ----------- .../extensions/podsecuritypolicy/BUILD | 4 -- .../podsecuritypolicy/storage/storage.go | 3 +- .../extensions/podsecuritypolicy/strategy.go | 29 ----------- pkg/registry/networking/networkpolicy/BUILD | 4 -- .../networkpolicy/storage/storage.go | 3 +- .../networking/networkpolicy/strategy.go | 29 ----------- pkg/registry/policy/poddisruptionbudget/BUILD | 4 -- .../poddisruptionbudget/storage/storage.go | 3 +- .../policy/poddisruptionbudget/strategy.go | 30 ----------- pkg/registry/rbac/clusterrole/BUILD | 3 -- .../rbac/clusterrole/storage/storage.go | 3 +- pkg/registry/rbac/clusterrole/strategy.go | 28 ---------- pkg/registry/rbac/clusterrolebinding/BUILD | 3 -- .../clusterrolebinding/storage/storage.go | 3 +- .../rbac/clusterrolebinding/strategy.go | 28 ---------- pkg/registry/rbac/role/BUILD | 3 -- pkg/registry/rbac/role/storage/storage.go | 3 +- pkg/registry/rbac/role/strategy.go | 28 ---------- pkg/registry/rbac/rolebinding/BUILD | 3 -- .../rbac/rolebinding/storage/storage.go | 3 +- pkg/registry/rbac/rolebinding/strategy.go | 28 ---------- pkg/registry/scheduling/priorityclass/BUILD | 4 -- .../priorityclass/storage/storage.go | 3 +- .../scheduling/priorityclass/strategy.go | 30 ----------- pkg/registry/settings/podpreset/BUILD | 4 -- .../settings/podpreset/storage/storage.go | 3 +- pkg/registry/settings/podpreset/strategy.go | 30 ----------- pkg/registry/storage/storageclass/BUILD | 4 -- .../storage/storageclass/storage/storage.go | 3 +- pkg/registry/storage/storageclass/strategy.go | 29 ----------- 94 files changed, 27 insertions(+), 1299 deletions(-) delete mode 100644 pkg/registry/autoscaling/horizontalpodautoscaler/strategy_test.go delete mode 100644 pkg/registry/core/endpoint/strategy_test.go delete mode 100644 pkg/registry/core/limitrange/strategy_test.go delete mode 100644 pkg/registry/core/podtemplate/strategy_test.go delete mode 100644 pkg/registry/core/serviceaccount/strategy_test.go diff --git a/hack/.golint_failures b/hack/.golint_failures index 6f06c2e926d..f978757401c 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -309,12 +309,9 @@ pkg/proxy/util pkg/proxy/winuserspace pkg/quota/evaluator/core pkg/quota/generic -pkg/registry/admissionregistration/externaladmissionhookconfiguration pkg/registry/admissionregistration/externaladmissionhookconfiguration/storage -pkg/registry/admissionregistration/initializerconfiguration pkg/registry/admissionregistration/initializerconfiguration/storage pkg/registry/admissionregistration/rest -pkg/registry/apps/controllerrevision pkg/registry/apps/rest pkg/registry/apps/statefulset pkg/registry/apps/statefulset/storage @@ -337,11 +334,9 @@ pkg/registry/certificates/certificates pkg/registry/certificates/certificates/storage pkg/registry/certificates/rest pkg/registry/core/componentstatus -pkg/registry/core/configmap pkg/registry/core/endpoint/storage pkg/registry/core/event pkg/registry/core/event/storage -pkg/registry/core/limitrange pkg/registry/core/limitrange/storage pkg/registry/core/namespace pkg/registry/core/namespace/storage @@ -353,7 +348,6 @@ pkg/registry/core/persistentvolumeclaim pkg/registry/core/persistentvolumeclaim/storage pkg/registry/core/pod pkg/registry/core/pod/rest -pkg/registry/core/podtemplate pkg/registry/core/podtemplate/storage pkg/registry/core/replicationcontroller pkg/registry/core/replicationcontroller/storage @@ -377,13 +371,10 @@ pkg/registry/extensions/deployment pkg/registry/extensions/deployment/storage pkg/registry/extensions/ingress pkg/registry/extensions/ingress/storage -pkg/registry/extensions/networkpolicy pkg/registry/extensions/networkpolicy/storage -pkg/registry/extensions/podsecuritypolicy pkg/registry/extensions/replicaset pkg/registry/extensions/replicaset/storage pkg/registry/extensions/rest -pkg/registry/networking/networkpolicy pkg/registry/networking/networkpolicy/storage pkg/registry/networking/rest pkg/registry/policy/poddisruptionbudget @@ -404,7 +395,6 @@ pkg/registry/rbac/validation pkg/registry/registrytest pkg/registry/scheduling/priorityclass/storage pkg/registry/scheduling/rest -pkg/registry/settings/podpreset pkg/registry/settings/podpreset/storage pkg/registry/settings/rest pkg/registry/storage/rest diff --git a/pkg/registry/admissionregistration/externaladmissionhookconfiguration/BUILD b/pkg/registry/admissionregistration/externaladmissionhookconfiguration/BUILD index a699ba6685f..e391b43c2d2 100644 --- a/pkg/registry/admissionregistration/externaladmissionhookconfiguration/BUILD +++ b/pkg/registry/admissionregistration/externaladmissionhookconfiguration/BUILD @@ -18,13 +18,9 @@ go_library( "//pkg/api:go_default_library", "//pkg/apis/admissionregistration:go_default_library", "//pkg/apis/admissionregistration/validation:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/admissionregistration/externaladmissionhookconfiguration/storage/storage.go b/pkg/registry/admissionregistration/externaladmissionhookconfiguration/storage/storage.go index 8e7d570bcb0..86f5c7edd7e 100644 --- a/pkg/registry/admissionregistration/externaladmissionhookconfiguration/storage/storage.go +++ b/pkg/registry/admissionregistration/externaladmissionhookconfiguration/storage/storage.go @@ -40,7 +40,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*admissionregistration.ExternalAdmissionHookConfiguration).Name, nil }, - PredicateFunc: externaladmissionhookconfiguration.MatchExternalAdmissionHookConfiguration, DefaultQualifiedResource: admissionregistration.Resource("externaladmissionhookconfigurations"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("externaladmissionhookconfigurations"), @@ -48,7 +47,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { UpdateStrategy: externaladmissionhookconfiguration.Strategy, DeleteStrategy: externaladmissionhookconfiguration.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: externaladmissionhookconfiguration.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/admissionregistration/externaladmissionhookconfiguration/strategy.go b/pkg/registry/admissionregistration/externaladmissionhookconfiguration/strategy.go index 8876a40b42a..d6d26379542 100644 --- a/pkg/registry/admissionregistration/externaladmissionhookconfiguration/strategy.go +++ b/pkg/registry/admissionregistration/externaladmissionhookconfiguration/strategy.go @@ -17,16 +17,11 @@ limitations under the License. package externaladmissionhookconfiguration import ( - "fmt" "reflect" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/admissionregistration" @@ -93,28 +88,3 @@ func (externaladmissionhookConfigurationStrategy) ValidateUpdate(ctx genericapir func (externaladmissionhookConfigurationStrategy) AllowUnconditionalUpdate() bool { return false } - -// MatchReplicaSet is the filter used by the generic etcd backend to route -// watch events from etcd to clients of the apiserver only interested in specific -// labels/fields. -func MatchExternalAdmissionHookConfiguration(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - ic, ok := obj.(*admissionregistration.ExternalAdmissionHookConfiguration) - if !ok { - return nil, nil, false, fmt.Errorf("Given object is not a ExternalAdmissionHookConfiguration.") - } - return labels.Set(ic.ObjectMeta.Labels), ExternalAdmissionHookConfigurationToSelectableFields(ic), ic.Initializers != nil, nil -} - -// ExternalAdmissionHookConfigurationToSelectableFields returns a field set that represents the object. -func ExternalAdmissionHookConfigurationToSelectableFields(ic *admissionregistration.ExternalAdmissionHookConfiguration) fields.Set { - return generic.ObjectMetaFieldsSet(&ic.ObjectMeta, false) -} diff --git a/pkg/registry/admissionregistration/initializerconfiguration/BUILD b/pkg/registry/admissionregistration/initializerconfiguration/BUILD index 0d42bfc42e6..f8320d35ac7 100644 --- a/pkg/registry/admissionregistration/initializerconfiguration/BUILD +++ b/pkg/registry/admissionregistration/initializerconfiguration/BUILD @@ -18,13 +18,9 @@ go_library( "//pkg/api:go_default_library", "//pkg/apis/admissionregistration:go_default_library", "//pkg/apis/admissionregistration/validation:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/admissionregistration/initializerconfiguration/storage/storage.go b/pkg/registry/admissionregistration/initializerconfiguration/storage/storage.go index becea1ae56b..590759a9165 100644 --- a/pkg/registry/admissionregistration/initializerconfiguration/storage/storage.go +++ b/pkg/registry/admissionregistration/initializerconfiguration/storage/storage.go @@ -40,7 +40,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*admissionregistration.InitializerConfiguration).Name, nil }, - PredicateFunc: initializerconfiguration.MatchInitializerConfiguration, DefaultQualifiedResource: admissionregistration.Resource("initializerconfigurations"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("initializerconfigurations"), @@ -48,7 +47,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { UpdateStrategy: initializerconfiguration.Strategy, DeleteStrategy: initializerconfiguration.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: initializerconfiguration.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/admissionregistration/initializerconfiguration/strategy.go b/pkg/registry/admissionregistration/initializerconfiguration/strategy.go index 6484e5c70b2..3f0e73925bc 100644 --- a/pkg/registry/admissionregistration/initializerconfiguration/strategy.go +++ b/pkg/registry/admissionregistration/initializerconfiguration/strategy.go @@ -17,16 +17,11 @@ limitations under the License. package initializerconfiguration import ( - "fmt" "reflect" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/admissionregistration" @@ -93,28 +88,3 @@ func (initializerConfigurationStrategy) ValidateUpdate(ctx genericapirequest.Con func (initializerConfigurationStrategy) AllowUnconditionalUpdate() bool { return false } - -// MatchReplicaSet is the filter used by the generic etcd backend to route -// watch events from etcd to clients of the apiserver only interested in specific -// labels/fields. -func MatchInitializerConfiguration(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - ic, ok := obj.(*admissionregistration.InitializerConfiguration) - if !ok { - return nil, nil, false, fmt.Errorf("Given object is not a InitializerConfiguration.") - } - return labels.Set(ic.ObjectMeta.Labels), InitializerConfigurationToSelectableFields(ic), ic.ObjectMeta.Initializers != nil, nil -} - -// InitializerConfigurationToSelectableFields returns a field set that represents the object. -func InitializerConfigurationToSelectableFields(ic *admissionregistration.InitializerConfiguration) fields.Set { - return generic.ObjectMetaFieldsSet(&ic.ObjectMeta, false) -} diff --git a/pkg/registry/apps/controllerrevision/BUILD b/pkg/registry/apps/controllerrevision/BUILD index e656b6cb948..51490282900 100644 --- a/pkg/registry/apps/controllerrevision/BUILD +++ b/pkg/registry/apps/controllerrevision/BUILD @@ -17,8 +17,6 @@ go_test( "//pkg/api:go_default_library", "//pkg/apis/apps:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", ], @@ -35,14 +33,10 @@ go_library( "//pkg/api:go_default_library", "//pkg/apis/apps:go_default_library", "//pkg/apis/apps/validation:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/apps/controllerrevision/storage/storage.go b/pkg/registry/apps/controllerrevision/storage/storage.go index 556d5ef1be5..8c4d7024fdb 100644 --- a/pkg/registry/apps/controllerrevision/storage/storage.go +++ b/pkg/registry/apps/controllerrevision/storage/storage.go @@ -37,7 +37,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &apps.ControllerRevision{} }, NewListFunc: func() runtime.Object { return &apps.ControllerRevisionList{} }, - PredicateFunc: controllerrevision.MatchControllerRevision, DefaultQualifiedResource: apps.Resource("controllerrevisions"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("controllerrevisions"), @@ -45,7 +44,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { UpdateStrategy: controllerrevision.Strategy, DeleteStrategy: controllerrevision.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: controllerrevision.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) } diff --git a/pkg/registry/apps/controllerrevision/strategy.go b/pkg/registry/apps/controllerrevision/strategy.go index 89cb78ab9e6..f6f207b9053 100644 --- a/pkg/registry/apps/controllerrevision/strategy.go +++ b/pkg/registry/apps/controllerrevision/strategy.go @@ -17,16 +17,10 @@ limitations under the License. package controllerrevision import ( - "errors" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/rest" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/apps" @@ -83,26 +77,3 @@ func (strategy) ValidateUpdate(ctx genericapirequest.Context, newObj, oldObj run oldRevision, newRevision := oldObj.(*apps.ControllerRevision), newObj.(*apps.ControllerRevision) return validation.ValidateControllerRevisionUpdate(newRevision, oldRevision) } - -// ControllerRevisionToSelectableFields returns a field set that represents the object for matching purposes. -func ControllerRevisionToSelectableFields(revision *apps.ControllerRevision) fields.Set { - return generic.ObjectMetaFieldsSet(&revision.ObjectMeta, true) -} - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - history, ok := obj.(*apps.ControllerRevision) - if !ok { - return nil, nil, false, errors.New("supplied object is not an ControllerRevision") - } - return labels.Set(history.ObjectMeta.Labels), ControllerRevisionToSelectableFields(history), history.Initializers != nil, nil -} - -// MatchControllerRevision returns a generic matcher for a given label and field selector. -func MatchControllerRevision(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} diff --git a/pkg/registry/apps/controllerrevision/strategy_test.go b/pkg/registry/apps/controllerrevision/strategy_test.go index bba85db130c..594e33277ac 100644 --- a/pkg/registry/apps/controllerrevision/strategy_test.go +++ b/pkg/registry/apps/controllerrevision/strategy_test.go @@ -20,8 +20,6 @@ import ( "testing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/kubernetes/pkg/api" @@ -127,55 +125,6 @@ func TestStrategy_ValidateUpdate(t *testing.T) { } } -func TestControllerRevisionToSelectableFields(t *testing.T) { - rev := newControllerRevision("validname", "validns", newObject(), 0) - fieldSet := ControllerRevisionToSelectableFields(rev) - if fieldSet.Get("metadata.name") != rev.Name { - t.Errorf("expeted %s found %s", rev.Name, fieldSet.Get("metadata.name")) - } - if fieldSet.Get("metadata.namespace") != rev.Namespace { - t.Errorf("expeted %s found %s", rev.Namespace, fieldSet.Get("metadata.namespace")) - } -} - -func TestGetAttrs(t *testing.T) { - rev := newControllerRevision("validname", "validns", newObject(), 0) - labelSet, fieldSet, uninitialized, err := GetAttrs(rev) - if err != nil { - t.Fatal(err) - } - if uninitialized { - t.Errorf("unexpected attrs") - } - if fieldSet.Get("metadata.name") != rev.Name { - t.Errorf("expeted %s found %s", rev.Name, fieldSet.Get("metadata.name")) - } - if fieldSet.Get("metadata.namespace") != rev.Namespace { - t.Errorf("expeted %s found %s", rev.Namespace, fieldSet.Get("metadata.namespace")) - } - if labelSet.Get("foo") != rev.Labels["foo"] { - t.Errorf("expected %s found %s", rev.Labels["foo"], labelSet.Get("foo")) - } -} - -func TestMatchControllerRevision(t *testing.T) { - rev := newControllerRevision("validname", "validns", newObject(), 0) - ls := labels.SelectorFromSet(labels.Set(rev.Labels)) - pred := MatchControllerRevision(ls, nil) - if matches, err := pred.Matches(rev); err != nil { - t.Error(err) - } else if !matches { - t.Error("failed to match ControllerRevision by labels") - } - fs := fields.SelectorFromSet(ControllerRevisionToSelectableFields(rev)) - pred = MatchControllerRevision(ls, fs) - if matches, err := pred.Matches(rev); err != nil { - t.Error(err) - } else if !matches { - t.Error("failed to match ControllerRevision by fields") - } -} - func newControllerRevision(name, namespace string, data runtime.Object, revision int64) *apps.ControllerRevision { return &apps.ControllerRevision{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/registry/apps/statefulset/BUILD b/pkg/registry/apps/statefulset/BUILD index ca2c3e58142..c9d4f03c723 100644 --- a/pkg/registry/apps/statefulset/BUILD +++ b/pkg/registry/apps/statefulset/BUILD @@ -20,14 +20,10 @@ go_library( "//pkg/apis/apps:go_default_library", "//pkg/apis/apps/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/apps/statefulset/storage/storage.go b/pkg/registry/apps/statefulset/storage/storage.go index d40f1d14c5e..dac28b0e846 100644 --- a/pkg/registry/apps/statefulset/storage/storage.go +++ b/pkg/registry/apps/statefulset/storage/storage.go @@ -40,7 +40,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { Copier: api.Scheme, NewFunc: func() runtime.Object { return &appsapi.StatefulSet{} }, NewListFunc: func() runtime.Object { return &appsapi.StatefulSetList{} }, - PredicateFunc: statefulset.MatchStatefulSet, DefaultQualifiedResource: appsapi.Resource("statefulsets"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("statefulsets"), @@ -48,7 +47,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { UpdateStrategy: statefulset.Strategy, DeleteStrategy: statefulset.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: statefulset.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/apps/statefulset/strategy.go b/pkg/registry/apps/statefulset/strategy.go index eda0b2a1fe8..9a737e8cfd2 100644 --- a/pkg/registry/apps/statefulset/strategy.go +++ b/pkg/registry/apps/statefulset/strategy.go @@ -17,17 +17,11 @@ limitations under the License. package statefulset import ( - "fmt" - apiequality "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/rest" - "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/apps" @@ -106,30 +100,6 @@ func (statefulSetStrategy) AllowUnconditionalUpdate() bool { return true } -// StatefulSetToSelectableFields returns a field set that represents the object. -func StatefulSetToSelectableFields(statefulSet *apps.StatefulSet) fields.Set { - return generic.ObjectMetaFieldsSet(&statefulSet.ObjectMeta, true) -} - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - statefulSet, ok := obj.(*apps.StatefulSet) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not an StatefulSet.") - } - return labels.Set(statefulSet.ObjectMeta.Labels), StatefulSetToSelectableFields(statefulSet), statefulSet.Initializers != nil, nil -} - -// MatchStatefulSet is the filter used by the generic etcd backend to watch events -// from etcd to clients of the apiserver only interested in specific labels/fields. -func MatchStatefulSet(label labels.Selector, field fields.Selector) storage.SelectionPredicate { - return storage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - type statefulSetStatusStrategy struct { statefulSetStrategy } diff --git a/pkg/registry/autoscaling/horizontalpodautoscaler/BUILD b/pkg/registry/autoscaling/horizontalpodautoscaler/BUILD index 8a4f6c8af45..fb954d07f4e 100644 --- a/pkg/registry/autoscaling/horizontalpodautoscaler/BUILD +++ b/pkg/registry/autoscaling/horizontalpodautoscaler/BUILD @@ -5,7 +5,6 @@ licenses(["notice"]) load( "@io_bazel_rules_go//go:def.bzl", "go_library", - "go_test", ) go_library( @@ -19,29 +18,13 @@ go_library( "//pkg/api:go_default_library", "//pkg/apis/autoscaling:go_default_library", "//pkg/apis/autoscaling/validation:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) -go_test( - name = "go_default_test", - srcs = ["strategy_test.go"], - library = ":go_default_library", - tags = ["automanaged"], - deps = [ - "//pkg/api:go_default_library", - "//pkg/api/testapi:go_default_library", - "//pkg/api/testing:go_default_library", - "//pkg/apis/autoscaling:go_default_library", - ], -) - filegroup( name = "package-srcs", srcs = glob(["**"]), diff --git a/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go b/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go index 3421192cd07..fb5b598d3a3 100644 --- a/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go +++ b/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go @@ -39,7 +39,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { Copier: api.Scheme, NewFunc: func() runtime.Object { return &autoscaling.HorizontalPodAutoscaler{} }, NewListFunc: func() runtime.Object { return &autoscaling.HorizontalPodAutoscalerList{} }, - PredicateFunc: horizontalpodautoscaler.MatchAutoscaler, DefaultQualifiedResource: autoscaling.Resource("horizontalpodautoscalers"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("horizontalpodautoscalers"), @@ -47,7 +46,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { UpdateStrategy: horizontalpodautoscaler.Strategy, DeleteStrategy: horizontalpodautoscaler.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: horizontalpodautoscaler.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/autoscaling/horizontalpodautoscaler/strategy.go b/pkg/registry/autoscaling/horizontalpodautoscaler/strategy.go index e7b05126e18..d9c81ab07e5 100644 --- a/pkg/registry/autoscaling/horizontalpodautoscaler/strategy.go +++ b/pkg/registry/autoscaling/horizontalpodautoscaler/strategy.go @@ -17,14 +17,9 @@ limitations under the License. package horizontalpodautoscaler import ( - "fmt" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/autoscaling" @@ -86,27 +81,6 @@ func (autoscalerStrategy) AllowUnconditionalUpdate() bool { return true } -func AutoscalerToSelectableFields(hpa *autoscaling.HorizontalPodAutoscaler) fields.Set { - return nil -} - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - hpa, ok := obj.(*autoscaling.HorizontalPodAutoscaler) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not a horizontal pod autoscaler.") - } - return labels.Set(hpa.ObjectMeta.Labels), AutoscalerToSelectableFields(hpa), hpa.Initializers != nil, nil -} - -func MatchAutoscaler(label labels.Selector, field fields.Selector) storage.SelectionPredicate { - return storage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - type autoscalerStatusStrategy struct { autoscalerStrategy } diff --git a/pkg/registry/autoscaling/horizontalpodautoscaler/strategy_test.go b/pkg/registry/autoscaling/horizontalpodautoscaler/strategy_test.go deleted file mode 100644 index f5df8fa211b..00000000000 --- a/pkg/registry/autoscaling/horizontalpodautoscaler/strategy_test.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -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 horizontalpodautoscaler - -import ( - "testing" - - _ "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - apitesting "k8s.io/kubernetes/pkg/api/testing" - "k8s.io/kubernetes/pkg/apis/autoscaling" -) - -func TestSelectableFieldLabelConversions(t *testing.T) { - apitesting.TestSelectableFieldLabelConversionsOfKind(t, - testapi.Autoscaling.GroupVersion().String(), - "Autoscaler", - AutoscalerToSelectableFields(&autoscaling.HorizontalPodAutoscaler{}), - nil, - ) -} diff --git a/pkg/registry/certificates/certificates/BUILD b/pkg/registry/certificates/certificates/BUILD index 87f6427ff28..7b2d0f21dc0 100644 --- a/pkg/registry/certificates/certificates/BUILD +++ b/pkg/registry/certificates/certificates/BUILD @@ -22,15 +22,11 @@ go_library( "//pkg/apis/certificates/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/certificates/certificates/storage/storage.go b/pkg/registry/certificates/certificates/storage/storage.go index 2da56ee106f..372717f3ee3 100644 --- a/pkg/registry/certificates/certificates/storage/storage.go +++ b/pkg/registry/certificates/certificates/storage/storage.go @@ -39,7 +39,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Approva Copier: api.Scheme, NewFunc: func() runtime.Object { return &certificates.CertificateSigningRequest{} }, NewListFunc: func() runtime.Object { return &certificates.CertificateSigningRequestList{} }, - PredicateFunc: csrregistry.Matcher, DefaultQualifiedResource: certificates.Resource("certificatesigningrequests"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("certificatesigningrequests"), @@ -47,7 +46,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Approva UpdateStrategy: csrregistry.Strategy, DeleteStrategy: csrregistry.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: csrregistry.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/certificates/certificates/strategy.go b/pkg/registry/certificates/certificates/strategy.go index 15a3794e6f7..8ef65a2c01a 100644 --- a/pkg/registry/certificates/certificates/strategy.go +++ b/pkg/registry/certificates/certificates/strategy.go @@ -19,13 +19,9 @@ package certificates import ( "fmt" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/certificates" @@ -176,26 +172,3 @@ func (csrApprovalStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, func (csrApprovalStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { return validation.ValidateCertificateSigningRequestUpdate(obj.(*certificates.CertificateSigningRequest), old.(*certificates.CertificateSigningRequest)) } - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - sa, ok := obj.(*certificates.CertificateSigningRequest) - if !ok { - return nil, nil, false, fmt.Errorf("not a CertificateSigningRequest") - } - return labels.Set(sa.Labels), SelectableFields(sa), sa.Initializers != nil, nil -} - -// Matcher returns a generic matcher for a given label and field selector. -func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - -// SelectableFields returns a field set that can be used for filter selection -func SelectableFields(obj *certificates.CertificateSigningRequest) fields.Set { - return generic.ObjectMetaFieldsSet(&obj.ObjectMeta, false) -} diff --git a/pkg/registry/core/configmap/BUILD b/pkg/registry/core/configmap/BUILD index 6fb83539718..ff694136733 100644 --- a/pkg/registry/core/configmap/BUILD +++ b/pkg/registry/core/configmap/BUILD @@ -21,15 +21,11 @@ go_library( "//pkg/api/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) @@ -41,7 +37,6 @@ go_test( tags = ["automanaged"], deps = [ "//pkg/api:go_default_library", - "//pkg/api/testing:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", ], diff --git a/pkg/registry/core/configmap/storage/storage.go b/pkg/registry/core/configmap/storage/storage.go index 8aed341b7c0..4a22ed8a310 100644 --- a/pkg/registry/core/configmap/storage/storage.go +++ b/pkg/registry/core/configmap/storage/storage.go @@ -37,7 +37,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &api.ConfigMap{} }, NewListFunc: func() runtime.Object { return &api.ConfigMapList{} }, - PredicateFunc: configmap.MatchConfigMap, DefaultQualifiedResource: api.Resource("configmaps"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("configmaps"), @@ -45,7 +44,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { UpdateStrategy: configmap.Strategy, DeleteStrategy: configmap.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: configmap.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/core/configmap/strategy.go b/pkg/registry/core/configmap/strategy.go index abb663882fd..b44da007423 100644 --- a/pkg/registry/core/configmap/strategy.go +++ b/pkg/registry/core/configmap/strategy.go @@ -17,16 +17,10 @@ limitations under the License. package configmap import ( - "fmt" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/rest" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/validation" @@ -84,26 +78,3 @@ func (strategy) ValidateUpdate(ctx genericapirequest.Context, newObj, oldObj run return validation.ValidateConfigMapUpdate(newCfg, oldCfg) } - -// ConfigMapToSelectableFields returns a field set that represents the object for matching purposes. -func ConfigMapToSelectableFields(cfg *api.ConfigMap) fields.Set { - return generic.ObjectMetaFieldsSet(&cfg.ObjectMeta, true) -} - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - cfg, ok := obj.(*api.ConfigMap) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not a ConfigMap") - } - return labels.Set(cfg.ObjectMeta.Labels), ConfigMapToSelectableFields(cfg), cfg.Initializers != nil, nil -} - -// MatchConfigMap returns a generic matcher for a given label and field selector. -func MatchConfigMap(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} diff --git a/pkg/registry/core/configmap/strategy_test.go b/pkg/registry/core/configmap/strategy_test.go index c001f59c420..a6ec3254e19 100644 --- a/pkg/registry/core/configmap/strategy_test.go +++ b/pkg/registry/core/configmap/strategy_test.go @@ -22,7 +22,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/kubernetes/pkg/api" - apitesting "k8s.io/kubernetes/pkg/api/testing" ) func TestConfigMapStrategy(t *testing.T) { @@ -69,12 +68,3 @@ func TestConfigMapStrategy(t *testing.T) { t.Errorf("Expected a validation error") } } - -func TestSelectableFieldLabelConversions(t *testing.T) { - apitesting.TestSelectableFieldLabelConversionsOfKind(t, - api.Registry.GroupOrDie(api.GroupName).GroupVersion.String(), - "ConfigMap", - ConfigMapToSelectableFields(&api.ConfigMap{}), - nil, - ) -} diff --git a/pkg/registry/core/endpoint/BUILD b/pkg/registry/core/endpoint/BUILD index 0c6153559b9..3ccf3b9c5c7 100644 --- a/pkg/registry/core/endpoint/BUILD +++ b/pkg/registry/core/endpoint/BUILD @@ -5,7 +5,6 @@ licenses(["notice"]) load( "@io_bazel_rules_go//go:def.bzl", "go_library", - "go_test", ) go_library( @@ -22,30 +21,15 @@ go_library( "//pkg/api/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) -go_test( - name = "go_default_test", - srcs = ["strategy_test.go"], - library = ":go_default_library", - tags = ["automanaged"], - deps = [ - "//pkg/api:go_default_library", - "//pkg/api/testing:go_default_library", - ], -) - filegroup( name = "package-srcs", srcs = glob(["**"]), diff --git a/pkg/registry/core/endpoint/storage/storage.go b/pkg/registry/core/endpoint/storage/storage.go index 44abfc437a8..28e30837199 100644 --- a/pkg/registry/core/endpoint/storage/storage.go +++ b/pkg/registry/core/endpoint/storage/storage.go @@ -36,7 +36,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &api.Endpoints{} }, NewListFunc: func() runtime.Object { return &api.EndpointsList{} }, - PredicateFunc: endpoint.MatchEndpoints, DefaultQualifiedResource: api.Resource("endpoints"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("endpoints"), @@ -44,7 +43,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { UpdateStrategy: endpoint.Strategy, DeleteStrategy: endpoint.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: endpoint.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/core/endpoint/strategy.go b/pkg/registry/core/endpoint/strategy.go index c50894b2e69..b3a6d4ee78b 100644 --- a/pkg/registry/core/endpoint/strategy.go +++ b/pkg/registry/core/endpoint/strategy.go @@ -17,15 +17,9 @@ limitations under the License. package endpoint import ( - "fmt" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - pkgstorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" endptspkg "k8s.io/kubernetes/pkg/api/endpoints" @@ -80,27 +74,3 @@ func (endpointsStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old func (endpointsStrategy) AllowUnconditionalUpdate() bool { return true } - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - endpoints, ok := obj.(*api.Endpoints) - if !ok { - return nil, nil, false, fmt.Errorf("invalid object type %#v", obj) - } - return endpoints.Labels, EndpointsToSelectableFields(endpoints), endpoints.Initializers != nil, nil -} - -// MatchEndpoints returns a generic matcher for a given label and field selector. -func MatchEndpoints(label labels.Selector, field fields.Selector) pkgstorage.SelectionPredicate { - return pkgstorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - -// EndpointsToSelectableFields returns a field set that represents the object -// TODO: fields are not labels, and the validation rules for them do not apply. -func EndpointsToSelectableFields(endpoints *api.Endpoints) fields.Set { - return generic.ObjectMetaFieldsSet(&endpoints.ObjectMeta, true) -} diff --git a/pkg/registry/core/endpoint/strategy_test.go b/pkg/registry/core/endpoint/strategy_test.go deleted file mode 100644 index b520dd4c9d8..00000000000 --- a/pkg/registry/core/endpoint/strategy_test.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -Copyright 2014 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package endpoint - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - apitesting "k8s.io/kubernetes/pkg/api/testing" -) - -func TestSelectableFieldLabelConversions(t *testing.T) { - apitesting.TestSelectableFieldLabelConversionsOfKind(t, - api.Registry.GroupOrDie(api.GroupName).GroupVersion.String(), - "Endpoints", - EndpointsToSelectableFields(&api.Endpoints{}), - nil, - ) -} diff --git a/pkg/registry/core/limitrange/BUILD b/pkg/registry/core/limitrange/BUILD index 12732c4d4a4..a8ee1cf5167 100644 --- a/pkg/registry/core/limitrange/BUILD +++ b/pkg/registry/core/limitrange/BUILD @@ -5,7 +5,6 @@ licenses(["notice"]) load( "@io_bazel_rules_go//go:def.bzl", "go_library", - "go_test", ) go_library( @@ -18,28 +17,14 @@ go_library( deps = [ "//pkg/api:go_default_library", "//pkg/api/validation:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) -go_test( - name = "go_default_test", - srcs = ["strategy_test.go"], - library = ":go_default_library", - tags = ["automanaged"], - deps = [ - "//pkg/api:go_default_library", - "//pkg/api/testing:go_default_library", - ], -) - filegroup( name = "package-srcs", srcs = glob(["**"]), diff --git a/pkg/registry/core/limitrange/storage/storage.go b/pkg/registry/core/limitrange/storage/storage.go index ba29960e362..68d43499d64 100644 --- a/pkg/registry/core/limitrange/storage/storage.go +++ b/pkg/registry/core/limitrange/storage/storage.go @@ -36,7 +36,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &api.LimitRange{} }, NewListFunc: func() runtime.Object { return &api.LimitRangeList{} }, - PredicateFunc: limitrange.MatchLimitRange, DefaultQualifiedResource: api.Resource("limitranges"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("limitranges"), @@ -45,7 +44,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { DeleteStrategy: limitrange.Strategy, ExportStrategy: limitrange.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: limitrange.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/core/limitrange/strategy.go b/pkg/registry/core/limitrange/strategy.go index 7f49d635a4f..25b9f14135a 100644 --- a/pkg/registry/core/limitrange/strategy.go +++ b/pkg/registry/core/limitrange/strategy.go @@ -17,15 +17,10 @@ limitations under the License. package limitrange import ( - "fmt" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/validation" @@ -76,30 +71,9 @@ func (limitrangeStrategy) AllowUnconditionalUpdate() bool { return true } -func LimitRangeToSelectableFields(limitRange *api.LimitRange) fields.Set { - return nil -} - func (limitrangeStrategy) Export(genericapirequest.Context, runtime.Object, bool) error { // Copied from OpenShift exporter // TODO: this needs to be fixed // limitrange.Strategy.PrepareForCreate(ctx, obj) return nil } - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - lr, ok := obj.(*api.LimitRange) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not a limit range.") - } - return labels.Set(lr.ObjectMeta.Labels), LimitRangeToSelectableFields(lr), lr.Initializers != nil, nil -} - -func MatchLimitRange(label labels.Selector, field fields.Selector) storage.SelectionPredicate { - return storage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} diff --git a/pkg/registry/core/limitrange/strategy_test.go b/pkg/registry/core/limitrange/strategy_test.go deleted file mode 100644 index 8c7c98e6efc..00000000000 --- a/pkg/registry/core/limitrange/strategy_test.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -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 limitrange - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - apitesting "k8s.io/kubernetes/pkg/api/testing" -) - -func TestSelectableFieldLabelConversions(t *testing.T) { - apitesting.TestSelectableFieldLabelConversionsOfKind(t, - api.Registry.GroupOrDie(api.GroupName).GroupVersion.String(), - "LimitRange", - LimitRangeToSelectableFields(&api.LimitRange{}), - nil, - ) -} diff --git a/pkg/registry/core/podtemplate/BUILD b/pkg/registry/core/podtemplate/BUILD index ae3e776b5b8..24f51cc7e29 100644 --- a/pkg/registry/core/podtemplate/BUILD +++ b/pkg/registry/core/podtemplate/BUILD @@ -5,7 +5,6 @@ licenses(["notice"]) load( "@io_bazel_rules_go//go:def.bzl", "go_library", - "go_test", ) go_library( @@ -18,27 +17,13 @@ go_library( deps = [ "//pkg/api:go_default_library", "//pkg/api/validation:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) -go_test( - name = "go_default_test", - srcs = ["strategy_test.go"], - library = ":go_default_library", - tags = ["automanaged"], - deps = [ - "//pkg/api:go_default_library", - "//pkg/api/testing:go_default_library", - ], -) - filegroup( name = "package-srcs", srcs = glob(["**"]), diff --git a/pkg/registry/core/podtemplate/storage/storage.go b/pkg/registry/core/podtemplate/storage/storage.go index 13d1d436594..9376d4b4ba4 100644 --- a/pkg/registry/core/podtemplate/storage/storage.go +++ b/pkg/registry/core/podtemplate/storage/storage.go @@ -35,7 +35,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &api.PodTemplate{} }, NewListFunc: func() runtime.Object { return &api.PodTemplateList{} }, - PredicateFunc: podtemplate.MatchPodTemplate, DefaultQualifiedResource: api.Resource("podtemplates"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("podtemplates"), @@ -46,7 +45,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { ReturnDeletedObject: true, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: podtemplate.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/core/podtemplate/strategy.go b/pkg/registry/core/podtemplate/strategy.go index 87012c541cb..77a1dee56a3 100644 --- a/pkg/registry/core/podtemplate/strategy.go +++ b/pkg/registry/core/podtemplate/strategy.go @@ -17,14 +17,9 @@ limitations under the License. package podtemplate import ( - "fmt" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/validation" @@ -83,24 +78,3 @@ func (podTemplateStrategy) Export(ctx genericapirequest.Context, obj runtime.Obj // Do nothing return nil } - -func PodTemplateToSelectableFields(podTemplate *api.PodTemplate) fields.Set { - return nil -} - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - pt, ok := obj.(*api.PodTemplate) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not a pod template.") - } - return labels.Set(pt.ObjectMeta.Labels), PodTemplateToSelectableFields(pt), pt.Initializers != nil, nil -} - -func MatchPodTemplate(label labels.Selector, field fields.Selector) storage.SelectionPredicate { - return storage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} diff --git a/pkg/registry/core/podtemplate/strategy_test.go b/pkg/registry/core/podtemplate/strategy_test.go deleted file mode 100644 index 0f82101df0b..00000000000 --- a/pkg/registry/core/podtemplate/strategy_test.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -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 podtemplate - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - apitesting "k8s.io/kubernetes/pkg/api/testing" -) - -func TestSelectableFieldLabelConversions(t *testing.T) { - apitesting.TestSelectableFieldLabelConversionsOfKind(t, - api.Registry.GroupOrDie(api.GroupName).GroupVersion.String(), - "PodTemplate", - PodTemplateToSelectableFields(&api.PodTemplate{}), - nil, - ) -} diff --git a/pkg/registry/core/resourcequota/BUILD b/pkg/registry/core/resourcequota/BUILD index d77733d99bb..dd19588a71d 100644 --- a/pkg/registry/core/resourcequota/BUILD +++ b/pkg/registry/core/resourcequota/BUILD @@ -18,13 +18,9 @@ go_library( deps = [ "//pkg/api:go_default_library", "//pkg/api/validation:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) @@ -36,7 +32,6 @@ go_test( tags = ["automanaged"], deps = [ "//pkg/api:go_default_library", - "//pkg/api/testing:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", diff --git a/pkg/registry/core/resourcequota/storage/storage.go b/pkg/registry/core/resourcequota/storage/storage.go index 90638de8a6f..c708d114a41 100644 --- a/pkg/registry/core/resourcequota/storage/storage.go +++ b/pkg/registry/core/resourcequota/storage/storage.go @@ -38,7 +38,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { Copier: api.Scheme, NewFunc: func() runtime.Object { return &api.ResourceQuota{} }, NewListFunc: func() runtime.Object { return &api.ResourceQuotaList{} }, - PredicateFunc: resourcequota.MatchResourceQuota, DefaultQualifiedResource: api.Resource("resourcequotas"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("resourcequotas"), @@ -47,7 +46,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { DeleteStrategy: resourcequota.Strategy, ReturnDeletedObject: true, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: resourcequota.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/core/resourcequota/strategy.go b/pkg/registry/core/resourcequota/strategy.go index 6211e8f4bb0..e9c26c009ca 100644 --- a/pkg/registry/core/resourcequota/strategy.go +++ b/pkg/registry/core/resourcequota/strategy.go @@ -17,15 +17,9 @@ limitations under the License. package resourcequota import ( - "fmt" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/validation" @@ -99,26 +93,3 @@ func (resourcequotaStatusStrategy) PrepareForUpdate(ctx genericapirequest.Contex func (resourcequotaStatusStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { return validation.ValidateResourceQuotaStatusUpdate(obj.(*api.ResourceQuota), old.(*api.ResourceQuota)) } - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - resourcequotaObj, ok := obj.(*api.ResourceQuota) - if !ok { - return nil, nil, false, fmt.Errorf("not a resourcequota") - } - return labels.Set(resourcequotaObj.Labels), ResourceQuotaToSelectableFields(resourcequotaObj), resourcequotaObj.Initializers != nil, nil -} - -// MatchResourceQuota returns a generic matcher for a given label and field selector. -func MatchResourceQuota(label labels.Selector, field fields.Selector) storage.SelectionPredicate { - return storage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - -// ResourceQuotaToSelectableFields returns a field set that represents the object -func ResourceQuotaToSelectableFields(resourcequota *api.ResourceQuota) fields.Set { - return generic.ObjectMetaFieldsSet(&resourcequota.ObjectMeta, true) -} diff --git a/pkg/registry/core/resourcequota/strategy_test.go b/pkg/registry/core/resourcequota/strategy_test.go index bde07ac7a6f..6ec3816fc06 100644 --- a/pkg/registry/core/resourcequota/strategy_test.go +++ b/pkg/registry/core/resourcequota/strategy_test.go @@ -23,7 +23,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/kubernetes/pkg/api" - apitesting "k8s.io/kubernetes/pkg/api/testing" ) func TestResourceQuotaStrategy(t *testing.T) { @@ -59,12 +58,3 @@ func TestResourceQuotaStrategy(t *testing.T) { t.Errorf("ResourceQuota does not allow setting status on create") } } - -func TestSelectableFieldLabelConversions(t *testing.T) { - apitesting.TestSelectableFieldLabelConversionsOfKind(t, - api.Registry.GroupOrDie(api.GroupName).GroupVersion.String(), - "ResourceQuota", - ResourceQuotaToSelectableFields(&api.ResourceQuota{}), - nil, - ) -} diff --git a/pkg/registry/core/service/BUILD b/pkg/registry/core/service/BUILD index 727511864b4..cc97dfc4545 100644 --- a/pkg/registry/core/service/BUILD +++ b/pkg/registry/core/service/BUILD @@ -32,8 +32,6 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/proxy:go_default_library", @@ -41,9 +39,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", "//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library", ], @@ -61,7 +57,6 @@ go_test( "//pkg/api:go_default_library", "//pkg/api/helper:go_default_library", "//pkg/api/service:go_default_library", - "//pkg/api/testing:go_default_library", "//pkg/features:go_default_library", "//pkg/registry/core/service/ipallocator:go_default_library", "//pkg/registry/core/service/portallocator:go_default_library", diff --git a/pkg/registry/core/service/storage/storage.go b/pkg/registry/core/service/storage/storage.go index 6192b2842c8..886f227592e 100644 --- a/pkg/registry/core/service/storage/storage.go +++ b/pkg/registry/core/service/storage/storage.go @@ -38,7 +38,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { Copier: api.Scheme, NewFunc: func() runtime.Object { return &api.Service{} }, NewListFunc: func() runtime.Object { return &api.ServiceList{} }, - PredicateFunc: service.MatchServices, DefaultQualifiedResource: api.Resource("services"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("services"), @@ -47,7 +46,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { DeleteStrategy: service.Strategy, ExportStrategy: service.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: service.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/core/service/strategy.go b/pkg/registry/core/service/strategy.go index 7736f6c2cce..1d3da254978 100644 --- a/pkg/registry/core/service/strategy.go +++ b/pkg/registry/core/service/strategy.go @@ -19,13 +19,9 @@ package service import ( "fmt" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/validation" @@ -103,27 +99,6 @@ func (svcStrategy) Export(ctx genericapirequest.Context, obj runtime.Object, exa return nil } -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - service, ok := obj.(*api.Service) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not a service") - } - return labels.Set(service.ObjectMeta.Labels), ServiceToSelectableFields(service), service.Initializers != nil, nil -} - -func MatchServices(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - -func ServiceToSelectableFields(service *api.Service) fields.Set { - return generic.ObjectMetaFieldsSet(&service.ObjectMeta, true) -} - type serviceStatusStrategy struct { svcStrategy } diff --git a/pkg/registry/core/service/strategy_test.go b/pkg/registry/core/service/strategy_test.go index 7f01450bfda..46e3c65546e 100644 --- a/pkg/registry/core/service/strategy_test.go +++ b/pkg/registry/core/service/strategy_test.go @@ -27,7 +27,6 @@ import ( genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" "k8s.io/kubernetes/pkg/api" - apitesting "k8s.io/kubernetes/pkg/api/testing" ) func TestExportService(t *testing.T) { @@ -212,15 +211,6 @@ func TestBeforeUpdate(t *testing.T) { } } -func TestSelectableFieldLabelConversions(t *testing.T) { - apitesting.TestSelectableFieldLabelConversionsOfKind(t, - api.Registry.GroupOrDie(api.GroupName).GroupVersion.String(), - "Service", - ServiceToSelectableFields(&api.Service{}), - nil, - ) -} - func TestServiceStatusStrategy(t *testing.T) { ctx := genericapirequest.NewDefaultContext() if !StatusStrategy.NamespaceScoped() { diff --git a/pkg/registry/core/serviceaccount/BUILD b/pkg/registry/core/serviceaccount/BUILD index d3e815ffc36..6e460eda990 100644 --- a/pkg/registry/core/serviceaccount/BUILD +++ b/pkg/registry/core/serviceaccount/BUILD @@ -5,7 +5,6 @@ licenses(["notice"]) load( "@io_bazel_rules_go//go:def.bzl", "go_library", - "go_test", ) go_library( @@ -21,30 +20,15 @@ go_library( "//pkg/api/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) -go_test( - name = "go_default_test", - srcs = ["strategy_test.go"], - library = ":go_default_library", - tags = ["automanaged"], - deps = [ - "//pkg/api:go_default_library", - "//pkg/api/testing:go_default_library", - ], -) - filegroup( name = "package-srcs", srcs = glob(["**"]), diff --git a/pkg/registry/core/serviceaccount/storage/storage.go b/pkg/registry/core/serviceaccount/storage/storage.go index 3c9b1d26e94..9d6b8c1ffbb 100644 --- a/pkg/registry/core/serviceaccount/storage/storage.go +++ b/pkg/registry/core/serviceaccount/storage/storage.go @@ -36,7 +36,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &api.ServiceAccount{} }, NewListFunc: func() runtime.Object { return &api.ServiceAccountList{} }, - PredicateFunc: serviceaccount.Matcher, DefaultQualifiedResource: api.Resource("serviceaccounts"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("serviceaccounts"), @@ -45,7 +44,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { DeleteStrategy: serviceaccount.Strategy, ReturnDeletedObject: true, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: serviceaccount.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/core/serviceaccount/strategy.go b/pkg/registry/core/serviceaccount/strategy.go index 2ff37ecd94a..501e0436189 100644 --- a/pkg/registry/core/serviceaccount/strategy.go +++ b/pkg/registry/core/serviceaccount/strategy.go @@ -17,15 +17,9 @@ limitations under the License. package serviceaccount import ( - "fmt" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/validation" @@ -78,26 +72,3 @@ func (strategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.O func (strategy) AllowUnconditionalUpdate() bool { return true } - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - sa, ok := obj.(*api.ServiceAccount) - if !ok { - return nil, nil, false, fmt.Errorf("not a serviceaccount") - } - return labels.Set(sa.Labels), SelectableFields(sa), sa.Initializers != nil, nil -} - -// Matcher returns a generic matcher for a given label and field selector. -func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - -// SelectableFields returns a field set that represents the object -func SelectableFields(obj *api.ServiceAccount) fields.Set { - return generic.ObjectMetaFieldsSet(&obj.ObjectMeta, true) -} diff --git a/pkg/registry/core/serviceaccount/strategy_test.go b/pkg/registry/core/serviceaccount/strategy_test.go deleted file mode 100644 index 76b1cdc57c1..00000000000 --- a/pkg/registry/core/serviceaccount/strategy_test.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -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 serviceaccount - -import ( - "testing" - - "k8s.io/kubernetes/pkg/api" - apitesting "k8s.io/kubernetes/pkg/api/testing" -) - -func TestSelectableFieldLabelConversions(t *testing.T) { - apitesting.TestSelectableFieldLabelConversionsOfKind(t, - api.Registry.GroupOrDie(api.GroupName).GroupVersion.String(), - "ServiceAccount", - SelectableFields(&api.ServiceAccount{}), - nil, - ) -} diff --git a/pkg/registry/extensions/daemonset/BUILD b/pkg/registry/extensions/daemonset/BUILD index 294aaec3378..eb6a4a423fc 100644 --- a/pkg/registry/extensions/daemonset/BUILD +++ b/pkg/registry/extensions/daemonset/BUILD @@ -20,14 +20,10 @@ go_library( "//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) @@ -39,9 +35,6 @@ go_test( tags = ["automanaged"], deps = [ "//pkg/api:go_default_library", - "//pkg/api/testapi:go_default_library", - "//pkg/api/testing:go_default_library", - "//pkg/apis/extensions:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", ], ) diff --git a/pkg/registry/extensions/daemonset/storage/storage.go b/pkg/registry/extensions/daemonset/storage/storage.go index 86e7e45c2fb..b3d754d093f 100644 --- a/pkg/registry/extensions/daemonset/storage/storage.go +++ b/pkg/registry/extensions/daemonset/storage/storage.go @@ -40,7 +40,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { Copier: api.Scheme, NewFunc: func() runtime.Object { return &extensions.DaemonSet{} }, NewListFunc: func() runtime.Object { return &extensions.DaemonSetList{} }, - PredicateFunc: daemonset.MatchDaemonSet, DefaultQualifiedResource: extensions.Resource("daemonsets"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("daemonsets"), @@ -48,7 +47,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { UpdateStrategy: daemonset.Strategy, DeleteStrategy: daemonset.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: daemonset.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/extensions/daemonset/strategy.go b/pkg/registry/extensions/daemonset/strategy.go index 80e03dc1665..c75fa365237 100644 --- a/pkg/registry/extensions/daemonset/strategy.go +++ b/pkg/registry/extensions/daemonset/strategy.go @@ -17,17 +17,11 @@ limitations under the License. package daemonset import ( - "fmt" - apiequality "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/rest" - "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/extensions" @@ -125,31 +119,6 @@ func (daemonSetStrategy) AllowUnconditionalUpdate() bool { return true } -// DaemonSetToSelectableFields returns a field set that represents the object. -func DaemonSetToSelectableFields(daemon *extensions.DaemonSet) fields.Set { - return generic.ObjectMetaFieldsSet(&daemon.ObjectMeta, true) -} - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - ds, ok := obj.(*extensions.DaemonSet) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not a ds.") - } - return labels.Set(ds.ObjectMeta.Labels), DaemonSetToSelectableFields(ds), ds.Initializers != nil, nil -} - -// MatchSetDaemon is the filter used by the generic etcd backend to route -// watch events from etcd to clients of the apiserver only interested in specific -// labels/fields. -func MatchDaemonSet(label labels.Selector, field fields.Selector) storage.SelectionPredicate { - return storage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - type daemonSetStatusStrategy struct { daemonSetStrategy } diff --git a/pkg/registry/extensions/daemonset/strategy_test.go b/pkg/registry/extensions/daemonset/strategy_test.go index 0590c76473a..0f16ce4d96d 100644 --- a/pkg/registry/extensions/daemonset/strategy_test.go +++ b/pkg/registry/extensions/daemonset/strategy_test.go @@ -21,20 +21,8 @@ import ( "k8s.io/apiserver/pkg/registry/rest" _ "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - apitesting "k8s.io/kubernetes/pkg/api/testing" - "k8s.io/kubernetes/pkg/apis/extensions" ) -func TestSelectableFieldLabelConversions(t *testing.T) { - apitesting.TestSelectableFieldLabelConversionsOfKind(t, - testapi.Extensions.GroupVersion().String(), - "DaemonSet", - DaemonSetToSelectableFields(&extensions.DaemonSet{}), - nil, - ) -} - func TestDefaultGarbageCollectionPolicy(t *testing.T) { // Make sure we correctly implement the interface. // Otherwise a typo could silently change the default. diff --git a/pkg/registry/extensions/deployment/BUILD b/pkg/registry/extensions/deployment/BUILD index c4280eaaed3..ff1210ed68b 100644 --- a/pkg/registry/extensions/deployment/BUILD +++ b/pkg/registry/extensions/deployment/BUILD @@ -23,14 +23,10 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) @@ -42,8 +38,6 @@ go_test( tags = ["automanaged"], deps = [ "//pkg/api:go_default_library", - "//pkg/api/testapi:go_default_library", - "//pkg/api/testing:go_default_library", "//pkg/apis/extensions:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", diff --git a/pkg/registry/extensions/deployment/storage/storage.go b/pkg/registry/extensions/deployment/storage/storage.go index d747ecad8af..1e6fcbe83d5 100644 --- a/pkg/registry/extensions/deployment/storage/storage.go +++ b/pkg/registry/extensions/deployment/storage/storage.go @@ -66,7 +66,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Rollbac Copier: api.Scheme, NewFunc: func() runtime.Object { return &extensions.Deployment{} }, NewListFunc: func() runtime.Object { return &extensions.DeploymentList{} }, - PredicateFunc: deployment.MatchDeployment, DefaultQualifiedResource: extensions.Resource("deployments"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("deployments"), @@ -74,7 +73,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Rollbac UpdateStrategy: deployment.Strategy, DeleteStrategy: deployment.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: deployment.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/extensions/deployment/strategy.go b/pkg/registry/extensions/deployment/strategy.go index 819ca5e965f..a90aeb89bae 100644 --- a/pkg/registry/extensions/deployment/strategy.go +++ b/pkg/registry/extensions/deployment/strategy.go @@ -17,17 +17,11 @@ limitations under the License. package deployment import ( - "fmt" - apiequality "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/rest" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/extensions" @@ -119,28 +113,3 @@ func (deploymentStatusStrategy) PrepareForUpdate(ctx genericapirequest.Context, func (deploymentStatusStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { return validation.ValidateDeploymentStatusUpdate(obj.(*extensions.Deployment), old.(*extensions.Deployment)) } - -// DeploymentToSelectableFields returns a field set that represents the object. -func DeploymentToSelectableFields(deployment *extensions.Deployment) fields.Set { - return generic.ObjectMetaFieldsSet(&deployment.ObjectMeta, true) -} - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - deployment, ok := obj.(*extensions.Deployment) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not a deployment.") - } - return labels.Set(deployment.ObjectMeta.Labels), DeploymentToSelectableFields(deployment), deployment.Initializers != nil, nil -} - -// MatchDeployment is the filter used by the generic etcd backend to route -// watch events from etcd to clients of the apiserver only interested in specific -// labels/fields. -func MatchDeployment(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} diff --git a/pkg/registry/extensions/deployment/strategy_test.go b/pkg/registry/extensions/deployment/strategy_test.go index c166457f121..74668493540 100644 --- a/pkg/registry/extensions/deployment/strategy_test.go +++ b/pkg/registry/extensions/deployment/strategy_test.go @@ -24,20 +24,9 @@ import ( "k8s.io/apimachinery/pkg/runtime" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/apis/extensions" ) -func TestSelectableFieldLabelConversions(t *testing.T) { - apitesting.TestSelectableFieldLabelConversionsOfKind(t, - testapi.Extensions.GroupVersion().String(), - "Deployment", - DeploymentToSelectableFields(&extensions.Deployment{}), - nil, - ) -} - func TestStatusUpdates(t *testing.T) { tests := []struct { old runtime.Object diff --git a/pkg/registry/extensions/ingress/BUILD b/pkg/registry/extensions/ingress/BUILD index d849d82d872..bdd561af8e5 100644 --- a/pkg/registry/extensions/ingress/BUILD +++ b/pkg/registry/extensions/ingress/BUILD @@ -20,13 +20,9 @@ go_library( "//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) @@ -38,8 +34,6 @@ go_test( tags = ["automanaged"], deps = [ "//pkg/api:go_default_library", - "//pkg/api/testapi:go_default_library", - "//pkg/api/testing:go_default_library", "//pkg/apis/extensions:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", diff --git a/pkg/registry/extensions/ingress/storage/storage.go b/pkg/registry/extensions/ingress/storage/storage.go index 8b891a6209a..f6c7d6a915c 100644 --- a/pkg/registry/extensions/ingress/storage/storage.go +++ b/pkg/registry/extensions/ingress/storage/storage.go @@ -40,7 +40,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { Copier: api.Scheme, NewFunc: func() runtime.Object { return &extensions.Ingress{} }, NewListFunc: func() runtime.Object { return &extensions.IngressList{} }, - PredicateFunc: ingress.MatchIngress, DefaultQualifiedResource: extensions.Resource("ingresses"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("ingresses"), @@ -48,7 +47,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { UpdateStrategy: ingress.Strategy, DeleteStrategy: ingress.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: ingress.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/extensions/ingress/strategy.go b/pkg/registry/extensions/ingress/strategy.go index add1e0a3276..41edf5bc7f4 100644 --- a/pkg/registry/extensions/ingress/strategy.go +++ b/pkg/registry/extensions/ingress/strategy.go @@ -17,16 +17,10 @@ limitations under the License. package ingress import ( - "fmt" - apiequality "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/extensions" @@ -100,31 +94,6 @@ func (ingressStrategy) AllowUnconditionalUpdate() bool { return true } -// IngressToSelectableFields returns a field set that represents the object. -func IngressToSelectableFields(ingress *extensions.Ingress) fields.Set { - return generic.ObjectMetaFieldsSet(&ingress.ObjectMeta, true) -} - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - ingress, ok := obj.(*extensions.Ingress) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not an Ingress.") - } - return labels.Set(ingress.ObjectMeta.Labels), IngressToSelectableFields(ingress), ingress.Initializers != nil, nil -} - -// MatchIngress is the filter used by the generic etcd backend to ingress -// watch events from etcd to clients of the apiserver only interested in specific -// labels/fields. -func MatchIngress(label labels.Selector, field fields.Selector) storage.SelectionPredicate { - return storage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - type ingressStatusStrategy struct { ingressStrategy } diff --git a/pkg/registry/extensions/ingress/strategy_test.go b/pkg/registry/extensions/ingress/strategy_test.go index 3e381eca41b..15db6711ff5 100644 --- a/pkg/registry/extensions/ingress/strategy_test.go +++ b/pkg/registry/extensions/ingress/strategy_test.go @@ -23,8 +23,6 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" - apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/apis/extensions" ) @@ -132,12 +130,3 @@ func TestIngressStatusStrategy(t *testing.T) { t.Errorf("Unexpected error %v", errs) } } - -func TestSelectableFieldLabelConversions(t *testing.T) { - apitesting.TestSelectableFieldLabelConversionsOfKind(t, - testapi.Extensions.GroupVersion().String(), - "Ingress", - IngressToSelectableFields(&extensions.Ingress{}), - nil, - ) -} diff --git a/pkg/registry/extensions/networkpolicy/BUILD b/pkg/registry/extensions/networkpolicy/BUILD index 782847dde18..8d1bedf5269 100644 --- a/pkg/registry/extensions/networkpolicy/BUILD +++ b/pkg/registry/extensions/networkpolicy/BUILD @@ -20,13 +20,9 @@ go_library( "//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/extensions/networkpolicy/storage/storage.go b/pkg/registry/extensions/networkpolicy/storage/storage.go index 8bb412a4a0d..cb69d55ee47 100644 --- a/pkg/registry/extensions/networkpolicy/storage/storage.go +++ b/pkg/registry/extensions/networkpolicy/storage/storage.go @@ -37,7 +37,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &extensionsapi.NetworkPolicy{} }, NewListFunc: func() runtime.Object { return &extensionsapi.NetworkPolicyList{} }, - PredicateFunc: networkpolicy.MatchNetworkPolicy, DefaultQualifiedResource: extensionsapi.Resource("networkpolicies"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("networkpolicies"), @@ -45,7 +44,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { UpdateStrategy: networkpolicy.Strategy, DeleteStrategy: networkpolicy.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: networkpolicy.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/extensions/networkpolicy/strategy.go b/pkg/registry/extensions/networkpolicy/strategy.go index 63eb28b719f..1c1ea71377d 100644 --- a/pkg/registry/extensions/networkpolicy/strategy.go +++ b/pkg/registry/extensions/networkpolicy/strategy.go @@ -17,16 +17,10 @@ limitations under the License. package networkpolicy import ( - "fmt" - apiequality "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/extensions" @@ -92,27 +86,3 @@ func (networkPolicyStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, func (networkPolicyStrategy) AllowUnconditionalUpdate() bool { return true } - -// NetworkPolicyToSelectableFields returns a field set that represents the object. -func NetworkPolicyToSelectableFields(networkPolicy *extensions.NetworkPolicy) fields.Set { - return generic.ObjectMetaFieldsSet(&networkPolicy.ObjectMeta, true) -} - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - networkPolicy, ok := obj.(*extensions.NetworkPolicy) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not a NetworkPolicy.") - } - return labels.Set(networkPolicy.ObjectMeta.Labels), NetworkPolicyToSelectableFields(networkPolicy), networkPolicy.Initializers != nil, nil -} - -// MatchNetworkPolicy is the filter used by the generic etcd backend to watch events -// from etcd to clients of the apiserver only interested in specific labels/fields. -func MatchNetworkPolicy(label labels.Selector, field fields.Selector) storage.SelectionPredicate { - return storage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} diff --git a/pkg/registry/extensions/podsecuritypolicy/BUILD b/pkg/registry/extensions/podsecuritypolicy/BUILD index 658ab4cdc29..fe59a04ecb0 100644 --- a/pkg/registry/extensions/podsecuritypolicy/BUILD +++ b/pkg/registry/extensions/podsecuritypolicy/BUILD @@ -18,14 +18,10 @@ go_library( "//pkg/api:go_default_library", "//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions/validation:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/extensions/podsecuritypolicy/storage/storage.go b/pkg/registry/extensions/podsecuritypolicy/storage/storage.go index eff8d11b76e..7f4eed37aa5 100644 --- a/pkg/registry/extensions/podsecuritypolicy/storage/storage.go +++ b/pkg/registry/extensions/podsecuritypolicy/storage/storage.go @@ -37,7 +37,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &extensions.PodSecurityPolicy{} }, NewListFunc: func() runtime.Object { return &extensions.PodSecurityPolicyList{} }, - PredicateFunc: podsecuritypolicy.MatchPodSecurityPolicy, DefaultQualifiedResource: extensions.Resource("podsecuritypolicies"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("podsecuritypolicies"), @@ -46,7 +45,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { DeleteStrategy: podsecuritypolicy.Strategy, ReturnDeletedObject: true, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: podsecuritypolicy.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/extensions/podsecuritypolicy/strategy.go b/pkg/registry/extensions/podsecuritypolicy/strategy.go index d9213619da3..130b90d338b 100644 --- a/pkg/registry/extensions/podsecuritypolicy/strategy.go +++ b/pkg/registry/extensions/podsecuritypolicy/strategy.go @@ -17,16 +17,10 @@ limitations under the License. package podsecuritypolicy import ( - "fmt" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/rest" - "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/extensions" @@ -75,26 +69,3 @@ func (strategy) Validate(ctx genericapirequest.Context, obj runtime.Object) fiel func (strategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { return validation.ValidatePodSecurityPolicyUpdate(old.(*extensions.PodSecurityPolicy), obj.(*extensions.PodSecurityPolicy)) } - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - psp, ok := obj.(*extensions.PodSecurityPolicy) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not a pod security policy.") - } - return labels.Set(psp.ObjectMeta.Labels), PodSecurityPolicyToSelectableFields(psp), psp.Initializers != nil, nil -} - -// Matcher returns a generic matcher for a given label and field selector. -func MatchPodSecurityPolicy(label labels.Selector, field fields.Selector) storage.SelectionPredicate { - return storage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - -// PodSecurityPolicyToSelectableFields returns a label set that represents the object -func PodSecurityPolicyToSelectableFields(obj *extensions.PodSecurityPolicy) fields.Set { - return generic.ObjectMetaFieldsSet(&obj.ObjectMeta, false) -} diff --git a/pkg/registry/networking/networkpolicy/BUILD b/pkg/registry/networking/networkpolicy/BUILD index 6a09bac280d..a3f9aea9321 100644 --- a/pkg/registry/networking/networkpolicy/BUILD +++ b/pkg/registry/networking/networkpolicy/BUILD @@ -21,15 +21,11 @@ go_library( "//pkg/apis/networking/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/networking/networkpolicy/storage/storage.go b/pkg/registry/networking/networkpolicy/storage/storage.go index 3e26dd33751..0ee45bde784 100644 --- a/pkg/registry/networking/networkpolicy/storage/storage.go +++ b/pkg/registry/networking/networkpolicy/storage/storage.go @@ -38,7 +38,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &networkingapi.NetworkPolicy{} }, NewListFunc: func() runtime.Object { return &networkingapi.NetworkPolicyList{} }, - PredicateFunc: networkpolicy.Matcher, DefaultQualifiedResource: networkingapi.Resource("networkpolicies"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("networkpolicies"), @@ -46,7 +45,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { UpdateStrategy: networkpolicy.Strategy, DeleteStrategy: networkpolicy.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: networkpolicy.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/networking/networkpolicy/strategy.go b/pkg/registry/networking/networkpolicy/strategy.go index 2b58c6c1288..fe66f6f62e2 100644 --- a/pkg/registry/networking/networkpolicy/strategy.go +++ b/pkg/registry/networking/networkpolicy/strategy.go @@ -17,16 +17,11 @@ limitations under the License. package networkpolicy import ( - "fmt" "reflect" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/networking" @@ -91,27 +86,3 @@ func (networkPolicyStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, func (networkPolicyStrategy) AllowUnconditionalUpdate() bool { return true } - -// SelectableFields returns a field set that represents the object. -func SelectableFields(networkPolicy *networking.NetworkPolicy) fields.Set { - return generic.ObjectMetaFieldsSet(&networkPolicy.ObjectMeta, true) -} - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - networkPolicy, ok := obj.(*networking.NetworkPolicy) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not a NetworkPolicy.") - } - return labels.Set(networkPolicy.ObjectMeta.Labels), SelectableFields(networkPolicy), networkPolicy.Initializers != nil, nil -} - -// Matcher is the filter used by the generic etcd backend to watch events -// from etcd to clients of the apiserver only interested in specific labels/fields. -func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} diff --git a/pkg/registry/policy/poddisruptionbudget/BUILD b/pkg/registry/policy/poddisruptionbudget/BUILD index b492e7221ed..0f918b931b0 100644 --- a/pkg/registry/policy/poddisruptionbudget/BUILD +++ b/pkg/registry/policy/poddisruptionbudget/BUILD @@ -20,13 +20,9 @@ go_library( "//pkg/apis/policy:go_default_library", "//pkg/apis/policy/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/policy/poddisruptionbudget/storage/storage.go b/pkg/registry/policy/poddisruptionbudget/storage/storage.go index 2daa230365d..e97136c2958 100644 --- a/pkg/registry/policy/poddisruptionbudget/storage/storage.go +++ b/pkg/registry/policy/poddisruptionbudget/storage/storage.go @@ -40,7 +40,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { Copier: api.Scheme, NewFunc: func() runtime.Object { return &policyapi.PodDisruptionBudget{} }, NewListFunc: func() runtime.Object { return &policyapi.PodDisruptionBudgetList{} }, - PredicateFunc: poddisruptionbudget.MatchPodDisruptionBudget, DefaultQualifiedResource: policyapi.Resource("poddisruptionbudgets"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("poddisruptionbudgets"), @@ -48,7 +47,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { UpdateStrategy: poddisruptionbudget.Strategy, DeleteStrategy: poddisruptionbudget.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: poddisruptionbudget.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/policy/poddisruptionbudget/strategy.go b/pkg/registry/policy/poddisruptionbudget/strategy.go index a0f6a71d22a..43ef18c0a7a 100644 --- a/pkg/registry/policy/poddisruptionbudget/strategy.go +++ b/pkg/registry/policy/poddisruptionbudget/strategy.go @@ -17,16 +17,10 @@ limitations under the License. package poddisruptionbudget import ( - "fmt" - apiequality "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/policy" @@ -99,30 +93,6 @@ func (podDisruptionBudgetStrategy) AllowUnconditionalUpdate() bool { return false } -// PodDisruptionBudgetToSelectableFields returns a field set that represents the object. -func PodDisruptionBudgetToSelectableFields(podDisruptionBudget *policy.PodDisruptionBudget) fields.Set { - return generic.ObjectMetaFieldsSet(&podDisruptionBudget.ObjectMeta, true) -} - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - podDisruptionBudget, ok := obj.(*policy.PodDisruptionBudget) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not a PodDisruptionBudget.") - } - return labels.Set(podDisruptionBudget.ObjectMeta.Labels), PodDisruptionBudgetToSelectableFields(podDisruptionBudget), podDisruptionBudget.Initializers != nil, nil -} - -// MatchPodDisruptionBudget is the filter used by the generic etcd backend to watch events -// from etcd to clients of the apiserver only interested in specific labels/fields. -func MatchPodDisruptionBudget(label labels.Selector, field fields.Selector) storage.SelectionPredicate { - return storage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - type podDisruptionBudgetStatusStrategy struct { podDisruptionBudgetStrategy } diff --git a/pkg/registry/rbac/clusterrole/BUILD b/pkg/registry/rbac/clusterrole/BUILD index caa3362b1aa..36124e2898d 100644 --- a/pkg/registry/rbac/clusterrole/BUILD +++ b/pkg/registry/rbac/clusterrole/BUILD @@ -21,14 +21,11 @@ go_library( "//pkg/apis/rbac/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/rbac/clusterrole/storage/storage.go b/pkg/registry/rbac/clusterrole/storage/storage.go index e434280a409..9c8dbc86279 100644 --- a/pkg/registry/rbac/clusterrole/storage/storage.go +++ b/pkg/registry/rbac/clusterrole/storage/storage.go @@ -37,7 +37,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &rbac.ClusterRole{} }, NewListFunc: func() runtime.Object { return &rbac.ClusterRoleList{} }, - PredicateFunc: clusterrole.Matcher, DefaultQualifiedResource: rbac.Resource("clusterroles"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("clusterroles"), @@ -45,7 +44,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { UpdateStrategy: clusterrole.Strategy, DeleteStrategy: clusterrole.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: clusterrole.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/rbac/clusterrole/strategy.go b/pkg/registry/rbac/clusterrole/strategy.go index ead2a876f27..7d4e766a880 100644 --- a/pkg/registry/rbac/clusterrole/strategy.go +++ b/pkg/registry/rbac/clusterrole/strategy.go @@ -17,15 +17,10 @@ limitations under the License. package clusterrole import ( - "fmt" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/rbac" @@ -102,26 +97,3 @@ func (strategy) AllowUnconditionalUpdate() bool { func (s strategy) Export(ctx genericapirequest.Context, obj runtime.Object, exact bool) error { return nil } - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - role, ok := obj.(*rbac.ClusterRole) - if !ok { - return nil, nil, false, fmt.Errorf("not a ClusterRole") - } - return labels.Set(role.Labels), SelectableFields(role), role.Initializers != nil, nil -} - -// Matcher returns a generic matcher for a given label and field selector. -func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - -// SelectableFields returns a field set that can be used for filter selection -func SelectableFields(obj *rbac.ClusterRole) fields.Set { - return nil -} diff --git a/pkg/registry/rbac/clusterrolebinding/BUILD b/pkg/registry/rbac/clusterrolebinding/BUILD index f08b0a01342..8de2b3d2f1b 100644 --- a/pkg/registry/rbac/clusterrolebinding/BUILD +++ b/pkg/registry/rbac/clusterrolebinding/BUILD @@ -21,14 +21,11 @@ go_library( "//pkg/apis/rbac/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/rbac/clusterrolebinding/storage/storage.go b/pkg/registry/rbac/clusterrolebinding/storage/storage.go index 2cb178ca63f..31eefd21911 100644 --- a/pkg/registry/rbac/clusterrolebinding/storage/storage.go +++ b/pkg/registry/rbac/clusterrolebinding/storage/storage.go @@ -37,7 +37,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &rbac.ClusterRoleBinding{} }, NewListFunc: func() runtime.Object { return &rbac.ClusterRoleBindingList{} }, - PredicateFunc: clusterrolebinding.Matcher, DefaultQualifiedResource: rbac.Resource("clusterrolebindings"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("clusterrolebindings"), @@ -45,7 +44,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { UpdateStrategy: clusterrolebinding.Strategy, DeleteStrategy: clusterrolebinding.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: clusterrolebinding.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/rbac/clusterrolebinding/strategy.go b/pkg/registry/rbac/clusterrolebinding/strategy.go index bb9168d51d4..383be8bd2d9 100644 --- a/pkg/registry/rbac/clusterrolebinding/strategy.go +++ b/pkg/registry/rbac/clusterrolebinding/strategy.go @@ -17,15 +17,10 @@ limitations under the License. package clusterrolebinding import ( - "fmt" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/rbac" @@ -102,26 +97,3 @@ func (strategy) AllowUnconditionalUpdate() bool { func (s strategy) Export(ctx genericapirequest.Context, obj runtime.Object, exact bool) error { return nil } - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - roleBinding, ok := obj.(*rbac.ClusterRoleBinding) - if !ok { - return nil, nil, false, fmt.Errorf("not a ClusterRoleBinding") - } - return labels.Set(roleBinding.Labels), SelectableFields(roleBinding), roleBinding.Initializers != nil, nil -} - -// Matcher returns a generic matcher for a given label and field selector. -func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - -// SelectableFields returns a field set that can be used for filter selection -func SelectableFields(obj *rbac.ClusterRoleBinding) fields.Set { - return nil -} diff --git a/pkg/registry/rbac/role/BUILD b/pkg/registry/rbac/role/BUILD index 6f4756a478e..a42f6c9182f 100644 --- a/pkg/registry/rbac/role/BUILD +++ b/pkg/registry/rbac/role/BUILD @@ -21,14 +21,11 @@ go_library( "//pkg/apis/rbac/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/rbac/role/storage/storage.go b/pkg/registry/rbac/role/storage/storage.go index 18f17867252..4717f9371f2 100644 --- a/pkg/registry/rbac/role/storage/storage.go +++ b/pkg/registry/rbac/role/storage/storage.go @@ -37,7 +37,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &rbac.Role{} }, NewListFunc: func() runtime.Object { return &rbac.RoleList{} }, - PredicateFunc: role.Matcher, DefaultQualifiedResource: rbac.Resource("roles"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("roles"), @@ -45,7 +44,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { UpdateStrategy: role.Strategy, DeleteStrategy: role.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: role.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/rbac/role/strategy.go b/pkg/registry/rbac/role/strategy.go index aaf2a56abb8..da80adc4ed7 100644 --- a/pkg/registry/rbac/role/strategy.go +++ b/pkg/registry/rbac/role/strategy.go @@ -17,15 +17,10 @@ limitations under the License. package role import ( - "fmt" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/rbac" @@ -102,26 +97,3 @@ func (strategy) AllowUnconditionalUpdate() bool { func (s strategy) Export(ctx genericapirequest.Context, obj runtime.Object, exact bool) error { return nil } - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - role, ok := obj.(*rbac.Role) - if !ok { - return nil, nil, false, fmt.Errorf("not a Role") - } - return labels.Set(role.Labels), SelectableFields(role), role.Initializers != nil, nil -} - -// Matcher returns a generic matcher for a given label and field selector. -func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - -// SelectableFields returns a field set that can be used for filter selection -func SelectableFields(obj *rbac.Role) fields.Set { - return nil -} diff --git a/pkg/registry/rbac/rolebinding/BUILD b/pkg/registry/rbac/rolebinding/BUILD index 18f5138a017..fde3b7a5d95 100644 --- a/pkg/registry/rbac/rolebinding/BUILD +++ b/pkg/registry/rbac/rolebinding/BUILD @@ -21,14 +21,11 @@ go_library( "//pkg/apis/rbac/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/rbac/rolebinding/storage/storage.go b/pkg/registry/rbac/rolebinding/storage/storage.go index 3ccf4f72cf6..25ff81aad0d 100644 --- a/pkg/registry/rbac/rolebinding/storage/storage.go +++ b/pkg/registry/rbac/rolebinding/storage/storage.go @@ -37,7 +37,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &rbac.RoleBinding{} }, NewListFunc: func() runtime.Object { return &rbac.RoleBindingList{} }, - PredicateFunc: rolebinding.Matcher, DefaultQualifiedResource: rbac.Resource("rolebindings"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("rolebindings"), @@ -45,7 +44,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { UpdateStrategy: rolebinding.Strategy, DeleteStrategy: rolebinding.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: rolebinding.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/rbac/rolebinding/strategy.go b/pkg/registry/rbac/rolebinding/strategy.go index f5291ab0b23..0abeb7ed76c 100644 --- a/pkg/registry/rbac/rolebinding/strategy.go +++ b/pkg/registry/rbac/rolebinding/strategy.go @@ -17,15 +17,10 @@ limitations under the License. package rolebinding import ( - "fmt" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/rbac" @@ -102,26 +97,3 @@ func (strategy) AllowUnconditionalUpdate() bool { func (s strategy) Export(ctx genericapirequest.Context, obj runtime.Object, exact bool) error { return nil } - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - roleBinding, ok := obj.(*rbac.RoleBinding) - if !ok { - return nil, nil, false, fmt.Errorf("not a RoleBinding") - } - return labels.Set(roleBinding.Labels), SelectableFields(roleBinding), roleBinding.Initializers != nil, nil -} - -// Matcher returns a generic matcher for a given label and field selector. -func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - -// SelectableFields returns a field set that can be used for filter selection -func SelectableFields(obj *rbac.RoleBinding) fields.Set { - return nil -} diff --git a/pkg/registry/scheduling/priorityclass/BUILD b/pkg/registry/scheduling/priorityclass/BUILD index ffea9f8a5b4..b92c2898890 100644 --- a/pkg/registry/scheduling/priorityclass/BUILD +++ b/pkg/registry/scheduling/priorityclass/BUILD @@ -34,15 +34,11 @@ go_library( "//pkg/apis/scheduling/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/scheduling/priorityclass/storage/storage.go b/pkg/registry/scheduling/priorityclass/storage/storage.go index 5f2ba014b5d..6390381f1f1 100644 --- a/pkg/registry/scheduling/priorityclass/storage/storage.go +++ b/pkg/registry/scheduling/priorityclass/storage/storage.go @@ -38,7 +38,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &schedulingapi.PriorityClass{} }, NewListFunc: func() runtime.Object { return &schedulingapi.PriorityClassList{} }, - PredicateFunc: priorityclass.Matcher, DefaultQualifiedResource: schedulingapi.Resource("priorityclasses"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("priorityclasses"), @@ -46,7 +45,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { UpdateStrategy: priorityclass.Strategy, DeleteStrategy: priorityclass.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: priorityclass.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/scheduling/priorityclass/strategy.go b/pkg/registry/scheduling/priorityclass/strategy.go index 64be7365f81..6a6e060eef7 100644 --- a/pkg/registry/scheduling/priorityclass/strategy.go +++ b/pkg/registry/scheduling/priorityclass/strategy.go @@ -17,15 +17,9 @@ limitations under the License. package priorityclass import ( - "fmt" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/scheduling" @@ -83,27 +77,3 @@ func (priorityClassStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, func (priorityClassStrategy) AllowUnconditionalUpdate() bool { return true } - -// SelectableFields returns a field set that represents the object. -func SelectableFields(pc *scheduling.PriorityClass) fields.Set { - return generic.ObjectMetaFieldsSet(&pc.ObjectMeta, false) -} - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - pc, ok := obj.(*scheduling.PriorityClass) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not a PriorityClass") - } - return labels.Set(pc.ObjectMeta.Labels), SelectableFields(pc), pc.Initializers != nil, nil -} - -// Matcher is the filter used by the generic etcd backend to watch events -// from etcd to clients of the apiserver only interested in specific labels/fields. -func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} diff --git a/pkg/registry/settings/podpreset/BUILD b/pkg/registry/settings/podpreset/BUILD index 4868f51f3c9..010ecd82f3e 100644 --- a/pkg/registry/settings/podpreset/BUILD +++ b/pkg/registry/settings/podpreset/BUILD @@ -21,15 +21,11 @@ go_library( "//pkg/apis/settings/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/settings/podpreset/storage/storage.go b/pkg/registry/settings/podpreset/storage/storage.go index 4d1c7ca54b5..7a40f2c9f62 100644 --- a/pkg/registry/settings/podpreset/storage/storage.go +++ b/pkg/registry/settings/podpreset/storage/storage.go @@ -37,7 +37,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &settingsapi.PodPreset{} }, NewListFunc: func() runtime.Object { return &settingsapi.PodPresetList{} }, - PredicateFunc: podpreset.Matcher, DefaultQualifiedResource: settingsapi.Resource("podpresets"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("podpresets"), @@ -45,7 +44,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { UpdateStrategy: podpreset.Strategy, DeleteStrategy: podpreset.Strategy, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: podpreset.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/settings/podpreset/strategy.go b/pkg/registry/settings/podpreset/strategy.go index 6eb0f94acd8..2d53ed16e68 100644 --- a/pkg/registry/settings/podpreset/strategy.go +++ b/pkg/registry/settings/podpreset/strategy.go @@ -17,15 +17,9 @@ limitations under the License. package podpreset import ( - "fmt" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/settings" @@ -86,27 +80,3 @@ func (podPresetStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old func (podPresetStrategy) AllowUnconditionalUpdate() bool { return true } - -// SelectableFields returns a field set that represents the object. -func SelectableFields(pip *settings.PodPreset) fields.Set { - return generic.ObjectMetaFieldsSet(&pip.ObjectMeta, true) -} - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - pip, ok := obj.(*settings.PodPreset) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not a PodPreset.") - } - return labels.Set(pip.ObjectMeta.Labels), SelectableFields(pip), pip.Initializers != nil, nil -} - -// Matcher is the filter used by the generic etcd backend to watch events -// from etcd to clients of the apiserver only interested in specific labels/fields. -func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} diff --git a/pkg/registry/storage/storageclass/BUILD b/pkg/registry/storage/storageclass/BUILD index c11ddcf4ddf..5bf50fad01f 100644 --- a/pkg/registry/storage/storageclass/BUILD +++ b/pkg/registry/storage/storageclass/BUILD @@ -19,13 +19,9 @@ go_library( "//pkg/api:go_default_library", "//pkg/apis/storage:go_default_library", "//pkg/apis/storage/validation:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", - "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", - "//vendor/k8s.io/apiserver/pkg/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", ], ) diff --git a/pkg/registry/storage/storageclass/storage/storage.go b/pkg/registry/storage/storageclass/storage/storage.go index 3dcb18a301f..25c1807e49f 100644 --- a/pkg/registry/storage/storageclass/storage/storage.go +++ b/pkg/registry/storage/storageclass/storage/storage.go @@ -37,7 +37,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { Copier: api.Scheme, NewFunc: func() runtime.Object { return &storageapi.StorageClass{} }, NewListFunc: func() runtime.Object { return &storageapi.StorageClassList{} }, - PredicateFunc: storageclass.MatchStorageClasses, DefaultQualifiedResource: storageapi.Resource("storageclasses"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("storageclass"), @@ -46,7 +45,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) *REST { DeleteStrategy: storageclass.Strategy, ReturnDeletedObject: true, } - options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: storageclass.GetAttrs} + options := &generic.StoreOptions{RESTOptions: optsGetter} if err := store.CompleteWithOptions(options); err != nil { panic(err) // TODO: Propagate error up } diff --git a/pkg/registry/storage/storageclass/strategy.go b/pkg/registry/storage/storageclass/strategy.go index 73aa7d2b37f..216cc7ddc26 100644 --- a/pkg/registry/storage/storageclass/strategy.go +++ b/pkg/registry/storage/storageclass/strategy.go @@ -17,15 +17,9 @@ limitations under the License. package storageclass import ( - "fmt" - - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - apistorage "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/storage" @@ -78,26 +72,3 @@ func (storageClassStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, o func (storageClassStrategy) AllowUnconditionalUpdate() bool { return true } - -// GetAttrs returns labels and fields of a given object for filtering purposes. -func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) { - cls, ok := obj.(*storage.StorageClass) - if !ok { - return nil, nil, false, fmt.Errorf("given object is not of type StorageClass") - } - return labels.Set(cls.ObjectMeta.Labels), StorageClassToSelectableFields(cls), cls.Initializers != nil, nil -} - -// MatchStorageClass returns a generic matcher for a given label and field selector. -func MatchStorageClasses(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { - return apistorage.SelectionPredicate{ - Label: label, - Field: field, - GetAttrs: GetAttrs, - } -} - -// StorageClassToSelectableFields returns a label set that represents the object -func StorageClassToSelectableFields(storageClass *storage.StorageClass) fields.Set { - return generic.ObjectMetaFieldsSet(&storageClass.ObjectMeta, false) -} From 173bc31c25033939f4d9f807f63e96a136d4bf2d Mon Sep 17 00:00:00 2001 From: Jamie Hannaford Date: Mon, 7 Aug 2017 11:31:19 +0200 Subject: [PATCH 27/38] Enable selfHosted feature flag --- cmd/kubeadm/app/apis/kubeadm/types.go | 5 ----- cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go | 5 ----- cmd/kubeadm/app/cmd/BUILD | 1 + cmd/kubeadm/app/cmd/features/features.go | 6 ++++++ cmd/kubeadm/app/cmd/init.go | 7 ++----- 5 files changed, 9 insertions(+), 15 deletions(-) diff --git a/cmd/kubeadm/app/apis/kubeadm/types.go b/cmd/kubeadm/app/apis/kubeadm/types.go index 2e132f913cd..b6baf01ee64 100644 --- a/cmd/kubeadm/app/apis/kubeadm/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/types.go @@ -39,11 +39,6 @@ type MasterConfiguration struct { Token string TokenTTL time.Duration - // SelfHosted enables an alpha deployment type where the apiserver, scheduler, and - // controller manager are managed by Kubernetes itself. This option is likely to - // become the default in the future. - SelfHosted bool - APIServerExtraArgs map[string]string ControllerManagerExtraArgs map[string]string SchedulerExtraArgs map[string]string diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go index ca2b698f152..38c2e77b123 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go @@ -38,11 +38,6 @@ type MasterConfiguration struct { Token string `json:"token"` TokenTTL time.Duration `json:"tokenTTL"` - // SelfHosted enables an alpha deployment type where the apiserver, scheduler, and - // controller manager are managed by Kubernetes itself. This option is likely to - // become the default in the future. - SelfHosted bool `json:"selfHosted"` - APIServerExtraArgs map[string]string `json:"apiServerExtraArgs"` ControllerManagerExtraArgs map[string]string `json:"controllerManagerExtraArgs"` SchedulerExtraArgs map[string]string `json:"schedulerExtraArgs"` diff --git a/cmd/kubeadm/app/cmd/BUILD b/cmd/kubeadm/app/cmd/BUILD index 57d18294b2c..d803edc9ae6 100644 --- a/cmd/kubeadm/app/cmd/BUILD +++ b/cmd/kubeadm/app/cmd/BUILD @@ -24,6 +24,7 @@ go_library( "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library", + "//cmd/kubeadm/app/cmd/features:go_default_library", "//cmd/kubeadm/app/cmd/phases:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/discovery:go_default_library", diff --git a/cmd/kubeadm/app/cmd/features/features.go b/cmd/kubeadm/app/cmd/features/features.go index ad2a584b4b8..e77ef8412d2 100644 --- a/cmd/kubeadm/app/cmd/features/features.go +++ b/cmd/kubeadm/app/cmd/features/features.go @@ -31,6 +31,12 @@ const ( // FeatureList represents a list of feature gates type FeatureList map[utilfeature.Feature]utilfeature.FeatureSpec +// Enabled indicates whether a feature name has been enabled +func Enabled(featureList map[string]bool, featureName utilfeature.Feature) bool { + _, ok := featureList[string(featureName)] + return ok +} + // Supports indicates whether a feature name is supported on the given // feature set func Supports(featureList FeatureList, featureName string) bool { diff --git a/cmd/kubeadm/app/cmd/init.go b/cmd/kubeadm/app/cmd/init.go index bd2e8266663..e08ec4dab4e 100644 --- a/cmd/kubeadm/app/cmd/init.go +++ b/cmd/kubeadm/app/cmd/init.go @@ -31,6 +31,7 @@ import ( kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation" + "k8s.io/kubernetes/cmd/kubeadm/app/cmd/features" cmdphases "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" addonsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons" @@ -147,10 +148,6 @@ func NewCmdInit(out io.Writer) *cobra.Command { &skipTokenPrint, "skip-token-print", skipTokenPrint, "Skip printing of the default bootstrap token generated by 'kubeadm init'", ) - cmd.PersistentFlags().BoolVar( - &cfg.SelfHosted, "self-hosted", cfg.SelfHosted, - "[experimental] If kubeadm should make this control plane self-hosted", - ) cmd.PersistentFlags().StringVar( &cfg.Token, "token", cfg.Token, @@ -288,7 +285,7 @@ func (i *Init) Run(out io.Writer) error { } // Is deployment type self-hosted? - if i.cfg.SelfHosted { + if features.Enabled(i.cfg.FeatureFlags, features.SelfHosting) { // Temporary control plane is up, now we create our self hosted control // plane components and remove the static manifests: fmt.Println("[self-hosted] Creating self-hosted control plane...") From 2f747f3d3c99c249131487c441c9085643c2309c Mon Sep 17 00:00:00 2001 From: Beata Skiba Date: Thu, 20 Jul 2017 15:05:25 +0200 Subject: [PATCH 28/38] Add a simple cloud provider for e2e tests on kubemark This is needed for cluster autoscaler e2e test to run on kubemark. We need the ability to add and remove nodes and operate on nodegroups. Kubemark does not provide this at the moment. --- hack/verify-flags/known-flags.txt | 1 + pkg/kubemark/BUILD | 8 + pkg/kubemark/controller.go | 343 +++++++++++++++++++++++++++++ test/e2e/framework/BUILD | 2 + test/e2e/framework/framework.go | 26 +++ test/e2e/framework/size.go | 6 + test/e2e/framework/test_context.go | 18 +- 7 files changed, 397 insertions(+), 7 deletions(-) create mode 100644 pkg/kubemark/controller.go diff --git a/hack/verify-flags/known-flags.txt b/hack/verify-flags/known-flags.txt index a3d243b78bd..d2ee5b68bb8 100644 --- a/hack/verify-flags/known-flags.txt +++ b/hack/verify-flags/known-flags.txt @@ -425,6 +425,7 @@ kube-reserved kube-reserved-cgroup kube-master-url kube-reserved +kubemark-external-kubeconfig kubernetes-anywhere-cluster kubernetes-anywhere-path kubernetes-anywhere-phase2-provider diff --git a/pkg/kubemark/BUILD b/pkg/kubemark/BUILD index 1831c1c8398..70835e5c55e 100644 --- a/pkg/kubemark/BUILD +++ b/pkg/kubemark/BUILD @@ -10,6 +10,7 @@ load( go_library( name = "go_default_library", srcs = [ + "controller.go", "hollow_kubelet.go", "hollow_proxy.go", ], @@ -22,6 +23,7 @@ go_library( "//pkg/apis/componentconfig:go_default_library", "//pkg/apis/componentconfig/v1alpha1:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library", + "//pkg/controller:go_default_library", "//pkg/kubelet:go_default_library", "//pkg/kubelet/cadvisor:go_default_library", "//pkg/kubelet/cm:go_default_library", @@ -44,9 +46,15 @@ go_library( "//vendor/github.com/golang/glog:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + "//vendor/k8s.io/client-go/informers:go_default_library", + "//vendor/k8s.io/client-go/informers/core/v1:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", "//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library", + "//vendor/k8s.io/client-go/listers/core/v1:go_default_library", + "//vendor/k8s.io/client-go/tools/cache:go_default_library", "//vendor/k8s.io/client-go/tools/record:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library", ], diff --git a/pkg/kubemark/controller.go b/pkg/kubemark/controller.go new file mode 100644 index 00000000000..53d163175cc --- /dev/null +++ b/pkg/kubemark/controller.go @@ -0,0 +1,343 @@ +/* +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 kubemark + +import ( + "fmt" + "math/rand" + "sync" + "time" + + apiv1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/informers" + informersv1 "k8s.io/client-go/informers/core/v1" + kubeclient "k8s.io/client-go/kubernetes" + listersv1 "k8s.io/client-go/listers/core/v1" + "k8s.io/client-go/tools/cache" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/controller" + + "github.com/golang/glog" +) + +const ( + namespaceKubemark = "kubemark" + hollowNodeName = "hollow-node" + nodeGroupLabel = "autoscaling.k8s.io/nodegroup" + numRetries = 3 +) + +// KubemarkController is a simplified version of cloud provider for kubemark. It allows +// to add and delete nodes from a kubemark cluster and introduces nodegroups +// by applying labels to the kubemark's hollow-nodes. +type KubemarkController struct { + nodeTemplate *apiv1.ReplicationController + externalCluster externalCluster + kubemarkCluster kubemarkCluster + rand *rand.Rand +} + +// externalCluster is used to communicate with the external cluster that hosts +// kubemark, in order to be able to list, create and delete hollow nodes +// by manipulating the replication controllers. +type externalCluster struct { + rcLister listersv1.ReplicationControllerLister + rcSynced cache.InformerSynced + podLister listersv1.PodLister + podSynced cache.InformerSynced + client kubeclient.Interface +} + +// kubemarkCluster is used to delete nodes from kubemark cluster once their +// respective replication controllers have been deleted and the nodes have +// become unready. This is to cover for the fact that there is no proper cloud +// provider for kubemark that would care for deleting the nodes. +type kubemarkCluster struct { + client kubeclient.Interface + nodeLister listersv1.NodeLister + nodeSynced cache.InformerSynced + nodesToDelete map[string]bool + nodesToDeleteLock sync.Mutex +} + +// NewKubemarkController creates KubemarkController using the provided clients to talk to external +// and kubemark clusters. +func NewKubemarkController(externalClient kubeclient.Interface, externalInformerFactory informers.SharedInformerFactory, + kubemarkClient kubeclient.Interface, kubemarkNodeInformer informersv1.NodeInformer) (*KubemarkController, error) { + rcInformer := externalInformerFactory.InformerFor(&apiv1.ReplicationController{}, newReplicationControllerInformer) + podInformer := externalInformerFactory.InformerFor(&apiv1.Pod{}, newPodInformer) + controller := &KubemarkController{ + externalCluster: externalCluster{ + rcLister: listersv1.NewReplicationControllerLister(rcInformer.GetIndexer()), + rcSynced: rcInformer.HasSynced, + podLister: listersv1.NewPodLister(podInformer.GetIndexer()), + podSynced: podInformer.HasSynced, + client: externalClient, + }, + kubemarkCluster: kubemarkCluster{ + nodeLister: kubemarkNodeInformer.Lister(), + nodeSynced: kubemarkNodeInformer.Informer().HasSynced, + client: kubemarkClient, + nodesToDelete: make(map[string]bool), + nodesToDeleteLock: sync.Mutex{}, + }, + rand: rand.New(rand.NewSource(time.Now().UTC().UnixNano())), + } + + kubemarkNodeInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + UpdateFunc: controller.kubemarkCluster.removeUnneededNodes, + }) + + return controller, nil +} + +// Init waits for population of caches and populates the node template needed +// for creation of kubemark nodes. +func (kubemarkController *KubemarkController) Init(stopCh chan struct{}) { + if !controller.WaitForCacheSync("kubemark", stopCh, + kubemarkController.externalCluster.rcSynced, + kubemarkController.externalCluster.podSynced, + kubemarkController.kubemarkCluster.nodeSynced) { + return + } + + // Get hollow node template from an existing hollow node to be able to create + // new nodes based on it. + nodeTemplate, err := kubemarkController.getNodeTemplate() + if err != nil { + glog.Fatalf("Failed to get node template: %s", err) + } + kubemarkController.nodeTemplate = nodeTemplate +} + +// GetNodesForNodegroup returns list of the nodes in the node group. +func (kubemarkController *KubemarkController) GetNodeNamesForNodegroup(nodeGroup string) ([]string, error) { + selector := labels.SelectorFromSet(labels.Set{nodeGroupLabel: nodeGroup}) + pods, err := kubemarkController.externalCluster.podLister.List(selector) + if err != nil { + return nil, err + } + result := make([]string, 0, len(pods)) + for _, pod := range pods { + result = append(result, pod.ObjectMeta.Name) + } + return result, nil +} + +// GetNodeGroupSize returns the current size for the node group. +func (kubemarkController *KubemarkController) GetNodeGroupSize(nodeGroup string) (int, error) { + selector := labels.SelectorFromSet(labels.Set(map[string]string{nodeGroupLabel: nodeGroup})) + nodes, err := kubemarkController.externalCluster.rcLister.List(selector) + if err != nil { + return 0, err + } + return len(nodes), nil +} + +// SetNodeGroupSize changes the size of node group by adding or removing nodes. +func (kubemarkController *KubemarkController) SetNodeGroupSize(nodeGroup string, size int) error { + currSize, err := kubemarkController.GetNodeGroupSize(nodeGroup) + if err != nil { + return err + } + switch delta := size - currSize; { + case delta < 0: + absDelta := -delta + nodes, err := kubemarkController.GetNodeNamesForNodegroup(nodeGroup) + if err != nil { + return err + } + if len(nodes) > absDelta { + return fmt.Errorf("can't remove %d nodes from %s nodegroup, not enough nodes", absDelta, nodeGroup) + } + for i, node := range nodes { + if i == absDelta { + return nil + } + if err := kubemarkController.removeNodeFromNodeGroup(nodeGroup, node); err != nil { + return err + } + } + case delta > 0: + for i := 0; i < delta; i++ { + if err := kubemarkController.addNodeToNodeGroup(nodeGroup); err != nil { + return err + } + } + } + + return nil +} + +func (kubemarkController *KubemarkController) addNodeToNodeGroup(nodeGroup string) error { + templateCopy, err := api.Scheme.Copy(kubemarkController.nodeTemplate) + if err != nil { + return err + } + node := templateCopy.(*apiv1.ReplicationController) + node.Name = fmt.Sprintf("%s-%d", nodeGroup, kubemarkController.rand.Int63()) + node.Labels = map[string]string{nodeGroupLabel: nodeGroup, "name": node.Name} + node.Spec.Template.Labels = node.Labels + + for i := 0; i < numRetries; i++ { + _, err = kubemarkController.externalCluster.client.CoreV1().ReplicationControllers(node.Namespace).Create(node) + if err == nil { + return nil + } + } + + return err +} + +func (kubemarkController *KubemarkController) removeNodeFromNodeGroup(nodeGroup string, node string) error { + pods, err := kubemarkController.externalCluster.podLister.List(labels.Everything()) + if err != nil { + return err + } + for _, pod := range pods { + if pod.ObjectMeta.Name == node { + if pod.ObjectMeta.Labels[nodeGroupLabel] != nodeGroup { + return fmt.Errorf("can't delete node %s from nodegroup %s. Node is not in nodegroup", node, nodeGroup) + } + policy := metav1.DeletePropagationForeground + for i := 0; i < numRetries; i++ { + err := kubemarkController.externalCluster.client.CoreV1().ReplicationControllers(namespaceKubemark).Delete( + pod.ObjectMeta.Labels["name"], + &metav1.DeleteOptions{PropagationPolicy: &policy}) + if err == nil { + glog.Infof("marking node %s for deletion", node) + // Mark node for deletion from kubemark cluster. + // Once it becomes unready after replication controller + // deletion has been noticed, we will delete it explicitly. + // This is to cover for the fact that kubemark does not + // take care of this itself. + kubemarkController.kubemarkCluster.markNodeForDeletion(node) + return nil + } + } + } + } + + return fmt.Errorf("can't delete node %s from nodegroup %s. Node does not exist", node, nodeGroup) +} + +func (kubemarkController *KubemarkController) getReplicationControllerByName(name string) *apiv1.ReplicationController { + rcs, err := kubemarkController.externalCluster.rcLister.List(labels.Everything()) + if err != nil { + return nil + } + for _, rc := range rcs { + if rc.ObjectMeta.Name == name { + return rc + } + } + return nil +} + +func (kubemarkController *KubemarkController) getNodeNameForPod(podName string) (string, error) { + pods, err := kubemarkController.externalCluster.podLister.List(labels.Everything()) + if err != nil { + return "", err + } + for _, pod := range pods { + if pod.ObjectMeta.Name == podName { + return pod.Labels["name"], nil + } + } + return "", fmt.Errorf("pod %s not found", podName) +} + +// getNodeTemplate returns the template for hollow node replication controllers +// by looking for an existing hollow node specification. This requires at least +// one kubemark node to be present on startup. +func (kubemarkController *KubemarkController) getNodeTemplate() (*apiv1.ReplicationController, error) { + podName, err := kubemarkController.kubemarkCluster.getHollowNodeName() + if err != nil { + return nil, err + } + hollowNodeName, err := kubemarkController.getNodeNameForPod(podName) + if err != nil { + return nil, err + } + if hollowNode := kubemarkController.getReplicationControllerByName(hollowNodeName); hollowNode != nil { + nodeTemplate := &apiv1.ReplicationController{ + Spec: apiv1.ReplicationControllerSpec{ + Template: hollowNode.Spec.Template, + }, + } + + nodeTemplate.Spec.Selector = nil + nodeTemplate.Namespace = namespaceKubemark + one := int32(1) + nodeTemplate.Spec.Replicas = &one + + return nodeTemplate, nil + } + return nil, fmt.Errorf("can't get hollow node template") +} + +func (kubemarkCluster *kubemarkCluster) getHollowNodeName() (string, error) { + nodes, err := kubemarkCluster.nodeLister.List(labels.Everything()) + if err != nil { + return "", err + } + for _, node := range nodes { + return node.Name, nil + } + return "", fmt.Errorf("did not find any hollow nodes in the cluster") +} + +func (kubemarkCluster *kubemarkCluster) removeUnneededNodes(oldObj interface{}, newObj interface{}) { + node, ok := newObj.(*apiv1.Node) + if !ok { + return + } + for _, condition := range node.Status.Conditions { + // Delete node if it is in unready state, and it has been + // explicitly marked for deletion. + if condition.Type == apiv1.NodeReady && condition.Status != apiv1.ConditionTrue { + kubemarkCluster.nodesToDeleteLock.Lock() + defer kubemarkCluster.nodesToDeleteLock.Unlock() + if kubemarkCluster.nodesToDelete[node.Name] { + kubemarkCluster.nodesToDelete[node.Name] = false + if err := kubemarkCluster.client.CoreV1().Nodes().Delete(node.Name, &metav1.DeleteOptions{}); err != nil { + glog.Errorf("failed to delete node %s from kubemark cluster", node.Name) + } + } + return + } + } +} + +func (kubemarkCluster *kubemarkCluster) markNodeForDeletion(name string) { + kubemarkCluster.nodesToDeleteLock.Lock() + defer kubemarkCluster.nodesToDeleteLock.Unlock() + kubemarkCluster.nodesToDelete[name] = true +} + +func newReplicationControllerInformer(kubeClient kubeclient.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + rcListWatch := cache.NewListWatchFromClient(kubeClient.CoreV1().RESTClient(), "replicationcontrollers", namespaceKubemark, fields.Everything()) + return cache.NewSharedIndexInformer(rcListWatch, &apiv1.ReplicationController{}, resyncPeriod, nil) +} + +func newPodInformer(kubeClient kubeclient.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + podListWatch := cache.NewListWatchFromClient(kubeClient.CoreV1().RESTClient(), "pods", namespaceKubemark, fields.Everything()) + return cache.NewSharedIndexInformer(podListWatch, &apiv1.Pod{}, resyncPeriod, nil) +} diff --git a/test/e2e/framework/BUILD b/test/e2e/framework/BUILD index 06be9b417a8..c9d7f0de190 100644 --- a/test/e2e/framework/BUILD +++ b/test/e2e/framework/BUILD @@ -68,6 +68,7 @@ go_library( "//pkg/kubelet/metrics:go_default_library", "//pkg/kubelet/sysctl:go_default_library", "//pkg/kubelet/util/format:go_default_library", + "//pkg/kubemark:go_default_library", "//pkg/master/ports:go_default_library", "//pkg/ssh:go_default_library", "//pkg/util/file:go_default_library", @@ -128,6 +129,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/client-go/discovery:go_default_library", "//vendor/k8s.io/client-go/dynamic:go_default_library", + "//vendor/k8s.io/client-go/informers:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", "//vendor/k8s.io/client-go/kubernetes/typed/authorization/v1beta1:go_default_library", "//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library", diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index ccf9c43fdf8..f4fa2bfa8f5 100644 --- a/test/e2e/framework/framework.go +++ b/test/e2e/framework/framework.go @@ -37,12 +37,15 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/dynamic" + "k8s.io/client-go/informers" clientset "k8s.io/client-go/kubernetes" staging "k8s.io/client-go/kubernetes" clientreporestclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + "k8s.io/kubernetes/pkg/kubemark" "k8s.io/kubernetes/test/e2e/framework/metrics" testutils "k8s.io/kubernetes/test/utils" @@ -95,6 +98,8 @@ type Framework struct { // Place where various additional data is stored during test run to be printed to ReportDir, // or stdout if ReportDir is not set once test ends. TestSummaries []TestDataSummary + + kubemarkControllerCloseChannel chan struct{} } type TestDataSummary interface { @@ -190,6 +195,23 @@ func (f *Framework) BeforeEach() { f.StagingClient, err = staging.NewForConfig(clientRepoConfig) Expect(err).NotTo(HaveOccurred()) f.ClientPool = dynamic.NewClientPool(config, api.Registry.RESTMapper(), dynamic.LegacyAPIPathResolverFunc) + if ProviderIs("kubemark") && TestContext.KubemarkExternalKubeConfig != "" && TestContext.CloudConfig.KubemarkController == nil { + externalConfig, err := clientcmd.BuildConfigFromFlags("", TestContext.KubemarkExternalKubeConfig) + externalConfig.QPS = f.Options.ClientQPS + externalConfig.Burst = f.Options.ClientBurst + Expect(err).NotTo(HaveOccurred()) + externalClient, err := clientset.NewForConfig(externalConfig) + Expect(err).NotTo(HaveOccurred()) + f.kubemarkControllerCloseChannel = make(chan struct{}) + externalInformerFactory := informers.NewSharedInformerFactory(externalClient, 0) + kubemarkInformerFactory := informers.NewSharedInformerFactory(f.ClientSet, 0) + kubemarkNodeInformer := kubemarkInformerFactory.Core().V1().Nodes() + go kubemarkNodeInformer.Informer().Run(f.kubemarkControllerCloseChannel) + TestContext.CloudConfig.KubemarkController, err = kubemark.NewKubemarkController(externalClient, externalInformerFactory, f.ClientSet, kubemarkNodeInformer) + Expect(err).NotTo(HaveOccurred()) + externalInformerFactory.Start(f.kubemarkControllerCloseChannel) + TestContext.CloudConfig.KubemarkController.Init(f.kubemarkControllerCloseChannel) + } } if !f.SkipNamespaceCreation { @@ -342,6 +364,10 @@ func (f *Framework) AfterEach() { } } + if TestContext.CloudConfig.KubemarkController != nil { + close(f.kubemarkControllerCloseChannel) + } + PrintSummaries(f.TestSummaries, f.BaseName) // Check whether all nodes are ready after the test. diff --git a/test/e2e/framework/size.go b/test/e2e/framework/size.go index ec20dafbe89..7883f41fc08 100644 --- a/test/e2e/framework/size.go +++ b/test/e2e/framework/size.go @@ -51,6 +51,8 @@ func ResizeGroup(group string, size int32) error { } else if TestContext.Provider == "aws" { client := autoscaling.New(session.New()) return awscloud.ResizeInstanceGroup(client, group, int(size)) + } else if TestContext.Provider == "kubemark" { + return TestContext.CloudConfig.KubemarkController.SetNodeGroupSize(group, int(size)) } else { return fmt.Errorf("Provider does not support InstanceGroups") } @@ -72,6 +74,8 @@ func GetGroupNodes(group string) ([]string, error) { lines[i] = line[:strings.Index(line, " ")] } return lines, nil + } else if TestContext.Provider == "kubemark" { + return TestContext.CloudConfig.KubemarkController.GetNodeNamesForNodegroup(group) } else { return nil, fmt.Errorf("provider does not support InstanceGroups") } @@ -99,6 +103,8 @@ func GroupSize(group string) (int, error) { return -1, fmt.Errorf("instance group not found: %s", group) } return instanceGroup.CurrentSize() + } else if TestContext.Provider == "kubemark" { + return TestContext.CloudConfig.KubemarkController.GetNodeGroupSize(group) } else { return -1, fmt.Errorf("provider does not support InstanceGroups") } diff --git a/test/e2e/framework/test_context.go b/test/e2e/framework/test_context.go index 309a372b63c..b4f87b092cb 100644 --- a/test/e2e/framework/test_context.go +++ b/test/e2e/framework/test_context.go @@ -27,17 +27,19 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/kubernetes/pkg/apis/componentconfig" "k8s.io/kubernetes/pkg/cloudprovider" + "k8s.io/kubernetes/pkg/kubemark" ) const defaultHost = "http://127.0.0.1:8080" type TestContextType struct { - KubeConfig string - KubeContext string - KubeAPIContentType string - KubeVolumeDir string - CertDir string - Host string + KubeConfig string + KubemarkExternalKubeConfig string + KubeContext string + KubeAPIContentType string + KubeVolumeDir string + CertDir string + Host string // TODO: Deprecating this over time... instead just use gobindata_util.go , see #23987. RepoRoot string DockershimCheckpointDir string @@ -158,7 +160,8 @@ type CloudConfig struct { NodeTag string MasterTag string - Provider cloudprovider.Interface + Provider cloudprovider.Interface + KubemarkController *kubemark.KubemarkController } var TestContext TestContextType @@ -201,6 +204,7 @@ func RegisterCommonFlags() { func RegisterClusterFlags() { flag.BoolVar(&TestContext.VerifyServiceAccount, "e2e-verify-service-account", true, "If true tests will verify the service account before running.") flag.StringVar(&TestContext.KubeConfig, clientcmd.RecommendedConfigPathFlag, os.Getenv(clientcmd.RecommendedConfigPathEnvVar), "Path to kubeconfig containing embedded authinfo.") + flag.StringVar(&TestContext.KubemarkExternalKubeConfig, fmt.Sprintf("%s-%s", "kubemark-external", clientcmd.RecommendedConfigPathFlag), "", "Path to kubeconfig containing embedded authinfo for external cluster.") flag.StringVar(&TestContext.KubeContext, clientcmd.FlagContext, "", "kubeconfig context to use/override. If unset, will use value from 'current-context'") flag.StringVar(&TestContext.KubeAPIContentType, "kube-api-content-type", "application/vnd.kubernetes.protobuf", "ContentType used to communicate with apiserver") flag.StringVar(&TestContext.FederatedKubeContext, "federated-kube-context", "e2e-federation", "kubeconfig context for federation.") From 17eb58131a29c7299299fa8c690bcf8fa4c20c84 Mon Sep 17 00:00:00 2001 From: Solly Ross Date: Fri, 4 Aug 2017 14:44:54 -0400 Subject: [PATCH 29/38] Handle missing OpenAPI specs on aggregated servers Previously, the aggregator would fail to actually set up the aggregator proxy for an API server that was missing an OpenAPI spec. It would show up in discovery, but the actual proxying would fail to occur. Now, we simply log an error if we can't fetch an OpenAPI spec for a particular aggregated server, and continue on. --- staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go index 40b07d267f3..0d72b9e43df 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go @@ -17,6 +17,7 @@ limitations under the License. package apiserver import ( + "fmt" "net/http" "time" @@ -26,6 +27,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/sets" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/rest" @@ -261,7 +263,7 @@ func (s *APIAggregator) AddAPIService(apiService *apiregistration.APIService) er } proxyHandler.updateAPIService(apiService) if err := s.openAPIAggregator.loadApiServiceSpec(proxyHandler, apiService); err != nil { - return err + utilruntime.HandleError(fmt.Errorf("unable to load OpenAPI spec for API service %s: %v", apiService.Name, err)) } s.proxyHandlers[apiService.Name] = proxyHandler s.GenericAPIServer.Handler.NonGoRestfulMux.Handle(proxyPath, proxyHandler) From 47d426c4413e4a3d3dd81158b8e6172ca55fc025 Mon Sep 17 00:00:00 2001 From: David Eads Date: Mon, 7 Aug 2017 12:55:29 -0400 Subject: [PATCH 30/38] provide the failing health as part of the controller error --- cmd/kube-controller-manager/app/controllermanager.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/kube-controller-manager/app/controllermanager.go b/cmd/kube-controller-manager/app/controllermanager.go index 7fed1789165..0c7659cbb41 100644 --- a/cmd/kube-controller-manager/app/controllermanager.go +++ b/cmd/kube-controller-manager/app/controllermanager.go @@ -342,6 +342,7 @@ func NewControllerInitializers() map[string]InitFunc { func GetAvailableResources(clientBuilder controller.ControllerClientBuilder) (map[schema.GroupVersionResource]bool, error) { var discoveryClient discovery.DiscoveryInterface + var healthzContent string // If apiserver is not running we should wait for some time and fail only then. This is particularly // important when we start apiserver and controller manager at the same time. err := wait.PollImmediate(time.Second, 10*time.Second, func() (bool, error) { @@ -352,17 +353,19 @@ func GetAvailableResources(clientBuilder controller.ControllerClientBuilder) (ma } healthStatus := 0 - client.Discovery().RESTClient().Get().AbsPath("/healthz").Do().StatusCode(&healthStatus) + resp := client.Discovery().RESTClient().Get().AbsPath("/healthz").Do().StatusCode(&healthStatus) if healthStatus != http.StatusOK { glog.Errorf("Server isn't healthy yet. Waiting a little while.") return false, nil } + content, _ := resp.Raw() + healthzContent = string(content) discoveryClient = client.Discovery() return true, nil }) if err != nil { - return nil, fmt.Errorf("failed to get api versions from server: %v", err) + return nil, fmt.Errorf("failed to get api versions from server: %v: %v", healthzContent, err) } resourceMap, err := discoveryClient.ServerResources() From 4dcdfd4aa8b3c2412c2bfd086831dd2f97368c99 Mon Sep 17 00:00:00 2001 From: jianhuiz Date: Thu, 3 Aug 2017 11:01:48 -0700 Subject: [PATCH 31/38] add job controller --- .../federation-controller-manager/app/BUILD | 1 + .../app/controllermanager.go | 9 + .../app/options/options.go | 6 + federation/pkg/federation-controller/BUILD | 1 + .../pkg/federation-controller/job/BUILD | 80 +++ .../job/jobcontroller.go | 561 ++++++++++++++++++ .../job/jobcontroller_test.go | 282 +++++++++ .../federation-controller/util/handlers.go | 1 + hack/verify-flags/known-flags.txt | 1 + 9 files changed, 942 insertions(+) create mode 100644 federation/pkg/federation-controller/job/BUILD create mode 100644 federation/pkg/federation-controller/job/jobcontroller.go create mode 100644 federation/pkg/federation-controller/job/jobcontroller_test.go diff --git a/federation/cmd/federation-controller-manager/app/BUILD b/federation/cmd/federation-controller-manager/app/BUILD index a4fffac2a2b..0d7514b4aef 100644 --- a/federation/cmd/federation-controller-manager/app/BUILD +++ b/federation/cmd/federation-controller-manager/app/BUILD @@ -24,6 +24,7 @@ go_library( "//federation/pkg/federatedtypes:go_default_library", "//federation/pkg/federation-controller/cluster:go_default_library", "//federation/pkg/federation-controller/ingress:go_default_library", + "//federation/pkg/federation-controller/job:go_default_library", "//federation/pkg/federation-controller/service:go_default_library", "//federation/pkg/federation-controller/service/dns:go_default_library", "//federation/pkg/federation-controller/sync:go_default_library", diff --git a/federation/cmd/federation-controller-manager/app/controllermanager.go b/federation/cmd/federation-controller-manager/app/controllermanager.go index 9144eca97f4..d4abefe200c 100644 --- a/federation/cmd/federation-controller-manager/app/controllermanager.go +++ b/federation/cmd/federation-controller-manager/app/controllermanager.go @@ -37,6 +37,7 @@ import ( "k8s.io/kubernetes/federation/pkg/federatedtypes" clustercontroller "k8s.io/kubernetes/federation/pkg/federation-controller/cluster" ingresscontroller "k8s.io/kubernetes/federation/pkg/federation-controller/ingress" + jobcontroller "k8s.io/kubernetes/federation/pkg/federation-controller/job" servicecontroller "k8s.io/kubernetes/federation/pkg/federation-controller/service" servicednscontroller "k8s.io/kubernetes/federation/pkg/federation-controller/service/dns" synccontroller "k8s.io/kubernetes/federation/pkg/federation-controller/sync" @@ -155,6 +156,14 @@ func StartControllers(s *options.CMServer, restClientCfg *restclient.Config) err } } + if controllerEnabled(s.Controllers, serverResources, jobcontroller.ControllerName, jobcontroller.RequiredResources, true) { + glog.V(3).Infof("Loading client config for job controller %q", jobcontroller.UserAgentName) + jobClientset := federationclientset.NewForConfigOrDie(restclient.AddUserAgent(restClientCfg, jobcontroller.UserAgentName)) + jobController := jobcontroller.NewJobController(jobClientset) + glog.V(3).Infof("Running job controller") + go jobController.Run(s.ConcurrentJobSyncs, wait.NeverStop) + } + if controllerEnabled(s.Controllers, serverResources, ingresscontroller.ControllerName, ingresscontroller.RequiredResources, true) { glog.V(3).Infof("Loading client config for ingress controller %q", ingresscontroller.UserAgentName) ingClientset := federationclientset.NewForConfigOrDie(restclient.AddUserAgent(restClientCfg, ingresscontroller.UserAgentName)) diff --git a/federation/cmd/federation-controller-manager/app/options/options.go b/federation/cmd/federation-controller-manager/app/options/options.go index e6659c36977..5d6efca5a10 100644 --- a/federation/cmd/federation-controller-manager/app/options/options.go +++ b/federation/cmd/federation-controller-manager/app/options/options.go @@ -56,6 +56,10 @@ type ControllerManagerConfiguration struct { // allowed to sync concurrently. Larger number = more responsive service // management, but more CPU (and network) load. ConcurrentReplicaSetSyncs int `json:"concurrentReplicaSetSyncs"` + // concurrentJobSyncs is the number of Jobs that are + // allowed to sync concurrently. Larger number = more responsive service + // management, but more CPU (and network) load. + ConcurrentJobSyncs int `json:"concurrentJobSyncs"` // clusterMonitorPeriod is the period for syncing ClusterStatus in cluster controller. ClusterMonitorPeriod metav1.Duration `json:"clusterMonitorPeriod"` // APIServerQPS is the QPS to use while talking with federation apiserver. @@ -96,6 +100,7 @@ func NewCMServer() *CMServer { ConcurrentServiceSyncs: 10, ConcurrentReplicaSetSyncs: 10, ClusterMonitorPeriod: metav1.Duration{Duration: 40 * time.Second}, + ConcurrentJobSyncs: 10, APIServerQPS: 20.0, APIServerBurst: 30, LeaderElection: leaderelectionconfig.DefaultLeaderElectionConfiguration(), @@ -115,6 +120,7 @@ func (s *CMServer) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&s.ServiceDnsSuffix, "service-dns-suffix", s.ServiceDnsSuffix, "DNS Suffix to use when publishing federated service names. Defaults to zone-name") fs.IntVar(&s.ConcurrentServiceSyncs, "concurrent-service-syncs", s.ConcurrentServiceSyncs, "The number of service syncing operations that will be done concurrently. Larger number = faster endpoint updating, but more CPU (and network) load") fs.IntVar(&s.ConcurrentReplicaSetSyncs, "concurrent-replicaset-syncs", s.ConcurrentReplicaSetSyncs, "The number of ReplicaSets syncing operations that will be done concurrently. Larger number = faster endpoint updating, but more CPU (and network) load") + fs.IntVar(&s.ConcurrentJobSyncs, "concurrent-job-syncs", s.ConcurrentJobSyncs, "The number of Jobs syncing operations that will be done concurrently. Larger number = faster endpoint updating, but more CPU (and network) load") fs.DurationVar(&s.ClusterMonitorPeriod.Duration, "cluster-monitor-period", s.ClusterMonitorPeriod.Duration, "The period for syncing ClusterStatus in ClusterController.") fs.BoolVar(&s.EnableProfiling, "profiling", true, "Enable profiling via web interface host:port/debug/pprof/") fs.BoolVar(&s.EnableContentionProfiling, "contention-profiling", false, "Enable lock contention profiling, if profiling is enabled") diff --git a/federation/pkg/federation-controller/BUILD b/federation/pkg/federation-controller/BUILD index 28057fc52ea..951da1b7786 100644 --- a/federation/pkg/federation-controller/BUILD +++ b/federation/pkg/federation-controller/BUILD @@ -26,6 +26,7 @@ filegroup( ":package-srcs", "//federation/pkg/federation-controller/cluster:all-srcs", "//federation/pkg/federation-controller/ingress:all-srcs", + "//federation/pkg/federation-controller/job:all-srcs", "//federation/pkg/federation-controller/service:all-srcs", "//federation/pkg/federation-controller/sync:all-srcs", "//federation/pkg/federation-controller/util:all-srcs", diff --git a/federation/pkg/federation-controller/job/BUILD b/federation/pkg/federation-controller/job/BUILD new file mode 100644 index 00000000000..da81dc92077 --- /dev/null +++ b/federation/pkg/federation-controller/job/BUILD @@ -0,0 +1,80 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_library( + name = "go_default_library", + srcs = ["jobcontroller.go"], + tags = ["automanaged"], + deps = [ + "//federation/apis/federation:go_default_library", + "//federation/apis/federation/v1beta1:go_default_library", + "//federation/client/clientset_generated/federation_clientset:go_default_library", + "//federation/pkg/federation-controller/util:go_default_library", + "//federation/pkg/federation-controller/util/deletionhelper:go_default_library", + "//federation/pkg/federation-controller/util/eventsink:go_default_library", + "//federation/pkg/federation-controller/util/planner:go_default_library", + "//federation/pkg/federation-controller/util/replicapreferences:go_default_library", + "//pkg/api:go_default_library", + "//pkg/controller:go_default_library", + "//vendor/github.com/davecgh/go-spew/spew:go_default_library", + "//vendor/github.com/golang/glog:go_default_library", + "//vendor/k8s.io/api/batch/v1:go_default_library", + "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", + "//vendor/k8s.io/client-go/kubernetes:go_default_library", + "//vendor/k8s.io/client-go/tools/cache:go_default_library", + "//vendor/k8s.io/client-go/tools/record:go_default_library", + "//vendor/k8s.io/client-go/util/flowcontrol:go_default_library", + "//vendor/k8s.io/client-go/util/workqueue:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["jobcontroller_test.go"], + library = ":go_default_library", + tags = ["automanaged"], + deps = [ + "//federation/apis/federation/v1beta1:go_default_library", + "//federation/client/clientset_generated/federation_clientset/fake:go_default_library", + "//federation/pkg/federation-controller/util:go_default_library", + "//federation/pkg/federation-controller/util/finalizers:go_default_library", + "//federation/pkg/federation-controller/util/test:go_default_library", + "//pkg/apis/batch/v1:go_default_library", + "//vendor/github.com/stretchr/testify/assert:go_default_library", + "//vendor/k8s.io/api/batch/v1:go_default_library", + "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", + "//vendor/k8s.io/client-go/kubernetes:go_default_library", + "//vendor/k8s.io/client-go/kubernetes/fake:go_default_library", + "//vendor/k8s.io/client-go/testing:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) diff --git a/federation/pkg/federation-controller/job/jobcontroller.go b/federation/pkg/federation-controller/job/jobcontroller.go new file mode 100644 index 00000000000..d3977182fc1 --- /dev/null +++ b/federation/pkg/federation-controller/job/jobcontroller.go @@ -0,0 +1,561 @@ +/* +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 job + +import ( + "fmt" + "reflect" + "time" + + "github.com/davecgh/go-spew/spew" + "github.com/golang/glog" + + batchv1 "k8s.io/api/batch/v1" + clientv1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apimachinery/pkg/watch" + kubeclientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/flowcontrol" + "k8s.io/client-go/util/workqueue" + fed "k8s.io/kubernetes/federation/apis/federation" + fedv1 "k8s.io/kubernetes/federation/apis/federation/v1beta1" + fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" + fedutil "k8s.io/kubernetes/federation/pkg/federation-controller/util" + "k8s.io/kubernetes/federation/pkg/federation-controller/util/deletionhelper" + "k8s.io/kubernetes/federation/pkg/federation-controller/util/eventsink" + "k8s.io/kubernetes/federation/pkg/federation-controller/util/planner" + "k8s.io/kubernetes/federation/pkg/federation-controller/util/replicapreferences" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/controller" +) + +const ( + fedJobPreferencesAnnotation = "federation.kubernetes.io/job-preferences" + allClustersKey = "THE_ALL_CLUSTER_KEY" + // UserAgentName is the user agent used in the federation client + UserAgentName = "Federation-Job-Controller" + // ControllerName is name of this controller + ControllerName = "jobs" +) + +var ( + // RequiredResources is the resource group version of the type this controller manages + RequiredResources = []schema.GroupVersionResource{batchv1.SchemeGroupVersion.WithResource("jobs")} + jobReviewDelay = 10 * time.Second + clusterAvailableDelay = 20 * time.Second + clusterUnavailableDelay = 60 * time.Second + updateTimeout = 30 * time.Second + backoffInitial = 5 * time.Second + backoffMax = 1 * time.Minute +) + +// FederationJobController synchronizes the state of a federated job object +// to clusters that are members of the federation. +type FederationJobController struct { + fedClient fedclientset.Interface + + jobController cache.Controller + jobStore cache.Store + + fedJobInformer fedutil.FederatedInformer + + jobDeliverer *fedutil.DelayingDeliverer + clusterDeliverer *fedutil.DelayingDeliverer + jobWorkQueue workqueue.Interface + // For updating members of federation. + fedUpdater fedutil.FederatedUpdater + + jobBackoff *flowcontrol.Backoff + // For events + eventRecorder record.EventRecorder + + defaultPlanner *planner.Planner + deletionHelper *deletionhelper.DeletionHelper +} + +// NewJobController creates a new federation job controller +func NewJobController(fedClient fedclientset.Interface) *FederationJobController { + broadcaster := record.NewBroadcaster() + broadcaster.StartRecordingToSink(eventsink.NewFederatedEventSink(fedClient)) + recorder := broadcaster.NewRecorder(api.Scheme, clientv1.EventSource{Component: "federated-job-controller"}) + fjc := &FederationJobController{ + fedClient: fedClient, + jobDeliverer: fedutil.NewDelayingDeliverer(), + clusterDeliverer: fedutil.NewDelayingDeliverer(), + jobWorkQueue: workqueue.New(), + jobBackoff: flowcontrol.NewBackOff(backoffInitial, backoffMax), + defaultPlanner: planner.NewPlanner(&fed.ReplicaAllocationPreferences{ + Clusters: map[string]fed.ClusterPreferences{ + "*": {Weight: 1}, + }, + }), + eventRecorder: recorder, + } + + jobFedInformerFactory := func(cluster *fedv1.Cluster, clientset kubeclientset.Interface) (cache.Store, cache.Controller) { + return cache.NewInformer( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + return clientset.BatchV1().Jobs(metav1.NamespaceAll).List(options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + return clientset.BatchV1().Jobs(metav1.NamespaceAll).Watch(options) + }, + }, + &batchv1.Job{}, + controller.NoResyncPeriodFunc(), + fedutil.NewTriggerOnAllChanges( + func(obj runtime.Object) { fjc.deliverLocalJob(obj, jobReviewDelay) }, + ), + ) + } + clusterLifecycle := fedutil.ClusterLifecycleHandlerFuncs{ + ClusterAvailable: func(cluster *fedv1.Cluster) { + fjc.clusterDeliverer.DeliverAfter(allClustersKey, nil, clusterAvailableDelay) + }, + ClusterUnavailable: func(cluster *fedv1.Cluster, _ []interface{}) { + fjc.clusterDeliverer.DeliverAfter(allClustersKey, nil, clusterUnavailableDelay) + }, + } + fjc.fedJobInformer = fedutil.NewFederatedInformer(fedClient, jobFedInformerFactory, &clusterLifecycle) + + fjc.jobStore, fjc.jobController = cache.NewInformer( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + return fjc.fedClient.BatchV1().Jobs(metav1.NamespaceAll).List(options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + return fjc.fedClient.BatchV1().Jobs(metav1.NamespaceAll).Watch(options) + }, + }, + &batchv1.Job{}, + controller.NoResyncPeriodFunc(), + fedutil.NewTriggerOnMetaAndSpecChanges( + func(obj runtime.Object) { fjc.deliverFedJobObj(obj, 0) }, + ), + ) + + fjc.fedUpdater = fedutil.NewFederatedUpdater(fjc.fedJobInformer, "job", updateTimeout, fjc.eventRecorder, + func(client kubeclientset.Interface, obj runtime.Object) error { + rs := obj.(*batchv1.Job) + _, err := client.BatchV1().Jobs(rs.Namespace).Create(rs) + return err + }, + func(client kubeclientset.Interface, obj runtime.Object) error { + rs := obj.(*batchv1.Job) + _, err := client.BatchV1().Jobs(rs.Namespace).Update(rs) + return err + }, + func(client kubeclientset.Interface, obj runtime.Object) error { + rs := obj.(*batchv1.Job) + err := client.BatchV1().Jobs(rs.Namespace).Delete(rs.Name, &metav1.DeleteOptions{}) + return err + }) + + fjc.deletionHelper = deletionhelper.NewDeletionHelper( + fjc.updateJob, + // objNameFunc + func(obj runtime.Object) string { + job := obj.(*batchv1.Job) + return job.Name + }, + fjc.fedJobInformer, + fjc.fedUpdater, + ) + + return fjc +} + +// Sends the given updated object to apiserver. +// Assumes that the given object is a job. +func (fjc *FederationJobController) updateJob(obj runtime.Object) (runtime.Object, error) { + job := obj.(*batchv1.Job) + return fjc.fedClient.BatchV1().Jobs(job.Namespace).Update(job) +} + +// Run starts the syncing of federation jobs to the clusters. +func (fjc *FederationJobController) Run(workers int, stopCh <-chan struct{}) { + go fjc.jobController.Run(stopCh) + fjc.fedJobInformer.Start() + + fjc.jobDeliverer.StartWithHandler(func(item *fedutil.DelayingDelivererItem) { + fjc.jobWorkQueue.Add(item.Key) + }) + fjc.clusterDeliverer.StartWithHandler(func(_ *fedutil.DelayingDelivererItem) { + fjc.reconcileJobsOnClusterChange() + }) + + for !fjc.isSynced() { + time.Sleep(5 * time.Millisecond) + } + + for i := 0; i < workers; i++ { + go wait.Until(fjc.worker, time.Second, stopCh) + } + + fedutil.StartBackoffGC(fjc.jobBackoff, stopCh) + + <-stopCh + glog.Infof("Shutting down FederationJobController") + fjc.jobDeliverer.Stop() + fjc.clusterDeliverer.Stop() + fjc.jobWorkQueue.ShutDown() + fjc.fedJobInformer.Stop() +} + +func (fjc *FederationJobController) isSynced() bool { + if !fjc.fedJobInformer.ClustersSynced() { + glog.V(3).Infof("Cluster list not synced") + return false + } + clusters, err := fjc.fedJobInformer.GetReadyClusters() + if err != nil { + glog.Errorf("Failed to get ready clusters: %v", err) + return false + } + if !fjc.fedJobInformer.GetTargetStore().ClustersSynced(clusters) { + glog.V(2).Infof("cluster job list not synced") + return false + } + + if !fjc.jobController.HasSynced() { + glog.V(2).Infof("federation job list not synced") + return false + } + return true +} + +func (fjc *FederationJobController) deliverLocalJob(obj interface{}, duration time.Duration) { + key, err := controller.KeyFunc(obj) + if err != nil { + glog.Errorf("Couldn't get key for object %v: %v", obj, err) + return + } + _, exists, err := fjc.jobStore.GetByKey(key) + if err != nil { + glog.Errorf("Couldn't get federated job %v: %v", key, err) + return + } + if exists { // ignore jobs exists only in local k8s + fjc.deliverJobByKey(key, duration, false) + } +} + +func (fjc *FederationJobController) deliverFedJobObj(obj interface{}, delay time.Duration) { + key, err := controller.KeyFunc(obj) + if err != nil { + glog.Errorf("Couldn't get key for object %+v: %v", obj, err) + return + } + fjc.deliverJobByKey(key, delay, false) +} + +func (fjc *FederationJobController) deliverJobByKey(key string, delay time.Duration, failed bool) { + if failed { + fjc.jobBackoff.Next(key, time.Now()) + delay = delay + fjc.jobBackoff.Get(key) + } else { + fjc.jobBackoff.Reset(key) + } + fjc.jobDeliverer.DeliverAfter(key, nil, delay) +} + +type reconciliationStatus string + +const ( + statusAllOk = reconciliationStatus("ALL_OK") + statusNeedRecheck = reconciliationStatus("RECHECK") + statusError = reconciliationStatus("ERROR") + statusNotSynced = reconciliationStatus("NOSYNC") +) + +func (fjc *FederationJobController) worker() { + for { + item, quit := fjc.jobWorkQueue.Get() + if quit { + return + } + key := item.(string) + status, err := fjc.reconcileJob(key) + fjc.jobWorkQueue.Done(item) + if err != nil { + glog.Errorf("Error syncing job controller: %v", err) + fjc.deliverJobByKey(key, 0, true) + } else { + switch status { + case statusAllOk: + break + case statusError: + fjc.deliverJobByKey(key, 0, true) + case statusNeedRecheck: + fjc.deliverJobByKey(key, jobReviewDelay, false) + case statusNotSynced: + fjc.deliverJobByKey(key, clusterAvailableDelay, false) + default: + glog.Errorf("Unhandled reconciliation status: %s", status) + fjc.deliverJobByKey(key, jobReviewDelay, false) + } + } + } +} + +type scheduleResult struct { + Parallelism *int32 + Completions *int32 +} + +func (fjc *FederationJobController) schedule(fjob *batchv1.Job, clusters []*fedv1.Cluster) map[string]scheduleResult { + plnr := fjc.defaultPlanner + frsPref, err := replicapreferences.GetAllocationPreferences(fjob, fedJobPreferencesAnnotation) + if err != nil { + glog.Warningf("Invalid job specific preference, use default. rs: %v, err: %v", fjob, err) + } + if frsPref != nil { // create a new planner if user specified a preference + plnr = planner.NewPlanner(frsPref) + } + + parallelism := int64(*fjob.Spec.Parallelism) + var clusterNames []string + for _, cluster := range clusters { + clusterNames = append(clusterNames, cluster.Name) + } + parallelismResult, _ := plnr.Plan(parallelism, clusterNames, nil, nil, fjob.Namespace+"/"+fjob.Name) + + if frsPref != nil { + for _, clusterPref := range frsPref.Clusters { + clusterPref.MinReplicas = 0 + clusterPref.MaxReplicas = nil + } + plnr = planner.NewPlanner(frsPref) + } + clusterNames = nil + for clusterName := range parallelismResult { + clusterNames = append(clusterNames, clusterName) + } + completionsResult := make(map[string]int64) + if fjob.Spec.Completions != nil { + completionsResult, _ = plnr.Plan(int64(*fjob.Spec.Completions), clusterNames, nil, nil, fjob.Namespace+"/"+fjob.Name) + } + + results := make(map[string]scheduleResult) + for _, clusterName := range clusterNames { + paralle := int32(parallelismResult[clusterName]) + complet := int32(completionsResult[clusterName]) + result := scheduleResult{ + Parallelism: ¶lle, + } + if fjob.Spec.Completions != nil { + result.Completions = &complet + } + results[clusterName] = result + } + + return results +} + +func (fjc *FederationJobController) reconcileJob(key string) (reconciliationStatus, error) { + if !fjc.isSynced() { + return statusNotSynced, nil + } + + glog.V(4).Infof("Start reconcile job %q", key) + startTime := time.Now() + defer glog.V(4).Infof("Finished reconcile job %q (%v)", key, time.Now().Sub(startTime)) + + objFromStore, exists, err := fjc.jobStore.GetByKey(key) + if err != nil { + return statusError, err + } + if !exists { + // deleted federated job, nothing need to do + return statusAllOk, nil + } + + // Create a copy before modifying the obj to prevent race condition with other readers of obj from store. + obj, err := api.Scheme.DeepCopy(objFromStore) + fjob, ok := obj.(*batchv1.Job) + if err != nil || !ok { + return statusError, err + } + + // delete job + if fjob.DeletionTimestamp != nil { + if err := fjc.delete(fjob); err != nil { + fjc.eventRecorder.Eventf(fjob, api.EventTypeNormal, "DeleteFailed", "Job delete failed: %v", err) + return statusError, err + } + return statusAllOk, nil + } + + glog.V(3).Infof("Ensuring delete object from underlying clusters finalizer for job: %s\n", key) + // Add the required finalizers before creating a job in underlying clusters. + updatedJobObj, err := fjc.deletionHelper.EnsureFinalizers(fjob) + if err != nil { + return statusError, err + } + fjob = updatedJobObj.(*batchv1.Job) + + clusters, err := fjc.fedJobInformer.GetReadyClusters() + if err != nil { + return statusError, err + } + + scheduleResult := fjc.schedule(fjob, clusters) + glog.V(3).Infof("Start syncing local job %s: %s\n", key, spew.Sprintf("%v", scheduleResult)) + + fedStatus := batchv1.JobStatus{} + var fedStatusFailedCondition *batchv1.JobCondition + var fedStatusCompleteCondition *batchv1.JobCondition + var operations []fedutil.FederatedOperation + for clusterName, result := range scheduleResult { + ljobObj, exists, err := fjc.fedJobInformer.GetTargetStore().GetByKey(clusterName, key) + if err != nil { + return statusError, err + } + ljob := &batchv1.Job{ + ObjectMeta: fedutil.DeepCopyRelevantObjectMeta(fjob.ObjectMeta), + Spec: *fedutil.DeepCopyApiTypeOrPanic(&fjob.Spec).(*batchv1.JobSpec), + } + // use selector generated at federation level, or user specified value + manualSelector := true + ljob.Spec.ManualSelector = &manualSelector + ljob.Spec.Parallelism = result.Parallelism + ljob.Spec.Completions = result.Completions + + if !exists { + if *ljob.Spec.Parallelism > 0 { + fjc.eventRecorder.Eventf(fjob, api.EventTypeNormal, "CreateInCluster", "Creating job in cluster %s", clusterName) + operations = append(operations, fedutil.FederatedOperation{ + Type: fedutil.OperationTypeAdd, + Obj: ljob, + ClusterName: clusterName, + }) + } + } else { + currentLjob := ljobObj.(*batchv1.Job) + + // Update existing job, if needed. + if !fedutil.ObjectMetaAndSpecEquivalent(ljob, currentLjob) { + fjc.eventRecorder.Eventf(fjob, api.EventTypeNormal, "UpdateInCluster", "Updating job in cluster %s", clusterName) + operations = append(operations, fedutil.FederatedOperation{ + Type: fedutil.OperationTypeUpdate, + Obj: ljob, + ClusterName: clusterName, + }) + } + + // collect local job status + for _, condition := range currentLjob.Status.Conditions { + if condition.Type == batchv1.JobComplete { + if fedStatusCompleteCondition == nil || + fedStatusCompleteCondition.LastTransitionTime.Before(condition.LastTransitionTime) { + fedStatusCompleteCondition = &condition + } + } else if condition.Type == batchv1.JobFailed { + if fedStatusFailedCondition == nil || + fedStatusFailedCondition.LastTransitionTime.Before(condition.LastTransitionTime) { + fedStatusFailedCondition = &condition + } + } + } + if currentLjob.Status.StartTime != nil { + if fedStatus.StartTime == nil || fedStatus.StartTime.After(currentLjob.Status.StartTime.Time) { + fedStatus.StartTime = currentLjob.Status.StartTime + } + } + if currentLjob.Status.CompletionTime != nil { + if fedStatus.CompletionTime == nil || fedStatus.CompletionTime.Before(*currentLjob.Status.CompletionTime) { + fedStatus.CompletionTime = currentLjob.Status.CompletionTime + } + } + fedStatus.Active += currentLjob.Status.Active + fedStatus.Succeeded += currentLjob.Status.Succeeded + fedStatus.Failed += currentLjob.Status.Failed + } + } + + // federated job fails if any local job failes + if fedStatusFailedCondition != nil { + fedStatus.Conditions = append(fedStatus.Conditions, *fedStatusFailedCondition) + } else if fedStatusCompleteCondition != nil { + fedStatus.Conditions = append(fedStatus.Conditions, *fedStatusCompleteCondition) + } + if !reflect.DeepEqual(fedStatus, fjob.Status) { + fjob.Status = fedStatus + _, err = fjc.fedClient.BatchV1().Jobs(fjob.Namespace).UpdateStatus(fjob) + if err != nil { + return statusError, err + } + } + + if len(operations) == 0 { + // Everything is in order + return statusAllOk, nil + } + + if glog.V(4) { + for i, op := range operations { + job := op.Obj.(*batchv1.Job) + glog.V(4).Infof("operation[%d]: %s, %s/%s/%s, %d", i, op.Type, op.ClusterName, job.Namespace, job.Name, *job.Spec.Parallelism) + } + } + err = fjc.fedUpdater.Update(operations) + if err != nil { + return statusError, err + } + + // Some operations were made, reconcile after a while. + return statusNeedRecheck, nil + +} + +func (fjc *FederationJobController) reconcileJobsOnClusterChange() { + if !fjc.isSynced() { + fjc.clusterDeliverer.DeliverAfter(allClustersKey, nil, clusterAvailableDelay) + } + jobs := fjc.jobStore.List() + for _, job := range jobs { + key, _ := controller.KeyFunc(job) + fjc.deliverJobByKey(key, 0, false) + } +} + +// delete deletes the given job or returns error if the deletion was not complete. +func (fjc *FederationJobController) delete(job *batchv1.Job) error { + glog.V(3).Infof("Handling deletion of job: %s/%s\n", job.Namespace, job.Name) + _, err := fjc.deletionHelper.HandleObjectInUnderlyingClusters(job) + if err != nil { + return err + } + + err = fjc.fedClient.BatchV1().Jobs(job.Namespace).Delete(job.Name, nil) + if err != nil { + // Its all good if the error is not found error. That means it is deleted already and we do not have to do anything. + // This is expected when we are processing an update as a result of job finalizer deletion. + // The process that deleted the last finalizer is also going to delete the job and we do not have to do anything. + if !errors.IsNotFound(err) { + return fmt.Errorf("failed to delete job: %s/%s, %v", job.Namespace, job.Name, err) + } + } + return nil +} diff --git a/federation/pkg/federation-controller/job/jobcontroller_test.go b/federation/pkg/federation-controller/job/jobcontroller_test.go new file mode 100644 index 00000000000..65a869baa4a --- /dev/null +++ b/federation/pkg/federation-controller/job/jobcontroller_test.go @@ -0,0 +1,282 @@ +/* +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 job + +import ( + "flag" + "fmt" + "testing" + "time" + + batchv1 "k8s.io/api/batch/v1" + apiv1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + kubeclientset "k8s.io/client-go/kubernetes" + kubeclientfake "k8s.io/client-go/kubernetes/fake" + core "k8s.io/client-go/testing" + fedv1 "k8s.io/kubernetes/federation/apis/federation/v1beta1" + fedclientfake "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset/fake" + fedutil "k8s.io/kubernetes/federation/pkg/federation-controller/util" + finalizersutil "k8s.io/kubernetes/federation/pkg/federation-controller/util/finalizers" + testutil "k8s.io/kubernetes/federation/pkg/federation-controller/util/test" + batchv1internal "k8s.io/kubernetes/pkg/apis/batch/v1" + + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/util/sets" + "reflect" + "strings" +) + +func installWatchReactor(fakeClien *core.Fake, resource string) chan runtime.Object { + objChan := make(chan runtime.Object, 100) + + fakeWatch := watch.NewRaceFreeFake() + fakeClien.PrependWatchReactor(resource, core.DefaultWatchReactor(fakeWatch, nil)) + fakeClien.PrependReactor("create", resource, func(action core.Action) (handled bool, ret runtime.Object, err error) { + obj := action.(core.CreateAction).GetObject() + batchv1internal.SetDefaults_Job(obj.(*batchv1.Job)) + fakeWatch.Add(obj) + objChan <- obj + return false, nil, nil + }) + fakeClien.PrependReactor("update", resource, func(action core.Action) (handled bool, ret runtime.Object, err error) { + obj := action.(core.UpdateAction).GetObject() + fakeWatch.Modify(obj) + objChan <- obj + return false, nil, nil + }) + fakeClien.PrependReactor("delete", resource, func(action core.Action) (handled bool, ret runtime.Object, err error) { + obj := &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: action.(core.DeleteAction).GetName(), + Namespace: action.GetNamespace(), + }, + } + fakeWatch.Delete(obj) + objChan <- obj + return false, nil, nil + }) + + return objChan +} + +func TestJobController(t *testing.T) { + flag.Set("logtostderr", "true") + flag.Set("v", "5") + flag.Parse() + + jobReviewDelay = 50 * time.Millisecond + clusterAvailableDelay = 200 * time.Millisecond + clusterUnavailableDelay = 200 * time.Millisecond + + fedclientset := fedclientfake.NewSimpleClientset() + fedChan := installWatchReactor(&fedclientset.Fake, "jobs") + + fedclientset.Federation().Clusters().Create(testutil.NewCluster("k8s-1", apiv1.ConditionTrue)) + fedclientset.Federation().Clusters().Create(testutil.NewCluster("k8s-2", apiv1.ConditionTrue)) + + kube1clientset := kubeclientfake.NewSimpleClientset() + kube1Chan := installWatchReactor(&kube1clientset.Fake, "jobs") + kube2clientset := kubeclientfake.NewSimpleClientset() + kube2Chan := installWatchReactor(&kube2clientset.Fake, "jobs") + + fedInformerClientFactory := func(cluster *fedv1.Cluster) (kubeclientset.Interface, error) { + switch cluster.Name { + case "k8s-1": + return kube1clientset, nil + case "k8s-2": + return kube2clientset, nil + default: + return nil, fmt.Errorf("Unknown cluster: %v", cluster.Name) + } + } + jobController := NewJobController(fedclientset) + fedjobinformer := testutil.ToFederatedInformerForTestOnly(jobController.fedJobInformer) + fedjobinformer.SetClientFactory(fedInformerClientFactory) + + stopChan := make(chan struct{}) + defer close(stopChan) + go jobController.Run(5, stopChan) + + test := func(job *batchv1.Job, parallelism1, parallelism2, completions1, completions2 int32) { + job, _ = fedclientset.Batch().Jobs(metav1.NamespaceDefault).Create(job) + + joinErrors := func(errors []error) error { + if len(errors) == 0 { + return nil + } + errorStrings := []string{} + for _, err := range errors { + errorStrings = append(errorStrings, err.Error()) + } + return fmt.Errorf("%s", strings.Join(errorStrings, "\n")) + } + + // check local jobs are created with correct spec + checkLocalJob := func(parallelism, completions int32) testutil.CheckingFunction { + return func(obj runtime.Object) error { + errors := []error{} + ljob := obj.(*batchv1.Job) + if !fedutil.ObjectMetaEquivalent(job.ObjectMeta, ljob.ObjectMeta) { + errors = append(errors, fmt.Errorf("Job meta un-equivalent: %#v (expected) != %#v (actual)", job.ObjectMeta, ljob.ObjectMeta)) + } + if err := checkEqual(t, *ljob.Spec.Parallelism, parallelism, "Spec.Parallelism"); err != nil { + errors = append(errors, err) + } + if ljob.Spec.Completions != nil { + if err := checkEqual(t, *ljob.Spec.Completions, completions, "Spec.Completions"); err != nil { + errors = append(errors, err) + } + } + return joinErrors(errors) + } + } + checkFedJob := func(obj runtime.Object) error { + errors := []error{} + return joinErrors(errors) + } + assert.NoError(t, testutil.CheckObjectFromChan(kube1Chan, checkLocalJob(parallelism1, completions1))) + assert.NoError(t, testutil.CheckObjectFromChan(kube2Chan, checkLocalJob(parallelism2, completions2))) + assert.NoError(t, testutil.CheckObjectFromChan(fedChan, checkFedJob)) + + // finish local jobs + job1, _ := kube1clientset.Batch().Jobs(metav1.NamespaceDefault).Get(job.Name, metav1.GetOptions{}) + finishJob(job1, 100*time.Millisecond) + job1, _ = kube1clientset.Batch().Jobs(metav1.NamespaceDefault).UpdateStatus(job1) + job2, _ := kube2clientset.Batch().Jobs(metav1.NamespaceDefault).Get(job.Name, metav1.GetOptions{}) + finishJob(job2, 100*time.Millisecond) + job2, _ = kube2clientset.Batch().Jobs(metav1.NamespaceDefault).UpdateStatus(job2) + + // check fed job status updated + assert.NoError(t, testutil.CheckObjectFromChan(fedChan, func(obj runtime.Object) error { + errors := []error{} + job := obj.(*batchv1.Job) + if err := checkEqual(t, *job.Spec.Parallelism, *job1.Spec.Parallelism+*job2.Spec.Parallelism, "Spec.Parallelism"); err != nil { + errors = append(errors, err) + } + if job.Spec.Completions != nil { + if err := checkEqual(t, *job.Spec.Completions, *job1.Spec.Completions+*job2.Spec.Completions, "Spec.Completions"); err != nil { + errors = append(errors, err) + } + } + if err := checkEqual(t, job.Status.Succeeded, job1.Status.Succeeded+job2.Status.Succeeded, "Status.Succeeded"); err != nil { + errors = append(errors, err) + } + return joinErrors(errors) + })) + + // delete fed job by set deletion time, and remove orphan finalizer + job, _ = fedclientset.Batch().Jobs(metav1.NamespaceDefault).Get(job.Name, metav1.GetOptions{}) + deletionTimestamp := metav1.Now() + job.DeletionTimestamp = &deletionTimestamp + finalizersutil.RemoveFinalizers(job, sets.NewString(metav1.FinalizerOrphanDependents)) + fedclientset.Batch().Jobs(metav1.NamespaceDefault).Update(job) + + // check jobs are deleted + checkDeleted := func(obj runtime.Object) error { + djob := obj.(*batchv1.Job) + deletedJob := &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: djob.Name, + Namespace: djob.Namespace, + }, + } + if !reflect.DeepEqual(djob, deletedJob) { + return fmt.Errorf("%s/%s should be deleted", djob.Namespace, djob.Name) + } + return nil + } + assert.NoError(t, testutil.CheckObjectFromChan(kube1Chan, checkDeleted)) + assert.NoError(t, testutil.CheckObjectFromChan(kube2Chan, checkDeleted)) + assert.NoError(t, testutil.CheckObjectFromChan(fedChan, checkDeleted)) + } + + test(newJob("job1", 2, 7), 1, 1, 4, 3) + test(newJob("job2", 2, -1), 1, 1, -1, -1) + test(newJob("job3", 7, 2), 4, 3, 1, 1) + test(newJob("job4", 7, 1), 4, 3, 1, 0) +} + +func checkEqual(_ *testing.T, expected, actual interface{}, msg string) error { + if !assert.ObjectsAreEqual(expected, actual) { + return fmt.Errorf("%s not equal: %#v (expected) != %#v (actual)", msg, expected, actual) + } + return nil +} + +func newJob(name string, parallelism int32, completions int32) *batchv1.Job { + job := batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: metav1.NamespaceDefault, + SelfLink: "/api/v1/namespaces/default/jobs/name", + }, + Spec: batchv1.JobSpec{ + Parallelism: ¶llelism, + Completions: &completions, + Template: apiv1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "foo": name, + }, + }, + Spec: apiv1.PodSpec{ + Containers: []apiv1.Container{ + {Image: "foo/bar"}, + }, + RestartPolicy: apiv1.RestartPolicyNever, + }, + }, + }, + } + if parallelism < 0 { + job.Spec.Parallelism = nil + } + if completions < 0 { + job.Spec.Completions = nil + } + + batchv1internal.SetDefaults_Job(&job) + return &job +} + +func newCondition(conditionType batchv1.JobConditionType, reason, message string) batchv1.JobCondition { + return batchv1.JobCondition{ + Type: conditionType, + Status: apiv1.ConditionTrue, + LastProbeTime: metav1.Now(), + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + } +} + +func finishJob(job *batchv1.Job, duration time.Duration) { + job.Status.Conditions = append(job.Status.Conditions, newCondition(batchv1.JobComplete, "", "")) + if job.Spec.Completions == nil { + job.Status.Succeeded = 1 + } else { + job.Status.Succeeded = *job.Spec.Completions + } + now := metav1.Now() + job.Status.StartTime = &now + time.Sleep(duration) + now = metav1.Now() + job.Status.CompletionTime = &now +} diff --git a/federation/pkg/federation-controller/util/handlers.go b/federation/pkg/federation-controller/util/handlers.go index 0e2dec5bf58..406d5ca3161 100644 --- a/federation/pkg/federation-controller/util/handlers.go +++ b/federation/pkg/federation-controller/util/handlers.go @@ -71,6 +71,7 @@ func NewTriggerOnMetaAndSpecChanges(triggerFunc func(pkgruntime.Object)) *cache. oldMeta := getFieldOrPanic(old, "ObjectMeta").(metav1.ObjectMeta) curMeta := getFieldOrPanic(cur, "ObjectMeta").(metav1.ObjectMeta) if !ObjectMetaEquivalent(oldMeta, curMeta) || + !reflect.DeepEqual(oldMeta.DeletionTimestamp, curMeta.DeletionTimestamp) || !reflect.DeepEqual(getFieldOrPanic(old, "Spec"), getFieldOrPanic(cur, "Spec")) { triggerFunc(curObj) } diff --git a/hack/verify-flags/known-flags.txt b/hack/verify-flags/known-flags.txt index a3d243b78bd..453b36fb402 100644 --- a/hack/verify-flags/known-flags.txt +++ b/hack/verify-flags/known-flags.txt @@ -119,6 +119,7 @@ cni-conf-dir concurrent-deployment-syncs concurrent-endpoint-syncs concurrent-gc-syncs +concurrent-job-syncs concurrent-namespace-syncs concurrent-replicaset-syncs concurrent-resource-quota-syncs From 746444e43a8a1965f6907ee29f65804e36187505 Mon Sep 17 00:00:00 2001 From: jianhuiz Date: Thu, 3 Aug 2017 11:02:06 -0700 Subject: [PATCH 32/38] add fed job e2e test --- test/e2e_federation/BUILD | 3 + test/e2e_federation/job.go | 291 +++++++++++++++++++++++++++++++++++++ 2 files changed, 294 insertions(+) create mode 100644 test/e2e_federation/job.go diff --git a/test/e2e_federation/BUILD b/test/e2e_federation/BUILD index 274a930df30..181a626ba59 100644 --- a/test/e2e_federation/BUILD +++ b/test/e2e_federation/BUILD @@ -15,6 +15,7 @@ go_library( "crud.go", "event.go", "ingress.go", + "job.go", "namespace.go", "replicaset.go", "service.go", @@ -29,6 +30,7 @@ go_library( "//federation/client/clientset_generated/federation_clientset/typed/core/v1:go_default_library", "//federation/pkg/federatedtypes:go_default_library", "//federation/pkg/federation-controller/util:go_default_library", + "//pkg/api:go_default_library", "//pkg/api/v1:go_default_library", "//pkg/cloudprovider:go_default_library", "//test/e2e/chaosmonkey:go_default_library", @@ -38,6 +40,7 @@ go_library( "//test/e2e_federation/upgrades:go_default_library", "//vendor/github.com/onsi/ginkgo:go_default_library", "//vendor/github.com/onsi/gomega:go_default_library", + "//vendor/k8s.io/api/batch/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", diff --git a/test/e2e_federation/job.go b/test/e2e_federation/job.go new file mode 100644 index 00000000000..71ad152ba4b --- /dev/null +++ b/test/e2e_federation/job.go @@ -0,0 +1,291 @@ +/* +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 e2e_federation + +import ( + "fmt" + "strings" + "time" + + batchv1 "k8s.io/api/batch/v1" + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset" + fedutil "k8s.io/kubernetes/federation/pkg/federation-controller/util" + "k8s.io/kubernetes/test/e2e/framework" + fedframework "k8s.io/kubernetes/test/e2e_federation/framework" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/kubernetes/pkg/api" +) + +const ( + FederationJobName = "federation-job" +) + +var _ = framework.KubeDescribe("Federation jobs [Feature:Federation]", func() { + + f := fedframework.NewDefaultFederatedFramework("federation-job") + + Describe("Job objects [NoCluster]", func() { + AfterEach(func() { + fedframework.SkipUnlessFederated(f.ClientSet) + + // Delete all jobs. + nsName := f.FederationNamespace.Name + deleteAllJobsOrFail(f.FederationClientset, nsName) + }) + + It("should be created and deleted successfully", func() { + fedframework.SkipUnlessFederated(f.ClientSet) + + nsName := f.FederationNamespace.Name + job := createJobOrFail(f.FederationClientset, nsName) + By(fmt.Sprintf("Creation of job %q in namespace %q succeeded. Deleting job.", job.Name, nsName)) + // Cleanup + err := f.FederationClientset.Batch().Jobs(nsName).Delete(job.Name, &metav1.DeleteOptions{}) + framework.ExpectNoError(err, "Error deleting job %q in namespace %q", job.Name, job.Namespace) + By(fmt.Sprintf("Deletion of job %q in namespace %q succeeded.", job.Name, nsName)) + }) + + }) + + // e2e cases for federated job controller + Describe("Federated Job", func() { + var ( + clusters fedframework.ClusterSlice + ) + BeforeEach(func() { + fedframework.SkipUnlessFederated(f.ClientSet) + clusters = f.GetRegisteredClusters() + }) + + AfterEach(func() { + nsName := f.FederationNamespace.Name + deleteAllJobsOrFail(f.FederationClientset, nsName) + }) + + It("should create and update matching jobs in underlying clusters", func() { + nsName := f.FederationNamespace.Name + job := createJobOrFail(f.FederationClientset, nsName) + defer func() { + // cleanup. deletion of jobs is not supported for underlying clusters + By(fmt.Sprintf("Deleting job %q/%q", nsName, job.Name)) + waitForJobOrFail(f.FederationClientset, nsName, job.Name, clusters) + f.FederationClientset.Batch().Jobs(nsName).Delete(job.Name, &metav1.DeleteOptions{}) + }() + + waitForJobOrFail(f.FederationClientset, nsName, job.Name, clusters) + By(fmt.Sprintf("Successfuly created and synced job %q/%q to clusters", nsName, job.Name)) + }) + + It("should be deleted from underlying clusters when OrphanDependents is false", func() { + fedframework.SkipUnlessFederated(f.ClientSet) + nsName := f.FederationNamespace.Name + orphanDependents := false + verifyCascadingDeletionForJob(f.FederationClientset, clusters, &orphanDependents, nsName) + By(fmt.Sprintf("Verified that jobs were deleted from underlying clusters")) + }) + + It("should not be deleted from underlying clusters when OrphanDependents is true", func() { + fedframework.SkipUnlessFederated(f.ClientSet) + nsName := f.FederationNamespace.Name + orphanDependents := true + verifyCascadingDeletionForJob(f.FederationClientset, clusters, &orphanDependents, nsName) + By(fmt.Sprintf("Verified that jobs were not deleted from underlying clusters")) + }) + + It("should not be deleted from underlying clusters when OrphanDependents is nil", func() { + fedframework.SkipUnlessFederated(f.ClientSet) + nsName := f.FederationNamespace.Name + verifyCascadingDeletionForJob(f.FederationClientset, clusters, nil, nsName) + By(fmt.Sprintf("Verified that jobs were not deleted from underlying clusters")) + }) + + }) +}) + +// deleteAllJobsOrFail deletes all jobs in the given namespace name. +func deleteAllJobsOrFail(clientset *fedclientset.Clientset, nsName string) { + jobList, err := clientset.Batch().Jobs(nsName).List(metav1.ListOptions{}) + Expect(err).NotTo(HaveOccurred()) + orphanDependents := false + for _, job := range jobList.Items { + deleteJobOrFail(clientset, nsName, job.Name, &orphanDependents) + } +} + +// verifyCascadingDeletionForJob verifies that job are deleted +// from underlying clusters when orphan dependents is false and they are not +// deleted when orphan dependents is true. +func verifyCascadingDeletionForJob(clientset *fedclientset.Clientset, clusters fedframework.ClusterSlice, orphanDependents *bool, nsName string) { + job := createJobOrFail(clientset, nsName) + jobName := job.Name + // Check subclusters if the job was created there. + By(fmt.Sprintf("Waiting for job %s to be created in all underlying clusters", jobName)) + err := wait.Poll(5*time.Second, 2*time.Minute, func() (bool, error) { + for _, cluster := range clusters { + _, err := cluster.Batch().Jobs(nsName).Get(jobName, metav1.GetOptions{}) + if err != nil && errors.IsNotFound(err) { + return false, nil + } + if err != nil { + return false, err + } + } + return true, nil + }) + framework.ExpectNoError(err, "Not all jobs created") + + By(fmt.Sprintf("Deleting job %s", jobName)) + deleteJobOrFail(clientset, nsName, jobName, orphanDependents) + + By(fmt.Sprintf("Verifying job %s in underlying clusters", jobName)) + errMessages := []string{} + // job should be present in underlying clusters unless orphanDependents is false. + shouldExist := orphanDependents == nil || *orphanDependents == true + for _, cluster := range clusters { + clusterName := cluster.Name + _, err := cluster.Batch().Jobs(nsName).Get(jobName, metav1.GetOptions{}) + if shouldExist && errors.IsNotFound(err) { + errMessages = append(errMessages, fmt.Sprintf("unexpected NotFound error for job %s in cluster %s, expected job to exist", jobName, clusterName)) + } else if !shouldExist && !errors.IsNotFound(err) { + errMessages = append(errMessages, fmt.Sprintf("expected NotFound error for job %s in cluster %s, got error: %v", jobName, clusterName, err)) + } + } + if len(errMessages) != 0 { + framework.Failf("%s", strings.Join(errMessages, "; ")) + } +} + +func waitForJobOrFail(c *fedclientset.Clientset, namespace string, jobName string, clusters fedframework.ClusterSlice) { + err := waitForJob(c, namespace, jobName, clusters) + framework.ExpectNoError(err, "Failed to verify job %q/%q, err: %v", namespace, jobName, err) +} + +func waitForJob(c *fedclientset.Clientset, namespace string, jobName string, clusters fedframework.ClusterSlice) error { + err := wait.Poll(10*time.Second, fedframework.FederatedDefaultTestTimeout, func() (bool, error) { + fjob, err := c.Batch().Jobs(namespace).Get(jobName, metav1.GetOptions{}) + if err != nil { + return false, err + } + succeeded := int32(0) + for _, cluster := range clusters { + job, err := cluster.Batch().Jobs(namespace).Get(jobName, metav1.GetOptions{}) + if err != nil && !errors.IsNotFound(err) { + By(fmt.Sprintf("Failed getting job: %q/%q/%q, err: %v", cluster.Name, namespace, jobName, err)) + return false, err + } + if err == nil { + if !verifyJob(fjob, job) { + By(fmt.Sprintf("Job meta or spec not match for cluster %q:\n federation: %v\n cluster: %v", cluster.Name, fjob, job)) + return false, nil + } + succeeded += job.Status.Succeeded + } + } + if succeeded == fjob.Status.Succeeded && + (fjob.Spec.Completions != nil && succeeded == *fjob.Spec.Completions) { + return true, nil + } + By(fmt.Sprintf("Job statuses not match, federation succeeded: %v/%v, clusters succeeded: %v\n", + fjob.Status.Succeeded, func(p *int32) int32 { + if p != nil { + return *p + } else { + return -1 + } + }(fjob.Spec.Completions), succeeded)) + return false, nil + }) + + return err +} + +func verifyJob(fedJob, localJob *batchv1.Job) bool { + localJobObj, _ := api.Scheme.DeepCopy(localJob) + localJob = localJobObj.(*batchv1.Job) + localJob.Spec.ManualSelector = fedJob.Spec.ManualSelector + localJob.Spec.Completions = fedJob.Spec.Completions + localJob.Spec.Parallelism = fedJob.Spec.Parallelism + return fedutil.ObjectMetaAndSpecEquivalent(fedJob, localJob) +} + +func createJobOrFail(clientset *fedclientset.Clientset, namespace string) *batchv1.Job { + if clientset == nil || len(namespace) == 0 { + Fail(fmt.Sprintf("Internal error: invalid parameters passed to createJobOrFail: clientset: %v, namespace: %v", clientset, namespace)) + } + By(fmt.Sprintf("Creating federation job %q in namespace %q", FederationJobName, namespace)) + + job := newJobForFed(namespace, FederationJobName, 5, 5) + + _, err := clientset.Batch().Jobs(namespace).Create(job) + framework.ExpectNoError(err, "Creating job %q in namespace %q", job.Name, namespace) + By(fmt.Sprintf("Successfully created federation job %q in namespace %q", FederationJobName, namespace)) + return job +} + +func deleteJobOrFail(clientset *fedclientset.Clientset, nsName string, jobName string, orphanDependents *bool) { + By(fmt.Sprintf("Deleting job %q in namespace %q", jobName, nsName)) + err := clientset.Batch().Jobs(nsName).Delete(jobName, &metav1.DeleteOptions{OrphanDependents: orphanDependents}) + if err != nil && !errors.IsNotFound(err) { + framework.ExpectNoError(err, "Error deleting job %q in namespace %q", jobName, nsName) + } + + // Wait for the job to be deleted. + err = wait.Poll(10*time.Second, fedframework.FederatedDefaultTestTimeout, func() (bool, error) { + _, err := clientset.Batch().Jobs(nsName).Get(jobName, metav1.GetOptions{}) + if err != nil && errors.IsNotFound(err) { + return true, nil + } + return false, err + }) + if err != nil { + framework.Failf("Error in deleting job %s: %v", jobName, err) + } +} + +func newJobForFed(namespace string, name string, completions int32, parallelism int32) *batchv1.Job { + return &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: batchv1.JobSpec{ + Parallelism: ¶llelism, + Completions: &completions, + Template: v1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"name": "fjob"}, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "sleep", + Image: "gcr.io/google_containers/busybox:1.24", + Command: []string{"sleep", "1"}, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + }, + }, + }, + } +} From 91f100b50178e1f46b46df21ea11a2f60e5d04aa Mon Sep 17 00:00:00 2001 From: Jun Xiang Tee Date: Thu, 20 Jul 2017 12:49:13 -0700 Subject: [PATCH 33/38] implement statefulset scale subresource --- api/openapi-spec/swagger.json | 308 ++++ api/swagger-spec/apps_v1beta1.json | 165 ++ api/swagger-spec/apps_v1beta2.json | 165 ++ .../apps/v1beta1/operations.html | 1429 ++++++++++------ .../apps/v1beta2/operations.html | 1453 +++++++++++------ pkg/registry/apps/rest/storage_apps.go | 14 +- pkg/registry/apps/statefulset/BUILD | 5 + pkg/registry/apps/statefulset/registry.go | 95 ++ pkg/registry/apps/statefulset/storage/BUILD | 7 + .../apps/statefulset/storage/storage.go | 122 +- .../apps/statefulset/storage/storage_test.go | 151 +- test/e2e/apps/statefulset.go | 37 + test/e2e/framework/BUILD | 1 + test/e2e/framework/statefulset_utils.go | 20 +- 14 files changed, 2870 insertions(+), 1102 deletions(-) create mode 100644 pkg/registry/apps/statefulset/registry.go diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 02b632f57de..9684eb25e16 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -21339,6 +21339,160 @@ } ] }, + "/apis/apps/v1beta1/namespaces/{namespace}/statefulsets/{name}/scale": { + "get": { + "description": "read scale of the specified StatefulSet", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "apps_v1beta1" + ], + "operationId": "readAppsV1beta1NamespacedStatefulSetScale", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.Scale" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "apps", + "kind": "Scale", + "version": "v1beta1" + } + }, + "put": { + "description": "replace scale of the specified StatefulSet", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "apps_v1beta1" + ], + "operationId": "replaceAppsV1beta1NamespacedStatefulSetScale", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.Scale" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.Scale" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "apps", + "kind": "Scale", + "version": "v1beta1" + } + }, + "patch": { + "description": "partially update scale of the specified StatefulSet", + "consumes": [ + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "apps_v1beta1" + ], + "operationId": "patchAppsV1beta1NamespacedStatefulSetScale", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta1.Scale" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "apps", + "kind": "Scale", + "version": "v1beta1" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "string", + "description": "name of the Scale", + "name": "name", + "in": "path", + "required": true + }, + { + "uniqueItems": true, + "type": "string", + "description": "object name and auth scope, such as for teams and projects", + "name": "namespace", + "in": "path", + "required": true + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + } + ] + }, "/apis/apps/v1beta1/namespaces/{namespace}/statefulsets/{name}/status": { "get": { "description": "read status of the specified StatefulSet", @@ -25360,6 +25514,160 @@ } ] }, + "/apis/apps/v1beta2/namespaces/{namespace}/statefulsets/{name}/scale": { + "get": { + "description": "read scale of the specified StatefulSet", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "apps_v1beta2" + ], + "operationId": "readAppsV1beta2NamespacedStatefulSetScale", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.Scale" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "apps", + "kind": "Scale", + "version": "v1beta2" + } + }, + "put": { + "description": "replace scale of the specified StatefulSet", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "apps_v1beta2" + ], + "operationId": "replaceAppsV1beta2NamespacedStatefulSetScale", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.Scale" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.Scale" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "apps", + "kind": "Scale", + "version": "v1beta2" + } + }, + "patch": { + "description": "partially update scale of the specified StatefulSet", + "consumes": [ + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "apps_v1beta2" + ], + "operationId": "patchAppsV1beta2NamespacedStatefulSetScale", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.api.apps.v1beta2.Scale" + } + }, + "401": { + "description": "Unauthorized" + } + }, + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "apps", + "kind": "Scale", + "version": "v1beta2" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "string", + "description": "name of the Scale", + "name": "name", + "in": "path", + "required": true + }, + { + "uniqueItems": true, + "type": "string", + "description": "object name and auth scope, such as for teams and projects", + "name": "namespace", + "in": "path", + "required": true + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + } + ] + }, "/apis/apps/v1beta2/namespaces/{namespace}/statefulsets/{name}/status": { "get": { "description": "read status of the specified StatefulSet", diff --git a/api/swagger-spec/apps_v1beta1.json b/api/swagger-spec/apps_v1beta1.json index 5524e1f712d..2f384bd93c8 100644 --- a/api/swagger-spec/apps_v1beta1.json +++ b/api/swagger-spec/apps_v1beta1.json @@ -2982,6 +2982,171 @@ } ] }, + { + "path": "/apis/apps/v1beta1/namespaces/{namespace}/statefulsets/{name}/scale", + "description": "API at /apis/apps/v1beta1", + "operations": [ + { + "type": "v1beta1.Scale", + "method": "GET", + "summary": "read scale of the specified StatefulSet", + "nickname": "readNamespacedStatefulSetScale", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Scale", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1beta1.Scale" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "v1beta1.Scale", + "method": "PUT", + "summary": "replace scale of the specified StatefulSet", + "nickname": "replaceNamespacedStatefulSetScale", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta1.Scale", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Scale", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1beta1.Scale" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "v1beta1.Scale", + "method": "PATCH", + "summary": "partially update scale of the specified StatefulSet", + "nickname": "patchNamespacedStatefulSetScale", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "v1.Patch", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Scale", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1beta1.Scale" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "consumes": [ + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json" + ] + } + ] + }, { "path": "/apis/apps/v1beta1/namespaces/{namespace}/statefulsets/{name}/status", "description": "API at /apis/apps/v1beta1", diff --git a/api/swagger-spec/apps_v1beta2.json b/api/swagger-spec/apps_v1beta2.json index dec3f47f914..0dd7d9ae7e4 100644 --- a/api/swagger-spec/apps_v1beta2.json +++ b/api/swagger-spec/apps_v1beta2.json @@ -4338,6 +4338,171 @@ } ] }, + { + "path": "/apis/apps/v1beta2/namespaces/{namespace}/statefulsets/{name}/scale", + "description": "API at /apis/apps/v1beta2", + "operations": [ + { + "type": "v1beta2.Scale", + "method": "GET", + "summary": "read scale of the specified StatefulSet", + "nickname": "readNamespacedStatefulSetScale", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Scale", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1beta2.Scale" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "v1beta2.Scale", + "method": "PUT", + "summary": "replace scale of the specified StatefulSet", + "nickname": "replaceNamespacedStatefulSetScale", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "v1beta2.Scale", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Scale", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1beta2.Scale" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "consumes": [ + "*/*" + ] + }, + { + "type": "v1beta2.Scale", + "method": "PATCH", + "summary": "partially update scale of the specified StatefulSet", + "nickname": "patchNamespacedStatefulSetScale", + "parameters": [ + { + "type": "string", + "paramType": "query", + "name": "pretty", + "description": "If 'true', then the output is pretty printed.", + "required": false, + "allowMultiple": false + }, + { + "type": "v1.Patch", + "paramType": "body", + "name": "body", + "description": "", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "namespace", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "allowMultiple": false + }, + { + "type": "string", + "paramType": "path", + "name": "name", + "description": "name of the Scale", + "required": true, + "allowMultiple": false + } + ], + "responseMessages": [ + { + "code": 200, + "message": "OK", + "responseModel": "v1beta2.Scale" + } + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "consumes": [ + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json" + ] + } + ] + }, { "path": "/apis/apps/v1beta2/namespaces/{namespace}/statefulsets/{name}/status", "description": "API at /apis/apps/v1beta2", diff --git a/docs/api-reference/apps/v1beta1/operations.html b/docs/api-reference/apps/v1beta1/operations.html index 42ce7aeb673..fee327b7d07 100755 --- a/docs/api-reference/apps/v1beta1/operations.html +++ b/docs/api-reference/apps/v1beta1/operations.html @@ -4609,6 +4609,385 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
+

read scale of the specified StatefulSet

+
+
+
GET /apis/apps/v1beta1/namespaces/{namespace}/statefulsets/{name}/scale
+
+
+
+

Parameters

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

PathParameter

namespace

object name and auth scope, such as for teams and projects

true

string

PathParameter

name

name of the Scale

true

string

+ +
+
+

Responses

+ +++++ + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

success

v1beta1.Scale

+ +
+
+

Consumes

+
+
    +
  • +

    /

    +
  • +
+
+
+
+

Produces

+
+
    +
  • +

    application/json

    +
  • +
  • +

    application/yaml

    +
  • +
  • +

    application/vnd.kubernetes.protobuf

    +
  • +
+
+
+
+

Tags

+
+
    +
  • +

    apisappsv1beta1

    +
  • +
+
+
+
+
+

replace scale of the specified StatefulSet

+
+
+
PUT /apis/apps/v1beta1/namespaces/{namespace}/statefulsets/{name}/scale
+
+
+
+

Parameters

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

BodyParameter

body

true

v1beta1.Scale

PathParameter

namespace

object name and auth scope, such as for teams and projects

true

string

PathParameter

name

name of the Scale

true

string

+ +
+
+

Responses

+ +++++ + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

success

v1beta1.Scale

+ +
+
+

Consumes

+
+
    +
  • +

    /

    +
  • +
+
+
+
+

Produces

+
+
    +
  • +

    application/json

    +
  • +
  • +

    application/yaml

    +
  • +
  • +

    application/vnd.kubernetes.protobuf

    +
  • +
+
+
+
+

Tags

+
+
    +
  • +

    apisappsv1beta1

    +
  • +
+
+
+
+
+

partially update scale of the specified StatefulSet

+
+
+
PATCH /apis/apps/v1beta1/namespaces/{namespace}/statefulsets/{name}/scale
+
+
+
+

Parameters

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

BodyParameter

body

true

v1.Patch

PathParameter

namespace

object name and auth scope, such as for teams and projects

true

string

PathParameter

name

name of the Scale

true

string

+ +
+
+

Responses

+ +++++ + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

success

v1beta1.Scale

+ +
+
+

Consumes

+
+
    +
  • +

    application/json-patch+json

    +
  • +
  • +

    application/merge-patch+json

    +
  • +
  • +

    application/strategic-merge-patch+json

    +
  • +
+
+
+
+

Produces

+
+
    +
  • +

    application/json

    +
  • +
  • +

    application/yaml

    +
  • +
  • +

    application/vnd.kubernetes.protobuf

    +
  • +
+
+
+
+

Tags

+
+
    +
  • +

    apisappsv1beta1

    +
  • +
+
+
+
+

read status of the specified StatefulSet

@@ -4616,7 +4995,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -4666,7 +5045,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -4691,7 +5070,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -4701,7 +5080,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -4717,7 +5096,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -4735,7 +5114,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -4793,7 +5172,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -4818,7 +5197,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -4828,7 +5207,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -4844,7 +5223,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -4862,7 +5241,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -4920,7 +5299,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -4945,7 +5324,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -4961,471 +5340,6 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

-
-
    -
  • -

    application/json

    -
  • -
  • -

    application/yaml

    -
  • -
  • -

    application/vnd.kubernetes.protobuf

    -
  • -
-
-
-
-

Tags

-
-
    -
  • -

    apisappsv1beta1

    -
  • -
-
-
- -
-

list or watch objects of kind StatefulSet

-
-
-
GET /apis/apps/v1beta1/statefulsets
-
-
-
-

Parameters

-
-------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

QueryParameter

labelSelector

A selector to restrict the list of returned objects by their labels. Defaults to everything.

false

string

QueryParameter

fieldSelector

A selector to restrict the list of returned objects by their fields. Defaults to everything.

false

string

QueryParameter

includeUninitialized

If true, partially initialized resources are included in the response.

false

boolean

QueryParameter

watch

Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.

false

boolean

QueryParameter

resourceVersion

When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it’s 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.

false

string

QueryParameter

timeoutSeconds

Timeout for the list/watch call.

false

integer (int32)

- -
-
-

Responses

- ----- - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

success

v1beta1.StatefulSetList

- -
-
-

Consumes

-
-
    -
  • -

    /

    -
  • -
-
-
-
-

Produces

-
-
    -
  • -

    application/json

    -
  • -
  • -

    application/yaml

    -
  • -
  • -

    application/vnd.kubernetes.protobuf

    -
  • -
  • -

    application/json;stream=watch

    -
  • -
  • -

    application/vnd.kubernetes.protobuf;stream=watch

    -
  • -
-
-
-
-

Tags

-
-
    -
  • -

    apisappsv1beta1

    -
  • -
-
-
-
-
-

watch individual changes to a list of ControllerRevision

-
-
-
GET /apis/apps/v1beta1/watch/controllerrevisions
-
-
-
-

Parameters

- -------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

QueryParameter

labelSelector

A selector to restrict the list of returned objects by their labels. Defaults to everything.

false

string

QueryParameter

fieldSelector

A selector to restrict the list of returned objects by their fields. Defaults to everything.

false

string

QueryParameter

includeUninitialized

If true, partially initialized resources are included in the response.

false

boolean

QueryParameter

watch

Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.

false

boolean

QueryParameter

resourceVersion

When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it’s 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.

false

string

QueryParameter

timeoutSeconds

Timeout for the list/watch call.

false

integer (int32)

- -
-
-

Responses

- ----- - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

success

v1.WatchEvent

- -
-
-

Consumes

-
-
    -
  • -

    /

    -
  • -
-
-
-
-

Produces

-
-
    -
  • -

    application/json

    -
  • -
  • -

    application/yaml

    -
  • -
  • -

    application/vnd.kubernetes.protobuf

    -
  • -
  • -

    application/json;stream=watch

    -
  • -
  • -

    application/vnd.kubernetes.protobuf;stream=watch

    -
  • -
-
-
-
-

Tags

-
-
    -
  • -

    apisappsv1beta1

    -
  • -
-
-
-
-
-

watch individual changes to a list of Deployment

-
-
-
GET /apis/apps/v1beta1/watch/deployments
-
-
-
-

Parameters

- -------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

QueryParameter

labelSelector

A selector to restrict the list of returned objects by their labels. Defaults to everything.

false

string

QueryParameter

fieldSelector

A selector to restrict the list of returned objects by their fields. Defaults to everything.

false

string

QueryParameter

includeUninitialized

If true, partially initialized resources are included in the response.

false

boolean

QueryParameter

watch

Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.

false

boolean

QueryParameter

resourceVersion

When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it’s 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.

false

string

QueryParameter

timeoutSeconds

Timeout for the list/watch call.

false

integer (int32)

- -
-
-

Responses

- ----- - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

success

v1.WatchEvent

- -
-
-

Consumes

-
-
    -
  • -

    /

    -
  • -
-
-
-

Produces

    @@ -5438,12 +5352,6 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/vnd.kubernetes.protobuf

  • -
  • -

    application/json;stream=watch

    -
  • -
  • -

    application/vnd.kubernetes.protobuf;stream=watch

    -
@@ -5459,10 +5367,10 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

watch individual changes to a list of ControllerRevision

+

list or watch objects of kind StatefulSet

-
GET /apis/apps/v1beta1/watch/namespaces/{namespace}/controllerrevisions
+
GET /apis/apps/v1beta1/statefulsets
@@ -5543,14 +5451,6 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }

integer (int32)

- -

PathParameter

-

namespace

-

object name and auth scope, such as for teams and projects

-

true

-

string

- - @@ -5574,7 +5474,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }

200

success

-

v1.WatchEvent

+

v1beta1.StatefulSetList

@@ -5624,6 +5524,485 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
+

watch individual changes to a list of ControllerRevision

+
+
+
GET /apis/apps/v1beta1/watch/controllerrevisions
+
+
+
+

Parameters

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

QueryParameter

labelSelector

A selector to restrict the list of returned objects by their labels. Defaults to everything.

false

string

QueryParameter

fieldSelector

A selector to restrict the list of returned objects by their fields. Defaults to everything.

false

string

QueryParameter

includeUninitialized

If true, partially initialized resources are included in the response.

false

boolean

QueryParameter

watch

Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.

false

boolean

QueryParameter

resourceVersion

When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it’s 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.

false

string

QueryParameter

timeoutSeconds

Timeout for the list/watch call.

false

integer (int32)

+ +
+
+

Responses

+ +++++ + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

success

v1.WatchEvent

+ +
+
+

Consumes

+
+
    +
  • +

    /

    +
  • +
+
+
+
+

Produces

+
+
    +
  • +

    application/json

    +
  • +
  • +

    application/yaml

    +
  • +
  • +

    application/vnd.kubernetes.protobuf

    +
  • +
  • +

    application/json;stream=watch

    +
  • +
  • +

    application/vnd.kubernetes.protobuf;stream=watch

    +
  • +
+
+
+
+

Tags

+
+
    +
  • +

    apisappsv1beta1

    +
  • +
+
+
+
+
+

watch individual changes to a list of Deployment

+
+
+
GET /apis/apps/v1beta1/watch/deployments
+
+
+
+

Parameters

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

QueryParameter

labelSelector

A selector to restrict the list of returned objects by their labels. Defaults to everything.

false

string

QueryParameter

fieldSelector

A selector to restrict the list of returned objects by their fields. Defaults to everything.

false

string

QueryParameter

includeUninitialized

If true, partially initialized resources are included in the response.

false

boolean

QueryParameter

watch

Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.

false

boolean

QueryParameter

resourceVersion

When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it’s 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.

false

string

QueryParameter

timeoutSeconds

Timeout for the list/watch call.

false

integer (int32)

+ +
+
+

Responses

+ +++++ + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

success

v1.WatchEvent

+ +
+
+

Consumes

+
+
    +
  • +

    /

    +
  • +
+
+
+
+

Produces

+
+
    +
  • +

    application/json

    +
  • +
  • +

    application/yaml

    +
  • +
  • +

    application/vnd.kubernetes.protobuf

    +
  • +
  • +

    application/json;stream=watch

    +
  • +
  • +

    application/vnd.kubernetes.protobuf;stream=watch

    +
  • +
+
+
+
+

Tags

+
+
    +
  • +

    apisappsv1beta1

    +
  • +
+
+
+
+
+

watch individual changes to a list of ControllerRevision

+
+
+
GET /apis/apps/v1beta1/watch/namespaces/{namespace}/controllerrevisions
+
+
+
+

Parameters

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

QueryParameter

labelSelector

A selector to restrict the list of returned objects by their labels. Defaults to everything.

false

string

QueryParameter

fieldSelector

A selector to restrict the list of returned objects by their fields. Defaults to everything.

false

string

QueryParameter

includeUninitialized

If true, partially initialized resources are included in the response.

false

boolean

QueryParameter

watch

Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.

false

boolean

QueryParameter

resourceVersion

When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it’s 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.

false

string

QueryParameter

timeoutSeconds

Timeout for the list/watch call.

false

integer (int32)

PathParameter

namespace

object name and auth scope, such as for teams and projects

true

string

+ +
+
+

Responses

+ +++++ + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

success

v1.WatchEvent

+ +
+
+

Consumes

+
+
    +
  • +

    /

    +
  • +
+
+
+
+

Produces

+
+
    +
  • +

    application/json

    +
  • +
  • +

    application/yaml

    +
  • +
  • +

    application/vnd.kubernetes.protobuf

    +
  • +
  • +

    application/json;stream=watch

    +
  • +
  • +

    application/vnd.kubernetes.protobuf;stream=watch

    +
  • +
+
+
+
+

Tags

+
+
    +
  • +

    apisappsv1beta1

    +
  • +
+
+
+
+

watch changes to an object of kind ControllerRevision

@@ -5631,7 +6010,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -5729,7 +6108,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -5754,7 +6133,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -5764,7 +6143,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -5786,7 +6165,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -5804,7 +6183,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -5894,7 +6273,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -5919,7 +6298,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -5929,7 +6308,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -5951,7 +6330,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -5969,7 +6348,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -6067,7 +6446,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -6092,7 +6471,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -6102,7 +6481,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -6124,7 +6503,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -6142,7 +6521,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -6232,7 +6611,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -6257,7 +6636,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -6267,7 +6646,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -6289,7 +6668,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -6307,7 +6686,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -6405,7 +6784,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -6430,7 +6809,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -6440,7 +6819,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -6462,7 +6841,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -6480,7 +6859,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -6562,7 +6941,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -6587,7 +6966,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -6597,7 +6976,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -6619,7 +6998,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • diff --git a/docs/api-reference/apps/v1beta2/operations.html b/docs/api-reference/apps/v1beta2/operations.html index ef9f89c69f1..c7f4095ff51 100755 --- a/docs/api-reference/apps/v1beta2/operations.html +++ b/docs/api-reference/apps/v1beta2/operations.html @@ -6735,6 +6735,385 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
+

read scale of the specified StatefulSet

+
+
+
GET /apis/apps/v1beta2/namespaces/{namespace}/statefulsets/{name}/scale
+
+
+
+

Parameters

+
++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

PathParameter

namespace

object name and auth scope, such as for teams and projects

true

string

PathParameter

name

name of the Scale

true

string

+ +
+
+

Responses

+ +++++ + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

success

v1beta2.Scale

+ +
+
+

Consumes

+
+
    +
  • +

    /

    +
  • +
+
+
+
+

Produces

+
+
    +
  • +

    application/json

    +
  • +
  • +

    application/yaml

    +
  • +
  • +

    application/vnd.kubernetes.protobuf

    +
  • +
+
+
+
+

Tags

+
+
    +
  • +

    apisappsv1beta2

    +
  • +
+
+
+
+
+

replace scale of the specified StatefulSet

+
+
+
PUT /apis/apps/v1beta2/namespaces/{namespace}/statefulsets/{name}/scale
+
+
+
+

Parameters

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

BodyParameter

body

true

v1beta2.Scale

PathParameter

namespace

object name and auth scope, such as for teams and projects

true

string

PathParameter

name

name of the Scale

true

string

+ +
+
+

Responses

+ +++++ + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

success

v1beta2.Scale

+ +
+
+

Consumes

+
+
    +
  • +

    /

    +
  • +
+
+
+
+

Produces

+
+
    +
  • +

    application/json

    +
  • +
  • +

    application/yaml

    +
  • +
  • +

    application/vnd.kubernetes.protobuf

    +
  • +
+
+
+
+

Tags

+
+
    +
  • +

    apisappsv1beta2

    +
  • +
+
+
+
+
+

partially update scale of the specified StatefulSet

+
+
+
PATCH /apis/apps/v1beta2/namespaces/{namespace}/statefulsets/{name}/scale
+
+
+
+

Parameters

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

BodyParameter

body

true

v1.Patch

PathParameter

namespace

object name and auth scope, such as for teams and projects

true

string

PathParameter

name

name of the Scale

true

string

+ +
+
+

Responses

+ +++++ + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

success

v1beta2.Scale

+ +
+
+

Consumes

+
+
    +
  • +

    application/json-patch+json

    +
  • +
  • +

    application/merge-patch+json

    +
  • +
  • +

    application/strategic-merge-patch+json

    +
  • +
+
+
+
+

Produces

+
+
    +
  • +

    application/json

    +
  • +
  • +

    application/yaml

    +
  • +
  • +

    application/vnd.kubernetes.protobuf

    +
  • +
+
+
+
+

Tags

+
+
    +
  • +

    apisappsv1beta2

    +
  • +
+
+
+
+

read status of the specified StatefulSet

@@ -6742,7 +7121,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -6792,7 +7171,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -6817,7 +7196,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -6827,7 +7206,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -6843,7 +7222,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -6861,7 +7240,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -6919,7 +7298,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -6944,7 +7323,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -6954,7 +7333,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -6970,7 +7349,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -6988,7 +7367,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -7046,7 +7425,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -7071,7 +7450,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -7087,471 +7466,6 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

-
-
    -
  • -

    application/json

    -
  • -
  • -

    application/yaml

    -
  • -
  • -

    application/vnd.kubernetes.protobuf

    -
  • -
-
-
-
-

Tags

-
-
    -
  • -

    apisappsv1beta2

    -
  • -
-
-
- -
-

list or watch objects of kind ReplicaSet

-
-
-
GET /apis/apps/v1beta2/replicasets
-
-
-
-

Parameters

-
-------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

QueryParameter

labelSelector

A selector to restrict the list of returned objects by their labels. Defaults to everything.

false

string

QueryParameter

fieldSelector

A selector to restrict the list of returned objects by their fields. Defaults to everything.

false

string

QueryParameter

includeUninitialized

If true, partially initialized resources are included in the response.

false

boolean

QueryParameter

watch

Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.

false

boolean

QueryParameter

resourceVersion

When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it’s 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.

false

string

QueryParameter

timeoutSeconds

Timeout for the list/watch call.

false

integer (int32)

- -
-
-

Responses

- ----- - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

success

v1beta2.ReplicaSetList

- -
-
-

Consumes

-
-
    -
  • -

    /

    -
  • -
-
-
-
-

Produces

-
-
    -
  • -

    application/json

    -
  • -
  • -

    application/yaml

    -
  • -
  • -

    application/vnd.kubernetes.protobuf

    -
  • -
  • -

    application/json;stream=watch

    -
  • -
  • -

    application/vnd.kubernetes.protobuf;stream=watch

    -
  • -
-
-
-
-

Tags

-
-
    -
  • -

    apisappsv1beta2

    -
  • -
-
-
-
-
-

list or watch objects of kind StatefulSet

-
-
-
GET /apis/apps/v1beta2/statefulsets
-
-
-
-

Parameters

- -------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

QueryParameter

labelSelector

A selector to restrict the list of returned objects by their labels. Defaults to everything.

false

string

QueryParameter

fieldSelector

A selector to restrict the list of returned objects by their fields. Defaults to everything.

false

string

QueryParameter

includeUninitialized

If true, partially initialized resources are included in the response.

false

boolean

QueryParameter

watch

Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.

false

boolean

QueryParameter

resourceVersion

When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it’s 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.

false

string

QueryParameter

timeoutSeconds

Timeout for the list/watch call.

false

integer (int32)

- -
-
-

Responses

- ----- - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

success

v1beta2.StatefulSetList

- -
-
-

Consumes

-
-
    -
  • -

    /

    -
  • -
-
-
-
-

Produces

-
-
    -
  • -

    application/json

    -
  • -
  • -

    application/yaml

    -
  • -
  • -

    application/vnd.kubernetes.protobuf

    -
  • -
  • -

    application/json;stream=watch

    -
  • -
  • -

    application/vnd.kubernetes.protobuf;stream=watch

    -
  • -
-
-
-
-

Tags

-
-
    -
  • -

    apisappsv1beta2

    -
  • -
-
-
-
-
-

watch individual changes to a list of DaemonSet

-
-
-
GET /apis/apps/v1beta2/watch/daemonsets
-
-
-
-

Parameters

- -------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

QueryParameter

labelSelector

A selector to restrict the list of returned objects by their labels. Defaults to everything.

false

string

QueryParameter

fieldSelector

A selector to restrict the list of returned objects by their fields. Defaults to everything.

false

string

QueryParameter

includeUninitialized

If true, partially initialized resources are included in the response.

false

boolean

QueryParameter

watch

Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.

false

boolean

QueryParameter

resourceVersion

When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it’s 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.

false

string

QueryParameter

timeoutSeconds

Timeout for the list/watch call.

false

integer (int32)

- -
-
-

Responses

- ----- - - - - - - - - - - - - - - -
HTTP CodeDescriptionSchema

200

success

v1.WatchEvent

- -
-
-

Consumes

-
-
    -
  • -

    /

    -
  • -
-
-
-

Produces

    @@ -7564,12 +7478,6 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/vnd.kubernetes.protobuf

  • -
  • -

    application/json;stream=watch

    -
  • -
  • -

    application/vnd.kubernetes.protobuf;stream=watch

    -
@@ -7585,10 +7493,10 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

watch individual changes to a list of Deployment

+

list or watch objects of kind ReplicaSet

-
GET /apis/apps/v1beta2/watch/deployments
+
GET /apis/apps/v1beta2/replicasets
@@ -7692,7 +7600,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }

200

success

-

v1.WatchEvent

+

v1beta2.ReplicaSetList

@@ -7742,6 +7650,477 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
+

list or watch objects of kind StatefulSet

+
+
+
GET /apis/apps/v1beta2/statefulsets
+
+
+
+

Parameters

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

QueryParameter

labelSelector

A selector to restrict the list of returned objects by their labels. Defaults to everything.

false

string

QueryParameter

fieldSelector

A selector to restrict the list of returned objects by their fields. Defaults to everything.

false

string

QueryParameter

includeUninitialized

If true, partially initialized resources are included in the response.

false

boolean

QueryParameter

watch

Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.

false

boolean

QueryParameter

resourceVersion

When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it’s 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.

false

string

QueryParameter

timeoutSeconds

Timeout for the list/watch call.

false

integer (int32)

+ +
+
+

Responses

+ +++++ + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

success

v1beta2.StatefulSetList

+ +
+
+

Consumes

+
+
    +
  • +

    /

    +
  • +
+
+
+
+

Produces

+
+
    +
  • +

    application/json

    +
  • +
  • +

    application/yaml

    +
  • +
  • +

    application/vnd.kubernetes.protobuf

    +
  • +
  • +

    application/json;stream=watch

    +
  • +
  • +

    application/vnd.kubernetes.protobuf;stream=watch

    +
  • +
+
+
+
+

Tags

+
+
    +
  • +

    apisappsv1beta2

    +
  • +
+
+
+
+
+

watch individual changes to a list of DaemonSet

+
+
+
GET /apis/apps/v1beta2/watch/daemonsets
+
+
+
+

Parameters

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

QueryParameter

labelSelector

A selector to restrict the list of returned objects by their labels. Defaults to everything.

false

string

QueryParameter

fieldSelector

A selector to restrict the list of returned objects by their fields. Defaults to everything.

false

string

QueryParameter

includeUninitialized

If true, partially initialized resources are included in the response.

false

boolean

QueryParameter

watch

Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.

false

boolean

QueryParameter

resourceVersion

When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it’s 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.

false

string

QueryParameter

timeoutSeconds

Timeout for the list/watch call.

false

integer (int32)

+ +
+
+

Responses

+ +++++ + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

success

v1.WatchEvent

+ +
+
+

Consumes

+
+
    +
  • +

    /

    +
  • +
+
+
+
+

Produces

+
+
    +
  • +

    application/json

    +
  • +
  • +

    application/yaml

    +
  • +
  • +

    application/vnd.kubernetes.protobuf

    +
  • +
  • +

    application/json;stream=watch

    +
  • +
  • +

    application/vnd.kubernetes.protobuf;stream=watch

    +
  • +
+
+
+
+

Tags

+
+
    +
  • +

    apisappsv1beta2

    +
  • +
+
+
+
+
+

watch individual changes to a list of Deployment

+
+
+
GET /apis/apps/v1beta2/watch/deployments
+
+
+
+

Parameters

+ ++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescriptionRequiredSchemaDefault

QueryParameter

pretty

If true, then the output is pretty printed.

false

string

QueryParameter

labelSelector

A selector to restrict the list of returned objects by their labels. Defaults to everything.

false

string

QueryParameter

fieldSelector

A selector to restrict the list of returned objects by their fields. Defaults to everything.

false

string

QueryParameter

includeUninitialized

If true, partially initialized resources are included in the response.

false

boolean

QueryParameter

watch

Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.

false

boolean

QueryParameter

resourceVersion

When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it’s 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.

false

string

QueryParameter

timeoutSeconds

Timeout for the list/watch call.

false

integer (int32)

+ +
+
+

Responses

+ +++++ + + + + + + + + + + + + + + +
HTTP CodeDescriptionSchema

200

success

v1.WatchEvent

+ +
+
+

Consumes

+
+
    +
  • +

    /

    +
  • +
+
+
+
+

Produces

+
+
    +
  • +

    application/json

    +
  • +
  • +

    application/yaml

    +
  • +
  • +

    application/vnd.kubernetes.protobuf

    +
  • +
  • +

    application/json;stream=watch

    +
  • +
  • +

    application/vnd.kubernetes.protobuf;stream=watch

    +
  • +
+
+
+
+

Tags

+
+
    +
  • +

    apisappsv1beta2

    +
  • +
+
+
+
+

watch individual changes to a list of DaemonSet

@@ -7749,7 +8128,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -7839,7 +8218,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -7864,7 +8243,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -7874,7 +8253,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -7896,7 +8275,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -7914,7 +8293,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -8012,7 +8391,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -8037,7 +8416,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -8047,7 +8426,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -8069,7 +8448,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -8087,7 +8466,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -8177,7 +8556,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -8202,7 +8581,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -8212,7 +8591,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -8234,7 +8613,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -8252,7 +8631,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -8350,7 +8729,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -8375,7 +8754,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -8385,7 +8764,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -8407,7 +8786,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -8425,7 +8804,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -8515,7 +8894,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -8540,7 +8919,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -8550,7 +8929,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -8572,7 +8951,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -8590,7 +8969,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -8688,7 +9067,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -8713,7 +9092,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -8723,7 +9102,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -8745,7 +9124,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -8763,7 +9142,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -8853,7 +9232,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -8878,7 +9257,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -8888,7 +9267,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -8910,7 +9289,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -8928,7 +9307,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -9026,7 +9405,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -9051,7 +9430,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -9061,7 +9440,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -9083,7 +9462,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -9101,7 +9480,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -9183,7 +9562,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -9208,7 +9587,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -9218,7 +9597,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -9240,7 +9619,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • @@ -9258,7 +9637,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Parameters

+

Parameters

@@ -9340,7 +9719,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Responses

+

Responses

@@ -9365,7 +9744,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Consumes

+

Consumes

  • @@ -9375,7 +9754,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Produces

+

Produces

  • @@ -9397,7 +9776,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
-

Tags

+

Tags

  • diff --git a/pkg/registry/apps/rest/storage_apps.go b/pkg/registry/apps/rest/storage_apps.go index ff82d94c240..545d46405df 100644 --- a/pkg/registry/apps/rest/storage_apps.go +++ b/pkg/registry/apps/rest/storage_apps.go @@ -63,9 +63,10 @@ func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorag storage["deployments/scale"] = deploymentStorage.Scale } if apiResourceConfigSource.ResourceEnabled(version.WithResource("statefulsets")) { - statefulsetStorage, statefulsetStatusStorage := statefulsetstore.NewREST(restOptionsGetter) - storage["statefulsets"] = statefulsetStorage - storage["statefulsets/status"] = statefulsetStatusStorage + statefulSetStorage := statefulsetstore.NewStorage(restOptionsGetter) + storage["statefulsets"] = statefulSetStorage.StatefulSet + storage["statefulsets/status"] = statefulSetStorage.Status + storage["statefulsets/scale"] = statefulSetStorage.Scale } if apiResourceConfigSource.ResourceEnabled(version.WithResource("controllerrevisions")) { historyStorage := controllerrevisionsstore.NewREST(restOptionsGetter) @@ -86,9 +87,10 @@ func (p RESTStorageProvider) v1beta2Storage(apiResourceConfigSource serverstorag storage["deployments/scale"] = deploymentStorage.Scale } if apiResourceConfigSource.ResourceEnabled(version.WithResource("statefulsets")) { - statefulsetStorage, statefulsetStatusStorage := statefulsetstore.NewREST(restOptionsGetter) - storage["statefulsets"] = statefulsetStorage - storage["statefulsets/status"] = statefulsetStatusStorage + statefulSetStorage := statefulsetstore.NewStorage(restOptionsGetter) + storage["statefulsets"] = statefulSetStorage.StatefulSet + storage["statefulsets/status"] = statefulSetStorage.Status + storage["statefulsets/scale"] = statefulSetStorage.Scale } if apiResourceConfigSource.ResourceEnabled(version.WithResource("daemonsets")) { daemonSetStorage, daemonSetStatusStorage := daemonsetstore.NewREST(restOptionsGetter) diff --git a/pkg/registry/apps/statefulset/BUILD b/pkg/registry/apps/statefulset/BUILD index c9d4f03c723..b2e690d623d 100644 --- a/pkg/registry/apps/statefulset/BUILD +++ b/pkg/registry/apps/statefulset/BUILD @@ -12,6 +12,7 @@ go_library( name = "go_default_library", srcs = [ "doc.go", + "registry.go", "strategy.go", ], tags = ["automanaged"], @@ -20,8 +21,12 @@ go_library( "//pkg/apis/apps:go_default_library", "//pkg/apis/apps/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library", diff --git a/pkg/registry/apps/statefulset/registry.go b/pkg/registry/apps/statefulset/registry.go new file mode 100644 index 00000000000..25d136f8620 --- /dev/null +++ b/pkg/registry/apps/statefulset/registry.go @@ -0,0 +1,95 @@ +/* +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 statefulset + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/api/errors" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" + genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/apiserver/pkg/registry/rest" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/apps" +) + +// Registry is an interface for things that know how to store StatefulSets. +type Registry interface { + ListStatefulSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*apps.StatefulSetList, error) + WatchStatefulSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) + GetStatefulSet(ctx genericapirequest.Context, statefulSetID string, options *metav1.GetOptions) (*apps.StatefulSet, error) + CreateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet) (*apps.StatefulSet, error) + UpdateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet) (*apps.StatefulSet, error) + DeleteStatefulSet(ctx genericapirequest.Context, statefulSetID string) error +} + +// storage puts strong typing around storage calls +type storage struct { + rest.StandardStorage +} + +// NewRegistry returns a new Registry interface for the given Storage. Any mismatched +// types will panic. +func NewRegistry(s rest.StandardStorage) Registry { + return &storage{s} +} + +func (s *storage) ListStatefulSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (*apps.StatefulSetList, error) { + if options != nil && options.FieldSelector != nil && !options.FieldSelector.Empty() { + return nil, fmt.Errorf("field selector not supported yet") + } + obj, err := s.List(ctx, options) + if err != nil { + return nil, err + } + return obj.(*apps.StatefulSetList), err +} + +func (s *storage) WatchStatefulSets(ctx genericapirequest.Context, options *metainternalversion.ListOptions) (watch.Interface, error) { + return s.Watch(ctx, options) +} + +func (s *storage) GetStatefulSet(ctx genericapirequest.Context, statefulSetID string, options *metav1.GetOptions) (*apps.StatefulSet, error) { + obj, err := s.Get(ctx, statefulSetID, options) + if err != nil { + return nil, errors.NewNotFound(apps.Resource("statefulsets/scale"), statefulSetID) + } + return obj.(*apps.StatefulSet), nil +} + +func (s *storage) CreateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet) (*apps.StatefulSet, error) { + obj, err := s.Create(ctx, statefulSet, false) + if err != nil { + return nil, err + } + return obj.(*apps.StatefulSet), nil +} + +func (s *storage) UpdateStatefulSet(ctx genericapirequest.Context, statefulSet *apps.StatefulSet) (*apps.StatefulSet, error) { + obj, _, err := s.Update(ctx, statefulSet.Name, rest.DefaultUpdatedObjectInfo(statefulSet, api.Scheme)) + if err != nil { + return nil, err + } + return obj.(*apps.StatefulSet), nil +} + +func (s *storage) DeleteStatefulSet(ctx genericapirequest.Context, statefulSetID string) error { + _, _, err := s.Delete(ctx, statefulSetID, nil) + return err +} diff --git a/pkg/registry/apps/statefulset/storage/BUILD b/pkg/registry/apps/statefulset/storage/BUILD index 9efc691e4fc..c8d5023a1af 100644 --- a/pkg/registry/apps/statefulset/storage/BUILD +++ b/pkg/registry/apps/statefulset/storage/BUILD @@ -16,10 +16,14 @@ go_test( deps = [ "//pkg/api:go_default_library", "//pkg/apis/apps:go_default_library", + "//pkg/apis/extensions:go_default_library", "//pkg/registry/registrytest:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", @@ -34,8 +38,11 @@ go_library( deps = [ "//pkg/api:go_default_library", "//pkg/apis/apps:go_default_library", + "//pkg/apis/extensions:go_default_library", + "//pkg/apis/extensions/validation:go_default_library", "//pkg/registry/apps/statefulset:go_default_library", "//pkg/registry/cachesize:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", diff --git a/pkg/registry/apps/statefulset/storage/storage.go b/pkg/registry/apps/statefulset/storage/storage.go index dac28b0e846..80375251a96 100644 --- a/pkg/registry/apps/statefulset/storage/storage.go +++ b/pkg/registry/apps/statefulset/storage/storage.go @@ -17,6 +17,9 @@ limitations under the License. package storage import ( + "fmt" + + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" @@ -24,23 +27,43 @@ import ( genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" "k8s.io/apiserver/pkg/registry/rest" "k8s.io/kubernetes/pkg/api" - appsapi "k8s.io/kubernetes/pkg/apis/apps" + "k8s.io/kubernetes/pkg/apis/apps" + "k8s.io/kubernetes/pkg/apis/extensions" + extvalidation "k8s.io/kubernetes/pkg/apis/extensions/validation" "k8s.io/kubernetes/pkg/registry/apps/statefulset" "k8s.io/kubernetes/pkg/registry/cachesize" ) -// rest implements a RESTStorage for replication controllers against etcd +// StatefulSetStorage includes dummy storage for StatefulSets, and their Status and Scale subresource. +type StatefulSetStorage struct { + StatefulSet *REST + Status *StatusREST + Scale *ScaleREST +} + +func NewStorage(optsGetter generic.RESTOptionsGetter) StatefulSetStorage { + statefulSetRest, statefulSetStatusRest := NewREST(optsGetter) + statefulSetRegistry := statefulset.NewRegistry(statefulSetRest) + + return StatefulSetStorage{ + StatefulSet: statefulSetRest, + Status: statefulSetStatusRest, + Scale: &ScaleREST{registry: statefulSetRegistry}, + } +} + +// rest implements a RESTStorage for statefulsets against etcd type REST struct { *genericregistry.Store } -// NewREST returns a RESTStorage object that will work against replication controllers. +// NewREST returns a RESTStorage object that will work against statefulsets. func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST) { store := &genericregistry.Store{ Copier: api.Scheme, - NewFunc: func() runtime.Object { return &appsapi.StatefulSet{} }, - NewListFunc: func() runtime.Object { return &appsapi.StatefulSetList{} }, - DefaultQualifiedResource: appsapi.Resource("statefulsets"), + NewFunc: func() runtime.Object { return &apps.StatefulSet{} }, + NewListFunc: func() runtime.Object { return &apps.StatefulSetList{} }, + DefaultQualifiedResource: apps.Resource("statefulsets"), WatchCacheSize: cachesize.GetWatchCacheSizeByResource("statefulsets"), CreateStrategy: statefulset.Strategy, @@ -71,7 +94,7 @@ type StatusREST struct { } func (r *StatusREST) New() runtime.Object { - return &appsapi.StatefulSet{} + return &apps.StatefulSet{} } // Get retrieves the object from the storage. It is required to support Patch. @@ -91,3 +114,88 @@ var _ rest.ShortNamesProvider = &REST{} func (r *REST) ShortNames() []string { return []string{"sts"} } + +type ScaleREST struct { + registry statefulset.Registry +} + +// ScaleREST implements Patcher +var _ = rest.Patcher(&ScaleREST{}) + +// New creates a new Scale object +func (r *ScaleREST) New() runtime.Object { + return &extensions.Scale{} +} + +func (r *ScaleREST) Get(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (runtime.Object, error) { + ss, err := r.registry.GetStatefulSet(ctx, name, options) + if err != nil { + return nil, err + } + scale, err := scaleFromStatefulSet(ss) + if err != nil { + return nil, errors.NewBadRequest(fmt.Sprintf("%v", err)) + } + return scale, err +} + +func (r *ScaleREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + ss, err := r.registry.GetStatefulSet(ctx, name, &metav1.GetOptions{}) + if err != nil { + return nil, false, err + } + + oldScale, err := scaleFromStatefulSet(ss) + if err != nil { + return nil, false, err + } + + obj, err := objInfo.UpdatedObject(ctx, oldScale) + if err != nil { + return nil, false, err + } + if obj == nil { + return nil, false, errors.NewBadRequest(fmt.Sprintf("nil update passed to Scale")) + } + scale, ok := obj.(*extensions.Scale) + if !ok { + return nil, false, errors.NewBadRequest(fmt.Sprintf("wrong object passed to Scale update: %v", obj)) + } + + if errs := extvalidation.ValidateScale(scale); len(errs) > 0 { + return nil, false, errors.NewInvalid(extensions.Kind("Scale"), scale.Name, errs) + } + + ss.Spec.Replicas = scale.Spec.Replicas + ss.ResourceVersion = scale.ResourceVersion + ss, err = r.registry.UpdateStatefulSet(ctx, ss) + if err != nil { + return nil, false, err + } + newScale, err := scaleFromStatefulSet(ss) + if err != nil { + return nil, false, errors.NewBadRequest(fmt.Sprintf("%v", err)) + } + return newScale, false, err +} + +// scaleFromStatefulSet returns a scale subresource for a statefulset. +func scaleFromStatefulSet(ss *apps.StatefulSet) (*extensions.Scale, error) { + return &extensions.Scale{ + // TODO: Create a variant of ObjectMeta type that only contains the fields below. + ObjectMeta: metav1.ObjectMeta{ + Name: ss.Name, + Namespace: ss.Namespace, + UID: ss.UID, + ResourceVersion: ss.ResourceVersion, + CreationTimestamp: ss.CreationTimestamp, + }, + Spec: extensions.ScaleSpec{ + Replicas: ss.Spec.Replicas, + }, + Status: extensions.ScaleStatus{ + Replicas: ss.Status.Replicas, + Selector: ss.Spec.Selector, + }, + }, nil +} diff --git a/pkg/registry/apps/statefulset/storage/storage_test.go b/pkg/registry/apps/statefulset/storage/storage_test.go index 9d4001d82ea..933d99b2d42 100644 --- a/pkg/registry/apps/statefulset/storage/storage_test.go +++ b/pkg/registry/apps/statefulset/storage/storage_test.go @@ -19,24 +19,28 @@ package storage import ( "testing" + apiequality "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/diff" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/rest" etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/apps" + "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/registry/registrytest" ) // TODO: allow for global factory override -func newStorage(t *testing.T) (*REST, *StatusREST, *etcdtesting.EtcdTestServer) { +func newStorage(t *testing.T) (StatefulSetStorage, *etcdtesting.EtcdTestServer) { etcdStorage, server := registrytest.NewEtcdStorage(t, apps.GroupName) restOptions := generic.RESTOptions{StorageConfig: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1, ResourcePrefix: "statefulsets"} - statefulSetStorage, statusStorage := NewREST(restOptions) - return statefulSetStorage, statusStorage, server + storage := NewStorage(restOptions) + return storage, server } // createStatefulSet is a helper function that returns a StatefulSet with the updated resource version. @@ -83,11 +87,13 @@ func validNewStatefulSet() *apps.StatefulSet { } } +var validStatefulSet = *validNewStatefulSet() + func TestCreate(t *testing.T) { - storage, _, server := newStorage(t) + storage, server := newStorage(t) defer server.Terminate(t) - defer storage.Store.DestroyFunc() - test := registrytest.New(t, storage.Store) + defer storage.StatefulSet.Store.DestroyFunc() + test := registrytest.New(t, storage.StatefulSet.Store) ps := validNewStatefulSet() ps.ObjectMeta = metav1.ObjectMeta{} test.TestCreate( @@ -100,13 +106,13 @@ func TestCreate(t *testing.T) { // TODO: Test updates to spec when we allow them. func TestStatusUpdate(t *testing.T) { - storage, statusStorage, server := newStorage(t) + storage, server := newStorage(t) defer server.Terminate(t) - defer storage.Store.DestroyFunc() + defer storage.StatefulSet.Store.DestroyFunc() ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault) key := "/statefulsets/" + metav1.NamespaceDefault + "/foo" validStatefulSet := validNewStatefulSet() - if err := storage.Storage.Create(ctx, key, validStatefulSet, nil, 0); err != nil { + if err := storage.StatefulSet.Storage.Create(ctx, key, validStatefulSet, nil, 0); err != nil { t.Fatalf("unexpected error: %v", err) } update := apps.StatefulSet{ @@ -119,10 +125,10 @@ func TestStatusUpdate(t *testing.T) { }, } - if _, _, err := statusStorage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil { + if _, _, err := storage.Status.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil { t.Fatalf("unexpected error: %v", err) } - obj, err := storage.Get(ctx, "foo", &metav1.GetOptions{}) + obj, err := storage.StatefulSet.Get(ctx, "foo", &metav1.GetOptions{}) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -137,34 +143,34 @@ func TestStatusUpdate(t *testing.T) { } func TestGet(t *testing.T) { - storage, _, server := newStorage(t) + storage, server := newStorage(t) defer server.Terminate(t) - defer storage.Store.DestroyFunc() - test := registrytest.New(t, storage.Store) + defer storage.StatefulSet.Store.DestroyFunc() + test := registrytest.New(t, storage.StatefulSet.Store) test.TestGet(validNewStatefulSet()) } func TestList(t *testing.T) { - storage, _, server := newStorage(t) + storage, server := newStorage(t) defer server.Terminate(t) - defer storage.Store.DestroyFunc() - test := registrytest.New(t, storage.Store) + defer storage.StatefulSet.Store.DestroyFunc() + test := registrytest.New(t, storage.StatefulSet.Store) test.TestList(validNewStatefulSet()) } func TestDelete(t *testing.T) { - storage, _, server := newStorage(t) + storage, server := newStorage(t) defer server.Terminate(t) - defer storage.Store.DestroyFunc() - test := registrytest.New(t, storage.Store) + defer storage.StatefulSet.Store.DestroyFunc() + test := registrytest.New(t, storage.StatefulSet.Store) test.TestDelete(validNewStatefulSet()) } func TestWatch(t *testing.T) { - storage, _, server := newStorage(t) + storage, server := newStorage(t) defer server.Terminate(t) - defer storage.Store.DestroyFunc() - test := registrytest.New(t, storage.Store) + defer storage.StatefulSet.Store.DestroyFunc() + test := registrytest.New(t, storage.StatefulSet.Store) test.TestWatch( validNewStatefulSet(), // matching labels @@ -189,11 +195,104 @@ func TestWatch(t *testing.T) { } func TestCategories(t *testing.T) { - storage, _, server := newStorage(t) + storage, server := newStorage(t) defer server.Terminate(t) - defer storage.Store.DestroyFunc() + defer storage.StatefulSet.Store.DestroyFunc() expected := []string{"all"} - registrytest.AssertCategories(t, storage, expected) + registrytest.AssertCategories(t, storage.StatefulSet, expected) +} + +func TestShortNames(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.StatefulSet.Store.DestroyFunc() + expected := []string{"sts"} + registrytest.AssertShortNames(t, storage.StatefulSet, expected) +} + +func TestScaleGet(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.StatefulSet.Store.DestroyFunc() + + name := "foo" + + var sts apps.StatefulSet + ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault) + key := "/statefulsets/" + metav1.NamespaceDefault + "/" + name + if err := storage.StatefulSet.Storage.Create(ctx, key, &validStatefulSet, &sts, 0); err != nil { + t.Fatalf("error setting new statefulset (key: %s) %v: %v", key, validStatefulSet, err) + } + + want := &extensions.Scale{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: metav1.NamespaceDefault, + UID: sts.UID, + ResourceVersion: sts.ResourceVersion, + CreationTimestamp: sts.CreationTimestamp, + }, + Spec: extensions.ScaleSpec{ + Replicas: validStatefulSet.Spec.Replicas, + }, + Status: extensions.ScaleStatus{ + Replicas: validStatefulSet.Status.Replicas, + Selector: validStatefulSet.Spec.Selector, + }, + } + obj, err := storage.Scale.Get(ctx, name, &metav1.GetOptions{}) + got := obj.(*extensions.Scale) + if err != nil { + t.Fatalf("error fetching scale for %s: %v", name, err) + } + if !apiequality.Semantic.DeepEqual(got, want) { + t.Errorf("unexpected scale: %s", diff.ObjectDiff(got, want)) + } +} + +func TestScaleUpdate(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.StatefulSet.Store.DestroyFunc() + + name := "foo" + + var sts apps.StatefulSet + ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault) + key := "/statefulsets/" + metav1.NamespaceDefault + "/" + name + if err := storage.StatefulSet.Storage.Create(ctx, key, &validStatefulSet, &sts, 0); err != nil { + t.Fatalf("error setting new statefulset (key: %s) %v: %v", key, validStatefulSet, err) + } + replicas := 12 + update := extensions.Scale{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: metav1.NamespaceDefault, + }, + Spec: extensions.ScaleSpec{ + Replicas: int32(replicas), + }, + } + + if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil { + t.Fatalf("error updating scale %v: %v", update, err) + } + + obj, err := storage.Scale.Get(ctx, name, &metav1.GetOptions{}) + if err != nil { + t.Fatalf("error fetching scale for %s: %v", name, err) + } + scale := obj.(*extensions.Scale) + if scale.Spec.Replicas != int32(replicas) { + t.Errorf("wrong replicas count expected: %d got: %d", replicas, scale.Spec.Replicas) + } + + update.ResourceVersion = sts.ResourceVersion + update.Spec.Replicas = 15 + + if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update, api.Scheme)); err != nil && !errors.IsConflict(err) { + t.Fatalf("unexpected error, expecting an update conflict but got %v", err) + } } // TODO: Test generation number. diff --git a/test/e2e/apps/statefulset.go b/test/e2e/apps/statefulset.go index bced0081214..548995bf7d3 100644 --- a/test/e2e/apps/statefulset.go +++ b/test/e2e/apps/statefulset.go @@ -843,6 +843,43 @@ var _ = SIGDescribe("StatefulSet", func() { return nil }, framework.StatefulPodTimeout, 2*time.Second).Should(BeNil()) }) + + It("should have a working scale subresource", func() { + By("Creating statefulset " + ssName + " in namespace " + ns) + ss := framework.NewStatefulSet(ssName, ns, headlessSvcName, 1, nil, nil, labels) + sst := framework.NewStatefulSetTester(c) + sst.SetHttpProbe(ss) + ss, err := c.AppsV1beta1().StatefulSets(ns).Create(ss) + Expect(err).NotTo(HaveOccurred()) + sst.WaitForRunningAndReady(*ss.Spec.Replicas, ss) + ss = sst.WaitForStatus(ss) + + By("getting scale subresource") + scale := framework.NewStatefulSetScale(ss) + scaleResult := &apps.Scale{} + err = c.AppsV1beta1().RESTClient().Get().AbsPath("/apis/apps/v1beta1").Namespace(ns).Resource("statefulsets").Name(ssName).SubResource("scale").Do().Into(scale) + if err != nil { + framework.Failf("Failed to get scale subresource: %v", err) + } + Expect(scale.Spec.Replicas).To(Equal(int32(1))) + Expect(scale.Status.Replicas).To(Equal(int32(1))) + + By("updating a scale subresource") + scale.ResourceVersion = "" //unconditionally update to 2 replicas + scale.Spec.Replicas = 2 + err = c.AppsV1beta1().RESTClient().Put().AbsPath("/apis/apps/v1beta1").Namespace(ns).Resource("statefulsets").Name(ssName).SubResource("scale").Body(scale).Do().Into(scaleResult) + if err != nil { + framework.Failf("Failed to put scale subresource: %v", err) + } + Expect(scaleResult.Spec.Replicas).To(Equal(int32(2))) + + By("verifying the statefulset Spec.Replicas was modified") + ss, err = c.AppsV1beta1().StatefulSets(ns).Get(ssName, metav1.GetOptions{}) + if err != nil { + framework.Failf("Failed to get statefulset resource: %v", err) + } + Expect(*(ss.Spec.Replicas)).To(Equal(int32(2))) + }) }) framework.KubeDescribe("Deploy clustered applications [Feature:StatefulSet] [Slow]", func() { diff --git a/test/e2e/framework/BUILD b/test/e2e/framework/BUILD index 06be9b417a8..cc8957abc03 100644 --- a/test/e2e/framework/BUILD +++ b/test/e2e/framework/BUILD @@ -101,6 +101,7 @@ go_library( "//vendor/google.golang.org/api/compute/v1:go_default_library", "//vendor/google.golang.org/api/googleapi:go_default_library", "//vendor/k8s.io/api/apps/v1beta1:go_default_library", + "//vendor/k8s.io/api/apps/v1beta2:go_default_library", "//vendor/k8s.io/api/authorization/v1beta1:go_default_library", "//vendor/k8s.io/api/batch/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", diff --git a/test/e2e/framework/statefulset_utils.go b/test/e2e/framework/statefulset_utils.go index bc4c9a68dac..b33cc332c54 100644 --- a/test/e2e/framework/statefulset_utils.go +++ b/test/e2e/framework/statefulset_utils.go @@ -29,6 +29,7 @@ import ( . "github.com/onsi/gomega" apps "k8s.io/api/apps/v1beta1" + appsV1beta2 "k8s.io/api/apps/v1beta2" "k8s.io/api/core/v1" apierrs "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" @@ -224,7 +225,7 @@ func (s *StatefulSetTester) Scale(ss *apps.StatefulSet, count int32) error { // UpdateReplicas updates the replicas of ss to count. func (s *StatefulSetTester) UpdateReplicas(ss *apps.StatefulSet, count int32) { - s.update(ss.Namespace, ss.Name, func(ss *apps.StatefulSet) { ss.Spec.Replicas = &count }) + s.update(ss.Namespace, ss.Name, func(ss *apps.StatefulSet) { *(ss.Spec.Replicas) = count }) } // Restart scales ss to 0 and then back to its previous number of replicas. @@ -812,6 +813,23 @@ func NewStatefulSet(name, ns, governingSvcName string, replicas int32, statefulP } } +// NewStatefulSetScale creates a new StatefulSet scale subresource and returns it +func NewStatefulSetScale(ss *apps.StatefulSet) *appsV1beta2.Scale { + return &appsV1beta2.Scale{ + // TODO: Create a variant of ObjectMeta type that only contains the fields below. + ObjectMeta: metav1.ObjectMeta{ + Name: ss.Name, + Namespace: ss.Namespace, + }, + Spec: appsV1beta2.ScaleSpec{ + Replicas: *(ss.Spec.Replicas), + }, + Status: appsV1beta2.ScaleStatus{ + Replicas: ss.Status.Replicas, + }, + } +} + var statefulPodRegex = regexp.MustCompile("(.*)-([0-9]+)$") func getStatefulPodOrdinal(pod *v1.Pod) int { From c5f011beee58e96c5c538498225f65dd219f989e Mon Sep 17 00:00:00 2001 From: Quinton Hoole Date: Mon, 7 Aug 2017 13:08:40 -0700 Subject: [PATCH 34/38] Add Shashi as approver for e2e_federation --- test/e2e_federation/OWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/test/e2e_federation/OWNERS b/test/e2e_federation/OWNERS index ad98c68b671..b60580337e5 100644 --- a/test/e2e_federation/OWNERS +++ b/test/e2e_federation/OWNERS @@ -13,3 +13,4 @@ approvers: - mwielgus - nikhiljindal - quinton-hoole + - shashidharatd From 4ee72eb300423772020dd1cf208159058ba7dab5 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Mon, 7 Aug 2017 09:42:32 -0400 Subject: [PATCH 35/38] Revert "Merge pull request #47353 from apelisse/http-cache" This reverts commit fc89743dca6b563063b74728c3b28100cf674d9d, reversing changes made to 29ab38e898988c36e2de34f77fa33be556eb21bd. --- Godeps/Godeps.json | 13 - Godeps/LICENSES | 55 -- pkg/kubectl/cmd/get.go | 7 +- pkg/kubectl/cmd/testing/fake.go | 4 +- pkg/kubectl/cmd/util/factory.go | 2 +- .../cmd/util/factory_object_mapping.go | 25 +- pkg/kubectl/cmd/util/helpers.go | 13 + pkg/kubectl/cmd/util/openapi/BUILD | 5 + pkg/kubectl/cmd/util/openapi/openapi_cache.go | 163 +++++ .../cmd/util/openapi/openapi_cache_test.go | 268 ++++++++ .../cmd/util/openapi/openapi_getter.go | 16 +- .../cmd/util/openapi/openapi_getter_test.go | 72 +-- .../Godeps/Godeps.json | 16 - .../src/k8s.io/apiserver/Godeps/Godeps.json | 12 - .../src/k8s.io/client-go/Godeps/Godeps.json | 16 - staging/src/k8s.io/client-go/rest/config.go | 4 - .../src/k8s.io/client-go/rest/config_test.go | 1 - .../src/k8s.io/client-go/rest/transport.go | 1 - .../tools/clientcmd/client_config.go | 5 - .../client-go/tools/clientcmd/overrides.go | 8 - staging/src/k8s.io/client-go/transport/BUILD | 3 - .../src/k8s.io/client-go/transport/config.go | 4 - .../client-go/transport/round_trippers.go | 17 - .../transport/round_trippers_test.go | 61 -- .../k8s.io/kube-aggregator/Godeps/Godeps.json | 16 - .../src/k8s.io/kube-gen/Godeps/Godeps.json | 16 - staging/src/k8s.io/metrics/Godeps/Godeps.json | 16 - .../sample-apiserver/Godeps/Godeps.json | 16 - vendor/BUILD | 2 - .../gregjones/httpcache/.travis.yml | 18 - vendor/github.com/gregjones/httpcache/BUILD | 30 - .../gregjones/httpcache/LICENSE.txt | 7 - .../github.com/gregjones/httpcache/README.md | 24 - .../gregjones/httpcache/diskcache/BUILD | 28 - .../httpcache/diskcache/diskcache.go | 61 -- .../gregjones/httpcache/httpcache.go | 553 ----------------- vendor/github.com/peterbourgon/diskv/BUILD | 32 - vendor/github.com/peterbourgon/diskv/LICENSE | 19 - .../github.com/peterbourgon/diskv/README.md | 141 ----- .../peterbourgon/diskv/compression.go | 64 -- vendor/github.com/peterbourgon/diskv/diskv.go | 578 ------------------ vendor/github.com/peterbourgon/diskv/index.go | 115 ---- 42 files changed, 491 insertions(+), 2036 deletions(-) create mode 100644 pkg/kubectl/cmd/util/openapi/openapi_cache.go create mode 100644 pkg/kubectl/cmd/util/openapi/openapi_cache_test.go delete mode 100644 vendor/github.com/gregjones/httpcache/.travis.yml delete mode 100644 vendor/github.com/gregjones/httpcache/BUILD delete mode 100644 vendor/github.com/gregjones/httpcache/LICENSE.txt delete mode 100644 vendor/github.com/gregjones/httpcache/README.md delete mode 100644 vendor/github.com/gregjones/httpcache/diskcache/BUILD delete mode 100644 vendor/github.com/gregjones/httpcache/diskcache/diskcache.go delete mode 100644 vendor/github.com/gregjones/httpcache/httpcache.go delete mode 100644 vendor/github.com/peterbourgon/diskv/BUILD delete mode 100644 vendor/github.com/peterbourgon/diskv/LICENSE delete mode 100644 vendor/github.com/peterbourgon/diskv/README.md delete mode 100644 vendor/github.com/peterbourgon/diskv/compression.go delete mode 100644 vendor/github.com/peterbourgon/diskv/diskv.go delete mode 100644 vendor/github.com/peterbourgon/diskv/index.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index d8d99622bda..271f154f560 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1707,14 +1707,6 @@ "ImportPath": "github.com/gorilla/websocket", "Rev": "6eb6ad425a89d9da7a5549bc6da8f79ba5c17844" }, - { - "ImportPath": "github.com/gregjones/httpcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, - { - "ImportPath": "github.com/gregjones/httpcache/diskcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, { "ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus", "Comment": "v1.1-4-g2500245", @@ -2247,11 +2239,6 @@ "Comment": "v0.3.5-10-g0049ab3", "Rev": "0049ab3dc4c4c70a9eee23087437b69c0dde2130" }, - { - "ImportPath": "github.com/peterbourgon/diskv", - "Comment": "v2.0.0-2-g5dfcb07", - "Rev": "5dfcb07a075adbaaa4094cddfd160b1e1c77a043" - }, { "ImportPath": "github.com/pkg/errors", "Comment": "v0.7.0-13-ga221380", diff --git a/Godeps/LICENSES b/Godeps/LICENSES index 5aeb01e9ee6..5911292ad9c 100644 --- a/Godeps/LICENSES +++ b/Godeps/LICENSES @@ -59441,34 +59441,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================================================ -================================================================================ -= vendor/github.com/gregjones/httpcache licensed under: = - -Copyright © 2012 Greg Jones (greg.jones@gmail.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -= vendor/github.com/gregjones/httpcache/LICENSE.txt 3cfef421226b2dacde78a4871380ac24 - -================================================================================ - - -================================================================================ -= vendor/github.com/gregjones/httpcache/diskcache licensed under: = - -Copyright © 2012 Greg Jones (greg.jones@gmail.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -= vendor/github.com/gregjones/httpcache/LICENSE.txt 3cfef421226b2dacde78a4871380ac24 - -================================================================================ - - ================================================================================ = vendor/github.com/grpc-ecosystem/go-grpc-prometheus licensed under: = @@ -71602,33 +71574,6 @@ SOFTWARE. ================================================================================ -================================================================================ -= vendor/github.com/peterbourgon/diskv licensed under: = - -Copyright (c) 2011-2012 Peter Bourgon - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -= vendor/github.com/peterbourgon/diskv/LICENSE f9f3e815fc84aa04c4f4db33c553eef9 - -================================================================================ - - ================================================================================ = vendor/github.com/pkg/errors licensed under: = diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index 0d1cf0ebc93..077fb958aa9 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -138,6 +138,7 @@ func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comman usage := "identifying the resource to get from a server." cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) cmdutil.AddInclude3rdPartyFlags(cmd) + cmdutil.AddOpenAPIFlags(cmd) cmd.Flags().StringVar(&options.Raw, "raw", options.Raw, "Raw URI to request from the server. Uses the transport specified by the kubeconfig file.") return cmd } @@ -456,7 +457,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [ // if cmd does not specify output format and useOpenAPIPrintColumnFlagLabel flag is true, // then get the default output options for this mapping from OpenAPI schema. if !cmdSpecifiesOutputFmt(cmd) && useOpenAPIPrintColumns { - outputOpts, _ = outputOptsForMappingFromOpenAPI(f, mapping) + outputOpts, _ = outputOptsForMappingFromOpenAPI(f, cmdutil.GetOpenAPICacheDir(cmd), mapping) } printer, err = f.PrinterForMapping(cmd, false, outputOpts, mapping, allNamespaces) @@ -555,11 +556,11 @@ func cmdSpecifiesOutputFmt(cmd *cobra.Command) bool { // outputOptsForMappingFromOpenAPI looks for the output format metatadata in the // openapi schema and returns the output options for the mapping if found. -func outputOptsForMappingFromOpenAPI(f cmdutil.Factory, mapping *meta.RESTMapping) (*printers.OutputOptions, bool) { +func outputOptsForMappingFromOpenAPI(f cmdutil.Factory, openAPIcacheDir string, mapping *meta.RESTMapping) (*printers.OutputOptions, bool) { // user has not specified any output format, check if OpenAPI has // default specification to print this resource type - api, err := f.OpenAPISchema() + api, err := f.OpenAPISchema(openAPIcacheDir) if err != nil { // Error getting schema return nil, false diff --git a/pkg/kubectl/cmd/testing/fake.go b/pkg/kubectl/cmd/testing/fake.go index b69bc1123ef..23c27a45653 100644 --- a/pkg/kubectl/cmd/testing/fake.go +++ b/pkg/kubectl/cmd/testing/fake.go @@ -418,7 +418,7 @@ func (f *FakeFactory) SwaggerSchema(schema.GroupVersionKind) (*swagger.ApiDeclar return nil, nil } -func (f *FakeFactory) OpenAPISchema() (openapi.Resources, error) { +func (f *FakeFactory) OpenAPISchema(cacheDir string) (openapi.Resources, error) { return nil, nil } @@ -756,7 +756,7 @@ func (f *fakeAPIFactory) SwaggerSchema(schema.GroupVersionKind) (*swagger.ApiDec return nil, nil } -func (f *fakeAPIFactory) OpenAPISchema() (openapi.Resources, error) { +func (f *fakeAPIFactory) OpenAPISchema(cacheDir string) (openapi.Resources, error) { if f.tf.OpenAPISchemaFunc != nil { return f.tf.OpenAPISchemaFunc() } diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index d2ccc5c9179..999e49d72fe 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -224,7 +224,7 @@ type ObjectMappingFactory interface { // SwaggerSchema returns the schema declaration for the provided group version kind. SwaggerSchema(schema.GroupVersionKind) (*swagger.ApiDeclaration, error) // OpenAPISchema returns the schema openapi schema definiton - OpenAPISchema() (openapi.Resources, error) + OpenAPISchema(cacheDir string) (openapi.Resources, error) } // BuilderFactory holds the second level of factory methods. These functions depend upon ObjectMappingFactory and ClientAccessFactory methods. diff --git a/pkg/kubectl/cmd/util/factory_object_mapping.go b/pkg/kubectl/cmd/util/factory_object_mapping.go index 6adb64fb54f..83cb1b30dbf 100644 --- a/pkg/kubectl/cmd/util/factory_object_mapping.go +++ b/pkg/kubectl/cmd/util/factory_object_mapping.go @@ -439,7 +439,13 @@ func (f *ring1Factory) SwaggerSchema(gvk schema.GroupVersionKind) (*swagger.ApiD } // OpenAPISchema returns metadata and structural information about Kubernetes object definitions. -func (f *ring1Factory) OpenAPISchema() (openapi.Resources, error) { +// Will try to cache the data to a local file. Cache is written and read from a +// file created with ioutil.TempFile and obeys the expiration semantics of that file. +// The cache location is a function of the client and server versions so that the open API +// schema will be cached separately for different client / server combinations. +// Note, the cache will not be invalidated if the server changes its open API schema without +// changing the server version. +func (f *ring1Factory) OpenAPISchema(cacheDir string) (openapi.Resources, error) { discovery, err := f.clientAccessFactory.DiscoveryClient() if err != nil { return nil, err @@ -447,8 +453,23 @@ func (f *ring1Factory) OpenAPISchema() (openapi.Resources, error) { // Lazily initialize the OpenAPIGetter once f.openAPIGetter.once.Do(func() { + // Get the server version for caching the openapi spec + versionString := "" + version, err := discovery.ServerVersion() + if err != nil { + // Cache the result under the server version + versionString = version.String() + } + + // Get the cache directory for caching the openapi spec + cacheDir, err = substituteUserHome(cacheDir) + if err != nil { + // Don't cache the result if we couldn't substitute the home directory + cacheDir = "" + } + // Create the caching OpenAPIGetter - f.openAPIGetter.getter = openapi.NewOpenAPIGetter(discovery) + f.openAPIGetter.getter = openapi.NewOpenAPIGetter(cacheDir, versionString, discovery) }) // Delegate to the OpenAPIGetter diff --git a/pkg/kubectl/cmd/util/helpers.go b/pkg/kubectl/cmd/util/helpers.go index 885cd8754d7..0cd966c6328 100644 --- a/pkg/kubectl/cmd/util/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -404,6 +404,19 @@ func AddValidateOptionFlags(cmd *cobra.Command, options *ValidateOptions) { cmd.MarkFlagFilename("schema-cache-dir") } +func AddOpenAPIFlags(cmd *cobra.Command) { + cmd.Flags().String("schema-cache-dir", + fmt.Sprintf("~/%s/%s", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName), + fmt.Sprintf("If non-empty, load/store cached API schemas in this directory, default is '$HOME/%s/%s'", + clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName), + ) + cmd.MarkFlagFilename("schema-cache-dir") +} + +func GetOpenAPICacheDir(cmd *cobra.Command) string { + return GetFlagString(cmd, "schema-cache-dir") +} + func AddFilenameOptionFlags(cmd *cobra.Command, options *resource.FilenameOptions, usage string) { kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, "Filename, directory, or URL to files "+usage) cmd.Flags().BoolVarP(&options.Recursive, "recursive", "R", options.Recursive, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.") diff --git a/pkg/kubectl/cmd/util/openapi/BUILD b/pkg/kubectl/cmd/util/openapi/BUILD index 01920e15696..eb926ee7e97 100644 --- a/pkg/kubectl/cmd/util/openapi/BUILD +++ b/pkg/kubectl/cmd/util/openapi/BUILD @@ -15,11 +15,15 @@ go_library( "document.go", "extensions.go", "openapi.go", + "openapi_cache.go", "openapi_getter.go", ], tags = ["automanaged"], deps = [ + "//pkg/version:go_default_library", "//vendor/github.com/go-openapi/spec:go_default_library", + "//vendor/github.com/golang/glog:go_default_library", + "//vendor/github.com/golang/protobuf/proto:go_default_library", "//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", "//vendor/gopkg.in/yaml.v2:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", @@ -31,6 +35,7 @@ go_test( name = "go_default_xtest", size = "small", srcs = [ + "openapi_cache_test.go", "openapi_getter_test.go", "openapi_suite_test.go", "openapi_test.go", diff --git a/pkg/kubectl/cmd/util/openapi/openapi_cache.go b/pkg/kubectl/cmd/util/openapi/openapi_cache.go new file mode 100644 index 00000000000..faf83456b49 --- /dev/null +++ b/pkg/kubectl/cmd/util/openapi/openapi_cache.go @@ -0,0 +1,163 @@ +/* +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 openapi + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + + "github.com/golang/glog" + "github.com/golang/protobuf/proto" + openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2" + + "k8s.io/client-go/discovery" + "k8s.io/kubernetes/pkg/version" +) + +const openapiFileName = "openapi_cache" + +type CachingOpenAPIClient struct { + version string + client discovery.OpenAPISchemaInterface + cacheDirName string +} + +// NewCachingOpenAPIClient returns a new discovery.OpenAPISchemaInterface +// that will read the openapi spec from a local cache if it exists, and +// if not will then fetch an openapi spec using a client. +// client: used to fetch a new openapi spec if a local cache is not found +// version: the server version and used as part of the cache file location +// cacheDir: the directory under which the cache file will be written +func NewCachingOpenAPIClient(client discovery.OpenAPISchemaInterface, version, cacheDir string) *CachingOpenAPIClient { + return &CachingOpenAPIClient{ + client: client, + version: version, + cacheDirName: cacheDir, + } +} + +// OpenAPIData returns an openapi spec. +// It will first attempt to read the spec from a local cache +// If it cannot read a local cache, it will read the file +// using the client and then write the cache. +func (c *CachingOpenAPIClient) OpenAPIData() (Resources, error) { + // Try to use the cached version + if c.useCache() { + doc, err := c.readOpenAPICache() + if err == nil { + return NewOpenAPIData(doc) + } + } + + // No cached version found, download from server + s, err := c.client.OpenAPISchema() + if err != nil { + glog.V(2).Infof("Failed to download openapi data %v", err) + return nil, err + } + + oa, err := NewOpenAPIData(s) + if err != nil { + glog.V(2).Infof("Failed to parse openapi data %v", err) + return nil, err + } + + // Try to cache the openapi spec + if c.useCache() { + err = c.writeToCache(s) + if err != nil { + // Just log an message, no need to fail the command since we got the data we need + glog.V(2).Infof("Unable to cache openapi spec %v", err) + } + } + + // Return the parsed data + return oa, nil +} + +// useCache returns true if the client should try to use the cache file +func (c *CachingOpenAPIClient) useCache() bool { + return len(c.version) > 0 && len(c.cacheDirName) > 0 +} + +// readOpenAPICache tries to read the openapi spec from the local file cache +func (c *CachingOpenAPIClient) readOpenAPICache() (*openapi_v2.Document, error) { + // Get the filename to read + filename := c.openAPICacheFilename() + + // Read the cached file + data, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + + doc := &openapi_v2.Document{} + return doc, proto.Unmarshal(data, doc) +} + +// writeToCache tries to write the openapi spec to the local file cache. +// writes the data to a new tempfile, and then links the cache file and the tempfile +func (c *CachingOpenAPIClient) writeToCache(doc *openapi_v2.Document) error { + // Get the constant filename used to read the cache. + cacheFile := c.openAPICacheFilename() + + // Binary encode the spec. This is 10x as fast as using json encoding. (60ms vs 600ms) + b, err := proto.Marshal(doc) + if err != nil { + return fmt.Errorf("Could not binary encode openapi spec: %v", err) + } + + // Create a new temp file for the cached openapi spec. + cacheDir := filepath.Dir(cacheFile) + if err := os.MkdirAll(cacheDir, 0755); err != nil { + return fmt.Errorf("Could not create directory: %v %v", cacheDir, err) + } + tmpFile, err := ioutil.TempFile(cacheDir, "openapi") + if err != nil { + return fmt.Errorf("Could not create temp cache file: %v %v", cacheFile, err) + } + + // Write the binary encoded openapi spec to the temp file + if _, err := io.Copy(tmpFile, bytes.NewBuffer(b)); err != nil { + return fmt.Errorf("Could not write temp cache file: %v", err) + } + + // Link the temp cache file to the constant cache filepath + return linkFiles(tmpFile.Name(), cacheFile) +} + +// openAPICacheFilename returns the filename to read the cache from +func (c *CachingOpenAPIClient) openAPICacheFilename() string { + // Cache using the client and server versions + return filepath.Join(c.cacheDirName, c.version, version.Get().GitVersion, openapiFileName) +} + +// linkFiles links the old file to the new file +func linkFiles(old, new string) error { + if err := os.Link(old, new); err != nil { + // If we can't write due to file existing, or permission problems, keep going. + if os.IsExist(err) || os.IsPermission(err) { + return nil + } + return err + } + return nil +} diff --git a/pkg/kubectl/cmd/util/openapi/openapi_cache_test.go b/pkg/kubectl/cmd/util/openapi/openapi_cache_test.go new file mode 100644 index 00000000000..de93c028484 --- /dev/null +++ b/pkg/kubectl/cmd/util/openapi/openapi_cache_test.go @@ -0,0 +1,268 @@ +/* +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 openapi_test + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sync" + + "gopkg.in/yaml.v2" + + "github.com/googleapis/gnostic/OpenAPIv2" + "github.com/googleapis/gnostic/compiler" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi" +) + +var _ = Describe("When reading openAPIData", func() { + var tmpDir string + var err error + var client *fakeOpenAPIClient + var instance *openapi.CachingOpenAPIClient + var expectedData openapi.Resources + + BeforeEach(func() { + tmpDir, err = ioutil.TempDir("", "openapi_cache_test") + Expect(err).To(BeNil()) + client = &fakeOpenAPIClient{} + instance = openapi.NewCachingOpenAPIClient(client, "v1.6", tmpDir) + + d, err := data.OpenAPISchema() + Expect(err).To(BeNil()) + + expectedData, err = openapi.NewOpenAPIData(d) + Expect(err).To(BeNil()) + }) + + AfterEach(func() { + os.RemoveAll(tmpDir) + }) + + It("should write to the cache", func() { + By("getting the live openapi spec from the server") + result, err := instance.OpenAPIData() + Expect(err).To(BeNil()) + Expect(result).To(Equal(expectedData)) + Expect(client.calls).To(Equal(1)) + + By("writing the live openapi spec to a local cache file") + names, err := getFilenames(tmpDir) + Expect(err).To(BeNil()) + Expect(names).To(ConsistOf("v1.6")) + + names, err = getFilenames(filepath.Join(tmpDir, "v1.6")) + Expect(err).To(BeNil()) + Expect(names).To(HaveLen(1)) + clientVersion := names[0] + + names, err = getFilenames(filepath.Join(tmpDir, "v1.6", clientVersion)) + Expect(err).To(BeNil()) + Expect(names).To(ContainElement("openapi_cache")) + }) + + It("should read from the cache", func() { + // First call should use the client + result, err := instance.OpenAPIData() + Expect(err).To(BeNil()) + Expect(result).To(Equal(expectedData)) + Expect(client.calls).To(Equal(1)) + + // Second call shouldn't use the client + result, err = instance.OpenAPIData() + Expect(err).To(BeNil()) + Expect(result).To(Equal(expectedData)) + Expect(client.calls).To(Equal(1)) + + names, err := getFilenames(tmpDir) + Expect(err).To(BeNil()) + Expect(names).To(ConsistOf("v1.6")) + }) + + It("propagate errors that are encountered", func() { + // Expect an error + client.err = fmt.Errorf("expected error") + result, err := instance.OpenAPIData() + Expect(err.Error()).To(Equal(client.err.Error())) + Expect(result).To(BeNil()) + Expect(client.calls).To(Equal(1)) + + // No cache file is written + files, err := ioutil.ReadDir(tmpDir) + Expect(err).To(BeNil()) + Expect(files).To(HaveLen(0)) + + // Client error is not cached + result, err = instance.OpenAPIData() + Expect(err.Error()).To(Equal(client.err.Error())) + Expect(result).To(BeNil()) + Expect(client.calls).To(Equal(2)) + }) +}) + +var _ = Describe("Reading openAPIData", func() { + var tmpDir string + var serverVersion string + var cacheDir string + + BeforeEach(func() { + var err error + tmpDir, err = ioutil.TempDir("", "openapi_cache_test") + Expect(err).To(BeNil()) + }) + + AfterEach(func() { + os.RemoveAll(tmpDir) + }) + + // Set the serverVersion to empty + Context("when the server version is empty", func() { + BeforeEach(func() { + serverVersion = "" + cacheDir = tmpDir + }) + It("should not cache the result", func() { + client := &fakeOpenAPIClient{} + + instance := openapi.NewCachingOpenAPIClient(client, serverVersion, cacheDir) + + d, err := data.OpenAPISchema() + Expect(err).To(BeNil()) + + expectedData, err := openapi.NewOpenAPIData(d) + Expect(err).To(BeNil()) + + By("getting the live openapi schema") + result, err := instance.OpenAPIData() + Expect(err).To(BeNil()) + Expect(result).To(Equal(expectedData)) + Expect(client.calls).To(Equal(1)) + + files, err := ioutil.ReadDir(tmpDir) + Expect(err).To(BeNil()) + Expect(files).To(HaveLen(0)) + }) + }) + + Context("when the cache directory is empty", func() { + BeforeEach(func() { + serverVersion = "v1.6" + cacheDir = "" + }) + It("should not cache the result", func() { + client := &fakeOpenAPIClient{} + + instance := openapi.NewCachingOpenAPIClient(client, serverVersion, cacheDir) + + d, err := data.OpenAPISchema() + Expect(err).To(BeNil()) + + expectedData, err := openapi.NewOpenAPIData(d) + Expect(err).To(BeNil()) + + By("getting the live openapi schema") + result, err := instance.OpenAPIData() + Expect(err).To(BeNil()) + Expect(result).To(Equal(expectedData)) + Expect(client.calls).To(Equal(1)) + + files, err := ioutil.ReadDir(tmpDir) + Expect(err).To(BeNil()) + Expect(files).To(HaveLen(0)) + }) + }) +}) + +// Test Utils +func getFilenames(path string) ([]string, error) { + files, err := ioutil.ReadDir(path) + if err != nil { + return nil, err + } + result := []string{} + for _, n := range files { + result = append(result, n.Name()) + } + return result, nil +} + +type fakeOpenAPIClient struct { + calls int + err error +} + +func (f *fakeOpenAPIClient) OpenAPISchema() (*openapi_v2.Document, error) { + f.calls = f.calls + 1 + + if f.err != nil { + return nil, f.err + } + + return data.OpenAPISchema() +} + +// Test utils +var data apiData + +type apiData struct { + sync.Once + data *openapi_v2.Document + err error +} + +func (d *apiData) OpenAPISchema() (*openapi_v2.Document, error) { + d.Do(func() { + // Get the path to the swagger.json file + wd, err := os.Getwd() + if err != nil { + d.err = err + return + } + + abs, err := filepath.Abs(wd) + if err != nil { + d.err = err + return + } + + root := filepath.Dir(filepath.Dir(filepath.Dir(filepath.Dir(filepath.Dir(abs))))) + specpath := filepath.Join(root, "api", "openapi-spec", "swagger.json") + _, err = os.Stat(specpath) + if err != nil { + d.err = err + return + } + spec, err := ioutil.ReadFile(specpath) + if err != nil { + d.err = err + return + } + var info yaml.MapSlice + err = yaml.Unmarshal(spec, &info) + if err != nil { + d.err = err + return + } + d.data, d.err = openapi_v2.NewDocument(info, compiler.NewContext("$root", nil)) + }) + + return d.data, d.err +} diff --git a/pkg/kubectl/cmd/util/openapi/openapi_getter.go b/pkg/kubectl/cmd/util/openapi/openapi_getter.go index d5c9476a02b..0b655dc0471 100644 --- a/pkg/kubectl/cmd/util/openapi/openapi_getter.go +++ b/pkg/kubectl/cmd/util/openapi/openapi_getter.go @@ -29,6 +29,8 @@ type synchronizedOpenAPIGetter struct { openAPISchema Resources err error + serverVersion string + cacheDir string openAPIClient discovery.OpenAPISchemaInterface } @@ -40,10 +42,12 @@ type Getter interface { Get() (Resources, error) } -// NewOpenAPIGetter returns an object to return OpenAPIDatas which reads -// from a server, and then stores in memory for subsequent invocations -func NewOpenAPIGetter(openAPIClient discovery.OpenAPISchemaInterface) Getter { +// NewOpenAPIGetter returns an object to return OpenAPIDatas which either read from a +// local file cache or read from a server, and then stored in memory for subsequent invocations +func NewOpenAPIGetter(cacheDir, serverVersion string, openAPIClient discovery.OpenAPISchemaInterface) Getter { return &synchronizedOpenAPIGetter{ + serverVersion: serverVersion, + cacheDir: cacheDir, openAPIClient: openAPIClient, } } @@ -51,13 +55,15 @@ func NewOpenAPIGetter(openAPIClient discovery.OpenAPISchemaInterface) Getter { // Resources implements Getter func (g *synchronizedOpenAPIGetter) Get() (Resources, error) { g.Do(func() { - s, err := g.openAPIClient.OpenAPISchema() + client := NewCachingOpenAPIClient(g.openAPIClient, g.serverVersion, g.cacheDir) + result, err := client.OpenAPIData() if err != nil { g.err = err return } - g.openAPISchema, g.err = NewOpenAPIData(s) + // Save the result + g.openAPISchema = result }) // Return the save result diff --git a/pkg/kubectl/cmd/util/openapi/openapi_getter_test.go b/pkg/kubectl/cmd/util/openapi/openapi_getter_test.go index d8c7467840f..bbaca9dee35 100644 --- a/pkg/kubectl/cmd/util/openapi/openapi_getter_test.go +++ b/pkg/kubectl/cmd/util/openapi/openapi_getter_test.go @@ -18,83 +18,13 @@ package openapi_test import ( "fmt" - "io/ioutil" - "os" - "path/filepath" - "sync" - "gopkg.in/yaml.v2" - - "github.com/googleapis/gnostic/OpenAPIv2" - "github.com/googleapis/gnostic/compiler" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi" ) -// Test utils -var data apiData - -type apiData struct { - sync.Once - data *openapi_v2.Document - err error -} - -func (d *apiData) OpenAPISchema() (*openapi_v2.Document, error) { - d.Do(func() { - // Get the path to the swagger.json file - wd, err := os.Getwd() - if err != nil { - d.err = err - return - } - - abs, err := filepath.Abs(wd) - if err != nil { - d.err = err - return - } - - root := filepath.Dir(filepath.Dir(filepath.Dir(filepath.Dir(filepath.Dir(abs))))) - specpath := filepath.Join(root, "api", "openapi-spec", "swagger.json") - _, err = os.Stat(specpath) - if err != nil { - d.err = err - return - } - spec, err := ioutil.ReadFile(specpath) - if err != nil { - d.err = err - return - } - var info yaml.MapSlice - err = yaml.Unmarshal(spec, &info) - if err != nil { - d.err = err - return - } - d.data, d.err = openapi_v2.NewDocument(info, compiler.NewContext("$root", nil)) - }) - return d.data, d.err -} - -type fakeOpenAPIClient struct { - calls int - err error -} - -func (f *fakeOpenAPIClient) OpenAPISchema() (*openapi_v2.Document, error) { - f.calls = f.calls + 1 - - if f.err != nil { - return nil, f.err - } - - return data.OpenAPISchema() -} - var _ = Describe("Getting the Resources", func() { var client *fakeOpenAPIClient var expectedData openapi.Resources @@ -108,7 +38,7 @@ var _ = Describe("Getting the Resources", func() { expectedData, err = openapi.NewOpenAPIData(d) Expect(err).To(BeNil()) - instance = openapi.NewOpenAPIGetter(client) + instance = openapi.NewOpenAPIGetter("", "", client) }) Context("when the server returns a successful result", func() { diff --git a/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json b/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json index 7170e656def..d55a95c8eaf 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json +++ b/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json @@ -162,10 +162,6 @@ "ImportPath": "github.com/golang/protobuf/ptypes/timestamp", "Rev": "4bd1920723d7b7c925de087aa32e2187708897f7" }, - { - "ImportPath": "github.com/google/btree", - "Rev": "7d79101e329e5a3adf994758c578dab82b90c017" - }, { "ImportPath": "github.com/google/gofuzz", "Rev": "44d81051d367757e1c7c6a5a86423ece9afcf63c" @@ -182,14 +178,6 @@ "ImportPath": "github.com/googleapis/gnostic/extensions", "Rev": "0c5108395e2debce0d731cf0287ddf7242066aba" }, - { - "ImportPath": "github.com/gregjones/httpcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, - { - "ImportPath": "github.com/gregjones/httpcache/diskcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, { "ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus", "Rev": "2500245aa6110c562d17020fb31a2c133d737799" @@ -254,10 +242,6 @@ "ImportPath": "github.com/pborman/uuid", "Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4" }, - { - "ImportPath": "github.com/peterbourgon/diskv", - "Rev": "5dfcb07a075adbaaa4094cddfd160b1e1c77a043" - }, { "ImportPath": "github.com/pkg/errors", "Rev": "a22138067af1c4942683050411a841ade67fe1eb" diff --git a/staging/src/k8s.io/apiserver/Godeps/Godeps.json b/staging/src/k8s.io/apiserver/Godeps/Godeps.json index f4579bc859e..a526d8d83dc 100644 --- a/staging/src/k8s.io/apiserver/Godeps/Godeps.json +++ b/staging/src/k8s.io/apiserver/Godeps/Godeps.json @@ -430,14 +430,6 @@ "ImportPath": "github.com/gophercloud/gophercloud/pagination", "Rev": "ed590d9afe113c6107cd60717b196155e6579e78" }, - { - "ImportPath": "github.com/gregjones/httpcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, - { - "ImportPath": "github.com/gregjones/httpcache/diskcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, { "ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus", "Rev": "2500245aa6110c562d17020fb31a2c133d737799" @@ -506,10 +498,6 @@ "ImportPath": "github.com/pborman/uuid", "Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4" }, - { - "ImportPath": "github.com/peterbourgon/diskv", - "Rev": "5dfcb07a075adbaaa4094cddfd160b1e1c77a043" - }, { "ImportPath": "github.com/pkg/errors", "Rev": "a22138067af1c4942683050411a841ade67fe1eb" diff --git a/staging/src/k8s.io/client-go/Godeps/Godeps.json b/staging/src/k8s.io/client-go/Godeps/Godeps.json index 328e0a168e2..c72a9d07672 100644 --- a/staging/src/k8s.io/client-go/Godeps/Godeps.json +++ b/staging/src/k8s.io/client-go/Godeps/Godeps.json @@ -154,10 +154,6 @@ "ImportPath": "github.com/golang/protobuf/ptypes/timestamp", "Rev": "4bd1920723d7b7c925de087aa32e2187708897f7" }, - { - "ImportPath": "github.com/google/btree", - "Rev": "7d79101e329e5a3adf994758c578dab82b90c017" - }, { "ImportPath": "github.com/google/gofuzz", "Rev": "44d81051d367757e1c7c6a5a86423ece9afcf63c" @@ -202,14 +198,6 @@ "ImportPath": "github.com/gophercloud/gophercloud/pagination", "Rev": "ed590d9afe113c6107cd60717b196155e6579e78" }, - { - "ImportPath": "github.com/gregjones/httpcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, - { - "ImportPath": "github.com/gregjones/httpcache/diskcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, { "ImportPath": "github.com/hashicorp/golang-lru", "Rev": "a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4" @@ -246,10 +234,6 @@ "ImportPath": "github.com/mailru/easyjson/jwriter", "Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0" }, - { - "ImportPath": "github.com/peterbourgon/diskv", - "Rev": "5dfcb07a075adbaaa4094cddfd160b1e1c77a043" - }, { "ImportPath": "github.com/pmezard/go-difflib/difflib", "Rev": "d8ed2627bdf02c080bf22230dbb337003b7aba2d" diff --git a/staging/src/k8s.io/client-go/rest/config.go b/staging/src/k8s.io/client-go/rest/config.go index 627a9cc9672..dca7333dd00 100644 --- a/staging/src/k8s.io/client-go/rest/config.go +++ b/staging/src/k8s.io/client-go/rest/config.go @@ -71,10 +71,6 @@ type Config struct { // TODO: demonstrate an OAuth2 compatible client. BearerToken string - // CacheDir is the directory where we'll store HTTP cached responses. - // If set to empty string, no caching mechanism will be used. - CacheDir string - // Impersonate is the configuration that RESTClient will use for impersonation. Impersonate ImpersonationConfig diff --git a/staging/src/k8s.io/client-go/rest/config_test.go b/staging/src/k8s.io/client-go/rest/config_test.go index f20ed722dd6..f04135a4288 100644 --- a/staging/src/k8s.io/client-go/rest/config_test.go +++ b/staging/src/k8s.io/client-go/rest/config_test.go @@ -249,7 +249,6 @@ func TestAnonymousConfig(t *testing.T) { expected.BearerToken = "" expected.Username = "" expected.Password = "" - expected.CacheDir = "" expected.AuthProvider = nil expected.AuthConfigPersister = nil expected.TLSClientConfig.CertData = nil diff --git a/staging/src/k8s.io/client-go/rest/transport.go b/staging/src/k8s.io/client-go/rest/transport.go index 4c5b1648e96..ba43752bc93 100644 --- a/staging/src/k8s.io/client-go/rest/transport.go +++ b/staging/src/k8s.io/client-go/rest/transport.go @@ -89,7 +89,6 @@ func (c *Config) TransportConfig() (*transport.Config, error) { }, Username: c.Username, Password: c.Password, - CacheDir: c.CacheDir, BearerToken: c.BearerToken, Impersonate: transport.ImpersonationConfig{ UserName: c.Impersonate.UserName, diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go b/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go index 9646c6b7c24..a8698af2432 100644 --- a/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go +++ b/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go @@ -22,7 +22,6 @@ import ( "io/ioutil" "net/url" "os" - "path/filepath" "strings" "github.com/golang/glog" @@ -32,19 +31,16 @@ import ( restclient "k8s.io/client-go/rest" clientauth "k8s.io/client-go/tools/auth" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - "k8s.io/client-go/util/homedir" ) var ( // ClusterDefaults has the same behavior as the old EnvVar and DefaultCluster fields // DEPRECATED will be replaced ClusterDefaults = clientcmdapi.Cluster{Server: getDefaultServer()} - cacheDirDefault = filepath.Join(homedir.HomeDir(), ".kube", "http-cache") // DefaultClientConfig represents the legacy behavior of this package for defaulting // DEPRECATED will be replace DefaultClientConfig = DirectClientConfig{*clientcmdapi.NewConfig(), "", &ConfigOverrides{ ClusterDefaults: ClusterDefaults, - CacheDir: cacheDirDefault, }, nil, NewDefaultClientConfigLoadingRules(), promptedCredentials{}} ) @@ -135,7 +131,6 @@ func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) { clientConfig := &restclient.Config{} clientConfig.Host = configClusterInfo.Server - clientConfig.CacheDir = config.overrides.CacheDir if len(config.overrides.Timeout) > 0 { timeout, err := ParseTimeout(config.overrides.Timeout) diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go b/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go index 25ab1ea1aa6..963ac8fae9b 100644 --- a/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go +++ b/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go @@ -17,13 +17,11 @@ limitations under the License. package clientcmd import ( - "path/filepath" "strconv" "github.com/spf13/pflag" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - "k8s.io/client-go/util/homedir" ) // ConfigOverrides holds values that should override whatever information is pulled from the actual Config object. You can't @@ -36,7 +34,6 @@ type ConfigOverrides struct { Context clientcmdapi.Context CurrentContext string Timeout string - CacheDir string } // ConfigOverrideFlags holds the flag names to be used for binding command line flags. Notice that this structure tightly @@ -47,7 +44,6 @@ type ConfigOverrideFlags struct { ContextOverrideFlags ContextOverrideFlags CurrentContext FlagInfo Timeout FlagInfo - CacheDir FlagInfo } // AuthOverrideFlags holds the flag names to be used for binding command line flags for AuthInfo objects @@ -150,12 +146,10 @@ const ( FlagUsername = "username" FlagPassword = "password" FlagTimeout = "request-timeout" - FlagCacheDir = "cachedir" ) // RecommendedConfigOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing func RecommendedConfigOverrideFlags(prefix string) ConfigOverrideFlags { - defaultCacheDir := filepath.Join(homedir.HomeDir(), ".kube", "http-cache") return ConfigOverrideFlags{ AuthOverrideFlags: RecommendedAuthOverrideFlags(prefix), ClusterOverrideFlags: RecommendedClusterOverrideFlags(prefix), @@ -163,7 +157,6 @@ func RecommendedConfigOverrideFlags(prefix string) ConfigOverrideFlags { CurrentContext: FlagInfo{prefix + FlagContext, "", "", "The name of the kubeconfig context to use"}, Timeout: FlagInfo{prefix + FlagTimeout, "", "0", "The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests."}, - CacheDir: FlagInfo{prefix + FlagCacheDir, "", defaultCacheDir, "Path to http-cache directory"}, } } @@ -205,7 +198,6 @@ func BindOverrideFlags(overrides *ConfigOverrides, flags *pflag.FlagSet, flagNam BindContextFlags(&overrides.Context, flags, flagNames.ContextOverrideFlags) flagNames.CurrentContext.BindStringFlag(flags, &overrides.CurrentContext) flagNames.Timeout.BindStringFlag(flags, &overrides.Timeout) - flagNames.CacheDir.BindStringFlag(flags, &overrides.CacheDir) } // BindAuthInfoFlags is a convenience method to bind the specified flags to their associated variables diff --git a/staging/src/k8s.io/client-go/transport/BUILD b/staging/src/k8s.io/client-go/transport/BUILD index c17299d5355..1a54f4835e9 100644 --- a/staging/src/k8s.io/client-go/transport/BUILD +++ b/staging/src/k8s.io/client-go/transport/BUILD @@ -30,9 +30,6 @@ go_library( tags = ["automanaged"], deps = [ "//vendor/github.com/golang/glog:go_default_library", - "//vendor/github.com/gregjones/httpcache:go_default_library", - "//vendor/github.com/gregjones/httpcache/diskcache:go_default_library", - "//vendor/github.com/peterbourgon/diskv:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library", ], ) diff --git a/staging/src/k8s.io/client-go/transport/config.go b/staging/src/k8s.io/client-go/transport/config.go index e34d6e8c774..820594ba354 100644 --- a/staging/src/k8s.io/client-go/transport/config.go +++ b/staging/src/k8s.io/client-go/transport/config.go @@ -34,10 +34,6 @@ type Config struct { // Bearer token for authentication BearerToken string - // CacheDir is the directory where we'll store HTTP cached responses. - // If set to empty string, no caching mechanism will be used. - CacheDir string - // Impersonate is the config that this Config will impersonate using Impersonate ImpersonationConfig diff --git a/staging/src/k8s.io/client-go/transport/round_trippers.go b/staging/src/k8s.io/client-go/transport/round_trippers.go index 2394c42c9b0..c728b18775f 100644 --- a/staging/src/k8s.io/client-go/transport/round_trippers.go +++ b/staging/src/k8s.io/client-go/transport/round_trippers.go @@ -23,9 +23,6 @@ import ( "time" "github.com/golang/glog" - "github.com/gregjones/httpcache" - "github.com/gregjones/httpcache/diskcache" - "github.com/peterbourgon/diskv" utilnet "k8s.io/apimachinery/pkg/util/net" ) @@ -59,9 +56,6 @@ func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTrip len(config.Impersonate.Extra) > 0 { rt = NewImpersonatingRoundTripper(config.Impersonate, rt) } - if len(config.CacheDir) > 0 { - rt = NewCacheRoundTripper(config.CacheDir, rt) - } return rt, nil } @@ -93,17 +87,6 @@ type authProxyRoundTripper struct { rt http.RoundTripper } -// NewCacheRoundTripper creates a roundtripper that reads the ETag on -// response headers and send the If-None-Match header on subsequent -// corresponding requests. -func NewCacheRoundTripper(cacheDir string, rt http.RoundTripper) http.RoundTripper { - d := diskv.New(diskv.Options{BasePath: cacheDir}) - t := httpcache.NewTransport(diskcache.NewWithDiskv(d)) - t.Transport = rt - - return t -} - // NewAuthProxyRoundTripper provides a roundtripper which will add auth proxy fields to requests for // authentication terminating proxy cases // assuming you pull the user from the context: diff --git a/staging/src/k8s.io/client-go/transport/round_trippers_test.go b/staging/src/k8s.io/client-go/transport/round_trippers_test.go index c1e30c3f208..d5ffc6bde30 100644 --- a/staging/src/k8s.io/client-go/transport/round_trippers_test.go +++ b/staging/src/k8s.io/client-go/transport/round_trippers_test.go @@ -17,11 +17,7 @@ limitations under the License. package transport import ( - "bytes" - "io/ioutil" "net/http" - "net/url" - "os" "reflect" "strings" "testing" @@ -220,60 +216,3 @@ func TestAuthProxyRoundTripper(t *testing.T) { } } } - -func TestCacheRoundTripper(t *testing.T) { - rt := &testRoundTripper{} - cacheDir, err := ioutil.TempDir("", "cache-rt") - defer os.RemoveAll(cacheDir) - if err != nil { - t.Fatal(err) - } - cache := NewCacheRoundTripper(cacheDir, rt) - - // First call, caches the response - req := &http.Request{ - Method: http.MethodGet, - URL: &url.URL{Host: "localhost"}, - } - rt.Response = &http.Response{ - Header: http.Header{"ETag": []string{`"123456"`}}, - Body: ioutil.NopCloser(bytes.NewReader([]byte("Content"))), - StatusCode: http.StatusOK, - } - resp, err := cache.RoundTrip(req) - if err != nil { - t.Fatal(err) - } - content, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatal(err) - } - if string(content) != "Content" { - t.Errorf(`Expected Body to be "Content", got %q`, string(content)) - } - - // Second call, returns cached response - req = &http.Request{ - Method: http.MethodGet, - URL: &url.URL{Host: "localhost"}, - } - rt.Response = &http.Response{ - StatusCode: http.StatusNotModified, - Body: ioutil.NopCloser(bytes.NewReader([]byte("Other Content"))), - } - - resp, err = cache.RoundTrip(req) - if err != nil { - t.Fatal(err) - } - - // Read body and make sure we have the initial content - content, err = ioutil.ReadAll(resp.Body) - resp.Body.Close() - if err != nil { - t.Fatal(err) - } - if string(content) != "Content" { - t.Errorf("Invalid content read from cache %q", string(content)) - } -} diff --git a/staging/src/k8s.io/kube-aggregator/Godeps/Godeps.json b/staging/src/k8s.io/kube-aggregator/Godeps/Godeps.json index dbd7ff6b25c..46cff8e267b 100644 --- a/staging/src/k8s.io/kube-aggregator/Godeps/Godeps.json +++ b/staging/src/k8s.io/kube-aggregator/Godeps/Godeps.json @@ -170,10 +170,6 @@ "ImportPath": "github.com/golang/protobuf/ptypes/timestamp", "Rev": "4bd1920723d7b7c925de087aa32e2187708897f7" }, - { - "ImportPath": "github.com/google/btree", - "Rev": "7d79101e329e5a3adf994758c578dab82b90c017" - }, { "ImportPath": "github.com/google/gofuzz", "Rev": "44d81051d367757e1c7c6a5a86423ece9afcf63c" @@ -190,14 +186,6 @@ "ImportPath": "github.com/googleapis/gnostic/extensions", "Rev": "0c5108395e2debce0d731cf0287ddf7242066aba" }, - { - "ImportPath": "github.com/gregjones/httpcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, - { - "ImportPath": "github.com/gregjones/httpcache/diskcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, { "ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus", "Rev": "2500245aa6110c562d17020fb31a2c133d737799" @@ -262,10 +250,6 @@ "ImportPath": "github.com/pborman/uuid", "Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4" }, - { - "ImportPath": "github.com/peterbourgon/diskv", - "Rev": "5dfcb07a075adbaaa4094cddfd160b1e1c77a043" - }, { "ImportPath": "github.com/pkg/errors", "Rev": "a22138067af1c4942683050411a841ade67fe1eb" diff --git a/staging/src/k8s.io/kube-gen/Godeps/Godeps.json b/staging/src/k8s.io/kube-gen/Godeps/Godeps.json index acb30da54fa..3e5428dd33f 100644 --- a/staging/src/k8s.io/kube-gen/Godeps/Godeps.json +++ b/staging/src/k8s.io/kube-gen/Godeps/Godeps.json @@ -174,10 +174,6 @@ "ImportPath": "github.com/golang/protobuf/ptypes/timestamp", "Rev": "4bd1920723d7b7c925de087aa32e2187708897f7" }, - { - "ImportPath": "github.com/google/btree", - "Rev": "7d79101e329e5a3adf994758c578dab82b90c017" - }, { "ImportPath": "github.com/google/gofuzz", "Rev": "44d81051d367757e1c7c6a5a86423ece9afcf63c" @@ -194,14 +190,6 @@ "ImportPath": "github.com/googleapis/gnostic/extensions", "Rev": "0c5108395e2debce0d731cf0287ddf7242066aba" }, - { - "ImportPath": "github.com/gregjones/httpcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, - { - "ImportPath": "github.com/gregjones/httpcache/diskcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, { "ImportPath": "github.com/hashicorp/golang-lru", "Rev": "a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4" @@ -226,10 +214,6 @@ "ImportPath": "github.com/mailru/easyjson/jwriter", "Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0" }, - { - "ImportPath": "github.com/peterbourgon/diskv", - "Rev": "5dfcb07a075adbaaa4094cddfd160b1e1c77a043" - }, { "ImportPath": "github.com/spf13/pflag", "Rev": "9ff6c6923cfffbcd502984b8e0c80539a94968b7" diff --git a/staging/src/k8s.io/metrics/Godeps/Godeps.json b/staging/src/k8s.io/metrics/Godeps/Godeps.json index 06f902d2542..84161570f62 100644 --- a/staging/src/k8s.io/metrics/Godeps/Godeps.json +++ b/staging/src/k8s.io/metrics/Godeps/Godeps.json @@ -82,10 +82,6 @@ "ImportPath": "github.com/golang/protobuf/ptypes/timestamp", "Rev": "4bd1920723d7b7c925de087aa32e2187708897f7" }, - { - "ImportPath": "github.com/google/btree", - "Rev": "7d79101e329e5a3adf994758c578dab82b90c017" - }, { "ImportPath": "github.com/google/gofuzz", "Rev": "44d81051d367757e1c7c6a5a86423ece9afcf63c" @@ -102,14 +98,6 @@ "ImportPath": "github.com/googleapis/gnostic/extensions", "Rev": "0c5108395e2debce0d731cf0287ddf7242066aba" }, - { - "ImportPath": "github.com/gregjones/httpcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, - { - "ImportPath": "github.com/gregjones/httpcache/diskcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, { "ImportPath": "github.com/juju/ratelimit", "Rev": "5b9ff866471762aa2ab2dced63c9fb6f53921342" @@ -126,10 +114,6 @@ "ImportPath": "github.com/mailru/easyjson/jwriter", "Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0" }, - { - "ImportPath": "github.com/peterbourgon/diskv", - "Rev": "5dfcb07a075adbaaa4094cddfd160b1e1c77a043" - }, { "ImportPath": "github.com/spf13/pflag", "Rev": "9ff6c6923cfffbcd502984b8e0c80539a94968b7" diff --git a/staging/src/k8s.io/sample-apiserver/Godeps/Godeps.json b/staging/src/k8s.io/sample-apiserver/Godeps/Godeps.json index da0397c8228..85d28883145 100644 --- a/staging/src/k8s.io/sample-apiserver/Godeps/Godeps.json +++ b/staging/src/k8s.io/sample-apiserver/Godeps/Godeps.json @@ -162,10 +162,6 @@ "ImportPath": "github.com/golang/protobuf/ptypes/timestamp", "Rev": "4bd1920723d7b7c925de087aa32e2187708897f7" }, - { - "ImportPath": "github.com/google/btree", - "Rev": "7d79101e329e5a3adf994758c578dab82b90c017" - }, { "ImportPath": "github.com/google/gofuzz", "Rev": "44d81051d367757e1c7c6a5a86423ece9afcf63c" @@ -182,14 +178,6 @@ "ImportPath": "github.com/googleapis/gnostic/extensions", "Rev": "0c5108395e2debce0d731cf0287ddf7242066aba" }, - { - "ImportPath": "github.com/gregjones/httpcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, - { - "ImportPath": "github.com/gregjones/httpcache/diskcache", - "Rev": "787624de3eb7bd915c329cba748687a3b22666a6" - }, { "ImportPath": "github.com/grpc-ecosystem/go-grpc-prometheus", "Rev": "2500245aa6110c562d17020fb31a2c133d737799" @@ -254,10 +242,6 @@ "ImportPath": "github.com/pborman/uuid", "Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4" }, - { - "ImportPath": "github.com/peterbourgon/diskv", - "Rev": "5dfcb07a075adbaaa4094cddfd160b1e1c77a043" - }, { "ImportPath": "github.com/pkg/errors", "Rev": "a22138067af1c4942683050411a841ade67fe1eb" diff --git a/vendor/BUILD b/vendor/BUILD index 91e67487faa..9ca062889c5 100644 --- a/vendor/BUILD +++ b/vendor/BUILD @@ -230,7 +230,6 @@ filegroup( "//vendor/github.com/gorilla/context:all-srcs", "//vendor/github.com/gorilla/mux:all-srcs", "//vendor/github.com/gorilla/websocket:all-srcs", - "//vendor/github.com/gregjones/httpcache:all-srcs", "//vendor/github.com/grpc-ecosystem/go-grpc-prometheus:all-srcs", "//vendor/github.com/grpc-ecosystem/grpc-gateway/runtime:all-srcs", "//vendor/github.com/grpc-ecosystem/grpc-gateway/utilities:all-srcs", @@ -277,7 +276,6 @@ filegroup( "//vendor/github.com/pborman/uuid:all-srcs", "//vendor/github.com/pelletier/go-buffruneio:all-srcs", "//vendor/github.com/pelletier/go-toml:all-srcs", - "//vendor/github.com/peterbourgon/diskv:all-srcs", "//vendor/github.com/pkg/errors:all-srcs", "//vendor/github.com/pkg/sftp:all-srcs", "//vendor/github.com/pmezard/go-difflib/difflib:all-srcs", diff --git a/vendor/github.com/gregjones/httpcache/.travis.yml b/vendor/github.com/gregjones/httpcache/.travis.yml deleted file mode 100644 index 2bca4c599fd..00000000000 --- a/vendor/github.com/gregjones/httpcache/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -sudo: false -language: go -go: - - 1.6.x - - 1.7.x - - 1.8.x - - master -matrix: - allow_failures: - - go: master - fast_finish: true -install: - - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). -script: - - go get -t -v ./... - - diff -u <(echo -n) <(gofmt -d .) - - go tool vet . - - go test -v -race ./... diff --git a/vendor/github.com/gregjones/httpcache/BUILD b/vendor/github.com/gregjones/httpcache/BUILD deleted file mode 100644 index a1aa088b72e..00000000000 --- a/vendor/github.com/gregjones/httpcache/BUILD +++ /dev/null @@ -1,30 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", -) - -go_library( - name = "go_default_library", - srcs = ["httpcache.go"], - tags = ["automanaged"], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [ - ":package-srcs", - "//vendor/github.com/gregjones/httpcache/diskcache:all-srcs", - ], - tags = ["automanaged"], -) diff --git a/vendor/github.com/gregjones/httpcache/LICENSE.txt b/vendor/github.com/gregjones/httpcache/LICENSE.txt deleted file mode 100644 index 81316beb0cb..00000000000 --- a/vendor/github.com/gregjones/httpcache/LICENSE.txt +++ /dev/null @@ -1,7 +0,0 @@ -Copyright © 2012 Greg Jones (greg.jones@gmail.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/gregjones/httpcache/README.md b/vendor/github.com/gregjones/httpcache/README.md deleted file mode 100644 index 61bd830e57c..00000000000 --- a/vendor/github.com/gregjones/httpcache/README.md +++ /dev/null @@ -1,24 +0,0 @@ -httpcache -========= - -[![Build Status](https://travis-ci.org/gregjones/httpcache.svg?branch=master)](https://travis-ci.org/gregjones/httpcache) [![GoDoc](https://godoc.org/github.com/gregjones/httpcache?status.svg)](https://godoc.org/github.com/gregjones/httpcache) - -Package httpcache provides a http.RoundTripper implementation that works as a mostly RFC-compliant cache for http responses. - -It is only suitable for use as a 'private' cache (i.e. for a web-browser or an API-client and not for a shared proxy). - -Cache Backends --------------- - -- The built-in 'memory' cache stores responses in an in-memory map. -- [`github.com/gregjones/httpcache/diskcache`](https://github.com/gregjones/httpcache/tree/master/diskcache) provides a filesystem-backed cache using the [diskv](https://github.com/peterbourgon/diskv) library. -- [`github.com/gregjones/httpcache/memcache`](https://github.com/gregjones/httpcache/tree/master/memcache) provides memcache implementations, for both App Engine and 'normal' memcache servers. -- [`sourcegraph.com/sourcegraph/s3cache`](https://sourcegraph.com/github.com/sourcegraph/s3cache) uses Amazon S3 for storage. -- [`github.com/gregjones/httpcache/leveldbcache`](https://github.com/gregjones/httpcache/tree/master/leveldbcache) provides a filesystem-backed cache using [leveldb](https://github.com/syndtr/goleveldb/leveldb). -- [`github.com/die-net/lrucache`](https://github.com/die-net/lrucache) provides an in-memory cache that will evict least-recently used entries. -- [`github.com/die-net/lrucache/twotier`](https://github.com/die-net/lrucache/tree/master/twotier) allows caches to be combined, for example to use lrucache above with a persistent disk-cache. - -License -------- - -- [MIT License](LICENSE.txt) diff --git a/vendor/github.com/gregjones/httpcache/diskcache/BUILD b/vendor/github.com/gregjones/httpcache/diskcache/BUILD deleted file mode 100644 index e7580ec7077..00000000000 --- a/vendor/github.com/gregjones/httpcache/diskcache/BUILD +++ /dev/null @@ -1,28 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", -) - -go_library( - name = "go_default_library", - srcs = ["diskcache.go"], - tags = ["automanaged"], - deps = ["//vendor/github.com/peterbourgon/diskv:go_default_library"], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/vendor/github.com/gregjones/httpcache/diskcache/diskcache.go b/vendor/github.com/gregjones/httpcache/diskcache/diskcache.go deleted file mode 100644 index 42e3129d823..00000000000 --- a/vendor/github.com/gregjones/httpcache/diskcache/diskcache.go +++ /dev/null @@ -1,61 +0,0 @@ -// Package diskcache provides an implementation of httpcache.Cache that uses the diskv package -// to supplement an in-memory map with persistent storage -// -package diskcache - -import ( - "bytes" - "crypto/md5" - "encoding/hex" - "github.com/peterbourgon/diskv" - "io" -) - -// Cache is an implementation of httpcache.Cache that supplements the in-memory map with persistent storage -type Cache struct { - d *diskv.Diskv -} - -// Get returns the response corresponding to key if present -func (c *Cache) Get(key string) (resp []byte, ok bool) { - key = keyToFilename(key) - resp, err := c.d.Read(key) - if err != nil { - return []byte{}, false - } - return resp, true -} - -// Set saves a response to the cache as key -func (c *Cache) Set(key string, resp []byte) { - key = keyToFilename(key) - c.d.WriteStream(key, bytes.NewReader(resp), true) -} - -// Delete removes the response with key from the cache -func (c *Cache) Delete(key string) { - key = keyToFilename(key) - c.d.Erase(key) -} - -func keyToFilename(key string) string { - h := md5.New() - io.WriteString(h, key) - return hex.EncodeToString(h.Sum(nil)) -} - -// New returns a new Cache that will store files in basePath -func New(basePath string) *Cache { - return &Cache{ - d: diskv.New(diskv.Options{ - BasePath: basePath, - CacheSizeMax: 100 * 1024 * 1024, // 100MB - }), - } -} - -// NewWithDiskv returns a new Cache using the provided Diskv as underlying -// storage. -func NewWithDiskv(d *diskv.Diskv) *Cache { - return &Cache{d} -} diff --git a/vendor/github.com/gregjones/httpcache/httpcache.go b/vendor/github.com/gregjones/httpcache/httpcache.go deleted file mode 100644 index 8239edc2cb2..00000000000 --- a/vendor/github.com/gregjones/httpcache/httpcache.go +++ /dev/null @@ -1,553 +0,0 @@ -// Package httpcache provides a http.RoundTripper implementation that works as a -// mostly RFC-compliant cache for http responses. -// -// It is only suitable for use as a 'private' cache (i.e. for a web-browser or an API-client -// and not for a shared proxy). -// -package httpcache - -import ( - "bufio" - "bytes" - "errors" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/http/httputil" - "strings" - "sync" - "time" -) - -const ( - stale = iota - fresh - transparent - // XFromCache is the header added to responses that are returned from the cache - XFromCache = "X-From-Cache" -) - -// A Cache interface is used by the Transport to store and retrieve responses. -type Cache interface { - // Get returns the []byte representation of a cached response and a bool - // set to true if the value isn't empty - Get(key string) (responseBytes []byte, ok bool) - // Set stores the []byte representation of a response against a key - Set(key string, responseBytes []byte) - // Delete removes the value associated with the key - Delete(key string) -} - -// cacheKey returns the cache key for req. -func cacheKey(req *http.Request) string { - return req.URL.String() -} - -// CachedResponse returns the cached http.Response for req if present, and nil -// otherwise. -func CachedResponse(c Cache, req *http.Request) (resp *http.Response, err error) { - cachedVal, ok := c.Get(cacheKey(req)) - if !ok { - return - } - - b := bytes.NewBuffer(cachedVal) - return http.ReadResponse(bufio.NewReader(b), req) -} - -// MemoryCache is an implemtation of Cache that stores responses in an in-memory map. -type MemoryCache struct { - mu sync.RWMutex - items map[string][]byte -} - -// Get returns the []byte representation of the response and true if present, false if not -func (c *MemoryCache) Get(key string) (resp []byte, ok bool) { - c.mu.RLock() - resp, ok = c.items[key] - c.mu.RUnlock() - return resp, ok -} - -// Set saves response resp to the cache with key -func (c *MemoryCache) Set(key string, resp []byte) { - c.mu.Lock() - c.items[key] = resp - c.mu.Unlock() -} - -// Delete removes key from the cache -func (c *MemoryCache) Delete(key string) { - c.mu.Lock() - delete(c.items, key) - c.mu.Unlock() -} - -// NewMemoryCache returns a new Cache that will store items in an in-memory map -func NewMemoryCache() *MemoryCache { - c := &MemoryCache{items: map[string][]byte{}} - return c -} - -// Transport is an implementation of http.RoundTripper that will return values from a cache -// where possible (avoiding a network request) and will additionally add validators (etag/if-modified-since) -// to repeated requests allowing servers to return 304 / Not Modified -type Transport struct { - // The RoundTripper interface actually used to make requests - // If nil, http.DefaultTransport is used - Transport http.RoundTripper - Cache Cache - // If true, responses returned from the cache will be given an extra header, X-From-Cache - MarkCachedResponses bool -} - -// NewTransport returns a new Transport with the -// provided Cache implementation and MarkCachedResponses set to true -func NewTransport(c Cache) *Transport { - return &Transport{Cache: c, MarkCachedResponses: true} -} - -// Client returns an *http.Client that caches responses. -func (t *Transport) Client() *http.Client { - return &http.Client{Transport: t} -} - -// varyMatches will return false unless all of the cached values for the headers listed in Vary -// match the new request -func varyMatches(cachedResp *http.Response, req *http.Request) bool { - for _, header := range headerAllCommaSepValues(cachedResp.Header, "vary") { - header = http.CanonicalHeaderKey(header) - if header != "" && req.Header.Get(header) != cachedResp.Header.Get("X-Varied-"+header) { - return false - } - } - return true -} - -// RoundTrip takes a Request and returns a Response -// -// If there is a fresh Response already in cache, then it will be returned without connecting to -// the server. -// -// If there is a stale Response, then any validators it contains will be set on the new request -// to give the server a chance to respond with NotModified. If this happens, then the cached Response -// will be returned. -func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) { - cacheKey := cacheKey(req) - cacheable := (req.Method == "GET" || req.Method == "HEAD") && req.Header.Get("range") == "" - var cachedResp *http.Response - if cacheable { - cachedResp, err = CachedResponse(t.Cache, req) - } else { - // Need to invalidate an existing value - t.Cache.Delete(cacheKey) - } - - transport := t.Transport - if transport == nil { - transport = http.DefaultTransport - } - - if cacheable && cachedResp != nil && err == nil { - if t.MarkCachedResponses { - cachedResp.Header.Set(XFromCache, "1") - } - - if varyMatches(cachedResp, req) { - // Can only use cached value if the new request doesn't Vary significantly - freshness := getFreshness(cachedResp.Header, req.Header) - if freshness == fresh { - return cachedResp, nil - } - - if freshness == stale { - var req2 *http.Request - // Add validators if caller hasn't already done so - etag := cachedResp.Header.Get("etag") - if etag != "" && req.Header.Get("etag") == "" { - req2 = cloneRequest(req) - req2.Header.Set("if-none-match", etag) - } - lastModified := cachedResp.Header.Get("last-modified") - if lastModified != "" && req.Header.Get("last-modified") == "" { - if req2 == nil { - req2 = cloneRequest(req) - } - req2.Header.Set("if-modified-since", lastModified) - } - if req2 != nil { - req = req2 - } - } - } - - resp, err = transport.RoundTrip(req) - if err == nil && req.Method == "GET" && resp.StatusCode == http.StatusNotModified { - // Replace the 304 response with the one from cache, but update with some new headers - endToEndHeaders := getEndToEndHeaders(resp.Header) - for _, header := range endToEndHeaders { - cachedResp.Header[header] = resp.Header[header] - } - cachedResp.Status = fmt.Sprintf("%d %s", http.StatusOK, http.StatusText(http.StatusOK)) - cachedResp.StatusCode = http.StatusOK - - resp = cachedResp - } else if (err != nil || (cachedResp != nil && resp.StatusCode >= 500)) && - req.Method == "GET" && canStaleOnError(cachedResp.Header, req.Header) { - // In case of transport failure and stale-if-error activated, returns cached content - // when available - cachedResp.Status = fmt.Sprintf("%d %s", http.StatusOK, http.StatusText(http.StatusOK)) - cachedResp.StatusCode = http.StatusOK - return cachedResp, nil - } else { - if err != nil || resp.StatusCode != http.StatusOK { - t.Cache.Delete(cacheKey) - } - if err != nil { - return nil, err - } - } - } else { - reqCacheControl := parseCacheControl(req.Header) - if _, ok := reqCacheControl["only-if-cached"]; ok { - resp = newGatewayTimeoutResponse(req) - } else { - resp, err = transport.RoundTrip(req) - if err != nil { - return nil, err - } - } - } - - if cacheable && canStore(parseCacheControl(req.Header), parseCacheControl(resp.Header)) { - for _, varyKey := range headerAllCommaSepValues(resp.Header, "vary") { - varyKey = http.CanonicalHeaderKey(varyKey) - fakeHeader := "X-Varied-" + varyKey - reqValue := req.Header.Get(varyKey) - if reqValue != "" { - resp.Header.Set(fakeHeader, reqValue) - } - } - switch req.Method { - case "GET": - // Delay caching until EOF is reached. - resp.Body = &cachingReadCloser{ - R: resp.Body, - OnEOF: func(r io.Reader) { - resp := *resp - resp.Body = ioutil.NopCloser(r) - respBytes, err := httputil.DumpResponse(&resp, true) - if err == nil { - t.Cache.Set(cacheKey, respBytes) - } - }, - } - default: - respBytes, err := httputil.DumpResponse(resp, true) - if err == nil { - t.Cache.Set(cacheKey, respBytes) - } - } - } else { - t.Cache.Delete(cacheKey) - } - return resp, nil -} - -// ErrNoDateHeader indicates that the HTTP headers contained no Date header. -var ErrNoDateHeader = errors.New("no Date header") - -// Date parses and returns the value of the Date header. -func Date(respHeaders http.Header) (date time.Time, err error) { - dateHeader := respHeaders.Get("date") - if dateHeader == "" { - err = ErrNoDateHeader - return - } - - return time.Parse(time.RFC1123, dateHeader) -} - -type realClock struct{} - -func (c *realClock) since(d time.Time) time.Duration { - return time.Since(d) -} - -type timer interface { - since(d time.Time) time.Duration -} - -var clock timer = &realClock{} - -// getFreshness will return one of fresh/stale/transparent based on the cache-control -// values of the request and the response -// -// fresh indicates the response can be returned -// stale indicates that the response needs validating before it is returned -// transparent indicates the response should not be used to fulfil the request -// -// Because this is only a private cache, 'public' and 'private' in cache-control aren't -// signficant. Similarly, smax-age isn't used. -func getFreshness(respHeaders, reqHeaders http.Header) (freshness int) { - respCacheControl := parseCacheControl(respHeaders) - reqCacheControl := parseCacheControl(reqHeaders) - if _, ok := reqCacheControl["no-cache"]; ok { - return transparent - } - if _, ok := respCacheControl["no-cache"]; ok { - return stale - } - if _, ok := reqCacheControl["only-if-cached"]; ok { - return fresh - } - - date, err := Date(respHeaders) - if err != nil { - return stale - } - currentAge := clock.since(date) - - var lifetime time.Duration - var zeroDuration time.Duration - - // If a response includes both an Expires header and a max-age directive, - // the max-age directive overrides the Expires header, even if the Expires header is more restrictive. - if maxAge, ok := respCacheControl["max-age"]; ok { - lifetime, err = time.ParseDuration(maxAge + "s") - if err != nil { - lifetime = zeroDuration - } - } else { - expiresHeader := respHeaders.Get("Expires") - if expiresHeader != "" { - expires, err := time.Parse(time.RFC1123, expiresHeader) - if err != nil { - lifetime = zeroDuration - } else { - lifetime = expires.Sub(date) - } - } - } - - if maxAge, ok := reqCacheControl["max-age"]; ok { - // the client is willing to accept a response whose age is no greater than the specified time in seconds - lifetime, err = time.ParseDuration(maxAge + "s") - if err != nil { - lifetime = zeroDuration - } - } - if minfresh, ok := reqCacheControl["min-fresh"]; ok { - // the client wants a response that will still be fresh for at least the specified number of seconds. - minfreshDuration, err := time.ParseDuration(minfresh + "s") - if err == nil { - currentAge = time.Duration(currentAge + minfreshDuration) - } - } - - if maxstale, ok := reqCacheControl["max-stale"]; ok { - // Indicates that the client is willing to accept a response that has exceeded its expiration time. - // If max-stale is assigned a value, then the client is willing to accept a response that has exceeded - // its expiration time by no more than the specified number of seconds. - // If no value is assigned to max-stale, then the client is willing to accept a stale response of any age. - // - // Responses served only because of a max-stale value are supposed to have a Warning header added to them, - // but that seems like a hassle, and is it actually useful? If so, then there needs to be a different - // return-value available here. - if maxstale == "" { - return fresh - } - maxstaleDuration, err := time.ParseDuration(maxstale + "s") - if err == nil { - currentAge = time.Duration(currentAge - maxstaleDuration) - } - } - - if lifetime > currentAge { - return fresh - } - - return stale -} - -// Returns true if either the request or the response includes the stale-if-error -// cache control extension: https://tools.ietf.org/html/rfc5861 -func canStaleOnError(respHeaders, reqHeaders http.Header) bool { - respCacheControl := parseCacheControl(respHeaders) - reqCacheControl := parseCacheControl(reqHeaders) - - var err error - lifetime := time.Duration(-1) - - if staleMaxAge, ok := respCacheControl["stale-if-error"]; ok { - if staleMaxAge != "" { - lifetime, err = time.ParseDuration(staleMaxAge + "s") - if err != nil { - return false - } - } else { - return true - } - } - if staleMaxAge, ok := reqCacheControl["stale-if-error"]; ok { - if staleMaxAge != "" { - lifetime, err = time.ParseDuration(staleMaxAge + "s") - if err != nil { - return false - } - } else { - return true - } - } - - if lifetime >= 0 { - date, err := Date(respHeaders) - if err != nil { - return false - } - currentAge := clock.since(date) - if lifetime > currentAge { - return true - } - } - - return false -} - -func getEndToEndHeaders(respHeaders http.Header) []string { - // These headers are always hop-by-hop - hopByHopHeaders := map[string]struct{}{ - "Connection": struct{}{}, - "Keep-Alive": struct{}{}, - "Proxy-Authenticate": struct{}{}, - "Proxy-Authorization": struct{}{}, - "Te": struct{}{}, - "Trailers": struct{}{}, - "Transfer-Encoding": struct{}{}, - "Upgrade": struct{}{}, - } - - for _, extra := range strings.Split(respHeaders.Get("connection"), ",") { - // any header listed in connection, if present, is also considered hop-by-hop - if strings.Trim(extra, " ") != "" { - hopByHopHeaders[http.CanonicalHeaderKey(extra)] = struct{}{} - } - } - endToEndHeaders := []string{} - for respHeader, _ := range respHeaders { - if _, ok := hopByHopHeaders[respHeader]; !ok { - endToEndHeaders = append(endToEndHeaders, respHeader) - } - } - return endToEndHeaders -} - -func canStore(reqCacheControl, respCacheControl cacheControl) (canStore bool) { - if _, ok := respCacheControl["no-store"]; ok { - return false - } - if _, ok := reqCacheControl["no-store"]; ok { - return false - } - return true -} - -func newGatewayTimeoutResponse(req *http.Request) *http.Response { - var braw bytes.Buffer - braw.WriteString("HTTP/1.1 504 Gateway Timeout\r\n\r\n") - resp, err := http.ReadResponse(bufio.NewReader(&braw), req) - if err != nil { - panic(err) - } - return resp -} - -// cloneRequest returns a clone of the provided *http.Request. -// The clone is a shallow copy of the struct and its Header map. -// (This function copyright goauth2 authors: https://code.google.com/p/goauth2) -func cloneRequest(r *http.Request) *http.Request { - // shallow copy of the struct - r2 := new(http.Request) - *r2 = *r - // deep copy of the Header - r2.Header = make(http.Header) - for k, s := range r.Header { - r2.Header[k] = s - } - return r2 -} - -type cacheControl map[string]string - -func parseCacheControl(headers http.Header) cacheControl { - cc := cacheControl{} - ccHeader := headers.Get("Cache-Control") - for _, part := range strings.Split(ccHeader, ",") { - part = strings.Trim(part, " ") - if part == "" { - continue - } - if strings.ContainsRune(part, '=') { - keyval := strings.Split(part, "=") - cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",") - } else { - cc[part] = "" - } - } - return cc -} - -// headerAllCommaSepValues returns all comma-separated values (each -// with whitespace trimmed) for header name in headers. According to -// Section 4.2 of the HTTP/1.1 spec -// (http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2), -// values from multiple occurrences of a header should be concatenated, if -// the header's value is a comma-separated list. -func headerAllCommaSepValues(headers http.Header, name string) []string { - var vals []string - for _, val := range headers[http.CanonicalHeaderKey(name)] { - fields := strings.Split(val, ",") - for i, f := range fields { - fields[i] = strings.TrimSpace(f) - } - vals = append(vals, fields...) - } - return vals -} - -// cachingReadCloser is a wrapper around ReadCloser R that calls OnEOF -// handler with a full copy of the content read from R when EOF is -// reached. -type cachingReadCloser struct { - // Underlying ReadCloser. - R io.ReadCloser - // OnEOF is called with a copy of the content of R when EOF is reached. - OnEOF func(io.Reader) - - buf bytes.Buffer // buf stores a copy of the content of R. -} - -// Read reads the next len(p) bytes from R or until R is drained. The -// return value n is the number of bytes read. If R has no data to -// return, err is io.EOF and OnEOF is called with a full copy of what -// has been read so far. -func (r *cachingReadCloser) Read(p []byte) (n int, err error) { - n, err = r.R.Read(p) - r.buf.Write(p[:n]) - if err == io.EOF { - r.OnEOF(bytes.NewReader(r.buf.Bytes())) - } - return n, err -} - -func (r *cachingReadCloser) Close() error { - return r.R.Close() -} - -// NewMemoryCacheTransport returns a new Transport using the in-memory cache implementation -func NewMemoryCacheTransport() *Transport { - c := NewMemoryCache() - t := NewTransport(c) - return t -} diff --git a/vendor/github.com/peterbourgon/diskv/BUILD b/vendor/github.com/peterbourgon/diskv/BUILD deleted file mode 100644 index fcda66010b7..00000000000 --- a/vendor/github.com/peterbourgon/diskv/BUILD +++ /dev/null @@ -1,32 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", -) - -go_library( - name = "go_default_library", - srcs = [ - "compression.go", - "diskv.go", - "index.go", - ], - tags = ["automanaged"], - deps = ["//vendor/github.com/google/btree:go_default_library"], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/vendor/github.com/peterbourgon/diskv/LICENSE b/vendor/github.com/peterbourgon/diskv/LICENSE deleted file mode 100644 index 41ce7f16e1d..00000000000 --- a/vendor/github.com/peterbourgon/diskv/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2011-2012 Peter Bourgon - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/github.com/peterbourgon/diskv/README.md b/vendor/github.com/peterbourgon/diskv/README.md deleted file mode 100644 index 3474739edc7..00000000000 --- a/vendor/github.com/peterbourgon/diskv/README.md +++ /dev/null @@ -1,141 +0,0 @@ -# What is diskv? - -Diskv (disk-vee) is a simple, persistent key-value store written in the Go -language. It starts with an incredibly simple API for storing arbitrary data on -a filesystem by key, and builds several layers of performance-enhancing -abstraction on top. The end result is a conceptually simple, but highly -performant, disk-backed storage system. - -[![Build Status][1]][2] - -[1]: https://drone.io/github.com/peterbourgon/diskv/status.png -[2]: https://drone.io/github.com/peterbourgon/diskv/latest - - -# Installing - -Install [Go 1][3], either [from source][4] or [with a prepackaged binary][5]. -Then, - -```bash -$ go get github.com/peterbourgon/diskv -``` - -[3]: http://golang.org -[4]: http://golang.org/doc/install/source -[5]: http://golang.org/doc/install - - -# Usage - -```go -package main - -import ( - "fmt" - "github.com/peterbourgon/diskv" -) - -func main() { - // Simplest transform function: put all the data files into the base dir. - flatTransform := func(s string) []string { return []string{} } - - // Initialize a new diskv store, rooted at "my-data-dir", with a 1MB cache. - d := diskv.New(diskv.Options{ - BasePath: "my-data-dir", - Transform: flatTransform, - CacheSizeMax: 1024 * 1024, - }) - - // Write three bytes to the key "alpha". - key := "alpha" - d.Write(key, []byte{'1', '2', '3'}) - - // Read the value back out of the store. - value, _ := d.Read(key) - fmt.Printf("%v\n", value) - - // Erase the key+value from the store (and the disk). - d.Erase(key) -} -``` - -More complex examples can be found in the "examples" subdirectory. - - -# Theory - -## Basic idea - -At its core, diskv is a map of a key (`string`) to arbitrary data (`[]byte`). -The data is written to a single file on disk, with the same name as the key. -The key determines where that file will be stored, via a user-provided -`TransformFunc`, which takes a key and returns a slice (`[]string`) -corresponding to a path list where the key file will be stored. The simplest -TransformFunc, - -```go -func SimpleTransform (key string) []string { - return []string{} -} -``` - -will place all keys in the same, base directory. The design is inspired by -[Redis diskstore][6]; a TransformFunc which emulates the default diskstore -behavior is available in the content-addressable-storage example. - -[6]: http://groups.google.com/group/redis-db/browse_thread/thread/d444bc786689bde9?pli=1 - -**Note** that your TransformFunc should ensure that one valid key doesn't -transform to a subset of another valid key. That is, it shouldn't be possible -to construct valid keys that resolve to directory names. As a concrete example, -if your TransformFunc splits on every 3 characters, then - -```go -d.Write("abcabc", val) // OK: written to /abc/abc/abcabc -d.Write("abc", val) // Error: attempted write to /abc/abc, but it's a directory -``` - -This will be addressed in an upcoming version of diskv. - -Probably the most important design principle behind diskv is that your data is -always flatly available on the disk. diskv will never do anything that would -prevent you from accessing, copying, backing up, or otherwise interacting with -your data via common UNIX commandline tools. - -## Adding a cache - -An in-memory caching layer is provided by combining the BasicStore -functionality with a simple map structure, and keeping it up-to-date as -appropriate. Since the map structure in Go is not threadsafe, it's combined -with a RWMutex to provide safe concurrent access. - -## Adding order - -diskv is a key-value store and therefore inherently unordered. An ordering -system can be injected into the store by passing something which satisfies the -diskv.Index interface. (A default implementation, using Google's -[btree][7] package, is provided.) Basically, diskv keeps an ordered (by a -user-provided Less function) index of the keys, which can be queried. - -[7]: https://github.com/google/btree - -## Adding compression - -Something which implements the diskv.Compression interface may be passed -during store creation, so that all Writes and Reads are filtered through -a compression/decompression pipeline. Several default implementations, -using stdlib compression algorithms, are provided. Note that data is cached -compressed; the cost of decompression is borne with each Read. - -## Streaming - -diskv also now provides ReadStream and WriteStream methods, to allow very large -data to be handled efficiently. - - -# Future plans - - * Needs plenty of robust testing: huge datasets, etc... - * More thorough benchmarking - * Your suggestions for use-cases I haven't thought of diff --git a/vendor/github.com/peterbourgon/diskv/compression.go b/vendor/github.com/peterbourgon/diskv/compression.go deleted file mode 100644 index 5192b027330..00000000000 --- a/vendor/github.com/peterbourgon/diskv/compression.go +++ /dev/null @@ -1,64 +0,0 @@ -package diskv - -import ( - "compress/flate" - "compress/gzip" - "compress/zlib" - "io" -) - -// Compression is an interface that Diskv uses to implement compression of -// data. Writer takes a destination io.Writer and returns a WriteCloser that -// compresses all data written through it. Reader takes a source io.Reader and -// returns a ReadCloser that decompresses all data read through it. You may -// define these methods on your own type, or use one of the NewCompression -// helpers. -type Compression interface { - Writer(dst io.Writer) (io.WriteCloser, error) - Reader(src io.Reader) (io.ReadCloser, error) -} - -// NewGzipCompression returns a Gzip-based Compression. -func NewGzipCompression() Compression { - return NewGzipCompressionLevel(flate.DefaultCompression) -} - -// NewGzipCompressionLevel returns a Gzip-based Compression with the given level. -func NewGzipCompressionLevel(level int) Compression { - return &genericCompression{ - wf: func(w io.Writer) (io.WriteCloser, error) { return gzip.NewWriterLevel(w, level) }, - rf: func(r io.Reader) (io.ReadCloser, error) { return gzip.NewReader(r) }, - } -} - -// NewZlibCompression returns a Zlib-based Compression. -func NewZlibCompression() Compression { - return NewZlibCompressionLevel(flate.DefaultCompression) -} - -// NewZlibCompressionLevel returns a Zlib-based Compression with the given level. -func NewZlibCompressionLevel(level int) Compression { - return NewZlibCompressionLevelDict(level, nil) -} - -// NewZlibCompressionLevelDict returns a Zlib-based Compression with the given -// level, based on the given dictionary. -func NewZlibCompressionLevelDict(level int, dict []byte) Compression { - return &genericCompression{ - func(w io.Writer) (io.WriteCloser, error) { return zlib.NewWriterLevelDict(w, level, dict) }, - func(r io.Reader) (io.ReadCloser, error) { return zlib.NewReaderDict(r, dict) }, - } -} - -type genericCompression struct { - wf func(w io.Writer) (io.WriteCloser, error) - rf func(r io.Reader) (io.ReadCloser, error) -} - -func (g *genericCompression) Writer(dst io.Writer) (io.WriteCloser, error) { - return g.wf(dst) -} - -func (g *genericCompression) Reader(src io.Reader) (io.ReadCloser, error) { - return g.rf(src) -} diff --git a/vendor/github.com/peterbourgon/diskv/diskv.go b/vendor/github.com/peterbourgon/diskv/diskv.go deleted file mode 100644 index ea05842cbd1..00000000000 --- a/vendor/github.com/peterbourgon/diskv/diskv.go +++ /dev/null @@ -1,578 +0,0 @@ -// Diskv (disk-vee) is a simple, persistent, key-value store. -// It stores all data flatly on the filesystem. - -package diskv - -import ( - "bytes" - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "sync" - "syscall" -) - -const ( - defaultBasePath = "diskv" - defaultFilePerm os.FileMode = 0666 - defaultPathPerm os.FileMode = 0777 -) - -var ( - defaultTransform = func(s string) []string { return []string{} } - errCanceled = errors.New("canceled") - errEmptyKey = errors.New("empty key") - errBadKey = errors.New("bad key") - errImportDirectory = errors.New("can't import a directory") -) - -// TransformFunction transforms a key into a slice of strings, with each -// element in the slice representing a directory in the file path where the -// key's entry will eventually be stored. -// -// For example, if TransformFunc transforms "abcdef" to ["ab", "cde", "f"], -// the final location of the data file will be /ab/cde/f/abcdef -type TransformFunction func(s string) []string - -// Options define a set of properties that dictate Diskv behavior. -// All values are optional. -type Options struct { - BasePath string - Transform TransformFunction - CacheSizeMax uint64 // bytes - PathPerm os.FileMode - FilePerm os.FileMode - - Index Index - IndexLess LessFunction - - Compression Compression -} - -// Diskv implements the Diskv interface. You shouldn't construct Diskv -// structures directly; instead, use the New constructor. -type Diskv struct { - Options - mu sync.RWMutex - cache map[string][]byte - cacheSize uint64 -} - -// New returns an initialized Diskv structure, ready to use. -// If the path identified by baseDir already contains data, -// it will be accessible, but not yet cached. -func New(o Options) *Diskv { - if o.BasePath == "" { - o.BasePath = defaultBasePath - } - if o.Transform == nil { - o.Transform = defaultTransform - } - if o.PathPerm == 0 { - o.PathPerm = defaultPathPerm - } - if o.FilePerm == 0 { - o.FilePerm = defaultFilePerm - } - - d := &Diskv{ - Options: o, - cache: map[string][]byte{}, - cacheSize: 0, - } - - if d.Index != nil && d.IndexLess != nil { - d.Index.Initialize(d.IndexLess, d.Keys(nil)) - } - - return d -} - -// Write synchronously writes the key-value pair to disk, making it immediately -// available for reads. Write relies on the filesystem to perform an eventual -// sync to physical media. If you need stronger guarantees, see WriteStream. -func (d *Diskv) Write(key string, val []byte) error { - return d.WriteStream(key, bytes.NewBuffer(val), false) -} - -// WriteStream writes the data represented by the io.Reader to the disk, under -// the provided key. If sync is true, WriteStream performs an explicit sync on -// the file as soon as it's written. -// -// bytes.Buffer provides io.Reader semantics for basic data types. -func (d *Diskv) WriteStream(key string, r io.Reader, sync bool) error { - if len(key) <= 0 { - return errEmptyKey - } - - d.mu.Lock() - defer d.mu.Unlock() - - return d.writeStreamWithLock(key, r, sync) -} - -// writeStream does no input validation checking. -// TODO: use atomic FS ops. -func (d *Diskv) writeStreamWithLock(key string, r io.Reader, sync bool) error { - if err := d.ensurePathWithLock(key); err != nil { - return fmt.Errorf("ensure path: %s", err) - } - - mode := os.O_WRONLY | os.O_CREATE | os.O_TRUNC // overwrite if exists - f, err := os.OpenFile(d.completeFilename(key), mode, d.FilePerm) - if err != nil { - return fmt.Errorf("open file: %s", err) - } - - wc := io.WriteCloser(&nopWriteCloser{f}) - if d.Compression != nil { - wc, err = d.Compression.Writer(f) - if err != nil { - f.Close() // error deliberately ignored - return fmt.Errorf("compression writer: %s", err) - } - } - - if _, err := io.Copy(wc, r); err != nil { - f.Close() // error deliberately ignored - return fmt.Errorf("i/o copy: %s", err) - } - - if err := wc.Close(); err != nil { - f.Close() // error deliberately ignored - return fmt.Errorf("compression close: %s", err) - } - - if sync { - if err := f.Sync(); err != nil { - f.Close() // error deliberately ignored - return fmt.Errorf("file sync: %s", err) - } - } - - if err := f.Close(); err != nil { - return fmt.Errorf("file close: %s", err) - } - - if d.Index != nil { - d.Index.Insert(key) - } - - d.bustCacheWithLock(key) // cache only on read - - return nil -} - -// Import imports the source file into diskv under the destination key. If the -// destination key already exists, it's overwritten. If move is true, the -// source file is removed after a successful import. -func (d *Diskv) Import(srcFilename, dstKey string, move bool) (err error) { - if dstKey == "" { - return errEmptyKey - } - - if fi, err := os.Stat(srcFilename); err != nil { - return err - } else if fi.IsDir() { - return errImportDirectory - } - - d.mu.Lock() - defer d.mu.Unlock() - - if err := d.ensurePathWithLock(dstKey); err != nil { - return fmt.Errorf("ensure path: %s", err) - } - - if move { - if err := syscall.Rename(srcFilename, d.completeFilename(dstKey)); err == nil { - d.bustCacheWithLock(dstKey) - return nil - } else if err != syscall.EXDEV { - // If it failed due to being on a different device, fall back to copying - return err - } - } - - f, err := os.Open(srcFilename) - if err != nil { - return err - } - defer f.Close() - err = d.writeStreamWithLock(dstKey, f, false) - if err == nil && move { - err = os.Remove(srcFilename) - } - return err -} - -// Read reads the key and returns the value. -// If the key is available in the cache, Read won't touch the disk. -// If the key is not in the cache, Read will have the side-effect of -// lazily caching the value. -func (d *Diskv) Read(key string) ([]byte, error) { - rc, err := d.ReadStream(key, false) - if err != nil { - return []byte{}, err - } - defer rc.Close() - return ioutil.ReadAll(rc) -} - -// ReadStream reads the key and returns the value (data) as an io.ReadCloser. -// If the value is cached from a previous read, and direct is false, -// ReadStream will use the cached value. Otherwise, it will return a handle to -// the file on disk, and cache the data on read. -// -// If direct is true, ReadStream will lazily delete any cached value for the -// key, and return a direct handle to the file on disk. -// -// If compression is enabled, ReadStream taps into the io.Reader stream prior -// to decompression, and caches the compressed data. -func (d *Diskv) ReadStream(key string, direct bool) (io.ReadCloser, error) { - d.mu.RLock() - defer d.mu.RUnlock() - - if val, ok := d.cache[key]; ok { - if !direct { - buf := bytes.NewBuffer(val) - if d.Compression != nil { - return d.Compression.Reader(buf) - } - return ioutil.NopCloser(buf), nil - } - - go func() { - d.mu.Lock() - defer d.mu.Unlock() - d.uncacheWithLock(key, uint64(len(val))) - }() - } - - return d.readWithRLock(key) -} - -// read ignores the cache, and returns an io.ReadCloser representing the -// decompressed data for the given key, streamed from the disk. Clients should -// acquire a read lock on the Diskv and check the cache themselves before -// calling read. -func (d *Diskv) readWithRLock(key string) (io.ReadCloser, error) { - filename := d.completeFilename(key) - - fi, err := os.Stat(filename) - if err != nil { - return nil, err - } - if fi.IsDir() { - return nil, os.ErrNotExist - } - - f, err := os.Open(filename) - if err != nil { - return nil, err - } - - var r io.Reader - if d.CacheSizeMax > 0 { - r = newSiphon(f, d, key) - } else { - r = &closingReader{f} - } - - var rc = io.ReadCloser(ioutil.NopCloser(r)) - if d.Compression != nil { - rc, err = d.Compression.Reader(r) - if err != nil { - return nil, err - } - } - - return rc, nil -} - -// closingReader provides a Reader that automatically closes the -// embedded ReadCloser when it reaches EOF -type closingReader struct { - rc io.ReadCloser -} - -func (cr closingReader) Read(p []byte) (int, error) { - n, err := cr.rc.Read(p) - if err == io.EOF { - if closeErr := cr.rc.Close(); closeErr != nil { - return n, closeErr // close must succeed for Read to succeed - } - } - return n, err -} - -// siphon is like a TeeReader: it copies all data read through it to an -// internal buffer, and moves that buffer to the cache at EOF. -type siphon struct { - f *os.File - d *Diskv - key string - buf *bytes.Buffer -} - -// newSiphon constructs a siphoning reader that represents the passed file. -// When a successful series of reads ends in an EOF, the siphon will write -// the buffered data to Diskv's cache under the given key. -func newSiphon(f *os.File, d *Diskv, key string) io.Reader { - return &siphon{ - f: f, - d: d, - key: key, - buf: &bytes.Buffer{}, - } -} - -// Read implements the io.Reader interface for siphon. -func (s *siphon) Read(p []byte) (int, error) { - n, err := s.f.Read(p) - - if err == nil { - return s.buf.Write(p[0:n]) // Write must succeed for Read to succeed - } - - if err == io.EOF { - s.d.cacheWithoutLock(s.key, s.buf.Bytes()) // cache may fail - if closeErr := s.f.Close(); closeErr != nil { - return n, closeErr // close must succeed for Read to succeed - } - return n, err - } - - return n, err -} - -// Erase synchronously erases the given key from the disk and the cache. -func (d *Diskv) Erase(key string) error { - d.mu.Lock() - defer d.mu.Unlock() - - d.bustCacheWithLock(key) - - // erase from index - if d.Index != nil { - d.Index.Delete(key) - } - - // erase from disk - filename := d.completeFilename(key) - if s, err := os.Stat(filename); err == nil { - if s.IsDir() { - return errBadKey - } - if err = os.Remove(filename); err != nil { - return err - } - } else { - // Return err as-is so caller can do os.IsNotExist(err). - return err - } - - // clean up and return - d.pruneDirsWithLock(key) - return nil -} - -// EraseAll will delete all of the data from the store, both in the cache and on -// the disk. Note that EraseAll doesn't distinguish diskv-related data from non- -// diskv-related data. Care should be taken to always specify a diskv base -// directory that is exclusively for diskv data. -func (d *Diskv) EraseAll() error { - d.mu.Lock() - defer d.mu.Unlock() - d.cache = make(map[string][]byte) - d.cacheSize = 0 - return os.RemoveAll(d.BasePath) -} - -// Has returns true if the given key exists. -func (d *Diskv) Has(key string) bool { - d.mu.Lock() - defer d.mu.Unlock() - - if _, ok := d.cache[key]; ok { - return true - } - - filename := d.completeFilename(key) - s, err := os.Stat(filename) - if err != nil { - return false - } - if s.IsDir() { - return false - } - - return true -} - -// Keys returns a channel that will yield every key accessible by the store, -// in undefined order. If a cancel channel is provided, closing it will -// terminate and close the keys channel. -func (d *Diskv) Keys(cancel <-chan struct{}) <-chan string { - return d.KeysPrefix("", cancel) -} - -// KeysPrefix returns a channel that will yield every key accessible by the -// store with the given prefix, in undefined order. If a cancel channel is -// provided, closing it will terminate and close the keys channel. If the -// provided prefix is the empty string, all keys will be yielded. -func (d *Diskv) KeysPrefix(prefix string, cancel <-chan struct{}) <-chan string { - var prepath string - if prefix == "" { - prepath = d.BasePath - } else { - prepath = d.pathFor(prefix) - } - c := make(chan string) - go func() { - filepath.Walk(prepath, walker(c, prefix, cancel)) - close(c) - }() - return c -} - -// walker returns a function which satisfies the filepath.WalkFunc interface. -// It sends every non-directory file entry down the channel c. -func walker(c chan<- string, prefix string, cancel <-chan struct{}) filepath.WalkFunc { - return func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - if info.IsDir() || !strings.HasPrefix(info.Name(), prefix) { - return nil // "pass" - } - - select { - case c <- info.Name(): - case <-cancel: - return errCanceled - } - - return nil - } -} - -// pathFor returns the absolute path for location on the filesystem where the -// data for the given key will be stored. -func (d *Diskv) pathFor(key string) string { - return filepath.Join(d.BasePath, filepath.Join(d.Transform(key)...)) -} - -// ensurePathWithLock is a helper function that generates all necessary -// directories on the filesystem for the given key. -func (d *Diskv) ensurePathWithLock(key string) error { - return os.MkdirAll(d.pathFor(key), d.PathPerm) -} - -// completeFilename returns the absolute path to the file for the given key. -func (d *Diskv) completeFilename(key string) string { - return filepath.Join(d.pathFor(key), key) -} - -// cacheWithLock attempts to cache the given key-value pair in the store's -// cache. It can fail if the value is larger than the cache's maximum size. -func (d *Diskv) cacheWithLock(key string, val []byte) error { - valueSize := uint64(len(val)) - if err := d.ensureCacheSpaceWithLock(valueSize); err != nil { - return fmt.Errorf("%s; not caching", err) - } - - // be very strict about memory guarantees - if (d.cacheSize + valueSize) > d.CacheSizeMax { - panic(fmt.Sprintf("failed to make room for value (%d/%d)", valueSize, d.CacheSizeMax)) - } - - d.cache[key] = val - d.cacheSize += valueSize - return nil -} - -// cacheWithoutLock acquires the store's (write) mutex and calls cacheWithLock. -func (d *Diskv) cacheWithoutLock(key string, val []byte) error { - d.mu.Lock() - defer d.mu.Unlock() - return d.cacheWithLock(key, val) -} - -func (d *Diskv) bustCacheWithLock(key string) { - if val, ok := d.cache[key]; ok { - d.uncacheWithLock(key, uint64(len(val))) - } -} - -func (d *Diskv) uncacheWithLock(key string, sz uint64) { - d.cacheSize -= sz - delete(d.cache, key) -} - -// pruneDirsWithLock deletes empty directories in the path walk leading to the -// key k. Typically this function is called after an Erase is made. -func (d *Diskv) pruneDirsWithLock(key string) error { - pathlist := d.Transform(key) - for i := range pathlist { - dir := filepath.Join(d.BasePath, filepath.Join(pathlist[:len(pathlist)-i]...)) - - // thanks to Steven Blenkinsop for this snippet - switch fi, err := os.Stat(dir); true { - case err != nil: - return err - case !fi.IsDir(): - panic(fmt.Sprintf("corrupt dirstate at %s", dir)) - } - - nlinks, err := filepath.Glob(filepath.Join(dir, "*")) - if err != nil { - return err - } else if len(nlinks) > 0 { - return nil // has subdirs -- do not prune - } - if err = os.Remove(dir); err != nil { - return err - } - } - - return nil -} - -// ensureCacheSpaceWithLock deletes entries from the cache in arbitrary order -// until the cache has at least valueSize bytes available. -func (d *Diskv) ensureCacheSpaceWithLock(valueSize uint64) error { - if valueSize > d.CacheSizeMax { - return fmt.Errorf("value size (%d bytes) too large for cache (%d bytes)", valueSize, d.CacheSizeMax) - } - - safe := func() bool { return (d.cacheSize + valueSize) <= d.CacheSizeMax } - - for key, val := range d.cache { - if safe() { - break - } - - d.uncacheWithLock(key, uint64(len(val))) - } - - if !safe() { - panic(fmt.Sprintf("%d bytes still won't fit in the cache! (max %d bytes)", valueSize, d.CacheSizeMax)) - } - - return nil -} - -// nopWriteCloser wraps an io.Writer and provides a no-op Close method to -// satisfy the io.WriteCloser interface. -type nopWriteCloser struct { - io.Writer -} - -func (wc *nopWriteCloser) Write(p []byte) (int, error) { return wc.Writer.Write(p) } -func (wc *nopWriteCloser) Close() error { return nil } diff --git a/vendor/github.com/peterbourgon/diskv/index.go b/vendor/github.com/peterbourgon/diskv/index.go deleted file mode 100644 index 96fee5152b1..00000000000 --- a/vendor/github.com/peterbourgon/diskv/index.go +++ /dev/null @@ -1,115 +0,0 @@ -package diskv - -import ( - "sync" - - "github.com/google/btree" -) - -// Index is a generic interface for things that can -// provide an ordered list of keys. -type Index interface { - Initialize(less LessFunction, keys <-chan string) - Insert(key string) - Delete(key string) - Keys(from string, n int) []string -} - -// LessFunction is used to initialize an Index of keys in a specific order. -type LessFunction func(string, string) bool - -// btreeString is a custom data type that satisfies the BTree Less interface, -// making the strings it wraps sortable by the BTree package. -type btreeString struct { - s string - l LessFunction -} - -// Less satisfies the BTree.Less interface using the btreeString's LessFunction. -func (s btreeString) Less(i btree.Item) bool { - return s.l(s.s, i.(btreeString).s) -} - -// BTreeIndex is an implementation of the Index interface using google/btree. -type BTreeIndex struct { - sync.RWMutex - LessFunction - *btree.BTree -} - -// Initialize populates the BTree tree with data from the keys channel, -// according to the passed less function. It's destructive to the BTreeIndex. -func (i *BTreeIndex) Initialize(less LessFunction, keys <-chan string) { - i.Lock() - defer i.Unlock() - i.LessFunction = less - i.BTree = rebuild(less, keys) -} - -// Insert inserts the given key (only) into the BTree tree. -func (i *BTreeIndex) Insert(key string) { - i.Lock() - defer i.Unlock() - if i.BTree == nil || i.LessFunction == nil { - panic("uninitialized index") - } - i.BTree.ReplaceOrInsert(btreeString{s: key, l: i.LessFunction}) -} - -// Delete removes the given key (only) from the BTree tree. -func (i *BTreeIndex) Delete(key string) { - i.Lock() - defer i.Unlock() - if i.BTree == nil || i.LessFunction == nil { - panic("uninitialized index") - } - i.BTree.Delete(btreeString{s: key, l: i.LessFunction}) -} - -// Keys yields a maximum of n keys in order. If the passed 'from' key is empty, -// Keys will return the first n keys. If the passed 'from' key is non-empty, the -// first key in the returned slice will be the key that immediately follows the -// passed key, in key order. -func (i *BTreeIndex) Keys(from string, n int) []string { - i.RLock() - defer i.RUnlock() - - if i.BTree == nil || i.LessFunction == nil { - panic("uninitialized index") - } - - if i.BTree.Len() <= 0 { - return []string{} - } - - btreeFrom := btreeString{s: from, l: i.LessFunction} - skipFirst := true - if len(from) <= 0 || !i.BTree.Has(btreeFrom) { - // no such key, so fabricate an always-smallest item - btreeFrom = btreeString{s: "", l: func(string, string) bool { return true }} - skipFirst = false - } - - keys := []string{} - iterator := func(i btree.Item) bool { - keys = append(keys, i.(btreeString).s) - return len(keys) < n - } - i.BTree.AscendGreaterOrEqual(btreeFrom, iterator) - - if skipFirst && len(keys) > 0 { - keys = keys[1:] - } - - return keys -} - -// rebuildIndex does the work of regenerating the index -// with the given keys. -func rebuild(less LessFunction, keys <-chan string) *btree.BTree { - tree := btree.New(2) - for key := range keys { - tree.ReplaceOrInsert(btreeString{s: key, l: less}) - } - return tree -} From 6cfc3d163c8fd020c66cb1bb93b819edcaed0e8f Mon Sep 17 00:00:00 2001 From: Phillip Wittrock Date: Mon, 7 Aug 2017 13:48:13 -0700 Subject: [PATCH 36/38] Move ownership of proxy test to sig-network directory --- test/e2e/kubectl/BUILD | 3 --- test/e2e/network/BUILD | 3 +++ test/e2e/{kubectl => network}/proxy.go | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) rename test/e2e/{kubectl => network}/proxy.go (99%) diff --git a/test/e2e/kubectl/BUILD b/test/e2e/kubectl/BUILD index 8a76fe4aa5a..6636907a542 100644 --- a/test/e2e/kubectl/BUILD +++ b/test/e2e/kubectl/BUILD @@ -13,11 +13,9 @@ go_library( "framework.go", "kubectl.go", "portforward.go", - "proxy.go", ], tags = ["automanaged"], deps = [ - "//pkg/api/testapi:go_default_library", "//pkg/apis/batch/v2alpha1:go_default_library", "//pkg/controller:go_default_library", "//pkg/kubectl/cmd/util:go_default_library", @@ -38,7 +36,6 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", diff --git a/test/e2e/network/BUILD b/test/e2e/network/BUILD index 4d33ef3873b..c82d8ae7b43 100644 --- a/test/e2e/network/BUILD +++ b/test/e2e/network/BUILD @@ -22,6 +22,7 @@ go_library( "networking.go", "networking_perf.go", "no_snat.go", + "proxy.go", "service.go", "service_latency.go", "serviceloadbalancers.go", @@ -47,12 +48,14 @@ go_library( "//vendor/github.com/onsi/gomega:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/rbac/v1beta1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", diff --git a/test/e2e/kubectl/proxy.go b/test/e2e/network/proxy.go similarity index 99% rename from test/e2e/kubectl/proxy.go rename to test/e2e/network/proxy.go index 5fb16bbdbab..dd6ae3b4f26 100644 --- a/test/e2e/kubectl/proxy.go +++ b/test/e2e/network/proxy.go @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -// OWNER = sig/cli +// OWNER = sig/network -package kubectl +package network import ( "fmt" @@ -51,7 +51,7 @@ const ( proxyHTTPCallTimeout = 30 * time.Second ) -var _ = SIGDescribe("Kubectl Proxy", func() { +var _ = SIGDescribe("Proxy", func() { version := testapi.Groups[v1.GroupName].GroupVersion().Version Context("version "+version, func() { options := framework.FrameworkOptions{ From 4fdb701e3cc6e4907baeec0397fbbff465e42ede Mon Sep 17 00:00:00 2001 From: Klaus Ma Date: Sat, 5 Aug 2017 07:34:28 +0800 Subject: [PATCH 37/38] Moved node/testutil to upper dir. --- hack/.golint_failures | 1 - pkg/controller/BUILD | 3 ++- pkg/controller/cloud/BUILD | 2 +- pkg/controller/cloud/node_controller_test.go | 2 +- pkg/controller/controller_utils_test.go | 2 +- pkg/controller/node/BUILD | 7 ++---- pkg/controller/node/cidr_allocator_test.go | 2 +- pkg/controller/node/nodecontroller_test.go | 2 +- pkg/controller/node/taint_controller_test.go | 2 +- pkg/controller/podgc/BUILD | 2 +- pkg/controller/podgc/gc_controller_test.go | 2 +- pkg/controller/{node => }/testutil/BUILD | 0 .../{node => }/testutil/test_utils.go | 22 +++++++++---------- 13 files changed, 23 insertions(+), 26 deletions(-) rename pkg/controller/{node => }/testutil/BUILD (100%) rename pkg/controller/{node => }/testutil/test_utils.go (95%) diff --git a/hack/.golint_failures b/hack/.golint_failures index 136282523b0..57d2bc2b388 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -182,7 +182,6 @@ pkg/controller/job pkg/controller/namespace pkg/controller/namespace/deletion pkg/controller/node -pkg/controller/node/testutil pkg/controller/podautoscaler pkg/controller/podautoscaler/metrics pkg/controller/podgc diff --git a/pkg/controller/BUILD b/pkg/controller/BUILD index dc6dbfbac2b..1cdc7f61fc8 100644 --- a/pkg/controller/BUILD +++ b/pkg/controller/BUILD @@ -20,7 +20,7 @@ go_test( "//pkg/api:go_default_library", "//pkg/api/install:go_default_library", "//pkg/api/testapi:go_default_library", - "//pkg/controller/node/testutil:go_default_library", + "//pkg/controller/testutil:go_default_library", "//pkg/securitycontext:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", @@ -128,6 +128,7 @@ filegroup( "//pkg/controller/service:all-srcs", "//pkg/controller/serviceaccount:all-srcs", "//pkg/controller/statefulset:all-srcs", + "//pkg/controller/testutil:all-srcs", "//pkg/controller/ttl:all-srcs", "//pkg/controller/volume/attachdetach:all-srcs", "//pkg/controller/volume/events:all-srcs", diff --git a/pkg/controller/cloud/BUILD b/pkg/controller/cloud/BUILD index dd5392b721b..d6f3e7d40f3 100644 --- a/pkg/controller/cloud/BUILD +++ b/pkg/controller/cloud/BUILD @@ -43,7 +43,7 @@ go_test( "//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider/providers/fake:go_default_library", "//pkg/controller:go_default_library", - "//pkg/controller/node/testutil:go_default_library", + "//pkg/controller/testutil:go_default_library", "//pkg/kubelet/apis:go_default_library", "//plugin/pkg/scheduler/algorithm:go_default_library", "//vendor/github.com/golang/glog:go_default_library", diff --git a/pkg/controller/cloud/node_controller_test.go b/pkg/controller/cloud/node_controller_test.go index 333081af2f6..033dbe90889 100644 --- a/pkg/controller/cloud/node_controller_test.go +++ b/pkg/controller/cloud/node_controller_test.go @@ -34,7 +34,7 @@ import ( "k8s.io/kubernetes/pkg/cloudprovider" fakecloud "k8s.io/kubernetes/pkg/cloudprovider/providers/fake" "k8s.io/kubernetes/pkg/controller" - "k8s.io/kubernetes/pkg/controller/node/testutil" + "k8s.io/kubernetes/pkg/controller/testutil" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm" ) diff --git a/pkg/controller/controller_utils_test.go b/pkg/controller/controller_utils_test.go index 7abc0af277e..bd82e48e703 100644 --- a/pkg/controller/controller_utils_test.go +++ b/pkg/controller/controller_utils_test.go @@ -45,7 +45,7 @@ import ( "k8s.io/kubernetes/pkg/api" _ "k8s.io/kubernetes/pkg/api/install" "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/controller/node/testutil" + "k8s.io/kubernetes/pkg/controller/testutil" "k8s.io/kubernetes/pkg/securitycontext" ) diff --git a/pkg/controller/node/BUILD b/pkg/controller/node/BUILD index a48ebc072c7..d580fdf4cd1 100644 --- a/pkg/controller/node/BUILD +++ b/pkg/controller/node/BUILD @@ -24,7 +24,7 @@ go_test( "//pkg/cloudprovider:go_default_library", "//pkg/cloudprovider/providers/fake:go_default_library", "//pkg/controller:go_default_library", - "//pkg/controller/node/testutil:go_default_library", + "//pkg/controller/testutil:go_default_library", "//pkg/kubelet/apis:go_default_library", "//pkg/util/node:go_default_library", "//pkg/util/taints:go_default_library", @@ -115,9 +115,6 @@ filegroup( filegroup( name = "all-srcs", - srcs = [ - ":package-srcs", - "//pkg/controller/node/testutil:all-srcs", - ], + srcs = [":package-srcs"], tags = ["automanaged"], ) diff --git a/pkg/controller/node/cidr_allocator_test.go b/pkg/controller/node/cidr_allocator_test.go index 3fc5d4d8ef9..7d2ffb69d1d 100644 --- a/pkg/controller/node/cidr_allocator_test.go +++ b/pkg/controller/node/cidr_allocator_test.go @@ -25,7 +25,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes/fake" - "k8s.io/kubernetes/pkg/controller/node/testutil" + "k8s.io/kubernetes/pkg/controller/testutil" ) const ( diff --git a/pkg/controller/node/nodecontroller_test.go b/pkg/controller/node/nodecontroller_test.go index 34d78faaed1..e3782631786 100644 --- a/pkg/controller/node/nodecontroller_test.go +++ b/pkg/controller/node/nodecontroller_test.go @@ -39,7 +39,7 @@ import ( "k8s.io/kubernetes/pkg/cloudprovider" fakecloud "k8s.io/kubernetes/pkg/cloudprovider/providers/fake" "k8s.io/kubernetes/pkg/controller" - "k8s.io/kubernetes/pkg/controller/node/testutil" + "k8s.io/kubernetes/pkg/controller/testutil" kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis" "k8s.io/kubernetes/pkg/util/node" taintutils "k8s.io/kubernetes/pkg/util/taints" diff --git a/pkg/controller/node/taint_controller_test.go b/pkg/controller/node/taint_controller_test.go index f6da5ddd5d1..6ce5eee4705 100644 --- a/pkg/controller/node/taint_controller_test.go +++ b/pkg/controller/node/taint_controller_test.go @@ -24,7 +24,7 @@ import ( "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes/fake" - "k8s.io/kubernetes/pkg/controller/node/testutil" + "k8s.io/kubernetes/pkg/controller/testutil" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" clienttesting "k8s.io/client-go/testing" diff --git a/pkg/controller/podgc/BUILD b/pkg/controller/podgc/BUILD index 315bab74cd0..b288636778c 100644 --- a/pkg/controller/podgc/BUILD +++ b/pkg/controller/podgc/BUILD @@ -39,7 +39,7 @@ go_test( tags = ["automanaged"], deps = [ "//pkg/controller:go_default_library", - "//pkg/controller/node/testutil:go_default_library", + "//pkg/controller/testutil:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", diff --git a/pkg/controller/podgc/gc_controller_test.go b/pkg/controller/podgc/gc_controller_test.go index 8ffce2aae47..73ecd7a8ccf 100644 --- a/pkg/controller/podgc/gc_controller_test.go +++ b/pkg/controller/podgc/gc_controller_test.go @@ -30,7 +30,7 @@ import ( clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" "k8s.io/kubernetes/pkg/controller" - "k8s.io/kubernetes/pkg/controller/node/testutil" + "k8s.io/kubernetes/pkg/controller/testutil" ) type FakeController struct{} diff --git a/pkg/controller/node/testutil/BUILD b/pkg/controller/testutil/BUILD similarity index 100% rename from pkg/controller/node/testutil/BUILD rename to pkg/controller/testutil/BUILD diff --git a/pkg/controller/node/testutil/test_utils.go b/pkg/controller/testutil/test_utils.go similarity index 95% rename from pkg/controller/node/testutil/test_utils.go rename to pkg/controller/testutil/test_utils.go index 3309e05cf38..3801c4a93d6 100644 --- a/pkg/controller/node/testutil/test_utils.go +++ b/pkg/controller/testutil/test_utils.go @@ -68,30 +68,31 @@ type FakeNodeHandler struct { DeleteWaitChan chan struct{} } +// FakeLegacyHandler is a fake implemtation of CoreV1Interface. type FakeLegacyHandler struct { v1core.CoreV1Interface n *FakeNodeHandler } // GetUpdatedNodesCopy returns a slice of Nodes with updates applied. -func (c *FakeNodeHandler) GetUpdatedNodesCopy() []*v1.Node { - c.lock.Lock() - defer c.lock.Unlock() - updatedNodesCopy := make([]*v1.Node, len(c.UpdatedNodes), len(c.UpdatedNodes)) - for i, ptr := range c.UpdatedNodes { +func (m *FakeNodeHandler) GetUpdatedNodesCopy() []*v1.Node { + m.lock.Lock() + defer m.lock.Unlock() + updatedNodesCopy := make([]*v1.Node, len(m.UpdatedNodes), len(m.UpdatedNodes)) + for i, ptr := range m.UpdatedNodes { updatedNodesCopy[i] = ptr } return updatedNodesCopy } // Core returns fake CoreInterface. -func (c *FakeNodeHandler) Core() v1core.CoreV1Interface { - return &FakeLegacyHandler{c.Clientset.Core(), c} +func (m *FakeNodeHandler) Core() v1core.CoreV1Interface { + return &FakeLegacyHandler{m.Clientset.Core(), m} } // CoreV1 returns fake CoreV1Interface -func (c *FakeNodeHandler) CoreV1() v1core.CoreV1Interface { - return &FakeLegacyHandler{c.Clientset.CoreV1(), c} +func (m *FakeNodeHandler) CoreV1() v1core.CoreV1Interface { + return &FakeLegacyHandler{m.Clientset.CoreV1(), m} } // Nodes return fake NodeInterfaces. @@ -115,9 +116,8 @@ func (m *FakeNodeHandler) Create(node *v1.Node) (*v1.Node, error) { nodeCopy := *node m.CreatedNodes = append(m.CreatedNodes, &nodeCopy) return node, nil - } else { - return nil, errors.New("Create error.") } + return nil, errors.New("create error") } // Get returns a Node from the fake store. From 65c28eb64f05ebdff1a5ee659f8b9ec24b682f62 Mon Sep 17 00:00:00 2001 From: Xing Zhou Date: Tue, 8 Aug 2017 13:38:31 +0800 Subject: [PATCH 38/38] Add error return for the Marshal object invocation. Add error return for the Marshal object invocation. --- pkg/kubectl/cmd/drain.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/kubectl/cmd/drain.go b/pkg/kubectl/cmd/drain.go index 8f6d511bc03..7e2d8fd5e00 100644 --- a/pkg/kubectl/cmd/drain.go +++ b/pkg/kubectl/cmd/drain.go @@ -631,6 +631,9 @@ func (o *DrainOptions) RunCordonOrUncordon(desired bool) error { return err } oldData, err := json.Marshal(obj) + if err != nil { + return err + } node, ok := obj.(*corev1.Node) if !ok { return fmt.Errorf("unexpected Type%T, expected Node", obj) @@ -642,6 +645,9 @@ func (o *DrainOptions) RunCordonOrUncordon(desired bool) error { helper := resource.NewHelper(o.restClient, o.nodeInfo.Mapping) node.Spec.Unschedulable = desired newData, err := json.Marshal(obj) + if err != nil { + return err + } patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, obj) if err != nil { return err