Skip to content
Draft
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 10 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,14 @@ import (
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"

argov1beta1api "github.com/argoproj-labs/argocd-operator/api/v1beta1"
argoapi "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
gitopsv1alpha1 "github.com/hybrid-cloud-patterns/patterns-operator/api/v1alpha1"
controllers "github.com/hybrid-cloud-patterns/patterns-operator/internal/controller"
"github.com/hybrid-cloud-patterns/patterns-operator/version"
apiv1 "github.com/openshift/api/config/v1"
operatorv1 "github.com/openshift/api/operator/v1"
routev1 "github.com/openshift/api/route/v1"
operatorv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
Expand All @@ -59,6 +64,11 @@ func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))

utilruntime.Must(gitopsv1alpha1.AddToScheme(scheme))
utilruntime.Must(apiv1.Install(scheme))
utilruntime.Must(operatorv1.Install(scheme))
utilruntime.Must(argoapi.AddToScheme(scheme))
utilruntime.Must(operatorv1alpha1.AddToScheme(scheme))
utilruntime.Must(routev1.AddToScheme(scheme))
//+kubebuilder:scaffold:scheme
}

Expand Down
4 changes: 4 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ rules:
resources:
- configmaps
verbs:
- create
- get
- list
- watch
- apiGroups:
- ""
resources:
Expand Down Expand Up @@ -53,6 +55,7 @@ rules:
verbs:
- get
- list
- watch
- apiGroups:
- gitops.hybrid-cloud-patterns.io
resources:
Expand Down Expand Up @@ -104,6 +107,7 @@ rules:
- list
- patch
- update
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
Expand Down
3 changes: 0 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ require (
github.com/onsi/ginkgo/v2 v2.25.2
github.com/onsi/gomega v1.38.2
github.com/openshift/api v3.9.1-0.20190916204813-cdbe64fb0c91+incompatible
github.com/openshift/client-go v0.0.0-20240115204758-e6bf7d631d5e // release-4.16
github.com/operator-framework/api v0.33.0
github.com/operator-framework/operator-lifecycle-manager v0.33.0
github.com/segmentio/analytics-go/v3 v3.3.0
go.uber.org/mock v0.6.0
golang.org/x/crypto v0.41.0
Expand Down Expand Up @@ -195,7 +193,6 @@ replace (
// Usually not needed unless we have newer k8s.io deps and argocd still uses an old gitops-engine
// github.com/argoproj/gitops-engine => github.com/argoproj/gitops-engine v0.0.0-20240905010810-bd7681ae3f8b
github.com/openshift/api => github.com/openshift/api v0.0.0-20240124164020-e2ce40831f2e
github.com/openshift/client-go => github.com/openshift/client-go v0.0.0-20240115204758-e6bf7d631d5e
// Caused by Argo importing 'k8s.io/api'
// Override all the v0.0.0 entries
k8s.io/api => k8s.io/api v0.33.4
Expand Down
12 changes: 4 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs=
github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA=
Expand Down Expand Up @@ -325,12 +325,8 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/openshift/api v0.0.0-20240124164020-e2ce40831f2e h1:cxgCNo/R769CO23AK5TCh45H9SMUGZ8RukiF2/Qif3o=
github.com/openshift/api v0.0.0-20240124164020-e2ce40831f2e/go.mod h1:CxgbWAlvu2iQB0UmKTtRu1YfepRg1/vJ64n2DlIEVz4=
github.com/openshift/client-go v0.0.0-20240115204758-e6bf7d631d5e h1:qGjfKX8i0h4efMNEnhgTdxcdx6gwwOwhTfBJ20WFqA8=
github.com/openshift/client-go v0.0.0-20240115204758-e6bf7d631d5e/go.mod h1:2am3qrggh9LlDCf/MDGzcFWMhdaushxFQi0+ZZDhdVk=
github.com/operator-framework/api v0.33.0 h1:Tdu9doXz6Key2riIiP3/JPahHEgFBXAqyWQN4kOITS8=
github.com/operator-framework/api v0.33.0/go.mod h1:sEh1VqwQCJUj+l/rKNWPDEJdFNAbdTu8QcM+x+wdYYo=
github.com/operator-framework/operator-lifecycle-manager v0.33.0 h1:KFBrln/E0itngxLNgP5CGQQoTf9whtudrLnZsxS2Ey4=
github.com/operator-framework/operator-lifecycle-manager v0.33.0/go.mod h1:FsL9ACmFMFfDIA/Y20ECuiT49d6gVFfcdebReqtIWyM=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
Expand Down Expand Up @@ -559,8 +555,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 h1:pmJpJEvT846VzausCQ5d7KreSROcDqmO388w5YbnltA=
Expand Down
42 changes: 25 additions & 17 deletions internal/controller/acm.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,32 @@ import (
"fmt"
"log"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func haveACMHub(r *PatternReconciler) bool {
gvrMCH := schema.GroupVersionResource{Group: "operator.open-cluster-management.io", Version: "v1", Resource: "multiclusterhubs"}
var mchGVK = schema.GroupVersionKind{
Group: "operator.open-cluster-management.io",
Kind: "multiclusterhubs",
Version: "v1",
}

serverNamespace := ""
func haveACMHub(cl client.Client) bool {
labelSelector, err := labels.Parse(fmt.Sprintf("%v = %v", "ocm-configmap-type", "image-manifest"))

cms, err := r.fullClient.CoreV1().ConfigMaps("").List(context.TODO(), metav1.ListOptions{
LabelSelector: fmt.Sprintf("%v = %v", "ocm-configmap-type", "image-manifest"),
})
if (err != nil || len(cms.Items) == 0) && serverNamespace != "" {
cms, err = r.fullClient.CoreV1().ConfigMaps(serverNamespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: fmt.Sprintf("%v = %v", "ocm-configmap-type", "image-manifest"),
})
}
if err != nil || len(cms.Items) == 0 {
cms, err = r.fullClient.CoreV1().ConfigMaps("open-cluster-management").List(context.TODO(), metav1.ListOptions{
LabelSelector: fmt.Sprintf("%v = %v", "ocm-configmap-type", "image-manifest"),
})
if err != nil {
log.Printf("config map error: %s\n", err.Error())
return false
}

cms := corev1.ConfigMapList{}
err = cl.List(context.Background(), &cms, &client.ListOptions{
LabelSelector: labelSelector,
})

if err != nil {
log.Printf("config map error: %s\n", err.Error())
return false
Expand All @@ -54,7 +58,11 @@ func haveACMHub(r *PatternReconciler) bool {
}
ns := cms.Items[0].Namespace

umch, err := r.dynamicClient.Resource(gvrMCH).Namespace(ns).List(context.TODO(), metav1.ListOptions{})
umch := &unstructured.UnstructuredList{}
umch.SetGroupVersionKind(mchGVK)

err = cl.List(context.Background(), umch, &client.ListOptions{Namespace: ns})

if err != nil {
log.Printf("Error obtaining hub: %s\n", err)
return false
Expand Down
138 changes: 60 additions & 78 deletions internal/controller/acm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,118 +4,100 @@ import (
"context"
"fmt"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
dynamicfake "k8s.io/client-go/dynamic/fake"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/testing"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/client/interceptor"

v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

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

var _ = Describe("HaveACMHub", func() {

var (
patternReconciler *PatternReconciler
kubeClient *fake.Clientset
dynamicClient *dynamicfake.FakeDynamicClient
gvrMCH schema.GroupVersionResource
fakeClient client.Client
configMap *v1.ConfigMap
hub *unstructured.Unstructured
)

BeforeEach(func() {
kubeClient = fake.NewSimpleClientset()
gvrMCH = schema.GroupVersionResource{Group: "operator.open-cluster-management.io", Version: "v1", Resource: "multiclusterhubs"}

dynamicClient = dynamicfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), map[schema.GroupVersionResource]string{
gvrMCH: "MultiClusterHubList",
})

patternReconciler = &PatternReconciler{
fullClient: kubeClient,
dynamicClient: dynamicClient,
}

})

Context("when the ACM Hub exists", func() {
BeforeEach(func() {
configMap := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-configmap",
Namespace: "default",
Labels: map[string]string{
"ocm-configmap-type": "image-manifest",
},
configMap = &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-configmap",
Namespace: "default",
Labels: map[string]string{
"ocm-configmap-type": "image-manifest",
},
}
_, err := kubeClient.CoreV1().ConfigMaps("default").Create(context.Background(), configMap, metav1.CreateOptions{})
Expect(err).ToNot(HaveOccurred())

hub := &unstructured.Unstructured{
Object: map[string]any{
"apiVersion": "operator.open-cluster-management.io/v1",
"kind": "MultiClusterHub",
"metadata": map[string]any{
"name": "test-hub",
"namespace": "default",
},
},
}
hub = &unstructured.Unstructured{
Object: map[string]any{
"apiVersion": "operator.open-cluster-management.io/v1",
"kind": "MultiClusterHub",
"metadata": map[string]any{
"name": "test-hub",
"namespace": "default",
},
}
_, err = dynamicClient.Resource(gvrMCH).Namespace("default").Create(context.Background(), hub, metav1.CreateOptions{})
Expect(err).ToNot(HaveOccurred())
})
},
}
hub.SetGroupVersionKind(mchGVK)
})

Context("when the ACM Hub exists in same ns as configmap", func() {
It("should return true", func() {
result := haveACMHub(patternReconciler)
fakeClient = fake.NewClientBuilder().WithScheme(testEnv.Scheme).
WithRuntimeObjects(configMap, hub).Build()

result := haveACMHub(fakeClient)
Expect(result).To(BeTrue())
})
})

Context("when the ACM Hub does not exist", func() {
Context("when the ACM Hub exists different ns as configmap", func() {
It("should return false", func() {
result := haveACMHub(patternReconciler)
hub.SetNamespace("different")

fakeClient = fake.NewClientBuilder().WithScheme(testEnv.Scheme).
WithRuntimeObjects(configMap, hub).Build()

result := haveACMHub(fakeClient)
Expect(result).To(BeFalse())
})
})

Context("when there is an error listing ConfigMaps", func() {
BeforeEach(func() {
kubeClient.PrependReactor("list", "configmaps", func(testing.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, fmt.Errorf("config map error")
})
})
Context("when the ACM Hub does not exist", func() {
It("should return false", func() {
fakeClient = fake.NewClientBuilder().WithScheme(testEnv.Scheme).
WithRuntimeObjects().Build()

It("should return false and log the error", func() {
result := haveACMHub(patternReconciler)
result := haveACMHub(fakeClient)
Expect(result).To(BeFalse())
})
})

Context("when there is an error listing the MultiClusterHubs", func() {
BeforeEach(func() {
configMap := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "test-configmap",
Namespace: "default",
Labels: map[string]string{
"ocm-configmap-type": "image-manifest",
},
},
}
_, err := kubeClient.CoreV1().ConfigMaps("default").Create(context.Background(), configMap, metav1.CreateOptions{})
Expect(err).ToNot(HaveOccurred())
Context("when there is an error listing ConfigMaps", func() {
It("should return false and log the error", func() {
fakeClient = fake.NewClientBuilder().WithInterceptorFuncs(
interceptor.Funcs{List: func(ctx context.Context, client client.WithWatch, obj client.ObjectList, opts ...client.ListOption) error {
return fmt.Errorf("list error")
}}).WithScheme(testEnv.Scheme).Build()

dynamicClient.PrependReactor("list", "multiclusterhubs", func(testing.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, fmt.Errorf("multiclusterhub error")
})
result := haveACMHub(fakeClient)
Expect(result).To(BeFalse())
})
})

Context("when there is an error listing the MultiClusterHubs", func() {
It("should return false and log the error", func() {
result := haveACMHub(patternReconciler)
fakeClient = fake.NewClientBuilder().WithInterceptorFuncs(
interceptor.Funcs{List: func(ctx context.Context, client client.WithWatch, list client.ObjectList, opts ...client.ListOption) error {
return fmt.Errorf("list error")
}}).WithScheme(testEnv.Scheme).WithRuntimeObjects(configMap).Build()

result := haveACMHub(fakeClient)
Expect(result).To(BeFalse())
})
})
Expand Down
Loading