Merge pull request #99661 from kevindelgado/status-wiping

Server-Side Apply status wiping
This commit is contained in:
Kubernetes Prow Robot 2021-03-10 00:35:14 -08:00 committed by GitHub
commit b7d23d7111
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 1468 additions and 125 deletions

View File

@ -270,7 +270,6 @@ API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/ap
API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,CustomResourceDefinitionNames,Categories API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,CustomResourceDefinitionNames,Categories
API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,CustomResourceDefinitionNames,ShortNames API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,CustomResourceDefinitionNames,ShortNames
API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,CustomResourceDefinitionSpec,Versions API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,CustomResourceDefinitionSpec,Versions
API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,CustomResourceDefinitionStatus,Conditions
API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,CustomResourceDefinitionStatus,StoredVersions API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,CustomResourceDefinitionStatus,StoredVersions
API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,CustomResourceDefinitionVersion,AdditionalPrinterColumns API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,CustomResourceDefinitionVersion,AdditionalPrinterColumns
API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,JSON,Raw API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,JSON,Raw
@ -291,7 +290,6 @@ API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/ap
API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,CustomResourceDefinitionNames,ShortNames API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,CustomResourceDefinitionNames,ShortNames
API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,CustomResourceDefinitionSpec,AdditionalPrinterColumns API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,CustomResourceDefinitionSpec,AdditionalPrinterColumns
API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,CustomResourceDefinitionSpec,Versions API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,CustomResourceDefinitionSpec,Versions
API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,CustomResourceDefinitionStatus,Conditions
API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,CustomResourceDefinitionStatus,StoredVersions API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,CustomResourceDefinitionStatus,StoredVersions
API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,CustomResourceDefinitionVersion,AdditionalPrinterColumns API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,CustomResourceDefinitionVersion,AdditionalPrinterColumns
API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,JSON,Raw API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,JSON,Raw

View File

