Merge pull request #99662 from swetharepakula/eps-ga-api-changes

Graduate EndpointSlice API to GA
This commit is contained in:
Kubernetes Prow Robot
2021-03-06 15:23:54 -08:00
committed by GitHub
57 changed files with 2569 additions and 687 deletions

View File

@@ -19,10 +19,13 @@ package endpointslice
import (
"context"
discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/api/legacyscheme"
@@ -51,6 +54,7 @@ func (endpointSliceStrategy) PrepareForCreate(ctx context.Context, obj runtime.O
endpointSlice.Generation = 1
dropDisabledFieldsOnCreate(endpointSlice)
dropTopologyOnV1(ctx, nil, endpointSlice)
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
@@ -62,8 +66,8 @@ func (endpointSliceStrategy) PrepareForUpdate(ctx context.Context, obj, old runt
// This needs to be changed if a status attribute is added to EndpointSlice
ogNewMeta := newEPS.ObjectMeta
ogOldMeta := oldEPS.ObjectMeta
newEPS.ObjectMeta = v1.ObjectMeta{}
oldEPS.ObjectMeta = v1.ObjectMeta{}
newEPS.ObjectMeta = metav1.ObjectMeta{}
oldEPS.ObjectMeta = metav1.ObjectMeta{}
if !apiequality.Semantic.DeepEqual(newEPS, oldEPS) || !apiequality.Semantic.DeepEqual(ogNewMeta.Labels, ogOldMeta.Labels) {
ogNewMeta.Generation = ogOldMeta.Generation + 1
@@ -73,6 +77,7 @@ func (endpointSliceStrategy) PrepareForUpdate(ctx context.Context, obj, old runt
oldEPS.ObjectMeta = ogOldMeta
dropDisabledFieldsOnUpdate(oldEPS, newEPS)
dropTopologyOnV1(ctx, oldEPS, newEPS)
}
// Validate validates a new EndpointSlice.
@@ -139,3 +144,26 @@ func dropDisabledFieldsOnUpdate(oldEPS, newEPS *discovery.EndpointSlice) {
}
}
}
// dropTopologyOnV1 on V1 request wipes the DeprecatedTopology field and copies
// the NodeName value into DeprecatedTopology
func dropTopologyOnV1(ctx context.Context, oldEPS, newEPS *discovery.EndpointSlice) {
if info, ok := genericapirequest.RequestInfoFrom(ctx); ok {
requestGV := schema.GroupVersion{Group: info.APIGroup, Version: info.APIVersion}
if requestGV == discoveryv1beta1.SchemeGroupVersion {
return
}
// do not drop topology if endpoints have not been changed
if oldEPS != nil && apiequality.Semantic.DeepEqual(oldEPS.Endpoints, newEPS.Endpoints) {
return
}
for i := range newEPS.Endpoints {
ep := &newEPS.Endpoints[i]
//Silently clear out DeprecatedTopology
ep.DeprecatedTopology = nil
}
}
}

View File

@@ -20,8 +20,10 @@ import (
"context"
"testing"
corev1 "k8s.io/api/core/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/pkg/apis/discovery"
@@ -628,3 +630,241 @@ func TestPrepareForUpdate(t *testing.T) {
})
}
}
func Test_dropTopologyOnV1(t *testing.T) {
testcases := []struct {
name string
v1Request bool
newEPS *discovery.EndpointSlice
originalEPS *discovery.EndpointSlice
expectedEPS *discovery.EndpointSlice
}{
{
name: "v1 request, without deprecated topology",
v1Request: true,
newEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{Hostname: utilpointer.StringPtr("hostname-1")},
{Hostname: utilpointer.StringPtr("hostname-1")},
},
},
expectedEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{Hostname: utilpointer.StringPtr("hostname-1")},
{Hostname: utilpointer.StringPtr("hostname-1")},
},
},
},
{
name: "v1beta1 request, without deprecated topology",
newEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{Hostname: utilpointer.StringPtr("hostname-1")},
{Hostname: utilpointer.StringPtr("hostname-1")},
},
},
expectedEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{Hostname: utilpointer.StringPtr("hostname-1")},
{Hostname: utilpointer.StringPtr("hostname-1")},
},
},
},
{
name: "v1 request, with deprecated topology",
v1Request: true,
newEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{DeprecatedTopology: map[string]string{"key": "value"}},
{DeprecatedTopology: map[string]string{"key": "value"}},
},
},
expectedEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{{}, {}},
},
},
{
name: "v1beta1 request, with deprecated topology",
newEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{DeprecatedTopology: map[string]string{"key": "value"}},
{DeprecatedTopology: map[string]string{"key": "value"}},
},
},
expectedEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{DeprecatedTopology: map[string]string{"key": "value"}},
{DeprecatedTopology: map[string]string{"key": "value"}},
},
},
},
{
name: "v1 request, updated metadata",
v1Request: true,
originalEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
NodeName: utilpointer.StringPtr("node-1"),
DeprecatedTopology: map[string]string{"key": "value"},
},
{
NodeName: utilpointer.StringPtr("node-1"),
DeprecatedTopology: map[string]string{"key": "value"},
},
},
},
newEPS: &discovery.EndpointSlice{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"example": "one"},
},
Endpoints: []discovery.Endpoint{
{
NodeName: utilpointer.StringPtr("node-1"),
DeprecatedTopology: map[string]string{"key": "value"},
},
{
NodeName: utilpointer.StringPtr("node-1"),
DeprecatedTopology: map[string]string{"key": "value"},
},
},
},
expectedEPS: &discovery.EndpointSlice{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"example": "one"},
},
Endpoints: []discovery.Endpoint{
{
NodeName: utilpointer.StringPtr("node-1"),
DeprecatedTopology: map[string]string{"key": "value"},
},
{
NodeName: utilpointer.StringPtr("node-1"),
DeprecatedTopology: map[string]string{"key": "value"},
},
},
},
},
{
name: "v1beta1 request, updated metadata",
originalEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
NodeName: utilpointer.StringPtr("node-1"),
DeprecatedTopology: map[string]string{"key": "value"},
},
{
NodeName: utilpointer.StringPtr("node-1"),
DeprecatedTopology: map[string]string{"key": "value"},
},
},
},
newEPS: &discovery.EndpointSlice{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"example": "one"},
},
Endpoints: []discovery.Endpoint{
{
NodeName: utilpointer.StringPtr("node-1"),
DeprecatedTopology: map[string]string{"key": "value"},
},
{
NodeName: utilpointer.StringPtr("node-1"),
DeprecatedTopology: map[string]string{"key": "value"},
},
},
},
expectedEPS: &discovery.EndpointSlice{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"example": "one"},
},
Endpoints: []discovery.Endpoint{
{
NodeName: utilpointer.StringPtr("node-1"),
DeprecatedTopology: map[string]string{"key": "value"},
},
{
NodeName: utilpointer.StringPtr("node-1"),
DeprecatedTopology: map[string]string{"key": "value"},
},
},
},
},
{
name: "v1 request, updated endpoints",
v1Request: true,
originalEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{DeprecatedTopology: map[string]string{"key": "value"}},
{DeprecatedTopology: map[string]string{"key": "value"}},
},
},
newEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Hostname: utilpointer.StringPtr("hostname-1"),
DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
},
{
Hostname: utilpointer.StringPtr("hostname-1"),
DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
},
},
},
expectedEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{Hostname: utilpointer.StringPtr("hostname-1")},
{Hostname: utilpointer.StringPtr("hostname-1")},
},
},
},
{
name: "v1beta1 request, updated endpoints",
originalEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{DeprecatedTopology: map[string]string{"key": "value"}},
{DeprecatedTopology: map[string]string{"key": "value"}},
},
},
newEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Hostname: utilpointer.StringPtr("hostname-1"),
DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
},
{
Hostname: utilpointer.StringPtr("hostname-1"),
DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
},
},
},
expectedEPS: &discovery.EndpointSlice{
Endpoints: []discovery.Endpoint{
{
Hostname: utilpointer.StringPtr("hostname-1"),
DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
},
{
Hostname: utilpointer.StringPtr("hostname-1"),
DeprecatedTopology: map[string]string{corev1.LabelHostname: "node-1"},
},
},
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{APIGroup: "discovery.k8s.io", APIVersion: "v1beta1", Resource: "endpointslices"})
if tc.v1Request {
ctx = genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{APIGroup: "discovery.k8s.io", APIVersion: "v1", Resource: "endpointslices"})
}
dropTopologyOnV1(ctx, tc.originalEPS, tc.newEPS)
if !apiequality.Semantic.DeepEqual(tc.newEPS, tc.expectedEPS) {
t.Logf("actual endpointslice: %v", tc.newEPS)
t.Logf("expected endpointslice: %v", tc.expectedEPS)
t.Errorf("unexpected EndpointSlice on API topology strategy")
}
})
}
}

