mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
finish removal of exportoptions
This commit is contained in:
parent
82ebcd1719
commit
37cc89ed8d
@ -167,7 +167,7 @@ var nonRoundTrippableTypes = sets.NewString(
|
||||
"PatchOptions",
|
||||
)
|
||||
|
||||
var commonKinds = []string{"Status", "ListOptions", "DeleteOptions", "ExportOptions", "GetOptions", "CreateOptions", "UpdateOptions", "PatchOptions"}
|
||||
var commonKinds = []string{"Status", "ListOptions", "DeleteOptions", "GetOptions", "CreateOptions", "UpdateOptions", "PatchOptions"}
|
||||
|
||||
// TestCommonKindsRegistered verifies that all group/versions registered with
|
||||
// the legacyscheme package have the common kinds.
|
||||
|
@ -64,7 +64,6 @@ var typesAllowedTags = map[reflect.Type]bool{
|
||||
reflect.TypeOf(metav1.OwnerReference{}): true,
|
||||
reflect.TypeOf(metav1.LabelSelector{}): true,
|
||||
reflect.TypeOf(metav1.GetOptions{}): true,
|
||||
reflect.TypeOf(metav1.ExportOptions{}): true,
|
||||
reflect.TypeOf(metav1.ListOptions{}): true,
|
||||
reflect.TypeOf(metav1.DeleteOptions{}): true,
|
||||
reflect.TypeOf(metav1.GroupVersionKind{}): true,
|
||||
|
@ -160,18 +160,7 @@ func TestLegacyRestStorageStrategies(t *testing.T) {
|
||||
t.Errorf("failed to create legacy REST storage: %v", err)
|
||||
}
|
||||
|
||||
// Any new stores with export logic will need to be added here:
|
||||
exceptions := registrytest.StrategyExceptions{
|
||||
// Only these stores should have an export strategy defined:
|
||||
HasExportStrategy: []string{
|
||||
"secrets",
|
||||
"limitRanges",
|
||||
"nodes",
|
||||
"podTemplates",
|
||||
},
|
||||
}
|
||||
|
||||
strategyErrors := registrytest.ValidateStorageStrategies(apiGroupInfo.VersionedResourcesStorageMap["v1"], exceptions)
|
||||
strategyErrors := registrytest.ValidateStorageStrategies(apiGroupInfo.VersionedResourcesStorageMap["v1"])
|
||||
for _, err := range strategyErrors {
|
||||
t.Error(err)
|
||||
}
|
||||
@ -187,14 +176,8 @@ func TestCertificatesRestStorageStrategies(t *testing.T) {
|
||||
t.Fatalf("unexpected error from REST storage: %v", err)
|
||||
}
|
||||
|
||||
exceptions := registrytest.StrategyExceptions{
|
||||
HasExportStrategy: []string{
|
||||
"certificatesigningrequests",
|
||||
},
|
||||
}
|
||||
|
||||
strategyErrors := registrytest.ValidateStorageStrategies(
|
||||
apiGroupInfo.VersionedResourcesStorageMap[certificatesapiv1beta1.SchemeGroupVersion.Version], exceptions)
|
||||
apiGroupInfo.VersionedResourcesStorageMap[certificatesapiv1beta1.SchemeGroupVersion.Version])
|
||||
for _, err := range strategyErrors {
|
||||
t.Error(err)
|
||||
}
|
||||
|
@ -46,7 +46,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, *Approva
|
||||
CreateStrategy: csrregistry.Strategy,
|
||||
UpdateStrategy: csrregistry.Strategy,
|
||||
DeleteStrategy: csrregistry.Strategy,
|
||||
ExportStrategy: csrregistry.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
|
@ -118,21 +118,6 @@ func (csrStrategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s csrStrategy) Export(ctx context.Context, obj runtime.Object, exact bool) error {
|
||||
csr, ok := obj.(*certificates.CertificateSigningRequest)
|
||||
if !ok {
|
||||
// unexpected programmer error
|
||||
return fmt.Errorf("unexpected object: %v", obj)
|
||||
}
|
||||
s.PrepareForCreate(ctx, obj)
|
||||
if exact {
|
||||
return nil
|
||||
}
|
||||
// CSRs allow direct subresource edits, we clear them without exact so the CSR value can be reused.
|
||||
csr.Status = certificates.CertificateSigningRequestStatus{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Storage strategy for the Status subresource
|
||||
type csrStatusStrategy struct {
|
||||
csrStrategy
|
||||
|
@ -40,7 +40,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) {
|
||||
CreateStrategy: limitrange.Strategy,
|
||||
UpdateStrategy: limitrange.Strategy,
|
||||
DeleteStrategy: limitrange.Strategy,
|
||||
ExportStrategy: limitrange.Strategy,
|
||||
|
||||
// TODO: define table converter that exposes more than name/creation timestamp
|
||||
TableConvertor: rest.NewDefaultTableConvertor(api.Resource("limitranges")),
|
||||
|
@ -72,10 +72,3 @@ func (limitrangeStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.O
|
||||
func (limitrangeStrategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (limitrangeStrategy) Export(context.Context, runtime.Object, bool) error {
|
||||
// Copied from OpenShift exporter
|
||||
// TODO: this needs to be fixed
|
||||
// limitrange.Strategy.PrepareForCreate(ctx, obj)
|
||||
return nil
|
||||
}
|
||||
|
@ -119,10 +119,6 @@ func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptio
|
||||
return r.store.Watch(ctx, options)
|
||||
}
|
||||
|
||||
func (r *REST) Export(ctx context.Context, name string, opts metav1.ExportOptions) (runtime.Object, error) {
|
||||
return r.store.Export(ctx, name, opts)
|
||||
}
|
||||
|
||||
// Delete enforces life-cycle rules for namespace termination
|
||||
func (r *REST) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||
nsObj, err := r.Get(ctx, name, &metav1.GetOptions{})
|
||||
|
@ -88,7 +88,6 @@ func NewStorage(optsGetter generic.RESTOptionsGetter, kubeletClientConfig client
|
||||
CreateStrategy: node.Strategy,
|
||||
UpdateStrategy: node.Strategy,
|
||||
DeleteStrategy: node.Strategy,
|
||||
ExportStrategy: node.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
|
@ -141,22 +141,6 @@ func (nodeStrategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (ns nodeStrategy) Export(ctx context.Context, obj runtime.Object, exact bool) error {
|
||||
n, ok := obj.(*api.Node)
|
||||
if !ok {
|
||||
// unexpected programmer error
|
||||
return fmt.Errorf("unexpected object: %v", obj)
|
||||
}
|
||||
ns.PrepareForCreate(ctx, obj)
|
||||
if exact {
|
||||
return nil
|
||||
}
|
||||
// Nodes are the only resources that allow direct status edits, therefore
|
||||
// we clear that without exact so that the node value can be reused.
|
||||
n.Status = api.NodeStatus{}
|
||||
return nil
|
||||
}
|
||||
|
||||
type nodeStatusStrategy struct {
|
||||
nodeStrategy
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) {
|
||||
CreateStrategy: podtemplate.Strategy,
|
||||
UpdateStrategy: podtemplate.Strategy,
|
||||
DeleteStrategy: podtemplate.Strategy,
|
||||
ExportStrategy: podtemplate.Strategy,
|
||||
|
||||
ReturnDeletedObject: true,
|
||||
|
||||
|
@ -87,8 +87,3 @@ func (podTemplateStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.
|
||||
func (podTemplateStrategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (podTemplateStrategy) Export(ctx context.Context, obj runtime.Object, exact bool) error {
|
||||
// Do nothing
|
||||
return nil
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ go_library(
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/core/validation:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
@ -37,9 +36,6 @@ go_test(
|
||||
"//pkg/api/testing:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/core/install:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -44,7 +44,6 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) {
|
||||
CreateStrategy: secret.Strategy,
|
||||
UpdateStrategy: secret.Strategy,
|
||||
DeleteStrategy: secret.Strategy,
|
||||
ExportStrategy: secret.Strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@ -85,26 +84,6 @@ func (strategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s strategy) Export(ctx context.Context, obj runtime.Object, exact bool) error {
|
||||
t, ok := obj.(*api.Secret)
|
||||
if !ok {
|
||||
// unexpected programmer error
|
||||
return fmt.Errorf("unexpected object: %v", obj)
|
||||
}
|
||||
s.PrepareForCreate(ctx, obj)
|
||||
if exact {
|
||||
return nil
|
||||
}
|
||||
// secrets that are tied to the UID of a service account cannot be exported anyway
|
||||
if t.Type == api.SecretTypeServiceAccountToken || len(t.Annotations[api.ServiceAccountUIDKey]) > 0 {
|
||||
errs := []*field.Error{
|
||||
field.Invalid(field.NewPath("type"), t, "can not export service account secrets"),
|
||||
}
|
||||
return errors.NewInvalid(api.Kind("Secret"), t.Name, errs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAttrs returns labels and fields of a given object for filtering purposes.
|
||||
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
|
||||
secret, ok := obj.(*api.Secret)
|
||||
|
@ -17,12 +17,8 @@ limitations under the License.
|
||||
package secret
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
apitesting "k8s.io/kubernetes/pkg/api/testing"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
|
||||
@ -30,80 +26,6 @@ import (
|
||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||
)
|
||||
|
||||
func TestExportSecret(t *testing.T) {
|
||||
tests := []struct {
|
||||
objIn runtime.Object
|
||||
objOut runtime.Object
|
||||
exact bool
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
objIn: &api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "bar",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"foo": []byte("bar"),
|
||||
},
|
||||
},
|
||||
objOut: &api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "bar",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"foo": []byte("bar"),
|
||||
},
|
||||
},
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
objIn: &api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "bar",
|
||||
},
|
||||
Type: api.SecretTypeServiceAccountToken,
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
objIn: &api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "bar",
|
||||
Annotations: map[string]string{
|
||||
api.ServiceAccountUIDKey: "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
objIn: &api.Pod{},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := Strategy.Export(genericapirequest.NewContext(), test.objIn, test.exact)
|
||||
if err != nil {
|
||||
if !test.expectErr {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if test.expectErr {
|
||||
t.Error("unexpected non-error")
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(test.objIn, test.objOut) {
|
||||
t.Errorf("expected:\n%v\nsaw:\n%v\n", test.objOut, test.objIn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelectableFieldLabelConversions(t *testing.T) {
|
||||
apitesting.TestSelectableFieldLabelConversionsOfKind(t,
|
||||
"v1",
|
||||
|
@ -79,7 +79,6 @@ type ServiceStorage interface {
|
||||
rest.CreaterUpdater
|
||||
rest.GracefulDeleter
|
||||
rest.Watcher
|
||||
rest.Exporter
|
||||
rest.StorageVersionProvider
|
||||
}
|
||||
|
||||
@ -189,10 +188,6 @@ func (rs *REST) Watch(ctx context.Context, options *metainternalversion.ListOpti
|
||||
return rs.services.Watch(ctx, options)
|
||||
}
|
||||
|
||||
func (rs *REST) Export(ctx context.Context, name string, opts metav1.ExportOptions) (runtime.Object, error) {
|
||||
return rs.services.Export(ctx, name, opts)
|
||||
}
|
||||
|
||||
func (rs *REST) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
|
||||
service := obj.(*api.Service)
|
||||
|
||||
|
@ -163,10 +163,6 @@ func (s *serviceStorage) ConvertToTable(ctx context.Context, object runtime.Obje
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (s *serviceStorage) Export(ctx context.Context, name string, opts metav1.ExportOptions) (runtime.Object, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (s *serviceStorage) StorageVersion() runtime.GroupVersioner {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
@ -57,7 +57,6 @@ func NewGenericREST(optsGetter generic.RESTOptionsGetter, serviceCIDR net.IPNet,
|
||||
CreateStrategy: strategy,
|
||||
UpdateStrategy: strategy,
|
||||
DeleteStrategy: strategy,
|
||||
ExportStrategy: strategy,
|
||||
|
||||
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
|
||||
@ -37,7 +36,6 @@ import (
|
||||
|
||||
type Strategy interface {
|
||||
rest.RESTCreateUpdateStrategy
|
||||
rest.RESTExportStrategy
|
||||
}
|
||||
|
||||
// svcStrategy implements behavior for Services
|
||||
@ -139,30 +137,6 @@ func (svcStrategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (svcStrategy) Export(ctx context.Context, obj runtime.Object, exact bool) error {
|
||||
t, ok := obj.(*api.Service)
|
||||
if !ok {
|
||||
// unexpected programmer error
|
||||
return fmt.Errorf("unexpected object: %v", obj)
|
||||
}
|
||||
// TODO: service does not yet have a prepare create strategy (see above)
|
||||
t.Status = api.ServiceStatus{}
|
||||
if exact {
|
||||
return nil
|
||||
}
|
||||
//set ClusterIPs as nil - if ClusterIPs[0] != None
|
||||
if len(t.Spec.ClusterIPs) > 0 && t.Spec.ClusterIPs[0] != api.ClusterIPNone {
|
||||
t.Spec.ClusterIP = ""
|
||||
t.Spec.ClusterIPs = nil
|
||||
}
|
||||
if t.Spec.Type == api.ServiceTypeNodePort {
|
||||
for i := range t.Spec.Ports {
|
||||
t.Spec.Ports[i].NodePort = 0
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// dropServiceDisabledFields drops fields that are not used if their associated feature gates
|
||||
// are not enabled. The typical pattern is:
|
||||
// if !utilfeature.DefaultFeatureGate.Enabled(features.MyFeature) && !myFeatureInUse(oldSvc) {
|
||||
|
@ -47,115 +47,6 @@ func newStrategy(cidr string, hasSecondary bool) (testStrategy Strategy, testSta
|
||||
return
|
||||
}
|
||||
|
||||
func TestExportService(t *testing.T) {
|
||||
testStrategy, _ := newStrategy("10.0.0.0/16", false)
|
||||
tests := []struct {
|
||||
objIn runtime.Object
|
||||
objOut runtime.Object
|
||||
exact bool
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
objIn: &api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "bar",
|
||||
},
|
||||
Status: api.ServiceStatus{
|
||||
LoadBalancer: api.LoadBalancerStatus{
|
||||
Ingress: []api.LoadBalancerIngress{
|
||||
{IP: "1.2.3.4"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
objOut: &api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "bar",
|
||||
},
|
||||
},
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
objIn: &api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "bar",
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
ClusterIPs: []string{"10.0.0.1"},
|
||||
},
|
||||
Status: api.ServiceStatus{
|
||||
LoadBalancer: api.LoadBalancerStatus{
|
||||
Ingress: []api.LoadBalancerIngress{
|
||||
{IP: "1.2.3.4"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
objOut: &api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "bar",
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
ClusterIPs: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
objIn: &api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "bar",
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
ClusterIPs: []string{"10.0.0.1", "2001::1"},
|
||||
},
|
||||
Status: api.ServiceStatus{
|
||||
LoadBalancer: api.LoadBalancerStatus{
|
||||
Ingress: []api.LoadBalancerIngress{
|
||||
{IP: "1.2.3.4"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
objOut: &api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "bar",
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
ClusterIPs: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
objIn: &api.Pod{},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := testStrategy.Export(genericapirequest.NewContext(), test.objIn, test.exact)
|
||||
if err != nil {
|
||||
if !test.expectErr {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if test.expectErr {
|
||||
t.Error("unexpected non-error")
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(test.objIn, test.objOut) {
|
||||
t.Errorf("expected:\n%v\nsaw:\n%v\n", test.objOut, test.objIn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckGeneratedNameError(t *testing.T) {
|
||||
testStrategy, _ := newStrategy("10.0.0.0/16", false)
|
||||
expect := errors.NewNotFound(api.Resource("foos"), "bar")
|
||||
|
@ -21,7 +21,6 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/kubeapiserver:go_default_library",
|
||||
"//pkg/util/slice:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
|
@ -117,10 +117,3 @@ func (r *ServiceRegistry) WatchServices(ctx context.Context, options *metaintern
|
||||
|
||||
return nil, r.Err
|
||||
}
|
||||
|
||||
func (r *ServiceRegistry) ExportService(ctx context.Context, name string, options metav1.ExportOptions) (*api.Service, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
return r.Service, r.Err
|
||||
}
|
||||
|
@ -21,17 +21,13 @@ import (
|
||||
|
||||
"k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/kubernetes/pkg/util/slice"
|
||||
)
|
||||
|
||||
// ValidateStorageStrategies ensures any instances of the generic registry.Store in the given storage map
|
||||
// have expected strategies defined.
|
||||
func ValidateStorageStrategies(storageMap map[string]rest.Storage, exceptions StrategyExceptions) []error {
|
||||
func ValidateStorageStrategies(storageMap map[string]rest.Storage) []error {
|
||||
errs := []error{}
|
||||
|
||||
// Used to ensure we saw all the expected exceptions:
|
||||
hasExportExceptionsSeen := []string{}
|
||||
|
||||
for k, storage := range storageMap {
|
||||
switch t := storage.(type) {
|
||||
case registry.GenericStore:
|
||||
@ -46,27 +42,6 @@ func ValidateStorageStrategies(storageMap map[string]rest.Storage, exceptions St
|
||||
if t.GetDeleteStrategy() == nil {
|
||||
errs = append(errs, fmt.Errorf("store for type [%v] does not have a DeleteStrategy", k))
|
||||
}
|
||||
|
||||
// Check that ExportStrategy is set if applicable:
|
||||
if slice.ContainsString(exceptions.HasExportStrategy, k, nil) {
|
||||
hasExportExceptionsSeen = append(hasExportExceptionsSeen, k)
|
||||
if t.GetExportStrategy() == nil {
|
||||
errs = append(errs, fmt.Errorf("store for type [%v] does not have an ExportStrategy", k))
|
||||
}
|
||||
} else {
|
||||
// By default we expect Stores to not have additional export logic:
|
||||
if t.GetExportStrategy() != nil {
|
||||
errs = append(errs, fmt.Errorf("store for type [%v] has an unexpected ExportStrategy", k))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that we saw all our expected exceptions:
|
||||
for _, expKey := range exceptions.HasExportStrategy {
|
||||
if !slice.ContainsString(hasExportExceptionsSeen, expKey, nil) {
|
||||
errs = append(errs, fmt.Errorf("no generic store seen for expected ExportStrategy: %v", expKey))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -388,7 +388,7 @@ func (r *crdHandler) serveResource(w http.ResponseWriter, req *http.Request, req
|
||||
|
||||
switch requestInfo.Verb {
|
||||
case "get":
|
||||
return handlers.GetResource(storage, storage, requestScope)
|
||||
return handlers.GetResource(storage, requestScope)
|
||||
case "list":
|
||||
forceWatch := false
|
||||
return handlers.ListResource(storage, storage, requestScope, forceWatch, r.minRequestTimeout)
|
||||
@ -428,7 +428,7 @@ func (r *crdHandler) serveStatus(w http.ResponseWriter, req *http.Request, reque
|
||||
|
||||
switch requestInfo.Verb {
|
||||
case "get":
|
||||
return handlers.GetResource(storage, nil, requestScope)
|
||||
return handlers.GetResource(storage, requestScope)
|
||||
case "update":
|
||||
return handlers.UpdateResource(storage, requestScope, r.admission)
|
||||
case "patch":
|
||||
@ -448,7 +448,7 @@ func (r *crdHandler) serveScale(w http.ResponseWriter, req *http.Request, reques
|
||||
|
||||
switch requestInfo.Verb {
|
||||
case "get":
|
||||
return handlers.GetResource(storage, nil, requestScope)
|
||||
return handlers.GetResource(storage, requestScope)
|
||||
case "update":
|
||||
return handlers.UpdateResource(storage, requestScope, r.admission)
|
||||
case "patch":
|
||||
@ -698,7 +698,6 @@ func (r *crdHandler) getOrCreateServingInfoFor(uid types.UID, name string) (*crd
|
||||
parameterScheme := runtime.NewScheme()
|
||||
parameterScheme.AddUnversionedTypes(schema.GroupVersion{Group: crd.Spec.Group, Version: v.Name},
|
||||
&metav1.ListOptions{},
|
||||
&metav1.ExportOptions{},
|
||||
&metav1.GetOptions{},
|
||||
&metav1.DeleteOptions{},
|
||||
)
|
||||
|
@ -55,7 +55,6 @@ func newStorage(t *testing.T) (customresource.CustomResourceStorage, *etcd3testi
|
||||
parameterScheme := runtime.NewScheme()
|
||||
parameterScheme.AddUnversionedTypes(schema.GroupVersion{Group: "mygroup.example.com", Version: "v1beta1"},
|
||||
&metav1.ListOptions{},
|
||||
&metav1.ExportOptions{},
|
||||
&metav1.GetOptions{},
|
||||
&metav1.DeleteOptions{},
|
||||
)
|
||||
|
@ -52,7 +52,6 @@ func addToGroupVersion(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&ListOptions{},
|
||||
&metav1.GetOptions{},
|
||||
&metav1.ExportOptions{},
|
||||
&metav1.DeleteOptions{},
|
||||
&metav1.CreateOptions{},
|
||||
&metav1.UpdateOptions{},
|
||||
|
@ -55,7 +55,6 @@ var ParameterCodec = runtime.NewParameterCodec(scheme)
|
||||
|
||||
var optionsTypes = []runtime.Object{
|
||||
&ListOptions{},
|
||||
&ExportOptions{},
|
||||
&GetOptions{},
|
||||
&DeleteOptions{},
|
||||
&CreateOptions{},
|
||||
|
@ -142,10 +142,10 @@ func init() {
|
||||
|
||||
func addGrouplessTypes() {
|
||||
scheme.AddKnownTypes(grouplessGroupVersion,
|
||||
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ListOptions{}, &metav1.ExportOptions{},
|
||||
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ListOptions{},
|
||||
&metav1.DeleteOptions{}, &genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{})
|
||||
scheme.AddKnownTypes(grouplessInternalGroupVersion,
|
||||
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ExportOptions{},
|
||||
&genericapitesting.Simple{}, &genericapitesting.SimpleList{},
|
||||
&genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{})
|
||||
|
||||
utilruntime.Must(genericapitesting.RegisterConversions(scheme))
|
||||
@ -153,20 +153,20 @@ func addGrouplessTypes() {
|
||||
|
||||
func addTestTypes() {
|
||||
scheme.AddKnownTypes(testGroupVersion,
|
||||
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ExportOptions{},
|
||||
&genericapitesting.Simple{}, &genericapitesting.SimpleList{},
|
||||
&metav1.DeleteOptions{}, &genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{},
|
||||
&genericapitesting.SimpleXGSubresource{})
|
||||
scheme.AddKnownTypes(testGroupVersion, &examplev1.Pod{})
|
||||
scheme.AddKnownTypes(testInternalGroupVersion,
|
||||
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ExportOptions{},
|
||||
&genericapitesting.Simple{}, &genericapitesting.SimpleList{},
|
||||
&genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{},
|
||||
&genericapitesting.SimpleXGSubresource{})
|
||||
scheme.AddKnownTypes(testInternalGroupVersion, &example.Pod{})
|
||||
// Register SimpleXGSubresource in both testGroupVersion and testGroup2Version, and also their
|
||||
// their corresponding internal versions, to verify that the desired group version object is
|
||||
// served in the tests.
|
||||
scheme.AddKnownTypes(testGroup2Version, &genericapitesting.SimpleXGSubresource{}, &metav1.ExportOptions{})
|
||||
scheme.AddKnownTypes(testInternalGroup2Version, &genericapitesting.SimpleXGSubresource{}, &metav1.ExportOptions{})
|
||||
scheme.AddKnownTypes(testGroup2Version, &genericapitesting.SimpleXGSubresource{})
|
||||
scheme.AddKnownTypes(testInternalGroup2Version, &genericapitesting.SimpleXGSubresource{})
|
||||
metav1.AddToGroupVersion(scheme, testGroupVersion)
|
||||
|
||||
utilruntime.Must(genericapitesting.RegisterConversions(scheme))
|
||||
@ -174,7 +174,7 @@ func addTestTypes() {
|
||||
|
||||
func addNewTestTypes() {
|
||||
scheme.AddKnownTypes(newGroupVersion,
|
||||
&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ExportOptions{},
|
||||
&genericapitesting.Simple{}, &genericapitesting.SimpleList{},
|
||||
&metav1.DeleteOptions{}, &genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{},
|
||||
&examplev1.Pod{},
|
||||
)
|
||||
@ -368,21 +368,6 @@ func (storage *SimpleRESTStorage) NamespaceScoped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (storage *SimpleRESTStorage) Export(ctx context.Context, name string, opts metav1.ExportOptions) (runtime.Object, error) {
|
||||
obj, err := storage.Get(ctx, name, &metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s, ok := obj.(*genericapitesting.Simple)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected object")
|
||||
}
|
||||
|
||||
// Set a marker to verify the method was called
|
||||
s.Other = "exported"
|
||||
return obj, storage.errors["export"]
|
||||
}
|
||||
|
||||
func (storage *SimpleRESTStorage) ConvertToTable(ctx context.Context, obj runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
|
||||
return rest.NewDefaultTableConvertor(schema.GroupResource{Resource: "simple"}).ConvertToTable(ctx, obj, tableOptions)
|
||||
}
|
||||
@ -1556,55 +1541,6 @@ func TestMetadata(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExport(t *testing.T) {
|
||||
storage := map[string]rest.Storage{}
|
||||
simpleStorage := SimpleRESTStorage{
|
||||
item: genericapitesting.Simple{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: "1234",
|
||||
CreationTimestamp: metav1.NewTime(time.Unix(10, 10)),
|
||||
},
|
||||
Other: "foo",
|
||||
},
|
||||
}
|
||||
selfLinker := &setTestSelfLinker{
|
||||
t: t,
|
||||
expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id",
|
||||
name: "id",
|
||||
namespace: "default",
|
||||
}
|
||||
storage["simple"] = &simpleStorage
|
||||
handler := handleLinker(storage, selfLinker)
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
|
||||
resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id?export=true")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
data, _ := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
t.Fatalf("unexpected response: %#v\n%s\n", resp, string(data))
|
||||
}
|
||||
var itemOut genericapitesting.Simple
|
||||
body, err := extractBody(resp, &itemOut)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if itemOut.Name != simpleStorage.item.Name {
|
||||
t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simpleStorage.item, string(body))
|
||||
}
|
||||
if itemOut.Other != "exported" {
|
||||
t.Errorf("Expected: exported, saw: %s", itemOut.Other)
|
||||
}
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.RemoveSelfLink) == selfLinker.called {
|
||||
t.Errorf("unexpected selfLinker.called: %v", selfLinker.called)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
storage := map[string]rest.Storage{}
|
||||
simpleStorage := SimpleRESTStorage{
|
||||
|
@ -19,14 +19,15 @@ package handlers
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
metainternalversionvalidation "k8s.io/apimachinery/pkg/apis/meta/internalversion/validation"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
metainternalversionvalidation "k8s.io/apimachinery/pkg/apis/meta/internalversion/validation"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
@ -81,22 +82,22 @@ func getResourceHandler(scope *RequestScope, getter getterFunc) http.HandlerFunc
|
||||
}
|
||||
|
||||
// GetResource returns a function that handles retrieving a single resource from a rest.Storage object.
|
||||
func GetResource(r rest.Getter, e rest.Exporter, scope *RequestScope) http.HandlerFunc {
|
||||
func GetResource(r rest.Getter, scope *RequestScope) http.HandlerFunc {
|
||||
return getResourceHandler(scope,
|
||||
func(ctx context.Context, name string, req *http.Request, trace *utiltrace.Trace) (runtime.Object, error) {
|
||||
// check for export
|
||||
options := metav1.GetOptions{}
|
||||
if values := req.URL.Query(); len(values) > 0 {
|
||||
exports := metav1.ExportOptions{}
|
||||
if err := metainternalversionscheme.ParameterCodec.DecodeParameters(values, scope.MetaGroupVersion, &exports); err != nil {
|
||||
err = errors.NewBadRequest(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
if exports.Export {
|
||||
if e == nil {
|
||||
return nil, errors.NewBadRequest(fmt.Sprintf("export of %q is not supported", scope.Resource.Resource))
|
||||
if len(values["export"]) > 0 {
|
||||
exportBool := true
|
||||
exportStrings := values["export"]
|
||||
err := runtime.Convert_Slice_string_To_bool(&exportStrings, &exportBool, nil)
|
||||
if err != nil {
|
||||
return nil, errors.NewBadRequest(fmt.Sprintf("the export parameter cannot be parsed: %v", err))
|
||||
}
|
||||
if exportBool {
|
||||
return nil, errors.NewBadRequest("the export parameter, deprecated since v1.14, is no longer supported")
|
||||
}
|
||||
return e.Export(ctx, name, exports)
|
||||
}
|
||||
if err := metainternalversionscheme.ParameterCodec.DecodeParameters(values, scope.MetaGroupVersion, &options); err != nil {
|
||||
err = errors.NewBadRequest(err.Error())
|
||||
|
@ -253,15 +253,6 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
if !isMetadata {
|
||||
storageMeta = defaultStorageMetadata{}
|
||||
}
|
||||
exporter, isExporter := storage.(rest.Exporter)
|
||||
if !isExporter {
|
||||
exporter = nil
|
||||
}
|
||||
|
||||
versionedExportOptions, err := a.group.Creater.New(optionsExternalVersion.WithKind("ExportOptions"))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if isNamedCreater {
|
||||
isCreater = true
|
||||
@ -684,7 +675,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
if isGetterWithOptions {
|
||||
handler = restfulGetResourceWithOptions(getterWithOptions, reqScope, isSubresource)
|
||||
} else {
|
||||
handler = restfulGetResource(getter, exporter, reqScope)
|
||||
handler = restfulGetResource(getter, reqScope)
|
||||
}
|
||||
|
||||
if needOverride {
|
||||
@ -713,11 +704,6 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
if isExporter {
|
||||
if err := AddObjectParams(ws, route, versionedExportOptions); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
addParams(route, action.Params)
|
||||
routes = append(routes, route)
|
||||
case "LIST": // List all resources of a kind.
|
||||
@ -1227,9 +1213,9 @@ func restfulPatchResource(r rest.Patcher, scope handlers.RequestScope, admit adm
|
||||
}
|
||||
}
|
||||
|
||||
func restfulGetResource(r rest.Getter, e rest.Exporter, scope handlers.RequestScope) restful.RouteFunction {
|
||||
func restfulGetResource(r rest.Getter, scope handlers.RequestScope) restful.RouteFunction {
|
||||
return func(req *restful.Request, res *restful.Response) {
|
||||
handlers.GetResource(r, e, &scope)(res.ResponseWriter, req.Request)
|
||||
handlers.GetResource(r, &scope)(res.ResponseWriter, req.Request)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,6 @@ package registry
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@ -73,7 +72,6 @@ type GenericStore interface {
|
||||
GetCreateStrategy() rest.RESTCreateStrategy
|
||||
GetUpdateStrategy() rest.RESTUpdateStrategy
|
||||
GetDeleteStrategy() rest.RESTDeleteStrategy
|
||||
GetExportStrategy() rest.RESTExportStrategy
|
||||
}
|
||||
|
||||
// Store implements k8s.io/apiserver/pkg/registry/rest.StandardStorage. It's
|
||||
@ -199,10 +197,6 @@ type Store struct {
|
||||
// deletionTimestamp, and deletionGracePeriodSeconds checks.
|
||||
ShouldDeleteDuringUpdate func(ctx context.Context, key string, obj, existing runtime.Object) bool
|
||||
|
||||
// ExportStrategy implements resource-specific behavior during export,
|
||||
// optional. Exported objects are not decorated.
|
||||
ExportStrategy rest.RESTExportStrategy
|
||||
|
||||
// TableConvertor is an optional interface for transforming items or lists
|
||||
// of items into tabular output. If unset, the default will be used.
|
||||
TableConvertor rest.TableConvertor
|
||||
@ -223,7 +217,6 @@ type Store struct {
|
||||
|
||||
// Note: the rest.StandardStorage interface aggregates the common REST verbs
|
||||
var _ rest.StandardStorage = &Store{}
|
||||
var _ rest.Exporter = &Store{}
|
||||
var _ rest.TableConvertor = &Store{}
|
||||
var _ GenericStore = &Store{}
|
||||
|
||||
@ -312,11 +305,6 @@ func (e *Store) GetDeleteStrategy() rest.RESTDeleteStrategy {
|
||||
return e.DeleteStrategy
|
||||
}
|
||||
|
||||
// GetExportStrategy implements GenericStore.
|
||||
func (e *Store) GetExportStrategy() rest.RESTExportStrategy {
|
||||
return e.ExportStrategy
|
||||
}
|
||||
|
||||
// List returns a list of items matching labels and field according to the
|
||||
// store's PredicateFunc.
|
||||
func (e *Store) List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error) {
|
||||
@ -1267,44 +1255,6 @@ func (e *Store) calculateTTL(obj runtime.Object, defaultTTL int64, update bool)
|
||||
return ttl, err
|
||||
}
|
||||
|
||||
// exportObjectMeta unsets the fields on the given object that should not be
|
||||
// present when the object is exported.
|
||||
func exportObjectMeta(accessor metav1.Object, exact bool) {
|
||||
accessor.SetUID("")
|
||||
if !exact {
|
||||
accessor.SetNamespace("")
|
||||
}
|
||||
accessor.SetCreationTimestamp(metav1.Time{})
|
||||
accessor.SetDeletionTimestamp(nil)
|
||||
accessor.SetResourceVersion("")
|
||||
accessor.SetSelfLink("")
|
||||
if len(accessor.GetGenerateName()) > 0 && !exact {
|
||||
accessor.SetName("")
|
||||
}
|
||||
}
|
||||
|
||||
// Export implements the rest.Exporter interface
|
||||
func (e *Store) Export(ctx context.Context, name string, opts metav1.ExportOptions) (runtime.Object, error) {
|
||||
obj, err := e.Get(ctx, name, &metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if accessor, err := meta.Accessor(obj); err == nil {
|
||||
exportObjectMeta(accessor, opts.Exact)
|
||||
} else {
|
||||
klog.V(4).Infof("Object of type %v does not have ObjectMeta: %v", reflect.TypeOf(obj), err)
|
||||
}
|
||||
|
||||
if e.ExportStrategy != nil {
|
||||
if err = e.ExportStrategy.Export(ctx, obj, opts.Exact); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
e.CreateStrategy.PrepareForCreate(ctx, obj)
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// CompleteWithOptions updates the store with the provided options and
|
||||
// defaults common fields.
|
||||
func (e *Store) CompleteWithOptions(options *generic.StoreOptions) error {
|
||||
|
@ -1162,104 +1162,6 @@ func TestStoreUpdateHooksInnerRetry(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add a test to check no-op update if we have object with ResourceVersion
|
||||
// already stored in etcd. Currently there is no easy way to store object with
|
||||
// ResourceVersion in etcd.
|
||||
|
||||
type testPodExport struct{}
|
||||
|
||||
func (t testPodExport) Export(ctx context.Context, obj runtime.Object, exact bool) error {
|
||||
pod := obj.(*example.Pod)
|
||||
if pod.Labels == nil {
|
||||
pod.Labels = map[string]string{}
|
||||
}
|
||||
pod.Labels["exported"] = "true"
|
||||
pod.Labels["exact"] = strconv.FormatBool(exact)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestStoreCustomExport(t *testing.T) {
|
||||
podA := example.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test",
|
||||
Name: "foo",
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
Spec: example.PodSpec{NodeName: "machine"},
|
||||
}
|
||||
|
||||
destroyFunc, registry := NewTestGenericStoreRegistry(t)
|
||||
defer destroyFunc()
|
||||
|
||||
registry.ExportStrategy = testPodExport{}
|
||||
|
||||
testContext := genericapirequest.WithNamespace(genericapirequest.NewContext(), "test")
|
||||
registry.UpdateStrategy.(*testRESTStrategy).allowCreateOnUpdate = true
|
||||
if !updateAndVerify(t, testContext, registry, &podA) {
|
||||
t.Errorf("Unexpected error updating podA")
|
||||
}
|
||||
|
||||
obj, err := registry.Export(testContext, podA.Name, metav1.ExportOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
exportedPod := obj.(*example.Pod)
|
||||
if exportedPod.Labels["exported"] != "true" {
|
||||
t.Errorf("expected: exported->true, found: %s", exportedPod.Labels["exported"])
|
||||
}
|
||||
if exportedPod.Labels["exact"] != "false" {
|
||||
t.Errorf("expected: exact->false, found: %s", exportedPod.Labels["exact"])
|
||||
}
|
||||
if exportedPod.Labels["prepare_create"] != "true" {
|
||||
t.Errorf("expected: prepare_create->true, found: %s", exportedPod.Labels["prepare_create"])
|
||||
}
|
||||
delete(exportedPod.Labels, "exported")
|
||||
delete(exportedPod.Labels, "exact")
|
||||
delete(exportedPod.Labels, "prepare_create")
|
||||
exportObjectMeta(&podA.ObjectMeta, false)
|
||||
podA.Spec = exportedPod.Spec
|
||||
if !reflect.DeepEqual(&podA, exportedPod) {
|
||||
t.Errorf("expected:\n%v\nsaw:\n%v\n", &podA, exportedPod)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreBasicExport(t *testing.T) {
|
||||
podA := example.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test",
|
||||
Name: "foo",
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
Spec: example.PodSpec{NodeName: "machine"},
|
||||
Status: example.PodStatus{HostIP: "1.2.3.4"},
|
||||
}
|
||||
|
||||
destroyFunc, registry := NewTestGenericStoreRegistry(t)
|
||||
defer destroyFunc()
|
||||
|
||||
testContext := genericapirequest.WithNamespace(genericapirequest.NewContext(), "test")
|
||||
registry.UpdateStrategy.(*testRESTStrategy).allowCreateOnUpdate = true
|
||||
if !updateAndVerify(t, testContext, registry, &podA) {
|
||||
t.Errorf("Unexpected error updating podA")
|
||||
}
|
||||
|
||||
obj, err := registry.Export(testContext, podA.Name, metav1.ExportOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
exportedPod := obj.(*example.Pod)
|
||||
if exportedPod.Labels["prepare_create"] != "true" {
|
||||
t.Errorf("expected: prepare_create->true, found: %s", exportedPod.Labels["prepare_create"])
|
||||
}
|
||||
delete(exportedPod.Labels, "prepare_create")
|
||||
exportObjectMeta(&podA.ObjectMeta, false)
|
||||
podA.Spec = exportedPod.Spec
|
||||
if !reflect.DeepEqual(&podA, exportedPod) {
|
||||
t.Errorf("expected:\n%v\nsaw:\n%v\n", &podA, exportedPod)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreGet(t *testing.T) {
|
||||
podA := &example.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "test", Name: "foo"},
|
||||
|
@ -25,7 +25,6 @@ go_library(
|
||||
"create_update.go",
|
||||
"delete.go",
|
||||
"doc.go",
|
||||
"export.go",
|
||||
"meta.go",
|
||||
"rest.go",
|
||||
"table.go",
|
||||
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 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 rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// RESTExportStrategy is the interface that defines how to export a Kubernetes
|
||||
// object. An exported object is stripped of non-user-settable fields and
|
||||
// optionally, the identifying information related to the object's identity in
|
||||
// the cluster so that it can be loaded into a different namespace or entirely
|
||||
// different cluster without conflict.
|
||||
type RESTExportStrategy interface {
|
||||
// Export strips fields that can not be set by the user. If 'exact' is false
|
||||
// fields specific to the cluster are also stripped
|
||||
Export(ctx context.Context, obj runtime.Object, exact bool) error
|
||||
}
|
@ -102,16 +102,6 @@ type Lister interface {
|
||||
TableConvertor
|
||||
}
|
||||
|
||||
// Exporter is an object that knows how to strip a RESTful resource for export. A store should implement this interface
|
||||
// if export is generally supported for that type. Errors can still be returned during the actual Export when certain
|
||||
// instances of the type are not exportable.
|
||||
type Exporter interface {
|
||||
// Export an object. Fields that are not user specified (e.g. Status, ObjectMeta.ResourceVersion) are stripped out
|
||||
// Returns the stripped object. If 'exact' is true, fields that are specific to the cluster (e.g. namespace) are
|
||||
// retained, otherwise they are stripped also.
|
||||
Export(ctx context.Context, name string, opts metav1.ExportOptions) (runtime.Object, error)
|
||||
}
|
||||
|
||||
// Getter is an object that can retrieve a named RESTful resource.
|
||||
type Getter interface {
|
||||
// Get finds a resource in the storage by name and returns it.
|
||||
|
@ -10,6 +10,7 @@ go_test(
|
||||
size = "large",
|
||||
srcs = [
|
||||
"apiserver_test.go",
|
||||
"export_test.go",
|
||||
"main_test.go",
|
||||
"max_json_patch_operations_test.go",
|
||||
"max_request_body_bytes_test.go",
|
||||
|
57
test/integration/apiserver/export_test.go
Normal file
57
test/integration/apiserver/export_test.go
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
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"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Tests that the apiserver rejects the export param
|
||||
func TestExportRejection(t *testing.T) {
|
||||
_, clientSet, closeFn := setup(t)
|
||||
defer closeFn()
|
||||
|
||||
_, err := clientSet.CoreV1().Namespaces().Create(context.Background(), &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "export-fail"},
|
||||
}, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
clientSet.CoreV1().Namespaces().Delete(context.Background(), "export-fail", metav1.DeleteOptions{})
|
||||
}()
|
||||
|
||||
result := clientSet.Discovery().RESTClient().Get().AbsPath("/api/v1/namespaces/export-fail").Param("export", "true").Do(context.Background())
|
||||
statusCode := 0
|
||||
result.StatusCode(&statusCode)
|
||||
if statusCode != http.StatusBadRequest {
|
||||
t.Errorf("expected %v, got %v", http.StatusBadRequest, statusCode)
|
||||
}
|
||||
|
||||
result = clientSet.Discovery().RESTClient().Get().AbsPath("/api/v1/namespaces/export-fail").Param("export", "false").Do(context.Background())
|
||||
statusCode = 0
|
||||
result.StatusCode(&statusCode)
|
||||
if statusCode != http.StatusOK {
|
||||
t.Errorf("expected %v, got %v", http.StatusOK, statusCode)
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user