@ -17256,7 +17256,11 @@
"items": { "items": {
"$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinitionCondition" "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.CustomResourceDefinitionCondition"
}, },
"type": "array" "type": "array",
"x-kubernetes-list-map-keys": [
"type"
],
"x-kubernetes-list-type": "map"
}, },
"storedVersions": { "storedVersions": {
"description": "storedVersions lists all versions of CustomResources that were ever persisted. Tracking these versions allows a migration path for stored versions in etcd. The field is mutable so a migration controller can finish a migration to another version (ensuring no old objects are left in storage), and then remove the rest of the versions from this list. Versions may not be removed from `spec.versions` while they exist in this list.", "description": "storedVersions lists all versions of CustomResources that were ever persisted. Tracking these versions allows a migration path for stored versions in etcd. The field is mutable so a migration controller can finish a migration to another version (ensuring no old objects are left in storage), and then remove the rest of the versions from this list. Versions may not be removed from `spec.versions` while they exist in this list.",
@ -17904,7 +17908,11 @@
"items": { "items": {
"$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionCondition" "$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionCondition"
}, },
"type": "array" "type": "array",
"x-kubernetes-list-map-keys": [
"type"
],
"x-kubernetes-list-type": "map"
}, },
"storedVersions": { "storedVersions": {
"description": "storedVersions lists all versions of CustomResources that were ever persisted. Tracking these versions allows a migration path for stored versions in etcd. The field is mutable so a migration controller can finish a migration to another version (ensuring no old objects are left in storage), and then remove the rest of the versions from this list. Versions may not be removed from `spec.versions` while they exist in this list.", "description": "storedVersions lists all versions of CustomResources that were ever persisted. Tracking these versions allows a migration path for stored versions in etcd. The field is mutable so a migration controller can finish a migration to another version (ensuring no old objects are left in storage), and then remove the rest of the versions from this list. Versions may not be removed from `spec.versions` while they exist in this list.",

1
go.mod
View File

@ -137,6 +137,7 @@ require (
k8s.io/sample-apiserver v0.0.0 k8s.io/sample-apiserver v0.0.0
k8s.io/system-validators v1.4.0 k8s.io/system-validators v1.4.0
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 k8s.io/utils v0.0.0-20201110183641-67b214c5f920
sigs.k8s.io/structured-merge-diff/v4 v4.0.3
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )

View File

@ -130,6 +130,10 @@ EOF
# Creates a node object with name 127.0.0.1. This is required because we do not # Creates a node object with name 127.0.0.1. This is required because we do not
# run kubelet. # run kubelet.
# #
# An arbitrary annotation is needed to ensure field managers are saved on the
# object. Without it, we would be creating an empty object and because status
# and name get wiped, there were be no field managers tracking any fields.
#
# Exports: # Exports:
# SUPPORTED_RESOURCES(Array of all resources supported by the apiserver). # SUPPORTED_RESOURCES(Array of all resources supported by the apiserver).
function create_node() { function create_node() {
@ -138,7 +142,10 @@ function create_node() {
"kind": "Node", "kind": "Node",
"apiVersion": "v1", "apiVersion": "v1",
"metadata": { "metadata": {
"name": "127.0.0.1" "name": "127.0.0.1",
"annotations": {
"save-managers": "true"
}
}, },
"status": { "status": {
"capacity": { "capacity": {

View File

@ -29,6 +29,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
strategy "k8s.io/kubernetes/pkg/registry/apiserverinternal/storageversion" strategy "k8s.io/kubernetes/pkg/registry/apiserverinternal/storageversion"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// REST implements a RESTStorage for storage version against etcd // REST implements a RESTStorage for storage version against etcd
@ -49,6 +50,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
CreateStrategy: strategy.Strategy, CreateStrategy: strategy.Strategy,
UpdateStrategy: strategy.Strategy, UpdateStrategy: strategy.Strategy,
DeleteStrategy: strategy.Strategy, DeleteStrategy: strategy.Strategy,
ResetFieldsStrategy: strategy.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
options := &generic.StoreOptions{RESTOptions: optsGetter} options := &generic.StoreOptions{RESTOptions: optsGetter}
@ -57,6 +59,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
} }
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = strategy.StatusStrategy statusStore.UpdateStrategy = strategy.StatusStrategy
statusStore.ResetFieldsStrategy = strategy.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}, nil return &REST{store}, &StatusREST{store: &statusStore}, nil
} }
@ -81,3 +84,8 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -26,6 +26,7 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/apiserverinternal" "k8s.io/kubernetes/pkg/apis/apiserverinternal"
"k8s.io/kubernetes/pkg/apis/apiserverinternal/validation" "k8s.io/kubernetes/pkg/apis/apiserverinternal/validation"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// storageVersionStrategy implements verification logic for StorageVersion. // storageVersionStrategy implements verification logic for StorageVersion.
@ -42,6 +43,18 @@ func (storageVersionStrategy) NamespaceScoped() bool {
return false return false
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (storageVersionStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"internal.apiserver.k8s.io/v1alpha1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears the status of an StorageVersion before creation. // PrepareForCreate clears the status of an StorageVersion before creation.
func (storageVersionStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (storageVersionStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
sv := obj.(*apiserverinternal.StorageVersion) sv := obj.(*apiserverinternal.StorageVersion)
@ -90,6 +103,19 @@ type storageVersionStatusStrategy struct {
// StatusStrategy is the default logic invoked when updating object status. // StatusStrategy is the default logic invoked when updating object status.
var StatusStrategy = storageVersionStatusStrategy{Strategy} var StatusStrategy = storageVersionStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (storageVersionStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"internal.apiserver.k8s.io/v1alpha1": fieldpath.NewSet(
fieldpath.MakePathOrDie("metadata"),
fieldpath.MakePathOrDie("spec"),
),
}
return fields
}
func (storageVersionStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (storageVersionStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newSV := obj.(*apiserverinternal.StorageVersion) newSV := obj.(*apiserverinternal.StorageVersion)
oldSV := old.(*apiserverinternal.StorageVersion) oldSV := old.(*apiserverinternal.StorageVersion)

View File

@ -29,6 +29,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/apps/daemonset" "k8s.io/kubernetes/pkg/registry/apps/daemonset"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// REST implements a RESTStorage for DaemonSets // REST implements a RESTStorage for DaemonSets
@ -47,6 +48,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
CreateStrategy: daemonset.Strategy, CreateStrategy: daemonset.Strategy,
UpdateStrategy: daemonset.Strategy, UpdateStrategy: daemonset.Strategy,
DeleteStrategy: daemonset.Strategy, DeleteStrategy: daemonset.Strategy,
ResetFieldsStrategy: daemonset.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -57,6 +59,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = daemonset.StatusStrategy statusStore.UpdateStrategy = daemonset.StatusStrategy
statusStore.ResetFieldsStrategy = daemonset.StatusStrategy
return &REST{store, []string{"all"}}, &StatusREST{store: &statusStore}, nil return &REST{store, []string{"all"}}, &StatusREST{store: &statusStore}, nil
} }
@ -103,3 +106,8 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -36,6 +36,7 @@ import (
"k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/apps/validation" "k8s.io/kubernetes/pkg/apis/apps/validation"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// daemonSetStrategy implements verification logic for daemon sets. // daemonSetStrategy implements verification logic for daemon sets.
@ -68,6 +69,18 @@ func (daemonSetStrategy) NamespaceScoped() bool {
return true return true
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (daemonSetStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"apps/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears the status of a daemon set before creation. // PrepareForCreate clears the status of a daemon set before creation.
func (daemonSetStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (daemonSetStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
daemonSet := obj.(*apps.DaemonSet) daemonSet := obj.(*apps.DaemonSet)
@ -202,6 +215,16 @@ type daemonSetStatusStrategy struct {
// StatusStrategy is the default logic invoked when updating object status. // StatusStrategy is the default logic invoked when updating object status.
var StatusStrategy = daemonSetStatusStrategy{Strategy} var StatusStrategy = daemonSetStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (daemonSetStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return map[fieldpath.APIVersion]*fieldpath.Set{
"apps/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
}
}
func (daemonSetStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (daemonSetStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newDaemonSet := obj.(*apps.DaemonSet) newDaemonSet := obj.(*apps.DaemonSet)
oldDaemonSet := old.(*apps.DaemonSet) oldDaemonSet := old.(*apps.DaemonSet)

View File

@ -43,6 +43,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/apps/deployment" "k8s.io/kubernetes/pkg/registry/apps/deployment"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// DeploymentStorage includes dummy storage for Deployments and for Scale subresource. // DeploymentStorage includes dummy storage for Deployments and for Scale subresource.
@ -84,6 +85,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Rollbac
CreateStrategy: deployment.Strategy, CreateStrategy: deployment.Strategy,
UpdateStrategy: deployment.Strategy, UpdateStrategy: deployment.Strategy,
DeleteStrategy: deployment.Strategy, DeleteStrategy: deployment.Strategy,
ResetFieldsStrategy: deployment.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -94,6 +96,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Rollbac
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = deployment.StatusStrategy statusStore.UpdateStrategy = deployment.StatusStrategy
statusStore.ResetFieldsStrategy = deployment.StatusStrategy
return &REST{store, []string{"all"}}, &StatusREST{store: &statusStore}, &RollbackREST{store: store}, nil return &REST{store, []string{"all"}}, &StatusREST{store: &statusStore}, &RollbackREST{store: store}, nil
} }
@ -141,6 +144,11 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}
// RollbackREST implements the REST endpoint for initiating the rollback of a deployment // RollbackREST implements the REST endpoint for initiating the rollback of a deployment
type RollbackREST struct { type RollbackREST struct {
store *genericregistry.Store store *genericregistry.Store

View File

@ -34,6 +34,7 @@ import (
"k8s.io/kubernetes/pkg/api/pod" "k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/apps/validation" "k8s.io/kubernetes/pkg/apis/apps/validation"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// deploymentStrategy implements behavior for Deployments. // deploymentStrategy implements behavior for Deployments.
@ -67,6 +68,18 @@ func (deploymentStrategy) NamespaceScoped() bool {
return true return true
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (deploymentStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"apps/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears fields that are not allowed to be set by end users on creation. // PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (deploymentStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (deploymentStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
deployment := obj.(*apps.Deployment) deployment := obj.(*apps.Deployment)
@ -147,6 +160,17 @@ type deploymentStatusStrategy struct {
// StatusStrategy is the default logic invoked when updating object status. // StatusStrategy is the default logic invoked when updating object status.
var StatusStrategy = deploymentStatusStrategy{Strategy} var StatusStrategy = deploymentStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (deploymentStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return map[fieldpath.APIVersion]*fieldpath.Set{
"apps/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
fieldpath.MakePathOrDie("metadata", "labels"),
),
}
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update of status // PrepareForUpdate clears fields that are not allowed to be set by end users on update of status
func (deploymentStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (deploymentStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newDeployment := obj.(*apps.Deployment) newDeployment := obj.(*apps.Deployment)

View File

@ -40,6 +40,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/apps/replicaset" "k8s.io/kubernetes/pkg/registry/apps/replicaset"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// ReplicaSetStorage includes dummy storage for ReplicaSets and for Scale subresource. // ReplicaSetStorage includes dummy storage for ReplicaSets and for Scale subresource.
@ -80,6 +81,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
CreateStrategy: replicaset.Strategy, CreateStrategy: replicaset.Strategy,
UpdateStrategy: replicaset.Strategy, UpdateStrategy: replicaset.Strategy,
DeleteStrategy: replicaset.Strategy, DeleteStrategy: replicaset.Strategy,
ResetFieldsStrategy: replicaset.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -90,6 +92,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = replicaset.StatusStrategy statusStore.UpdateStrategy = replicaset.StatusStrategy
statusStore.ResetFieldsStrategy = replicaset.StatusStrategy
return &REST{store, []string{"all"}}, &StatusREST{store: &statusStore}, nil return &REST{store, []string{"all"}}, &StatusREST{store: &statusStore}, nil
} }
@ -138,6 +141,11 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}
// ScaleREST implements a Scale for ReplicaSet. // ScaleREST implements a Scale for ReplicaSet.
type ScaleREST struct { type ScaleREST struct {
store *genericregistry.Store store *genericregistry.Store

View File

@ -41,6 +41,7 @@ import (
"k8s.io/kubernetes/pkg/api/pod" "k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/apps/validation" "k8s.io/kubernetes/pkg/apis/apps/validation"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// rsStrategy implements verification logic for ReplicaSets. // rsStrategy implements verification logic for ReplicaSets.
@ -73,6 +74,18 @@ func (rsStrategy) NamespaceScoped() bool {
return true return true
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (rsStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"apps/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears the status of a ReplicaSet before creation. // PrepareForCreate clears the status of a ReplicaSet before creation.
func (rsStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (rsStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
rs := obj.(*apps.ReplicaSet) rs := obj.(*apps.ReplicaSet)
@ -189,6 +202,16 @@ type rsStatusStrategy struct {
// StatusStrategy is the default logic invoked when updating object status. // StatusStrategy is the default logic invoked when updating object status.
var StatusStrategy = rsStatusStrategy{Strategy} var StatusStrategy = rsStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (rsStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return map[fieldpath.APIVersion]*fieldpath.Set{
"apps/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
}
}
func (rsStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (rsStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newRS := obj.(*apps.ReplicaSet) newRS := obj.(*apps.ReplicaSet)
oldRS := old.(*apps.ReplicaSet) oldRS := old.(*apps.ReplicaSet)

View File

@ -37,6 +37,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/apps/statefulset" "k8s.io/kubernetes/pkg/registry/apps/statefulset"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// StatefulSetStorage includes dummy storage for StatefulSets, and their Status and Scale subresource. // StatefulSetStorage includes dummy storage for StatefulSets, and their Status and Scale subresource.
@ -75,6 +76,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
CreateStrategy: statefulset.Strategy, CreateStrategy: statefulset.Strategy,
UpdateStrategy: statefulset.Strategy, UpdateStrategy: statefulset.Strategy,
DeleteStrategy: statefulset.Strategy, DeleteStrategy: statefulset.Strategy,
ResetFieldsStrategy: statefulset.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -85,6 +87,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = statefulset.StatusStrategy statusStore.UpdateStrategy = statefulset.StatusStrategy
statusStore.ResetFieldsStrategy = statefulset.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}, nil return &REST{store}, &StatusREST{store: &statusStore}, nil
} }
@ -118,6 +121,11 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}
// Implement ShortNamesProvider // Implement ShortNamesProvider
var _ rest.ShortNamesProvider = &REST{} var _ rest.ShortNamesProvider = &REST{}

View File

@ -32,6 +32,7 @@ import (
"k8s.io/kubernetes/pkg/api/pod" "k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/apps/validation" "k8s.io/kubernetes/pkg/apis/apps/validation"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// statefulSetStrategy implements verification logic for Replication StatefulSets. // statefulSetStrategy implements verification logic for Replication StatefulSets.
@ -64,6 +65,18 @@ func (statefulSetStrategy) NamespaceScoped() bool {
return true return true
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (statefulSetStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"apps/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears the status of an StatefulSet before creation. // PrepareForCreate clears the status of an StatefulSet before creation.
func (statefulSetStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (statefulSetStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
statefulSet := obj.(*apps.StatefulSet) statefulSet := obj.(*apps.StatefulSet)
@ -132,6 +145,16 @@ type statefulSetStatusStrategy struct {
// StatusStrategy is the default logic invoked when updating object status. // StatusStrategy is the default logic invoked when updating object status.
var StatusStrategy = statefulSetStatusStrategy{Strategy} var StatusStrategy = statefulSetStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (statefulSetStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return map[fieldpath.APIVersion]*fieldpath.Set{
"apps/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
}
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update of status // PrepareForUpdate clears fields that are not allowed to be set by end users on update of status
func (statefulSetStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (statefulSetStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newStatefulSet := obj.(*apps.StatefulSet) newStatefulSet := obj.(*apps.StatefulSet)

View File

@ -29,6 +29,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/autoscaling/horizontalpodautoscaler" "k8s.io/kubernetes/pkg/registry/autoscaling/horizontalpodautoscaler"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// REST implements a RESTStorage for pod disruption budgets against etcd // REST implements a RESTStorage for pod disruption budgets against etcd
@ -46,6 +47,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
CreateStrategy: horizontalpodautoscaler.Strategy, CreateStrategy: horizontalpodautoscaler.Strategy,
UpdateStrategy: horizontalpodautoscaler.Strategy, UpdateStrategy: horizontalpodautoscaler.Strategy,
DeleteStrategy: horizontalpodautoscaler.Strategy, DeleteStrategy: horizontalpodautoscaler.Strategy,
ResetFieldsStrategy: horizontalpodautoscaler.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -56,6 +58,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = horizontalpodautoscaler.StatusStrategy statusStore.UpdateStrategy = horizontalpodautoscaler.StatusStrategy
statusStore.ResetFieldsStrategy = horizontalpodautoscaler.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}, nil return &REST{store}, &StatusREST{store: &statusStore}, nil
} }
@ -96,3 +99,8 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -27,6 +27,7 @@ import (
"k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/autoscaling"
"k8s.io/kubernetes/pkg/apis/autoscaling/validation" "k8s.io/kubernetes/pkg/apis/autoscaling/validation"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// autoscalerStrategy implements behavior for HorizontalPodAutoscalers // autoscalerStrategy implements behavior for HorizontalPodAutoscalers
@ -44,6 +45,24 @@ func (autoscalerStrategy) NamespaceScoped() bool {
return true return true
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (autoscalerStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"autoscaling/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
"autoscaling/v2beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
"autoscaling/v2beta2": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears fields that are not allowed to be set by end users on creation. // PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (autoscalerStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (autoscalerStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
newHPA := obj.(*autoscaling.HorizontalPodAutoscaler) newHPA := obj.(*autoscaling.HorizontalPodAutoscaler)
@ -115,6 +134,24 @@ type autoscalerStatusStrategy struct {
// StatusStrategy is the default logic invoked when updating object status. // StatusStrategy is the default logic invoked when updating object status.
var StatusStrategy = autoscalerStatusStrategy{Strategy} var StatusStrategy = autoscalerStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (autoscalerStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"autoscaling/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
"autoscaling/v2beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
"autoscaling/v2beta2": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
}
return fields
}
func (autoscalerStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (autoscalerStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newAutoscaler := obj.(*autoscaling.HorizontalPodAutoscaler) newAutoscaler := obj.(*autoscaling.HorizontalPodAutoscaler)
oldAutoscaler := old.(*autoscaling.HorizontalPodAutoscaler) oldAutoscaler := old.(*autoscaling.HorizontalPodAutoscaler)

View File

@ -29,6 +29,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/batch/cronjob" "k8s.io/kubernetes/pkg/registry/batch/cronjob"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// REST implements a RESTStorage for scheduled jobs against etcd // REST implements a RESTStorage for scheduled jobs against etcd
@ -46,6 +47,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
CreateStrategy: cronjob.Strategy, CreateStrategy: cronjob.Strategy,
UpdateStrategy: cronjob.Strategy, UpdateStrategy: cronjob.Strategy,
DeleteStrategy: cronjob.Strategy, DeleteStrategy: cronjob.Strategy,
ResetFieldsStrategy: cronjob.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -56,6 +58,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = cronjob.StatusStrategy statusStore.UpdateStrategy = cronjob.StatusStrategy
statusStore.ResetFieldsStrategy = cronjob.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}, nil return &REST{store}, &StatusREST{store: &statusStore}, nil
} }
@ -94,3 +97,8 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -30,6 +30,7 @@ import (
"k8s.io/kubernetes/pkg/api/pod" "k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/batch/validation" "k8s.io/kubernetes/pkg/apis/batch/validation"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// cronJobStrategy implements verification logic for Replication Controllers. // cronJobStrategy implements verification logic for Replication Controllers.
@ -62,6 +63,21 @@ func (cronJobStrategy) NamespaceScoped() bool {
return true return true
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (cronJobStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"batch/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
"batch/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears the status of a scheduled job before creation. // PrepareForCreate clears the status of a scheduled job before creation.
func (cronJobStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (cronJobStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
cronJob := obj.(*batch.CronJob) cronJob := obj.(*batch.CronJob)
@ -115,6 +131,19 @@ type cronJobStatusStrategy struct {
// StatusStrategy is the default logic invoked when updating object status. // StatusStrategy is the default logic invoked when updating object status.
var StatusStrategy = cronJobStatusStrategy{Strategy} var StatusStrategy = cronJobStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (cronJobStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return map[fieldpath.APIVersion]*fieldpath.Set{
"batch/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
"batch/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
}
}
func (cronJobStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (cronJobStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newJob := obj.(*batch.CronJob) newJob := obj.(*batch.CronJob)
oldJob := old.(*batch.CronJob) oldJob := old.(*batch.CronJob)

View File

@ -29,6 +29,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/batch/job" "k8s.io/kubernetes/pkg/registry/batch/job"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// JobStorage includes dummy storage for Job. // JobStorage includes dummy storage for Job.
@ -66,6 +67,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
CreateStrategy: job.Strategy, CreateStrategy: job.Strategy,
UpdateStrategy: job.Strategy, UpdateStrategy: job.Strategy,
DeleteStrategy: job.Strategy, DeleteStrategy: job.Strategy,
ResetFieldsStrategy: job.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -76,6 +78,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = job.StatusStrategy statusStore.UpdateStrategy = job.StatusStrategy
statusStore.ResetFieldsStrategy = job.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}, nil return &REST{store}, &StatusREST{store: &statusStore}, nil
} }
@ -109,3 +112,8 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -40,6 +40,7 @@ import (
"k8s.io/kubernetes/pkg/apis/batch/validation" "k8s.io/kubernetes/pkg/apis/batch/validation"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
"k8s.io/utils/pointer" "k8s.io/utils/pointer"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// jobStrategy implements verification logic for Replication Controllers. // jobStrategy implements verification logic for Replication Controllers.
@ -72,6 +73,18 @@ func (jobStrategy) NamespaceScoped() bool {
return true return true
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (jobStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"batch/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears the status of a job before creation. // PrepareForCreate clears the status of a job before creation.
func (jobStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (jobStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
job := obj.(*batch.Job) job := obj.(*batch.Job)
@ -210,6 +223,16 @@ type jobStatusStrategy struct {
var StatusStrategy = jobStatusStrategy{Strategy} var StatusStrategy = jobStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (jobStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return map[fieldpath.APIVersion]*fieldpath.Set{
"batch/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
}
}
func (jobStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (jobStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newJob := obj.(*batch.Job) newJob := obj.(*batch.Job)
oldJob := old.(*batch.Job) oldJob := old.(*batch.Job)

View File

@ -29,6 +29,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
csrregistry "k8s.io/kubernetes/pkg/registry/certificates/certificates" csrregistry "k8s.io/kubernetes/pkg/registry/certificates/certificates"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// REST implements a RESTStorage for CertificateSigningRequest. // REST implements a RESTStorage for CertificateSigningRequest.
@ -46,6 +47,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Approva
CreateStrategy: csrregistry.Strategy, CreateStrategy: csrregistry.Strategy,
UpdateStrategy: csrregistry.Strategy, UpdateStrategy: csrregistry.Strategy,
DeleteStrategy: csrregistry.Strategy, DeleteStrategy: csrregistry.Strategy,
ResetFieldsStrategy: csrregistry.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -59,9 +61,11 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Approva
// dedicated strategies. // dedicated strategies.
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = csrregistry.StatusStrategy statusStore.UpdateStrategy = csrregistry.StatusStrategy
statusStore.ResetFieldsStrategy = csrregistry.StatusStrategy
approvalStore := *store approvalStore := *store
approvalStore.UpdateStrategy = csrregistry.ApprovalStrategy approvalStore.UpdateStrategy = csrregistry.ApprovalStrategy
approvalStore.ResetFieldsStrategy = csrregistry.ApprovalStrategy
return &REST{store}, &StatusREST{store: &statusStore}, &ApprovalREST{store: &approvalStore}, nil return &REST{store}, &StatusREST{store: &statusStore}, &ApprovalREST{store: &approvalStore}, nil
} }
@ -96,6 +100,11 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}
var _ = rest.Patcher(&StatusREST{}) var _ = rest.Patcher(&StatusREST{})
// ApprovalREST implements the REST endpoint for changing the approval state of a CSR. // ApprovalREST implements the REST endpoint for changing the approval state of a CSR.
@ -120,4 +129,9 @@ func (r *ApprovalREST) Update(ctx context.Context, name string, objInfo rest.Upd
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *ApprovalREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}
var _ = rest.Patcher(&ApprovalREST{}) var _ = rest.Patcher(&ApprovalREST{})

View File

@ -33,6 +33,7 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/certificates" "k8s.io/kubernetes/pkg/apis/certificates"
"k8s.io/kubernetes/pkg/apis/certificates/validation" "k8s.io/kubernetes/pkg/apis/certificates/validation"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// csrStrategy implements behavior for CSRs // csrStrategy implements behavior for CSRs
@ -50,6 +51,23 @@ func (csrStrategy) NamespaceScoped() bool {
return false return false
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (csrStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"certificates.k8s.io/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
fieldpath.MakePathOrDie("status"),
),
"certificates.k8s.io/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// AllowCreateOnUpdate is false for CSRs. // AllowCreateOnUpdate is false for CSRs.
func (csrStrategy) AllowCreateOnUpdate() bool { func (csrStrategy) AllowCreateOnUpdate() bool {
return false return false
@ -125,6 +143,23 @@ type csrStatusStrategy struct {
var StatusStrategy = csrStatusStrategy{Strategy} var StatusStrategy = csrStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (csrStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"certificates.k8s.io/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
fieldpath.MakePathOrDie("status", "conditions"),
),
"certificates.k8s.io/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
fieldpath.MakePathOrDie("status", "conditions"),
),
}
return fields
}
func (csrStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (csrStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newCSR := obj.(*certificates.CertificateSigningRequest) newCSR := obj.(*certificates.CertificateSigningRequest)
oldCSR := old.(*certificates.CertificateSigningRequest) oldCSR := old.(*certificates.CertificateSigningRequest)
@ -220,6 +255,23 @@ type csrApprovalStrategy struct {
var ApprovalStrategy = csrApprovalStrategy{Strategy} var ApprovalStrategy = csrApprovalStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (csrApprovalStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"certificates.k8s.io/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
fieldpath.MakePathOrDie("status", "certificate"),
),
"certificates.k8s.io/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
fieldpath.MakePathOrDie("status", "certificate"),
),
}
return fields
}
// PrepareForUpdate prepares the new certificate signing request by limiting // PrepareForUpdate prepares the new certificate signing request by limiting
// the data that is updated to only the conditions and populating condition timestamps // the data that is updated to only the conditions and populating condition timestamps
func (csrApprovalStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (csrApprovalStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {

View File

@ -24,6 +24,7 @@ import (
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
@ -31,13 +32,12 @@ import (
"k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage"
storageerr "k8s.io/apiserver/pkg/storage/errors" storageerr "k8s.io/apiserver/pkg/storage/errors"
"k8s.io/apiserver/pkg/util/dryrun" "k8s.io/apiserver/pkg/util/dryrun"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/core/namespace" "k8s.io/kubernetes/pkg/registry/core/namespace"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// rest implements a RESTStorage for namespaces // rest implements a RESTStorage for namespaces
@ -67,6 +67,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Finaliz
CreateStrategy: namespace.Strategy, CreateStrategy: namespace.Strategy,
UpdateStrategy: namespace.Strategy, UpdateStrategy: namespace.Strategy,
DeleteStrategy: namespace.Strategy, DeleteStrategy: namespace.Strategy,
ResetFieldsStrategy: namespace.Strategy,
ReturnDeletedObject: true, ReturnDeletedObject: true,
ShouldDeleteDuringUpdate: ShouldDeleteNamespaceDuringUpdate, ShouldDeleteDuringUpdate: ShouldDeleteNamespaceDuringUpdate,
@ -80,9 +81,11 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Finaliz
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = namespace.StatusStrategy statusStore.UpdateStrategy = namespace.StatusStrategy
statusStore.ResetFieldsStrategy = namespace.StatusStrategy
finalizeStore := *store finalizeStore := *store
finalizeStore.UpdateStrategy = namespace.FinalizeStrategy finalizeStore.UpdateStrategy = namespace.FinalizeStrategy
finalizeStore.ResetFieldsStrategy = namespace.FinalizeStrategy
return &REST{store: store, status: &statusStore}, &StatusREST{store: &statusStore}, &FinalizeREST{store: &finalizeStore}, nil return &REST{store: store, status: &statusStore}, &StatusREST{store: &statusStore}, &FinalizeREST{store: &finalizeStore}, nil
} }
@ -291,6 +294,10 @@ func (r *REST) StorageVersion() runtime.GroupVersioner {
return r.store.StorageVersion() return r.store.StorageVersion()
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *REST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}
func (r *StatusREST) New() runtime.Object { func (r *StatusREST) New() runtime.Object {
return r.store.New() return r.store.New()
} }
@ -307,6 +314,10 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}
func (r *FinalizeREST) New() runtime.Object { func (r *FinalizeREST) New() runtime.Object {
return r.store.New() return r.store.New()
} }
@ -317,3 +328,8 @@ func (r *FinalizeREST) Update(ctx context.Context, name string, objInfo rest.Upd
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *FinalizeREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -33,6 +33,7 @@ import (
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// namespaceStrategy implements behavior for Namespaces // namespaceStrategy implements behavior for Namespaces
@ -50,6 +51,16 @@ func (namespaceStrategy) NamespaceScoped() bool {
return false return false
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (namespaceStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
}
// PrepareForCreate clears fields that are not allowed to be set by end users on creation. // PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (namespaceStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (namespaceStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
// on create, status is active // on create, status is active
@ -138,6 +149,16 @@ type namespaceStatusStrategy struct {
var StatusStrategy = namespaceStatusStrategy{Strategy} var StatusStrategy = namespaceStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (namespaceStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
}
}
func (namespaceStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (namespaceStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newNamespace := obj.(*api.Namespace) newNamespace := obj.(*api.Namespace)
oldNamespace := old.(*api.Namespace) oldNamespace := old.(*api.Namespace)
@ -158,6 +179,16 @@ func (namespaceFinalizeStrategy) ValidateUpdate(ctx context.Context, obj, old ru
return validation.ValidateNamespaceFinalizeUpdate(obj.(*api.Namespace), old.(*api.Namespace)) return validation.ValidateNamespaceFinalizeUpdate(obj.(*api.Namespace), old.(*api.Namespace))
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (namespaceFinalizeStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update. // PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (namespaceFinalizeStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (namespaceFinalizeStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newNamespace := obj.(*api.Namespace) newNamespace := obj.(*api.Namespace)

View File

@ -37,6 +37,7 @@ import (
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/core/node" "k8s.io/kubernetes/pkg/registry/core/node"
noderest "k8s.io/kubernetes/pkg/registry/core/node/rest" noderest "k8s.io/kubernetes/pkg/registry/core/node/rest"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// NodeStorage includes storage for nodes and all sub resources. // NodeStorage includes storage for nodes and all sub resources.
@ -77,6 +78,11 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}
// NewStorage returns a NodeStorage object that will work against nodes. // NewStorage returns a NodeStorage object that will work against nodes.
func NewStorage(optsGetter generic.RESTOptionsGetter, kubeletClientConfig client.KubeletClientConfig, proxyTransport http.RoundTripper) (*NodeStorage, error) { func NewStorage(optsGetter generic.RESTOptionsGetter, kubeletClientConfig client.KubeletClientConfig, proxyTransport http.RoundTripper) (*NodeStorage, error) {
store := &genericregistry.Store{ store := &genericregistry.Store{
@ -88,6 +94,7 @@ func NewStorage(optsGetter generic.RESTOptionsGetter, kubeletClientConfig client
CreateStrategy: node.Strategy, CreateStrategy: node.Strategy,
UpdateStrategy: node.Strategy, UpdateStrategy: node.Strategy,
DeleteStrategy: node.Strategy, DeleteStrategy: node.Strategy,
ResetFieldsStrategy: node.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -102,6 +109,7 @@ func NewStorage(optsGetter generic.RESTOptionsGetter, kubeletClientConfig client
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = node.StatusStrategy statusStore.UpdateStrategy = node.StatusStrategy
statusStore.ResetFieldsStrategy = node.StatusStrategy
// Set up REST handlers // Set up REST handlers
nodeREST := &REST{Store: store, proxyTransport: proxyTransport} nodeREST := &REST{Store: store, proxyTransport: proxyTransport}

View File

@ -41,6 +41,7 @@ import (
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/kubelet/client" "k8s.io/kubernetes/pkg/kubelet/client"
proxyutil "k8s.io/kubernetes/pkg/proxy/util" proxyutil "k8s.io/kubernetes/pkg/proxy/util"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// nodeStrategy implements behavior for nodes // nodeStrategy implements behavior for nodes
@ -58,6 +59,18 @@ func (nodeStrategy) NamespaceScoped() bool {
return false return false
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (nodeStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// AllowCreateOnUpdate is false for nodes. // AllowCreateOnUpdate is false for nodes.
func (nodeStrategy) AllowCreateOnUpdate() bool { func (nodeStrategy) AllowCreateOnUpdate() bool {
return false return false
@ -147,6 +160,18 @@ type nodeStatusStrategy struct {
var StatusStrategy = nodeStatusStrategy{Strategy} var StatusStrategy = nodeStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (nodeStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
}
return fields
}
func (nodeStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (nodeStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newNode := obj.(*api.Node) newNode := obj.(*api.Node)
oldNode := old.(*api.Node) oldNode := old.(*api.Node)

View File

@ -29,6 +29,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/core/persistentvolume" "k8s.io/kubernetes/pkg/registry/core/persistentvolume"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// REST implements a RESTStorage for persistent volumes. // REST implements a RESTStorage for persistent volumes.
@ -48,6 +49,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
UpdateStrategy: persistentvolume.Strategy, UpdateStrategy: persistentvolume.Strategy,
DeleteStrategy: persistentvolume.Strategy, DeleteStrategy: persistentvolume.Strategy,
ReturnDeletedObject: true, ReturnDeletedObject: true,
ResetFieldsStrategy: persistentvolume.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -58,6 +60,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = persistentvolume.StatusStrategy statusStore.UpdateStrategy = persistentvolume.StatusStrategy
statusStore.ResetFieldsStrategy = persistentvolume.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}, nil return &REST{store}, &StatusREST{store: &statusStore}, nil
} }
@ -91,3 +94,8 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -32,6 +32,7 @@ import (
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/core/validation"
volumevalidation "k8s.io/kubernetes/pkg/volume/validation" volumevalidation "k8s.io/kubernetes/pkg/volume/validation"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// persistentvolumeStrategy implements behavior for PersistentVolume objects // persistentvolumeStrategy implements behavior for PersistentVolume objects
@ -48,6 +49,18 @@ func (persistentvolumeStrategy) NamespaceScoped() bool {
return false return false
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (persistentvolumeStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// ResetBeforeCreate clears the Status field which is not allowed to be set by end users on creation. // ResetBeforeCreate clears the Status field which is not allowed to be set by end users on creation.
func (persistentvolumeStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (persistentvolumeStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
pv := obj.(*api.PersistentVolume) pv := obj.(*api.PersistentVolume)
@ -96,6 +109,18 @@ type persistentvolumeStatusStrategy struct {
var StatusStrategy = persistentvolumeStatusStrategy{Strategy} var StatusStrategy = persistentvolumeStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (persistentvolumeStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
}
return fields
}
// PrepareForUpdate sets the Spec field which is not allowed to be changed when updating a PV's Status // PrepareForUpdate sets the Spec field which is not allowed to be changed when updating a PV's Status
func (persistentvolumeStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (persistentvolumeStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newPv := obj.(*api.PersistentVolume) newPv := obj.(*api.PersistentVolume)

View File

@ -29,6 +29,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/core/persistentvolumeclaim" "k8s.io/kubernetes/pkg/registry/core/persistentvolumeclaim"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// REST implements a RESTStorage for persistent volume claims. // REST implements a RESTStorage for persistent volume claims.
@ -48,6 +49,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
UpdateStrategy: persistentvolumeclaim.Strategy, UpdateStrategy: persistentvolumeclaim.Strategy,
DeleteStrategy: persistentvolumeclaim.Strategy, DeleteStrategy: persistentvolumeclaim.Strategy,
ReturnDeletedObject: true, ReturnDeletedObject: true,
ResetFieldsStrategy: persistentvolumeclaim.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -58,6 +60,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = persistentvolumeclaim.StatusStrategy statusStore.UpdateStrategy = persistentvolumeclaim.StatusStrategy
statusStore.ResetFieldsStrategy = persistentvolumeclaim.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}, nil return &REST{store}, &StatusREST{store: &statusStore}, nil
} }
@ -91,3 +94,8 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -33,6 +33,7 @@ import (
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// persistentvolumeclaimStrategy implements behavior for PersistentVolumeClaim objects // persistentvolumeclaimStrategy implements behavior for PersistentVolumeClaim objects
@ -49,6 +50,18 @@ func (persistentvolumeclaimStrategy) NamespaceScoped() bool {
return true return true
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (persistentvolumeclaimStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears the Status field which is not allowed to be set by end users on creation. // PrepareForCreate clears the Status field which is not allowed to be set by end users on creation.
func (persistentvolumeclaimStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (persistentvolumeclaimStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
pvc := obj.(*api.PersistentVolumeClaim) pvc := obj.(*api.PersistentVolumeClaim)
@ -94,6 +107,18 @@ type persistentvolumeclaimStatusStrategy struct {
var StatusStrategy = persistentvolumeclaimStatusStrategy{Strategy} var StatusStrategy = persistentvolumeclaimStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (persistentvolumeclaimStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
}
return fields
}
// PrepareForUpdate sets the Spec field which is not allowed to be changed when updating a PV's Status // PrepareForUpdate sets the Spec field which is not allowed to be changed when updating a PV's Status
func (persistentvolumeclaimStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (persistentvolumeclaimStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newPv := obj.(*api.PersistentVolumeClaim) newPv := obj.(*api.PersistentVolumeClaim)

View File

@ -44,6 +44,7 @@ import (
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
registrypod "k8s.io/kubernetes/pkg/registry/core/pod" registrypod "k8s.io/kubernetes/pkg/registry/core/pod"
podrest "k8s.io/kubernetes/pkg/registry/core/pod/rest" podrest "k8s.io/kubernetes/pkg/registry/core/pod/rest"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// PodStorage includes storage for pods and all sub resources // PodStorage includes storage for pods and all sub resources
@ -79,6 +80,7 @@ func NewStorage(optsGetter generic.RESTOptionsGetter, k client.ConnectionInfoGet
CreateStrategy: registrypod.Strategy, CreateStrategy: registrypod.Strategy,
UpdateStrategy: registrypod.Strategy, UpdateStrategy: registrypod.Strategy,
DeleteStrategy: registrypod.Strategy, DeleteStrategy: registrypod.Strategy,
ResetFieldsStrategy: registrypod.Strategy,
ReturnDeletedObject: true, ReturnDeletedObject: true,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
@ -95,6 +97,7 @@ func NewStorage(optsGetter generic.RESTOptionsGetter, k client.ConnectionInfoGet
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = registrypod.StatusStrategy statusStore.UpdateStrategy = registrypod.StatusStrategy
statusStore.ResetFieldsStrategy = registrypod.StatusStrategy
ephemeralContainersStore := *store ephemeralContainersStore := *store
ephemeralContainersStore.UpdateStrategy = registrypod.EphemeralContainersStrategy ephemeralContainersStore.UpdateStrategy = registrypod.EphemeralContainersStrategy
@ -278,6 +281,11 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}
// EphemeralContainersREST implements the REST endpoint for adding EphemeralContainers // EphemeralContainersREST implements the REST endpoint for adding EphemeralContainers
type EphemeralContainersREST struct { type EphemeralContainersREST struct {
store *genericregistry.Store store *genericregistry.Store

View File

@ -48,6 +48,7 @@ import (
"k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/kubelet/client" "k8s.io/kubernetes/pkg/kubelet/client"
proxyutil "k8s.io/kubernetes/pkg/proxy/util" proxyutil "k8s.io/kubernetes/pkg/proxy/util"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// podStrategy implements behavior for Pods // podStrategy implements behavior for Pods
@ -65,6 +66,18 @@ func (podStrategy) NamespaceScoped() bool {
return true return true
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (podStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears fields that are not allowed to be set by end users on creation. // PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (podStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (podStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
pod := obj.(*api.Pod) pod := obj.(*api.Pod)
@ -154,6 +167,18 @@ type podStatusStrategy struct {
// StatusStrategy wraps and exports the used podStrategy for the storage package. // StatusStrategy wraps and exports the used podStrategy for the storage package.
var StatusStrategy = podStatusStrategy{Strategy} var StatusStrategy = podStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (podStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
fieldpath.MakePathOrDie("metadata", "deletionTimestamp"),
fieldpath.MakePathOrDie("metadata", "ownerReferences"),
),
}
}
func (podStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (podStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newPod := obj.(*api.Pod) newPod := obj.(*api.Pod)
oldPod := old.(*api.Pod) oldPod := old.(*api.Pod)

View File

@ -39,6 +39,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/core/replicationcontroller" "k8s.io/kubernetes/pkg/registry/core/replicationcontroller"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// ControllerStorage includes dummy storage for Replication Controllers and for Scale subresource. // ControllerStorage includes dummy storage for Replication Controllers and for Scale subresource.
@ -76,6 +77,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
CreateStrategy: replicationcontroller.Strategy, CreateStrategy: replicationcontroller.Strategy,
UpdateStrategy: replicationcontroller.Strategy, UpdateStrategy: replicationcontroller.Strategy,
DeleteStrategy: replicationcontroller.Strategy, DeleteStrategy: replicationcontroller.Strategy,
ResetFieldsStrategy: replicationcontroller.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -86,6 +88,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = replicationcontroller.StatusStrategy statusStore.UpdateStrategy = replicationcontroller.StatusStrategy
statusStore.ResetFieldsStrategy = replicationcontroller.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}, nil return &REST{store}, &StatusREST{store: &statusStore}, nil
} }
@ -127,6 +130,11 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}
type ScaleREST struct { type ScaleREST struct {
store *genericregistry.Store store *genericregistry.Store
} }

View File

@ -41,6 +41,7 @@ import (
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/helper" "k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/core/validation"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// rcStrategy implements verification logic for Replication Controllers. // rcStrategy implements verification logic for Replication Controllers.
@ -73,6 +74,18 @@ func (rcStrategy) NamespaceScoped() bool {
return true return true
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (rcStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears the status of a replication controller before creation. // PrepareForCreate clears the status of a replication controller before creation.
func (rcStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (rcStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
controller := obj.(*api.ReplicationController) controller := obj.(*api.ReplicationController)
@ -192,6 +205,16 @@ type rcStatusStrategy struct {
// StatusStrategy is the default logic invoked when updating object status. // StatusStrategy is the default logic invoked when updating object status.
var StatusStrategy = rcStatusStrategy{Strategy} var StatusStrategy = rcStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (rcStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
}
}
func (rcStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (rcStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newRc := obj.(*api.ReplicationController) newRc := obj.(*api.ReplicationController)
oldRc := old.(*api.ReplicationController) oldRc := old.(*api.ReplicationController)

View File

@ -29,6 +29,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/core/resourcequota" "k8s.io/kubernetes/pkg/registry/core/resourcequota"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// REST implements a RESTStorage for resource quotas. // REST implements a RESTStorage for resource quotas.
@ -46,6 +47,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
CreateStrategy: resourcequota.Strategy, CreateStrategy: resourcequota.Strategy,
UpdateStrategy: resourcequota.Strategy, UpdateStrategy: resourcequota.Strategy,
DeleteStrategy: resourcequota.Strategy, DeleteStrategy: resourcequota.Strategy,
ResetFieldsStrategy: resourcequota.Strategy,
ReturnDeletedObject: true, ReturnDeletedObject: true,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
@ -57,6 +59,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = resourcequota.StatusStrategy statusStore.UpdateStrategy = resourcequota.StatusStrategy
statusStore.ResetFieldsStrategy = resourcequota.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}, nil return &REST{store}, &StatusREST{store: &statusStore}, nil
} }
@ -90,3 +93,8 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -27,6 +27,7 @@ import (
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// resourcequotaStrategy implements behavior for ResourceQuota objects // resourcequotaStrategy implements behavior for ResourceQuota objects
@ -44,6 +45,18 @@ func (resourcequotaStrategy) NamespaceScoped() bool {
return true return true
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (resourcequotaStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears fields that are not allowed to be set by end users on creation. // PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (resourcequotaStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (resourcequotaStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
resourcequota := obj.(*api.ResourceQuota) resourcequota := obj.(*api.ResourceQuota)
@ -91,6 +104,18 @@ type resourcequotaStatusStrategy struct {
// StatusStrategy is the default logic invoked when updating object status. // StatusStrategy is the default logic invoked when updating object status.
var StatusStrategy = resourcequotaStatusStrategy{Strategy} var StatusStrategy = resourcequotaStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (resourcequotaStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
}
return fields
}
func (resourcequotaStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (resourcequotaStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newResourcequota := obj.(*api.ResourceQuota) newResourcequota := obj.(*api.ResourceQuota)
oldResourcequota := old.(*api.ResourceQuota) oldResourcequota := old.(*api.ResourceQuota)

View File

@ -36,18 +36,17 @@ import (
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"
"k8s.io/apiserver/pkg/util/dryrun" "k8s.io/apiserver/pkg/util/dryrun"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/klog/v2" "k8s.io/klog/v2"
apiservice "k8s.io/kubernetes/pkg/api/service" apiservice "k8s.io/kubernetes/pkg/api/service"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/features"
registry "k8s.io/kubernetes/pkg/registry/core/service" registry "k8s.io/kubernetes/pkg/registry/core/service"
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator" "k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
"k8s.io/kubernetes/pkg/registry/core/service/portallocator" "k8s.io/kubernetes/pkg/registry/core/service/portallocator"
netutil "k8s.io/utils/net" netutil "k8s.io/utils/net"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/features"
) )
// REST adapts a service registry into apiserver's RESTStorage model. // REST adapts a service registry into apiserver's RESTStorage model.
@ -80,6 +79,7 @@ type ServiceStorage interface {
rest.GracefulDeleter rest.GracefulDeleter
rest.Watcher rest.Watcher
rest.StorageVersionProvider rest.StorageVersionProvider
rest.ResetFieldsStrategy
} }
type EndpointsStorage interface { type EndpointsStorage interface {
@ -514,6 +514,11 @@ func (rs *REST) Update(ctx context.Context, name string, objInfo rest.UpdatedObj
return out, created, err return out, created, err
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (rs *REST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return rs.services.GetResetFields()
}
// Implement Redirector. // Implement Redirector.
var _ = rest.Redirector(&REST{}) var _ = rest.Redirector(&REST{})

View File

@ -44,6 +44,7 @@ import (
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator" "k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
"k8s.io/kubernetes/pkg/registry/core/service/portallocator" "k8s.io/kubernetes/pkg/registry/core/service/portallocator"
"k8s.io/kubernetes/pkg/registry/registrytest" "k8s.io/kubernetes/pkg/registry/registrytest"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing" featuregatetesting "k8s.io/component-base/featuregate/testing"
@ -167,6 +168,11 @@ func (s *serviceStorage) StorageVersion() runtime.GroupVersioner {
panic("not implemented") panic("not implemented")
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (s *serviceStorage) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return nil
}
func NewTestREST(t *testing.T, endpoints *api.EndpointsList, ipFamilies []api.IPFamily) (*REST, *serviceStorage, *etcd3testing.EtcdTestServer) { func NewTestREST(t *testing.T, endpoints *api.EndpointsList, ipFamilies []api.IPFamily) (*REST, *serviceStorage, *etcd3testing.EtcdTestServer) {
return NewTestRESTWithPods(t, endpoints, nil, ipFamilies) return NewTestRESTWithPods(t, endpoints, nil, ipFamilies)
} }

View File

@ -34,6 +34,7 @@ import (
"k8s.io/kubernetes/pkg/registry/core/service" "k8s.io/kubernetes/pkg/registry/core/service"
registry "k8s.io/kubernetes/pkg/registry/core/service" registry "k8s.io/kubernetes/pkg/registry/core/service"
svcreg "k8s.io/kubernetes/pkg/registry/core/service" svcreg "k8s.io/kubernetes/pkg/registry/core/service"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
netutil "k8s.io/utils/net" netutil "k8s.io/utils/net"
) )
@ -57,6 +58,7 @@ func NewGenericREST(optsGetter generic.RESTOptionsGetter, serviceCIDR net.IPNet,
CreateStrategy: strategy, CreateStrategy: strategy,
UpdateStrategy: strategy, UpdateStrategy: strategy,
DeleteStrategy: strategy, DeleteStrategy: strategy,
ResetFieldsStrategy: strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -66,7 +68,9 @@ func NewGenericREST(optsGetter generic.RESTOptionsGetter, serviceCIDR net.IPNet,
} }
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = service.NewServiceStatusStrategy(strategy) statusStrategy := service.NewServiceStatusStrategy(strategy)
statusStore.UpdateStrategy = statusStrategy
statusStore.ResetFieldsStrategy = statusStrategy
ipv4 := api.IPv4Protocol ipv4 := api.IPv4Protocol
ipv6 := api.IPv6Protocol ipv6 := api.IPv6Protocol
@ -125,6 +129,11 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}
// defaultOnRead sets interlinked fields that were not previously set on read. // defaultOnRead sets interlinked fields that were not previously set on read.
// We can't do this in the normal defaulting path because that same logic // We can't do this in the normal defaulting path because that same logic
// applies on Get, Create, and Update, but we need to distinguish between them. // applies on Get, Create, and Update, but we need to distinguish between them.
@ -212,7 +221,6 @@ func (r *GenericREST) defaultOnReadService(service *api.Service) {
service.Spec.IPFamilyPolicy = &singleStack service.Spec.IPFamilyPolicy = &singleStack
} }
} }
} else { } else {
// headful // headful
// make sure a slice exists to receive the families // make sure a slice exists to receive the families

View File

@ -32,10 +32,12 @@ import (
"k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
netutil "k8s.io/utils/net" netutil "k8s.io/utils/net"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
type Strategy interface { type Strategy interface {
rest.RESTCreateUpdateStrategy rest.RESTCreateUpdateStrategy
rest.ResetFieldsStrategy
} }
// svcStrategy implements behavior for Services // svcStrategy implements behavior for Services
@ -90,6 +92,18 @@ func (svcStrategy) NamespaceScoped() bool {
return true return true
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (svcStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate sets contextual defaults and clears fields that are not allowed to be set by end users on creation. // PrepareForCreate sets contextual defaults and clears fields that are not allowed to be set by end users on creation.
func (strategy svcStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (strategy svcStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
service := obj.(*api.Service) service := obj.(*api.Service)
@ -263,6 +277,18 @@ func NewServiceStatusStrategy(strategy Strategy) Strategy {
return serviceStatusStrategy{strategy} return serviceStatusStrategy{strategy}
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (serviceStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
}
return fields
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update of status // PrepareForUpdate clears fields that are not allowed to be set by end users on update of status
func (serviceStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (serviceStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newService := obj.(*api.Service) newService := obj.(*api.Service)

View File

@ -29,6 +29,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/flowcontrol/flowschema" "k8s.io/kubernetes/pkg/registry/flowcontrol/flowschema"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// FlowSchemaStorage implements storage for flow schema. // FlowSchemaStorage implements storage for flow schema.
@ -52,6 +53,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
CreateStrategy: flowschema.Strategy, CreateStrategy: flowschema.Strategy,
UpdateStrategy: flowschema.Strategy, UpdateStrategy: flowschema.Strategy,
DeleteStrategy: flowschema.Strategy, DeleteStrategy: flowschema.Strategy,
ResetFieldsStrategy: flowschema.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -64,6 +66,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
statusStore.CreateStrategy = nil statusStore.CreateStrategy = nil
statusStore.UpdateStrategy = flowschema.StatusStrategy statusStore.UpdateStrategy = flowschema.StatusStrategy
statusStore.DeleteStrategy = nil statusStore.DeleteStrategy = nil
statusStore.ResetFieldsStrategy = flowschema.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}, nil return &REST{store}, &StatusREST{store: &statusStore}, nil
} }
@ -89,3 +92,8 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -26,6 +26,7 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/flowcontrol" "k8s.io/kubernetes/pkg/apis/flowcontrol"
"k8s.io/kubernetes/pkg/apis/flowcontrol/validation" "k8s.io/kubernetes/pkg/apis/flowcontrol/validation"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// flowSchemaStrategy implements verification logic for FlowSchema. // flowSchemaStrategy implements verification logic for FlowSchema.
@ -42,6 +43,21 @@ func (flowSchemaStrategy) NamespaceScoped() bool {
return false return false
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (flowSchemaStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"flowcontrol.apiserver.k8s.io/v1alpha1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
"flowcontrol.apiserver.k8s.io/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears the status of a flow-schema before creation. // PrepareForCreate clears the status of a flow-schema before creation.
func (flowSchemaStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (flowSchemaStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
fl := obj.(*flowcontrol.FlowSchema) fl := obj.(*flowcontrol.FlowSchema)
@ -91,6 +107,23 @@ type flowSchemaStatusStrategy struct {
// StatusStrategy is the default logic that applies when updating flow-schema objects' status. // StatusStrategy is the default logic that applies when updating flow-schema objects' status.
var StatusStrategy = flowSchemaStatusStrategy{Strategy} var StatusStrategy = flowSchemaStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (flowSchemaStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"flowcontrol.apiserver.k8s.io/v1alpha1": fieldpath.NewSet(
fieldpath.MakePathOrDie("metadata"),
fieldpath.MakePathOrDie("spec"),
),
"flowcontrol.apiserver.k8s.io/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("metadata"),
fieldpath.MakePathOrDie("spec"),
),
}
return fields
}
func (flowSchemaStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (flowSchemaStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newFlowSchema := obj.(*flowcontrol.FlowSchema) newFlowSchema := obj.(*flowcontrol.FlowSchema)
oldFlowSchema := old.(*flowcontrol.FlowSchema) oldFlowSchema := old.(*flowcontrol.FlowSchema)

View File

@ -29,6 +29,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/flowcontrol/prioritylevelconfiguration" "k8s.io/kubernetes/pkg/registry/flowcontrol/prioritylevelconfiguration"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// PriorityLevelConfigurationStorage implements storage for priority level configuration. // PriorityLevelConfigurationStorage implements storage for priority level configuration.
@ -52,6 +53,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
CreateStrategy: prioritylevelconfiguration.Strategy, CreateStrategy: prioritylevelconfiguration.Strategy,
UpdateStrategy: prioritylevelconfiguration.Strategy, UpdateStrategy: prioritylevelconfiguration.Strategy,
DeleteStrategy: prioritylevelconfiguration.Strategy, DeleteStrategy: prioritylevelconfiguration.Strategy,
ResetFieldsStrategy: prioritylevelconfiguration.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -64,6 +66,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
statusStore.CreateStrategy = nil statusStore.CreateStrategy = nil
statusStore.UpdateStrategy = prioritylevelconfiguration.StatusStrategy statusStore.UpdateStrategy = prioritylevelconfiguration.StatusStrategy
statusStore.DeleteStrategy = nil statusStore.DeleteStrategy = nil
statusStore.ResetFieldsStrategy = prioritylevelconfiguration.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}, nil return &REST{store}, &StatusREST{store: &statusStore}, nil
} }
@ -89,3 +92,8 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -26,6 +26,7 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/flowcontrol" "k8s.io/kubernetes/pkg/apis/flowcontrol"
"k8s.io/kubernetes/pkg/apis/flowcontrol/validation" "k8s.io/kubernetes/pkg/apis/flowcontrol/validation"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// priorityLevelConfigurationStrategy implements verification logic for priority level configurations. // priorityLevelConfigurationStrategy implements verification logic for priority level configurations.
@ -42,6 +43,21 @@ func (priorityLevelConfigurationStrategy) NamespaceScoped() bool {
return false return false
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (priorityLevelConfigurationStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"flowcontrol.apiserver.k8s.io/v1alpha1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
"flowcontrol.apiserver.k8s.io/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears the status of a priority-level-configuration before creation. // PrepareForCreate clears the status of a priority-level-configuration before creation.
func (priorityLevelConfigurationStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (priorityLevelConfigurationStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
pl := obj.(*flowcontrol.PriorityLevelConfiguration) pl := obj.(*flowcontrol.PriorityLevelConfiguration)
@ -91,6 +107,23 @@ type priorityLevelConfigurationStatusStrategy struct {
// StatusStrategy is the default logic that applies when updating priority level configuration objects' status. // StatusStrategy is the default logic that applies when updating priority level configuration objects' status.
var StatusStrategy = priorityLevelConfigurationStatusStrategy{Strategy} var StatusStrategy = priorityLevelConfigurationStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (priorityLevelConfigurationStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"flowcontrol.apiserver.k8s.io/v1alpha1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
fieldpath.MakePathOrDie("metadata"),
),
"flowcontrol.apiserver.k8s.io/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
fieldpath.MakePathOrDie("metadata"),
),
}
return fields
}
func (priorityLevelConfigurationStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (priorityLevelConfigurationStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newPriorityLevelConfiguration := obj.(*flowcontrol.PriorityLevelConfiguration) newPriorityLevelConfiguration := obj.(*flowcontrol.PriorityLevelConfiguration)
oldPriorityLevelConfiguration := old.(*flowcontrol.PriorityLevelConfiguration) oldPriorityLevelConfiguration := old.(*flowcontrol.PriorityLevelConfiguration)

View File

@ -29,6 +29,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/networking/ingress" "k8s.io/kubernetes/pkg/registry/networking/ingress"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// REST implements a RESTStorage for replication controllers // REST implements a RESTStorage for replication controllers
@ -46,6 +47,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
CreateStrategy: ingress.Strategy, CreateStrategy: ingress.Strategy,
UpdateStrategy: ingress.Strategy, UpdateStrategy: ingress.Strategy,
DeleteStrategy: ingress.Strategy, DeleteStrategy: ingress.Strategy,
ResetFieldsStrategy: ingress.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -56,6 +58,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = ingress.StatusStrategy statusStore.UpdateStrategy = ingress.StatusStrategy
statusStore.ResetFieldsStrategy = ingress.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}, nil return &REST{store}, &StatusREST{store: &statusStore}, nil
} }
@ -88,3 +91,8 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -18,6 +18,7 @@ package ingress
import ( import (
"context" "context"
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/runtime/schema"
@ -27,6 +28,7 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/networking" "k8s.io/kubernetes/pkg/apis/networking"
"k8s.io/kubernetes/pkg/apis/networking/validation" "k8s.io/kubernetes/pkg/apis/networking/validation"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// ingressStrategy implements verification logic for Replication Ingress. // ingressStrategy implements verification logic for Replication Ingress.
@ -43,6 +45,24 @@ func (ingressStrategy) NamespaceScoped() bool {
return true return true
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (ingressStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"extensions/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
"networking.k8s.io/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
"networking.k8s.io/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears the status of an Ingress before creation. // PrepareForCreate clears the status of an Ingress before creation.
func (ingressStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (ingressStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
ingress := obj.(*networking.Ingress) ingress := obj.(*networking.Ingress)
@ -108,6 +128,24 @@ type ingressStatusStrategy struct {
// StatusStrategy implements logic used to validate and prepare for updates of the status subresource // StatusStrategy implements logic used to validate and prepare for updates of the status subresource
var StatusStrategy = ingressStatusStrategy{Strategy} var StatusStrategy = ingressStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (ingressStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"extensions/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
"networking.k8s.io/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
"networking.k8s.io/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
}
return fields
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update of status // PrepareForUpdate clears fields that are not allowed to be set by end users on update of status
func (ingressStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (ingressStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newIngress := obj.(*networking.Ingress) newIngress := obj.(*networking.Ingress)

View File

@ -29,6 +29,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/policy/poddisruptionbudget" "k8s.io/kubernetes/pkg/registry/policy/poddisruptionbudget"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// REST implements a RESTStorage for pod disruption budgets against etcd. // REST implements a RESTStorage for pod disruption budgets against etcd.
@ -46,6 +47,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
CreateStrategy: poddisruptionbudget.Strategy, CreateStrategy: poddisruptionbudget.Strategy,
UpdateStrategy: poddisruptionbudget.Strategy, UpdateStrategy: poddisruptionbudget.Strategy,
DeleteStrategy: poddisruptionbudget.Strategy, DeleteStrategy: poddisruptionbudget.Strategy,
ResetFieldsStrategy: poddisruptionbudget.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
} }
@ -56,6 +58,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = poddisruptionbudget.StatusStrategy statusStore.UpdateStrategy = poddisruptionbudget.StatusStrategy
statusStore.ResetFieldsStrategy = poddisruptionbudget.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}, nil return &REST{store}, &StatusREST{store: &statusStore}, nil
} }
@ -85,3 +88,8 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -28,6 +28,7 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/policy" "k8s.io/kubernetes/pkg/apis/policy"
"k8s.io/kubernetes/pkg/apis/policy/validation" "k8s.io/kubernetes/pkg/apis/policy/validation"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// podDisruptionBudgetStrategy implements verification logic for PodDisruptionBudgets. // podDisruptionBudgetStrategy implements verification logic for PodDisruptionBudgets.
@ -44,6 +45,21 @@ func (podDisruptionBudgetStrategy) NamespaceScoped() bool {
return true return true
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (podDisruptionBudgetStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"policy/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
"policy/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears the status of an PodDisruptionBudget before creation. // PrepareForCreate clears the status of an PodDisruptionBudget before creation.
func (podDisruptionBudgetStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (podDisruptionBudgetStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
podDisruptionBudget := obj.(*policy.PodDisruptionBudget) podDisruptionBudget := obj.(*policy.PodDisruptionBudget)
@ -101,6 +117,21 @@ type podDisruptionBudgetStatusStrategy struct {
// StatusStrategy is the default logic invoked when updating object status. // StatusStrategy is the default logic invoked when updating object status.
var StatusStrategy = podDisruptionBudgetStatusStrategy{Strategy} var StatusStrategy = podDisruptionBudgetStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (podDisruptionBudgetStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"policy/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
"policy/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
),
}
return fields
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update of status // PrepareForUpdate clears fields that are not allowed to be set by end users on update of status
func (podDisruptionBudgetStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (podDisruptionBudgetStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newPodDisruptionBudget := obj.(*policy.PodDisruptionBudget) newPodDisruptionBudget := obj.(*policy.PodDisruptionBudget)

View File

@ -29,6 +29,7 @@ import (
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage" printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/storage/volumeattachment" "k8s.io/kubernetes/pkg/registry/storage/volumeattachment"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// VolumeAttachmentStorage includes storage for VolumeAttachments and all subresources // VolumeAttachmentStorage includes storage for VolumeAttachments and all subresources
@ -52,6 +53,7 @@ func NewStorage(optsGetter generic.RESTOptionsGetter) (*VolumeAttachmentStorage,
CreateStrategy: volumeattachment.Strategy, CreateStrategy: volumeattachment.Strategy,
UpdateStrategy: volumeattachment.Strategy, UpdateStrategy: volumeattachment.Strategy,
DeleteStrategy: volumeattachment.Strategy, DeleteStrategy: volumeattachment.Strategy,
ResetFieldsStrategy: volumeattachment.Strategy,
ReturnDeletedObject: true, ReturnDeletedObject: true,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
@ -63,6 +65,7 @@ func NewStorage(optsGetter generic.RESTOptionsGetter) (*VolumeAttachmentStorage,
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = volumeattachment.StatusStrategy statusStore.UpdateStrategy = volumeattachment.StatusStrategy
statusStore.ResetFieldsStrategy = volumeattachment.StatusStrategy
return &VolumeAttachmentStorage{ return &VolumeAttachmentStorage{
VolumeAttachment: &REST{store}, VolumeAttachment: &REST{store},
@ -93,3 +96,8 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -31,6 +31,7 @@ import (
"k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/apis/storage"
"k8s.io/kubernetes/pkg/apis/storage/validation" "k8s.io/kubernetes/pkg/apis/storage/validation"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// volumeAttachmentStrategy implements behavior for VolumeAttachment objects // volumeAttachmentStrategy implements behavior for VolumeAttachment objects
@ -47,6 +48,18 @@ func (volumeAttachmentStrategy) NamespaceScoped() bool {
return false return false
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (volumeAttachmentStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"storage.k8s.io/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// ResetBeforeCreate clears the Status field which is not allowed to be set by end users on creation. // ResetBeforeCreate clears the Status field which is not allowed to be set by end users on creation.
func (volumeAttachmentStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (volumeAttachmentStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
var groupVersion schema.GroupVersion var groupVersion schema.GroupVersion
@ -143,6 +156,19 @@ type volumeAttachmentStatusStrategy struct {
// VolumeAttachmentStatus subresource via the REST API. // VolumeAttachmentStatus subresource via the REST API.
var StatusStrategy = volumeAttachmentStatusStrategy{Strategy} var StatusStrategy = volumeAttachmentStatusStrategy{Strategy}
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (volumeAttachmentStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"storage.k8s.io/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("metadata"),
fieldpath.MakePathOrDie("spec"),
),
}
return fields
}
// PrepareForUpdate sets the Status fields which is not allowed to be set by an end user updating a VolumeAttachment // PrepareForUpdate sets the Status fields which is not allowed to be set by an end user updating a VolumeAttachment
func (volumeAttachmentStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (volumeAttachmentStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newVolumeAttachment := obj.(*storage.VolumeAttachment) newVolumeAttachment := obj.(*storage.VolumeAttachment)

View File

@ -27,6 +27,7 @@ require (
k8s.io/klog/v2 v2.5.0 k8s.io/klog/v2 v2.5.0
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 k8s.io/utils v0.0.0-20201110183641-67b214c5f920
sigs.k8s.io/structured-merge-diff/v4 v4.0.3
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )

View File

@ -326,6 +326,8 @@ type CustomResourceDefinitionCondition struct {
// CustomResourceDefinitionStatus indicates the state of the CustomResourceDefinition // CustomResourceDefinitionStatus indicates the state of the CustomResourceDefinition
type CustomResourceDefinitionStatus struct { type CustomResourceDefinitionStatus struct {
// Conditions indicate state for particular aspects of a CustomResourceDefinition // Conditions indicate state for particular aspects of a CustomResourceDefinition
// +listType=map
// +listMapKey=type
Conditions []CustomResourceDefinitionCondition Conditions []CustomResourceDefinitionCondition
// AcceptedNames are the names that are actually being used to serve discovery // AcceptedNames are the names that are actually being used to serve discovery

View File

@ -234,6 +234,8 @@ message CustomResourceDefinitionSpec {
message CustomResourceDefinitionStatus { message CustomResourceDefinitionStatus {
// conditions indicate state for particular aspects of a CustomResourceDefinition // conditions indicate state for particular aspects of a CustomResourceDefinition
// +optional // +optional
// +listType=map
// +listMapKey=type
repeated CustomResourceDefinitionCondition conditions = 1; repeated CustomResourceDefinitionCondition conditions = 1;
// acceptedNames are the names that are actually being used to serve discovery. // acceptedNames are the names that are actually being used to serve discovery.

View File

@ -329,6 +329,8 @@ type CustomResourceDefinitionCondition struct {
type CustomResourceDefinitionStatus struct { type CustomResourceDefinitionStatus struct {
// conditions indicate state for particular aspects of a CustomResourceDefinition // conditions indicate state for particular aspects of a CustomResourceDefinition
// +optional // +optional
// +listType=map
// +listMapKey=type
Conditions []CustomResourceDefinitionCondition `json:"conditions" protobuf:"bytes,1,opt,name=conditions"` Conditions []CustomResourceDefinitionCondition `json:"conditions" protobuf:"bytes,1,opt,name=conditions"`
// acceptedNames are the names that are actually being used to serve discovery. // acceptedNames are the names that are actually being used to serve discovery.

View File

@ -280,6 +280,8 @@ message CustomResourceDefinitionSpec {
message CustomResourceDefinitionStatus { message CustomResourceDefinitionStatus {
// conditions indicate state for particular aspects of a CustomResourceDefinition // conditions indicate state for particular aspects of a CustomResourceDefinition
// +optional // +optional
// +listType=map
// +listMapKey=type
repeated CustomResourceDefinitionCondition conditions = 1; repeated CustomResourceDefinitionCondition conditions = 1;
// acceptedNames are the names that are actually being used to serve discovery. // acceptedNames are the names that are actually being used to serve discovery.

View File

@ -361,6 +361,8 @@ type CustomResourceDefinitionCondition struct {
type CustomResourceDefinitionStatus struct { type CustomResourceDefinitionStatus struct {
// conditions indicate state for particular aspects of a CustomResourceDefinition // conditions indicate state for particular aspects of a CustomResourceDefinition
// +optional // +optional
// +listType=map
// +listMapKey=type
Conditions []CustomResourceDefinitionCondition `json:"conditions" protobuf:"bytes,1,opt,name=conditions"` Conditions []CustomResourceDefinitionCondition `json:"conditions" protobuf:"bytes,1,opt,name=conditions"`
// acceptedNames are the names that are actually being used to serve discovery. // acceptedNames are the names that are actually being used to serve discovery.

View File

@ -27,6 +27,7 @@ import (
"time" "time"
goopenapispec "github.com/go-openapi/spec" goopenapispec "github.com/go-openapi/spec"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
apiextensionshelpers "k8s.io/apiextensions-apiserver/pkg/apihelpers" apiextensionshelpers "k8s.io/apiextensions-apiserver/pkg/apihelpers"
apiextensionsinternal "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" apiextensionsinternal "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
@ -865,14 +866,12 @@ func (r *crdHandler) getOrCreateServingInfoFor(uid types.UID, name string) (*crd
MaxRequestBodyBytes: r.maxRequestBodyBytes, MaxRequestBodyBytes: r.maxRequestBodyBytes,
} }
if utilfeature.DefaultFeatureGate.Enabled(features.ServerSideApply) { if utilfeature.DefaultFeatureGate.Enabled(features.ServerSideApply) {
resetFields := storages[v.Name].CustomResource.GetResetFields()
reqScope := *requestScopes[v.Name] reqScope := *requestScopes[v.Name]
reqScope.FieldManager, err = fieldmanager.NewDefaultCRDFieldManager( reqScope, err = scopeWithFieldManager(
typeConverter, typeConverter,
reqScope.Convertor, reqScope,
reqScope.Defaulter, resetFields,
reqScope.Creater,
reqScope.Kind,
reqScope.HubGroupVersion,
false, false,
) )
if err != nil { if err != nil {
@ -901,20 +900,6 @@ func (r *crdHandler) getOrCreateServingInfoFor(uid types.UID, name string) (*crd
// override status subresource values // override status subresource values
// shallow copy // shallow copy
statusScope := *requestScopes[v.Name] statusScope := *requestScopes[v.Name]
if utilfeature.DefaultFeatureGate.Enabled(features.ServerSideApply) {
statusScope.FieldManager, err = fieldmanager.NewDefaultCRDFieldManager(
typeConverter,
statusScope.Convertor,
statusScope.Defaulter,
statusScope.Creater,
statusScope.Kind,
statusScope.HubGroupVersion,
true,
)
if err != nil {
return nil, err
}
}
statusScope.Subresource = "status" statusScope.Subresource = "status"
statusScope.Namer = handlers.ContextBasedNaming{ statusScope.Namer = handlers.ContextBasedNaming{
SelfLinker: meta.NewAccessor(), SelfLinker: meta.NewAccessor(),
@ -922,6 +907,20 @@ func (r *crdHandler) getOrCreateServingInfoFor(uid types.UID, name string) (*crd
SelfLinkPathPrefix: selfLinkPrefix, SelfLinkPathPrefix: selfLinkPrefix,
SelfLinkPathSuffix: "/status", SelfLinkPathSuffix: "/status",
} }
if utilfeature.DefaultFeatureGate.Enabled(features.ServerSideApply) && subresources != nil && subresources.Status != nil {
resetFields := storages[v.Name].Status.GetResetFields()
statusScope, err = scopeWithFieldManager(
typeConverter,
statusScope,
resetFields,
true,
)
if err != nil {
return nil, err
}
}
statusScopes[v.Name] = &statusScope statusScopes[v.Name] = &statusScope
if v.Deprecated { if v.Deprecated {
@ -959,6 +958,24 @@ func (r *crdHandler) getOrCreateServingInfoFor(uid types.UID, name string) (*crd
return ret, nil return ret, nil
} }
func scopeWithFieldManager(typeConverter fieldmanager.TypeConverter, reqScope handlers.RequestScope, resetFields map[fieldpath.APIVersion]*fieldpath.Set, ignoreManagedFieldsFromRequestObject bool) (handlers.RequestScope, error) {
fieldManager, err := fieldmanager.NewDefaultCRDFieldManager(
typeConverter,
reqScope.Convertor,
reqScope.Defaulter,
reqScope.Creater,
reqScope.Kind,
reqScope.HubGroupVersion,
ignoreManagedFieldsFromRequestObject,
resetFields,
)
if err != nil {
return handlers.RequestScope{}, err
}
reqScope.FieldManager = fieldManager
return reqScope, nil
}
func defaultDeprecationWarning(deprecatedVersion string, crd apiextensionsv1.CustomResourceDefinitionSpec) string { func defaultDeprecationWarning(deprecatedVersion string, crd apiextensionsv1.CustomResourceDefinitionSpec) string {
msg := fmt.Sprintf("%s/%s %s is deprecated", crd.Group, deprecatedVersion, crd.Names.Kind) msg := fmt.Sprintf("%s/%s %s is deprecated", crd.Group, deprecatedVersion, crd.Names.Kind)

View File

@ -31,6 +31,7 @@ import (
"k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// CustomResourceStorage includes dummy storage for CustomResources, and their Status and Scale subresources. // CustomResourceStorage includes dummy storage for CustomResources, and their Status and Scale subresources.
@ -95,6 +96,7 @@ func newREST(resource schema.GroupResource, kind, listKind schema.GroupVersionKi
CreateStrategy: strategy, CreateStrategy: strategy,
UpdateStrategy: strategy, UpdateStrategy: strategy,
DeleteStrategy: strategy, DeleteStrategy: strategy,
ResetFieldsStrategy: strategy,
TableConvertor: tableConvertor, TableConvertor: tableConvertor,
} }
@ -104,7 +106,9 @@ func newREST(resource schema.GroupResource, kind, listKind schema.GroupVersionKi
} }
statusStore := *store statusStore := *store
statusStore.UpdateStrategy = NewStatusStrategy(strategy) statusStrategy := NewStatusStrategy(strategy)
statusStore.UpdateStrategy = statusStrategy
statusStore.ResetFieldsStrategy = statusStrategy
return &REST{store, categories}, &StatusREST{store: &statusStore} return &REST{store, categories}, &StatusREST{store: &statusStore}
} }
@ -199,6 +203,11 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}
type ScaleREST struct { type ScaleREST struct {
store *genericregistry.Store store *genericregistry.Store
specReplicasPath string specReplicasPath string

View File

@ -22,6 +22,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
type statusStrategy struct { type statusStrategy struct {
@ -32,6 +33,26 @@ func NewStatusStrategy(strategy customResourceStrategy) statusStrategy {
return statusStrategy{strategy} return statusStrategy{strategy}
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (a statusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
fieldpath.APIVersion(a.customResourceStrategy.kind.GroupVersion().String()): fieldpath.NewSet(
// Note that if there are other top level fields unique to CRDs,
// those will also get removed by the apiserver prior to persisting,
// but wont be added to the resetFields set.
// This isn't an issue now, but if it becomes an issue in the future
// we might need a mechanism that is the inverse of resetFields where
// you specify only the fields to be kept rather than the fields to be wiped
// that way you could wipe everything but the status in this case.
fieldpath.MakePathOrDie("spec"),
),
}
return fields
}
func (a statusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (a statusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
// update is only allowed to set status // update is only allowed to set status
newCustomResourceObject := obj.(*unstructured.Unstructured) newCustomResourceObject := obj.(*unstructured.Unstructured)

View File

@ -36,6 +36,7 @@ import (
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema" structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
structurallisttype "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/listtype" structurallisttype "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/listtype"
schemaobjectmeta "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta" schemaobjectmeta "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// customResourceStrategy implements behavior for CustomResources. // customResourceStrategy implements behavior for CustomResources.
@ -48,6 +49,7 @@ type customResourceStrategy struct {
structuralSchemas map[string]*structuralschema.Structural structuralSchemas map[string]*structuralschema.Structural
status *apiextensions.CustomResourceSubresourceStatus status *apiextensions.CustomResourceSubresourceStatus
scale *apiextensions.CustomResourceSubresourceScale scale *apiextensions.CustomResourceSubresourceScale
kind schema.GroupVersionKind
} }
func NewStrategy(typer runtime.ObjectTyper, namespaceScoped bool, kind schema.GroupVersionKind, schemaValidator, statusSchemaValidator *validate.SchemaValidator, structuralSchemas map[string]*structuralschema.Structural, status *apiextensions.CustomResourceSubresourceStatus, scale *apiextensions.CustomResourceSubresourceScale) customResourceStrategy { func NewStrategy(typer runtime.ObjectTyper, namespaceScoped bool, kind schema.GroupVersionKind, schemaValidator, statusSchemaValidator *validate.SchemaValidator, structuralSchemas map[string]*structuralschema.Structural, status *apiextensions.CustomResourceSubresourceStatus, scale *apiextensions.CustomResourceSubresourceScale) customResourceStrategy {
@ -64,6 +66,7 @@ func NewStrategy(typer runtime.ObjectTyper, namespaceScoped bool, kind schema.Gr
statusSchemaValidator: statusSchemaValidator, statusSchemaValidator: statusSchemaValidator,
}, },
structuralSchemas: structuralSchemas, structuralSchemas: structuralSchemas,
kind: kind,
} }
} }
@ -71,6 +74,20 @@ func (a customResourceStrategy) NamespaceScoped() bool {
return a.namespaceScoped return a.namespaceScoped
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (a customResourceStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{}
if a.status != nil {
fields[fieldpath.APIVersion(a.kind.GroupVersion().String())] = fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
)
}
return fields
}
// PrepareForCreate clears the status of a CustomResource before creation. // PrepareForCreate clears the status of a CustomResource before creation.
func (a customResourceStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (a customResourceStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
if a.status != nil { if a.status != nil {

View File

@ -30,6 +30,7 @@ import (
"k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage"
storageerr "k8s.io/apiserver/pkg/storage/errors" storageerr "k8s.io/apiserver/pkg/storage/errors"
"k8s.io/apiserver/pkg/util/dryrun" "k8s.io/apiserver/pkg/util/dryrun"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// rest implements a RESTStorage for API services against etcd // rest implements a RESTStorage for API services against etcd
@ -50,6 +51,7 @@ func NewREST(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*RES
CreateStrategy: strategy, CreateStrategy: strategy,
UpdateStrategy: strategy, UpdateStrategy: strategy,
DeleteStrategy: strategy, DeleteStrategy: strategy,
ResetFieldsStrategy: strategy,
// TODO: define table converter that exposes more than name/creation timestamp // TODO: define table converter that exposes more than name/creation timestamp
TableConvertor: rest.NewDefaultTableConvertor(apiextensions.Resource("customresourcedefinitions")), TableConvertor: rest.NewDefaultTableConvertor(apiextensions.Resource("customresourcedefinitions")),
@ -177,7 +179,9 @@ func NewStatusREST(scheme *runtime.Scheme, rest *REST) *StatusREST {
statusStore := *rest.Store statusStore := *rest.Store
statusStore.CreateStrategy = nil statusStore.CreateStrategy = nil
statusStore.DeleteStrategy = nil statusStore.DeleteStrategy = nil
statusStore.UpdateStrategy = NewStatusStrategy(scheme) statusStrategy := NewStatusStrategy(scheme)
statusStore.UpdateStrategy = statusStrategy
statusStore.ResetFieldsStrategy = statusStrategy
return &StatusREST{store: &statusStore} return &StatusREST{store: &statusStore}
} }
@ -202,3 +206,8 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -23,6 +23,7 @@ import (
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation"
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"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"
@ -32,6 +33,7 @@ import (
"k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/names" "k8s.io/apiserver/pkg/storage/names"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// strategy implements behavior for CustomResources. // strategy implements behavior for CustomResources.
@ -48,6 +50,21 @@ func (strategy) NamespaceScoped() bool {
return false return false
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (strategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"apiextensions.k8s.io/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
"apiextensions.k8s.io/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
// PrepareForCreate clears the status of a CustomResourceDefinition before creation. // PrepareForCreate clears the status of a CustomResourceDefinition before creation.
func (strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
crd := obj.(*apiextensions.CustomResourceDefinition) crd := obj.(*apiextensions.CustomResourceDefinition)
@ -140,18 +157,30 @@ func (statusStrategy) NamespaceScoped() bool {
return false return false
} }
// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (statusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"apiextensions.k8s.io/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("metadata"),
fieldpath.MakePathOrDie("spec"),
),
"apiextensions.k8s.io/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("metadata"),
fieldpath.MakePathOrDie("spec"),
),
}
return fields
}
func (statusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (statusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newObj := obj.(*apiextensions.CustomResourceDefinition) newObj := obj.(*apiextensions.CustomResourceDefinition)
oldObj := old.(*apiextensions.CustomResourceDefinition) oldObj := old.(*apiextensions.CustomResourceDefinition)
newObj.Spec = oldObj.Spec newObj.Spec = oldObj.Spec
// Status updates are for only for updating status, not objectmeta. // Status updates are for only for updating status, not objectmeta.
// TODO: Update after ResetObjectMetaForStatus is added to meta/v1. metav1.ResetObjectMetaForStatus(&newObj.ObjectMeta, &newObj.ObjectMeta)
newObj.Labels = oldObj.Labels
newObj.Annotations = oldObj.Annotations
newObj.OwnerReferences = oldObj.OwnerReferences
newObj.Generation = oldObj.Generation
newObj.SelfLink = oldObj.SelfLink
} }
func (statusStrategy) AllowCreateOnUpdate() bool { func (statusStrategy) AllowCreateOnUpdate() bool {

View File

@ -78,8 +78,8 @@ func NewFieldManager(f Manager, ignoreManagedFieldsFromRequestObject bool) *Fiel
// NewDefaultFieldManager creates a new FieldManager that merges apply requests // NewDefaultFieldManager creates a new FieldManager that merges apply requests
// and update managed fields for other types of requests. // and update managed fields for other types of requests.
func NewDefaultFieldManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion, ignoreManagedFieldsFromRequestObject bool) (*FieldManager, error) { func NewDefaultFieldManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion, ignoreManagedFieldsFromRequestObject bool, resetFields map[fieldpath.APIVersion]*fieldpath.Set) (*FieldManager, error) {
f, err := NewStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub) f, err := NewStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub, resetFields)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create field manager: %v", err) return nil, fmt.Errorf("failed to create field manager: %v", err)
} }
@ -89,8 +89,8 @@ func NewDefaultFieldManager(typeConverter TypeConverter, objectConverter runtime
// NewDefaultCRDFieldManager creates a new FieldManager specifically for // NewDefaultCRDFieldManager creates a new FieldManager specifically for
// CRDs. This allows for the possibility of fields which are not defined // CRDs. This allows for the possibility of fields which are not defined
// in models, as well as having no models defined at all. // in models, as well as having no models defined at all.
func NewDefaultCRDFieldManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion, ignoreManagedFieldsFromRequestObject bool) (_ *FieldManager, err error) { func NewDefaultCRDFieldManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion, ignoreManagedFieldsFromRequestObject bool, resetFields map[fieldpath.APIVersion]*fieldpath.Set) (_ *FieldManager, err error) {
f, err := NewCRDStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub) f, err := NewCRDStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub, resetFields)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create field manager: %v", err) return nil, fmt.Errorf("failed to create field manager: %v", err)
} }

View File

@ -103,6 +103,7 @@ func NewTestFieldManager(gvk schema.GroupVersionKind, ignoreManagedFieldsFromReq
&fakeObjectDefaulter{}, &fakeObjectDefaulter{},
gvk.GroupVersion(), gvk.GroupVersion(),
gvk.GroupVersion(), gvk.GroupVersion(),
nil,
) )
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -41,7 +41,7 @@ var _ Manager = &structuredMergeManager{}
// NewStructuredMergeManager creates a new Manager that merges apply requests // NewStructuredMergeManager creates a new Manager that merges apply requests
// and update managed fields for other types of requests. // and update managed fields for other types of requests.
func NewStructuredMergeManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, gv schema.GroupVersion, hub schema.GroupVersion) (Manager, error) { func NewStructuredMergeManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, gv schema.GroupVersion, hub schema.GroupVersion, resetFields map[fieldpath.APIVersion]*fieldpath.Set) (Manager, error) {
return &structuredMergeManager{ return &structuredMergeManager{
typeConverter: typeConverter, typeConverter: typeConverter,
objectConverter: objectConverter, objectConverter: objectConverter,
@ -50,6 +50,7 @@ func NewStructuredMergeManager(typeConverter TypeConverter, objectConverter runt
hubVersion: hub, hubVersion: hub,
updater: merge.Updater{ updater: merge.Updater{
Converter: newVersionConverter(typeConverter, objectConverter, hub), // This is the converter provided to SMD from k8s Converter: newVersionConverter(typeConverter, objectConverter, hub), // This is the converter provided to SMD from k8s
IgnoredFields: resetFields,
}, },
}, nil }, nil
} }
@ -57,7 +58,7 @@ func NewStructuredMergeManager(typeConverter TypeConverter, objectConverter runt
// NewCRDStructuredMergeManager creates a new Manager specifically for // NewCRDStructuredMergeManager creates a new Manager specifically for
// CRDs. This allows for the possibility of fields which are not defined // CRDs. This allows for the possibility of fields which are not defined
// in models, as well as having no models defined at all. // in models, as well as having no models defined at all.
func NewCRDStructuredMergeManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, gv schema.GroupVersion, hub schema.GroupVersion) (_ Manager, err error) { func NewCRDStructuredMergeManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, gv schema.GroupVersion, hub schema.GroupVersion, resetFields map[fieldpath.APIVersion]*fieldpath.Set) (_ Manager, err error) {
return &structuredMergeManager{ return &structuredMergeManager{
typeConverter: typeConverter, typeConverter: typeConverter,
objectConverter: objectConverter, objectConverter: objectConverter,
@ -66,6 +67,7 @@ func NewCRDStructuredMergeManager(typeConverter TypeConverter, objectConverter r
hubVersion: hub, hubVersion: hub,
updater: merge.Updater{ updater: merge.Updater{
Converter: newCRDVersionConverter(typeConverter, objectConverter, hub), Converter: newCRDVersionConverter(typeConverter, objectConverter, hub),
IgnoredFields: resetFields,
}, },
}, nil }, nil
} }

View File

@ -46,6 +46,7 @@ import (
"k8s.io/apiserver/pkg/storageversion" "k8s.io/apiserver/pkg/storageversion"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
versioninfo "k8s.io/component-base/version" versioninfo "k8s.io/component-base/version"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
const ( const (
@ -258,6 +259,13 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
isCreater = true isCreater = true
} }
var resetFields map[fieldpath.APIVersion]*fieldpath.Set
if a.group.OpenAPIModels != nil && utilfeature.DefaultFeatureGate.Enabled(features.ServerSideApply) {
if resetFieldsStrategy, isResetFieldsStrategy := storage.(rest.ResetFieldsStrategy); isResetFieldsStrategy {
resetFields = resetFieldsStrategy.GetResetFields()
}
}
var versionedList interface{} var versionedList interface{}
if isLister { if isLister {
list := lister.NewList() list := lister.NewList()
@ -597,6 +605,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
fqKindToRegister, fqKindToRegister,
reqScope.HubGroupVersion, reqScope.HubGroupVersion,
isSubresource, isSubresource,
resetFields,
) )
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to create field manager: %v", err) return nil, nil, fmt.Errorf("failed to create field manager: %v", err)

View File

@ -45,6 +45,7 @@ import (
"k8s.io/apiserver/pkg/storage/etcd3/metrics" "k8s.io/apiserver/pkg/storage/etcd3/metrics"
"k8s.io/apiserver/pkg/util/dryrun" "k8s.io/apiserver/pkg/util/dryrun"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
"k8s.io/klog/v2" "k8s.io/klog/v2"
) )
@ -201,6 +202,10 @@ type Store struct {
// of items into tabular output. If unset, the default will be used. // of items into tabular output. If unset, the default will be used.
TableConvertor rest.TableConvertor TableConvertor rest.TableConvertor
// ResetFieldsStrategy provides the fields reset by the strategy that
// should not be modified by the user.
ResetFieldsStrategy rest.ResetFieldsStrategy
// Storage is the interface for the underlying storage for the // Storage is the interface for the underlying storage for the
// resource. It is wrapped into a "DryRunnableStorage" that will // resource. It is wrapped into a "DryRunnableStorage" that will
// either pass-through or simply dry-run. // either pass-through or simply dry-run.
@ -1445,6 +1450,14 @@ func (e *Store) StorageVersion() runtime.GroupVersioner {
return e.StorageVersioner return e.StorageVersioner
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (e *Store) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
if e.ResetFieldsStrategy == nil {
return nil
}
return e.ResetFieldsStrategy.GetResetFields()
}
// validateIndexers will check the prefix of indexers. // validateIndexers will check the prefix of indexers.
func validateIndexers(indexers *cache.Indexers) error { func validateIndexers(indexers *cache.Indexers) error {
if indexers == nil { if indexers == nil {

View File

@ -27,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
//TODO: //TODO:
@ -339,3 +340,23 @@ type StorageVersionProvider interface {
// list of kinds the object might belong to. // list of kinds the object might belong to.
StorageVersion() runtime.GroupVersioner StorageVersion() runtime.GroupVersioner
} }
// ResetFieldsStrategy is an optional interface that a storage object can
// implement if it wishes to provide the fields reset by its strategies.
type ResetFieldsStrategy interface {
GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set
}
// CreateUpdateResetFieldsStrategy is a union of RESTCreateUpdateStrategy
// and ResetFieldsStrategy.
type CreateUpdateResetFieldsStrategy interface {
RESTCreateUpdateStrategy
ResetFieldsStrategy
}
// UpdateResetFieldsStrategy is a union of RESTUpdateStrategy
// and ResetFieldsStrategy.
type UpdateResetFieldsStrategy interface {
RESTUpdateStrategy
ResetFieldsStrategy
}

View File

@ -23,6 +23,7 @@ require (
k8s.io/klog/v2 v2.5.0 k8s.io/klog/v2 v2.5.0
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 k8s.io/utils v0.0.0-20201110183641-67b214c5f920
sigs.k8s.io/structured-merge-diff/v4 v4.0.3
) )
replace ( replace (

View File

@ -29,6 +29,7 @@ import (
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kube-aggregator/pkg/apis/apiregistration" "k8s.io/kube-aggregator/pkg/apis/apiregistration"
"k8s.io/kube-aggregator/pkg/registry/apiservice" "k8s.io/kube-aggregator/pkg/registry/apiservice"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
// REST implements a RESTStorage for API services against etcd // REST implements a RESTStorage for API services against etcd
@ -48,6 +49,7 @@ func NewREST(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) *REST
CreateStrategy: strategy, CreateStrategy: strategy,
UpdateStrategy: strategy, UpdateStrategy: strategy,
DeleteStrategy: strategy, DeleteStrategy: strategy,
ResetFieldsStrategy: strategy,
// TODO: define table converter that exposes more than name/creation timestamp // TODO: define table converter that exposes more than name/creation timestamp
TableConvertor: rest.NewDefaultTableConvertor(apiregistration.Resource("apiservices")), TableConvertor: rest.NewDefaultTableConvertor(apiregistration.Resource("apiservices")),
@ -126,10 +128,12 @@ func getCondition(conditions []apiregistration.APIServiceCondition, conditionTyp
// NewStatusREST makes a RESTStorage for status that has more limited options. // NewStatusREST makes a RESTStorage for status that has more limited options.
// It is based on the original REST so that we can share the same underlying store // It is based on the original REST so that we can share the same underlying store
func NewStatusREST(scheme *runtime.Scheme, rest *REST) *StatusREST { func NewStatusREST(scheme *runtime.Scheme, rest *REST) *StatusREST {
strategy := apiservice.NewStatusStrategy(scheme)
statusStore := *rest.Store statusStore := *rest.Store
statusStore.CreateStrategy = nil statusStore.CreateStrategy = nil
statusStore.DeleteStrategy = nil statusStore.DeleteStrategy = nil
statusStore.UpdateStrategy = apiservice.NewStatusStrategy(scheme) statusStore.UpdateStrategy = strategy
statusStore.ResetFieldsStrategy = strategy
return &StatusREST{store: &statusStore} return &StatusREST{store: &statusStore}
} }
@ -156,3 +160,8 @@ func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.Updat
// subresources should never allow create on update. // subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options) return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
} }
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View File

@ -31,6 +31,7 @@ import (
"k8s.io/kube-aggregator/pkg/apis/apiregistration" "k8s.io/kube-aggregator/pkg/apis/apiregistration"
"k8s.io/kube-aggregator/pkg/apis/apiregistration/validation" "k8s.io/kube-aggregator/pkg/apis/apiregistration/validation"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
type apiServerStrategy struct { type apiServerStrategy struct {
@ -40,9 +41,10 @@ type apiServerStrategy struct {
// apiServerStrategy must implement rest.RESTCreateUpdateStrategy // apiServerStrategy must implement rest.RESTCreateUpdateStrategy
var _ rest.RESTCreateUpdateStrategy = apiServerStrategy{} var _ rest.RESTCreateUpdateStrategy = apiServerStrategy{}
var Strategy = apiServerStrategy{}
// NewStrategy creates a new apiServerStrategy. // NewStrategy creates a new apiServerStrategy.
func NewStrategy(typer runtime.ObjectTyper) rest.RESTCreateUpdateStrategy { func NewStrategy(typer runtime.ObjectTyper) rest.CreateUpdateResetFieldsStrategy {
return apiServerStrategy{typer, names.SimpleNameGenerator} return apiServerStrategy{typer, names.SimpleNameGenerator}
} }
@ -50,6 +52,19 @@ func (apiServerStrategy) NamespaceScoped() bool {
return false return false
} }
func (apiServerStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"apiregistration.k8s.io/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
"apiregistration.k8s.io/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
func (apiServerStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (apiServerStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
apiservice := obj.(*apiregistration.APIService) apiservice := obj.(*apiregistration.APIService)
apiservice.Status = apiregistration.APIServiceStatus{} apiservice.Status = apiregistration.APIServiceStatus{}
@ -91,7 +106,7 @@ type apiServerStatusStrategy struct {
} }
// NewStatusStrategy creates a new apiServerStatusStrategy. // NewStatusStrategy creates a new apiServerStatusStrategy.
func NewStatusStrategy(typer runtime.ObjectTyper) rest.RESTUpdateStrategy { func NewStatusStrategy(typer runtime.ObjectTyper) rest.UpdateResetFieldsStrategy {
return apiServerStatusStrategy{typer, names.SimpleNameGenerator} return apiServerStatusStrategy{typer, names.SimpleNameGenerator}
} }
@ -99,6 +114,21 @@ func (apiServerStatusStrategy) NamespaceScoped() bool {
return false return false
} }
func (apiServerStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"apiregistration.k8s.io/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
fieldpath.MakePathOrDie("metadata"),
),
"apiregistration.k8s.io/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("spec"),
fieldpath.MakePathOrDie("metadata"),
),
}
return fields
}
func (apiServerStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (apiServerStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newAPIService := obj.(*apiregistration.APIService) newAPIService := obj.(*apiregistration.APIService)
oldAPIService := old.(*apiregistration.APIService) oldAPIService := old.(*apiregistration.APIService)

View File

@ -0,0 +1,298 @@
/*
Copyright 2020 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 apiserver
import (
"context"
"encoding/json"
"reflect"
"strings"
"testing"
v1 "k8s.io/api/core/v1"
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apimachinery/pkg/api/meta"
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"
genericfeatures "k8s.io/apiserver/pkg/features"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
featuregatetesting "k8s.io/component-base/featuregate/testing"
apiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
"k8s.io/kubernetes/test/integration/etcd"
"k8s.io/kubernetes/test/integration/framework"
"k8s.io/kubernetes/test/utils/image"
"sigs.k8s.io/yaml"
)
// namespace used for all tests, do not change this
const resetFieldsNamespace = "reset-fields-namespace"
// resetFieldsStatusData contains statuses for all the resources in the
// statusData list with slightly different data to create a field manager
// conflict.
var resetFieldsStatusData = map[schema.GroupVersionResource]string{
gvr("", "v1", "persistentvolumes"): `{"status": {"message": "hello2"}}`,
gvr("", "v1", "resourcequotas"): `{"status": {"used": {"cpu": "25M"}}}`,
gvr("", "v1", "services"): `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.2"}]}}}`,
gvr("extensions", "v1beta1", "ingresses"): `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.2"}]}}}`,
gvr("networking.k8s.io", "v1beta1", "ingresses"): `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.2"}]}}}`,
gvr("networking.k8s.io", "v1", "ingresses"): `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.2"}]}}}`,
gvr("autoscaling", "v1", "horizontalpodautoscalers"): `{"status": {"currentReplicas": 25}}`,
gvr("batch", "v1", "cronjobs"): `{"status": {"lastScheduleTime": "2020-01-01T00:00:00Z"}}`,
gvr("batch", "v1beta1", "cronjobs"): `{"status": {"lastScheduleTime": "2020-01-01T00:00:00Z"}}`,
gvr("storage.k8s.io", "v1", "volumeattachments"): `{"status": {"attached": false}}`,
gvr("policy", "v1", "poddisruptionbudgets"): `{"status": {"currentHealthy": 25}}`,
gvr("policy", "v1beta1", "poddisruptionbudgets"): `{"status": {"currentHealthy": 25}}`,
gvr("internal.apiserver.k8s.io", "v1alpha1", "storageversions"): `{"status": {"commonEncodingVersion":"v1","storageVersions":[{"apiServerID":"1","decodableVersions":["v1","v2"],"encodingVersion":"v1"}],"conditions":[{"type":"AllEncodingVersionsEqual","status":"False","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"allEncodingVersionsEqual","message":"all encoding versions are set to v1"}]}}`,
}
// resetFieldsStatusDefault conflicts with statusDefault
const resetFieldsStatusDefault = `{"status": {"conditions": [{"type": "MyStatus", "status":"False"}]}}`
var resetFieldsSkippedResources = map[string]struct{}{
// TODO: flowschemas is flaking,
// possible bug in the flowschemas controller.
"flowschemas": {},
}
// noConflicts is the set of reources for which
// a conflict cannot occur.
var noConflicts = map[string]struct{}{
// both spec and status get wiped for CSRs,
// nothing is expected to be managed for it, skip it
"certificatesigningrequests": {},
// storageVersions are skipped because their spec is empty
// and thus they can never have a conflict.
"storageversions": {},
// namespaces only have a spec.finalizers field which is also skipped,
// thus it will never have a conflict.
"namespaces": {},
}
var image2 = image.GetE2EImage(image.Etcd)
// resetFieldsSpecData contains conflicting data with the objects in
// etcd.GetEtcdStorageDataForNamespace()
// It contains the minimal changes needed to conflict with all the fields
// added to resetFields by the strategy of each resource.
// In most cases, just one field on the spec is changed, but
// some also wipe metadata or other fields.
var resetFieldsSpecData = map[schema.GroupVersionResource]string{
gvr("", "v1", "resourcequotas"): `{"spec": {"hard": {"cpu": "25M"}}}`,
gvr("", "v1", "namespaces"): `{"spec": {"finalizers": ["kubernetes2"]}}`,
gvr("", "v1", "nodes"): `{"spec": {"unschedulable": false}}`,
gvr("", "v1", "persistentvolumes"): `{"spec": {"capacity": {"storage": "23M"}}}`,
gvr("", "v1", "persistentvolumeclaims"): `{"spec": {"resources": {"limits": {"storage": "21M"}}}}`,
gvr("", "v1", "pods"): `{"metadata": {"deletionTimestamp": "2020-01-01T00:00:00Z", "ownerReferences":[]}, "spec": {"containers": [{"image": "` + image2 + `", "name": "container7"}]}}`,
gvr("", "v1", "replicationcontrollers"): `{"spec": {"selector": {"new": "stuff2"}}}`,
gvr("", "v1", "resourcequotas"): `{"spec": {"hard": {"cpu": "25M"}}}`,
gvr("", "v1", "services"): `{"spec": {"externalName": "service2name"}}`,
gvr("apps", "v1", "daemonsets"): `{"spec": {"template": {"spec": {"containers": [{"image": "` + image2 + `", "name": "container6"}]}}}}`,
gvr("apps", "v1", "deployments"): `{"metadata": {"labels": {"a":"c"}}, "spec": {"template": {"spec": {"containers": [{"image": "` + image2 + `", "name": "container6"}]}}}}`,
gvr("apps", "v1", "replicasets"): `{"spec": {"template": {"spec": {"containers": [{"image": "` + image2 + `", "name": "container4"}]}}}}`,
gvr("apps", "v1", "statefulsets"): `{"spec": {"selector": {"matchLabels": {"a2": "b2"}}}}`,
gvr("autoscaling", "v1", "horizontalpodautoscalers"): `{"spec": {"maxReplicas": 23}}`,
gvr("autoscaling", "v2beta1", "horizontalpodautoscalers"): `{"spec": {"maxReplicas": 23}}`,
gvr("autoscaling", "v2beta2", "horizontalpodautoscalers"): `{"spec": {"maxReplicas": 23}}`,
gvr("batch", "v1", "jobs"): `{"spec": {"template": {"spec": {"containers": [{"image": "` + image2 + `", "name": "container1"}]}}}}`,
gvr("batch", "v1", "cronjobs"): `{"spec": {"jobTemplate": {"spec": {"template": {"spec": {"containers": [{"image": "` + image2 + `", "name": "container0"}]}}}}}}`,
gvr("batch", "v1beta1", "cronjobs"): `{"spec": {"jobTemplate": {"spec": {"template": {"spec": {"containers": [{"image": "` + image2 + `", "name": "container0"}]}}}}}}`,
gvr("certificates.k8s.io", "v1", "certificatesigningrequests"): `{}`,
gvr("certificates.k8s.io", "v1beta1", "certificatesigningrequests"): `{}`,
gvr("flowcontrol.apiserver.k8s.io", "v1alpha1", "flowschemas"): `{"metadata": {"labels":{"a":"c"}}, "spec": {"priorityLevelConfiguration": {"name": "name2"}}}`,
gvr("flowcontrol.apiserver.k8s.io", "v1beta1", "flowschemas"): `{"metadata": {"labels":{"a":"c"}}, "spec": {"priorityLevelConfiguration": {"name": "name2"}}}`,
gvr("flowcontrol.apiserver.k8s.io", "v1alpha1", "prioritylevelconfigurations"): `{"metadata": {"labels":{"a":"c"}}, "spec": {"limited": {"assuredConcurrencyShares": 23}}}`,
gvr("flowcontrol.apiserver.k8s.io", "v1beta1", "prioritylevelconfigurations"): `{"metadata": {"labels":{"a":"c"}}, "spec": {"limited": {"assuredConcurrencyShares": 23}}}`,
gvr("extensions", "v1beta1", "ingresses"): `{"spec": {"backend": {"serviceName": "service2"}}}`,
gvr("networking.k8s.io", "v1beta1", "ingresses"): `{"spec": {"backend": {"serviceName": "service2"}}}`,
gvr("networking.k8s.io", "v1", "ingresses"): `{"spec": {"defaultBackend": {"service": {"name": "service2"}}}}`,
gvr("policy", "v1", "poddisruptionbudgets"): `{"spec": {"selector": {"matchLabels": {"anokkey2": "anokvalue"}}}}`,
gvr("policy", "v1beta1", "poddisruptionbudgets"): `{"spec": {"selector": {"matchLabels": {"anokkey2": "anokvalue"}}}}`,
gvr("storage.k8s.io", "v1alpha1", "volumeattachments"): `{"metadata": {"name": "vaName2"}, "spec": {"nodeName": "localhost2"}}`,
gvr("storage.k8s.io", "v1", "volumeattachments"): `{"metadata": {"name": "vaName2"}, "spec": {"nodeName": "localhost2"}}`,
gvr("apiextensions.k8s.io", "v1", "customresourcedefinitions"): `{"metadata": {"labels":{"a":"c"}}, "spec": {"group": "webconsole22.operator.openshift.io"}}`,
gvr("apiextensions.k8s.io", "v1beta1", "customresourcedefinitions"): `{"metadata": {"labels":{"a":"c"}}, "spec": {"group": "webconsole22.operator.openshift.io"}}`,
gvr("awesome.bears.com", "v1", "pandas"): `{"spec": {"replicas": 102}}`,
gvr("awesome.bears.com", "v3", "pandas"): `{"spec": {"replicas": 302}}`,
gvr("apiregistration.k8s.io", "v1beta1", "apiservices"): `{"metadata": {"labels": {"a":"c"}}, "spec": {"group": "foo2.com"}}`,
gvr("apiregistration.k8s.io", "v1", "apiservices"): `{"metadata": {"labels": {"a":"c"}}, "spec": {"group": "foo2.com"}}`,
gvr("internal.apiserver.k8s.io", "v1alpha1", "storageversions"): `{}`,
}
// TestResetFields makes sure that fieldManager does not own fields reset by the storage strategy.
// It takes 2 objects obj1 and obj2 that differ by one field in the spec and one field in the status.
// It applies obj1 to the spec endpoint and obj2 to the status endpoint, the lack of conflicts
// confirms that the fieldmanager1 is wiped of the status and fieldmanager2 is wiped of the spec.
// We then attempt to apply obj2 to the spec endpoint which fails with an expected conflict.
func TestApplyResetFields(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
server, err := apiservertesting.StartTestServer(t, apiservertesting.NewDefaultTestServerOptions(), []string{"--disable-admission-plugins", "ServiceAccount,TaintNodesByCondition"}, framework.SharedEtcd())
if err != nil {
t.Fatal(err)
}
defer server.TearDownFn()
client, err := kubernetes.NewForConfig(server.ClientConfig)
if err != nil {
t.Fatal(err)
}
dynamicClient, err := dynamic.NewForConfig(server.ClientConfig)
if err != nil {
t.Fatal(err)
}
// create CRDs so we can make sure that custom resources do not get lost
etcd.CreateTestCRDs(t, apiextensionsclientset.NewForConfigOrDie(server.ClientConfig), false, etcd.GetCustomResourceDefinitionData()...)
if _, err := client.CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: resetFieldsNamespace}}, metav1.CreateOptions{}); err != nil {
t.Fatal(err)
}
createData := etcd.GetEtcdStorageDataForNamespace(resetFieldsNamespace)
// gather resources to test
_, resourceLists, err := client.Discovery().ServerGroupsAndResources()
if err != nil {
t.Fatalf("Failed to get ServerGroupsAndResources with error: %+v", err)
}
for _, resourceList := range resourceLists {
for _, resource := range resourceList.APIResources {
if !strings.HasSuffix(resource.Name, "/status") {
continue
}
mapping, err := createMapping(resourceList.GroupVersion, resource)
if err != nil {
t.Fatal(err)
}
t.Run(mapping.Resource.String(), func(t *testing.T) {
if _, ok := resetFieldsSkippedResources[mapping.Resource.Resource]; ok {
t.Skip()
}
namespace := resetFieldsNamespace
if mapping.Scope == meta.RESTScopeRoot {
namespace = ""
}
// assemble first object
status, ok := statusData[mapping.Resource]
if !ok {
status = statusDefault
}
resource, ok := createData[mapping.Resource]
if !ok {
t.Fatalf("no test data for %s. Please add a test for your new type to etcd.GetEtcdStorageData() or getResetFieldsEtcdStorageData()", mapping.Resource)
}
obj1 := unstructured.Unstructured{}
if err := json.Unmarshal([]byte(resource.Stub), &obj1.Object); err != nil {
t.Fatal(err)
}
if err := json.Unmarshal([]byte(status), &obj1.Object); err != nil {
t.Fatal(err)
}
name := obj1.GetName()
obj1.SetAPIVersion(mapping.GroupVersionKind.GroupVersion().String())
obj1.SetKind(mapping.GroupVersionKind.Kind)
obj1.SetName(name)
obj1YAML, err := yaml.Marshal(obj1.Object)
if err != nil {
t.Fatal(err)
}
// apply the spec of the first object
_, err = dynamicClient.
Resource(mapping.Resource).
Namespace(namespace).
Patch(context.TODO(), name, types.ApplyPatchType, obj1YAML, metav1.PatchOptions{FieldManager: "fieldmanager1"}, "")
if err != nil {
t.Fatalf("Failed to apply obj1: %v", err)
}
// create second object
obj2 := &unstructured.Unstructured{}
obj1.DeepCopyInto(obj2)
if err := json.Unmarshal([]byte(resetFieldsSpecData[mapping.Resource]), &obj2.Object); err != nil {
t.Fatal(err)
}
status2, ok := resetFieldsStatusData[mapping.Resource]
if !ok {
status2 = resetFieldsStatusDefault
}
if err := json.Unmarshal([]byte(status2), &obj2.Object); err != nil {
t.Fatal(err)
}
if reflect.DeepEqual(obj1, obj2) {
t.Fatalf("obj1 and obj2 should not be equal %v", obj2)
}
obj2YAML, err := yaml.Marshal(obj2.Object)
if err != nil {
t.Fatal(err)
}
// apply the status of the second object
// this won't conflict if resetfields are set correctly
// and will conflict if they are not
_, err = dynamicClient.
Resource(mapping.Resource).
Namespace(namespace).
Patch(context.TODO(), name, types.ApplyPatchType, obj2YAML, metav1.PatchOptions{FieldManager: "fieldmanager2"}, "status")
if err != nil {
t.Fatalf("Failed to apply obj2: %v", err)
}
// skip checking for conflicts on resources
// that will never have conflicts
if _, ok = noConflicts[mapping.Resource.Resource]; !ok {
// reapply second object to the spec endpoint
// that should fail with a conflict
_, err = dynamicClient.
Resource(mapping.Resource).
Namespace(namespace).
Patch(context.TODO(), name, types.ApplyPatchType, obj2YAML, metav1.PatchOptions{FieldManager: "fieldmanager2"}, "")
if err == nil || !strings.Contains(err.Error(), "conflict") {
t.Fatalf("expected conflict, got error %v", err)
}
// reapply first object to the status endpoint
// that should fail with a conflict
_, err = dynamicClient.
Resource(mapping.Resource).
Namespace(namespace).
Patch(context.TODO(), name, types.ApplyPatchType, obj1YAML, metav1.PatchOptions{FieldManager: "fieldmanager1"}, "status")
if err == nil || !strings.Contains(err.Error(), "conflict") {
t.Fatalf("expected conflict, got error %v", err)
}
}
// cleanup
rsc := dynamicClient.Resource(mapping.Resource).Namespace(namespace)
if err := rsc.Delete(context.TODO(), name, *metav1.NewDeleteOptions(0)); err != nil {
t.Fatalf("deleting final object failed: %v", err)
}
})
}
}
}

View File

@ -56,8 +56,6 @@ var statusData = map[schema.GroupVersionResource]string{
gvr("storage.k8s.io", "v1", "volumeattachments"): `{"status": {"attached": true}}`, gvr("storage.k8s.io", "v1", "volumeattachments"): `{"status": {"attached": true}}`,
gvr("policy", "v1", "poddisruptionbudgets"): `{"status": {"currentHealthy": 5}}`, gvr("policy", "v1", "poddisruptionbudgets"): `{"status": {"currentHealthy": 5}}`,
gvr("policy", "v1beta1", "poddisruptionbudgets"): `{"status": {"currentHealthy": 5}}`, gvr("policy", "v1beta1", "poddisruptionbudgets"): `{"status": {"currentHealthy": 5}}`,
gvr("certificates.k8s.io", "v1beta1", "certificatesigningrequests"): `{"status": {"conditions": [{"type": "MyStatus"}]}}`,
gvr("certificates.k8s.io", "v1", "certificatesigningrequests"): `{"status": {"conditions": [{"type": "MyStatus", "status": "True"}]}}`,
gvr("internal.apiserver.k8s.io", "v1alpha1", "storageversions"): `{"status": {"commonEncodingVersion":"v1","storageVersions":[{"apiServerID":"1","decodableVersions":["v1","v2"],"encodingVersion":"v1"}],"conditions":[{"type":"AllEncodingVersionsEqual","status":"True","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"allEncodingVersionsEqual","message":"all encoding versions are set to v1"}]}}`, gvr("internal.apiserver.k8s.io", "v1alpha1", "storageversions"): `{"status": {"commonEncodingVersion":"v1","storageVersions":[{"apiServerID":"1","decodableVersions":["v1","v2"],"encodingVersion":"v1"}],"conditions":[{"type":"AllEncodingVersionsEqual","status":"True","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"allEncodingVersionsEqual","message":"all encoding versions are set to v1"}]}}`,
} }
@ -133,6 +131,12 @@ func TestApplyStatus(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
t.Run(mapping.Resource.String(), func(t *testing.T) { t.Run(mapping.Resource.String(), func(t *testing.T) {
// both spec and status get wiped for CSRs,
// nothing is expected to be managed for it, skip it
if mapping.Resource.Resource == "certificatesigningrequests" {
t.Skip()
}
status, ok := statusData[mapping.Resource] status, ok := statusData[mapping.Resource]
if !ok { if !ok {
status = statusDefault status = statusDefault

1
vendor/modules.txt vendored
View File

@ -2710,6 +2710,7 @@ sigs.k8s.io/kustomize/kyaml/yaml/merge3
sigs.k8s.io/kustomize/kyaml/yaml/schema sigs.k8s.io/kustomize/kyaml/yaml/schema
sigs.k8s.io/kustomize/kyaml/yaml/walk sigs.k8s.io/kustomize/kyaml/yaml/walk
# sigs.k8s.io/structured-merge-diff/v4 v4.0.3 => sigs.k8s.io/structured-merge-diff/v4 v4.0.3 # sigs.k8s.io/structured-merge-diff/v4 v4.0.3 => sigs.k8s.io/structured-merge-diff/v4 v4.0.3
## explicit
# sigs.k8s.io/structured-merge-diff/v4 => sigs.k8s.io/structured-merge-diff/v4 v4.0.3 # sigs.k8s.io/structured-merge-diff/v4 => sigs.k8s.io/structured-merge-diff/v4 v4.0.3
sigs.k8s.io/structured-merge-diff/v4/fieldpath sigs.k8s.io/structured-merge-diff/v4/fieldpath
sigs.k8s.io/structured-merge-diff/v4/merge sigs.k8s.io/structured-merge-diff/v4/merge