Unverified Commit 716344fd authored by Kubernetes Prow Robot's avatar Kubernetes Prow Robot Committed by GitHub

Merge pull request #76849 from liggitt/crd_webhook_integration_tests

Fix scale and rollback subresources with admission webhooks, add integration tests
parents dc395391 69042da9
...@@ -158,24 +158,27 @@ func (r *RollbackREST) New() runtime.Object { ...@@ -158,24 +158,27 @@ func (r *RollbackREST) New() runtime.Object {
return &apps.DeploymentRollback{} return &apps.DeploymentRollback{}
} }
var _ = rest.Creater(&RollbackREST{}) var _ = rest.NamedCreater(&RollbackREST{})
func (r *RollbackREST) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) { func (r *RollbackREST) Create(ctx context.Context, name string, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
rollback, ok := obj.(*apps.DeploymentRollback) rollback, ok := obj.(*apps.DeploymentRollback)
if !ok { if !ok {
return nil, errors.NewBadRequest(fmt.Sprintf("not a DeploymentRollback: %#v", obj)) return nil, errors.NewBadRequest(fmt.Sprintf("not a DeploymentRollback: %#v", obj))
} }
if errs := appsvalidation.ValidateDeploymentRollback(rollback); len(errs) != 0 {
return nil, errors.NewInvalid(apps.Kind("DeploymentRollback"), rollback.Name, errs)
}
if name != rollback.Name {
return nil, errors.NewBadRequest("name in URL does not match name in DeploymentRollback object")
}
if createValidation != nil { if createValidation != nil {
if err := createValidation(obj.DeepCopyObject()); err != nil { if err := createValidation(obj.DeepCopyObject()); err != nil {
return nil, err return nil, err
} }
} }
if errs := appsvalidation.ValidateDeploymentRollback(rollback); len(errs) != 0 {
return nil, errors.NewInvalid(apps.Kind("DeploymentRollback"), rollback.Name, errs)
}
// Update the Deployment with information in DeploymentRollback to trigger rollback // Update the Deployment with information in DeploymentRollback to trigger rollback
err := r.rollbackDeployment(ctx, rollback.Name, &rollback.RollbackTo, rollback.UpdatedAnnotations, dryrun.IsDryRun(options.DryRun)) err := r.rollbackDeployment(ctx, rollback.Name, &rollback.RollbackTo, rollback.UpdatedAnnotations, dryrun.IsDryRun(options.DryRun))
if err != nil { if err != nil {
...@@ -293,7 +296,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update ...@@ -293,7 +296,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
deployment.Spec.Replicas = scale.Spec.Replicas deployment.Spec.Replicas = scale.Spec.Replicas
deployment.ResourceVersion = scale.ResourceVersion deployment.ResourceVersion = scale.ResourceVersion
obj, _, err = r.store.Update(ctx, deployment.Name, rest.DefaultUpdatedObjectInfo(deployment), createValidation, updateValidation, false, options) obj, _, err = r.store.Update(
ctx,
deployment.Name,
rest.DefaultUpdatedObjectInfo(deployment),
toScaleCreateValidation(createValidation),
toScaleUpdateValidation(updateValidation),
false,
options,
)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
...@@ -305,6 +316,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update ...@@ -305,6 +316,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
return newScale, false, nil return newScale, false, nil
} }
func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
return func(obj runtime.Object) error {
scale, err := scaleFromDeployment(obj.(*apps.Deployment))
if err != nil {
return err
}
return f(scale)
}
}
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
return func(obj, old runtime.Object) error {
newScale, err := scaleFromDeployment(obj.(*apps.Deployment))
if err != nil {
return err
}
oldScale, err := scaleFromDeployment(old.(*apps.Deployment))
if err != nil {
return err
}
return f(newScale, oldScale)
}
}
// scaleFromDeployment returns a scale subresource for a deployment. // scaleFromDeployment returns a scale subresource for a deployment.
func scaleFromDeployment(deployment *apps.Deployment) (*autoscaling.Scale, error) { func scaleFromDeployment(deployment *apps.Deployment) (*autoscaling.Scale, error) {
selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector) selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector)
......
...@@ -351,7 +351,7 @@ func TestEtcdCreateDeploymentRollback(t *testing.T) { ...@@ -351,7 +351,7 @@ func TestEtcdCreateDeploymentRollback(t *testing.T) {
if _, err := storage.Deployment.Create(ctx, validNewDeployment(), rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil { if _, err := storage.Deployment.Create(ctx, validNewDeployment(), rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil {
t.Fatalf("%s: unexpected error: %v", k, err) t.Fatalf("%s: unexpected error: %v", k, err)
} }
rollbackRespStatus, err := rollbackStorage.Create(ctx, &test.rollback, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) rollbackRespStatus, err := rollbackStorage.Create(ctx, test.rollback.Name, &test.rollback, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
if !test.errOK(err) { if !test.errOK(err) {
t.Errorf("%s: unexpected error: %v", k, err) t.Errorf("%s: unexpected error: %v", k, err)
} else if err == nil { } else if err == nil {
...@@ -392,7 +392,7 @@ func TestCreateDeploymentRollbackValidation(t *testing.T) { ...@@ -392,7 +392,7 @@ func TestCreateDeploymentRollbackValidation(t *testing.T) {
validationError := fmt.Errorf("admission deny") validationError := fmt.Errorf("admission deny")
alwaysDenyValidationFunc := func(obj runtime.Object) error { return validationError } alwaysDenyValidationFunc := func(obj runtime.Object) error { return validationError }
_, err := rollbackStorage.Create(ctx, &rollback, alwaysDenyValidationFunc, &metav1.CreateOptions{}) _, err := rollbackStorage.Create(ctx, rollback.Name, &rollback, alwaysDenyValidationFunc, &metav1.CreateOptions{})
if err == nil || validationError != err { if err == nil || validationError != err {
t.Errorf("expected: %v, got: %v", validationError, err) t.Errorf("expected: %v, got: %v", validationError, err)
...@@ -411,7 +411,7 @@ func TestEtcdCreateDeploymentRollbackNoDeployment(t *testing.T) { ...@@ -411,7 +411,7 @@ func TestEtcdCreateDeploymentRollbackNoDeployment(t *testing.T) {
rollbackStorage := storage.Rollback rollbackStorage := storage.Rollback
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace) ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
_, err := rollbackStorage.Create(ctx, &apps.DeploymentRollback{ _, err := rollbackStorage.Create(ctx, name, &apps.DeploymentRollback{
Name: name, Name: name,
UpdatedAnnotations: map[string]string{}, UpdatedAnnotations: map[string]string{},
RollbackTo: apps.RollbackConfig{Revision: 1}, RollbackTo: apps.RollbackConfig{Revision: 1},
......
...@@ -201,7 +201,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update ...@@ -201,7 +201,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
rs.Spec.Replicas = scale.Spec.Replicas rs.Spec.Replicas = scale.Spec.Replicas
rs.ResourceVersion = scale.ResourceVersion rs.ResourceVersion = scale.ResourceVersion
obj, _, err = r.store.Update(ctx, rs.Name, rest.DefaultUpdatedObjectInfo(rs), createValidation, updateValidation, false, options) obj, _, err = r.store.Update(
ctx,
rs.Name,
rest.DefaultUpdatedObjectInfo(rs),
toScaleCreateValidation(createValidation),
toScaleUpdateValidation(updateValidation),
false,
options,
)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
...@@ -213,6 +221,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update ...@@ -213,6 +221,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
return newScale, false, err return newScale, false, err
} }
func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
return func(obj runtime.Object) error {
scale, err := scaleFromReplicaSet(obj.(*apps.ReplicaSet))
if err != nil {
return err
}
return f(scale)
}
}
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
return func(obj, old runtime.Object) error {
newScale, err := scaleFromReplicaSet(obj.(*apps.ReplicaSet))
if err != nil {
return err
}
oldScale, err := scaleFromReplicaSet(old.(*apps.ReplicaSet))
if err != nil {
return err
}
return f(newScale, oldScale)
}
}
// scaleFromReplicaSet returns a scale subresource for a replica set. // scaleFromReplicaSet returns a scale subresource for a replica set.
func scaleFromReplicaSet(rs *apps.ReplicaSet) (*autoscaling.Scale, error) { func scaleFromReplicaSet(rs *apps.ReplicaSet) (*autoscaling.Scale, error) {
selector, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector) selector, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector)
......
...@@ -188,7 +188,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update ...@@ -188,7 +188,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
ss.Spec.Replicas = scale.Spec.Replicas ss.Spec.Replicas = scale.Spec.Replicas
ss.ResourceVersion = scale.ResourceVersion ss.ResourceVersion = scale.ResourceVersion
obj, _, err = r.store.Update(ctx, ss.Name, rest.DefaultUpdatedObjectInfo(ss), createValidation, updateValidation, false, options) obj, _, err = r.store.Update(
ctx,
ss.Name,
rest.DefaultUpdatedObjectInfo(ss),
toScaleCreateValidation(createValidation),
toScaleUpdateValidation(updateValidation),
false,
options,
)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
...@@ -200,6 +208,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update ...@@ -200,6 +208,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
return newScale, false, err return newScale, false, err
} }
func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
return func(obj runtime.Object) error {
scale, err := scaleFromStatefulSet(obj.(*apps.StatefulSet))
if err != nil {
return err
}
return f(scale)
}
}
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
return func(obj, old runtime.Object) error {
newScale, err := scaleFromStatefulSet(obj.(*apps.StatefulSet))
if err != nil {
return err
}
oldScale, err := scaleFromStatefulSet(old.(*apps.StatefulSet))
if err != nil {
return err
}
return f(newScale, oldScale)
}
}
// scaleFromStatefulSet returns a scale subresource for a statefulset. // scaleFromStatefulSet returns a scale subresource for a statefulset.
func scaleFromStatefulSet(ss *apps.StatefulSet) (*autoscaling.Scale, error) { func scaleFromStatefulSet(ss *apps.StatefulSet) (*autoscaling.Scale, error) {
selector, err := metav1.LabelSelectorAsSelector(ss.Spec.Selector) selector, err := metav1.LabelSelectorAsSelector(ss.Spec.Selector)
......
...@@ -183,7 +183,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update ...@@ -183,7 +183,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
rc.Spec.Replicas = scale.Spec.Replicas rc.Spec.Replicas = scale.Spec.Replicas
rc.ResourceVersion = scale.ResourceVersion rc.ResourceVersion = scale.ResourceVersion
obj, _, err = r.store.Update(ctx, rc.Name, rest.DefaultUpdatedObjectInfo(rc), createValidation, updateValidation, false, options) obj, _, err = r.store.Update(
ctx,
rc.Name,
rest.DefaultUpdatedObjectInfo(rc),
toScaleCreateValidation(createValidation),
toScaleUpdateValidation(updateValidation),
false,
options,
)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
...@@ -191,6 +199,21 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update ...@@ -191,6 +199,21 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
return scaleFromRC(rc), false, nil return scaleFromRC(rc), false, nil
} }
func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
return func(obj runtime.Object) error {
return f(scaleFromRC(obj.(*api.ReplicationController)))
}
}
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
return func(obj, old runtime.Object) error {
return f(
scaleFromRC(obj.(*api.ReplicationController)),
scaleFromRC(old.(*api.ReplicationController)),
)
}
}
// scaleFromRC returns a scale subresource for a replication controller. // scaleFromRC returns a scale subresource for a replication controller.
func scaleFromRC(rc *api.ReplicationController) *autoscaling.Scale { func scaleFromRC(rc *api.ReplicationController) *autoscaling.Scale {
return &autoscaling.Scale{ return &autoscaling.Scale{
......
...@@ -95,7 +95,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update ...@@ -95,7 +95,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
rc.Spec.Replicas = scale.Spec.Replicas rc.Spec.Replicas = scale.Spec.Replicas
rc.ResourceVersion = scale.ResourceVersion rc.ResourceVersion = scale.ResourceVersion
obj, _, err = r.store.Update(ctx, rc.Name, rest.DefaultUpdatedObjectInfo(rc), createValidation, updateValidation, false, options) obj, _, err = r.store.Update(
ctx,
rc.Name,
rest.DefaultUpdatedObjectInfo(rc),
toScaleCreateValidation(createValidation),
toScaleUpdateValidation(updateValidation),
false,
options,
)
if err != nil { if err != nil {
return nil, false, errors.NewConflict(extensions.Resource("replicationcontrollers/scale"), scale.Name, err) return nil, false, errors.NewConflict(extensions.Resource("replicationcontrollers/scale"), scale.Name, err)
} }
...@@ -103,6 +111,21 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update ...@@ -103,6 +111,21 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
return scaleFromRC(rc), false, nil return scaleFromRC(rc), false, nil
} }
func toScaleCreateValidation(f rest.ValidateObjectFunc) rest.ValidateObjectFunc {
return func(obj runtime.Object) error {
return f(scaleFromRC(obj.(*api.ReplicationController)))
}
}
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc) rest.ValidateObjectUpdateFunc {
return func(obj, old runtime.Object) error {
return f(
scaleFromRC(obj.(*api.ReplicationController)),
scaleFromRC(old.(*api.ReplicationController)),
)
}
}
// scaleFromRC returns a scale subresource for a replication controller. // scaleFromRC returns a scale subresource for a replication controller.
func scaleFromRC(rc *api.ReplicationController) *autoscaling.Scale { func scaleFromRC(rc *api.ReplicationController) *autoscaling.Scale {
return &autoscaling.Scale{ return &autoscaling.Scale{
......
...@@ -190,9 +190,9 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc { ...@@ -190,9 +190,9 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
case result.Protected && result.Operation != reconciliation.ReconcileNone: case result.Protected && result.Operation != reconciliation.ReconcileNone:
klog.Warningf("skipped reconcile-protected clusterrole.%s/%s with missing permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules) klog.Warningf("skipped reconcile-protected clusterrole.%s/%s with missing permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules)
case result.Operation == reconciliation.ReconcileUpdate: case result.Operation == reconciliation.ReconcileUpdate:
klog.Infof("updated clusterrole.%s/%s with additional permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules) klog.V(2).Infof("updated clusterrole.%s/%s with additional permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules)
case result.Operation == reconciliation.ReconcileCreate: case result.Operation == reconciliation.ReconcileCreate:
klog.Infof("created clusterrole.%s/%s", rbac.GroupName, clusterRole.Name) klog.V(2).Infof("created clusterrole.%s/%s", rbac.GroupName, clusterRole.Name)
} }
return nil return nil
}) })
...@@ -218,11 +218,11 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc { ...@@ -218,11 +218,11 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
case result.Protected && result.Operation != reconciliation.ReconcileNone: case result.Protected && result.Operation != reconciliation.ReconcileNone:
klog.Warningf("skipped reconcile-protected clusterrolebinding.%s/%s with missing subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects) klog.Warningf("skipped reconcile-protected clusterrolebinding.%s/%s with missing subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
case result.Operation == reconciliation.ReconcileUpdate: case result.Operation == reconciliation.ReconcileUpdate:
klog.Infof("updated clusterrolebinding.%s/%s with additional subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects) klog.V(2).Infof("updated clusterrolebinding.%s/%s with additional subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
case result.Operation == reconciliation.ReconcileCreate: case result.Operation == reconciliation.ReconcileCreate:
klog.Infof("created clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name) klog.V(2).Infof("created clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
case result.Operation == reconciliation.ReconcileRecreate: case result.Operation == reconciliation.ReconcileRecreate:
klog.Infof("recreated clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name) klog.V(2).Infof("recreated clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
} }
return nil return nil
}) })
...@@ -249,9 +249,9 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc { ...@@ -249,9 +249,9 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
case result.Protected && result.Operation != reconciliation.ReconcileNone: case result.Protected && result.Operation != reconciliation.ReconcileNone:
klog.Warningf("skipped reconcile-protected role.%s/%s in %v with missing permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules) klog.Warningf("skipped reconcile-protected role.%s/%s in %v with missing permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules)
case result.Operation == reconciliation.ReconcileUpdate: case result.Operation == reconciliation.ReconcileUpdate:
klog.Infof("updated role.%s/%s in %v with additional permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules) klog.V(2).Infof("updated role.%s/%s in %v with additional permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules)
case result.Operation == reconciliation.ReconcileCreate: case result.Operation == reconciliation.ReconcileCreate:
klog.Infof("created role.%s/%s in %v", rbac.GroupName, role.Name, namespace) klog.V(2).Infof("created role.%s/%s in %v", rbac.GroupName, role.Name, namespace)
} }
return nil return nil
}) })
...@@ -279,11 +279,11 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc { ...@@ -279,11 +279,11 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
case result.Protected && result.Operation != reconciliation.ReconcileNone: case result.Protected && result.Operation != reconciliation.ReconcileNone:
klog.Warningf("skipped reconcile-protected rolebinding.%s/%s in %v with missing subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects) klog.Warningf("skipped reconcile-protected rolebinding.%s/%s in %v with missing subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
case result.Operation == reconciliation.ReconcileUpdate: case result.Operation == reconciliation.ReconcileUpdate:
klog.Infof("updated rolebinding.%s/%s in %v with additional subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects) klog.V(2).Infof("updated rolebinding.%s/%s in %v with additional subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
case result.Operation == reconciliation.ReconcileCreate: case result.Operation == reconciliation.ReconcileCreate:
klog.Infof("created rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace) klog.V(2).Infof("created rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace)
case result.Operation == reconciliation.ReconcileRecreate: case result.Operation == reconciliation.ReconcileRecreate:
klog.Infof("recreated rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace) klog.V(2).Infof("recreated rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace)
} }
return nil return nil
}) })
......
...@@ -12,6 +12,7 @@ go_library( ...@@ -12,6 +12,7 @@ go_library(
importpath = "k8s.io/apiextensions-apiserver/pkg/apiserver/conversion", importpath = "k8s.io/apiextensions-apiserver/pkg/apiserver/conversion",
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
"//staging/src/k8s.io/api/autoscaling/v1:go_default_library",
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
"//staging/src/k8s.io/apiextensions-apiserver/pkg/features:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/features:go_default_library",
...@@ -22,6 +23,7 @@ go_library( ...@@ -22,6 +23,7 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/webhook:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/webhook:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
"//staging/src/k8s.io/client-go/rest:go_default_library", "//staging/src/k8s.io/client-go/rest:go_default_library",
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library", "//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
], ],
......
...@@ -19,6 +19,7 @@ package conversion ...@@ -19,6 +19,7 @@ package conversion
import ( import (
"fmt" "fmt"
autoscalingv1 "k8s.io/api/autoscaling/v1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features" apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
...@@ -26,6 +27,7 @@ import ( ...@@ -26,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/apiserver/pkg/util/webhook" "k8s.io/apiserver/pkg/util/webhook"
typedscheme "k8s.io/client-go/kubernetes/scheme"
) )
// CRConverterFactory is the factory for all CR converters. // CRConverterFactory is the factory for all CR converters.
...@@ -77,7 +79,20 @@ func (m *CRConverterFactory) NewConverter(crd *apiextensions.CustomResourceDefin ...@@ -77,7 +79,20 @@ func (m *CRConverterFactory) NewConverter(crd *apiextensions.CustomResourceDefin
default: default:
return nil, nil, fmt.Errorf("unknown conversion strategy %q for CRD %s", crd.Spec.Conversion.Strategy, crd.Name) return nil, nil, fmt.Errorf("unknown conversion strategy %q for CRD %s", crd.Spec.Conversion.Strategy, crd.Name)
} }
// Determine whether we should expect to be asked to "convert" autoscaling/v1 Scale types
convertScale := false
if utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceSubresources) {
convertScale = crd.Spec.Subresources != nil && crd.Spec.Subresources.Scale != nil
for _, version := range crd.Spec.Versions {
if version.Subresources != nil && version.Subresources.Scale != nil {
convertScale = true
}
}
}
unsafe = &crConverter{ unsafe = &crConverter{
convertScale: convertScale,
validVersions: validVersions, validVersions: validVersions,
clusterScoped: crd.Spec.Scope == apiextensions.ClusterScoped, clusterScoped: crd.Spec.Scope == apiextensions.ClusterScoped,
converter: converter, converter: converter,
...@@ -96,6 +111,7 @@ type crConverterInterface interface { ...@@ -96,6 +111,7 @@ type crConverterInterface interface {
// crConverter extends the delegate converter with generic CR conversion behaviour. The delegate will implement the // crConverter extends the delegate converter with generic CR conversion behaviour. The delegate will implement the
// user defined conversion strategy given in the CustomResourceDefinition. // user defined conversion strategy given in the CustomResourceDefinition.
type crConverter struct { type crConverter struct {
convertScale bool
converter crConverterInterface converter crConverterInterface
validVersions map[schema.GroupVersion]bool validVersions map[schema.GroupVersion]bool
clusterScoped bool clusterScoped bool
...@@ -114,14 +130,23 @@ func (c *crConverter) ConvertFieldLabel(gvk schema.GroupVersionKind, label, valu ...@@ -114,14 +130,23 @@ func (c *crConverter) ConvertFieldLabel(gvk schema.GroupVersionKind, label, valu
} }
func (c *crConverter) Convert(in, out, context interface{}) error { func (c *crConverter) Convert(in, out, context interface{}) error {
// Special-case typed scale conversion if this custom resource supports a scale endpoint
if c.convertScale {
_, isInScale := in.(*autoscalingv1.Scale)
_, isOutScale := out.(*autoscalingv1.Scale)
if isInScale || isOutScale {
return typedscheme.Scheme.Convert(in, out, context)
}
}
unstructIn, ok := in.(*unstructured.Unstructured) unstructIn, ok := in.(*unstructured.Unstructured)
if !ok { if !ok {
return fmt.Errorf("input type %T in not valid for unstructured conversion", in) return fmt.Errorf("input type %T in not valid for unstructured conversion to %T", in, out)
} }
unstructOut, ok := out.(*unstructured.Unstructured) unstructOut, ok := out.(*unstructured.Unstructured)
if !ok { if !ok {
return fmt.Errorf("output type %T in not valid for unstructured conversion", out) return fmt.Errorf("output type %T in not valid for unstructured conversion from %T", out, in)
} }
outGVK := unstructOut.GroupVersionKind() outGVK := unstructOut.GroupVersionKind()
......
...@@ -268,7 +268,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update ...@@ -268,7 +268,15 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
} }
cr.SetResourceVersion(scale.ResourceVersion) cr.SetResourceVersion(scale.ResourceVersion)
obj, _, err = r.store.Update(ctx, cr.GetName(), rest.DefaultUpdatedObjectInfo(cr), createValidation, updateValidation, false, options) obj, _, err = r.store.Update(
ctx,
cr.GetName(),
rest.DefaultUpdatedObjectInfo(cr),
toScaleCreateValidation(createValidation, r.specReplicasPath, r.statusReplicasPath, r.labelSelectorPath),
toScaleUpdateValidation(updateValidation, r.specReplicasPath, r.statusReplicasPath, r.labelSelectorPath),
false,
options,
)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
...@@ -281,6 +289,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update ...@@ -281,6 +289,30 @@ func (r *ScaleREST) Update(ctx context.Context, name string, objInfo rest.Update
return newScale, false, err return newScale, false, err
} }
func toScaleCreateValidation(f rest.ValidateObjectFunc, specReplicasPath, statusReplicasPath, labelSelectorPath string) rest.ValidateObjectFunc {
return func(obj runtime.Object) error {
scale, _, err := scaleFromCustomResource(obj.(*unstructured.Unstructured), specReplicasPath, statusReplicasPath, labelSelectorPath)
if err != nil {
return err
}
return f(scale)
}
}
func toScaleUpdateValidation(f rest.ValidateObjectUpdateFunc, specReplicasPath, statusReplicasPath, labelSelectorPath string) rest.ValidateObjectUpdateFunc {
return func(obj, old runtime.Object) error {
newScale, _, err := scaleFromCustomResource(obj.(*unstructured.Unstructured), specReplicasPath, statusReplicasPath, labelSelectorPath)
if err != nil {
return err
}
oldScale, _, err := scaleFromCustomResource(old.(*unstructured.Unstructured), specReplicasPath, statusReplicasPath, labelSelectorPath)
if err != nil {
return err
}
return f(newScale, oldScale)
}
}
// scaleFromCustomResource returns a scale subresource for a customresource and a bool signalling wether // scaleFromCustomResource returns a scale subresource for a customresource and a bool signalling wether
// the specReplicas value was found. // the specReplicas value was found.
func scaleFromCustomResource(cr *unstructured.Unstructured, specReplicasPath, statusReplicasPath, labelSelectorPath string) (*autoscalingv1.Scale, bool, error) { func scaleFromCustomResource(cr *unstructured.Unstructured, specReplicasPath, statusReplicasPath, labelSelectorPath string) (*autoscalingv1.Scale, bool, error) {
...@@ -310,6 +342,11 @@ func scaleFromCustomResource(cr *unstructured.Unstructured, specReplicasPath, st ...@@ -310,6 +342,11 @@ func scaleFromCustomResource(cr *unstructured.Unstructured, specReplicasPath, st
} }
scale := &autoscalingv1.Scale{ scale := &autoscalingv1.Scale{
// Populate apiVersion and kind so conversion recognizes we are already in the desired GVK and doesn't try to convert
TypeMeta: metav1.TypeMeta{
APIVersion: "autoscaling/v1",
Kind: "Scale",
},
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: cr.GetName(), Name: cr.GetName(),
Namespace: cr.GetNamespace(), Namespace: cr.GetNamespace(),
......
...@@ -381,6 +381,10 @@ func TestScaleGet(t *testing.T) { ...@@ -381,6 +381,10 @@ func TestScaleGet(t *testing.T) {
} }
want := &autoscalingv1.Scale{ want := &autoscalingv1.Scale{
TypeMeta: metav1.TypeMeta{
Kind: "Scale",
APIVersion: "autoscaling/v1",
},
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: cr.GetName(), Name: cr.GetName(),
Namespace: metav1.NamespaceDefault, Namespace: metav1.NamespaceDefault,
......
...@@ -6,6 +6,10 @@ go_test( ...@@ -6,6 +6,10 @@ go_test(
"main_test.go", "main_test.go",
"watch_restart_test.go", "watch_restart_test.go",
], ],
tags = [
"etcd",
"integration",
],
deps = [ deps = [
"//pkg/api/testapi:go_default_library", "//pkg/api/testapi:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library",
......
...@@ -83,6 +83,7 @@ filegroup( ...@@ -83,6 +83,7 @@ filegroup(
name = "all-srcs", name = "all-srcs",
srcs = [ srcs = [
":package-srcs", ":package-srcs",
"//test/integration/apiserver/admissionwebhook:all-srcs",
"//test/integration/apiserver/apply:all-srcs", "//test/integration/apiserver/apply:all-srcs",
], ],
tags = ["automanaged"], tags = ["automanaged"],
......
load("@io_bazel_rules_go//go:def.bzl", "go_test")
go_test(
name = "go_default_test",
srcs = [
"admission_test.go",
"main_test.go",
],
tags = [
"etcd",
"integration",
],
deps = [
"//cmd/kube-apiserver/app/options:go_default_library",
"//staging/src/k8s.io/api/admission/v1beta1:go_default_library",
"//staging/src/k8s.io/api/admissionregistration/v1beta1:go_default_library",
"//staging/src/k8s.io/api/apps/v1beta1:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/api/extensions/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/client-go/dynamic:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/util/retry:go_default_library",
"//test/integration/etcd:go_default_library",
"//test/integration/framework:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
/*
Copyright 2019 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 admissionwebhook
import (
"testing"
"k8s.io/kubernetes/test/integration/framework"
)
func TestMain(m *testing.M) {
framework.EtcdMain(m.Run)
}
...@@ -139,13 +139,19 @@ func StartRealMasterOrDie(t *testing.T, configFuncs ...func(*options.ServerRunOp ...@@ -139,13 +139,19 @@ func StartRealMasterOrDie(t *testing.T, configFuncs ...func(*options.ServerRunOp
}() }()
lastHealth := "" lastHealth := ""
attempt := 0
if err := wait.PollImmediate(time.Second, time.Minute, func() (done bool, err error) { if err := wait.PollImmediate(time.Second, time.Minute, func() (done bool, err error) {
// wait for the server to be healthy // wait for the server to be healthy
result := kubeClient.RESTClient().Get().AbsPath("/healthz").Do() result := kubeClient.RESTClient().Get().AbsPath("/healthz").Do()
content, _ := result.Raw() content, _ := result.Raw()
lastHealth = string(content) lastHealth = string(content)
if errResult := result.Error(); errResult != nil { if errResult := result.Error(); errResult != nil {
t.Log(errResult) attempt++
if attempt < 10 {
t.Log("waiting for server to be healthy")
} else {
t.Log(errResult)
}
return false, nil return false, nil
} }
var status int var status int
......
...@@ -78,8 +78,10 @@ go_library( ...@@ -78,8 +78,10 @@ go_library(
"//staging/src/k8s.io/client-go/util/cert:go_default_library", "//staging/src/k8s.io/client-go/util/cert:go_default_library",
"//test/e2e/framework:go_default_library", "//test/e2e/framework:go_default_library",
"//test/utils:go_default_library", "//test/utils:go_default_library",
"//vendor/github.com/coreos/etcd/clientv3:go_default_library",
"//vendor/github.com/go-openapi/spec:go_default_library", "//vendor/github.com/go-openapi/spec:go_default_library",
"//vendor/github.com/pborman/uuid:go_default_library", "//vendor/github.com/pborman/uuid:go_default_library",
"//vendor/google.golang.org/grpc/grpclog:go_default_library",
"//vendor/k8s.io/klog:go_default_library", "//vendor/k8s.io/klog:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
], ],
......
...@@ -27,6 +27,8 @@ import ( ...@@ -27,6 +27,8 @@ import (
"runtime" "runtime"
"strings" "strings"
"github.com/coreos/etcd/clientv3"
"google.golang.org/grpc/grpclog"
"k8s.io/klog" "k8s.io/klog"
"k8s.io/kubernetes/pkg/util/env" "k8s.io/kubernetes/pkg/util/env"
...@@ -109,7 +111,7 @@ func startEtcd() (func(), error) { ...@@ -109,7 +111,7 @@ func startEtcd() (func(), error) {
"--listen-peer-urls", "--listen-peer-urls",
"http://127.0.0.1:0", "http://127.0.0.1:0",
"--log-package-levels", "--log-package-levels",
"*=DEBUG", "*=NOTICE", // set to INFO or DEBUG for more logs
) )
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
...@@ -123,6 +125,10 @@ func startEtcd() (func(), error) { ...@@ -123,6 +125,10 @@ func startEtcd() (func(), error) {
} }
} }
// Quiet etcd logs for integration tests
// Comment out to get verbose logs if desired
clientv3.SetLogger(grpclog.NewLoggerV2(ioutil.Discard, ioutil.Discard, os.Stderr))
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("failed to run etcd: %v", err) return nil, fmt.Errorf("failed to run etcd: %v", err)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment