mirror of https://github.com/k3s-io/k3s
Merge pull request #55148 from dixudx/controller_defaultGC_DeleteDependents
Automatic merge from submit-queue (batch tested with PRs 52767, 55065, 55148, 56228, 56221). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. change DefaultGarbageCollectionPolicy to DeleteDependents for workloads controllers **What this PR does / why we need it**: As part of the apps/v1 GA effort (kubernetes/features#353) for v1.9. For core controllers, like `Deployment`, `DaemonSet`, `ReplicaSet`, and `StatefulSet`, changing the `DefaultGarbageCollectionPolicy` from `OrphanDependents` to `DeleteDependents` will make these objects consistent with the default behavior for all new objects. For legacy API versions, the `DefaultGarbageCollectionPolicy` remains `OrphanDependents`. **Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*: ref #55027 **Special notes for your reviewer**: /cc @enisoc @caesarxuchao @kow3ns /assign @kubernetes/sig-apps-api-reviews **Release note**: ```release-note The default garbage collection policy for Deployment, DaemonSet, StatefulSet, and ReplicaSet has changed from OrphanDependents to DeleteDependents when the deletion is requested through an `apps/v1` endpoint. Clients using older endpoints will be unaffected. This change is only at the REST API level and is independent of the default behavior of particular clients (e.g. this does not affect the default for the kubectl `--cascade` flag). If you upgrade your client-go libs and use the `AppsV1()` interface, please note that the default garbage collection behavior is changed. ```pull/6/head
commit
00b2d95c86
|
@ -19,11 +19,14 @@ go_library(
|
||||||
"//pkg/api/pod:go_default_library",
|
"//pkg/api/pod:go_default_library",
|
||||||
"//pkg/apis/apps:go_default_library",
|
"//pkg/apis/apps:go_default_library",
|
||||||
"//pkg/apis/apps/validation:go_default_library",
|
"//pkg/apis/apps/validation:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/apps/v1beta2:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/equality: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/api/errors:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion: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/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime: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/validation/field: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/apimachinery/pkg/watch:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||||
|
|
|
@ -17,8 +17,11 @@ limitations under the License.
|
||||||
package statefulset
|
package statefulset
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||||
|
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
"k8s.io/apiserver/pkg/registry/rest"
|
"k8s.io/apiserver/pkg/registry/rest"
|
||||||
|
@ -38,9 +41,18 @@ type statefulSetStrategy struct {
|
||||||
// Strategy is the default logic that applies when creating and updating Replication StatefulSet objects.
|
// Strategy is the default logic that applies when creating and updating Replication StatefulSet objects.
|
||||||
var Strategy = statefulSetStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
var Strategy = statefulSetStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||||
|
|
||||||
// DefaultGarbageCollectionPolicy returns Orphan because that was the default
|
// DefaultGarbageCollectionPolicy returns OrphanDependents by default. For apps/v1, returns DeleteDependents.
|
||||||
// behavior before the server-side garbage collection was implemented.
|
func (statefulSetStrategy) DefaultGarbageCollectionPolicy(ctx genericapirequest.Context) rest.GarbageCollectionPolicy {
|
||||||
func (statefulSetStrategy) DefaultGarbageCollectionPolicy() rest.GarbageCollectionPolicy {
|
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
|
||||||
|
groupVersion := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
|
||||||
|
switch groupVersion {
|
||||||
|
case appsv1beta1.SchemeGroupVersion, appsv1beta2.SchemeGroupVersion:
|
||||||
|
// for back compatibility
|
||||||
|
return rest.OrphanDependents
|
||||||
|
default:
|
||||||
|
return rest.DeleteDependents
|
||||||
|
}
|
||||||
|
}
|
||||||
return rest.OrphanDependents
|
return rest.OrphanDependents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,12 +91,59 @@ func TestStatefulSetStrategy(t *testing.T) {
|
||||||
if len(errs) == 0 {
|
if len(errs) == 0 {
|
||||||
t.Errorf("expected a validation error since updates are disallowed on statefulsets.")
|
t.Errorf("expected a validation error since updates are disallowed on statefulsets.")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStatefulsetDefaultGarbageCollectionPolicy(t *testing.T) {
|
||||||
// Make sure we correctly implement the interface.
|
// Make sure we correctly implement the interface.
|
||||||
// Otherwise a typo could silently change the default.
|
// Otherwise a typo could silently change the default.
|
||||||
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
|
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
|
||||||
if got, want := gcds.DefaultGarbageCollectionPolicy(), rest.OrphanDependents; got != want {
|
tests := []struct {
|
||||||
t.Errorf("DefaultGarbageCollectionPolicy() = %#v, want %#v", got, want)
|
requestInfo genericapirequest.RequestInfo
|
||||||
|
expectedGCPolicy rest.GarbageCollectionPolicy
|
||||||
|
isNilRequestInfo bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "apps",
|
||||||
|
APIVersion: "v1beta1",
|
||||||
|
Resource: "statefulsets",
|
||||||
|
},
|
||||||
|
rest.OrphanDependents,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "apps",
|
||||||
|
APIVersion: "v1beta2",
|
||||||
|
Resource: "statefulsets",
|
||||||
|
},
|
||||||
|
rest.OrphanDependents,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "apps",
|
||||||
|
APIVersion: "v1",
|
||||||
|
Resource: "statefulsets",
|
||||||
|
},
|
||||||
|
rest.DeleteDependents,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expectedGCPolicy: rest.OrphanDependents,
|
||||||
|
isNilRequestInfo: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
context := genericapirequest.NewContext()
|
||||||
|
if !test.isNilRequestInfo {
|
||||||
|
context = genericapirequest.WithRequestInfo(context, &test.requestInfo)
|
||||||
|
}
|
||||||
|
if got, want := gcds.DefaultGarbageCollectionPolicy(context), test.expectedGCPolicy; got != want {
|
||||||
|
t.Errorf("%s/%s: DefaultGarbageCollectionPolicy() = %#v, want %#v", test.requestInfo.APIGroup,
|
||||||
|
test.requestInfo.APIVersion, got, want)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ var Strategy = cronJobStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||||
|
|
||||||
// DefaultGarbageCollectionPolicy returns Orphan because that was the default
|
// DefaultGarbageCollectionPolicy returns Orphan because that was the default
|
||||||
// behavior before the server-side garbage collection was implemented.
|
// behavior before the server-side garbage collection was implemented.
|
||||||
func (cronJobStrategy) DefaultGarbageCollectionPolicy() rest.GarbageCollectionPolicy {
|
func (cronJobStrategy) DefaultGarbageCollectionPolicy(ctx genericapirequest.Context) rest.GarbageCollectionPolicy {
|
||||||
return rest.OrphanDependents
|
return rest.OrphanDependents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ func TestCronJobStrategy(t *testing.T) {
|
||||||
// Make sure we correctly implement the interface.
|
// Make sure we correctly implement the interface.
|
||||||
// Otherwise a typo could silently change the default.
|
// Otherwise a typo could silently change the default.
|
||||||
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
|
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
|
||||||
if got, want := gcds.DefaultGarbageCollectionPolicy(), rest.OrphanDependents; got != want {
|
if got, want := gcds.DefaultGarbageCollectionPolicy(genericapirequest.NewContext()), rest.OrphanDependents; got != want {
|
||||||
t.Errorf("DefaultGarbageCollectionPolicy() = %#v, want %#v", got, want)
|
t.Errorf("DefaultGarbageCollectionPolicy() = %#v, want %#v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ var Strategy = jobStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||||
|
|
||||||
// DefaultGarbageCollectionPolicy returns Orphan because that was the default
|
// DefaultGarbageCollectionPolicy returns Orphan because that was the default
|
||||||
// behavior before the server-side garbage collection was implemented.
|
// behavior before the server-side garbage collection was implemented.
|
||||||
func (jobStrategy) DefaultGarbageCollectionPolicy() rest.GarbageCollectionPolicy {
|
func (jobStrategy) DefaultGarbageCollectionPolicy(ctx genericapirequest.Context) rest.GarbageCollectionPolicy {
|
||||||
return rest.OrphanDependents
|
return rest.OrphanDependents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ func TestJobStrategy(t *testing.T) {
|
||||||
// Make sure we correctly implement the interface.
|
// Make sure we correctly implement the interface.
|
||||||
// Otherwise a typo could silently change the default.
|
// Otherwise a typo could silently change the default.
|
||||||
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
|
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
|
||||||
if got, want := gcds.DefaultGarbageCollectionPolicy(), rest.OrphanDependents; got != want {
|
if got, want := gcds.DefaultGarbageCollectionPolicy(genericapirequest.NewContext()), rest.OrphanDependents; got != want {
|
||||||
t.Errorf("DefaultGarbageCollectionPolicy() = %#v, want %#v", got, want)
|
t.Errorf("DefaultGarbageCollectionPolicy() = %#v, want %#v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ type eventStrategy struct {
|
||||||
// Event objects via the REST API.
|
// Event objects via the REST API.
|
||||||
var Strategy = eventStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
var Strategy = eventStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||||
|
|
||||||
func (eventStrategy) DefaultGarbageCollectionPolicy() rest.GarbageCollectionPolicy {
|
func (eventStrategy) DefaultGarbageCollectionPolicy(ctx genericapirequest.Context) rest.GarbageCollectionPolicy {
|
||||||
return rest.Unsupported
|
return rest.Unsupported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ var Strategy = rcStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||||
|
|
||||||
// DefaultGarbageCollectionPolicy returns Orphan because that was the default
|
// DefaultGarbageCollectionPolicy returns Orphan because that was the default
|
||||||
// behavior before the server-side garbage collection was implemented.
|
// behavior before the server-side garbage collection was implemented.
|
||||||
func (rcStrategy) DefaultGarbageCollectionPolicy() rest.GarbageCollectionPolicy {
|
func (rcStrategy) DefaultGarbageCollectionPolicy(ctx genericapirequest.Context) rest.GarbageCollectionPolicy {
|
||||||
return rest.OrphanDependents
|
return rest.OrphanDependents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ go_library(
|
||||||
"//pkg/api/pod:go_default_library",
|
"//pkg/api/pod:go_default_library",
|
||||||
"//pkg/apis/extensions:go_default_library",
|
"//pkg/apis/extensions:go_default_library",
|
||||||
"//pkg/apis/extensions/validation:go_default_library",
|
"//pkg/apis/extensions/validation:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/apps/v1beta2:go_default_library",
|
||||||
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/validation:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/validation:go_default_library",
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
package daemonset
|
package daemonset
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
apivalidation "k8s.io/apimachinery/pkg/api/validation"
|
apivalidation "k8s.io/apimachinery/pkg/api/validation"
|
||||||
|
@ -41,9 +42,18 @@ type daemonSetStrategy struct {
|
||||||
// Strategy is the default logic that applies when creating and updating DaemonSet objects.
|
// Strategy is the default logic that applies when creating and updating DaemonSet objects.
|
||||||
var Strategy = daemonSetStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
var Strategy = daemonSetStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||||
|
|
||||||
// DefaultGarbageCollectionPolicy returns Orphan because that was the default
|
// DefaultGarbageCollectionPolicy returns OrphanDependents by default. For apps/v1, returns DeleteDependents.
|
||||||
// behavior before the server-side garbage collection was implemented.
|
func (daemonSetStrategy) DefaultGarbageCollectionPolicy(ctx genericapirequest.Context) rest.GarbageCollectionPolicy {
|
||||||
func (daemonSetStrategy) DefaultGarbageCollectionPolicy() rest.GarbageCollectionPolicy {
|
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
|
||||||
|
groupVersion := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
|
||||||
|
switch groupVersion {
|
||||||
|
case extensionsv1beta1.SchemeGroupVersion, appsv1beta2.SchemeGroupVersion:
|
||||||
|
// for back compatibility
|
||||||
|
return rest.OrphanDependents
|
||||||
|
default:
|
||||||
|
return rest.DeleteDependents
|
||||||
|
}
|
||||||
|
}
|
||||||
return rest.OrphanDependents
|
return rest.OrphanDependents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,12 +35,57 @@ const (
|
||||||
namespace = "test-namespace"
|
namespace = "test-namespace"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDefaultGarbageCollectionPolicy(t *testing.T) {
|
func TestDaemonsetDefaultGarbageCollectionPolicy(t *testing.T) {
|
||||||
// Make sure we correctly implement the interface.
|
// Make sure we correctly implement the interface.
|
||||||
// Otherwise a typo could silently change the default.
|
// Otherwise a typo could silently change the default.
|
||||||
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
|
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
|
||||||
if got, want := gcds.DefaultGarbageCollectionPolicy(), rest.OrphanDependents; got != want {
|
tests := []struct {
|
||||||
t.Errorf("DefaultGarbageCollectionPolicy() = %#v, want %#v", got, want)
|
requestInfo genericapirequest.RequestInfo
|
||||||
|
expectedGCPolicy rest.GarbageCollectionPolicy
|
||||||
|
isNilRequestInfo bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "extensions",
|
||||||
|
APIVersion: "v1beta1",
|
||||||
|
Resource: "daemonsets",
|
||||||
|
},
|
||||||
|
rest.OrphanDependents,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "apps",
|
||||||
|
APIVersion: "v1beta2",
|
||||||
|
Resource: "daemonsets",
|
||||||
|
},
|
||||||
|
rest.OrphanDependents,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "apps",
|
||||||
|
APIVersion: "v1",
|
||||||
|
Resource: "daemonsets",
|
||||||
|
},
|
||||||
|
rest.DeleteDependents,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expectedGCPolicy: rest.OrphanDependents,
|
||||||
|
isNilRequestInfo: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
context := genericapirequest.NewContext()
|
||||||
|
if !test.isNilRequestInfo {
|
||||||
|
context = genericapirequest.WithRequestInfo(context, &test.requestInfo)
|
||||||
|
}
|
||||||
|
if got, want := gcds.DefaultGarbageCollectionPolicy(context), test.expectedGCPolicy; got != want {
|
||||||
|
t.Errorf("%s/%s: DefaultGarbageCollectionPolicy() = %#v, want %#v", test.requestInfo.APIGroup,
|
||||||
|
test.requestInfo.APIVersion, got, want)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ go_library(
|
||||||
"//pkg/apis/extensions:go_default_library",
|
"//pkg/apis/extensions:go_default_library",
|
||||||
"//pkg/apis/extensions/validation:go_default_library",
|
"//pkg/apis/extensions/validation:go_default_library",
|
||||||
"//vendor/k8s.io/api/apps/v1beta1: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/extensions/v1beta1:go_default_library",
|
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/validation:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/validation:go_default_library",
|
||||||
|
@ -47,6 +48,7 @@ go_test(
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field: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/endpoints/request:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ package deployment
|
||||||
|
|
||||||
import (
|
import (
|
||||||
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||||
|
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
apivalidation "k8s.io/apimachinery/pkg/api/validation"
|
apivalidation "k8s.io/apimachinery/pkg/api/validation"
|
||||||
|
@ -43,9 +44,18 @@ type deploymentStrategy struct {
|
||||||
// objects via the REST API.
|
// objects via the REST API.
|
||||||
var Strategy = deploymentStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
var Strategy = deploymentStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||||
|
|
||||||
// DefaultGarbageCollectionPolicy returns Orphan because that's the default
|
// DefaultGarbageCollectionPolicy returns OrphanDependents by default. For apps/v1, returns DeleteDependents.
|
||||||
// behavior before the server-side garbage collection is implemented.
|
func (deploymentStrategy) DefaultGarbageCollectionPolicy(ctx genericapirequest.Context) rest.GarbageCollectionPolicy {
|
||||||
func (deploymentStrategy) DefaultGarbageCollectionPolicy() rest.GarbageCollectionPolicy {
|
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
|
||||||
|
groupVersion := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
|
||||||
|
switch groupVersion {
|
||||||
|
case extensionsv1beta1.SchemeGroupVersion, appsv1beta1.SchemeGroupVersion, appsv1beta2.SchemeGroupVersion:
|
||||||
|
// for back compatibility
|
||||||
|
return rest.OrphanDependents
|
||||||
|
default:
|
||||||
|
return rest.DeleteDependents
|
||||||
|
}
|
||||||
|
}
|
||||||
return rest.OrphanDependents
|
return rest.OrphanDependents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
|
"k8s.io/apiserver/pkg/registry/rest"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
)
|
)
|
||||||
|
@ -183,3 +184,66 @@ func newDeploymentWithSelectorLabels(selectorLabels map[string]string) *extensio
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeploymentDefaultGarbageCollectionPolicy(t *testing.T) {
|
||||||
|
// Make sure we correctly implement the interface.
|
||||||
|
// Otherwise a typo could silently change the default.
|
||||||
|
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
|
||||||
|
tests := []struct {
|
||||||
|
requestInfo genericapirequest.RequestInfo
|
||||||
|
expectedGCPolicy rest.GarbageCollectionPolicy
|
||||||
|
isNilRequestInfo bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "extensions",
|
||||||
|
APIVersion: "v1beta1",
|
||||||
|
Resource: "deployments",
|
||||||
|
},
|
||||||
|
rest.OrphanDependents,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "apps",
|
||||||
|
APIVersion: "v1beta1",
|
||||||
|
Resource: "deployments",
|
||||||
|
},
|
||||||
|
rest.OrphanDependents,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "apps",
|
||||||
|
APIVersion: "v1beta2",
|
||||||
|
Resource: "deployments",
|
||||||
|
},
|
||||||
|
rest.OrphanDependents,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "apps",
|
||||||
|
APIVersion: "v1",
|
||||||
|
Resource: "deployments",
|
||||||
|
},
|
||||||
|
rest.DeleteDependents,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expectedGCPolicy: rest.OrphanDependents,
|
||||||
|
isNilRequestInfo: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
context := genericapirequest.NewContext()
|
||||||
|
if !test.isNilRequestInfo {
|
||||||
|
context = genericapirequest.WithRequestInfo(context, &test.requestInfo)
|
||||||
|
}
|
||||||
|
if got, want := gcds.DefaultGarbageCollectionPolicy(context), test.expectedGCPolicy; got != want {
|
||||||
|
t.Errorf("%s/%s: DefaultGarbageCollectionPolicy() = %#v, want %#v", test.requestInfo.APIGroup,
|
||||||
|
test.requestInfo.APIVersion, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ go_library(
|
||||||
"//pkg/api/pod:go_default_library",
|
"//pkg/api/pod:go_default_library",
|
||||||
"//pkg/apis/extensions:go_default_library",
|
"//pkg/apis/extensions:go_default_library",
|
||||||
"//pkg/apis/extensions/validation:go_default_library",
|
"//pkg/apis/extensions/validation:go_default_library",
|
||||||
|
"//vendor/k8s.io/api/apps/v1beta2:go_default_library",
|
||||||
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/validation:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/validation:go_default_library",
|
||||||
|
@ -49,6 +50,7 @@ go_test(
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field: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/endpoints/request:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
apivalidation "k8s.io/apimachinery/pkg/api/validation"
|
apivalidation "k8s.io/apimachinery/pkg/api/validation"
|
||||||
|
@ -50,9 +51,18 @@ type rsStrategy struct {
|
||||||
// Strategy is the default logic that applies when creating and updating ReplicaSet objects.
|
// Strategy is the default logic that applies when creating and updating ReplicaSet objects.
|
||||||
var Strategy = rsStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
var Strategy = rsStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
|
||||||
|
|
||||||
// DefaultGarbageCollectionPolicy returns Orphan because that's the default
|
// DefaultGarbageCollectionPolicy returns OrphanDependents by default. For apps/v1, returns DeleteDependents.
|
||||||
// behavior before the server-side garbage collection is implemented.
|
func (rsStrategy) DefaultGarbageCollectionPolicy(ctx genericapirequest.Context) rest.GarbageCollectionPolicy {
|
||||||
func (rsStrategy) DefaultGarbageCollectionPolicy() rest.GarbageCollectionPolicy {
|
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
|
||||||
|
groupVersion := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
|
||||||
|
switch groupVersion {
|
||||||
|
case extensionsv1beta1.SchemeGroupVersion, appsv1beta2.SchemeGroupVersion:
|
||||||
|
// for back compatibility
|
||||||
|
return rest.OrphanDependents
|
||||||
|
default:
|
||||||
|
return rest.DeleteDependents
|
||||||
|
}
|
||||||
|
}
|
||||||
return rest.OrphanDependents
|
return rest.OrphanDependents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
|
"k8s.io/apiserver/pkg/registry/rest"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
)
|
)
|
||||||
|
@ -227,3 +228,57 @@ func newReplicaSetWithSelectorLabels(selectorLabels map[string]string) *extensio
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReplicasetDefaultGarbageCollectionPolicy(t *testing.T) {
|
||||||
|
// Make sure we correctly implement the interface.
|
||||||
|
// Otherwise a typo could silently change the default.
|
||||||
|
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
|
||||||
|
tests := []struct {
|
||||||
|
requestInfo genericapirequest.RequestInfo
|
||||||
|
expectedGCPolicy rest.GarbageCollectionPolicy
|
||||||
|
isNilRequestInfo bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "extensions",
|
||||||
|
APIVersion: "v1beta1",
|
||||||
|
Resource: "replicasets",
|
||||||
|
},
|
||||||
|
rest.OrphanDependents,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "apps",
|
||||||
|
APIVersion: "v1beta2",
|
||||||
|
Resource: "replicasets",
|
||||||
|
},
|
||||||
|
rest.OrphanDependents,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "apps",
|
||||||
|
APIVersion: "v1",
|
||||||
|
Resource: "replicasets",
|
||||||
|
},
|
||||||
|
rest.DeleteDependents,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expectedGCPolicy: rest.OrphanDependents,
|
||||||
|
isNilRequestInfo: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
context := genericapirequest.NewContext()
|
||||||
|
if !test.isNilRequestInfo {
|
||||||
|
context = genericapirequest.WithRequestInfo(context, &test.requestInfo)
|
||||||
|
}
|
||||||
|
if got, want := gcds.DefaultGarbageCollectionPolicy(context), test.expectedGCPolicy; got != want {
|
||||||
|
t.Errorf("%s/%s: DefaultGarbageCollectionPolicy() = %#v, want %#v", test.requestInfo.APIGroup,
|
||||||
|
test.requestInfo.APIVersion, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ go_test(
|
||||||
"//vendor/k8s.io/apimachinery/pkg/fields: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/labels:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime: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/runtime/serializer:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/selection:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/selection:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||||
|
|
|
@ -699,13 +699,18 @@ var (
|
||||||
// priority, there are three factors affect whether to add/remove the
|
// priority, there are three factors affect whether to add/remove the
|
||||||
// FinalizerOrphanDependents: options, existing finalizers of the object,
|
// FinalizerOrphanDependents: options, existing finalizers of the object,
|
||||||
// and e.DeleteStrategy.DefaultGarbageCollectionPolicy.
|
// and e.DeleteStrategy.DefaultGarbageCollectionPolicy.
|
||||||
func shouldOrphanDependents(e *Store, accessor metav1.Object, options *metav1.DeleteOptions) bool {
|
func shouldOrphanDependents(ctx genericapirequest.Context, e *Store, accessor metav1.Object, options *metav1.DeleteOptions) bool {
|
||||||
if gcStrategy, ok := e.DeleteStrategy.(rest.GarbageCollectionDeleteStrategy); ok {
|
// Get default GC policy from this REST object type
|
||||||
if gcStrategy.DefaultGarbageCollectionPolicy() == rest.Unsupported {
|
gcStrategy, ok := e.DeleteStrategy.(rest.GarbageCollectionDeleteStrategy)
|
||||||
|
var defaultGCPolicy rest.GarbageCollectionPolicy
|
||||||
|
if ok {
|
||||||
|
defaultGCPolicy = gcStrategy.DefaultGarbageCollectionPolicy(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if defaultGCPolicy == rest.Unsupported {
|
||||||
// return false to indicate that we should NOT orphan
|
// return false to indicate that we should NOT orphan
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// An explicit policy was set at deletion time, that overrides everything
|
// An explicit policy was set at deletion time, that overrides everything
|
||||||
if options != nil && options.OrphanDependents != nil {
|
if options != nil && options.OrphanDependents != nil {
|
||||||
|
@ -733,11 +738,9 @@ func shouldOrphanDependents(e *Store, accessor metav1.Object, options *metav1.De
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get default orphan policy from this REST object type if it exists
|
// Get default orphan policy from this REST object type if it exists
|
||||||
if gcStrategy, ok := e.DeleteStrategy.(rest.GarbageCollectionDeleteStrategy); ok {
|
if defaultGCPolicy == rest.OrphanDependents {
|
||||||
if gcStrategy.DefaultGarbageCollectionPolicy() == rest.OrphanDependents {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -746,9 +749,9 @@ func shouldOrphanDependents(e *Store, accessor metav1.Object, options *metav1.De
|
||||||
// priority, there are three factors affect whether to add/remove the
|
// priority, there are three factors affect whether to add/remove the
|
||||||
// FinalizerDeleteDependents: options, existing finalizers of the object, and
|
// FinalizerDeleteDependents: options, existing finalizers of the object, and
|
||||||
// e.DeleteStrategy.DefaultGarbageCollectionPolicy.
|
// e.DeleteStrategy.DefaultGarbageCollectionPolicy.
|
||||||
func shouldDeleteDependents(e *Store, accessor metav1.Object, options *metav1.DeleteOptions) bool {
|
func shouldDeleteDependents(ctx genericapirequest.Context, e *Store, accessor metav1.Object, options *metav1.DeleteOptions) bool {
|
||||||
// Get default orphan policy from this REST object type
|
// Get default GC policy from this REST object type
|
||||||
if gcStrategy, ok := e.DeleteStrategy.(rest.GarbageCollectionDeleteStrategy); ok && gcStrategy.DefaultGarbageCollectionPolicy() == rest.Unsupported {
|
if gcStrategy, ok := e.DeleteStrategy.(rest.GarbageCollectionDeleteStrategy); ok && gcStrategy.DefaultGarbageCollectionPolicy(ctx) == rest.Unsupported {
|
||||||
// return false to indicate that we should NOT delete in foreground
|
// return false to indicate that we should NOT delete in foreground
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -789,12 +792,12 @@ func shouldDeleteDependents(e *Store, accessor metav1.Object, options *metav1.De
|
||||||
// The finalizers returned are intended to be handled by the garbage collector.
|
// The finalizers returned are intended to be handled by the garbage collector.
|
||||||
// If garbage collection is disabled for the store, this function returns false
|
// If garbage collection is disabled for the store, this function returns false
|
||||||
// to ensure finalizers aren't set which will never be cleared.
|
// to ensure finalizers aren't set which will never be cleared.
|
||||||
func deletionFinalizersForGarbageCollection(e *Store, accessor metav1.Object, options *metav1.DeleteOptions) (bool, []string) {
|
func deletionFinalizersForGarbageCollection(ctx genericapirequest.Context, e *Store, accessor metav1.Object, options *metav1.DeleteOptions) (bool, []string) {
|
||||||
if !e.EnableGarbageCollection {
|
if !e.EnableGarbageCollection {
|
||||||
return false, []string{}
|
return false, []string{}
|
||||||
}
|
}
|
||||||
shouldOrphan := shouldOrphanDependents(e, accessor, options)
|
shouldOrphan := shouldOrphanDependents(ctx, e, accessor, options)
|
||||||
shouldDeleteDependentInForeground := shouldDeleteDependents(e, accessor, options)
|
shouldDeleteDependentInForeground := shouldDeleteDependents(ctx, e, accessor, options)
|
||||||
newFinalizers := []string{}
|
newFinalizers := []string{}
|
||||||
|
|
||||||
// first remove both finalizers, add them back if needed.
|
// first remove both finalizers, add them back if needed.
|
||||||
|
@ -880,7 +883,7 @@ func (e *Store) updateForGracefulDeletionAndFinalizers(ctx genericapirequest.Con
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
needsUpdate, newFinalizers := deletionFinalizersForGarbageCollection(e, existingAccessor, options)
|
needsUpdate, newFinalizers := deletionFinalizersForGarbageCollection(ctx, e, existingAccessor, options)
|
||||||
if needsUpdate {
|
if needsUpdate {
|
||||||
existingAccessor.SetFinalizers(newFinalizers)
|
existingAccessor.SetFinalizers(newFinalizers)
|
||||||
}
|
}
|
||||||
|
@ -972,7 +975,7 @@ func (e *Store) Delete(ctx genericapirequest.Context, name string, options *meta
|
||||||
|
|
||||||
// Handle combinations of graceful deletion and finalization by issuing
|
// Handle combinations of graceful deletion and finalization by issuing
|
||||||
// the correct updates.
|
// the correct updates.
|
||||||
shouldUpdateFinalizers, _ := deletionFinalizersForGarbageCollection(e, accessor, options)
|
shouldUpdateFinalizers, _ := deletionFinalizersForGarbageCollection(ctx, e, accessor, options)
|
||||||
// TODO: remove the check, because we support no-op updates now.
|
// TODO: remove the check, because we support no-op updates now.
|
||||||
if graceful || pendingFinalizers || shouldUpdateFinalizers {
|
if graceful || pendingFinalizers || shouldUpdateFinalizers {
|
||||||
err, ignoreNotFound, deleteImmediately, out, lastExisting = e.updateForGracefulDeletionAndFinalizers(ctx, name, key, options, preconditions, obj)
|
err, ignoreNotFound, deleteImmediately, out, lastExisting = e.updateForGracefulDeletionAndFinalizers(ctx, name, key, options, preconditions, obj)
|
||||||
|
|
|
@ -35,6 +35,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
"k8s.io/apimachinery/pkg/selection"
|
"k8s.io/apimachinery/pkg/selection"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
|
@ -82,7 +83,7 @@ type testOrphanDeleteStrategy struct {
|
||||||
*testRESTStrategy
|
*testRESTStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testOrphanDeleteStrategy) DefaultGarbageCollectionPolicy() rest.GarbageCollectionPolicy {
|
func (t *testOrphanDeleteStrategy) DefaultGarbageCollectionPolicy(ctx genericapirequest.Context) rest.GarbageCollectionPolicy {
|
||||||
return rest.OrphanDependents
|
return rest.OrphanDependents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2005,3 +2006,93 @@ func denyCreateValidation(obj runtime.Object) error {
|
||||||
func denyUpdateValidation(obj, old runtime.Object) error {
|
func denyUpdateValidation(obj, old runtime.Object) error {
|
||||||
return fmt.Errorf("admission denied")
|
return fmt.Errorf("admission denied")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fakeStrategy struct {
|
||||||
|
runtime.ObjectTyper
|
||||||
|
names.NameGenerator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fakeStrategy) DefaultGarbageCollectionPolicy(ctx genericapirequest.Context) rest.GarbageCollectionPolicy {
|
||||||
|
appsv1beta1 := schema.GroupVersion{Group: "apps", Version: "v1beta1"}
|
||||||
|
appsv1beta2 := schema.GroupVersion{Group: "apps", Version: "v1beta2"}
|
||||||
|
extensionsv1beta1 := schema.GroupVersion{Group: "extensions", Version: "v1beta1"}
|
||||||
|
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
|
||||||
|
groupVersion := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
|
||||||
|
switch groupVersion {
|
||||||
|
case appsv1beta1, appsv1beta2, extensionsv1beta1:
|
||||||
|
// for back compatibility
|
||||||
|
return rest.OrphanDependents
|
||||||
|
default:
|
||||||
|
return rest.DeleteDependents
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rest.OrphanDependents
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeletionFinalizersForGarbageCollection(t *testing.T) {
|
||||||
|
destroyFunc, registry := NewTestGenericStoreRegistry(t)
|
||||||
|
defer destroyFunc()
|
||||||
|
|
||||||
|
registry.DeleteStrategy = fakeStrategy{}
|
||||||
|
registry.EnableGarbageCollection = true
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
requestInfo genericapirequest.RequestInfo
|
||||||
|
desiredFinalizers []string
|
||||||
|
isNilRequestInfo bool
|
||||||
|
changed bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "extensions",
|
||||||
|
APIVersion: "v1beta1",
|
||||||
|
},
|
||||||
|
[]string{metav1.FinalizerOrphanDependents},
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "apps",
|
||||||
|
APIVersion: "v1beta1",
|
||||||
|
},
|
||||||
|
[]string{metav1.FinalizerOrphanDependents},
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "apps",
|
||||||
|
APIVersion: "v1beta2",
|
||||||
|
},
|
||||||
|
[]string{metav1.FinalizerOrphanDependents},
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
genericapirequest.RequestInfo{
|
||||||
|
APIGroup: "apps",
|
||||||
|
APIVersion: "v1",
|
||||||
|
},
|
||||||
|
[]string{},
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
context := genericapirequest.NewContext()
|
||||||
|
if !test.isNilRequestInfo {
|
||||||
|
context = genericapirequest.WithRequestInfo(context, &test.requestInfo)
|
||||||
|
}
|
||||||
|
changed, finalizers := deletionFinalizersForGarbageCollection(context, registry, &example.ReplicaSet{}, &metav1.DeleteOptions{})
|
||||||
|
if !changed {
|
||||||
|
if test.changed {
|
||||||
|
t.Errorf("%s/%s: no new finalizers are added", test.requestInfo.APIGroup, test.requestInfo.APIVersion)
|
||||||
|
}
|
||||||
|
} else if !reflect.DeepEqual(finalizers, test.desiredFinalizers) {
|
||||||
|
t.Errorf("%s/%s: want %#v, got %#v", test.requestInfo.APIGroup, test.requestInfo.APIVersion,
|
||||||
|
test.desiredFinalizers, finalizers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ const (
|
||||||
// orphan dependents by default.
|
// orphan dependents by default.
|
||||||
type GarbageCollectionDeleteStrategy interface {
|
type GarbageCollectionDeleteStrategy interface {
|
||||||
// DefaultGarbageCollectionPolicy returns the default garbage collection behavior.
|
// DefaultGarbageCollectionPolicy returns the default garbage collection behavior.
|
||||||
DefaultGarbageCollectionPolicy() GarbageCollectionPolicy
|
DefaultGarbageCollectionPolicy(ctx genericapirequest.Context) GarbageCollectionPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
// RESTGracefulDeleteStrategy must be implemented by the registry that supports
|
// RESTGracefulDeleteStrategy must be implemented by the registry that supports
|
||||||
|
|
|
@ -17,6 +17,7 @@ go_test(
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/api/v1/pod:go_default_library",
|
"//pkg/api/v1/pod:go_default_library",
|
||||||
"//pkg/controller/replicaset:go_default_library",
|
"//pkg/controller/replicaset:go_default_library",
|
||||||
|
"//pkg/util/slice:go_default_library",
|
||||||
"//test/integration/framework:go_default_library",
|
"//test/integration/framework:go_default_library",
|
||||||
"//test/utils:go_default_library",
|
"//test/utils:go_default_library",
|
||||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||||
|
|
|
@ -40,6 +40,7 @@ import (
|
||||||
"k8s.io/client-go/util/retry"
|
"k8s.io/client-go/util/retry"
|
||||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||||
"k8s.io/kubernetes/pkg/controller/replicaset"
|
"k8s.io/kubernetes/pkg/controller/replicaset"
|
||||||
|
"k8s.io/kubernetes/pkg/util/slice"
|
||||||
"k8s.io/kubernetes/test/integration/framework"
|
"k8s.io/kubernetes/test/integration/framework"
|
||||||
testutil "k8s.io/kubernetes/test/utils"
|
testutil "k8s.io/kubernetes/test/utils"
|
||||||
)
|
)
|
||||||
|
@ -924,3 +925,115 @@ func TestFullyLabeledReplicas(t *testing.T) {
|
||||||
t.Fatalf("Failed to verify only one pod is fully labeled: %v", err)
|
t.Fatalf("Failed to verify only one pod is fully labeled: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReplicaSetsExtensionsV1beta1DefaultGCPolicy(t *testing.T) {
|
||||||
|
s, closeFn, rm, informers, c := rmSetup(t)
|
||||||
|
defer closeFn()
|
||||||
|
ns := framework.CreateTestingNamespace("test-default-gc-extensions", s, t)
|
||||||
|
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||||
|
stopCh := runControllerAndInformers(t, rm, informers, 0)
|
||||||
|
defer close(stopCh)
|
||||||
|
|
||||||
|
rs := newRS("rs", ns.Name, 2)
|
||||||
|
fakeFinalizer := "kube.io/dummy-finalizer"
|
||||||
|
rs.Finalizers = []string{fakeFinalizer}
|
||||||
|
rss, _ := createRSsPods(t, c, []*v1beta1.ReplicaSet{rs}, []*v1.Pod{})
|
||||||
|
rs = rss[0]
|
||||||
|
waitRSStable(t, c, rs)
|
||||||
|
|
||||||
|
// Verify RS creates 2 pods
|
||||||
|
podClient := c.CoreV1().Pods(ns.Name)
|
||||||
|
pods := getPods(t, podClient, labelMap())
|
||||||
|
if len(pods.Items) != 2 {
|
||||||
|
t.Fatalf("len(pods) = %d, want 2", len(pods.Items))
|
||||||
|
}
|
||||||
|
|
||||||
|
rsClient := c.ExtensionsV1beta1().ReplicaSets(ns.Name)
|
||||||
|
err := rsClient.Delete(rs.Name, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to delete rs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify orphan finalizer has been added
|
||||||
|
if err := wait.PollImmediate(interval, timeout, func() (bool, error) {
|
||||||
|
newRS, err := rsClient.Get(rs.Name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return slice.ContainsString(newRS.Finalizers, metav1.FinalizerOrphanDependents, nil), nil
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("Failed to verify orphan finalizer is added: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRS(t, rsClient, rs.Name, func(rs *v1beta1.ReplicaSet) {
|
||||||
|
var finalizers []string
|
||||||
|
// remove fakeFinalizer
|
||||||
|
for _, finalizer := range rs.Finalizers {
|
||||||
|
if finalizer != fakeFinalizer {
|
||||||
|
finalizers = append(finalizers, finalizer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rs.Finalizers = finalizers
|
||||||
|
})
|
||||||
|
|
||||||
|
rsClient.Delete(rs.Name, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReplicaSetsAppsV1DefaultGCPolicy(t *testing.T) {
|
||||||
|
s, closeFn, rm, informers, c := rmSetup(t)
|
||||||
|
defer closeFn()
|
||||||
|
ns := framework.CreateTestingNamespace("test-default-gc-extensions", s, t)
|
||||||
|
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||||
|
stopCh := runControllerAndInformers(t, rm, informers, 0)
|
||||||
|
defer close(stopCh)
|
||||||
|
|
||||||
|
rs := newRS("rs", ns.Name, 2)
|
||||||
|
fakeFinalizer := "kube.io/dummy-finalizer"
|
||||||
|
rs.Finalizers = []string{fakeFinalizer}
|
||||||
|
rss, _ := createRSsPods(t, c, []*v1beta1.ReplicaSet{rs}, []*v1.Pod{})
|
||||||
|
rs = rss[0]
|
||||||
|
waitRSStable(t, c, rs)
|
||||||
|
|
||||||
|
// Verify RS creates 2 pods
|
||||||
|
podClient := c.CoreV1().Pods(ns.Name)
|
||||||
|
pods := getPods(t, podClient, labelMap())
|
||||||
|
if len(pods.Items) != 2 {
|
||||||
|
t.Fatalf("len(pods) = %d, want 2", len(pods.Items))
|
||||||
|
}
|
||||||
|
|
||||||
|
rsClient := c.AppsV1().ReplicaSets(ns.Name)
|
||||||
|
err := rsClient.Delete(rs.Name, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to delete rs: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify no new finalizer has been added
|
||||||
|
if err := wait.PollImmediate(interval, timeout, func() (bool, error) {
|
||||||
|
newRS, err := rsClient.Get(rs.Name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if newRS.DeletionTimestamp == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if got, want := newRS.Finalizers, []string{fakeFinalizer}; !reflect.DeepEqual(got, want) {
|
||||||
|
return false, fmt.Errorf("got finalizers: %+v; want: %+v", got, want)
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("Failed to verify the finalizer: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRS(t, c.ExtensionsV1beta1().ReplicaSets(ns.Name), rs.Name, func(rs *v1beta1.ReplicaSet) {
|
||||||
|
var finalizers []string
|
||||||
|
// remove fakeFinalizer
|
||||||
|
for _, finalizer := range rs.Finalizers {
|
||||||
|
if finalizer != fakeFinalizer {
|
||||||
|
finalizers = append(finalizers, finalizer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rs.Finalizers = finalizers
|
||||||
|
})
|
||||||
|
|
||||||
|
rsClient.Delete(rs.Name, nil)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue