Skip to content
Open
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 cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,19 @@ func main() {
}
}

pluginNamespace, err := util.GetOperatorNamespace()
if err != nil {
setupLog.Error(err, "Error retrieving operator's running namespace")
os.Exit(1)
}

if util.IsOpenShiftCluster() {
if err = (&controllers.ReconcileGitopsService{
Client: client,
Scheme: mgr.GetScheme(),
DisableDefaultInstall: strings.ToLower(os.Getenv(common.DisableDefaultInstallEnvVar)) == "true",
CentralTLSProfile: profile,
PluginNamespace: pluginNamespace,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "GitopsService")
os.Exit(1)
Expand Down
69 changes: 52 additions & 17 deletions controllers/consoleplugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func getPluginPodSpec(crImagePullPolicy corev1.PullPolicy) corev1.PodSpec {
return podSpec
}

func pluginDeployment(crImagePullPolicy corev1.PullPolicy) *appsv1.Deployment {
func pluginDeployment(namespace string, crImagePullPolicy corev1.PullPolicy) *appsv1.Deployment {
podSpec := getPluginPodSpec(crImagePullPolicy)
template := corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -149,13 +149,13 @@ func pluginDeployment(crImagePullPolicy corev1.PullPolicy) *appsv1.Deployment {
return &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: gitopsPluginName,
Namespace: serviceNamespace,
Namespace: namespace,
Labels: map[string]string{
kubeAppLabelApp: gitopsPluginName,
kubeAppLabelComponent: gitopsPluginName,
kubeAppLabelInstance: gitopsPluginName,
kubeAppLabelPartOf: gitopsPluginName,
kubeAppLabelRuntimeNamespace: serviceNamespace,
kubeAppLabelRuntimeNamespace: namespace,
},
},
Spec: appsv1.DeploymentSpec{
Expand All @@ -170,7 +170,7 @@ func pluginDeployment(crImagePullPolicy corev1.PullPolicy) *appsv1.Deployment {
}
}

func consolePlugin() *consolev1.ConsolePlugin {
func consolePlugin(namespace string) *consolev1.ConsolePlugin {
return &consolev1.ConsolePlugin{
ObjectMeta: metav1.ObjectMeta{
Name: gitopsPluginName,
Expand All @@ -181,7 +181,7 @@ func consolePlugin() *consolev1.ConsolePlugin {
Type: consolev1.Service,
Service: &consolev1.ConsolePluginService{
Name: gitopsPluginName,
Namespace: serviceNamespace,
Namespace: namespace,
Port: servicePort,
BasePath: "/",
},
Expand All @@ -193,7 +193,7 @@ func consolePlugin() *consolev1.ConsolePlugin {
}
}

func pluginService() *corev1.Service {
func pluginService(namespace string) *corev1.Service {
spec := corev1.ServiceSpec{
Selector: map[string]string{
kubeAppLabelApp: gitopsPluginName,
Expand All @@ -209,7 +209,7 @@ func pluginService() *corev1.Service {
svc := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: gitopsPluginName,
Namespace: serviceNamespace,
Namespace: namespace,
Labels: map[string]string{
kubeAppLabelApp: gitopsPluginName,
kubeAppLabelComponent: gitopsPluginName,
Expand Down Expand Up @@ -251,11 +251,11 @@ ServerRoot "/etc/httpd"
SSLCertificateKeyFile "/etc/httpd-ssl/private/tls.key"
</VirtualHost>`, servicePort, servicePort)

func pluginConfigMap() *corev1.ConfigMap {
func pluginConfigMap(namespace string) *corev1.ConfigMap {
cm := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: httpdConfigMapName,
Namespace: serviceNamespace,
Namespace: namespace,
Labels: map[string]string{
kubeAppLabelApp: gitopsPluginName,
kubeAppLabelPartOf: gitopsPluginName,
Expand Down Expand Up @@ -339,7 +339,7 @@ func sortTolerations(tolerations []corev1.Toleration) []corev1.Toleration {

func (r *ReconcileGitopsService) reconcileDeployment(cr *pipelinesv1alpha1.GitopsService, request reconcile.Request) (reconcile.Result, error) {
reqLogger := logs.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
newPluginDeployment := pluginDeployment(cr.Spec.ImagePullPolicy)
newPluginDeployment := pluginDeployment(r.PluginNamespace, cr.Spec.ImagePullPolicy)

if err := controllerutil.SetControllerReference(cr, newPluginDeployment, r.Scheme); err != nil {
return reconcile.Result{}, err
Expand Down Expand Up @@ -412,7 +412,7 @@ func (r *ReconcileGitopsService) reconcileDeployment(cr *pipelinesv1alpha1.Gitop

func (r *ReconcileGitopsService) reconcileService(instance *pipelinesv1alpha1.GitopsService, request reconcile.Request) (reconcile.Result, error) {
reqLogger := logs.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
pluginServiceRef := pluginService()
pluginServiceRef := pluginService(r.PluginNamespace)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// Set GitopsService instance as the owner and controller
if err := controllerutil.SetControllerReference(instance, pluginServiceRef, r.Scheme); err != nil {
return reconcile.Result{}, err
Expand Down Expand Up @@ -444,15 +444,15 @@ func (r *ReconcileGitopsService) reconcileService(instance *pipelinesv1alpha1.Gi
existingServiceRef.Labels = pluginServiceRef.Labels
existingServiceRef.Spec.Selector = pluginServiceRef.Spec.Selector
existingServiceRef.Spec.Ports = pluginServiceRef.Spec.Ports
return reconcile.Result{}, r.Client.Update(context.TODO(), pluginServiceRef)
return reconcile.Result{}, r.Client.Update(context.TODO(), existingServiceRef)
}
}
return reconcile.Result{}, nil
}

func (r *ReconcileGitopsService) reconcileConsolePlugin(instance *pipelinesv1alpha1.GitopsService, request reconcile.Request) (reconcile.Result, error) {
reqLogger := logs.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
newConsolePlugin := consolePlugin()
newConsolePlugin := consolePlugin(r.PluginNamespace)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if err := controllerutil.SetControllerReference(instance, newConsolePlugin, r.Scheme); err != nil {
return reconcile.Result{}, err
Expand All @@ -463,7 +463,7 @@ func (r *ReconcileGitopsService) reconcileConsolePlugin(instance *pipelinesv1alp
if err := r.Client.Get(context.TODO(), types.NamespacedName{Name: gitopsPluginName},
existingPlugin); err != nil {
if errors.IsNotFound(err) {
reqLogger.Info("Creating a new ConsolePlugin", "Namespace", serviceNamespace, "Name", gitopsPluginName)
reqLogger.Info("Creating a new ConsolePlugin", "Namespace", r.PluginNamespace, "Name", gitopsPluginName)
err = r.Client.Create(context.TODO(), newConsolePlugin)
if err != nil {
reqLogger.Error(err, "Error creating a new console plugin",
Expand All @@ -481,15 +481,15 @@ func (r *ReconcileGitopsService) reconcileConsolePlugin(instance *pipelinesv1alp
reqLogger.Info("Reconciling Console Plugin", "Namespace", existingPlugin.Namespace, "Name", existingPlugin.Name)
existingPlugin.Spec.DisplayName = newConsolePlugin.Spec.DisplayName
existingPlugin.Spec.Backend.Service = newConsolePlugin.Spec.Backend.Service
return reconcile.Result{}, r.Client.Update(context.TODO(), newConsolePlugin)
return reconcile.Result{}, r.Client.Update(context.TODO(), existingPlugin)
}
}
return reconcile.Result{}, nil
}

func (r *ReconcileGitopsService) reconcileConfigMap(instance *pipelinesv1alpha1.GitopsService, request reconcile.Request) (reconcile.Result, error) {
reqLogger := logs.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
newPluginConfigMap := pluginConfigMap()
newPluginConfigMap := pluginConfigMap(r.PluginNamespace)

if err := controllerutil.SetControllerReference(instance, newPluginConfigMap, r.Scheme); err != nil {
return reconcile.Result{}, err
Expand All @@ -515,12 +515,47 @@ func (r *ReconcileGitopsService) reconcileConfigMap(instance *pipelinesv1alpha1.
reqLogger.Info("Reconciling plugin configMap", "Namespace", existingPluginConfigMap.Namespace, "Name", existingPluginConfigMap.Name)
existingPluginConfigMap.Data = newPluginConfigMap.Data
existingPluginConfigMap.Labels = newPluginConfigMap.Labels
return reconcile.Result{}, r.Client.Update(context.TODO(), newPluginConfigMap)
return reconcile.Result{}, r.Client.Update(context.TODO(), existingPluginConfigMap)
}
}
return reconcile.Result{}, nil
}

// cleanupOldPluginResources removes plugin resources from the old namespace (openshift-gitops) after they have been moved to the operator namespace, since owner references on the cluster-scoped GitopsService CR won't trigger garbage collection.
func (r *ReconcileGitopsService) cleanupOldPluginResources(ctx context.Context) error {
if r.PluginNamespace == serviceNamespace {
return nil
}

reqLogger := logs.WithValues()

oldDeploy := &appsv1.Deployment{}
if err := r.Client.Get(ctx, types.NamespacedName{Name: gitopsPluginName, Namespace: serviceNamespace}, oldDeploy); err == nil {
reqLogger.Info("Cleaning up old plugin Deployment from previous namespace", "Namespace", serviceNamespace)
if err := r.Client.Delete(ctx, oldDeploy); err != nil && !errors.IsNotFound(err) {
return fmt.Errorf("failed to delete old plugin Deployment from namespace %s: %w", serviceNamespace, err)
}
}

oldSvc := &corev1.Service{}
if err := r.Client.Get(ctx, types.NamespacedName{Name: gitopsPluginName, Namespace: serviceNamespace}, oldSvc); err == nil {
reqLogger.Info("Cleaning up old plugin Service from previous namespace", "Namespace", serviceNamespace)
if err := r.Client.Delete(ctx, oldSvc); err != nil && !errors.IsNotFound(err) {
return fmt.Errorf("failed to delete old plugin Service from namespace %s: %w", serviceNamespace, err)
}
}

oldCM := &corev1.ConfigMap{}
if err := r.Client.Get(ctx, types.NamespacedName{Name: httpdConfigMapName, Namespace: serviceNamespace}, oldCM); err == nil {
reqLogger.Info("Cleaning up old plugin ConfigMap from previous namespace", "Namespace", serviceNamespace)
if err := r.Client.Delete(ctx, oldCM); err != nil && !errors.IsNotFound(err) {
return fmt.Errorf("failed to delete old plugin ConfigMap from namespace %s: %w", serviceNamespace, err)
}
}

return nil
}

// is this func the reconciler enty point to reconcile the current plugin state?
func (r *ReconcileGitopsService) reconcilePlugin(instance *pipelinesv1alpha1.GitopsService, request reconcile.Request) (reconcile.Result, error) {
reqLogger := logs.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
Expand Down
2 changes: 1 addition & 1 deletion controllers/consoleplugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
)

func TestPlugin(t *testing.T) {
testConsolePlugin := consolePlugin()
testConsolePlugin := consolePlugin(serviceNamespace)

testDisplayName := displayName
assert.Equal(t, testConsolePlugin.Spec.DisplayName, testDisplayName)
Expand Down
36 changes: 34 additions & 2 deletions controllers/gitopsservice_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ type ReconcileGitopsService struct {
DisableDefaultInstall bool
//CentralTLSProfile contains MinVersion and CipherSuites
CentralTLSProfile configv1.TLSProfileSpec

// PluginNamespace is the namespace where console plugin resources (Deployment, Service, ConfigMap) are deployed.
// This is typically the operator's own namespace (e.g. "openshift-gitops-operator").
PluginNamespace string
}

// +kubebuilder:rbac:groups=config.openshift.io,resources=authentications,verbs=get;list;watch
Expand Down Expand Up @@ -299,6 +303,25 @@ func (r *ReconcileGitopsService) Reconcile(ctx context.Context, request reconcil
return result, err
}

if r.PluginNamespace != namespace {
pluginNS := &corev1.Namespace{}
err = r.Client.Get(ctx, types.NamespacedName{Name: r.PluginNamespace}, pluginNS)
if err != nil {
if errors.IsNotFound(err) {
reqLogger.Info("Creating plugin namespace", "Name", r.PluginNamespace)
pluginNS = &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: r.PluginNamespace},
}
err = r.Client.Create(ctx, pluginNS)
if err != nil {
return reconcile.Result{}, err
}
} else {
return reconcile.Result{}, err
}
}
}

dynamicPluginStartOCPVersion := os.Getenv(dynamicPluginStartOCPVersionEnv)
if dynamicPluginStartOCPVersion == "" {
dynamicPluginStartOCPVersion = common.DefaultDynamicPluginStartOCPVersion
Expand Down Expand Up @@ -330,9 +353,18 @@ func (r *ReconcileGitopsService) Reconcile(ctx context.Context, request reconcil
if realMajorVersion < startMajorVersion || (realMajorVersion == startMajorVersion && realMinorVersion < startMinorVersion) {
// Skip plugin reconciliation if real OCP version is less than dynamic plugin start OCP version
return reconcile.Result{}, nil
} else {
return r.reconcilePlugin(instance, request)
}

result, err := r.reconcilePlugin(instance, request)
if err != nil {
return result, err
}

if err := r.cleanupOldPluginResources(ctx); err != nil {
return reconcile.Result{}, err
}

return result, nil
}

// Detect the unsupported KAM components across Deployments , Routes , Services and deletes them to perform cleanup as KAM is no longer supported since 1.15
Expand Down
5 changes: 3 additions & 2 deletions controllers/gitopsservice_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1133,8 +1133,9 @@ func addKnownTypesToScheme(scheme *runtime.Scheme) {

func newReconcileGitOpsService(client client.Client, scheme *runtime.Scheme) *ReconcileGitopsService {
return &ReconcileGitopsService{
Client: client,
Scheme: scheme,
Client: client,
Scheme: scheme,
PluginNamespace: serviceNamespace,
}
}

Expand Down
18 changes: 17 additions & 1 deletion controllers/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ import (
)

const (
clusterVersionName = "version"
clusterVersionName = "version"
operatorPodNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
DefaultOperatorNamespace = "openshift-gitops-operator"
)

var (
Expand Down Expand Up @@ -294,3 +296,17 @@ func AddSeccompProfileForOpenShift(client client.Client, podspec *corev1.PodSpec
}
}
}

// GetOperatorNamespace returns the namespace the operator is running in by reading
// the serviceaccount namespace file. If the file is not found (e.g. running locally),
// it returns the default operator namespace and a nil error.
func GetOperatorNamespace() (string, error) {
data, err := os.ReadFile(operatorPodNamespacePath)
if err != nil {
if os.IsNotExist(err) {
return DefaultOperatorNamespace, nil
}
return "", fmt.Errorf("error retrieving operator namespace: %w", err)
}
return strings.TrimSpace(string(data)), nil
}
8 changes: 6 additions & 2 deletions test/e2e/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,13 @@ var _ = BeforeSuite(func() {
})
Expect(err).NotTo(HaveOccurred())

pluginNamespace, err := util.GetOperatorNamespace()
Expect(err).NotTo(HaveOccurred(), "Error retrieving operator's running namespace")

err = (&controllers.ReconcileGitopsService{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
PluginNamespace: pluginNamespace,
}).SetupWithManager(mgr)
Expect(err).NotTo(HaveOccurred())

Expand Down
4 changes: 4 additions & 0 deletions test/nondefaulte2e/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,14 @@ var _ = BeforeSuite(func() {
})
Expect(err).NotTo(HaveOccurred())

pluginNamespace, err := util.GetOperatorNamespace()
Expect(err).NotTo(HaveOccurred(), "Error retrieving operator's running namespace")

err = (&controllers.ReconcileGitopsService{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
DisableDefaultInstall: strings.ToLower(os.Getenv(common.DisableDefaultInstallEnvVar)) == "true",
PluginNamespace: pluginNamespace,
}).SetupWithManager(mgr)
Expect(err).NotTo(HaveOccurred())

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,21 +97,21 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() {

By("verifying the plugin's Deployment, ConfigMap, Secret, Service, and other resources have expected values")

depl := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "gitops-plugin", Namespace: "openshift-gitops"}}
depl := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "gitops-plugin", Namespace: "openshift-gitops-operator"}}
Eventually(depl, "3m", "5s").Should(k8sFixture.ExistByName())
Eventually(depl, "60s", "5s").Should(deploymentFixture.HaveReadyReplicas(1))

configMap := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "httpd-cfg", Namespace: "openshift-gitops"}}
configMap := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "httpd-cfg", Namespace: "openshift-gitops-operator"}}
Eventually(configMap).Should(k8sFixture.ExistByName())

Expect(configMap).To(
And(k8sFixture.HaveLabelWithValue("app", "gitops-plugin"),
k8sFixture.HaveLabelWithValue("app.kubernetes.io/part-of", "gitops-plugin")))

secret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "console-serving-cert", Namespace: "openshift-gitops"}}
secret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "console-serving-cert", Namespace: "openshift-gitops-operator"}}
Eventually(secret).Should(k8sFixture.ExistByName())

service := &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "gitops-plugin", Namespace: "openshift-gitops"}}
service := &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "gitops-plugin", Namespace: "openshift-gitops-operator"}}
Eventually(service).Should(k8sFixture.ExistByName())

match := false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() {
})

By("verifying console plugin deployment has ImagePullPolicy set to Always")
pluginDepl := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "gitops-plugin", Namespace: argoCD.Namespace}}
pluginDepl := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "gitops-plugin", Namespace: "openshift-gitops-operator"}}
Eventually(pluginDepl).Should(k8sFixture.ExistByName())
Eventually(func() bool {
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(pluginDepl), pluginDepl)
Expand Down Expand Up @@ -243,7 +243,7 @@ var _ = Describe("GitOps Operator Sequential E2E Tests", func() {
}, "3m", "5s").Should(BeTrue())

By("verifying plugin deployment defaults to IfNotPresent")
pluginDepl := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "gitops-plugin", Namespace: argoCD.Namespace}}
pluginDepl := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Name: "gitops-plugin", Namespace: "openshift-gitops-operator"}}
Eventually(pluginDepl).Should(k8sFixture.ExistByName())
Eventually(func() bool {
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(pluginDepl), pluginDepl)
Expand Down
Loading
Loading