From f194a2ae5a76d3684c168024d8a97b5274838b4c Mon Sep 17 00:00:00 2001 From: Sergey Dudoladov Date: Wed, 7 Feb 2018 14:02:32 +0100 Subject: [PATCH 01/15] Introduce changes from the PR #200 by @alexeyklyukin --- pkg/apiserver/apiserver.go | 21 ++++++++++++--------- pkg/cluster/cluster.go | 4 ++++ pkg/cluster/resources.go | 2 +- pkg/cluster/sync.go | 1 + pkg/controller/node.go | 2 +- pkg/controller/pod.go | 4 ++-- pkg/controller/postgresql.go | 4 ++-- pkg/controller/status.go | 27 +++++++++++++++++++++------ 8 files changed, 44 insertions(+), 21 deletions(-) diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 2b0b2f98e..41dfed1ac 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -30,9 +30,9 @@ type controllerInformer interface { GetOperatorConfig() *config.Config GetStatus() *spec.ControllerStatus TeamClusterList() map[string][]spec.NamespacedName - ClusterStatus(team, cluster string) (*spec.ClusterStatus, error) - ClusterLogs(team, cluster string) ([]*spec.LogEntry, error) - ClusterHistory(team, cluster string) ([]*spec.Diff, error) + ClusterStatus(team, namespace, cluster string) (*spec.ClusterStatus, error) + ClusterLogs(team, namespace, cluster string) ([]*spec.LogEntry, error) + ClusterHistory(team, namespace, cluster string) ([]*spec.Diff, error) ClusterDatabasesMap() map[string][]string WorkerLogs(workerID uint32) ([]*spec.LogEntry, error) ListQueue(workerID uint32) (*spec.QueueDump, error) @@ -48,9 +48,9 @@ type Server struct { } var ( - clusterStatusURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)/(?P[a-zA-Z][a-zA-Z0-9-]*)/?$`) - clusterLogsURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)/(?P[a-zA-Z][a-zA-Z0-9-]*)/logs/?$`) - clusterHistoryURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)/(?P[a-zA-Z][a-zA-Z0-9-]*)/history/?$`) + clusterStatusURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)(/(?P[a-zA-Z][a-zA-Z0-9-]*))?/(?P[a-zA-Z][a-zA-Z0-9-]*)/?$`) + clusterLogsURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)(/(?P[a-zA-Z][a-zA-Z0-9-]*))?/(?P[a-zA-Z][a-zA-Z0-9-]*)/logs/?$`) + clusterHistoryURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)(/(?P[a-zA-Z][a-zA-Z0-9-]*))?/(?P[a-zA-Z][a-zA-Z0-9-]*)/history/?$`) teamURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)/?$`) workerLogsURL = regexp.MustCompile(`^/workers/(?P\d+)/logs/?$`) workerEventsQueueURL = regexp.MustCompile(`^/workers/(?P\d+)/queue/?$`) @@ -149,7 +149,8 @@ func (s *Server) clusters(w http.ResponseWriter, req *http.Request) { ) if matches := util.FindNamedStringSubmatch(clusterStatusURL, req.URL.Path); matches != nil { - resp, err = s.controller.ClusterStatus(matches["team"], matches["cluster"]) + namespace, _ := matches["namespace"] + resp, err = s.controller.ClusterStatus(matches["team"], namespace, matches["cluster"]) } else if matches := util.FindNamedStringSubmatch(teamURL, req.URL.Path); matches != nil { teamClusters := s.controller.TeamClusterList() clusters, found := teamClusters[matches["team"]] @@ -166,9 +167,11 @@ func (s *Server) clusters(w http.ResponseWriter, req *http.Request) { s.respond(clusterNames, nil, w) return } else if matches := util.FindNamedStringSubmatch(clusterLogsURL, req.URL.Path); matches != nil { - resp, err = s.controller.ClusterLogs(matches["team"], matches["cluster"]) + namespace, _ := matches["namespace"] + resp, err = s.controller.ClusterLogs(matches["team"], namespace, matches["cluster"]) } else if matches := util.FindNamedStringSubmatch(clusterHistoryURL, req.URL.Path); matches != nil { - resp, err = s.controller.ClusterHistory(matches["team"], matches["cluster"]) + namespace, _ := matches["namespace"] + resp, err = s.controller.ClusterHistory(matches["team"], namespace, matches["cluster"]) } else if req.URL.Path == clustersURL { res := make(map[string][]string) for team, clusters := range s.controller.TeamClusterList() { diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 19d8b6048..6dcf92eed 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -127,6 +127,10 @@ func (c *Cluster) clusterName() spec.NamespacedName { return util.NameFromMeta(c.ObjectMeta) } +func (c *Cluster) clusterNamespace() string { + return c.ObjectMeta.Namespace +} + func (c *Cluster) teamName() string { // TODO: check Teams API for the actual name (in case the user passes an integer Id). return c.Spec.TeamID diff --git a/pkg/cluster/resources.go b/pkg/cluster/resources.go index 4f770aaff..16d27b14b 100644 --- a/pkg/cluster/resources.go +++ b/pkg/cluster/resources.go @@ -110,7 +110,7 @@ func (c *Cluster) preScaleDown(newStatefulSet *v1beta1.StatefulSet) error { } podName := fmt.Sprintf("%s-0", c.Statefulset.Name) - masterCandidatePod, err := c.KubeClient.Pods(c.OpConfig.WatchedNamespace).Get(podName, metav1.GetOptions{}) + masterCandidatePod, err := c.KubeClient.Pods(c.clusterNamespace()).Get(podName, metav1.GetOptions{}) if err != nil { return fmt.Errorf("could not get master candidate pod: %v", err) } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 864f2a833..26388eede 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -24,6 +24,7 @@ func (c *Cluster) Sync(newSpec *spec.Postgresql) (err error) { defer func() { if err != nil { + c.logger.Warningf("error while syncing cluster state: %v", err) c.setStatus(spec.ClusterStatusSyncFailed) } else if c.Status != spec.ClusterStatusRunning { c.setStatus(spec.ClusterStatusRunning) diff --git a/pkg/controller/node.go b/pkg/controller/node.go index a4a558a2c..efa1ca5c2 100644 --- a/pkg/controller/node.go +++ b/pkg/controller/node.go @@ -80,7 +80,7 @@ func (c *Controller) moveMasterPodsOffNode(node *v1.Node) { opts := metav1.ListOptions{ LabelSelector: labels.Set(c.opConfig.ClusterLabels).String(), } - podList, err := c.KubeClient.Pods(c.opConfig.WatchedNamespace).List(opts) + podList, err := c.KubeClient.Pods("").List(opts) if err != nil { c.logger.Errorf("could not fetch list of the pods: %v", err) return diff --git a/pkg/controller/pod.go b/pkg/controller/pod.go index 070e6da2d..35b23cc52 100644 --- a/pkg/controller/pod.go +++ b/pkg/controller/pod.go @@ -17,7 +17,7 @@ func (c *Controller) podListFunc(options metav1.ListOptions) (runtime.Object, er TimeoutSeconds: options.TimeoutSeconds, } - return c.KubeClient.Pods(c.opConfig.WatchedNamespace).List(opts) + return c.KubeClient.Pods("").List(opts) } func (c *Controller) podWatchFunc(options metav1.ListOptions) (watch.Interface, error) { @@ -27,7 +27,7 @@ func (c *Controller) podWatchFunc(options metav1.ListOptions) (watch.Interface, TimeoutSeconds: options.TimeoutSeconds, } - return c.KubeClient.Pods(c.opConfig.WatchedNamespace).Watch(opts) + return c.KubeClient.Pods("").Watch(opts) } func (c *Controller) dispatchPodEvent(clusterName spec.NamespacedName, event spec.PodEvent) { diff --git a/pkg/controller/postgresql.go b/pkg/controller/postgresql.go index 25b4617a8..9e0b1c207 100644 --- a/pkg/controller/postgresql.go +++ b/pkg/controller/postgresql.go @@ -46,7 +46,7 @@ func (c *Controller) clusterListFunc(options metav1.ListOptions) (runtime.Object req := c.KubeClient.CRDREST. Get(). - Namespace(c.opConfig.WatchedNamespace). + Namespace(""). Resource(constants.CRDResource). VersionedParams(&options, metav1.ParameterCodec) @@ -110,7 +110,7 @@ func (c *Controller) clusterWatchFunc(options metav1.ListOptions) (watch.Interfa options.Watch = true r, err := c.KubeClient.CRDREST. Get(). - Namespace(c.opConfig.WatchedNamespace). + Namespace(""). Resource(constants.CRDResource). VersionedParams(&options, metav1.ParameterCodec). FieldsSelectorParam(nil). diff --git a/pkg/controller/status.go b/pkg/controller/status.go index 6aef0347c..932b77500 100644 --- a/pkg/controller/status.go +++ b/pkg/controller/status.go @@ -14,9 +14,14 @@ import ( ) // ClusterStatus provides status of the cluster -func (c *Controller) ClusterStatus(team, cluster string) (*spec.ClusterStatus, error) { +func (c *Controller) ClusterStatus(team, namespace, cluster string) (*spec.ClusterStatus, error) { + + if namespace == "" { + namespace = c.opConfig.WatchedNamespace + } + clusterName := spec.NamespacedName{ - Namespace: c.opConfig.WatchedNamespace, + Namespace: namespace, Name: team + "-" + cluster, } @@ -90,9 +95,14 @@ func (c *Controller) GetStatus() *spec.ControllerStatus { } // ClusterLogs dumps cluster ring logs -func (c *Controller) ClusterLogs(team, name string) ([]*spec.LogEntry, error) { +func (c *Controller) ClusterLogs(team, namespace, name string) ([]*spec.LogEntry, error) { + + if namespace == "" { + namespace = c.opConfig.WatchedNamespace + } + clusterName := spec.NamespacedName{ - Namespace: c.opConfig.WatchedNamespace, + Namespace: namespace, Name: team + "-" + name, } @@ -212,9 +222,14 @@ func (c *Controller) WorkerStatus(workerID uint32) (*spec.WorkerStatus, error) { } // ClusterHistory dumps history of cluster changes -func (c *Controller) ClusterHistory(team, name string) ([]*spec.Diff, error) { +func (c *Controller) ClusterHistory(team, namespace, name string) ([]*spec.Diff, error) { + + if namespace == "" { + namespace = c.opConfig.WatchedNamespace + } + clusterName := spec.NamespacedName{ - Namespace: c.opConfig.WatchedNamespace, + Namespace: namespace, Name: team + "-" + name, } From 74fa7b949230c683098a76e6f178af774e4de585 Mon Sep 17 00:00:00 2001 From: Sergey Dudoladov Date: Wed, 7 Feb 2018 16:44:49 +0100 Subject: [PATCH 02/15] Restrict operator to single watched namespace via env var --- pkg/controller/controller.go | 34 +++++++++++++++++++++++++--------- pkg/controller/node.go | 2 +- pkg/controller/pod.go | 4 ++-- pkg/controller/postgresql.go | 2 +- pkg/util/config/config.go | 2 +- 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index c697217e4..f905ade50 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -97,18 +97,34 @@ func (c *Controller) initOperatorConfig() { c.logger.Infoln("no ConfigMap specified. Loading default values") } - // env var takes priority over the same param from the operator ConfigMap - watchedNamespace := os.Getenv("WATCHED_NAMESPACE") - if watchedNamespace != "" { - c.logger.Infof("Watch the %q namespace specified in the env variable WATCHED_NAMESPACE\n", watchedNamespace) - configMapData["watched_namespace"] = watchedNamespace + // by default, the operator listens to all namespaces + // by setting the env variable, one can restrict the operator to a single namespace + watchedNamespace, isPresentInEnv := os.LookupEnv("WATCHED_NAMESPACE") + if isPresentInEnv { + // special case: v1.NamespaceAll currently also evaluates to the empty string + // so when the env var is set to the empty string, use the default ns + // since the meaning of this env var is only one namespace + if watchedNamespace == "" { + c.logger.Infof("The WATCHED_NAMESPACE env var evaluates to the empty string, falling back to watching the 'default' namespace.\n", watchedNamespace) + configMapData["watched_namespace"] = v1.NamespaceDefault + } else { + c.logger.Infof("Watch the %q namespace specified in the env variable WATCHED_NAMESPACE\n", watchedNamespace) + configMapData["watched_namespace"] = watchedNamespace + } + + } else { + c.logger.Infof("Watch all namespaces. Set the WATCHED_NAMESPACE env var to restrict to a single namespace.\n", watchedNamespace) + configMapData["watched_namespace"] = v1.NamespaceAll } - if configMapData["watched_namespace"] == "" { - c.logger.Infoln("No namespace to watch specified. Fall back to watching the 'default' namespace.") - configMapData["watched_namespace"] = v1.NamespaceDefault - } + /* + // env var takes priority over the same param from the operator ConfigMap + if configMapData["watched_namespace"] == "" { + c.logger.Infoln("No namespace to watch specified. Fall back to watching the 'default' namespace.") + configMapData["watched_namespace"] = v1.NamespaceDefault + } + */ if c.config.NoDatabaseAccess { configMapData["enable_database_access"] = "false" } diff --git a/pkg/controller/node.go b/pkg/controller/node.go index efa1ca5c2..a4a558a2c 100644 --- a/pkg/controller/node.go +++ b/pkg/controller/node.go @@ -80,7 +80,7 @@ func (c *Controller) moveMasterPodsOffNode(node *v1.Node) { opts := metav1.ListOptions{ LabelSelector: labels.Set(c.opConfig.ClusterLabels).String(), } - podList, err := c.KubeClient.Pods("").List(opts) + podList, err := c.KubeClient.Pods(c.opConfig.WatchedNamespace).List(opts) if err != nil { c.logger.Errorf("could not fetch list of the pods: %v", err) return diff --git a/pkg/controller/pod.go b/pkg/controller/pod.go index 35b23cc52..070e6da2d 100644 --- a/pkg/controller/pod.go +++ b/pkg/controller/pod.go @@ -17,7 +17,7 @@ func (c *Controller) podListFunc(options metav1.ListOptions) (runtime.Object, er TimeoutSeconds: options.TimeoutSeconds, } - return c.KubeClient.Pods("").List(opts) + return c.KubeClient.Pods(c.opConfig.WatchedNamespace).List(opts) } func (c *Controller) podWatchFunc(options metav1.ListOptions) (watch.Interface, error) { @@ -27,7 +27,7 @@ func (c *Controller) podWatchFunc(options metav1.ListOptions) (watch.Interface, TimeoutSeconds: options.TimeoutSeconds, } - return c.KubeClient.Pods("").Watch(opts) + return c.KubeClient.Pods(c.opConfig.WatchedNamespace).Watch(opts) } func (c *Controller) dispatchPodEvent(clusterName spec.NamespacedName, event spec.PodEvent) { diff --git a/pkg/controller/postgresql.go b/pkg/controller/postgresql.go index 9e0b1c207..f83779a21 100644 --- a/pkg/controller/postgresql.go +++ b/pkg/controller/postgresql.go @@ -46,7 +46,7 @@ func (c *Controller) clusterListFunc(options metav1.ListOptions) (runtime.Object req := c.KubeClient.CRDREST. Get(). - Namespace(""). + Namespace(c.opConfig.WatchedNamespace). Resource(constants.CRDResource). VersionedParams(&options, metav1.ParameterCodec) diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 3253d6288..0bd2b1edf 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -67,7 +67,7 @@ type Config struct { Resources Auth Scalyr - WatchedNamespace string `name:"watched_namespace"` + WatchedNamespace string `name:"watched_namespace"` // may be v1.NamespaceAll, meaning watch all namespaces EtcdHost string `name:"etcd_host" default:"etcd-client.default.svc.cluster.local:2379"` DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spiloprivate-9.6:1.2-p4"` ServiceAccountName string `name:"service_account_name" default:"operator"` From de2a0285923f46ceb504e98ba6e277d23224ba8b Mon Sep 17 00:00:00 2001 From: Sergey Dudoladov Date: Wed, 7 Feb 2018 17:43:05 +0100 Subject: [PATCH 03/15] Warn if the watched namespace does not exist --- pkg/controller/controller.go | 10 ++++++++++ pkg/util/k8sutil/k8sutil.go | 2 ++ 2 files changed, 12 insertions(+) diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index f905ade50..944033143 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -143,6 +143,16 @@ func (c *Controller) initOperatorConfig() { func (c *Controller) initController() { c.initClients() c.initOperatorConfig() + + // earliest point where we can check if the namespace to watch actually exists + if c.opConfig.WatchedNamespace != v1.NamespaceAll { + _, err := c.KubeClient.Namespaces().Get(c.opConfig.WatchedNamespace, metav1.GetOptions{}) + if err != nil { + c.logger.Warnf("Operator was told to watch the %q namespace but was unable to confirm it existense via Kubernetes API. Falling back to watching all namespaces instead (done automatically by k8s)", c.opConfig.WatchedNamespace) + } + + } + c.initSharedInformers() c.logger.Infof("config: %s", c.opConfig.MustMarshal()) diff --git a/pkg/util/k8sutil/k8sutil.go b/pkg/util/k8sutil/k8sutil.go index 1b4dc9a00..5f40148c4 100644 --- a/pkg/util/k8sutil/k8sutil.go +++ b/pkg/util/k8sutil/k8sutil.go @@ -32,6 +32,7 @@ type KubernetesClient struct { v1core.PersistentVolumeClaimsGetter v1core.ConfigMapsGetter v1core.NodesGetter + v1core.NamespacesGetter v1beta1.StatefulSetsGetter policyv1beta1.PodDisruptionBudgetsGetter apiextbeta1.CustomResourceDefinitionsGetter @@ -76,6 +77,7 @@ func NewFromConfig(cfg *rest.Config) (KubernetesClient, error) { kubeClient.PersistentVolumeClaimsGetter = client.CoreV1() kubeClient.PersistentVolumesGetter = client.CoreV1() kubeClient.NodesGetter = client.CoreV1() + kubeClient.NamespacesGetter = client.CoreV1() kubeClient.StatefulSetsGetter = client.AppsV1beta1() kubeClient.PodDisruptionBudgetsGetter = client.PolicyV1beta1() kubeClient.RESTClient = client.CoreV1().RESTClient() From 794feee3e1883e56264c6d4b5c4e6021c75038ca Mon Sep 17 00:00:00 2001 From: Sergey Dudoladov Date: Thu, 8 Feb 2018 13:49:44 +0100 Subject: [PATCH 04/15] Fix the bug with the operator always listening to all namespaces --- pkg/controller/postgresql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/postgresql.go b/pkg/controller/postgresql.go index f83779a21..25b4617a8 100644 --- a/pkg/controller/postgresql.go +++ b/pkg/controller/postgresql.go @@ -110,7 +110,7 @@ func (c *Controller) clusterWatchFunc(options metav1.ListOptions) (watch.Interfa options.Watch = true r, err := c.KubeClient.CRDREST. Get(). - Namespace(""). + Namespace(c.opConfig.WatchedNamespace). Resource(constants.CRDResource). VersionedParams(&options, metav1.ParameterCodec). FieldsSelectorParam(nil). From 86807d21ba8bbebf172790ca082e5baf9da35849 Mon Sep 17 00:00:00 2001 From: Sergey Dudoladov Date: Thu, 8 Feb 2018 14:24:47 +0100 Subject: [PATCH 05/15] Kill operator if the namespace to watch does not exist --- pkg/controller/controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 944033143..0b500315f 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -148,7 +148,7 @@ func (c *Controller) initController() { if c.opConfig.WatchedNamespace != v1.NamespaceAll { _, err := c.KubeClient.Namespaces().Get(c.opConfig.WatchedNamespace, metav1.GetOptions{}) if err != nil { - c.logger.Warnf("Operator was told to watch the %q namespace but was unable to confirm it existense via Kubernetes API. Falling back to watching all namespaces instead (done automatically by k8s)", c.opConfig.WatchedNamespace) + c.logger.Fatalf("Operator was told to watch the %q namespace but was unable to find it via Kubernetes API.", c.opConfig.WatchedNamespace) } } From b5b0b027f24bd92b79dbd16e2cd78ca34bf2250d Mon Sep 17 00:00:00 2001 From: Sergey Dudoladov Date: Thu, 8 Feb 2018 14:51:45 +0100 Subject: [PATCH 06/15] Handle watched namespace set in operator config map --- pkg/controller/controller.go | 39 +++++++++++++++--------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 0b500315f..f2e88ca83 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -98,33 +98,26 @@ func (c *Controller) initOperatorConfig() { } // by default, the operator listens to all namespaces - // by setting the env variable, one can restrict the operator to a single namespace - watchedNamespace, isPresentInEnv := os.LookupEnv("WATCHED_NAMESPACE") - if isPresentInEnv { - // special case: v1.NamespaceAll currently also evaluates to the empty string - // so when the env var is set to the empty string, use the default ns - // since the meaning of this env var is only one namespace - if watchedNamespace == "" { - c.logger.Infof("The WATCHED_NAMESPACE env var evaluates to the empty string, falling back to watching the 'default' namespace.\n", watchedNamespace) - configMapData["watched_namespace"] = v1.NamespaceDefault - } else { - c.logger.Infof("Watch the %q namespace specified in the env variable WATCHED_NAMESPACE\n", watchedNamespace) - configMapData["watched_namespace"] = watchedNamespace - } - - } else { - c.logger.Infof("Watch all namespaces. Set the WATCHED_NAMESPACE env var to restrict to a single namespace.\n", watchedNamespace) + if configMapData["watched_namespace"] == "" { + c.logger.Infoln("The operator config map specifies no namespace to watch. Falling back to watching all namespaces.") configMapData["watched_namespace"] = v1.NamespaceAll } - /* - // env var takes priority over the same param from the operator ConfigMap + watchedNsEnvVar, isPresentInEnv := os.LookupEnv("WATCHED_NAMESPACE") + if isPresentInEnv { + c.logger.Infoln("The WATCHED_NAMESPACE env variable takes priority over the same param from the operator configMap\n") + // special case: v1.NamespaceAll currently also evaluates to the empty string + // so when the env var is set to the empty string, use the default ns + // since the meaning of this env var is only one namespace + if watchedNsEnvVar == "" { + c.logger.Infof("The WATCHED_NAMESPACE env var evaluates to the empty string, falling back to watching the 'default' namespace.\n", watchedNsEnvVar) + configMapData["watched_namespace"] = v1.NamespaceDefault + } else { + c.logger.Infof("Watch the %q namespace specified in the env variable WATCHED_NAMESPACE\n", watchedNsEnvVar) + configMapData["watched_namespace"] = watchedNsEnvVar + } + } - if configMapData["watched_namespace"] == "" { - c.logger.Infoln("No namespace to watch specified. Fall back to watching the 'default' namespace.") - configMapData["watched_namespace"] = v1.NamespaceDefault - } - */ if c.config.NoDatabaseAccess { configMapData["enable_database_access"] = "false" } From c0bc8eaa6d1c5a934eb6205ef96f6342103c8c24 Mon Sep 17 00:00:00 2001 From: Sergey Dudoladov Date: Thu, 8 Feb 2018 15:15:47 +0100 Subject: [PATCH 07/15] Comment manifests --- manifests/configmap.yaml | 4 ++-- manifests/postgres-operator.yaml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 5afc7db31..ae64b0c13 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -3,8 +3,8 @@ kind: ConfigMap metadata: name: postgres-operator data: - # assumes the ns exists before the operator starts - # the env var with the same name may overwrite it in the operator pod + # the env var with the same name in the operator pod may overwrite this value + # if neither is set, listen to all namespaces # watched_namespace: development service_account_name: operator cluster_labels: application:spilo diff --git a/manifests/postgres-operator.yaml b/manifests/postgres-operator.yaml index 84c4a0770..1b4c80c34 100644 --- a/manifests/postgres-operator.yaml +++ b/manifests/postgres-operator.yaml @@ -16,6 +16,7 @@ spec: imagePullPolicy: IfNotPresent env: # uncomment to overwrite a similar setting from operator configmap + # if set to the empty string, watch the 'default' namespace # - name: WATCHED_NAMESPACE # valueFrom: # fieldRef: From 066f11cbbddbe37f3f799ff0411a960f05550819 Mon Sep 17 00:00:00 2001 From: Sergey Dudoladov Date: Fri, 9 Feb 2018 11:39:56 +0100 Subject: [PATCH 08/15] Streamline handling of the watched_namespace param/envvar --- pkg/controller/controller.go | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index f2e88ca83..b21fdc1dc 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -97,20 +97,34 @@ func (c *Controller) initOperatorConfig() { c.logger.Infoln("no ConfigMap specified. Loading default values") } - // by default, the operator listens to all namespaces - if configMapData["watched_namespace"] == "" { - c.logger.Infoln("The operator config map specifies no namespace to watch. Falling back to watching all namespaces.") + watchedNsConfigMapVar, isPresentInOperatorConfigMap := configMapData["watched_namespace"] + watchedNsEnvVar, isPresentInOperatorEnv := os.LookupEnv("WATCHED_NAMESPACE") + + if (!isPresentInOperatorConfigMap) && (!isPresentInOperatorEnv) { + c.logger.Infoln("Neither the operator config map nor operator pod's environment define a namespace to watch. Default to watching all namespaces.") configMapData["watched_namespace"] = v1.NamespaceAll } - watchedNsEnvVar, isPresentInEnv := os.LookupEnv("WATCHED_NAMESPACE") - if isPresentInEnv { - c.logger.Infoln("The WATCHED_NAMESPACE env variable takes priority over the same param from the operator configMap\n") + if (isPresentInOperatorConfigMap) && (!isPresentInOperatorEnv) { + // special case: v1.NamespaceAll currently also evaluates to the empty string - // so when the env var is set to the empty string, use the default ns - // since the meaning of this env var is only one namespace + // so when the param evaluates to the empty string, we use the default ns + // since the meaning of the param is a single namespace and *not* all namespaces + if watchedNsConfigMapVar == "" { + c.logger.Infof("The watched namespace field in the operator config map evaluates to the empty string, falling back to watching the 'default' namespace.\n") + configMapData["watched_namespace"] = v1.NamespaceDefault + } + } + + if isPresentInOperatorEnv { + + if isPresentInOperatorConfigMap { + c.logger.Infof("Both WATCHED_NAMESPACE=%q env var and wacthed_namespace=%q field in operator config map are defined. The env variable takes priority over the configMap param\n", watchedNsEnvVar, watchedNsConfigMapVar) + } + + // handle the empty string consistently if watchedNsEnvVar == "" { - c.logger.Infof("The WATCHED_NAMESPACE env var evaluates to the empty string, falling back to watching the 'default' namespace.\n", watchedNsEnvVar) + c.logger.Infoln("The WATCHED_NAMESPACE env var evaluates to the empty string, falling back to watching the 'default' namespace") configMapData["watched_namespace"] = v1.NamespaceDefault } else { c.logger.Infof("Watch the %q namespace specified in the env variable WATCHED_NAMESPACE\n", watchedNsEnvVar) From 4c23917d4214d4a7cc49ea8407cca64f3ec72dbe Mon Sep 17 00:00:00 2001 From: Sergey Dudoladov Date: Mon, 12 Feb 2018 11:47:56 +0100 Subject: [PATCH 09/15] Watch all namespaces if the relevant param is empty string / 'default' if param is unset --- manifests/configmap.yaml | 3 ++- pkg/controller/controller.go | 18 +++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index ae64b0c13..8401a335d 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -4,7 +4,8 @@ metadata: name: postgres-operator data: # the env var with the same name in the operator pod may overwrite this value - # if neither is set, listen to all namespaces + # if neither is set, listen to the 'default' namespace + # if set to the empty string "", listen to all namespaces # watched_namespace: development service_account_name: operator cluster_labels: application:spilo diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index b21fdc1dc..189a1584f 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -101,18 +101,18 @@ func (c *Controller) initOperatorConfig() { watchedNsEnvVar, isPresentInOperatorEnv := os.LookupEnv("WATCHED_NAMESPACE") if (!isPresentInOperatorConfigMap) && (!isPresentInOperatorEnv) { - c.logger.Infoln("Neither the operator config map nor operator pod's environment define a namespace to watch. Default to watching all namespaces.") - configMapData["watched_namespace"] = v1.NamespaceAll + c.logger.Infoln("Neither the operator config map nor operator pod's environment define a namespace to watch. Fall back to watching the 'default' namespace.") + configMapData["watched_namespace"] = v1.NamespaceDefault } if (isPresentInOperatorConfigMap) && (!isPresentInOperatorEnv) { - // special case: v1.NamespaceAll currently also evaluates to the empty string - // so when the param evaluates to the empty string, we use the default ns - // since the meaning of the param is a single namespace and *not* all namespaces + // explicitly specify the policy for handling the empty string + // v1.NamespaceAll currently also evaluates to the empty string + // so when the param is set to this value, we watch all ns if watchedNsConfigMapVar == "" { - c.logger.Infof("The watched namespace field in the operator config map evaluates to the empty string, falling back to watching the 'default' namespace.\n") - configMapData["watched_namespace"] = v1.NamespaceDefault + c.logger.Infof("The watched namespace field in the operator config map evaluates to the empty string, falling back to watching all namespaces.\n") + configMapData["watched_namespace"] = v1.NamespaceAll } } @@ -124,8 +124,8 @@ func (c *Controller) initOperatorConfig() { // handle the empty string consistently if watchedNsEnvVar == "" { - c.logger.Infoln("The WATCHED_NAMESPACE env var evaluates to the empty string, falling back to watching the 'default' namespace") - configMapData["watched_namespace"] = v1.NamespaceDefault + c.logger.Infoln("The WATCHED_NAMESPACE env var evaluates to the empty string, falling back to watching all namespaces") + configMapData["watched_namespace"] = v1.NamespaceAll } else { c.logger.Infof("Watch the %q namespace specified in the env variable WATCHED_NAMESPACE\n", watchedNsEnvVar) configMapData["watched_namespace"] = watchedNsEnvVar From e3d24344203c6f351c2401d12d48c1d1479e81e1 Mon Sep 17 00:00:00 2001 From: Sergey Dudoladov Date: Fri, 16 Feb 2018 15:20:26 +0100 Subject: [PATCH 10/15] Use '*' as an alias to denote all namespaces --- pkg/controller/controller.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 7dacef524..62046ab7f 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -109,11 +109,10 @@ func (c *Controller) initOperatorConfig() { if (isPresentInOperatorConfigMap) && (!isPresentInOperatorEnv) { - // explicitly specify the policy for handling the empty string - // v1.NamespaceAll currently also evaluates to the empty string - // so when the param is set to this value, we watch all ns - if watchedNsConfigMapVar == "" { - c.logger.Infof("The watched namespace field in the operator config map evaluates to the empty string, falling back to watching all namespaces.\n") + // explicitly specify the policy for handling all namespaces + // note that '*' is not a valid namespace name + if watchedNsConfigMapVar == "*" { + c.logger.Infof("The watched namespace field in the operator config map evaluates to '*', meaning watching all namespaces.\n") configMapData["watched_namespace"] = v1.NamespaceAll } } @@ -124,9 +123,9 @@ func (c *Controller) initOperatorConfig() { c.logger.Infof("Both WATCHED_NAMESPACE=%q env var and wacthed_namespace=%q field in operator config map are defined. The env variable takes priority over the configMap param\n", watchedNsEnvVar, watchedNsConfigMapVar) } - // handle the empty string consistently - if watchedNsEnvVar == "" { - c.logger.Infoln("The WATCHED_NAMESPACE env var evaluates to the empty string, falling back to watching all namespaces") + // handle all namespaces consistently + if watchedNsEnvVar == "*" { + c.logger.Infof("The watched namespace field in the operator config map evaluates to '*', meaning watching all namespaces.\n") configMapData["watched_namespace"] = v1.NamespaceAll } else { c.logger.Infof("Watch the %q namespace specified in the env variable WATCHED_NAMESPACE\n", watchedNsEnvVar) From b1fae716b182052598435e523176fb93308f0f75 Mon Sep 17 00:00:00 2001 From: Sergey Dudoladov Date: Fri, 16 Feb 2018 15:32:09 +0100 Subject: [PATCH 11/15] Update REST API to require namespace when fetching info about particular cluster --- pkg/apiserver/apiserver.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 41dfed1ac..76195f57d 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -48,9 +48,9 @@ type Server struct { } var ( - clusterStatusURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)(/(?P[a-zA-Z][a-zA-Z0-9-]*))?/(?P[a-zA-Z][a-zA-Z0-9-]*)/?$`) - clusterLogsURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)(/(?P[a-zA-Z][a-zA-Z0-9-]*))?/(?P[a-zA-Z][a-zA-Z0-9-]*)/logs/?$`) - clusterHistoryURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)(/(?P[a-zA-Z][a-zA-Z0-9-]*))?/(?P[a-zA-Z][a-zA-Z0-9-]*)/history/?$`) + clusterStatusURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)/(?P[a-zA-Z][a-zA-Z0-9-]*)/(?P[a-zA-Z][a-zA-Z0-9-]*)/?$`) + clusterLogsURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)/(?P[a-zA-Z][a-zA-Z0-9-]*)/(?P[a-zA-Z][a-zA-Z0-9-]*)/logs/?$`) + clusterHistoryURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)/(?P[a-zA-Z][a-zA-Z0-9-]*)/(?P[a-zA-Z][a-zA-Z0-9-]*)/history/?$`) teamURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)/?$`) workerLogsURL = regexp.MustCompile(`^/workers/(?P\d+)/logs/?$`) workerEventsQueueURL = regexp.MustCompile(`^/workers/(?P\d+)/queue/?$`) From dcfc9925f6b3ef5dea51f81a99f0c95a0963b945 Mon Sep 17 00:00:00 2001 From: Sergey Dudoladov Date: Tue, 20 Feb 2018 14:43:02 +0100 Subject: [PATCH 12/15] Respond to code review --- manifests/configmap.yaml | 4 +- manifests/postgres-operator.yaml | 3 +- pkg/apiserver/apiserver.go | 6 +-- pkg/controller/controller.go | 69 ++++++++++++-------------------- pkg/controller/status.go | 12 ------ pkg/util/config/config.go | 2 +- pkg/util/util.go | 7 ++++ 7 files changed, 40 insertions(+), 63 deletions(-) diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 8401a335d..cc0af9fe0 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -4,8 +4,8 @@ metadata: name: postgres-operator data: # the env var with the same name in the operator pod may overwrite this value - # if neither is set, listen to the 'default' namespace - # if set to the empty string "", listen to all namespaces + # if neither is set or evaluates to the empty string, listen to the operator's own namespace + # if set to the "*", listen to all namespaces # watched_namespace: development service_account_name: operator cluster_labels: application:spilo diff --git a/manifests/postgres-operator.yaml b/manifests/postgres-operator.yaml index 1b4c80c34..8e4425637 100644 --- a/manifests/postgres-operator.yaml +++ b/manifests/postgres-operator.yaml @@ -16,7 +16,8 @@ spec: imagePullPolicy: IfNotPresent env: # uncomment to overwrite a similar setting from operator configmap - # if set to the empty string, watch the 'default' namespace + # if set to the empty string, watch the operator's own namespace + # if set to the "*", listen to all namespaces # - name: WATCHED_NAMESPACE # valueFrom: # fieldRef: diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 76195f57d..8c01edeac 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -48,9 +48,9 @@ type Server struct { } var ( - clusterStatusURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)/(?P[a-zA-Z][a-zA-Z0-9-]*)/(?P[a-zA-Z][a-zA-Z0-9-]*)/?$`) - clusterLogsURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)/(?P[a-zA-Z][a-zA-Z0-9-]*)/(?P[a-zA-Z][a-zA-Z0-9-]*)/logs/?$`) - clusterHistoryURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)/(?P[a-zA-Z][a-zA-Z0-9-]*)/(?P[a-zA-Z][a-zA-Z0-9-]*)/history/?$`) + clusterStatusURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)/(?P[a-z0-9]([-a-z0-9]*[a-z0-9])?)/(?P[a-zA-Z][a-zA-Z0-9-]*)/?$`) + clusterLogsURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)/(?P[a-z0-9]([-a-z0-9]*[a-z0-9])?)/(?P[a-zA-Z][a-zA-Z0-9-]*)/logs/?$`) + clusterHistoryURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)/(?P[a-z0-9]([-a-z0-9]*[a-z0-9])?)/(?P[a-zA-Z][a-zA-Z0-9-]*)/history/?$`) teamURL = regexp.MustCompile(`^/clusters/(?P[a-zA-Z][a-zA-Z0-9]*)/?$`) workerLogsURL = regexp.MustCompile(`^/workers/(?P\d+)/logs/?$`) workerEventsQueueURL = regexp.MustCompile(`^/workers/(?P\d+)/queue/?$`) diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 62046ab7f..b0738bba8 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -14,6 +14,7 @@ import ( "github.com/zalando-incubator/postgres-operator/pkg/apiserver" "github.com/zalando-incubator/postgres-operator/pkg/cluster" "github.com/zalando-incubator/postgres-operator/pkg/spec" + "github.com/zalando-incubator/postgres-operator/pkg/util" "github.com/zalando-incubator/postgres-operator/pkg/util/config" "github.com/zalando-incubator/postgres-operator/pkg/util/constants" "github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil" @@ -97,41 +98,7 @@ func (c *Controller) initOperatorConfig() { c.logger.Infoln("no ConfigMap specified. Loading default values") } - watchedNsConfigMapVar, isPresentInOperatorConfigMap := configMapData["watched_namespace"] - watchedNsEnvVar, isPresentInOperatorEnv := os.LookupEnv("WATCHED_NAMESPACE") - - if (!isPresentInOperatorConfigMap) && (!isPresentInOperatorEnv) { - - c.logger.Infof("No namespace to watch specified. By convention, the operator falls back to watching the namespace it is deployed to: '%v' \n", spec.GetOperatorNamespace()) - configMapData["watched_namespace"] = spec.GetOperatorNamespace() - - } - - if (isPresentInOperatorConfigMap) && (!isPresentInOperatorEnv) { - - // explicitly specify the policy for handling all namespaces - // note that '*' is not a valid namespace name - if watchedNsConfigMapVar == "*" { - c.logger.Infof("The watched namespace field in the operator config map evaluates to '*', meaning watching all namespaces.\n") - configMapData["watched_namespace"] = v1.NamespaceAll - } - } - - if isPresentInOperatorEnv { - - if isPresentInOperatorConfigMap { - c.logger.Infof("Both WATCHED_NAMESPACE=%q env var and wacthed_namespace=%q field in operator config map are defined. The env variable takes priority over the configMap param\n", watchedNsEnvVar, watchedNsConfigMapVar) - } - - // handle all namespaces consistently - if watchedNsEnvVar == "*" { - c.logger.Infof("The watched namespace field in the operator config map evaluates to '*', meaning watching all namespaces.\n") - configMapData["watched_namespace"] = v1.NamespaceAll - } else { - c.logger.Infof("Watch the %q namespace specified in the env variable WATCHED_NAMESPACE\n", watchedNsEnvVar) - configMapData["watched_namespace"] = watchedNsEnvVar - } - } + configMapData["watched_namespace"] = c.getEffectiveNamespace(os.Getenv("WATCHED_NAMESPACE"), configMapData["watched_namespace"]) if c.config.NoDatabaseAccess { configMapData["enable_database_access"] = "false" @@ -152,15 +119,6 @@ func (c *Controller) initController() { c.initClients() c.initOperatorConfig() - // earliest point where we can check if the namespace to watch actually exists - if c.opConfig.WatchedNamespace != v1.NamespaceAll { - _, err := c.KubeClient.Namespaces().Get(c.opConfig.WatchedNamespace, metav1.GetOptions{}) - if err != nil { - c.logger.Fatalf("Operator was told to watch the %q namespace but was unable to find it via Kubernetes API.", c.opConfig.WatchedNamespace) - } - - } - c.initSharedInformers() c.logger.Infof("config: %s", c.opConfig.MustMarshal()) @@ -290,3 +248,26 @@ func (c *Controller) kubeNodesInformer(stopCh <-chan struct{}, wg *sync.WaitGrou c.nodesInformer.Run(stopCh) } + +func (c *Controller) getEffectiveNamespace(namespaceFromEnvironment, namespaceFromConfigMap string) string { + + namespace := util.Coalesce(namespaceFromEnvironment, util.Coalesce(namespaceFromConfigMap, spec.GetOperatorNamespace())) + + if namespace == "*" { + + namespace = v1.NamespaceAll + c.logger.Infof("Listening to all namespaces") + + } else { + + if _, err := c.KubeClient.Namespaces().Get(namespace, metav1.GetOptions{}); err != nil { + // the namespace may be created manually at runtime + c.logger.Warnf("Could not find the watched namespace %q", namespace) + } else { + c.logger.Infof("Listenting to the specific namespace %q", namespace) + } + + } + + return namespace +} diff --git a/pkg/controller/status.go b/pkg/controller/status.go index 932b77500..73f25f697 100644 --- a/pkg/controller/status.go +++ b/pkg/controller/status.go @@ -16,10 +16,6 @@ import ( // ClusterStatus provides status of the cluster func (c *Controller) ClusterStatus(team, namespace, cluster string) (*spec.ClusterStatus, error) { - if namespace == "" { - namespace = c.opConfig.WatchedNamespace - } - clusterName := spec.NamespacedName{ Namespace: namespace, Name: team + "-" + cluster, @@ -97,10 +93,6 @@ func (c *Controller) GetStatus() *spec.ControllerStatus { // ClusterLogs dumps cluster ring logs func (c *Controller) ClusterLogs(team, namespace, name string) ([]*spec.LogEntry, error) { - if namespace == "" { - namespace = c.opConfig.WatchedNamespace - } - clusterName := spec.NamespacedName{ Namespace: namespace, Name: team + "-" + name, @@ -224,10 +216,6 @@ func (c *Controller) WorkerStatus(workerID uint32) (*spec.WorkerStatus, error) { // ClusterHistory dumps history of cluster changes func (c *Controller) ClusterHistory(team, namespace, name string) ([]*spec.Diff, error) { - if namespace == "" { - namespace = c.opConfig.WatchedNamespace - } - clusterName := spec.NamespacedName{ Namespace: namespace, Name: team + "-" + name, diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 0bd2b1edf..edee72de4 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -67,7 +67,7 @@ type Config struct { Resources Auth Scalyr - WatchedNamespace string `name:"watched_namespace"` // may be v1.NamespaceAll, meaning watch all namespaces + WatchedNamespace string `name:"watched_namespace"` // may be "*", meaning watch all namespaces EtcdHost string `name:"etcd_host" default:"etcd-client.default.svc.cluster.local:2379"` DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spiloprivate-9.6:1.2-p4"` ServiceAccountName string `name:"service_account_name" default:"operator"` diff --git a/pkg/util/util.go b/pkg/util/util.go index c003ef477..b1b3d91b3 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -119,3 +119,10 @@ func MapContains(haystack, needle map[string]string) bool { return true } + +func Coalesce(val, defaultVal string) string { + if val == "" { + return defaultVal + } + return val +} From c47abed6ba87f10c02180ccd2727ec5cb3786628 Mon Sep 17 00:00:00 2001 From: Sergey Dudoladov Date: Tue, 20 Feb 2018 14:51:18 +0100 Subject: [PATCH 13/15] Document usage of multiple namespaces --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 27cb2e5b3..11ad33f6f 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ All subsequent `kubectl` commands will work with the `test` namespace. The opera Watching a namespace for an operator means tracking requests to change Postgresql clusters in the namespace such as "increase the number of Postgresql replicas to 5" and reacting to the requests, in this example by actually scaling up. -By default, the operator watches the namespace it is deployed to. You can change this by altering the `WATCHED_NAMESPACE` env var in the operator deployment manifest or the `watched_namespace` field in the operator configmap. In the case both are set, the env var takes the precedence. +By default, the operator watches the namespace it is deployed to. You can change this by altering the `WATCHED_NAMESPACE` env var in the operator deployment manifest or the `watched_namespace` field in the operator configmap. In the case both are set, the env var takes the precedence. To make the operator listen to all namespaces, explicitly set the field/env var to "`*`". Note that for an operator to manage pods in the watched namespace, the operator's service account (as specified in the operator deployment manifest) has to have appropriate privileges to access the watched namespace. The watched namespace also needs to have a (possibly different) service account in the case database pods need to talk to the Kubernetes API (e.g. when using Kubernetes-native configuration of Patroni). From 66a3b6830e609ee9ec5852aedaaee6440d889ae6 Mon Sep 17 00:00:00 2001 From: Sergey Dudoladov Date: Tue, 20 Feb 2018 16:13:48 +0100 Subject: [PATCH 14/15] Call fatalf if namespace to watch does not exist --- pkg/controller/controller.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index b0738bba8..d19da5b84 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -261,8 +261,7 @@ func (c *Controller) getEffectiveNamespace(namespaceFromEnvironment, namespaceFr } else { if _, err := c.KubeClient.Namespaces().Get(namespace, metav1.GetOptions{}); err != nil { - // the namespace may be created manually at runtime - c.logger.Warnf("Could not find the watched namespace %q", namespace) + c.logger.Fatalf("Could not find the watched namespace %q", namespace) } else { c.logger.Infof("Listenting to the specific namespace %q", namespace) } From e048328d6a70a2da9a97d6e1cd7f235a2c39fa18 Mon Sep 17 00:00:00 2001 From: Sergey Dudoladov Date: Tue, 20 Feb 2018 17:26:17 +0100 Subject: [PATCH 15/15] Comment on special values for watched namespace --- pkg/util/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index edee72de4..2c83b36b3 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -67,7 +67,7 @@ type Config struct { Resources Auth Scalyr - WatchedNamespace string `name:"watched_namespace"` // may be "*", meaning watch all namespaces + WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to' EtcdHost string `name:"etcd_host" default:"etcd-client.default.svc.cluster.local:2379"` DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spiloprivate-9.6:1.2-p4"` ServiceAccountName string `name:"service_account_name" default:"operator"`