Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[main] adds target namsepace metadata support #2132

Merged
merged 1 commit into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/TektonConfig.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ The TektonConfig CR provides the following features
name: config
spec:
targetNamespace: tekton-pipelines
targetNamespaceMetadata:
labels: {}
annotations: {}
profile: all
config:
nodeSelector: <>
Expand Down Expand Up @@ -151,6 +154,10 @@ By default, namespace would be `tekton-pipelines` for Kubernetes and `openshift-

**Note:** Namespace `openshift-operators` is not allowed in `OpenShift` as a `targetNamespace`.

### Target Namespace Metadata

`targetNamespaceMetadata` allows user to add their custom `labels` and `annotations` to the target namespace via TektonConfig CR.

### Profile

This allows user to choose which all components to install on the cluster.
Expand Down
8 changes: 8 additions & 0 deletions pkg/apis/operator/v1alpha1/tektonconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ func (p Prune) IsEmpty() bool {
return reflect.DeepEqual(p, Prune{})
}

type NamespaceMetadata struct {
Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
}

// TektonConfigSpec defines the desired state of TektonConfig
type TektonConfigSpec struct {
Profile string `json:"profile,omitempty"`
Expand Down Expand Up @@ -109,6 +114,9 @@ type TektonConfigSpec struct {
// Platforms allows configuring platform specific configurations
// +optional
Platforms Platforms `json:"platforms,omitempty"`
// holds target namespace metadata
// +optional
TargetNamespaceMetadata *NamespaceMetadata `json:"targetNamespaceMetadata,omitempty"`
}

// TektonConfigStatus defines the observed state of TektonConfig
Expand Down
35 changes: 35 additions & 0 deletions pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 38 additions & 10 deletions pkg/reconciler/common/targetnamespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const (
labelKeyTargetNamespace = "operator.tekton.dev/targetNamespace"
)

func ReconcileTargetNamespace(ctx context.Context, labels map[string]string, tektonComponent v1alpha1.TektonComponent, kubeClientSet kubernetes.Interface) error {
func ReconcileTargetNamespace(ctx context.Context, labels map[string]string, annotations map[string]string, tektonComponent v1alpha1.TektonComponent, kubeClientSet kubernetes.Interface) error {
// get logger
logger := logging.FromContext(ctx)

Expand Down Expand Up @@ -102,10 +102,24 @@ func ReconcileTargetNamespace(ctx context.Context, labels map[string]string, tek
if labels == nil {
labels = map[string]string{}
}

// update required annotations
if annotations == nil {
annotations = map[string]string{}
}

labels[labelKeyTargetNamespace] = "true" // include target namespace label

// if a namespace found, update the required fields
if targetNamespace != nil {
// initialize labels and annotations
if targetNamespace.Labels == nil {
targetNamespace.Labels = map[string]string{}
}
if targetNamespace.Annotations == nil {
targetNamespace.Annotations = map[string]string{}
}

// verify the existing namespace has the required fields, if not update
updateRequired := false

Expand All @@ -116,20 +130,33 @@ func ReconcileTargetNamespace(ctx context.Context, labels map[string]string, tek
}

// update labels
for expectedLabelKey, expectedLabelValue := range labels {
expectedLabelFound := false
for actualLabelKey, actualLabelValue := range targetNamespace.GetLabels() {
if expectedLabelKey == actualLabelKey && expectedLabelValue == actualLabelValue {
expectedLabelFound = true
for expectedKey, expectedValue := range labels {
found := false
for actualKey, actualValue := range targetNamespace.GetLabels() {
if expectedKey == actualKey && expectedValue == actualValue {
found = true
break
}
}
// update label if not found
if !expectedLabelFound {
if targetNamespace.Labels == nil {
targetNamespace.Labels = map[string]string{}
if !found {
targetNamespace.Labels[expectedKey] = expectedValue
updateRequired = true
}
}

// include annotations from targetNamespaceMetadata
for expectedKey, expectedValue := range annotations {
found := false
for actualKey, actualValue := range targetNamespace.GetAnnotations() {
if expectedKey == actualKey && expectedValue == actualValue {
found = true
break
}
targetNamespace.Labels[expectedLabelKey] = expectedLabelValue
}
// update annotation if not found
if !found {
targetNamespace.Annotations[expectedKey] = expectedValue
updateRequired = true
}
}
Expand All @@ -152,6 +179,7 @@ func ReconcileTargetNamespace(ctx context.Context, labels map[string]string, tek
ObjectMeta: metav1.ObjectMeta{
Name: tektonComponent.GetSpec().GetTargetNamespace(),
Labels: labels,
Annotations: annotations,
OwnerReferences: []metav1.OwnerReference{ownerRef},
},
}
Expand Down
141 changes: 126 additions & 15 deletions pkg/reconciler/common/targetnamespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@ func TestReconcileTargetNamespace(t *testing.T) {
namespaceTektonPipelines := "tekton-pipelines"

tests := []struct {
name string
component v1alpha1.TektonComponent
additionalLabels map[string]string
ownerReferences []metav1.OwnerReference
preFunc func(t *testing.T, fakeClientset *fake.Clientset) // preFunc used to update namespace details before reconcile
err error // error if any from ReconcileTargetNamespace function
postFunc func(t *testing.T, fakeClientset *fake.Clientset, namespace *corev1.Namespace) // postFunc used to verify additional fields
name string
component v1alpha1.TektonComponent
additionalLabels map[string]string
additionalAnnotations map[string]string
ownerReferences []metav1.OwnerReference
preFunc func(t *testing.T, fakeClientset *fake.Clientset) // preFunc used to update namespace details before reconcile
err error // error if any from ReconcileTargetNamespace function
postFunc func(t *testing.T, fakeClientset *fake.Clientset, namespace *corev1.Namespace) // postFunc used to verify additional fields
}{
{
name: "verify-tekton-config",
Expand Down Expand Up @@ -274,6 +275,96 @@ func TestReconcileTargetNamespace(t *testing.T) {
},
err: nil,
},
{
name: "verify-metadata-labels",
component: &v1alpha1.TektonConfig{
ObjectMeta: metav1.ObjectMeta{Name: "config"},
Spec: v1alpha1.TektonConfigSpec{
CommonSpec: v1alpha1.CommonSpec{
TargetNamespace: namespaceTektonPipelines,
},
},
},
additionalLabels: map[string]string{
"foo1": "bar1",
},
err: nil,
},
{
name: "verify-metadata-annotations",
component: &v1alpha1.TektonConfig{
ObjectMeta: metav1.ObjectMeta{Name: "config"},
Spec: v1alpha1.TektonConfigSpec{
CommonSpec: v1alpha1.CommonSpec{
TargetNamespace: namespaceTektonPipelines,
},
},
},
additionalAnnotations: map[string]string{
"foo": "bar",
"foo2": "bar2",
},
err: nil,
},
{
name: "verify-metadata-labels-annotations",
component: &v1alpha1.TektonConfig{
ObjectMeta: metav1.ObjectMeta{Name: "config"},
Spec: v1alpha1.TektonConfigSpec{
CommonSpec: v1alpha1.CommonSpec{
TargetNamespace: namespaceTektonPipelines,
},
},
},
additionalLabels: map[string]string{
"foo": "bar",
"foo2": "bar2",
},
additionalAnnotations: map[string]string{
"foo": "bar",
"foo2": "bar2",
},
err: nil,
},
{
name: "verify-metadata-labels-annotations-override",
component: &v1alpha1.TektonConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "config",
},
Spec: v1alpha1.TektonConfigSpec{
CommonSpec: v1alpha1.CommonSpec{
TargetNamespace: namespaceTektonPipelines,
},
},
},
additionalLabels: map[string]string{
"label-foo": "bar",
"foo2": "bar2",
},
additionalAnnotations: map[string]string{
"annotation-foo": "bar",
"foo2": "bar2",
"foo3": "bar3",
},
preFunc: func(t *testing.T, fakeClientset *fake.Clientset) {
namespace := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespaceTektonPipelines,
Labels: map[string]string{
"label-foo": "label-bar44",
},
Annotations: map[string]string{
"annotation-foo": "bar44",
"foo3": "bar3",
},
},
}
_, err := fakeClientset.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{})
assert.NilError(t, err)
},
err: nil,
},
}

for _, test := range tests {
Expand All @@ -288,6 +379,14 @@ func TestReconcileTargetNamespace(t *testing.T) {
expectedLabels[k] = v
}
}
// expected annotations
expectedAnnotations := map[string]string{}
// include additional annotations, if any
if len(test.additionalAnnotations) > 0 {
for k, v := range test.additionalAnnotations {
expectedAnnotations[k] = v
}
}
// create expected owner reference for that namespace and compute hash
expectedOwnerRef := []metav1.OwnerReference{*metav1.NewControllerRef(test.component, test.component.GroupVersionKind())}
if len(test.ownerReferences) > 0 {
Expand All @@ -302,24 +401,36 @@ func TestReconcileTargetNamespace(t *testing.T) {
}

// call reconciler
err = ReconcileTargetNamespace(context.Background(), test.additionalLabels, test.component, fakeClientset)
err = ReconcileTargetNamespace(context.Background(), test.additionalLabels, test.additionalAnnotations, test.component, fakeClientset)
assert.Equal(t, err, test.err)

if test.err == nil {
namespace, err := fakeClientset.CoreV1().Namespaces().Get(context.Background(), targetNamespace, metav1.GetOptions{})
assert.NilError(t, err)
assert.Equal(t, namespace.ObjectMeta.Name, targetNamespace)

// verify labels
for expectedLabelKey, expectedLabelValue := range expectedLabels {
labelFound := false
for actualLabelKey, actualLabelValue := range namespace.GetLabels() {
if expectedLabelKey == actualLabelKey && expectedLabelValue == actualLabelValue {
labelFound = true
// verify expected labels
for expectedKey, expectedValue := range expectedLabels {
found := false
for actualKey, actualValue := range namespace.GetLabels() {
if expectedKey == actualKey && expectedValue == actualValue {
found = true
break
}
}
assert.Equal(t, true, labelFound, "expected labelKey or labelValue not found:[%s=%s]", expectedLabelKey, expectedLabelValue)
assert.Equal(t, true, found, "expected label Key or Value not found:[%s=%s]", expectedKey, expectedValue)
}

// verify expected annotations
for expectedKey, expectedValue := range expectedAnnotations {
found := false
for actualKey, actualValue := range namespace.GetAnnotations() {
if expectedKey == actualKey && expectedValue == actualValue {
found = true
break
}
}
assert.Equal(t, true, found, "expected annotation Key or Value not found:[%s=%s]", expectedKey, expectedValue)
}

// verify owner reference
Expand Down
2 changes: 1 addition & 1 deletion pkg/reconciler/kubernetes/tektonhub/tektonhub.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, th *v1alpha1.TektonHub)
th.SetDefaults(ctx)

// reconcile target namespace
if err := common.ReconcileTargetNamespace(ctx, nil, th, r.kubeClientSet); err != nil {
if err := common.ReconcileTargetNamespace(ctx, nil, nil, th, r.kubeClientSet); err != nil {
logger.Errorw("error on reconciling targetNamespace",
"targetNamespace", th.Spec.GetTargetNamespace(),
err,
Expand Down
2 changes: 1 addition & 1 deletion pkg/reconciler/kubernetes/tektonpipeline/reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, tp *v1alpha1.TektonPipel
tp.SetDefaults(ctx)

// reconcile target namespace
if err := common.ReconcileTargetNamespace(ctx, nil, tp, r.kubeClientSet); err != nil {
if err := common.ReconcileTargetNamespace(ctx, nil, nil, tp, r.kubeClientSet); err != nil {
return err
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/reconciler/openshift/tektonpipeline/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (oe openshiftExtension) PreReconcile(ctx context.Context, comp v1alpha1.Tek
}

// reconcile namespace with updated labels
return common.ReconcileTargetNamespace(ctx, labels, comp, oe.kubeClientSet)
return common.ReconcileTargetNamespace(ctx, labels, nil, comp, oe.kubeClientSet)
}

func (oe openshiftExtension) PostReconcile(ctx context.Context, comp v1alpha1.TektonComponent) error {
Expand Down
8 changes: 7 additions & 1 deletion pkg/reconciler/shared/tektonconfig/tektonconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,13 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, tc *v1alpha1.TektonConfi
}

// reconcile target namespace
if err := common.ReconcileTargetNamespace(ctx, nil, tc, r.kubeClientSet); err != nil {
nsMetaLabels := map[string]string{}
nsMetaAnnotations := map[string]string{}
if tc.Spec.TargetNamespaceMetadata != nil {
nsMetaLabels = tc.Spec.TargetNamespaceMetadata.Labels
nsMetaAnnotations = tc.Spec.TargetNamespaceMetadata.Annotations
}
if err := common.ReconcileTargetNamespace(ctx, nsMetaLabels, nsMetaAnnotations, tc, r.kubeClientSet); err != nil {
return err
}

Expand Down