diff --git a/staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/validation/validation.go b/staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/validation/validation.go index ef69b78e612..f88585f4b53 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/validation/validation.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/validation/validation.go @@ -18,16 +18,17 @@ package validation import ( "fmt" + "strings" "k8s.io/apimachinery/pkg/api/validation" "k8s.io/apimachinery/pkg/api/validation/path" utilvalidation "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" - discoveryapi "k8s.io/kube-aggregator/pkg/apis/apiregistration" + "k8s.io/kube-aggregator/pkg/apis/apiregistration" ) -func ValidateAPIService(apiService *discoveryapi.APIService) field.ErrorList { +func ValidateAPIService(apiService *apiregistration.APIService) field.ErrorList { requiredName := apiService.Spec.Version + "." + apiService.Spec.Group allErrs := validation.ValidateObjectMeta(&apiService.ObjectMeta, false, @@ -86,9 +87,29 @@ func ValidateAPIService(apiService *discoveryapi.APIService) field.ErrorList { return allErrs } -func ValidateAPIServiceUpdate(newAPIService *discoveryapi.APIService, oldAPIService *discoveryapi.APIService) field.ErrorList { +func ValidateAPIServiceUpdate(newAPIService *apiregistration.APIService, oldAPIService *apiregistration.APIService) field.ErrorList { allErrs := validation.ValidateObjectMetaUpdate(&newAPIService.ObjectMeta, &oldAPIService.ObjectMeta, field.NewPath("metadata")) allErrs = append(allErrs, ValidateAPIService(newAPIService)...) return allErrs } + +func ValidateAPIServiceStatus(status *apiregistration.APIServiceStatus, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + for i, condition := range status.Conditions { + if condition.Status != apiregistration.ConditionTrue && + condition.Status != apiregistration.ConditionFalse && + condition.Status != apiregistration.ConditionUnknown { + allErrs = append(allErrs, field.NotSupported(fldPath.Child("conditions").Index(i).Child("status"), condition.Status, []string{apiregistration.ConditionTrue, apiregistration.ConditionFalse, apiregistration.ConditionUnknown})) + } + } + + return allErrs +} + +func ValidateAPIServiceStatusUpdate(newAPIService *apiregistration.APIService, oldAPIService *apiregistration.APIService) field.ErrorList { + allErrs := validation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata")) + allErrs = append(allErrs, ValidateAPIServiceStatus(&update.Status, field.NewPath("status"))...) + return allErrs +} diff --git a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go index 6de74e316d1..f8adab5c9c6 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go @@ -163,7 +163,9 @@ func (c completedConfig) NewWithDelegate(delegationTarget genericapiserver.Deleg apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiregistration.GroupName, registry, Scheme, metav1.ParameterCodec, Codecs) apiGroupInfo.GroupMeta.GroupVersion = v1alpha1.SchemeGroupVersion v1alpha1storage := map[string]rest.Storage{} - v1alpha1storage["apiservices"] = apiservicestorage.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter) + apiServiceREST := apiservicestorage.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter) + v1alpha1storage["apiservices"] = apiServiceREST + v1alpha1storage["apiservices/status"] = apiservicestorage.NewStatusREST(Scheme, apiServiceREST) apiGroupInfo.VersionedResourcesStorageMap["v1alpha1"] = v1alpha1storage if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil { diff --git a/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go b/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go index 53e86e6186f..01b3152b000 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go @@ -20,6 +20,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/registry/generic" genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" + "k8s.io/apiserver/pkg/registry/rest" "k8s.io/kube-aggregator/pkg/apis/apiregistration" "k8s.io/kube-aggregator/pkg/registry/apiservice" ) @@ -50,3 +51,28 @@ func NewREST(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) *REST } return &REST{store} } + +// 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 { + statusStore := *rest.store + statusStore.CreateStrategy = nil + statusStore.DeleteStrategy = nil + statusStore.UpdateStrategy = apiservice.NewStatusStrategy(scheme) + return &REST{store: statusStore} +} + +type StatusREST struct { + store *genericregistry.Store +} + +var _ = rest.Updater(&StatusREST{}) + +func (r *StatusREST) New() runtime.Object { + return &extensions.Deployment{} +} + +// Update alters the status subset of an object. +func (r *StatusREST) Update(ctx genericapirequest.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) { + return r.store.Update(ctx, name, objInfo) +} diff --git a/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/strategy.go b/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/strategy.go index 1689e1f6ac5..cad75ad60d2 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/strategy.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/strategy.go @@ -46,7 +46,8 @@ func (apiServerStrategy) NamespaceScoped() bool { } func (apiServerStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) { - _ = obj.(*apiregistration.APIService) + apiservice = obj.(*apiregistration.APIService) + apiservice.Status = apiregistration.APIServiceStatus{} } func (apiServerStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) { @@ -74,6 +75,44 @@ func (apiServerStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old return validation.ValidateAPIServiceUpdate(obj.(*apiregistration.APIService), old.(*apiregistration.APIService)) } +type apiServerStatusStrategy struct { + runtime.ObjectTyper + names.NameGenerator +} + +func NewStatusStrategy(typer runtime.ObjectTyper) apiServerStatusStrategy { + return apiServerStrategy{typer, names.SimpleNameGenerator} +} + +func (apiServerStrategy) NamespaceScoped() bool { + return false +} + +func (apiServerStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) { + newAPIService := obj.(*apiregistration.APIService) + oldAPIService := old.(*apiregistration.APIService) + newAPIService.Spec = oldAPIService.Spec + newAPIService.Labels = oldAPIService.Labels + newAPIService.Annotations = oldAPIService.Annotations + newAPIService.Finalizers = oldAPIService.Finalizers + newAPIService.OwnerReferences = oldAPIService.OwnerReferences +} + +func (apiServerStrategy) AllowCreateOnUpdate() bool { + return false +} + +func (apiServerStrategy) AllowUnconditionalUpdate() bool { + return false +} + +func (apiServerStrategy) Canonicalize(obj runtime.Object) { +} + +func (apiServerStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList { + return validation.ValidateAPIServiceStatusUpdate(obj.(*apiregistration.APIService), old.(*apiregistration.APIService)) +} + func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) { apiserver, ok := obj.(*apiregistration.APIService) if !ok {