Skip to content

Commit 1af8232

Browse files
feat(RHOAIENG-51619): add infrastructure part-of label for cache (#3231)
1 parent b4cdec9 commit 1af8232

File tree

15 files changed

+377
-111
lines changed

15 files changed

+377
-111
lines changed

cmd/cloudmanager/app/cache.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package app
2+
3+
import (
4+
"fmt"
5+
6+
rbacv1 "k8s.io/api/rbac/v1"
7+
extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
8+
"k8s.io/apimachinery/pkg/api/meta"
9+
k8slabels "k8s.io/apimachinery/pkg/labels"
10+
"k8s.io/apimachinery/pkg/runtime"
11+
"sigs.k8s.io/controller-runtime/pkg/cache"
12+
"sigs.k8s.io/controller-runtime/pkg/client"
13+
14+
"github.com/opendatahub-io/opendatahub-operator/v2/internal/controller/cloudmanager/common"
15+
"github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/labels"
16+
)
17+
18+
// DefaultCacheOptions builds cache.Options for the given scheme, watching the default
19+
// managed namespaces shared by all cloud managers and filtering cluster-scoped
20+
// resources by the infrastructure part-of label.
21+
func DefaultCacheOptions(scheme *runtime.Scheme, kind string) (cache.Options, error) {
22+
infraPartOfValue := labels.NormalizePartOfValue(kind)
23+
if infraPartOfValue == "" {
24+
return cache.Options{}, fmt.Errorf("infraPartOfValue must not be empty for label %s", labels.InfrastructurePartOf)
25+
}
26+
27+
nsConfig := make(map[string]cache.Config, len(common.ManagedNamespaces()))
28+
for _, ns := range common.ManagedNamespaces() {
29+
nsConfig[ns] = cache.Config{}
30+
}
31+
32+
labelSelector := k8slabels.Set{
33+
labels.InfrastructurePartOf: infraPartOfValue,
34+
}.AsSelector()
35+
36+
clusterScopedConfig := cache.ByObject{
37+
Label: labelSelector,
38+
}
39+
40+
return cache.Options{
41+
Scheme: scheme,
42+
DefaultNamespaces: nsConfig,
43+
ByObject: map[client.Object]cache.ByObject{
44+
&rbacv1.ClusterRole{}: clusterScopedConfig,
45+
&rbacv1.ClusterRoleBinding{}: clusterScopedConfig,
46+
&extv1.CustomResourceDefinition{}: clusterScopedConfig,
47+
},
48+
DefaultTransform: func(in any) (any, error) {
49+
// Nilcheck managed fields to avoid hitting https://github.com/kubernetes/kubernetes/issues/124337
50+
if obj, err := meta.Accessor(in); err == nil && obj.GetManagedFields() != nil {
51+
obj.SetManagedFields(nil)
52+
}
53+
54+
return in, nil
55+
},
56+
}, nil
57+
}

cmd/cloudmanager/app/cache_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package app_test
2+
3+
import (
4+
"testing"
5+
6+
rbacv1 "k8s.io/api/rbac/v1"
7+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8+
k8slabels "k8s.io/apimachinery/pkg/labels"
9+
"k8s.io/apimachinery/pkg/runtime"
10+
11+
"github.com/opendatahub-io/opendatahub-operator/v2/cmd/cloudmanager/app"
12+
"github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/labels"
13+
14+
. "github.com/onsi/gomega"
15+
)
16+
17+
func TestDefaultCacheOptions(t *testing.T) {
18+
g := NewWithT(t)
19+
s := runtime.NewScheme()
20+
21+
t.Run("label selector matches correct value", func(t *testing.T) {
22+
g := NewWithT(t)
23+
24+
opts, err := app.DefaultCacheOptions(s, "azurekubernetesengine")
25+
g.Expect(err).ShouldNot(HaveOccurred())
26+
27+
for obj, byObj := range opts.ByObject {
28+
matching := k8slabels.Set{
29+
labels.InfrastructurePartOf: "azurekubernetesengine",
30+
}
31+
g.Expect(byObj.Label.Matches(matching)).To(BeTrue(),
32+
"label selector for %T should match %v", obj, matching)
33+
34+
nonMatching := k8slabels.Set{
35+
labels.InfrastructurePartOf: "coreweavekubernetesengine",
36+
}
37+
g.Expect(byObj.Label.Matches(nonMatching)).To(BeFalse(),
38+
"label selector for %T should not match %v", obj, nonMatching)
39+
}
40+
})
41+
42+
t.Run("normalizes infraPartOfValue to lowercase and trimmed", func(t *testing.T) {
43+
g := NewWithT(t)
44+
45+
opts, err := app.DefaultCacheOptions(s, " AzureKubernetesEngine ")
46+
g.Expect(err).ShouldNot(HaveOccurred())
47+
48+
for obj, byObj := range opts.ByObject {
49+
matching := k8slabels.Set{
50+
labels.InfrastructurePartOf: "azurekubernetesengine",
51+
}
52+
g.Expect(byObj.Label.Matches(matching)).To(BeTrue(),
53+
"label selector for %T should match normalized value %v", obj, matching)
54+
}
55+
})
56+
57+
t.Run("returns error on empty infraPartOfValue", func(t *testing.T) {
58+
g := NewWithT(t)
59+
60+
_, err := app.DefaultCacheOptions(s, "")
61+
g.Expect(err).Should(HaveOccurred())
62+
})
63+
64+
t.Run("returns error on whitespace-only infraPartOfValue", func(t *testing.T) {
65+
g := NewWithT(t)
66+
67+
_, err := app.DefaultCacheOptions(s, " ")
68+
g.Expect(err).Should(HaveOccurred())
69+
})
70+
71+
t.Run("uses provided scheme", func(t *testing.T) {
72+
opts, err := app.DefaultCacheOptions(s, "azurekubernetesengine")
73+
g.Expect(err).ShouldNot(HaveOccurred())
74+
75+
g.Expect(opts.Scheme).To(Equal(s))
76+
})
77+
78+
t.Run("DefaultTransform clears ManagedFields", func(t *testing.T) {
79+
g := NewWithT(t)
80+
81+
opts, err := app.DefaultCacheOptions(s, "azurekubernetesengine")
82+
g.Expect(err).ShouldNot(HaveOccurred())
83+
g.Expect(opts.DefaultTransform).ShouldNot(BeNil())
84+
85+
obj := &rbacv1.ClusterRole{}
86+
obj.SetManagedFields([]metav1.ManagedFieldsEntry{{Manager: "test"}})
87+
88+
result, err := opts.DefaultTransform(obj)
89+
g.Expect(err).ShouldNot(HaveOccurred())
90+
91+
transformed, ok := result.(*rbacv1.ClusterRole)
92+
g.Expect(ok).To(BeTrue())
93+
g.Expect(transformed.GetManagedFields()).To(BeNil())
94+
})
95+
}

cmd/cloudmanager/app/provider.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type Provider struct {
2121
// NewReconciler creates and registers the provider's controller with the manager.
2222
NewReconciler func(ctx context.Context, mgr ctrl.Manager) error
2323
// CacheOptions returns the provider-specific cache configuration.
24-
CacheOptions func(scheme *runtime.Scheme) cache.Options
24+
CacheOptions func(scheme *runtime.Scheme) (cache.Options, error)
2525
// ClientOptions returns the provider-specific client configuration.
2626
// It always sets the unstructured cache to true.
2727
ClientOptions func() client.Options

cmd/cloudmanager/app/run.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@ import (
44
"fmt"
55

66
"github.com/spf13/cobra"
7+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
8+
"k8s.io/apimachinery/pkg/runtime"
9+
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
10+
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
711
ctrl "sigs.k8s.io/controller-runtime"
812
"sigs.k8s.io/controller-runtime/pkg/client"
913
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
1014
"sigs.k8s.io/controller-runtime/pkg/healthz"
1115
logf "sigs.k8s.io/controller-runtime/pkg/log"
1216
ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics/server"
1317

14-
"github.com/opendatahub-io/opendatahub-operator/v2/internal/controller/cloudmanager/common"
1518
"github.com/opendatahub-io/opendatahub-operator/v2/pkg/logger"
1619
"github.com/opendatahub-io/opendatahub-operator/v2/pkg/manager"
1720
"github.com/opendatahub-io/opendatahub-operator/v2/pkg/operatorconfig"
@@ -35,7 +38,7 @@ func Run(_ *cobra.Command, provider Provider) error {
3538
return fmt.Errorf("invalid provider configuration: %w", err)
3639
}
3740

38-
scheme := common.NewScheme(provider.AddToScheme)
41+
scheme := newScheme(provider.AddToScheme)
3942

4043
clientOptions := provider.ClientOptions()
4144
if clientOptions.Cache == nil {
@@ -44,6 +47,11 @@ func Run(_ *cobra.Command, provider Provider) error {
4447
// The unstructured cache must be used.
4548
clientOptions.Cache.Unstructured = true
4649

50+
cacheOptions, err := provider.CacheOptions(scheme)
51+
if err != nil {
52+
return fmt.Errorf("unable to get cache options: %w", err)
53+
}
54+
4755
mgrOpts := ctrl.Options{
4856
Scheme: scheme,
4957
Metrics: ctrlmetrics.Options{
@@ -55,7 +63,7 @@ func Run(_ *cobra.Command, provider Provider) error {
5563
HealthProbeBindAddress: cfg.HealthProbeAddr,
5664
LeaderElection: cfg.LeaderElection,
5765
LeaderElectionID: provider.LeaderElectionID,
58-
Cache: provider.CacheOptions(scheme),
66+
Cache: cacheOptions,
5967
Client: clientOptions,
6068
}
6169

@@ -86,3 +94,16 @@ func Run(_ *cobra.Command, provider Provider) error {
8694

8795
return nil
8896
}
97+
98+
func newScheme(addToSchemes ...func(*runtime.Scheme) error) *runtime.Scheme {
99+
scheme := runtime.NewScheme()
100+
101+
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
102+
utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
103+
104+
for _, addToScheme := range addToSchemes {
105+
utilruntime.Must(addToScheme(scheme))
106+
}
107+
108+
return scheme
109+
}

cmd/cloudmanager/azure/cmd.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
ccmv1alpha1 "github.com/opendatahub-io/opendatahub-operator/v2/api/cloudmanager/azure/v1alpha1"
99
"github.com/opendatahub-io/opendatahub-operator/v2/cmd/cloudmanager/app"
1010
azurectrl "github.com/opendatahub-io/opendatahub-operator/v2/internal/controller/cloudmanager/azure"
11-
"github.com/opendatahub-io/opendatahub-operator/v2/internal/controller/cloudmanager/common"
1211
)
1312

1413
// NewCmd returns the cobra command for the Azure cloud manager.
@@ -31,7 +30,7 @@ func NewCmd() *cobra.Command {
3130
return cmd
3231
}
3332

34-
func cacheOptions(scheme *runtime.Scheme) cache.Options {
35-
defaultCacheOptions := common.DefaultCacheOptions(scheme)
36-
return defaultCacheOptions
33+
func cacheOptions(scheme *runtime.Scheme) (cache.Options, error) {
34+
kind := ccmv1alpha1.AzureKubernetesEngineKind
35+
return app.DefaultCacheOptions(scheme, kind)
3736
}

cmd/cloudmanager/coreweave/cmd.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77

88
ccmv1alpha1 "github.com/opendatahub-io/opendatahub-operator/v2/api/cloudmanager/coreweave/v1alpha1"
99
"github.com/opendatahub-io/opendatahub-operator/v2/cmd/cloudmanager/app"
10-
"github.com/opendatahub-io/opendatahub-operator/v2/internal/controller/cloudmanager/common"
1110
coreweavectrl "github.com/opendatahub-io/opendatahub-operator/v2/internal/controller/cloudmanager/coreweave"
1211
)
1312

@@ -31,7 +30,7 @@ func NewCmd() *cobra.Command {
3130
return cmd
3231
}
3332

34-
func cacheOptions(scheme *runtime.Scheme) cache.Options {
35-
defaultCacheOptions := common.DefaultCacheOptions(scheme)
36-
return defaultCacheOptions
33+
func cacheOptions(scheme *runtime.Scheme) (cache.Options, error) {
34+
kind := ccmv1alpha1.CoreWeaveKubernetesEngineKind
35+
return app.DefaultCacheOptions(scheme, kind)
3736
}

internal/controller/cloudmanager/azure/azurekubernetesengine_controller_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
ccmv1alpha1 "github.com/opendatahub-io/opendatahub-operator/v2/api/cloudmanager/azure/v1alpha1"
1010
ccmcommon "github.com/opendatahub-io/opendatahub-operator/v2/api/cloudmanager/common"
1111
"github.com/opendatahub-io/opendatahub-operator/v2/pkg/cluster/gvk"
12+
"github.com/opendatahub-io/opendatahub-operator/v2/pkg/metadata/labels"
1213
"github.com/opendatahub-io/opendatahub-operator/v2/pkg/utils/test/matchers/jq"
1314
"github.com/opendatahub-io/opendatahub-operator/v2/pkg/utils/test/testf"
1415

@@ -45,6 +46,26 @@ func TestAzureKubernetesEngine(t *testing.T) {
4546
Name: "servicemesh-operator3", Namespace: "istio-system",
4647
}).Eventually().Should(Not(BeNil()))
4748
})
49+
50+
t.Run("sets infrastructure label on deployed resources", func(t *testing.T) {
51+
wt := tc.NewWithT(t)
52+
53+
createAzureCR(t, wt, ccmcommon.Dependencies{
54+
CertManager: ccmcommon.CertManagerDependency{ManagementPolicy: ccmcommon.Managed},
55+
})
56+
57+
nn := types.NamespacedName{Name: ccmv1alpha1.AzureKubernetesEngineInstanceName}
58+
59+
wt.Get(gvk.AzureKubernetesEngine, nn).Eventually().Should(
60+
jq.Match(`.status.conditions[] | select(.type == "Ready") | .status == "True"`),
61+
)
62+
63+
wt.Get(gvk.Deployment, types.NamespacedName{
64+
Name: "cert-manager-operator-controller-manager", Namespace: "cert-manager-operator",
65+
}).Eventually().Should(
66+
jq.Match(`.metadata.labels."%s" == "azurekubernetesengine"`, labels.InfrastructurePartOf),
67+
)
68+
})
4869
}
4970

5071
func createAzureCR(t *testing.T, wt *testf.WithT, deps ccmcommon.Dependencies) {

internal/controller/cloudmanager/common/cache.go

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)