Add default namespace labels to all namespaces for selectors (#96968)

* namespace by name default labelling

Co-authored-by: Jordan Liggitt <jordan@liggitt.net>
Co-authored-by: Abhishek Raut <rauta@vmware.com>

* Make some logic improvement into default namespace label

* Fix unit tests

* minor change to trigger the CI

* Correct some tests and validation behaviors

* Add Canonicalize normalization and improve validation

* Remove label validation that should be dealt by strategy

* Update defaults_test.go
add fuzzer
ns spec

* remove the finalizer thingy

* Fix integration test

* Add namespace canonicalize unit test

* Improve validation code and code comments

* move validation of labels to validateupdate

* spacex will save us all

* add comment to testget

* readablility of canonicalize

* Added namespace finalize and status update validation

* comment about ungenerated names

* correcting a missing line on storage_test

* Update the namespace validation unit test

* Add more missing unit test changes

* Let's just blast the value. Also documenting the workflow here

* Remove unnecessary validations

Co-authored-by: Jordan Liggitt <jordan@liggitt.net>
Co-authored-by: Abhishek Raut <rauta@vmware.com>
Co-authored-by: Ricardo Pchevuzinske Katz <ricardo.katz@gmail.com>
This commit is contained in:
jay vyas
2021-03-08 23:46:59 -05:00
committed by GitHub
parent 6d499e6768
commit c94ce8c507
10 changed files with 170 additions and 12 deletions

View File

@@ -109,6 +109,8 @@ func TestGet(t *testing.T) {
defer server.Terminate(t)
defer storage.store.DestroyFunc()
test := genericregistrytest.New(t, storage.store).ClusterScope()
// note that this ultimately may call validation
test.TestGet(validNewNamespace())
}

View File

@@ -20,6 +20,7 @@ import (
"context"
"fmt"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
@@ -27,9 +28,11 @@ import (
"k8s.io/apiserver/pkg/registry/generic"
apistorage "k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/names"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/features"
)
// namespaceStrategy implements behavior for Namespaces
@@ -88,6 +91,30 @@ func (namespaceStrategy) Validate(ctx context.Context, obj runtime.Object) field
// Canonicalize normalizes the object after validation.
func (namespaceStrategy) Canonicalize(obj runtime.Object) {
// Ensure the label matches the name for namespaces just created using GenerateName,
// where the final name wasn't available for defaulting to make this change.
// This code needs to be kept in sync with the implementation that exists
// in Namespace defaulting (pkg/apis/core/v1)
// Why this hook *and* defaults.go?
//
// CREATE:
// Defaulting and PrepareForCreate happen before generateName is completed
// (i.e. the name is not yet known). Validation happens after generateName
// but should not modify objects. Canonicalize happens later, which makes
// it the best hook for setting the label.
//
// UPDATE:
// Defaulting and Canonicalize will both trigger with the full name.
//
// GET:
// Only defaulting will be applied.
ns := obj.(*api.Namespace)
if utilfeature.DefaultFeatureGate.Enabled(features.NamespaceDefaultLabelName) {
if ns.Labels == nil {
ns.Labels = map[string]string{}
}
ns.Labels[v1.LabelMetadataName] = ns.Name
}
}
// AllowCreateOnUpdate is false for namespaces.

View File

@@ -19,10 +19,14 @@ package namespace
import (
"testing"
v1 "k8s.io/api/core/v1"
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"
apitesting "k8s.io/kubernetes/pkg/api/testing"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
// ensure types are installed
_ "k8s.io/kubernetes/pkg/apis/core/install"
@@ -37,7 +41,7 @@ func TestNamespaceStrategy(t *testing.T) {
t.Errorf("Namespaces should not allow create on update")
}
namespace := &api.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "10"},
ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "10", Labels: map[string]string{v1.LabelMetadataName: "foo"}},
Status: api.NamespaceStatus{Phase: api.NamespaceTerminating},
}
Strategy.PrepareForCreate(ctx, namespace)
@@ -68,6 +72,19 @@ func TestNamespaceStrategy(t *testing.T) {
}
}
func TestNamespaceDefaultLabelCanonicalize(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NamespaceDefaultLabelName, true)()
namespace := &api.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
}
Strategy.Canonicalize(namespace)
if namespace.Labels[v1.LabelMetadataName] != namespace.Name {
t.Errorf("Invalid namespace, default label was not added")
}
}
func TestNamespaceStatusStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
if StatusStrategy.NamespaceScoped() {
@@ -78,13 +95,15 @@ func TestNamespaceStatusStrategy(t *testing.T) {
}
now := metav1.Now()
oldNamespace := &api.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "10", DeletionTimestamp: &now},
Spec: api.NamespaceSpec{Finalizers: []api.FinalizerName{"kubernetes"}},
Status: api.NamespaceStatus{Phase: api.NamespaceActive},
ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "10", DeletionTimestamp: &now,
Labels: map[string]string{v1.LabelMetadataName: "foo"}},
Spec: api.NamespaceSpec{Finalizers: []api.FinalizerName{"kubernetes"}},
Status: api.NamespaceStatus{Phase: api.NamespaceActive},
}
namespace := &api.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "9", DeletionTimestamp: &now},
Status: api.NamespaceStatus{Phase: api.NamespaceTerminating},
ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "9", DeletionTimestamp: &now,
Labels: map[string]string{v1.LabelMetadataName: "foo"}},
Status: api.NamespaceStatus{Phase: api.NamespaceTerminating},
}
StatusStrategy.PrepareForUpdate(ctx, namespace, oldNamespace)
if namespace.Status.Phase != api.NamespaceTerminating {
@@ -111,14 +130,16 @@ func TestNamespaceFinalizeStrategy(t *testing.T) {
t.Errorf("Namespaces should not allow create on update")
}
oldNamespace := &api.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "10"},
Spec: api.NamespaceSpec{Finalizers: []api.FinalizerName{"kubernetes", "example.com/org"}},
Status: api.NamespaceStatus{Phase: api.NamespaceActive},
ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "10",
Labels: map[string]string{v1.LabelMetadataName: "foo"}},
Spec: api.NamespaceSpec{Finalizers: []api.FinalizerName{"kubernetes", "example.com/org"}},
Status: api.NamespaceStatus{Phase: api.NamespaceActive},
}
namespace := &api.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "9"},
Spec: api.NamespaceSpec{Finalizers: []api.FinalizerName{"example.com/foo"}},
Status: api.NamespaceStatus{Phase: api.NamespaceTerminating},
ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "9",
Labels: map[string]string{v1.LabelMetadataName: "foo"}},
Spec: api.NamespaceSpec{Finalizers: []api.FinalizerName{"example.com/foo"}},
Status: api.NamespaceStatus{Phase: api.NamespaceTerminating},
}
FinalizeStrategy.PrepareForUpdate(ctx, namespace, oldNamespace)
if namespace.Status.Phase != api.NamespaceActive {