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

Introduce the controllers.namespaceLabels value #2048

Merged
merged 1 commit into from
Jan 10, 2023
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
1 change: 1 addition & 0 deletions README.helm.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Here are all the values that can be set for the chart:
- `controllers`:
- `image` (_String_): Reference to the controllers container image.
- `include` (_Boolean_): Deploy the controllers component.
- `namespaceLabels`: Key value pairs that are going to be set as labels in the workload namespaces created by Korifi
- `processDefaults`:
- `diskQuotaMB` (_Integer_): Default disk quota for the `web` process.
- `memoryMB` (_Integer_): Default memory limit for the `web` process.
Expand Down
1 change: 1 addition & 0 deletions controllers/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type ControllerConfig struct {
WorkloadsTLSSecretNamespace string `yaml:"workloads_tls_secret_namespace"`
BuilderName string `yaml:"builderName"`
RunnerName string `yaml:"runnerName"`
NamespaceLabels map[string]string `yaml:"namespaceLabels"`
}

type CFProcessDefaults struct {
Expand Down
1 change: 1 addition & 0 deletions controllers/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ var _ = Describe("LoadFromPath", func() {
WorkloadsTLSSecretNamespace: "workloadsTLSSecretNamespace",
BuilderName: "buildReconciler",
RunnerName: "statefulset-runner",
NamespaceLabels: map[string]string{},
}))
})

Expand Down
29 changes: 19 additions & 10 deletions controllers/controllers/workloads/cforg_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/source"

korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1"
"code.cloudfoundry.org/korifi/controllers/controllers/workloads/labels"
"code.cloudfoundry.org/korifi/tools/k8s"
)

Expand All @@ -44,17 +45,23 @@ type CFOrgReconciler struct {
scheme *runtime.Scheme
log logr.Logger
containerRegistrySecretName string
labelCompiler labels.Compiler
}