View File

@@ -17,7 +17,7 @@ limitations under the License.
package rest
import (
discoveryv1alpha1 "k8s.io/api/discovery/v1alpha1"
discoveryv1 "k8s.io/api/discovery/v1"
discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
@@ -37,14 +37,6 @@ func (p StorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.AP
// 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 apiResourceConfigSource.VersionEnabled(discoveryv1alpha1.SchemeGroupVersion) {
storageMap, err := p.v1alpha1Storage(apiResourceConfigSource, restOptionsGetter)
if err != nil {
return genericapiserver.APIGroupInfo{}, false, err
}
apiGroupInfo.VersionedResourcesStorageMap[discoveryv1alpha1.SchemeGroupVersion.Version] = storageMap
}
if apiResourceConfigSource.VersionEnabled(discoveryv1beta1.SchemeGroupVersion) {
storageMap, err := p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
if err != nil {
@@ -53,10 +45,18 @@ func (p StorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.AP
apiGroupInfo.VersionedResourcesStorageMap[discoveryv1beta1.SchemeGroupVersion.Version] = storageMap
}
if apiResourceConfigSource.VersionEnabled(discoveryv1.SchemeGroupVersion) {
storageMap, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter)
if err != nil {
return genericapiserver.APIGroupInfo{}, false, err
}
apiGroupInfo.VersionedResourcesStorageMap[discoveryv1.SchemeGroupVersion.Version] = storageMap
}
return apiGroupInfo, true, nil
}
func (p StorageProvider) v1alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
func (p StorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{}
endpointSliceStorage, err := endpointslicestorage.NewREST(restOptionsGetter)
@@ -68,7 +68,7 @@ func (p StorageProvider) v1alpha1Storage(apiResourceConfigSource serverstorage.A
return storage, err
}
func (p StorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
func (p StorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{}
endpointSliceStorage, err := endpointslicestorage.NewREST(restOptionsGetter)