mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 03:11:40 +00:00
Merge pull request #99661 from kevindelgado/status-wiping
Server-Side Apply status wiping
This commit is contained in:
commit
b7d23d7111
@ -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,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,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,CustomResourceDefinitionVersion,AdditionalPrinterColumns
|
||||
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,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,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,CustomResourceDefinitionVersion,AdditionalPrinterColumns
|
||||
API rule violation: list_type_missing,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,JSON,Raw
|
||||
|
12
api/openapi-spec/swagger.json
generated
12
api/openapi-spec/swagger.json
generated
@ -17256,7 +17256,11 @@
|
||||
"items": {
|
||||
"$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": {
|
||||
"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": {
|
||||
"$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": {
|
||||
"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
1
go.mod
@ -137,6 +137,7 @@ require (
|
||||
k8s.io/sample-apiserver v0.0.0
|
||||
k8s.io/system-validators v1.4.0
|
||||
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
|
||||
)
|
||||
|
||||
|
@ -130,6 +130,10 @@ EOF
|
||||
# Creates a node object with name 127.0.0.1. This is required because we do not
|
||||
# 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:
|
||||
# SUPPORTED_RESOURCES(Array of all resources supported by the apiserver).
|
||||
function create_node() {
|
||||
@ -138,7 +142,10 @@ function create_node() {
|
||||
"kind": "Node",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "127.0.0.1"
|
||||
"name": "127.0.0.1",
|
||||
"annotations": {
|
||||
"save-managers": "true"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"capacity": {
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
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
|
||||
@ -46,10 +47,11 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
},
|
||||
DefaultQualifiedResource: apiserverinternal.Resource("storageversions"),
|
||||
|
||||
CreateStrategy: strategy.Strategy,
|
||||
UpdateStrategy: strategy.Strategy,
|
||||
DeleteStrategy: strategy.Strategy,
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
CreateStrategy: strategy.Strategy,
|
||||
UpdateStrategy: strategy.Strategy,
|
||||
DeleteStrategy: strategy.Strategy,
|
||||
ResetFieldsStrategy: strategy.Strategy,
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
options := &generic.StoreOptions{RESTOptions: optsGetter}
|
||||
if err := store.CompleteWithOptions(options); err != nil {
|
||||
@ -57,6 +59,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
}
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = strategy.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = strategy.StatusStrategy
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/apiserverinternal"
|
||||
"k8s.io/kubernetes/pkg/apis/apiserverinternal/validation"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// storageVersionStrategy implements verification logic for StorageVersion.
|
||||
@ -42,6 +43,18 @@ func (storageVersionStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (storageVersionStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
sv := obj.(*apiserverinternal.StorageVersion)
|
||||
@ -90,6 +103,19 @@ type storageVersionStatusStrategy struct {
|
||||
// StatusStrategy is the default logic invoked when updating object status.
|
||||
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) {
|
||||
newSV := obj.(*apiserverinternal.StorageVersion)
|
||||
oldSV := old.(*apiserverinternal.StorageVersion)
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/apps/daemonset"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for DaemonSets
|
||||
@ -44,9 +45,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
NewListFunc: func() runtime.Object { return &apps.DaemonSetList{} },
|
||||
DefaultQualifiedResource: apps.Resource("daemonsets"),
|
||||
|
||||
CreateStrategy: daemonset.Strategy,
|
||||
UpdateStrategy: daemonset.Strategy,
|
||||
DeleteStrategy: daemonset.Strategy,
|
||||
CreateStrategy: daemonset.Strategy,
|
||||
UpdateStrategy: daemonset.Strategy,
|
||||
DeleteStrategy: daemonset.Strategy,
|
||||
ResetFieldsStrategy: daemonset.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
@ -57,6 +59,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = daemonset.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = daemonset.StatusStrategy
|
||||
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/apps/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// daemonSetStrategy implements verification logic for daemon sets.
|
||||
@ -68,6 +69,18 @@ func (daemonSetStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (daemonSetStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
daemonSet := obj.(*apps.DaemonSet)
|
||||
@ -202,6 +215,16 @@ type daemonSetStatusStrategy struct {
|
||||
// StatusStrategy is the default logic invoked when updating object status.
|
||||
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) {
|
||||
newDaemonSet := obj.(*apps.DaemonSet)
|
||||
oldDaemonSet := old.(*apps.DaemonSet)
|
||||
|
@ -43,6 +43,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"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.
|
||||
@ -81,9 +82,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Rollbac
|
||||
NewListFunc: func() runtime.Object { return &apps.DeploymentList{} },
|
||||
DefaultQualifiedResource: apps.Resource("deployments"),
|
||||
|
||||
CreateStrategy: deployment.Strategy,
|
||||
UpdateStrategy: deployment.Strategy,
|
||||
DeleteStrategy: deployment.Strategy,
|
||||
CreateStrategy: deployment.Strategy,
|
||||
UpdateStrategy: deployment.Strategy,
|
||||
DeleteStrategy: deployment.Strategy,
|
||||
ResetFieldsStrategy: deployment.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
@ -94,6 +96,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Rollbac
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = deployment.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = deployment.StatusStrategy
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
type RollbackREST struct {
|
||||
store *genericregistry.Store
|
||||
|
@ -34,6 +34,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/pod"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/apps/validation"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// deploymentStrategy implements behavior for Deployments.
|
||||
@ -67,6 +68,18 @@ func (deploymentStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (deploymentStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
deployment := obj.(*apps.Deployment)
|
||||
@ -147,6 +160,17 @@ type deploymentStatusStrategy struct {
|
||||
// StatusStrategy is the default logic invoked when updating object status.
|
||||
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
|
||||
func (deploymentStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
newDeployment := obj.(*apps.Deployment)
|
||||
|
@ -40,6 +40,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"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.
|
||||
@ -77,9 +78,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
PredicateFunc: replicaset.MatchReplicaSet,
|
||||
DefaultQualifiedResource: apps.Resource("replicasets"),
|
||||
|
||||
CreateStrategy: replicaset.Strategy,
|
||||
UpdateStrategy: replicaset.Strategy,
|
||||
DeleteStrategy: replicaset.Strategy,
|
||||
CreateStrategy: replicaset.Strategy,
|
||||
UpdateStrategy: replicaset.Strategy,
|
||||
DeleteStrategy: replicaset.Strategy,
|
||||
ResetFieldsStrategy: replicaset.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
@ -90,6 +92,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = replicaset.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = replicaset.StatusStrategy
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// GetResetFields implements rest.ResetFieldsStrategy
|
||||
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
|
||||
return r.store.GetResetFields()
|
||||
}
|
||||
|
||||
// ScaleREST implements a Scale for ReplicaSet.
|
||||
type ScaleREST struct {
|
||||
store *genericregistry.Store
|
||||
|
@ -41,6 +41,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/pod"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/apps/validation"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// rsStrategy implements verification logic for ReplicaSets.
|
||||
@ -73,6 +74,18 @@ func (rsStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (rsStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
rs := obj.(*apps.ReplicaSet)
|
||||
@ -189,6 +202,16 @@ type rsStatusStrategy struct {
|
||||
// StatusStrategy is the default logic invoked when updating object status.
|
||||
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) {
|
||||
newRS := obj.(*apps.ReplicaSet)
|
||||
oldRS := old.(*apps.ReplicaSet)
|
||||
|
@ -37,6 +37,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"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.
|
||||
@ -72,9 +73,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
NewListFunc: func() runtime.Object { return &apps.StatefulSetList{} },
|
||||
DefaultQualifiedResource: apps.Resource("statefulsets"),
|
||||
|
||||
CreateStrategy: statefulset.Strategy,
|
||||
UpdateStrategy: statefulset.Strategy,
|
||||
DeleteStrategy: statefulset.Strategy,
|
||||
CreateStrategy: statefulset.Strategy,
|
||||
UpdateStrategy: statefulset.Strategy,
|
||||
DeleteStrategy: statefulset.Strategy,
|
||||
ResetFieldsStrategy: statefulset.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
@ -85,6 +87,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = statefulset.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = statefulset.StatusStrategy
|
||||
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)
|
||||
}
|
||||
|
||||
// GetResetFields implements rest.ResetFieldsStrategy
|
||||
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
|
||||
return r.store.GetResetFields()
|
||||
}
|
||||
|
||||
// Implement ShortNamesProvider
|
||||
var _ rest.ShortNamesProvider = &REST{}
|
||||
|
||||
|
@ -32,6 +32,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/pod"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/apps/validation"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// statefulSetStrategy implements verification logic for Replication StatefulSets.
|
||||
@ -64,6 +65,18 @@ func (statefulSetStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (statefulSetStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
statefulSet := obj.(*apps.StatefulSet)
|
||||
@ -132,6 +145,16 @@ type statefulSetStatusStrategy struct {
|
||||
// StatusStrategy is the default logic invoked when updating object status.
|
||||
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
|
||||
func (statefulSetStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
newStatefulSet := obj.(*apps.StatefulSet)
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"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
|
||||
@ -43,9 +44,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
NewListFunc: func() runtime.Object { return &autoscaling.HorizontalPodAutoscalerList{} },
|
||||
DefaultQualifiedResource: autoscaling.Resource("horizontalpodautoscalers"),
|
||||
|
||||
CreateStrategy: horizontalpodautoscaler.Strategy,
|
||||
UpdateStrategy: horizontalpodautoscaler.Strategy,
|
||||
DeleteStrategy: horizontalpodautoscaler.Strategy,
|
||||
CreateStrategy: horizontalpodautoscaler.Strategy,
|
||||
UpdateStrategy: horizontalpodautoscaler.Strategy,
|
||||
DeleteStrategy: horizontalpodautoscaler.Strategy,
|
||||
ResetFieldsStrategy: horizontalpodautoscaler.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
@ -56,6 +58,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = horizontalpodautoscaler.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = horizontalpodautoscaler.StatusStrategy
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// autoscalerStrategy implements behavior for HorizontalPodAutoscalers
|
||||
@ -44,6 +45,24 @@ func (autoscalerStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (autoscalerStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
newHPA := obj.(*autoscaling.HorizontalPodAutoscaler)
|
||||
@ -115,6 +134,24 @@ type autoscalerStatusStrategy struct {
|
||||
// StatusStrategy is the default logic invoked when updating object status.
|
||||
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) {
|
||||
newAutoscaler := obj.(*autoscaling.HorizontalPodAutoscaler)
|
||||
oldAutoscaler := old.(*autoscaling.HorizontalPodAutoscaler)
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/batch/cronjob"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for scheduled jobs against etcd
|
||||
@ -43,9 +44,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
NewListFunc: func() runtime.Object { return &batch.CronJobList{} },
|
||||
DefaultQualifiedResource: batch.Resource("cronjobs"),
|
||||
|
||||
CreateStrategy: cronjob.Strategy,
|
||||
UpdateStrategy: cronjob.Strategy,
|
||||
DeleteStrategy: cronjob.Strategy,
|
||||
CreateStrategy: cronjob.Strategy,
|
||||
UpdateStrategy: cronjob.Strategy,
|
||||
DeleteStrategy: cronjob.Strategy,
|
||||
ResetFieldsStrategy: cronjob.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
@ -56,6 +58,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = cronjob.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = cronjob.StatusStrategy
|
||||
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/pod"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/kubernetes/pkg/apis/batch/validation"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// cronJobStrategy implements verification logic for Replication Controllers.
|
||||
@ -62,6 +63,21 @@ func (cronJobStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (cronJobStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
cronJob := obj.(*batch.CronJob)
|
||||
@ -115,6 +131,19 @@ type cronJobStatusStrategy struct {
|
||||
// StatusStrategy is the default logic invoked when updating object status.
|
||||
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) {
|
||||
newJob := obj.(*batch.CronJob)
|
||||
oldJob := old.(*batch.CronJob)
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/batch/job"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// JobStorage includes dummy storage for Job.
|
||||
@ -63,9 +64,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
PredicateFunc: job.MatchJob,
|
||||
DefaultQualifiedResource: batch.Resource("jobs"),
|
||||
|
||||
CreateStrategy: job.Strategy,
|
||||
UpdateStrategy: job.Strategy,
|
||||
DeleteStrategy: job.Strategy,
|
||||
CreateStrategy: job.Strategy,
|
||||
UpdateStrategy: job.Strategy,
|
||||
DeleteStrategy: job.Strategy,
|
||||
ResetFieldsStrategy: job.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
@ -76,6 +78,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = job.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = job.StatusStrategy
|
||||
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/apis/batch/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// jobStrategy implements verification logic for Replication Controllers.
|
||||
@ -72,6 +73,18 @@ func (jobStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (jobStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
job := obj.(*batch.Job)
|
||||
@ -210,6 +223,16 @@ type jobStatusStrategy struct {
|
||||
|
||||
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) {
|
||||
newJob := obj.(*batch.Job)
|
||||
oldJob := old.(*batch.Job)
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
csrregistry "k8s.io/kubernetes/pkg/registry/certificates/certificates"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for CertificateSigningRequest.
|
||||
@ -43,9 +44,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Approva
|
||||
NewListFunc: func() runtime.Object { return &certificates.CertificateSigningRequestList{} },
|
||||
DefaultQualifiedResource: certificates.Resource("certificatesigningrequests"),
|
||||
|
||||
CreateStrategy: csrregistry.Strategy,
|
||||
UpdateStrategy: csrregistry.Strategy,
|
||||
DeleteStrategy: csrregistry.Strategy,
|
||||
CreateStrategy: csrregistry.Strategy,
|
||||
UpdateStrategy: csrregistry.Strategy,
|
||||
DeleteStrategy: csrregistry.Strategy,
|
||||
ResetFieldsStrategy: csrregistry.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
@ -59,9 +61,11 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Approva
|
||||
// dedicated strategies.
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = csrregistry.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = csrregistry.StatusStrategy
|
||||
|
||||
approvalStore := *store
|
||||
approvalStore.UpdateStrategy = csrregistry.ApprovalStrategy
|
||||
approvalStore.ResetFieldsStrategy = csrregistry.ApprovalStrategy
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// GetResetFields implements rest.ResetFieldsStrategy
|
||||
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
|
||||
return r.store.GetResetFields()
|
||||
}
|
||||
|
||||
var _ = rest.Patcher(&StatusREST{})
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// GetResetFields implements rest.ResetFieldsStrategy
|
||||
func (r *ApprovalREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
|
||||
return r.store.GetResetFields()
|
||||
}
|
||||
|
||||
var _ = rest.Patcher(&ApprovalREST{})
|
||||
|
@ -33,6 +33,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/certificates"
|
||||
"k8s.io/kubernetes/pkg/apis/certificates/validation"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// csrStrategy implements behavior for CSRs
|
||||
@ -50,6 +51,23 @@ func (csrStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (csrStrategy) AllowCreateOnUpdate() bool {
|
||||
return false
|
||||
@ -125,6 +143,23 @@ type csrStatusStrategy struct {
|
||||
|
||||
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) {
|
||||
newCSR := obj.(*certificates.CertificateSigningRequest)
|
||||
oldCSR := old.(*certificates.CertificateSigningRequest)
|
||||
@ -220,6 +255,23 @@ type csrApprovalStrategy struct {
|
||||
|
||||
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
|
||||
// the data that is updated to only the conditions and populating condition timestamps
|
||||
func (csrApprovalStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
@ -31,13 +32,12 @@ import (
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
storageerr "k8s.io/apiserver/pkg/storage/errors"
|
||||
"k8s.io/apiserver/pkg/util/dryrun"
|
||||
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/core/namespace"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// rest implements a RESTStorage for namespaces
|
||||
@ -67,6 +67,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Finaliz
|
||||
CreateStrategy: namespace.Strategy,
|
||||
UpdateStrategy: namespace.Strategy,
|
||||
DeleteStrategy: namespace.Strategy,
|
||||
ResetFieldsStrategy: namespace.Strategy,
|
||||
ReturnDeletedObject: true,
|
||||
|
||||
ShouldDeleteDuringUpdate: ShouldDeleteNamespaceDuringUpdate,
|
||||
@ -80,9 +81,11 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Finaliz
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = namespace.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = namespace.StatusStrategy
|
||||
|
||||
finalizeStore := *store
|
||||
finalizeStore.UpdateStrategy = namespace.FinalizeStrategy
|
||||
finalizeStore.ResetFieldsStrategy = namespace.FinalizeStrategy
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
// GetResetFields implements rest.ResetFieldsStrategy
|
||||
func (r *REST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
|
||||
return r.store.GetResetFields()
|
||||
}
|
||||
func (r *StatusREST) New() runtime.Object {
|
||||
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)
|
||||
}
|
||||
|
||||
// GetResetFields implements rest.ResetFieldsStrategy
|
||||
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
|
||||
return r.store.GetResetFields()
|
||||
}
|
||||
func (r *FinalizeREST) New() runtime.Object {
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import (
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// namespaceStrategy implements behavior for Namespaces
|
||||
@ -50,6 +51,16 @@ func (namespaceStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (namespaceStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
// on create, status is active
|
||||
@ -138,6 +149,16 @@ type namespaceStatusStrategy struct {
|
||||
|
||||
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) {
|
||||
newNamespace := obj.(*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))
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (namespaceFinalizeStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
newNamespace := obj.(*api.Namespace)
|
||||
|
@ -37,6 +37,7 @@ import (
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/core/node"
|
||||
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.
|
||||
@ -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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func NewStorage(optsGetter generic.RESTOptionsGetter, kubeletClientConfig client.KubeletClientConfig, proxyTransport http.RoundTripper) (*NodeStorage, error) {
|
||||
store := &genericregistry.Store{
|
||||
@ -85,9 +91,10 @@ func NewStorage(optsGetter generic.RESTOptionsGetter, kubeletClientConfig client
|
||||
PredicateFunc: node.MatchNode,
|
||||
DefaultQualifiedResource: api.Resource("nodes"),
|
||||
|
||||
CreateStrategy: node.Strategy,
|
||||
UpdateStrategy: node.Strategy,
|
||||
DeleteStrategy: node.Strategy,
|
||||
CreateStrategy: node.Strategy,
|
||||
UpdateStrategy: node.Strategy,
|
||||
DeleteStrategy: node.Strategy,
|
||||
ResetFieldsStrategy: node.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
@ -102,6 +109,7 @@ func NewStorage(optsGetter generic.RESTOptionsGetter, kubeletClientConfig client
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = node.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = node.StatusStrategy
|
||||
|
||||
// Set up REST handlers
|
||||
nodeREST := &REST{Store: store, proxyTransport: proxyTransport}
|
||||
|
@ -41,6 +41,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||
proxyutil "k8s.io/kubernetes/pkg/proxy/util"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// nodeStrategy implements behavior for nodes
|
||||
@ -58,6 +59,18 @@ func (nodeStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (nodeStrategy) AllowCreateOnUpdate() bool {
|
||||
return false
|
||||
@ -147,6 +160,18 @@ type nodeStatusStrategy struct {
|
||||
|
||||
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) {
|
||||
newNode := obj.(*api.Node)
|
||||
oldNode := old.(*api.Node)
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/core/persistentvolume"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for persistent volumes.
|
||||
@ -48,6 +49,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
UpdateStrategy: persistentvolume.Strategy,
|
||||
DeleteStrategy: persistentvolume.Strategy,
|
||||
ReturnDeletedObject: true,
|
||||
ResetFieldsStrategy: persistentvolume.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
@ -58,6 +60,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = persistentvolume.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = persistentvolume.StatusStrategy
|
||||
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import (
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
volumevalidation "k8s.io/kubernetes/pkg/volume/validation"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// persistentvolumeStrategy implements behavior for PersistentVolume objects
|
||||
@ -48,6 +49,18 @@ func (persistentvolumeStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (persistentvolumeStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
pv := obj.(*api.PersistentVolume)
|
||||
@ -96,6 +109,18 @@ type persistentvolumeStatusStrategy struct {
|
||||
|
||||
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
|
||||
func (persistentvolumeStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
newPv := obj.(*api.PersistentVolume)
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/core/persistentvolumeclaim"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for persistent volume claims.
|
||||
@ -48,6 +49,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
UpdateStrategy: persistentvolumeclaim.Strategy,
|
||||
DeleteStrategy: persistentvolumeclaim.Strategy,
|
||||
ReturnDeletedObject: true,
|
||||
ResetFieldsStrategy: persistentvolumeclaim.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
@ -58,6 +60,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = persistentvolumeclaim.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = persistentvolumeclaim.StatusStrategy
|
||||
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import (
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// persistentvolumeclaimStrategy implements behavior for PersistentVolumeClaim objects
|
||||
@ -49,6 +50,18 @@ func (persistentvolumeclaimStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (persistentvolumeclaimStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
pvc := obj.(*api.PersistentVolumeClaim)
|
||||
@ -94,6 +107,18 @@ type persistentvolumeclaimStatusStrategy struct {
|
||||
|
||||
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
|
||||
func (persistentvolumeclaimStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
newPv := obj.(*api.PersistentVolumeClaim)
|
||||
|
@ -44,6 +44,7 @@ import (
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
registrypod "k8s.io/kubernetes/pkg/registry/core/pod"
|
||||
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
|
||||
@ -79,6 +80,7 @@ func NewStorage(optsGetter generic.RESTOptionsGetter, k client.ConnectionInfoGet
|
||||
CreateStrategy: registrypod.Strategy,
|
||||
UpdateStrategy: registrypod.Strategy,
|
||||
DeleteStrategy: registrypod.Strategy,
|
||||
ResetFieldsStrategy: registrypod.Strategy,
|
||||
ReturnDeletedObject: true,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
@ -95,6 +97,7 @@ func NewStorage(optsGetter generic.RESTOptionsGetter, k client.ConnectionInfoGet
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = registrypod.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = registrypod.StatusStrategy
|
||||
ephemeralContainersStore := *store
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
type EphemeralContainersREST struct {
|
||||
store *genericregistry.Store
|
||||
|
@ -48,6 +48,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||
proxyutil "k8s.io/kubernetes/pkg/proxy/util"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// podStrategy implements behavior for Pods
|
||||
@ -65,6 +66,18 @@ func (podStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (podStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
pod := obj.(*api.Pod)
|
||||
@ -154,6 +167,18 @@ type podStatusStrategy struct {
|
||||
// StatusStrategy wraps and exports the used podStrategy for the storage package.
|
||||
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) {
|
||||
newPod := obj.(*api.Pod)
|
||||
oldPod := old.(*api.Pod)
|
||||
|
@ -39,6 +39,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"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.
|
||||
@ -73,9 +74,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
PredicateFunc: replicationcontroller.MatchController,
|
||||
DefaultQualifiedResource: api.Resource("replicationcontrollers"),
|
||||
|
||||
CreateStrategy: replicationcontroller.Strategy,
|
||||
UpdateStrategy: replicationcontroller.Strategy,
|
||||
DeleteStrategy: replicationcontroller.Strategy,
|
||||
CreateStrategy: replicationcontroller.Strategy,
|
||||
UpdateStrategy: replicationcontroller.Strategy,
|
||||
DeleteStrategy: replicationcontroller.Strategy,
|
||||
ResetFieldsStrategy: replicationcontroller.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
@ -86,6 +88,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = replicationcontroller.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = replicationcontroller.StatusStrategy
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// GetResetFields implements rest.ResetFieldsStrategy
|
||||
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
|
||||
return r.store.GetResetFields()
|
||||
}
|
||||
|
||||
type ScaleREST struct {
|
||||
store *genericregistry.Store
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ import (
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/helper"
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// rcStrategy implements verification logic for Replication Controllers.
|
||||
@ -73,6 +74,18 @@ func (rcStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (rcStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
controller := obj.(*api.ReplicationController)
|
||||
@ -192,6 +205,16 @@ type rcStatusStrategy struct {
|
||||
// StatusStrategy is the default logic invoked when updating object status.
|
||||
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) {
|
||||
newRc := obj.(*api.ReplicationController)
|
||||
oldRc := old.(*api.ReplicationController)
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/core/resourcequota"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for resource quotas.
|
||||
@ -46,6 +47,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
CreateStrategy: resourcequota.Strategy,
|
||||
UpdateStrategy: resourcequota.Strategy,
|
||||
DeleteStrategy: resourcequota.Strategy,
|
||||
ResetFieldsStrategy: resourcequota.Strategy,
|
||||
ReturnDeletedObject: true,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
@ -57,6 +59,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = resourcequota.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = resourcequota.StatusStrategy
|
||||
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// resourcequotaStrategy implements behavior for ResourceQuota objects
|
||||
@ -44,6 +45,18 @@ func (resourcequotaStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (resourcequotaStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
resourcequota := obj.(*api.ResourceQuota)
|
||||
@ -91,6 +104,18 @@ type resourcequotaStatusStrategy struct {
|
||||
// StatusStrategy is the default logic invoked when updating object status.
|
||||
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) {
|
||||
newResourcequota := obj.(*api.ResourceQuota)
|
||||
oldResourcequota := old.(*api.ResourceQuota)
|
||||
|
@ -36,18 +36,17 @@ import (
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/apiserver/pkg/util/dryrun"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
apiservice "k8s.io/kubernetes/pkg/api/service"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
registry "k8s.io/kubernetes/pkg/registry/core/service"
|
||||
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
|
||||
"k8s.io/kubernetes/pkg/registry/core/service/portallocator"
|
||||
netutil "k8s.io/utils/net"
|
||||
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// REST adapts a service registry into apiserver's RESTStorage model.
|
||||
@ -80,6 +79,7 @@ type ServiceStorage interface {
|
||||
rest.GracefulDeleter
|
||||
rest.Watcher
|
||||
rest.StorageVersionProvider
|
||||
rest.ResetFieldsStrategy
|
||||
}
|
||||
|
||||
type EndpointsStorage interface {
|
||||
@ -514,6 +514,11 @@ func (rs *REST) Update(ctx context.Context, name string, objInfo rest.UpdatedObj
|
||||
return out, created, err
|
||||
}
|
||||
|
||||
// GetResetFields implements rest.ResetFieldsStrategy
|
||||
func (rs *REST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
|
||||
return rs.services.GetResetFields()
|
||||
}
|
||||
|
||||
// Implement Redirector.
|
||||
var _ = rest.Redirector(&REST{})
|
||||
|
||||
|
@ -44,6 +44,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
|
||||
"k8s.io/kubernetes/pkg/registry/core/service/portallocator"
|
||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
@ -167,6 +168,11 @@ func (s *serviceStorage) StorageVersion() runtime.GroupVersioner {
|
||||
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) {
|
||||
return NewTestRESTWithPods(t, endpoints, nil, ipFamilies)
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/registry/core/service"
|
||||
registry "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"
|
||||
)
|
||||
@ -54,9 +55,10 @@ func NewGenericREST(optsGetter generic.RESTOptionsGetter, serviceCIDR net.IPNet,
|
||||
DefaultQualifiedResource: api.Resource("services"),
|
||||
ReturnDeletedObject: true,
|
||||
|
||||
CreateStrategy: strategy,
|
||||
UpdateStrategy: strategy,
|
||||
DeleteStrategy: strategy,
|
||||
CreateStrategy: strategy,
|
||||
UpdateStrategy: strategy,
|
||||
DeleteStrategy: strategy,
|
||||
ResetFieldsStrategy: strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
@ -66,7 +68,9 @@ func NewGenericREST(optsGetter generic.RESTOptionsGetter, serviceCIDR net.IPNet,
|
||||
}
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = service.NewServiceStatusStrategy(strategy)
|
||||
statusStrategy := service.NewServiceStatusStrategy(strategy)
|
||||
statusStore.UpdateStrategy = statusStrategy
|
||||
statusStore.ResetFieldsStrategy = statusStrategy
|
||||
|
||||
ipv4 := api.IPv4Protocol
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
@ -212,7 +221,6 @@ func (r *GenericREST) defaultOnReadService(service *api.Service) {
|
||||
service.Spec.IPFamilyPolicy = &singleStack
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// headful
|
||||
// make sure a slice exists to receive the families
|
||||
|
@ -32,10 +32,12 @@ import (
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
netutil "k8s.io/utils/net"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
type Strategy interface {
|
||||
rest.RESTCreateUpdateStrategy
|
||||
rest.ResetFieldsStrategy
|
||||
}
|
||||
|
||||
// svcStrategy implements behavior for Services
|
||||
@ -90,6 +92,18 @@ func (svcStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (strategy svcStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
service := obj.(*api.Service)
|
||||
@ -263,6 +277,18 @@ func NewServiceStatusStrategy(strategy Strategy) 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
|
||||
func (serviceStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
newService := obj.(*api.Service)
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/flowcontrol/flowschema"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// FlowSchemaStorage implements storage for flow schema.
|
||||
@ -49,9 +50,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
NewListFunc: func() runtime.Object { return &flowcontrol.FlowSchemaList{} },
|
||||
DefaultQualifiedResource: flowcontrol.Resource("flowschemas"),
|
||||
|
||||
CreateStrategy: flowschema.Strategy,
|
||||
UpdateStrategy: flowschema.Strategy,
|
||||
DeleteStrategy: flowschema.Strategy,
|
||||
CreateStrategy: flowschema.Strategy,
|
||||
UpdateStrategy: flowschema.Strategy,
|
||||
DeleteStrategy: flowschema.Strategy,
|
||||
ResetFieldsStrategy: flowschema.Strategy,
|
||||
|
||||
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.UpdateStrategy = flowschema.StatusStrategy
|
||||
statusStore.DeleteStrategy = nil
|
||||
statusStore.ResetFieldsStrategy = flowschema.StatusStrategy
|
||||
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol/validation"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// flowSchemaStrategy implements verification logic for FlowSchema.
|
||||
@ -42,6 +43,21 @@ func (flowSchemaStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (flowSchemaStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
fl := obj.(*flowcontrol.FlowSchema)
|
||||
@ -91,6 +107,23 @@ type flowSchemaStatusStrategy struct {
|
||||
// StatusStrategy is the default logic that applies when updating flow-schema objects' status.
|
||||
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) {
|
||||
newFlowSchema := obj.(*flowcontrol.FlowSchema)
|
||||
oldFlowSchema := old.(*flowcontrol.FlowSchema)
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/flowcontrol/prioritylevelconfiguration"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// PriorityLevelConfigurationStorage implements storage for priority level configuration.
|
||||
@ -49,9 +50,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
NewListFunc: func() runtime.Object { return &flowcontrol.PriorityLevelConfigurationList{} },
|
||||
DefaultQualifiedResource: flowcontrol.Resource("prioritylevelconfigurations"),
|
||||
|
||||
CreateStrategy: prioritylevelconfiguration.Strategy,
|
||||
UpdateStrategy: prioritylevelconfiguration.Strategy,
|
||||
DeleteStrategy: prioritylevelconfiguration.Strategy,
|
||||
CreateStrategy: prioritylevelconfiguration.Strategy,
|
||||
UpdateStrategy: prioritylevelconfiguration.Strategy,
|
||||
DeleteStrategy: prioritylevelconfiguration.Strategy,
|
||||
ResetFieldsStrategy: prioritylevelconfiguration.Strategy,
|
||||
|
||||
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.UpdateStrategy = prioritylevelconfiguration.StatusStrategy
|
||||
statusStore.DeleteStrategy = nil
|
||||
statusStore.ResetFieldsStrategy = prioritylevelconfiguration.StatusStrategy
|
||||
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol"
|
||||
"k8s.io/kubernetes/pkg/apis/flowcontrol/validation"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// priorityLevelConfigurationStrategy implements verification logic for priority level configurations.
|
||||
@ -42,6 +43,21 @@ func (priorityLevelConfigurationStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (priorityLevelConfigurationStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
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.
|
||||
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) {
|
||||
newPriorityLevelConfiguration := obj.(*flowcontrol.PriorityLevelConfiguration)
|
||||
oldPriorityLevelConfiguration := old.(*flowcontrol.PriorityLevelConfiguration)
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/networking/ingress"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for replication controllers
|
||||
@ -43,9 +44,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
NewListFunc: func() runtime.Object { return &networking.IngressList{} },
|
||||
DefaultQualifiedResource: networking.Resource("ingresses"),
|
||||
|
||||
CreateStrategy: ingress.Strategy,
|
||||
UpdateStrategy: ingress.Strategy,
|
||||
DeleteStrategy: ingress.Strategy,
|
||||
CreateStrategy: ingress.Strategy,
|
||||
UpdateStrategy: ingress.Strategy,
|
||||
DeleteStrategy: ingress.Strategy,
|
||||
ResetFieldsStrategy: ingress.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
@ -56,6 +58,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = ingress.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = ingress.StatusStrategy
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ package ingress
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
@ -27,6 +28,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/networking"
|
||||
"k8s.io/kubernetes/pkg/apis/networking/validation"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// ingressStrategy implements verification logic for Replication Ingress.
|
||||
@ -43,6 +45,24 @@ func (ingressStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (ingressStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
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
|
||||
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
|
||||
func (ingressStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
newIngress := obj.(*networking.Ingress)
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"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.
|
||||
@ -43,9 +44,10 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
NewListFunc: func() runtime.Object { return &policyapi.PodDisruptionBudgetList{} },
|
||||
DefaultQualifiedResource: policyapi.Resource("poddisruptionbudgets"),
|
||||
|
||||
CreateStrategy: poddisruptionbudget.Strategy,
|
||||
UpdateStrategy: poddisruptionbudget.Strategy,
|
||||
DeleteStrategy: poddisruptionbudget.Strategy,
|
||||
CreateStrategy: poddisruptionbudget.Strategy,
|
||||
UpdateStrategy: poddisruptionbudget.Strategy,
|
||||
DeleteStrategy: poddisruptionbudget.Strategy,
|
||||
ResetFieldsStrategy: poddisruptionbudget.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
@ -56,6 +58,7 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = poddisruptionbudget.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = poddisruptionbudget.StatusStrategy
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/policy"
|
||||
"k8s.io/kubernetes/pkg/apis/policy/validation"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// podDisruptionBudgetStrategy implements verification logic for PodDisruptionBudgets.
|
||||
@ -44,6 +45,21 @@ func (podDisruptionBudgetStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (podDisruptionBudgetStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
podDisruptionBudget := obj.(*policy.PodDisruptionBudget)
|
||||
@ -101,6 +117,21 @@ type podDisruptionBudgetStatusStrategy struct {
|
||||
// StatusStrategy is the default logic invoked when updating object status.
|
||||
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
|
||||
func (podDisruptionBudgetStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
newPodDisruptionBudget := obj.(*policy.PodDisruptionBudget)
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/storage/volumeattachment"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// VolumeAttachmentStorage includes storage for VolumeAttachments and all subresources
|
||||
@ -52,6 +53,7 @@ func NewStorage(optsGetter generic.RESTOptionsGetter) (*VolumeAttachmentStorage,
|
||||
CreateStrategy: volumeattachment.Strategy,
|
||||
UpdateStrategy: volumeattachment.Strategy,
|
||||
DeleteStrategy: volumeattachment.Strategy,
|
||||
ResetFieldsStrategy: volumeattachment.Strategy,
|
||||
ReturnDeletedObject: true,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
@ -63,6 +65,7 @@ func NewStorage(optsGetter generic.RESTOptionsGetter) (*VolumeAttachmentStorage,
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = volumeattachment.StatusStrategy
|
||||
statusStore.ResetFieldsStrategy = volumeattachment.StatusStrategy
|
||||
|
||||
return &VolumeAttachmentStorage{
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/apis/storage"
|
||||
"k8s.io/kubernetes/pkg/apis/storage/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// volumeAttachmentStrategy implements behavior for VolumeAttachment objects
|
||||
@ -47,6 +48,18 @@ func (volumeAttachmentStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (volumeAttachmentStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
var groupVersion schema.GroupVersion
|
||||
@ -143,6 +156,19 @@ type volumeAttachmentStatusStrategy struct {
|
||||
// VolumeAttachmentStatus subresource via the REST API.
|
||||
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
|
||||
func (volumeAttachmentStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
|
||||
newVolumeAttachment := obj.(*storage.VolumeAttachment)
|
||||
|
@ -27,6 +27,7 @@ require (
|
||||
k8s.io/klog/v2 v2.5.0
|
||||
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7
|
||||
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
|
||||
)
|
||||
|
||||
|
@ -326,6 +326,8 @@ type CustomResourceDefinitionCondition struct {
|
||||
// CustomResourceDefinitionStatus indicates the state of the CustomResourceDefinition
|
||||
type CustomResourceDefinitionStatus struct {
|
||||
// Conditions indicate state for particular aspects of a CustomResourceDefinition
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
Conditions []CustomResourceDefinitionCondition
|
||||
|
||||
// AcceptedNames are the names that are actually being used to serve discovery
|
||||
|
@ -234,6 +234,8 @@ message CustomResourceDefinitionSpec {
|
||||
message CustomResourceDefinitionStatus {
|
||||
// conditions indicate state for particular aspects of a CustomResourceDefinition
|
||||
// +optional
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
repeated CustomResourceDefinitionCondition conditions = 1;
|
||||
|
||||
// acceptedNames are the names that are actually being used to serve discovery.
|
||||
|
@ -329,6 +329,8 @@ type CustomResourceDefinitionCondition struct {
|
||||
type CustomResourceDefinitionStatus struct {
|
||||
// conditions indicate state for particular aspects of a CustomResourceDefinition
|
||||
// +optional
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
Conditions []CustomResourceDefinitionCondition `json:"conditions" protobuf:"bytes,1,opt,name=conditions"`
|
||||
|
||||
// acceptedNames are the names that are actually being used to serve discovery.
|
||||
|
@ -280,6 +280,8 @@ message CustomResourceDefinitionSpec {
|
||||
message CustomResourceDefinitionStatus {
|
||||
// conditions indicate state for particular aspects of a CustomResourceDefinition
|
||||
// +optional
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
repeated CustomResourceDefinitionCondition conditions = 1;
|
||||
|
||||
// acceptedNames are the names that are actually being used to serve discovery.
|
||||
|
@ -361,6 +361,8 @@ type CustomResourceDefinitionCondition struct {
|
||||
type CustomResourceDefinitionStatus struct {
|
||||
// conditions indicate state for particular aspects of a CustomResourceDefinition
|
||||
// +optional
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
Conditions []CustomResourceDefinitionCondition `json:"conditions" protobuf:"bytes,1,opt,name=conditions"`
|
||||
|
||||
// acceptedNames are the names that are actually being used to serve discovery.
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"time"
|
||||
|
||||
goopenapispec "github.com/go-openapi/spec"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
|
||||
apiextensionshelpers "k8s.io/apiextensions-apiserver/pkg/apihelpers"
|
||||
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,
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ServerSideApply) {
|
||||
resetFields := storages[v.Name].CustomResource.GetResetFields()
|
||||
reqScope := *requestScopes[v.Name]
|
||||
reqScope.FieldManager, err = fieldmanager.NewDefaultCRDFieldManager(
|
||||
reqScope, err = scopeWithFieldManager(
|
||||
typeConverter,
|
||||
reqScope.Convertor,
|
||||
reqScope.Defaulter,
|
||||
reqScope.Creater,
|
||||
reqScope.Kind,
|
||||
reqScope.HubGroupVersion,
|
||||
reqScope,
|
||||
resetFields,
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
@ -901,20 +900,6 @@ func (r *crdHandler) getOrCreateServingInfoFor(uid types.UID, name string) (*crd
|
||||
// override status subresource values
|
||||
// shallow copy
|
||||
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.Namer = handlers.ContextBasedNaming{
|
||||
SelfLinker: meta.NewAccessor(),
|
||||
@ -922,6 +907,20 @@ func (r *crdHandler) getOrCreateServingInfoFor(uid types.UID, name string) (*crd
|
||||
SelfLinkPathPrefix: selfLinkPrefix,
|
||||
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
|
||||
|
||||
if v.Deprecated {
|
||||
@ -959,6 +958,24 @@ func (r *crdHandler) getOrCreateServingInfoFor(uid types.UID, name string) (*crd
|
||||
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 {
|
||||
msg := fmt.Sprintf("%s/%s %s is deprecated", crd.Group, deprecatedVersion, crd.Names.Kind)
|
||||
|
||||
|
@ -31,6 +31,7 @@ import (
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"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.
|
||||
@ -92,9 +93,10 @@ func newREST(resource schema.GroupResource, kind, listKind schema.GroupVersionKi
|
||||
PredicateFunc: strategy.MatchCustomResourceDefinitionStorage,
|
||||
DefaultQualifiedResource: resource,
|
||||
|
||||
CreateStrategy: strategy,
|
||||
UpdateStrategy: strategy,
|
||||
DeleteStrategy: strategy,
|
||||
CreateStrategy: strategy,
|
||||
UpdateStrategy: strategy,
|
||||
DeleteStrategy: strategy,
|
||||
ResetFieldsStrategy: strategy,
|
||||
|
||||
TableConvertor: tableConvertor,
|
||||
}
|
||||
@ -104,7 +106,9 @@ func newREST(resource schema.GroupResource, kind, listKind schema.GroupVersionKi
|
||||
}
|
||||
|
||||
statusStore := *store
|
||||
statusStore.UpdateStrategy = NewStatusStrategy(strategy)
|
||||
statusStrategy := NewStatusStrategy(strategy)
|
||||
statusStore.UpdateStrategy = statusStrategy
|
||||
statusStore.ResetFieldsStrategy = statusStrategy
|
||||
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)
|
||||
}
|
||||
|
||||
// GetResetFields implements rest.ResetFieldsStrategy
|
||||
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
|
||||
return r.store.GetResetFields()
|
||||
}
|
||||
|
||||
type ScaleREST struct {
|
||||
store *genericregistry.Store
|
||||
specReplicasPath string
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
type statusStrategy struct {
|
||||
@ -32,6 +33,26 @@ func NewStatusStrategy(strategy customResourceStrategy) statusStrategy {
|
||||
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) {
|
||||
// update is only allowed to set status
|
||||
newCustomResourceObject := obj.(*unstructured.Unstructured)
|
||||
|
@ -36,6 +36,7 @@ import (
|
||||
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||
structurallisttype "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/listtype"
|
||||
schemaobjectmeta "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// customResourceStrategy implements behavior for CustomResources.
|
||||
@ -48,6 +49,7 @@ type customResourceStrategy struct {
|
||||
structuralSchemas map[string]*structuralschema.Structural
|
||||
status *apiextensions.CustomResourceSubresourceStatus
|
||||
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 {
|
||||
@ -64,6 +66,7 @@ func NewStrategy(typer runtime.ObjectTyper, namespaceScoped bool, kind schema.Gr
|
||||
statusSchemaValidator: statusSchemaValidator,
|
||||
},
|
||||
structuralSchemas: structuralSchemas,
|
||||
kind: kind,
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +74,20 @@ func (a customResourceStrategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (a customResourceStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
if a.status != nil {
|
||||
|
@ -30,6 +30,7 @@ import (
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
storageerr "k8s.io/apiserver/pkg/storage/errors"
|
||||
"k8s.io/apiserver/pkg/util/dryrun"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// rest implements a RESTStorage for API services against etcd
|
||||
@ -47,9 +48,10 @@ func NewREST(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*RES
|
||||
PredicateFunc: MatchCustomResourceDefinition,
|
||||
DefaultQualifiedResource: apiextensions.Resource("customresourcedefinitions"),
|
||||
|
||||
CreateStrategy: strategy,
|
||||
UpdateStrategy: strategy,
|
||||
DeleteStrategy: strategy,
|
||||
CreateStrategy: strategy,
|
||||
UpdateStrategy: strategy,
|
||||
DeleteStrategy: strategy,
|
||||
ResetFieldsStrategy: strategy,
|
||||
|
||||
// TODO: define table converter that exposes more than name/creation timestamp
|
||||
TableConvertor: rest.NewDefaultTableConvertor(apiextensions.Resource("customresourcedefinitions")),
|
||||
@ -177,7 +179,9 @@ func NewStatusREST(scheme *runtime.Scheme, rest *REST) *StatusREST {
|
||||
statusStore := *rest.Store
|
||||
statusStore.CreateStrategy = nil
|
||||
statusStore.DeleteStrategy = nil
|
||||
statusStore.UpdateStrategy = NewStatusStrategy(scheme)
|
||||
statusStrategy := NewStatusStrategy(scheme)
|
||||
statusStore.UpdateStrategy = statusStrategy
|
||||
statusStore.ResetFieldsStrategy = statusStrategy
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@ -32,6 +33,7 @@ import (
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
"k8s.io/apiserver/pkg/storage/names"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// strategy implements behavior for CustomResources.
|
||||
@ -48,6 +50,21 @@ func (strategy) NamespaceScoped() bool {
|
||||
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.
|
||||
func (strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||
crd := obj.(*apiextensions.CustomResourceDefinition)
|
||||
@ -140,18 +157,30 @@ func (statusStrategy) NamespaceScoped() bool {
|
||||
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) {
|
||||
newObj := obj.(*apiextensions.CustomResourceDefinition)
|
||||
oldObj := old.(*apiextensions.CustomResourceDefinition)
|
||||
newObj.Spec = oldObj.Spec
|
||||
|
||||
// Status updates are for only for updating status, not objectmeta.
|
||||
// TODO: Update after ResetObjectMetaForStatus is added to meta/v1.
|
||||
newObj.Labels = oldObj.Labels
|
||||
newObj.Annotations = oldObj.Annotations
|
||||
newObj.OwnerReferences = oldObj.OwnerReferences
|
||||
newObj.Generation = oldObj.Generation
|
||||
newObj.SelfLink = oldObj.SelfLink
|
||||
metav1.ResetObjectMetaForStatus(&newObj.ObjectMeta, &newObj.ObjectMeta)
|
||||
}
|
||||
|
||||
func (statusStrategy) AllowCreateOnUpdate() bool {
|
||||
|
@ -78,8 +78,8 @@ func NewFieldManager(f Manager, ignoreManagedFieldsFromRequestObject bool) *Fiel
|
||||
|
||||
// NewDefaultFieldManager creates a new FieldManager that merges apply 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) {
|
||||
f, err := NewStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub)
|
||||
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, resetFields)
|
||||
if err != nil {
|
||||
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
|
||||
// CRDs. This allows for the possibility of fields which are not defined
|
||||
// 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) {
|
||||
f, err := NewCRDStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub)
|
||||
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, resetFields)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create field manager: %v", err)
|
||||
}
|
||||
|
@ -103,6 +103,7 @@ func NewTestFieldManager(gvk schema.GroupVersionKind, ignoreManagedFieldsFromReq
|
||||
&fakeObjectDefaulter{},
|
||||
gvk.GroupVersion(),
|
||||
gvk.GroupVersion(),
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -41,7 +41,7 @@ var _ Manager = &structuredMergeManager{}
|
||||
|
||||
// NewStructuredMergeManager creates a new Manager that merges apply 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{
|
||||
typeConverter: typeConverter,
|
||||
objectConverter: objectConverter,
|
||||
@ -49,7 +49,8 @@ func NewStructuredMergeManager(typeConverter TypeConverter, objectConverter runt
|
||||
groupVersion: gv,
|
||||
hubVersion: hub,
|
||||
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
|
||||
}
|
||||
@ -57,7 +58,7 @@ func NewStructuredMergeManager(typeConverter TypeConverter, objectConverter runt
|
||||
// NewCRDStructuredMergeManager creates a new Manager specifically for
|
||||
// CRDs. This allows for the possibility of fields which are not defined
|
||||
// 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{
|
||||
typeConverter: typeConverter,
|
||||
objectConverter: objectConverter,
|
||||
@ -65,7 +66,8 @@ func NewCRDStructuredMergeManager(typeConverter TypeConverter, objectConverter r
|
||||
groupVersion: gv,
|
||||
hubVersion: hub,
|
||||
updater: merge.Updater{
|
||||
Converter: newCRDVersionConverter(typeConverter, objectConverter, hub),
|
||||
Converter: newCRDVersionConverter(typeConverter, objectConverter, hub),
|
||||
IgnoredFields: resetFields,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ import (
|
||||
"k8s.io/apiserver/pkg/storageversion"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
versioninfo "k8s.io/component-base/version"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -258,6 +259,13 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
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{}
|
||||
if isLister {
|
||||
list := lister.NewList()
|
||||
@ -597,6 +605,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
fqKindToRegister,
|
||||
reqScope.HubGroupVersion,
|
||||
isSubresource,
|
||||
resetFields,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create field manager: %v", err)
|
||||
|
@ -45,6 +45,7 @@ import (
|
||||
"k8s.io/apiserver/pkg/storage/etcd3/metrics"
|
||||
"k8s.io/apiserver/pkg/util/dryrun"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
@ -201,6 +202,10 @@ type Store struct {
|
||||
// of items into tabular output. If unset, the default will be used.
|
||||
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
|
||||
// resource. It is wrapped into a "DryRunnableStorage" that will
|
||||
// either pass-through or simply dry-run.
|
||||
@ -1445,6 +1450,14 @@ func (e *Store) StorageVersion() runtime.GroupVersioner {
|
||||
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.
|
||||
func validateIndexers(indexers *cache.Indexers) error {
|
||||
if indexers == nil {
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
//TODO:
|
||||
@ -339,3 +340,23 @@ type StorageVersionProvider interface {
|
||||
// list of kinds the object might belong to.
|
||||
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
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ require (
|
||||
k8s.io/klog/v2 v2.5.0
|
||||
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7
|
||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.3
|
||||
)
|
||||
|
||||
replace (
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/kube-aggregator/pkg/apis/apiregistration"
|
||||
"k8s.io/kube-aggregator/pkg/registry/apiservice"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for API services against etcd
|
||||
@ -45,9 +46,10 @@ func NewREST(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) *REST
|
||||
PredicateFunc: apiservice.MatchAPIService,
|
||||
DefaultQualifiedResource: apiregistration.Resource("apiservices"),
|
||||
|
||||
CreateStrategy: strategy,
|
||||
UpdateStrategy: strategy,
|
||||
DeleteStrategy: strategy,
|
||||
CreateStrategy: strategy,
|
||||
UpdateStrategy: strategy,
|
||||
DeleteStrategy: strategy,
|
||||
ResetFieldsStrategy: strategy,
|
||||
|
||||
// TODO: define table converter that exposes more than name/creation timestamp
|
||||
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.
|
||||
// It is based on the original REST so that we can share the same underlying store
|
||||
func NewStatusREST(scheme *runtime.Scheme, rest *REST) *StatusREST {
|
||||
strategy := apiservice.NewStatusStrategy(scheme)
|
||||
statusStore := *rest.Store
|
||||
statusStore.CreateStrategy = nil
|
||||
statusStore.DeleteStrategy = nil
|
||||
statusStore.UpdateStrategy = apiservice.NewStatusStrategy(scheme)
|
||||
statusStore.UpdateStrategy = strategy
|
||||
statusStore.ResetFieldsStrategy = strategy
|
||||
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.
|
||||
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()
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import (
|
||||
|
||||
"k8s.io/kube-aggregator/pkg/apis/apiregistration"
|
||||
"k8s.io/kube-aggregator/pkg/apis/apiregistration/validation"
|
||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||
)
|
||||
|
||||
type apiServerStrategy struct {
|
||||
@ -40,9 +41,10 @@ type apiServerStrategy struct {
|
||||
|
||||
// apiServerStrategy must implement rest.RESTCreateUpdateStrategy
|
||||
var _ rest.RESTCreateUpdateStrategy = apiServerStrategy{}
|
||||
var Strategy = 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}
|
||||
}
|
||||
|
||||
@ -50,6 +52,19 @@ func (apiServerStrategy) NamespaceScoped() bool {
|
||||
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) {
|
||||
apiservice := obj.(*apiregistration.APIService)
|
||||
apiservice.Status = apiregistration.APIServiceStatus{}
|
||||
@ -91,7 +106,7 @@ type apiServerStatusStrategy struct {
|
||||
}
|
||||
|
||||
// NewStatusStrategy creates a new apiServerStatusStrategy.
|
||||
func NewStatusStrategy(typer runtime.ObjectTyper) rest.RESTUpdateStrategy {
|
||||
func NewStatusStrategy(typer runtime.ObjectTyper) rest.UpdateResetFieldsStrategy {
|
||||
return apiServerStatusStrategy{typer, names.SimpleNameGenerator}
|
||||
}
|
||||
|
||||
@ -99,6 +114,21 @@ func (apiServerStatusStrategy) NamespaceScoped() bool {
|
||||
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) {
|
||||
newAPIService := obj.(*apiregistration.APIService)
|
||||
oldAPIService := old.(*apiregistration.APIService)
|
||||
|
298
test/integration/apiserver/apply/reset_fields_test.go
Normal file
298
test/integration/apiserver/apply/reset_fields_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -44,21 +44,19 @@ import (
|
||||
const testNamespace = "statusnamespace"
|
||||
|
||||
var statusData = map[schema.GroupVersionResource]string{
|
||||
gvr("", "v1", "persistentvolumes"): `{"status": {"message": "hello"}}`,
|
||||
gvr("", "v1", "resourcequotas"): `{"status": {"used": {"cpu": "5M"}}}`,
|
||||
gvr("", "v1", "services"): `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.1"}]}}}`,
|
||||
gvr("extensions", "v1beta1", "ingresses"): `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.1"}]}}}`,
|
||||
gvr("networking.k8s.io", "v1beta1", "ingresses"): `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.1"}]}}}`,
|
||||
gvr("networking.k8s.io", "v1", "ingresses"): `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.1"}]}}}`,
|
||||
gvr("autoscaling", "v1", "horizontalpodautoscalers"): `{"status": {"currentReplicas": 5}}`,
|
||||
gvr("batch", "v1", "cronjobs"): `{"status": {"lastScheduleTime": null}}`,
|
||||
gvr("batch", "v1beta1", "cronjobs"): `{"status": {"lastScheduleTime": null}}`,
|
||||
gvr("storage.k8s.io", "v1", "volumeattachments"): `{"status": {"attached": true}}`,
|
||||
gvr("policy", "v1", "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("", "v1", "persistentvolumes"): `{"status": {"message": "hello"}}`,
|
||||
gvr("", "v1", "resourcequotas"): `{"status": {"used": {"cpu": "5M"}}}`,
|
||||
gvr("", "v1", "services"): `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.1"}]}}}`,
|
||||
gvr("extensions", "v1beta1", "ingresses"): `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.1"}]}}}`,
|
||||
gvr("networking.k8s.io", "v1beta1", "ingresses"): `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.1"}]}}}`,
|
||||
gvr("networking.k8s.io", "v1", "ingresses"): `{"status": {"loadBalancer": {"ingress": [{"ip": "127.0.0.1"}]}}}`,
|
||||
gvr("autoscaling", "v1", "horizontalpodautoscalers"): `{"status": {"currentReplicas": 5}}`,
|
||||
gvr("batch", "v1", "cronjobs"): `{"status": {"lastScheduleTime": null}}`,
|
||||
gvr("batch", "v1beta1", "cronjobs"): `{"status": {"lastScheduleTime": null}}`,
|
||||
gvr("storage.k8s.io", "v1", "volumeattachments"): `{"status": {"attached": true}}`,
|
||||
gvr("policy", "v1", "poddisruptionbudgets"): `{"status": {"currentHealthy": 5}}`,
|
||||
gvr("policy", "v1beta1", "poddisruptionbudgets"): `{"status": {"currentHealthy": 5}}`,
|
||||
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"}]}}`,
|
||||
}
|
||||
|
||||
const statusDefault = `{"status": {"conditions": [{"type": "MyStatus", "status":"True"}]}}`
|
||||
@ -133,6 +131,12 @@ func TestApplyStatus(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
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]
|
||||
if !ok {
|
||||
status = statusDefault
|
||||
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@ -2710,6 +2710,7 @@ sigs.k8s.io/kustomize/kyaml/yaml/merge3
|
||||
sigs.k8s.io/kustomize/kyaml/yaml/schema
|
||||
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
|
||||
## 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/fieldpath
|
||||
sigs.k8s.io/structured-merge-diff/v4/merge
|
||||
|
Loading…
Reference in New Issue
Block a user