From 6884fd713ecfa1edb9a39bc4dabc89126ad69a54 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Fri, 24 May 2024 20:34:53 -0600 Subject: [PATCH] Adding support for OpenShift securityContext Adding autodetection of solr-operator running on an OpenShift cluster to remove the default Solr fsGroup, and have an empty securityContext on OpenShift. Fixes #466 --- controllers/autodetect.go | 70 +++++++++++++++++++++++++++++ controllers/solrcloud_controller.go | 5 ++- controllers/suite_test.go | 5 ++- controllers/util/solr_util.go | 21 ++++----- main.go | 21 ++++++++- 5 files changed, 106 insertions(+), 16 deletions(-) create mode 100644 controllers/autodetect.go diff --git a/controllers/autodetect.go b/controllers/autodetect.go new file mode 100644 index 00000000..4a36bd77 --- /dev/null +++ b/controllers/autodetect.go @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Copied from grafana-operator +// With the Apache License: https://github.com/grafana/grafana-operator/blob/master/LICENSE +// See: https://github.com/grafana/grafana-operator/blob/master/controllers/autodetect/main.go +// Package autodetect is for auto-detecting traits from the environment (platform, APIs, ...). +package controllers + +import ( + "k8s.io/client-go/discovery" + "k8s.io/client-go/rest" +) + +var _ AutoDetect = (*autoDetect)(nil) + +// AutoDetect provides an assortment of routines that auto-detect traits based on the runtime. +type AutoDetect interface { + IsOpenshift() (bool, error) +} + +type autoDetect struct { + dcl discovery.DiscoveryInterface +} + +// New creates a new auto-detection worker, using the given client when talking to the current cluster. +func NewAutodetect(restConfig *rest.Config) (AutoDetect, error) { + dcl, err := discovery.NewDiscoveryClientForConfig(restConfig) + if err != nil { + // it's pretty much impossible to get into this problem, as most of the + // code branches from the previous call just won't fail at all, + // but let's handle this error anyway... + return nil, err + } + + return &autoDetect{ + dcl: dcl, + }, nil +} + +// Platform returns the detected platform this operator is running on. Possible values: Kubernetes, OpenShift. +func (a *autoDetect) IsOpenshift() (bool, error) { + apiList, err := a.dcl.ServerGroups() + if err != nil { + return false, err + } + + apiGroups := apiList.Groups + for i := range apiGroups { + if apiGroups[i].Name == "route.openshift.io" { + return true, nil + } + } + + return false, nil +} diff --git a/controllers/solrcloud_controller.go b/controllers/solrcloud_controller.go index b18dbd14..fcc3b910 100644 --- a/controllers/solrcloud_controller.go +++ b/controllers/solrcloud_controller.go @@ -53,7 +53,8 @@ import ( // SolrCloudReconciler reconciles a SolrCloud object type SolrCloudReconciler struct { client.Client - Scheme *runtime.Scheme + Scheme *runtime.Scheme + IsOpenShift bool } var useZkCRD bool @@ -328,7 +329,7 @@ func (r *SolrCloudReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( var statefulSet *appsv1.StatefulSet if !blockReconciliationOfStatefulSet { // Generate StatefulSet that should exist - expectedStatefulSet := util.GenerateStatefulSet(instance, &newStatus, hostNameIpMap, reconcileConfigInfo, tls, security) + expectedStatefulSet := util.GenerateStatefulSet(instance, &newStatus, hostNameIpMap, reconcileConfigInfo, tls, security, r.IsOpenShift) // Check if the StatefulSet already exists statefulSetLogger := logger.WithValues("statefulSet", expectedStatefulSet.Name) diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 4d49ef58..9e55f55b 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -106,8 +106,9 @@ var _ = BeforeSuite(func(ctx context.Context) { // Start up Reconcilers By("starting the reconcilers") Expect((&SolrCloudReconciler{ - Client: k8sManager.GetClient(), - Scheme: k8sManager.GetScheme(), + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + IsOpenShift: false, }).SetupWithManager(k8sManager)).To(Succeed()) Expect((&SolrPrometheusExporterReconciler{ diff --git a/controllers/util/solr_util.go b/controllers/util/solr_util.go index 50fdbc48..a65e6707 100644 --- a/controllers/util/solr_util.go +++ b/controllers/util/solr_util.go @@ -84,7 +84,7 @@ var ( // replicas: the number of replicas for the SolrCloud instance // storage: the size of the storage for the SolrCloud instance (e.g. 100Gi) // zkConnectionString: the connectionString of the ZK instance to connect to -func GenerateStatefulSet(solrCloud *solr.SolrCloud, solrCloudStatus *solr.SolrCloudStatus, hostNameIPs map[string]string, reconcileConfigInfo map[string]string, tls *TLSCerts, security *SecurityConfig) *appsv1.StatefulSet { +func GenerateStatefulSet(solrCloud *solr.SolrCloud, solrCloudStatus *solr.SolrCloudStatus, hostNameIPs map[string]string, reconcileConfigInfo map[string]string, tls *TLSCerts, security *SecurityConfig, isOpenShift bool) *appsv1.StatefulSet { terminationGracePeriod := int64(60) shareProcessNamespace := false solrPodPort := solrCloud.Spec.SolrAddressability.PodPort @@ -549,19 +549,20 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud, solrCloudStatus *solr.SolrCl Spec: corev1.PodSpec{ TerminationGracePeriodSeconds: &terminationGracePeriod, ShareProcessNamespace: &shareProcessNamespace, - SecurityContext: &corev1.PodSecurityContext{ - FSGroup: &defaultFSGroup, - }, - Volumes: solrVolumes, - InitContainers: initContainers, - HostAliases: hostAliases, - Containers: containers, - ReadinessGates: podReadinessGates, + SecurityContext: &corev1.PodSecurityContext{}, + Volumes: solrVolumes, + InitContainers: initContainers, + HostAliases: hostAliases, + Containers: containers, + ReadinessGates: podReadinessGates, }, }, VolumeClaimTemplates: pvcs, }, } + if !isOpenShift { + stateful.Spec.Template.Spec.SecurityContext.FSGroup = &defaultFSGroup + } if solrCloud.UsesHeadlessService() { stateful.Spec.Template.Spec.Subdomain = solrCloud.HeadlessServiceName() } @@ -598,7 +599,7 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud, solrCloudStatus *solr.SolrCl if customPodOptions.PodSecurityContext != nil { stateful.Spec.Template.Spec.SecurityContext = customPodOptions.PodSecurityContext - if stateful.Spec.Template.Spec.SecurityContext.FSGroup == nil { + if stateful.Spec.Template.Spec.SecurityContext.FSGroup == nil && !isOpenShift { stateful.Spec.Template.Spec.SecurityContext.FSGroup = &defaultFSGroup } } diff --git a/main.go b/main.go index c4aee805..ef078f98 100644 --- a/main.go +++ b/main.go @@ -198,9 +198,26 @@ func main() { } } + // Fetch k8s api credentials and detect platform + restConfig := ctrl.GetConfigOrDie() + + autodetect, err := controllers.NewAutodetect(restConfig) + if err != nil { + setupLog.Error(err, "failed to setup auto-detect routine") + os.Exit(1) + } + + isOpenShift, err := autodetect.IsOpenshift() + setupLog.Info("autodetect", "isOpenShift", isOpenShift) + if err != nil { + setupLog.Error(err, "unable to detect the platform") + os.Exit(1) + } + if err = (&controllers.SolrCloudReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + IsOpenShift: isOpenShift, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "SolrCloud") os.Exit(1)