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:
@@ -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())
|
||||
}
|
||||
|
||||
|
@@ -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.
|
||||
|
@@ -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 {
|
||||
|
Reference in New Issue
Block a user