Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
subresource status
  • Loading branch information
phosae committed Jul 24, 2023
commit dd45a967451104846ff497da6831deac9a0d3cb6
2 changes: 1 addition & 1 deletion api-aggregation-lib/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
k8s.io/client-go v0.27.3
k8s.io/component-base v0.27.3
k8s.io/klog/v2 v2.100.1
sigs.k8s.io/structured-merge-diff/v4 v4.2.3
)

require (
Expand Down Expand Up @@ -99,7 +100,6 @@ require (
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.1.2 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)

Expand Down
2 changes: 1 addition & 1 deletion api-aggregation-lib/pkg/apiserver/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func (c completedConfig) New() (*HelloApiServer, error) {
}

v1storage := map[string]rest.Storage{"foos": restStorage.Foo}
v2storage := map[string]rest.Storage{"foos": restStorage.Foo, "foos/config": restStorage.Config}
v2storage := map[string]rest.Storage{"foos": restStorage.Foo, "foos/config": restStorage.Config, "foos/status": restStorage.Status}
apiGroupInfo.VersionedResourcesStorageMap["v1"] = v1storage
apiGroupInfo.VersionedResourcesStorageMap["v2"] = v2storage
}
Expand Down
48 changes: 47 additions & 1 deletion api-aggregation-lib/pkg/registry/hello.zeng.dev/foo/etcd.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type REST struct {
type fooStorage struct {
Foo *REST
Config *ConfigREST
Status *StatusREST
}

var _ rest.ShortNamesProvider = &REST{}
Expand All @@ -33,6 +34,7 @@ func (*REST) ShortNames() []string {
// NewREST returns a RESTStorage object that will work against API services.
func NewREST(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*fooStorage, error) {
strategy := NewStrategy(scheme)
statusStrategy := NewStatusStrategy(strategy)

store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &hello.Foo{} },
Expand All @@ -52,8 +54,11 @@ func NewREST(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) (*foo
}

configStore := *store
statusStore := *store
statusStore.UpdateStrategy = statusStrategy
statusStore.ResetFieldsStrategy = statusStrategy

return &fooStorage{&REST{store}, &ConfigREST{Store: &configStore}}, nil
return &fooStorage{&REST{store}, &ConfigREST{Store: &configStore}, &StatusREST{&statusStore}}, nil
}

// ConfigREST implements the config subresource for a Foo
Expand Down Expand Up @@ -89,6 +94,10 @@ func (r *ConfigREST) Get(ctx context.Context, name string, options *metav1.GetOp
}

// Update alters the spec.config subset of an object.
// Normally option createValidation and option updateValidation are validating admission control funcs
//
// see https://github.com/kubernetes/kubernetes/blob/d25c0a1bdb81b7a9b52abf10687d701c82704602/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go#L270
// see https://github.com/kubernetes/kubernetes/blob/d25c0a1bdb81b7a9b52abf10687d701c82704602/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/update.go#L210-L216
func (r *ConfigREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
obj, _, err := r.Store.Update(
ctx,
Expand Down Expand Up @@ -204,3 +213,40 @@ func (c *configUpdatedObjectInfo) UpdatedObject(ctx context.Context, oldObj runt

return foo, nil
}

// StatusREST implements the REST endpoint for changing the status of a foo.
type StatusREST struct {
store *genericregistry.Store
}

// New creates a new foo resource
func (r *StatusREST) New() runtime.Object {
return &hello.Foo{}
}

// Destroy cleans up resources on shutdown.
func (r *StatusREST) Destroy() {
// Given that underlying store is shared with REST,
// we don't destroy it here explicitly.
}

// Get retrieves the object from the storage. It is required to support Patch.
func (r *StatusREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
return r.store.Get(ctx, name, options)
}

// Update alters the status subset of an object.
func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
// 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()
}

func (r *StatusREST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
return r.store.ConvertToTable(ctx, object, tableOptions)
}
52 changes: 52 additions & 0 deletions api-aggregation-lib/pkg/registry/hello.zeng.dev/foo/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,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"

hello "github.com/phosae/x-kubernetes/api-aggregation-lib/pkg/api/hello.zeng.dev"
"github.com/phosae/x-kubernetes/api-aggregation-lib/pkg/api/hello.zeng.dev/validation"
Expand Down Expand Up @@ -55,6 +56,18 @@ func (fooStrategy) 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 (fooStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}

return fields
}

func (fooStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
}

Expand Down Expand Up @@ -113,3 +126,42 @@ func (fooStrategy) ConvertToTable(ctx context.Context, object runtime.Object, ta

return &table, nil
}

// NewStrategy creates and returns a fooStrategy instance
func NewStatusStrategy(s fooStrategy) fooStatusStrategy {
return fooStatusStrategy{fooStrategy: s}
}

type fooStatusStrategy struct {
fooStrategy
}

// GetResetFields returns the set of fields that get reset by the strategy
// and should not be modified by the user.
func (fooStatusStrategy) 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 (fooStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newFoo := obj.(*hello.Foo)
oldFoo := old.(*hello.Foo)
newFoo.Spec = oldFoo.Spec
newFoo.DeletionTimestamp = nil

newFoo.OwnerReferences = oldFoo.OwnerReferences
}

func (fooStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return field.ErrorList{}
}

// WarningsOnUpdate returns warnings for the given update.
func (fooStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}