diff --git a/operators/multiclusterobservability/bundle/manifests/multicluster-observability-operator.clusterserviceversion.yaml b/operators/multiclusterobservability/bundle/manifests/multicluster-observability-operator.clusterserviceversion.yaml index beb79f096..16b99d9c0 100644 --- a/operators/multiclusterobservability/bundle/manifests/multicluster-observability-operator.clusterserviceversion.yaml +++ b/operators/multiclusterobservability/bundle/manifests/multicluster-observability-operator.clusterserviceversion.yaml @@ -454,6 +454,14 @@ spec: - get - list - watch + - apiGroups: + - image.openshift.io + resources: + - imagestreams + verbs: + - get + - list + - watch - serviceAccountName: multicluster-observability-operator deployments: - label: diff --git a/operators/multiclusterobservability/controllers/multiclusterobservability/multiclusterobservability_controller.go b/operators/multiclusterobservability/controllers/multiclusterobservability/multiclusterobservability_controller.go index 04648beea..25837e4f0 100644 --- a/operators/multiclusterobservability/controllers/multiclusterobservability/multiclusterobservability_controller.go +++ b/operators/multiclusterobservability/controllers/multiclusterobservability/multiclusterobservability_controller.go @@ -8,6 +8,8 @@ import ( "context" cerr "errors" "fmt" + imagev1 "github.com/openshift/api/image/v1" + imagev1client "github.com/openshift/client-go/image/clientset/versioned/typed/image/v1" "os" "reflect" "strings" @@ -79,13 +81,14 @@ var ( // MultiClusterObservabilityReconciler reconciles a MultiClusterObservability object type MultiClusterObservabilityReconciler struct { - Manager manager.Manager - Client client.Client - Log logr.Logger - Scheme *runtime.Scheme - CRDMap map[string]bool - APIReader client.Reader - RESTMapper meta.RESTMapper + Manager manager.Manager + Client client.Client + Log logr.Logger + Scheme *runtime.Scheme + CRDMap map[string]bool + APIReader client.Reader + RESTMapper meta.RESTMapper + ImageClient *imagev1client.ImageV1Client } // +kubebuilder:rbac:groups=observability.open-cluster-management.io,resources=multiclusterobservabilities,verbs=get;list;watch;create;update;patch;delete @@ -249,8 +252,9 @@ func (r *MultiClusterObservabilityReconciler) Reconcile(ctx context.Context, req return ctrl.Result{}, err } instance.Spec.StorageConfig.StorageClass = storageClassSelected + // Render the templates with a specified CR - renderer := rendering.NewMCORenderer(instance, r.Client) + renderer := rendering.NewMCORenderer(instance, r.Client, r.ImageClient) toDeploy, err := renderer.Render() if err != nil { reqLogger.Error(err, "Failed to render multiClusterMonitoring templates") @@ -446,6 +450,7 @@ func (r *MultiClusterObservabilityReconciler) SetupWithManager(mgr ctrl.Manager) cmPred := GetConfigMapPredicateFunc() secretPred := GetAlertManagerSecretPredicateFunc() namespacePred := GetNamespacePredicateFunc() + imageStreamPred := GetImageStreamPredicateFunc() ctrBuilder := ctrl.NewControllerManagedBy(mgr). // Watch for changes to primary resource MultiClusterObservability with predicate @@ -469,6 +474,9 @@ func (r *MultiClusterObservabilityReconciler) SetupWithManager(mgr ctrl.Manager) // Watch the namespace for changes Watches(&corev1.Namespace{}, &handler.EnqueueRequestForObject{}, builder.WithPredicates(namespacePred)). + // Watch the imagestream for changes + Watches(&imagev1.ImageStream{}, &handler.EnqueueRequestForObject{}, + builder.WithPredicates(imageStreamPred)). // Watch the kube-system extension-apiserver-authentication ConfigMap for changes Watches(&corev1.ConfigMap{}, handler.EnqueueRequestsFromMapFunc( func(ctx context.Context, a client.Object) []reconcile.Request { diff --git a/operators/multiclusterobservability/controllers/multiclusterobservability/predicate_func.go b/operators/multiclusterobservability/controllers/multiclusterobservability/predicate_func.go index 9f195f228..bd2f27f10 100644 --- a/operators/multiclusterobservability/controllers/multiclusterobservability/predicate_func.go +++ b/operators/multiclusterobservability/controllers/multiclusterobservability/predicate_func.go @@ -189,3 +189,20 @@ func GetNamespacePredicateFunc() predicate.Funcs { }, } } + +func GetImageStreamPredicateFunc() predicate.Funcs { + return predicate.Funcs{ + CreateFunc: func(e event.CreateEvent) bool { + return e.Object.GetName() == config.OauthProxyImageStreamName + }, + UpdateFunc: func(e event.UpdateEvent) bool { + if e.ObjectNew.GetName() != config.OauthProxyImageStreamName { + return false + } + return e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration() + }, + DeleteFunc: func(e event.DeleteEvent) bool { + return false + }, + } +} diff --git a/operators/multiclusterobservability/main.go b/operators/multiclusterobservability/main.go index c1ac8b7d1..165c4917a 100644 --- a/operators/multiclusterobservability/main.go +++ b/operators/multiclusterobservability/main.go @@ -8,6 +8,8 @@ import ( "crypto/tls" "flag" "fmt" + imagev1client "github.com/openshift/client-go/image/clientset/versioned/typed/image/v1" + "k8s.io/apimachinery/pkg/api/meta" "os" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) @@ -281,14 +283,34 @@ func main() { config.MCGHCrdName: mcghCrdExists, } + if _, err := mgr.GetRESTMapper().KindFor(schema.GroupVersionResource{ + Group: "image.openshift.io", + Version: "v1", + Resource: "imagestreams", + }); err != nil { + if meta.IsNoMatchError(err) { + setupLog.Info("image.openshift.io/v1/imagestreams is not available") + } else { + setupLog.Error(err, "failed to get kind for image.openshift.io/v1/imagestreams") + os.Exit(1) + } + } + + imageClient, err := imagev1client.NewForConfig(ctrl.GetConfigOrDie()) + if err != nil { + setupLog.Error(err, "failed to create openshift image client") + os.Exit(1) + } + if err = (&mcoctrl.MultiClusterObservabilityReconciler{ - Manager: mgr, - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("MultiClusterObservability"), - Scheme: mgr.GetScheme(), - CRDMap: crdMaps, - APIReader: mgr.GetAPIReader(), - RESTMapper: mgr.GetRESTMapper(), + Manager: mgr, + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("MultiClusterObservability"), + Scheme: mgr.GetScheme(), + CRDMap: crdMaps, + APIReader: mgr.GetAPIReader(), + RESTMapper: mgr.GetRESTMapper(), + ImageClient: imageClient, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "MultiClusterObservability") os.Exit(1) diff --git a/operators/multiclusterobservability/pkg/config/config.go b/operators/multiclusterobservability/pkg/config/config.go index 7e41d8ccb..d4591057a 100644 --- a/operators/multiclusterobservability/pkg/config/config.go +++ b/operators/multiclusterobservability/pkg/config/config.go @@ -7,8 +7,10 @@ package config import ( "context" "fmt" + imagev1client "github.com/openshift/client-go/image/clientset/versioned/typed/image/v1" "net/url" "os" + "reflect" "strings" "time" @@ -148,11 +150,6 @@ const ( KubeRBACProxyKey = "kube_rbac_proxy" KubeRBACProxyImgName = "kube-rbac-proxy" - OauthProxyImgRepo = "quay.io/stolostron" - OauthProxyImgName = "origin-oauth-proxy" - OauthProxyImgTagSuffix = "2.0.12-SNAPSHOT-2021-06-11-19-40-10" - OauthProxyKey = "oauth_proxy" - EndpointControllerImgName = "endpoint-monitoring-operator" EndpointControllerKey = "endpoint_monitoring_operator" @@ -162,7 +159,7 @@ const ( ObservatoriumAPI = "observatorium-api" ThanosCompact = "thanos-compact" ThanosQuery = "thanos-query" - ThanosQueryFrontend = "thanos-query-frontend" + ThanosQueryFrontend = "THANOS-QUERY-FRONTEND" ThanosQueryFrontendMemcached = "thanos-query-frontend-memcached" ThanosRule = "thanos-rule" ThanosReceive = "thanos-receive-default" @@ -213,6 +210,11 @@ const ( HubEndpointSaName = "endpoint-observability-operator-sa" ) +const ( + OauthProxyImageStreamName = "oauth-proxy" + OauthProxyImageStreamNamespace = "openshift" +) + // ObjectStorgeConf is used to Unmarshal from bytes to do validation. type ObjectStorgeConf struct { Type string `yaml:"type"` @@ -819,3 +821,27 @@ func IsAlertingDisabledInSpec(mco *observabilityv1beta2.MultiClusterObservabilit annotations := mco.GetAnnotations() return annotations != nil && annotations[AnnotationDisableMCOAlerting] == "true" } + +func GetOauthProxyImage(imageClient imagev1client.ImageV1Interface) (bool, string) { + if imageClient != nil && !reflect.ValueOf(imageClient).IsNil() { + // set oauth-proxy from imagestream.image.openshift.io + oauthImageStream, err := imageClient.ImageStreams(OauthProxyImageStreamNamespace). + Get(context.TODO(), OauthProxyImageStreamName, v1.GetOptions{}) + if err != nil { + if !errors.IsNotFound(err) { + return false, "" + } + // do not expect error = IsNotFound in OCP environment. + // But for e2e test, it can be. for this case, just ignore + } else { + if oauthImageStream.Spec.Tags != nil { + tag := oauthImageStream.Spec.Tags[0] + if tag.From != nil && tag.From.Kind == "DockerImage" && len(tag.From.Name) > 0 { + return true, tag.From.Name + } + } + } + } + return false, "" + +} diff --git a/operators/multiclusterobservability/pkg/rendering/renderer.go b/operators/multiclusterobservability/pkg/rendering/renderer.go index 7771fa6dc..0b06bca2a 100644 --- a/operators/multiclusterobservability/pkg/rendering/renderer.go +++ b/operators/multiclusterobservability/pkg/rendering/renderer.go @@ -5,6 +5,7 @@ package rendering import ( + imagev1client "github.com/openshift/client-go/image/clientset/versioned/typed/image/v1" v1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -24,6 +25,7 @@ var log = logf.Log.WithName("renderer") type MCORenderer struct { kubeClient client.Client + imageClient *imagev1client.ImageV1Client renderer *rendererutil.Renderer cr *obv1beta2.MultiClusterObservability renderGrafanaFns map[string]rendererutil.RenderFn @@ -32,11 +34,12 @@ type MCORenderer struct { renderProxyFns map[string]rendererutil.RenderFn } -func NewMCORenderer(multipleClusterMonitoring *obv1beta2.MultiClusterObservability, kubeClient client.Client) *MCORenderer { +func NewMCORenderer(multipleClusterMonitoring *obv1beta2.MultiClusterObservability, kubeClient client.Client, imageClient *imagev1client.ImageV1Client) *MCORenderer { mcoRenderer := &MCORenderer{ - renderer: rendererutil.NewRenderer(), - cr: multipleClusterMonitoring, - kubeClient: kubeClient, + renderer: rendererutil.NewRenderer(), + cr: multipleClusterMonitoring, + kubeClient: kubeClient, + imageClient: imageClient, } mcoRenderer.newGranfanaRenderer() mcoRenderer.newAlertManagerRenderer() diff --git a/operators/multiclusterobservability/pkg/rendering/renderer_alertmanager.go b/operators/multiclusterobservability/pkg/rendering/renderer_alertmanager.go index 188396eaa..d490488de 100644 --- a/operators/multiclusterobservability/pkg/rendering/renderer_alertmanager.go +++ b/operators/multiclusterobservability/pkg/rendering/renderer_alertmanager.go @@ -100,10 +100,8 @@ func (r *MCORenderer) renderAlertManagerStatefulSet(res *resource.Resource, if found { spec.Containers[1].Image = image } - // the oauth-proxy image only exists in mch-image-manifest configmap - // pass nil annotation to make sure oauth-proxy overrided from mch-image-manifest - found, image = mcoconfig.ReplaceImage(nil, mcoconfig.OauthProxyImgRepo, - mcoconfig.OauthProxyKey) + + found, image = mcoconfig.GetOauthProxyImage(r.imageClient) if found { spec.Containers[2].Image = image } diff --git a/operators/multiclusterobservability/pkg/rendering/renderer_alertmanager_test.go b/operators/multiclusterobservability/pkg/rendering/renderer_alertmanager_test.go index c04c4de91..26e7abc10 100644 --- a/operators/multiclusterobservability/pkg/rendering/renderer_alertmanager_test.go +++ b/operators/multiclusterobservability/pkg/rendering/renderer_alertmanager_test.go @@ -237,7 +237,7 @@ func renderTemplates(t *testing.T, kubeClient client.Client, mco *mcov1beta2.Mul defer os.Unsetenv(templatesutil.TemplatesPathEnvVar) config.ReadImageManifestConfigMap(kubeClient, "v1") - renderer := NewMCORenderer(mco, kubeClient) + renderer := NewMCORenderer(mco, kubeClient, nil) //load and render alertmanager templates alertTemplates, err := templates.GetOrLoadAlertManagerTemplates(templatesutil.GetTemplateRenderer()) diff --git a/operators/multiclusterobservability/pkg/rendering/renderer_grafana.go b/operators/multiclusterobservability/pkg/rendering/renderer_grafana.go index 36962fea5..d60e8af23 100644 --- a/operators/multiclusterobservability/pkg/rendering/renderer_grafana.go +++ b/operators/multiclusterobservability/pkg/rendering/renderer_grafana.go @@ -68,8 +68,7 @@ func (r *MCORenderer) renderGrafanaDeployments(res *resource.Resource, } spec.Containers[1].ImagePullPolicy = imagePullPolicy - found, image = config.ReplaceImage(nil, config.OauthProxyImgRepo, - config.OauthProxyKey) + found, image = config.GetOauthProxyImage(r.imageClient) if found { spec.Containers[2].Image = image } diff --git a/operators/multiclusterobservability/pkg/rendering/renderer_proxy.go b/operators/multiclusterobservability/pkg/rendering/renderer_proxy.go index 3416639c1..4c9ddf3e5 100644 --- a/operators/multiclusterobservability/pkg/rendering/renderer_proxy.go +++ b/operators/multiclusterobservability/pkg/rendering/renderer_proxy.go @@ -93,10 +93,7 @@ func (r *MCORenderer) renderProxyDeployment(res *resource.Resource, spec.Containers[0].Image = image } - // the oauth-proxy image only exists in mch-image-manifest configmap - // pass nil annotation to make sure oauth-proxy overrided from mch-image-manifest - found, image = mcoconfig.ReplaceImage(nil, mcoconfig.OauthProxyImgRepo, - mcoconfig.OauthProxyKey) + found, image = mcoconfig.GetOauthProxyImage(r.imageClient) if found { spec.Containers[1].Image = image } diff --git a/operators/multiclusterobservability/pkg/rendering/renderer_test.go b/operators/multiclusterobservability/pkg/rendering/renderer_test.go index 50acac4f1..41eb3811d 100644 --- a/operators/multiclusterobservability/pkg/rendering/renderer_test.go +++ b/operators/multiclusterobservability/pkg/rendering/renderer_test.go @@ -59,7 +59,7 @@ func TestRender(t *testing.T) { } kubeClient := fake.NewClientBuilder().WithObjects(clientCa).Build() - renderer := NewMCORenderer(mchcr, kubeClient) + renderer := NewMCORenderer(mchcr, kubeClient, nil) _, err = renderer.Render() if err != nil { t.Fatalf("failed to render MultiClusterObservability: %v", err)