func NewCFOrgReconciler(client client.Client, scheme *runtime.Scheme, log logr.Logger, containerRegistrySecretName string) *k8s.PatchingReconciler[korifiv1alpha1.CFOrg, *korifiv1alpha1.CFOrg] {
orgReconciler := CFOrgReconciler{
func NewCFOrgReconciler(
client client.Client,
scheme *runtime.Scheme,
log logr.Logger,
containerRegistrySecretName string,
labelCompiler labels.Compiler,
) *k8s.PatchingReconciler[korifiv1alpha1.CFOrg, *korifiv1alpha1.CFOrg] {
return k8s.NewPatchingReconciler[korifiv1alpha1.CFOrg, *korifiv1alpha1.CFOrg](log, client, &CFOrgReconciler{
client: client,
scheme: scheme,
log: log,
containerRegistrySecretName: containerRegistrySecretName,
}

return k8s.NewPatchingReconciler[korifiv1alpha1.CFOrg, *korifiv1alpha1.CFOrg](log, client, &orgReconciler)
labelCompiler: labelCompiler,
})
}

const (
Expand Down Expand Up @@ -106,21 +113,24 @@ func (r *CFOrgReconciler) ReconcileResource(ctx context.Context, cfOrg *korifiv1
return ctrl.Result{}, err
}

cfOrg.Status.GUID = cfOrg.Name

getConditionOrSetAsUnknown(&cfOrg.Status.Conditions, korifiv1alpha1.ReadyConditionType)

if !cfOrg.GetDeletionTimestamp().IsZero() {
return r.finalize(ctx, log, cfOrg)
}

labels := map[string]string{korifiv1alpha1.OrgNameLabel: cfOrg.Spec.DisplayName}
err := createOrPatchNamespace(ctx, r.client, log, cfOrg, labels)
err := createOrPatchNamespace(ctx, r.client, log, cfOrg, r.labelCompiler.Compile(map[string]string{
korifiv1alpha1.OrgNameLabel: cfOrg.Spec.DisplayName,
}))
if err != nil {
log.Error(err, "Error creating namespace")
return ctrl.Result{}, err
}

namespace, ok := getNamespace(ctx, log, r.client, cfOrg.Name)
if !ok {
err = getNamespace(ctx, log, r.client, cfOrg.Name)
if err != nil {
return ctrl.Result{RequeueAfter: 100 * time.Millisecond}, nil
}

Expand All @@ -136,7 +146,6 @@ func (r *CFOrgReconciler) ReconcileResource(ctx context.Context, cfOrg *korifiv1
return ctrl.Result{}, err
}

cfOrg.Status.GUID = namespace.Name
meta.SetStatusCondition(&cfOrg.Status.Conditions, metav1.Condition{
Type: StatusConditionReady,
Status: metav1.ConditionTrue,
Expand Down
25 changes: 15 additions & 10 deletions controllers/controllers/workloads/cfspace_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@ import (
"time"

korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1"
"code.cloudfoundry.org/korifi/controllers/controllers/workloads/labels"
"code.cloudfoundry.org/korifi/tools/k8s"

"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
k8s_labels "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
Expand All @@ -52,6 +53,7 @@ type CFSpaceReconciler struct {
log logr.Logger
containerRegistrySecretName string
rootNamespace string
labelCompiler labels.Compiler
}

func NewCFSpaceReconciler(
Expand All @@ -60,15 +62,16 @@ func NewCFSpaceReconciler(
log logr.Logger,
containerRegistrySecretName string,
rootNamespace string,
labelCompiler labels.Compiler,
) *k8s.PatchingReconciler[korifiv1alpha1.CFSpace, *korifiv1alpha1.CFSpace] {
spaceReconciler := CFSpaceReconciler{
return k8s.NewPatchingReconciler[korifiv1alpha1.CFSpace, *korifiv1alpha1.CFSpace](log, client, &CFSpaceReconciler{
client: client,
scheme: scheme,
log: log,
containerRegistrySecretName: containerRegistrySecretName,
rootNamespace: rootNamespace,
}
return k8s.NewPatchingReconciler[korifiv1alpha1.CFSpace, *korifiv1alpha1.CFSpace](log, client, &spaceReconciler)
labelCompiler: labelCompiler,
})
}

//+kubebuilder:rbac:groups=korifi.cloudfoundry.org,resources=cfspaces,verbs=get;list;watch;create;update;patch;delete
Expand Down Expand Up @@ -97,21 +100,24 @@ func (r *CFSpaceReconciler) ReconcileResource(ctx context.Context, cfSpace *kori
return ctrl.Result{}, err
}

cfSpace.Status.GUID = cfSpace.GetName()

getConditionOrSetAsUnknown(&cfSpace.Status.Conditions, korifiv1alpha1.ReadyConditionType)

if !cfSpace.GetDeletionTimestamp().IsZero() {
return r.finalize(ctx, log, cfSpace)
}

labels := map[string]string{korifiv1alpha1.SpaceNameLabel: cfSpace.Spec.DisplayName}
err := createOrPatchNamespace(ctx, r.client, log, cfSpace, labels)
err := createOrPatchNamespace(ctx, r.client, log, cfSpace, r.labelCompiler.Compile(map[string]string{
korifiv1alpha1.SpaceNameLabel: cfSpace.Spec.DisplayName,
}))
if err != nil {
log.Error(err, "Error creating namespace")
return ctrl.Result{}, err
}

namespace, ok := getNamespace(ctx, log, r.client, cfSpace.Name)
if !ok {
err = getNamespace(ctx, log, r.client, cfSpace.Name)
if err != nil {
return ctrl.Result{RequeueAfter: 100 * time.Millisecond}, nil
}

Expand All @@ -133,7 +139,6 @@ func (r *CFSpaceReconciler) ReconcileResource(ctx context.Context, cfSpace *kori
return ctrl.Result{}, err
}

cfSpace.Status.GUID = namespace.Name
meta.SetStatusCondition(&cfSpace.Status.Conditions, metav1.Condition{
Type: StatusConditionReady,
Status: metav1.ConditionTrue,
Expand Down Expand Up @@ -220,7 +225,7 @@ func (r *CFSpaceReconciler) reconcileServiceAccounts(ctx context.Context, space
}

propagatedServiceAccounts := new(corev1.ServiceAccountList)
labelSelector, err := labels.ValidatedSelectorFromSet(map[string]string{
labelSelector, err := k8s_labels.ValidatedSelectorFromSet(map[string]string{
korifiv1alpha1.PropagatedFromLabel: r.rootNamespace,
})
if err != nil {
Expand Down
7 changes: 3 additions & 4 deletions controllers/controllers/workloads/cfspace_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import (
"context"
"time"

korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1"
. "code.cloudfoundry.org/korifi/controllers/controllers/workloads/testutils"
"code.cloudfoundry.org/korifi/tools/k8s"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"
Expand All @@ -15,10 +18,6 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/pod-security-admission/api"
"sigs.k8s.io/controller-runtime/pkg/client"

korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1"
. "code.cloudfoundry.org/korifi/controllers/controllers/workloads/testutils"
"code.cloudfoundry.org/korifi/tools/k8s"
)

var _ = Describe("CFSpaceReconciler Integration Tests", func() {
Expand Down
44 changes: 44 additions & 0 deletions controllers/controllers/workloads/labels/compiler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package labels

// Compiler is a reusable map composer. It persists a set of defaults, which
// can be overridden when calling Compile() to produce the combined map.
type Compiler struct {
defaults map[string]string
}

func NewCompiler() Compiler {
return Compiler{
defaults: map[string]string{},
}
}

func (o Compiler) Defaults(defaults map[string]string) Compiler {
defaultsCopy := copyMap(o.defaults)
for k, v := range defaults {
defaultsCopy[k] = v
}
return Compiler{
defaults: defaultsCopy,
}
}

func (o Compiler) Compile(overrides map[string]string) map[string]string {
res := map[string]string{}
for k, v := range o.defaults {
res[k] = v
}
for k, v := range overrides {
res[k] = v
}

return res
}

func copyMap(src map[string]string) map[string]string {
dst := map[string]string{}
for k, v := range src {
dst[k] = v
}

return dst
}
13 changes: 13 additions & 0 deletions controllers/controllers/workloads/labels/compiler_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package labels_test

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestLabels(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Labels Suite")
}
84 changes: 84 additions & 0 deletions controllers/controllers/workloads/labels/compiler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package labels_test

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"code.cloudfoundry.org/korifi/controllers/controllers/workloads/labels"
)

var _ = Describe("Labels", func() {
var (
compiler labels.Compiler
override map[string]string
output map[string]string
)

BeforeEach(func() {
override = nil
compiler = labels.NewCompiler()
})

JustBeforeEach(func() {
output = compiler.Compile(override)
})

It("will return empty if no defaults or override given", func() {
Expect(output).To(BeEmpty())
})

When("default values are provided", func() {
BeforeEach(func() {
compiler = compiler.Defaults(map[string]string{
"foo": "bar",
})
})

It("puts the default in the output", func() {
Expect(output).To(HaveKeyWithValue("foo", "bar"))
})
})

When("default values are provided twice", func() {
var oldCompiler labels.Compiler

BeforeEach(func() {
oldCompiler = compiler.Defaults(map[string]string{
"foo": "bar",
"hello": "there",
})
compiler = oldCompiler.Defaults(map[string]string{
"foo": "baz",
})
})

It("puts the latest default in the output", func() {
Expect(output).To(HaveKeyWithValue("foo", "baz"))
Expect(output).To(HaveKeyWithValue("hello", "there"))
})

It("is immutable", func() {
Expect(oldCompiler.Compile(nil)).To(HaveKeyWithValue("foo", "bar"))
Expect(oldCompiler.Compile(nil)).To(HaveKeyWithValue("hello", "there"))
})
})

When("a default value is overridden", func() {
BeforeEach(func() {
compiler = compiler.Defaults(map[string]string{
"foo": "bar",
})
override = map[string]string{
"foo": "baz",
}
})

It("will use overridden value", func() {
Expect(output).To(HaveKeyWithValue("foo", "baz"))
})

It("will not accidently store the override", func() {
Expect(compiler.Compile(nil)).To(HaveKeyWithValue("foo", "bar"))
})
})
})
10 changes: 3 additions & 7 deletions controllers/controllers/workloads/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,11 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"k8s.io/pod-security-admission/api"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate

func createOrPatchNamespace(ctx context.Context, client client.Client, log logr.Logger, orgOrSpace client.Object, labels map[string]string) error {
log = log.WithName("createOrPatchNamespace")

Expand All @@ -35,8 +33,6 @@ func createOrPatchNamespace(ctx context.Context, client client.Client, log logr.
for key, value := range labels {
namespace.Labels[key] = value
}
namespace.Labels[api.EnforceLevelLabel] = string(api.LevelRestricted)
namespace.Labels[api.AuditLevelLabel] = string(api.LevelRestricted)

return nil
})
Expand Down Expand Up @@ -168,14 +164,14 @@ func reconcileRoleBindings(ctx context.Context, kClient client.Client, log logr.
return nil
}

func getNamespace(ctx context.Context, log logr.Logger, client client.Client, namespaceName string) (*corev1.Namespace, bool) {
func getNamespace(ctx context.Context, log logr.Logger, client client.Client, namespaceName string) error {
log = log.WithValues("namespace", namespaceName)

namespace := new(corev1.Namespace)
err := client.Get(ctx, types.NamespacedName{Name: namespaceName}, namespace)
if err != nil {
log.Error(err, "failed to get namespace")
return nil, false
return err
}
return namespace, true
return nil
}
Loading