diff --git a/cmd/kube-apiserver/app/aggregator.go b/cmd/kube-apiserver/app/aggregator.go index c044147f60c..848f415b37f 100644 --- a/cmd/kube-apiserver/app/aggregator.go +++ b/cmd/kube-apiserver/app/aggregator.go @@ -50,6 +50,7 @@ var apiVersionPriorities = merge(controlplaneapiserver.DefaultGenericAPIServiceP {Group: "node.k8s.io", Version: "v1"}: {Group: 16300, Version: 15}, {Group: "node.k8s.io", Version: "v1alpha1"}: {Group: 16300, Version: 1}, {Group: "node.k8s.io", Version: "v1beta1"}: {Group: 16300, Version: 9}, + {Group: "resource.k8s.io", Version: "v1"}: {Group: 16200, Version: 21}, {Group: "resource.k8s.io", Version: "v1beta2"}: {Group: 16200, Version: 15}, {Group: "resource.k8s.io", Version: "v1beta1"}: {Group: 16200, Version: 9}, {Group: "resource.k8s.io", Version: "v1alpha3"}: {Group: 16200, Version: 1}, diff --git a/pkg/api/testing/defaulting_test.go b/pkg/api/testing/defaulting_test.go index 30d92694c2b..73cee1fa4c2 100644 --- a/pkg/api/testing/defaulting_test.go +++ b/pkg/api/testing/defaulting_test.go @@ -157,6 +157,12 @@ func TestDefaulting(t *testing.T) { {Group: "resource.k8s.io", Version: "v1beta2", Kind: "ResourceClaimTemplateList"}: {}, {Group: "resource.k8s.io", Version: "v1beta2", Kind: "ResourceSlice"}: {}, {Group: "resource.k8s.io", Version: "v1beta2", Kind: "ResourceSliceList"}: {}, + {Group: "resource.k8s.io", Version: "v1", Kind: "ResourceClaim"}: {}, + {Group: "resource.k8s.io", Version: "v1", Kind: "ResourceClaimList"}: {}, + {Group: "resource.k8s.io", Version: "v1", Kind: "ResourceClaimTemplate"}: {}, + {Group: "resource.k8s.io", Version: "v1", Kind: "ResourceClaimTemplateList"}: {}, + {Group: "resource.k8s.io", Version: "v1", Kind: "ResourceSlice"}: {}, + {Group: "resource.k8s.io", Version: "v1", Kind: "ResourceSliceList"}: {}, {Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingAdmissionPolicy"}: {}, {Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingAdmissionPolicyList"}: {}, {Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "ValidatingAdmissionPolicyBinding"}: {}, diff --git a/pkg/apis/resource/install/install.go b/pkg/apis/resource/install/install.go index 5e5f7104a36..114b1d4384b 100644 --- a/pkg/apis/resource/install/install.go +++ b/pkg/apis/resource/install/install.go @@ -23,6 +23,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/apis/resource" + v1 "k8s.io/kubernetes/pkg/apis/resource/v1" "k8s.io/kubernetes/pkg/apis/resource/v1alpha3" "k8s.io/kubernetes/pkg/apis/resource/v1beta1" "k8s.io/kubernetes/pkg/apis/resource/v1beta2" @@ -38,7 +39,8 @@ func Install(scheme *runtime.Scheme) { utilruntime.Must(v1alpha3.AddToScheme(scheme)) utilruntime.Must(v1beta1.AddToScheme(scheme)) utilruntime.Must(v1beta2.AddToScheme(scheme)) + utilruntime.Must(v1.AddToScheme(scheme)) // TODO(https://github.com/kubernetes/kubernetes/issues/129889) We should // change the serialization version to v1beta2 for 1.34. - utilruntime.Must(scheme.SetVersionPriority(v1beta1.SchemeGroupVersion, v1beta2.SchemeGroupVersion, v1alpha3.SchemeGroupVersion)) + utilruntime.Must(scheme.SetVersionPriority(v1beta1.SchemeGroupVersion, v1beta2.SchemeGroupVersion, v1.SchemeGroupVersion, v1alpha3.SchemeGroupVersion)) } diff --git a/pkg/controlplane/instance.go b/pkg/controlplane/instance.go index a5fb08ffbc8..e209618e36f 100644 --- a/pkg/controlplane/instance.go +++ b/pkg/controlplane/instance.go @@ -49,6 +49,7 @@ import ( nodev1 "k8s.io/api/node/v1" policyapiv1 "k8s.io/api/policy/v1" rbacv1 "k8s.io/api/rbac/v1" + resourcev1 "k8s.io/api/resource/v1" resourcev1alpha3 "k8s.io/api/resource/v1alpha3" resourcev1beta1 "k8s.io/api/resource/v1beta1" resourcev1beta2 "k8s.io/api/resource/v1beta2" @@ -459,6 +460,7 @@ var ( nodev1.SchemeGroupVersion, policyapiv1.SchemeGroupVersion, rbacv1.SchemeGroupVersion, + resourcev1.SchemeGroupVersion, storageapiv1.SchemeGroupVersion, schedulingapiv1.SchemeGroupVersion, flowcontrolv1.SchemeGroupVersion, diff --git a/pkg/registry/resource/resourceclaim/strategy.go b/pkg/registry/resource/resourceclaim/strategy.go index ac88b02e2a8..adf5ca8bab0 100644 --- a/pkg/registry/resource/resourceclaim/strategy.go +++ b/pkg/registry/resource/resourceclaim/strategy.go @@ -30,7 +30,7 @@ import ( "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" utilfeature "k8s.io/apiserver/pkg/util/feature" - "k8s.io/client-go/kubernetes/typed/core/v1" + v1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/dynamic-resource-allocation/structured" "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/apis/resource" @@ -74,6 +74,9 @@ func (*resourceclaimStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpa "resource.k8s.io/v1beta2": fieldpath.NewSet( fieldpath.MakePathOrDie("status"), ), + "resource.k8s.io/v1": fieldpath.NewSet( + fieldpath.MakePathOrDie("status"), + ), } return fields @@ -151,6 +154,9 @@ func (*resourceclaimStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*f "resource.k8s.io/v1beta2": fieldpath.NewSet( fieldpath.MakePathOrDie("spec"), ), + "resource.k8s.io/v1": fieldpath.NewSet( + fieldpath.MakePathOrDie("spec"), + ), } return fields diff --git a/pkg/registry/resource/rest/storage_resource.go b/pkg/registry/resource/rest/storage_resource.go index 96e1844bdef..27660fea478 100644 --- a/pkg/registry/resource/rest/storage_resource.go +++ b/pkg/registry/resource/rest/storage_resource.go @@ -17,6 +17,7 @@ limitations under the License. package rest import ( + resourcev1 "k8s.io/api/resource/v1" resourcev1alpha3 "k8s.io/api/resource/v1alpha3" resourcev1beta1 "k8s.io/api/resource/v1beta1" resourcev1beta2 "k8s.io/api/resource/v1beta2" @@ -47,6 +48,12 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag // If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities. // TODO refactor the plumbing to provide the information in the APIGroupInfo + if storageMap, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter, p.NamespaceClient); err != nil { + return genericapiserver.APIGroupInfo{}, err + } else if len(storageMap) > 0 { + apiGroupInfo.VersionedResourcesStorageMap[resourcev1.SchemeGroupVersion.Version] = storageMap + } + if storageMap, err := p.v1alpha3Storage(apiResourceConfigSource, restOptionsGetter, p.NamespaceClient); err != nil { return genericapiserver.APIGroupInfo{}, err } else if len(storageMap) > 0 { @@ -68,6 +75,45 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag return apiGroupInfo, nil } +func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter, nsClient v1.NamespaceInterface) (map[string]rest.Storage, error) { + storage := map[string]rest.Storage{} + + if resource := "deviceclasses"; apiResourceConfigSource.ResourceEnabled(resourcev1.SchemeGroupVersion.WithResource(resource)) { + deviceclassStorage, err := deviceclassstore.NewREST(restOptionsGetter) + if err != nil { + return nil, err + } + storage[resource] = deviceclassStorage + } + + if resource := "resourceclaims"; apiResourceConfigSource.ResourceEnabled(resourcev1.SchemeGroupVersion.WithResource(resource)) { + resourceClaimStorage, resourceClaimStatusStorage, err := resourceclaimstore.NewREST(restOptionsGetter, nsClient) + if err != nil { + return nil, err + } + storage[resource] = resourceClaimStorage + storage[resource+"/status"] = resourceClaimStatusStorage + } + + if resource := "resourceclaimtemplates"; apiResourceConfigSource.ResourceEnabled(resourcev1.SchemeGroupVersion.WithResource(resource)) { + resourceClaimTemplateStorage, err := resourceclaimtemplatestore.NewREST(restOptionsGetter, nsClient) + if err != nil { + return nil, err + } + storage[resource] = resourceClaimTemplateStorage + } + + if resource := "resourceslices"; apiResourceConfigSource.ResourceEnabled(resourcev1.SchemeGroupVersion.WithResource(resource)) { + resourceSliceStorage, err := resourceslicestore.NewREST(restOptionsGetter) + if err != nil { + return nil, err + } + storage[resource] = resourceSliceStorage + } + + return storage, nil +} + func (p RESTStorageProvider) v1alpha3Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter, nsClient v1.NamespaceInterface) (map[string]rest.Storage, error) { storage := map[string]rest.Storage{} diff --git a/staging/src/k8s.io/api/resource/v1/types.go b/staging/src/k8s.io/api/resource/v1/types.go index 77a53040440..d36084f3e07 100644 --- a/staging/src/k8s.io/api/resource/v1/types.go +++ b/staging/src/k8s.io/api/resource/v1/types.go @@ -36,7 +36,7 @@ const ( // +genclient // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +k8s:prerelease-lifecycle-gen:introduced=1.33 +// +k8s:prerelease-lifecycle-gen:introduced=1.34 // ResourceSlice represents one or more resources in a pool of similar resources, // managed by a common driver. A pool may span more than one ResourceSlice, and exactly how many @@ -500,7 +500,7 @@ const ( ) // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +k8s:prerelease-lifecycle-gen:introduced=1.33 +// +k8s:prerelease-lifecycle-gen:introduced=1.34 // ResourceSliceList is a collection of ResourceSlices. type ResourceSliceList struct { @@ -515,7 +515,7 @@ type ResourceSliceList struct { // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +k8s:prerelease-lifecycle-gen:introduced=1.33 +// +k8s:prerelease-lifecycle-gen:introduced=1.34 // ResourceClaim describes a request for access to resources in the cluster, // for use by workloads. For example, if a workload needs an accelerator device @@ -1314,7 +1314,7 @@ const ( ) // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +k8s:prerelease-lifecycle-gen:introduced=1.33 +// +k8s:prerelease-lifecycle-gen:introduced=1.34 // ResourceClaimList is a collection of claims. type ResourceClaimList struct { @@ -1330,7 +1330,7 @@ type ResourceClaimList struct { // +genclient // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +k8s:prerelease-lifecycle-gen:introduced=1.33 +// +k8s:prerelease-lifecycle-gen:introduced=1.34 // DeviceClass is a vendor- or admin-provided resource that contains // device configuration and selectors. It can be referenced in @@ -1387,7 +1387,7 @@ type DeviceClassConfiguration struct { } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +k8s:prerelease-lifecycle-gen:introduced=1.33 +// +k8s:prerelease-lifecycle-gen:introduced=1.34 // DeviceClassList is a collection of classes. type DeviceClassList struct { @@ -1402,7 +1402,7 @@ type DeviceClassList struct { // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +k8s:prerelease-lifecycle-gen:introduced=1.33 +// +k8s:prerelease-lifecycle-gen:introduced=1.34 // ResourceClaimTemplate is used to produce ResourceClaim objects. // @@ -1437,7 +1437,7 @@ type ResourceClaimTemplateSpec struct { } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +k8s:prerelease-lifecycle-gen:introduced=1.33 +// +k8s:prerelease-lifecycle-gen:introduced=1.34 // ResourceClaimTemplateList is a collection of claim templates. type ResourceClaimTemplateList struct { diff --git a/staging/src/k8s.io/api/roundtrip_test.go b/staging/src/k8s.io/api/roundtrip_test.go index 19ae5312ce8..630b77d25e5 100644 --- a/staging/src/k8s.io/api/roundtrip_test.go +++ b/staging/src/k8s.io/api/roundtrip_test.go @@ -68,6 +68,7 @@ import ( rbacv1 "k8s.io/api/rbac/v1" rbacv1alpha1 "k8s.io/api/rbac/v1alpha1" rbacv1beta1 "k8s.io/api/rbac/v1beta1" + resourcev1 "k8s.io/api/resource/v1" resourcev1alpha3 "k8s.io/api/resource/v1alpha3" resourcev1beta1 "k8s.io/api/resource/v1beta1" resourcev1beta2 "k8s.io/api/resource/v1beta2" @@ -138,6 +139,7 @@ var groups = []runtime.SchemeBuilder{ resourcev1alpha3.SchemeBuilder, resourcev1beta1.SchemeBuilder, resourcev1beta2.SchemeBuilder, + resourcev1.SchemeBuilder, schedulingv1alpha1.SchemeBuilder, schedulingv1beta1.SchemeBuilder, schedulingv1.SchemeBuilder, diff --git a/test/integration/apiserver/apply/reset_fields_test.go b/test/integration/apiserver/apply/reset_fields_test.go index 5119a4741c6..86a53619e5f 100644 --- a/test/integration/apiserver/apply/reset_fields_test.go +++ b/test/integration/apiserver/apply/reset_fields_test.go @@ -61,6 +61,7 @@ var resetFieldsStatusData = map[schema.GroupVersionResource]string{ gvr("policy", "v1beta1", "poddisruptionbudgets"): `{"status": {"currentHealthy": 25}}`, gvr("resource.k8s.io", "v1beta1", "resourceclaims"): `{"status": {"allocation": {"nodeSelector": {"nodeSelectorTerms": [{"matchExpressions": [{"key": "some-label", "operator": "In", "values": ["some-other-value"]}] }]}}}}`, gvr("resource.k8s.io", "v1beta2", "resourceclaims"): `{"status": {"allocation": {"nodeSelector": {"nodeSelectorTerms": [{"matchExpressions": [{"key": "some-label", "operator": "In", "values": ["some-other-value"]}] }]}}}}`, + gvr("resource.k8s.io", "v1", "resourceclaims"): `{"status": {"allocation": {"nodeSelector": {"nodeSelectorTerms": [{"matchExpressions": [{"key": "some-label", "operator": "In", "values": ["some-other-value"]}] }]}}}}`, 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"}]}}`, // standard for []metav1.Condition gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicies"): `{"status": {"conditions":[{"type":"Accepted","status":"True","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"RuleApplied","message":"Rule was applied"}]}}`, @@ -157,6 +158,9 @@ var resetFieldsSpecData = map[schema.GroupVersionResource]string{ gvr("resource.k8s.io", "v1beta2", "deviceclasses"): `{"metadata": {"labels":{"a":"c"}}}`, gvr("resource.k8s.io", "v1beta2", "resourceclaims"): `{"spec": {"devices": {"requests": [{"name": "req-0", "exactly": {"deviceClassName": "other-class"}}]}}}`, // spec is immutable, but that doesn't matter for the test. gvr("resource.k8s.io", "v1beta2", "resourceclaimtemplates"): `{"spec": {"spec": {"resourceClassName": "class2name"}}}`, + gvr("resource.k8s.io", "v1", "deviceclasses"): `{"metadata": {"labels":{"a":"c"}}}`, + gvr("resource.k8s.io", "v1", "resourceclaims"): `{"spec": {"devices": {"requests": [{"name": "req-0", "exactly": {"deviceClassName": "other-class"}}]}}}`, // spec is immutable, but that doesn't matter for the test. + gvr("resource.k8s.io", "v1", "resourceclaimtemplates"): `{"spec": {"spec": {"resourceClassName": "class2name"}}}`, gvr("internal.apiserver.k8s.io", "v1alpha1", "storageversions"): `{}`, gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicies"): `{"metadata": {"labels": {"a":"c"}}, "spec": {"paramKind": {"apiVersion": "apps/v1", "kind": "Deployment"}}}`, gvr("admissionregistration.k8s.io", "v1beta1", "validatingadmissionpolicies"): `{"metadata": {"labels": {"a":"c"}}, "spec": {"paramKind": {"apiVersion": "apps/v1", "kind": "Deployment"}}}`, diff --git a/test/integration/apiserver/apply/status_test.go b/test/integration/apiserver/apply/status_test.go index afc15b957b8..15aee66dafa 100644 --- a/test/integration/apiserver/apply/status_test.go +++ b/test/integration/apiserver/apply/status_test.go @@ -60,6 +60,7 @@ var statusData = map[schema.GroupVersionResource]string{ gvr("policy", "v1beta1", "poddisruptionbudgets"): `{"status": {"currentHealthy": 5}}`, gvr("resource.k8s.io", "v1beta1", "resourceclaims"): `{"status": {"allocation": {"nodeSelector": {"nodeSelectorTerms": [{"matchExpressions": [{"key": "some-label", "operator": "In", "values": ["some-value"]}] }]}}}}`, gvr("resource.k8s.io", "v1beta2", "resourceclaims"): `{"status": {"allocation": {"nodeSelector": {"nodeSelectorTerms": [{"matchExpressions": [{"key": "some-label", "operator": "In", "values": ["some-value"]}] }]}}}}`, + gvr("resource.k8s.io", "v1", "resourceclaims"): `{"status": {"allocation": {"nodeSelector": {"nodeSelectorTerms": [{"matchExpressions": [{"key": "some-label", "operator": "In", "values": ["some-value"]}] }]}}}}`, 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"}]}}`, // standard for []metav1.Condition gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicies"): `{"status": {"conditions":[{"type":"Accepted","status":"False","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"RuleApplied","message":"Rule was applied"}]}}`, diff --git a/test/integration/etcd/data.go b/test/integration/etcd/data.go index 905fbde89e4..8f020996e41 100644 --- a/test/integration/etcd/data.go +++ b/test/integration/etcd/data.go @@ -651,6 +651,33 @@ func GetEtcdStorageDataForNamespaceServedAt(namespace string, v string, isEmulat }, // -- + // k8s.io/kubernetes/pkg/apis/resource/v1 + gvr("resource.k8s.io", "v1", "deviceclasses"): { + Stub: `{"metadata": {"name": "class4name"}}`, + ExpectedEtcdPath: "/registry/deviceclasses/class4name", + ExpectedGVK: gvkP("resource.k8s.io", "v1beta1", "DeviceClass"), + IntroducedVersion: "1.34", + }, + gvr("resource.k8s.io", "v1", "resourceclaims"): { + Stub: `{"metadata": {"name": "claim4name"}, "spec": {"devices": {"requests": [{"name": "req-0", "exactly": {"deviceClassName": "example-class", "allocationMode": "ExactCount", "count": 1}}]}}}`, + ExpectedEtcdPath: "/registry/resourceclaims/" + namespace + "/claim4name", + ExpectedGVK: gvkP("resource.k8s.io", "v1beta1", "ResourceClaim"), + IntroducedVersion: "1.34", + }, + gvr("resource.k8s.io", "v1", "resourceclaimtemplates"): { + Stub: `{"metadata": {"name": "claimtemplate4name"}, "spec": {"spec": {"devices": {"requests": [{"name": "req-0", "exactly": {"deviceClassName": "example-class", "allocationMode": "ExactCount", "count": 1}}]}}}}`, + ExpectedEtcdPath: "/registry/resourceclaimtemplates/" + namespace + "/claimtemplate4name", + ExpectedGVK: gvkP("resource.k8s.io", "v1beta1", "ResourceClaimTemplate"), + IntroducedVersion: "1.34", + }, + gvr("resource.k8s.io", "v1", "resourceslices"): { + Stub: `{"metadata": {"name": "node4slice"}, "spec": {"nodeName": "worker1", "driver": "dra.example.com", "pool": {"name": "worker1", "resourceSliceCount": 1}}}`, + ExpectedEtcdPath: "/registry/resourceslices/node4slice", + ExpectedGVK: gvkP("resource.k8s.io", "v1beta1", "ResourceSlice"), + IntroducedVersion: "1.34", + }, + // -- + // k8s.io/apiserver/pkg/apis/apiserverinternal/v1alpha1 gvr("internal.apiserver.k8s.io", "v1alpha1", "storageversions"): { Stub: `{"metadata":{"name":"sv1.test"},"spec":{}}`,