From 6b73ac428264e2c7df971096a39be1a77ea1983f Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Fri, 9 Apr 2021 14:08:28 +0200 Subject: [PATCH 01/12] fix pooler sync with empty cluster name (#1448) --- pkg/cluster/connection_pooler.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index db4f1f56d..4e8af610d 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -566,7 +566,7 @@ func needSyncConnectionPoolerSpecs(oldSpec, newSpec *acidv1.ConnectionPooler, lo // Check if we need to synchronize connection pooler deployment due to new // defaults, that are different from what we see in the DeploymentSpec -func needSyncConnectionPoolerDefaults(Config *Config, spec *acidv1.ConnectionPooler, deployment *appsv1.Deployment) (sync bool, reasons []string) { +func (c *Cluster) needSyncConnectionPoolerDefaults(Config *Config, spec *acidv1.ConnectionPooler, deployment *appsv1.Deployment) (sync bool, reasons []string) { reasons = []string{} sync = false @@ -619,14 +619,14 @@ func needSyncConnectionPoolerDefaults(Config *Config, spec *acidv1.ConnectionPoo ref := env.ValueFrom.SecretKeyRef.LocalObjectReference secretName := Config.OpConfig.SecretNameTemplate.Format( "username", strings.Replace(config.User, "_", "-", -1), - "cluster", deployment.ClusterName, + "cluster", c.Name, "tprkind", acidv1.PostgresCRDResourceKind, "tprgroup", acidzalando.GroupName) if ref.Name != secretName { sync = true - msg := fmt.Sprintf("pooler user is different (having %s, required %s)", - ref.Name, config.User) + msg := fmt.Sprintf("pooler user and secret are different (having %s, required %s)", + ref.Name, secretName) reasons = append(reasons, msg) } } @@ -747,7 +747,7 @@ func (c *Cluster) syncConnectionPooler(oldSpec, newSpec *acidv1.Postgresql, Look Deployment: nil, Service: nil, Name: c.connectionPoolerName(role), - ClusterName: c.ClusterName, + ClusterName: c.Name, Namespace: c.Namespace, LookupFunction: false, Role: role, @@ -878,7 +878,7 @@ func (c *Cluster) syncConnectionPoolerWorker(oldSpec, newSpec *acidv1.Postgresql specSync, specReason = needSyncConnectionPoolerSpecs(oldConnectionPooler, newConnectionPooler, c.logger) } - defaultsSync, defaultsReason := needSyncConnectionPoolerDefaults(&c.Config, newConnectionPooler, deployment) + defaultsSync, defaultsReason := c.needSyncConnectionPoolerDefaults(&c.Config, newConnectionPooler, deployment) reason := append(specReason, defaultsReason...) if specSync || defaultsSync { From 32e6c135b9b387ad4f158f1b30fe0e3d151a364f Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 22 Apr 2021 11:22:52 +0200 Subject: [PATCH 02/12] replace statefulset on annotation diff (#1449) * replace statefulset on annotation diff * remove update annotation function for statefulset * add unit test for syncing annotations * add inherited annotation to unit test --- pkg/cluster/cluster.go | 9 +-- pkg/cluster/resources.go | 26 --------- pkg/cluster/sync.go | 2 - pkg/cluster/sync_test.go | 115 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 32 deletions(-) create mode 100644 pkg/cluster/sync_test.go diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 424c8e89a..512f6a6c9 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -361,6 +361,7 @@ func (c *Cluster) compareStatefulSetWith(statefulSet *appsv1.StatefulSet) *compa } if !reflect.DeepEqual(c.Statefulset.Annotations, statefulSet.Annotations) { match = false + needsReplace = true reasons = append(reasons, "new statefulset's annotations do not match the current one") } @@ -598,7 +599,7 @@ func (c *Cluster) enforceMinResourceLimits(spec *acidv1.PostgresSpec) error { // for a cluster that had no such job before. In this case a missing job is not an error. func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { updateFailed := false - syncStatetfulSet := false + syncStatefulSet := false c.mu.Lock() defer c.mu.Unlock() @@ -619,7 +620,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { if IsBiggerPostgresVersion(oldSpec.Spec.PostgresqlParam.PgVersion, c.GetDesiredMajorVersion()) { c.logger.Infof("postgresql version increased (%s -> %s), depending on config manual upgrade needed", oldSpec.Spec.PostgresqlParam.PgVersion, newSpec.Spec.PostgresqlParam.PgVersion) - syncStatetfulSet = true + syncStatefulSet = true } else { c.logger.Infof("postgresql major version unchanged or smaller, no changes needed") // sticking with old version, this will also advance GetDesiredVersion next time. @@ -688,9 +689,9 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { updateFailed = true return } - if syncStatetfulSet || !reflect.DeepEqual(oldSs, newSs) || !reflect.DeepEqual(oldSpec.Annotations, newSpec.Annotations) { + if syncStatefulSet || !reflect.DeepEqual(oldSs, newSs) || !reflect.DeepEqual(oldSpec.Annotations, newSpec.Annotations) { c.logger.Debugf("syncing statefulsets") - syncStatetfulSet = false + syncStatefulSet = false // TODO: avoid generating the StatefulSet object twice by passing it to syncStatefulSet if err := c.syncStatefulSet(); err != nil { c.logger.Errorf("could not sync statefulsets: %v", err) diff --git a/pkg/cluster/resources.go b/pkg/cluster/resources.go index ed8b4099c..48b17f532 100644 --- a/pkg/cluster/resources.go +++ b/pkg/cluster/resources.go @@ -147,25 +147,6 @@ func (c *Cluster) preScaleDown(newStatefulSet *appsv1.StatefulSet) error { return nil } -func (c *Cluster) updateStatefulSetAnnotations(annotations map[string]string) (*appsv1.StatefulSet, error) { - c.logger.Debugf("patching statefulset annotations") - patchData, err := metaAnnotationsPatch(annotations) - if err != nil { - return nil, fmt.Errorf("could not form patch for the statefulset metadata: %v", err) - } - result, err := c.KubeClient.StatefulSets(c.Statefulset.Namespace).Patch( - context.TODO(), - c.Statefulset.Name, - types.MergePatchType, - []byte(patchData), - metav1.PatchOptions{}, - "") - if err != nil { - return nil, fmt.Errorf("could not patch statefulset annotations %q: %v", patchData, err) - } - return result, nil -} - func (c *Cluster) updateStatefulSet(newStatefulSet *appsv1.StatefulSet) error { c.setProcessName("updating statefulset") if c.Statefulset == nil { @@ -197,13 +178,6 @@ func (c *Cluster) updateStatefulSet(newStatefulSet *appsv1.StatefulSet) error { return fmt.Errorf("could not patch statefulset spec %q: %v", statefulSetName, err) } - if newStatefulSet.Annotations != nil { - statefulSet, err = c.updateStatefulSetAnnotations(newStatefulSet.Annotations) - if err != nil { - return err - } - } - c.Statefulset = statefulSet return nil diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index c82e528fd..3f08cfb4d 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -373,8 +373,6 @@ func (c *Cluster) syncStatefulSet() error { } } - c.updateStatefulSetAnnotations(c.AnnotationsToPropagate(c.annotationsSet(c.Statefulset.Annotations))) - if len(podsToRecreate) == 0 && !c.OpConfig.EnableLazySpiloUpgrade { // even if the desired and the running statefulsets match // there still may be not up-to-date pods on condition diff --git a/pkg/cluster/sync_test.go b/pkg/cluster/sync_test.go new file mode 100644 index 000000000..e6f23914b --- /dev/null +++ b/pkg/cluster/sync_test.go @@ -0,0 +1,115 @@ +package cluster + +import ( + "testing" + "time" + + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + + "github.com/stretchr/testify/assert" + acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + fakeacidv1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/fake" + "github.com/zalando/postgres-operator/pkg/util/config" + "github.com/zalando/postgres-operator/pkg/util/k8sutil" + "k8s.io/client-go/kubernetes/fake" +) + +func newFakeK8sSyncClient() (k8sutil.KubernetesClient, *fake.Clientset) { + acidClientSet := fakeacidv1.NewSimpleClientset() + clientSet := fake.NewSimpleClientset() + + return k8sutil.KubernetesClient{ + PodsGetter: clientSet.CoreV1(), + PostgresqlsGetter: acidClientSet.AcidV1(), + StatefulSetsGetter: clientSet.AppsV1(), + }, clientSet +} + +func TestSyncStatefulSetsAnnotations(t *testing.T) { + testName := "test syncing statefulsets annotations" + client, _ := newFakeK8sSyncClient() + clusterName := "acid-test-cluster" + namespace := "default" + inheritedAnnotation := "environment" + + pg := acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + Annotations: map[string]string{inheritedAnnotation: "test"}, + }, + Spec: acidv1.PostgresSpec{ + Volume: acidv1.Volume{ + Size: "1Gi", + }, + }, + } + + var cluster = New( + Config{ + OpConfig: config.Config{ + PodManagementPolicy: "ordered_ready", + Resources: config.Resources{ + ClusterLabels: map[string]string{"application": "spilo"}, + ClusterNameLabel: "cluster-name", + DefaultCPURequest: "300m", + DefaultCPULimit: "300m", + DefaultMemoryRequest: "300Mi", + DefaultMemoryLimit: "300Mi", + InheritedAnnotations: []string{inheritedAnnotation}, + PodRoleLabel: "spilo-role", + ResourceCheckInterval: time.Duration(3), + ResourceCheckTimeout: time.Duration(10), + }, + }, + }, client, pg, logger, eventRecorder) + + cluster.Name = clusterName + cluster.Namespace = namespace + + // create a statefulset + _, err := cluster.createStatefulSet() + assert.NoError(t, err) + + // patch statefulset and add annotation + patchData, err := metaAnnotationsPatch(map[string]string{"test-anno": "true"}) + assert.NoError(t, err) + + newSts, err := cluster.KubeClient.StatefulSets(namespace).Patch( + context.TODO(), + clusterName, + types.MergePatchType, + []byte(patchData), + metav1.PatchOptions{}, + "") + assert.NoError(t, err) + + cluster.Statefulset = newSts + + // first compare running with desired statefulset - they should not match + // because no inherited annotations or downscaler annotations are configured + desiredSts, err := cluster.generateStatefulSet(&cluster.Postgresql.Spec) + assert.NoError(t, err) + + cmp := cluster.compareStatefulSetWith(desiredSts) + if cmp.match { + t.Errorf("%s: match between current and desired statefulsets albeit differences: %#v", testName, cmp) + } + + // now sync statefulset - the diff will trigger a replacement of the statefulset + cluster.syncStatefulSet() + + // compare again after the SYNC - must be identical to the desired state + cmp = cluster.compareStatefulSetWith(desiredSts) + if !cmp.match { + t.Errorf("%s: current and desired statefulsets are not matching %#v", testName, cmp) + } + + // check if inherited annotation exists + if _, exists := desiredSts.Annotations[inheritedAnnotation]; !exists { + t.Errorf("%s: inherited annotation not found in desired statefulset: %#v", testName, desiredSts.Annotations) + } +} From f0f7f25d309590713fe549e0c0664b46fdece11b Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 10 May 2021 11:48:03 +0200 Subject: [PATCH 03/12] Fix go lint errors (#1468) * fix linter errors * fix linter errors in kubectl plugin * update PyYAML dependency in e2e tests * declare a testVolume in volume_test --- e2e/requirements.txt | 2 +- kubectl-pg/cmd/addDb.go | 2 +- kubectl-pg/cmd/connect.go | 15 +++-- kubectl-pg/cmd/create.go | 3 + kubectl-pg/cmd/extVolume.go | 2 +- kubectl-pg/cmd/scale.go | 7 +- kubectl-pg/cmd/update.go | 3 + kubectl-pg/cmd/util.go | 6 +- pkg/cluster/cluster.go | 6 +- pkg/cluster/connection_pooler.go | 7 +- pkg/cluster/k8sres.go | 15 ++--- pkg/cluster/k8sres_test.go | 102 ++++++++++++++++------------- pkg/cluster/majorversionupgrade.go | 4 +- pkg/cluster/sync.go | 23 ------- pkg/cluster/util.go | 19 ++---- pkg/cluster/volumes.go | 9 ++- pkg/cluster/volumes_test.go | 51 +++++---------- 17 files changed, 122 insertions(+), 154 deletions(-) diff --git a/e2e/requirements.txt b/e2e/requirements.txt index 4f6f5ac5f..b276d2537 100644 --- a/e2e/requirements.txt +++ b/e2e/requirements.txt @@ -1,3 +1,3 @@ kubernetes==11.0.0 timeout_decorator==0.4.1 -pyyaml==5.3.1 +pyyaml==5.4.1 diff --git a/kubectl-pg/cmd/addDb.go b/kubectl-pg/cmd/addDb.go index cd45ea974..1c33579d9 100644 --- a/kubectl-pg/cmd/addDb.go +++ b/kubectl-pg/cmd/addDb.go @@ -71,7 +71,7 @@ func addDb(dbName string, dbOwner string, clusterName string) { var dbOwnerExists bool dbUsers := postgresql.Spec.Users - for key, _ := range dbUsers { + for key := range dbUsers { if key == dbOwner { dbOwnerExists = true } diff --git a/kubectl-pg/cmd/connect.go b/kubectl-pg/cmd/connect.go index 2f1500639..2c6d87835 100644 --- a/kubectl-pg/cmd/connect.go +++ b/kubectl-pg/cmd/connect.go @@ -23,13 +23,14 @@ THE SOFTWARE. package cmd import ( + "log" + "os" + user "os/user" + "github.com/spf13/cobra" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/remotecommand" - "log" - "os" - user "os/user" ) // connectCmd represents the kubectl pg connect command @@ -80,13 +81,13 @@ kubectl pg connect -c cluster -p -u user01 -d db01 func connect(clusterName string, master bool, replica string, psql bool, user string, dbName string) { config := getConfig() - client, er := kubernetes.NewForConfig(config) - if er != nil { - log.Fatal(er) + client, err := kubernetes.NewForConfig(config) + if err != nil { + log.Fatal(err) } podName := getPodName(clusterName, master, replica) - execRequest := &rest.Request{} + var execRequest *rest.Request if psql { execRequest = client.CoreV1().RESTClient().Post().Resource("pods"). diff --git a/kubectl-pg/cmd/create.go b/kubectl-pg/cmd/create.go index 4d1bc75fb..00ee7ac24 100644 --- a/kubectl-pg/cmd/create.go +++ b/kubectl-pg/cmd/create.go @@ -53,6 +53,9 @@ kubectl pg create -f cluster-manifest.yaml func create(fileName string) { config := getConfig() postgresConfig, err := PostgresqlLister.NewForConfig(config) + if err != nil { + log.Fatal(err) + } ymlFile, err := ioutil.ReadFile(fileName) if err != nil { log.Fatal(err) diff --git a/kubectl-pg/cmd/extVolume.go b/kubectl-pg/cmd/extVolume.go index 58a9eef67..02ccc372d 100644 --- a/kubectl-pg/cmd/extVolume.go +++ b/kubectl-pg/cmd/extVolume.go @@ -67,7 +67,7 @@ func extVolume(increasedVolumeSize string, clusterName string) { namespace := getCurrentNamespace() postgresql, err := postgresConfig.Postgresqls(namespace).Get(context.TODO(), clusterName, metav1.GetOptions{}) if err != nil { - log.Fatalf("hii %v", err) + log.Fatal(err) } oldSize, err := resource.ParseQuantity(postgresql.Spec.Volume.Size) diff --git a/kubectl-pg/cmd/scale.go b/kubectl-pg/cmd/scale.go index 5e8848de6..0a7bdc60f 100644 --- a/kubectl-pg/cmd/scale.go +++ b/kubectl-pg/cmd/scale.go @@ -31,7 +31,6 @@ import ( "github.com/spf13/cobra" PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1" - v1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" @@ -46,6 +45,9 @@ var scaleCmd = &cobra.Command{ Scaling to 0 leads to down time.`, Run: func(cmd *cobra.Command, args []string) { clusterName, err := cmd.Flags().GetString("cluster") + if err != nil { + log.Fatal(err) + } namespace, err := cmd.Flags().GetString("namespace") if err != nil { log.Fatal(err) @@ -129,8 +131,7 @@ func allowedMinMaxInstances(config *rest.Config) (int32, int32) { log.Fatal(err) } - var operator *v1.Deployment - operator = getPostgresOperator(k8sClient) + operator := getPostgresOperator(k8sClient) operatorContainer := operator.Spec.Template.Spec.Containers var configMapName, operatorConfigName string diff --git a/kubectl-pg/cmd/update.go b/kubectl-pg/cmd/update.go index 604d613fc..6a5f4e36d 100644 --- a/kubectl-pg/cmd/update.go +++ b/kubectl-pg/cmd/update.go @@ -57,6 +57,9 @@ kubectl pg update -f cluster-manifest.yaml func updatePgResources(fileName string) { config := getConfig() postgresConfig, err := PostgresqlLister.NewForConfig(config) + if err != nil { + log.Fatal(err) + } ymlFile, err := ioutil.ReadFile(fileName) if err != nil { log.Fatal(err) diff --git a/kubectl-pg/cmd/util.go b/kubectl-pg/cmd/util.go index a6bc10296..fa0eb6d42 100644 --- a/kubectl-pg/cmd/util.go +++ b/kubectl-pg/cmd/util.go @@ -99,9 +99,9 @@ func confirmAction(clusterName string, namespace string) { func getPodName(clusterName string, master bool, replicaNumber string) string { config := getConfig() - client, er := kubernetes.NewForConfig(config) - if er != nil { - log.Fatal(er) + client, err := kubernetes.NewForConfig(config) + if err != nil { + log.Fatal(err) } postgresConfig, err := PostgresqlLister.NewForConfig(config) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 512f6a6c9..273eb2932 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -1164,7 +1164,7 @@ func (c *Cluster) initHumanUsers() error { for _, superuserTeam := range superuserTeams { err := c.initTeamMembers(superuserTeam, true) if err != nil { - return fmt.Errorf("Cannot initialize members for team %q of Postgres superusers: %v", superuserTeam, err) + return fmt.Errorf("cannot initialize members for team %q of Postgres superusers: %v", superuserTeam, err) } if superuserTeam == c.Spec.TeamID { clusterIsOwnedBySuperuserTeam = true @@ -1177,7 +1177,7 @@ func (c *Cluster) initHumanUsers() error { if !(util.SliceContains(superuserTeams, additionalTeam)) { err := c.initTeamMembers(additionalTeam, false) if err != nil { - return fmt.Errorf("Cannot initialize members for additional team %q for cluster owned by %q: %v", additionalTeam, c.Spec.TeamID, err) + return fmt.Errorf("cannot initialize members for additional team %q for cluster owned by %q: %v", additionalTeam, c.Spec.TeamID, err) } } } @@ -1190,7 +1190,7 @@ func (c *Cluster) initHumanUsers() error { err := c.initTeamMembers(c.Spec.TeamID, false) if err != nil { - return fmt.Errorf("Cannot initialize members for team %q who owns the Postgres cluster: %v", c.Spec.TeamID, err) + return fmt.Errorf("cannot initialize members for team %q who owns the Postgres cluster: %v", c.Spec.TeamID, err) } return nil diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index 4e8af610d..f579b446e 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -420,9 +420,7 @@ func (c *Cluster) deleteConnectionPooler(role PostgresRole) (err error) { // Clean up the deployment object. If deployment resource we've remembered // is somehow empty, try to delete based on what would we generate - var deployment *appsv1.Deployment - deployment = c.ConnectionPooler[role].Deployment - + deployment := c.ConnectionPooler[role].Deployment policy := metav1.DeletePropagationForeground options := metav1.DeleteOptions{PropagationPolicy: &policy} @@ -445,8 +443,7 @@ func (c *Cluster) deleteConnectionPooler(role PostgresRole) (err error) { } // Repeat the same for the service object - var service *v1.Service - service = c.ConnectionPooler[role].Service + service := c.ConnectionPooler[role].Service if service == nil { c.logger.Debugf("no connection pooler service object to delete") } else { diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index ace665fb0..9e4b045ab 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -213,10 +213,10 @@ PatroniInitDBParams: for _, k := range initdbOptionNames { v := patroni.InitDB[k] for i, defaultParam := range config.Bootstrap.Initdb { - switch defaultParam.(type) { + switch t := defaultParam.(type) { case map[string]string: { - for k1 := range defaultParam.(map[string]string) { + for k1 := range t { if k1 == k { (config.Bootstrap.Initdb[i]).(map[string]string)[k] = v continue PatroniInitDBParams @@ -226,7 +226,7 @@ PatroniInitDBParams: case string: { /* if the option already occurs in the list */ - if defaultParam.(string) == v { + if t == v { continue PatroniInitDBParams } } @@ -264,7 +264,7 @@ PatroniInitDBParams: if patroni.SynchronousMode { config.Bootstrap.DCS.SynchronousMode = patroni.SynchronousMode } - if patroni.SynchronousModeStrict != false { + if patroni.SynchronousModeStrict { config.Bootstrap.DCS.SynchronousModeStrict = patroni.SynchronousModeStrict } @@ -336,7 +336,7 @@ func nodeAffinity(nodeReadinessLabel map[string]string, nodeAffinity *v1.NodeAff if len(nodeReadinessLabel) == 0 && nodeAffinity == nil { return nil } - nodeAffinityCopy := *&v1.NodeAffinity{} + nodeAffinityCopy := v1.NodeAffinity{} if nodeAffinity != nil { nodeAffinityCopy = *nodeAffinity.DeepCopy() } @@ -1279,15 +1279,12 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef return nil, fmt.Errorf("could not set the pod management policy to the unknown value: %v", c.OpConfig.PodManagementPolicy) } - stsAnnotations := make(map[string]string) - stsAnnotations = c.AnnotationsToPropagate(c.annotationsSet(nil)) - statefulSet := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ Name: c.statefulSetName(), Namespace: c.Namespace, Labels: c.labelsSet(true), - Annotations: stsAnnotations, + Annotations: c.AnnotationsToPropagate(c.annotationsSet(nil)), }, Spec: appsv1.StatefulSetSpec{ Replicas: &numberOfInstances, diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index cf0441f98..29908c0e5 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + fakeacidv1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/fake" "github.com/zalando/postgres-operator/pkg/spec" "github.com/zalando/postgres-operator/pkg/util" "github.com/zalando/postgres-operator/pkg/util/config" @@ -24,9 +25,21 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/kubernetes/fake" v1core "k8s.io/client-go/kubernetes/typed/core/v1" ) +func newFakeK8sTestClient() (k8sutil.KubernetesClient, *fake.Clientset) { + acidClientSet := fakeacidv1.NewSimpleClientset() + clientSet := fake.NewSimpleClientset() + + return k8sutil.KubernetesClient{ + PodsGetter: clientSet.CoreV1(), + PostgresqlsGetter: acidClientSet.AcidV1(), + StatefulSetsGetter: clientSet.AppsV1(), + }, clientSet +} + // For testing purposes type ExpectedValue struct { envIndex int @@ -930,15 +943,6 @@ func TestNodeAffinity(t *testing.T) { assert.Equal(t, s.Spec.Template.Spec.Affinity.NodeAffinity, nodeAff, "cluster template has correct node affinity") } -func testCustomPodTemplate(cluster *Cluster, podSpec *v1.PodTemplateSpec) error { - if podSpec.ObjectMeta.Name != "test-pod-template" { - return fmt.Errorf("Custom pod template is not used, current spec %+v", - podSpec) - } - - return nil -} - func testDeploymentOwnerReference(cluster *Cluster, deployment *appsv1.Deployment) error { owner := deployment.ObjectMeta.OwnerReferences[0] @@ -962,16 +966,23 @@ func testServiceOwnerReference(cluster *Cluster, service *v1.Service, role Postg } func TestTLS(t *testing.T) { - var err error - var spec acidv1.PostgresSpec - var cluster *Cluster - var spiloRunAsUser = int64(101) - var spiloRunAsGroup = int64(103) - var spiloFSGroup = int64(103) - var additionalVolumes = spec.AdditionalVolumes - makeSpec := func(tls acidv1.TLSDescription) acidv1.PostgresSpec { - return acidv1.PostgresSpec{ + client, _ := newFakeK8sTestClient() + clusterName := "acid-test-cluster" + namespace := "default" + tlsSecretName := "my-secret" + spiloRunAsUser := int64(101) + spiloRunAsGroup := int64(103) + spiloFSGroup := int64(103) + defaultMode := int32(0640) + mountPath := "/tls" + + pg := acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + }, + Spec: acidv1.PostgresSpec{ TeamID: "myapp", NumberOfInstances: 1, Resources: acidv1.Resources{ ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, @@ -980,11 +991,24 @@ func TestTLS(t *testing.T) { Volume: acidv1.Volume{ Size: "1G", }, - TLS: &tls, - } + TLS: &acidv1.TLSDescription{ + SecretName: tlsSecretName, CAFile: "ca.crt"}, + AdditionalVolumes: []acidv1.AdditionalVolume{ + acidv1.AdditionalVolume{ + Name: tlsSecretName, + MountPath: mountPath, + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: tlsSecretName, + DefaultMode: &defaultMode, + }, + }, + }, + }, + }, } - cluster = New( + var cluster = New( Config{ OpConfig: config.Config{ PodManagementPolicy: "ordered_ready", @@ -999,28 +1023,14 @@ func TestTLS(t *testing.T) { SpiloFSGroup: &spiloFSGroup, }, }, - }, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder) - spec = makeSpec(acidv1.TLSDescription{SecretName: "my-secret", CAFile: "ca.crt"}) - s, err := cluster.generateStatefulSet(&spec) - if err != nil { - assert.NoError(t, err) - } + }, client, pg, logger, eventRecorder) + + // create a statefulset + sts, err := cluster.createStatefulSet() + assert.NoError(t, err) fsGroup := int64(103) - assert.Equal(t, &fsGroup, s.Spec.Template.Spec.SecurityContext.FSGroup, "has a default FSGroup assigned") - - defaultMode := int32(0640) - mountPath := "/tls" - additionalVolumes = append(additionalVolumes, acidv1.AdditionalVolume{ - Name: spec.TLS.SecretName, - MountPath: mountPath, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ - SecretName: spec.TLS.SecretName, - DefaultMode: &defaultMode, - }, - }, - }) + assert.Equal(t, &fsGroup, sts.Spec.Template.Spec.SecurityContext.FSGroup, "has a default FSGroup assigned") volume := v1.Volume{ Name: "my-secret", @@ -1031,16 +1041,16 @@ func TestTLS(t *testing.T) { }, }, } - assert.Contains(t, s.Spec.Template.Spec.Volumes, volume, "the pod gets a secret volume") + assert.Contains(t, sts.Spec.Template.Spec.Volumes, volume, "the pod gets a secret volume") - assert.Contains(t, s.Spec.Template.Spec.Containers[0].VolumeMounts, v1.VolumeMount{ + assert.Contains(t, sts.Spec.Template.Spec.Containers[0].VolumeMounts, v1.VolumeMount{ MountPath: "/tls", Name: "my-secret", }, "the volume gets mounted in /tls") - assert.Contains(t, s.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "SSL_CERTIFICATE_FILE", Value: "/tls/tls.crt"}) - assert.Contains(t, s.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "SSL_PRIVATE_KEY_FILE", Value: "/tls/tls.key"}) - assert.Contains(t, s.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "SSL_CA_FILE", Value: "/tls/ca.crt"}) + assert.Contains(t, sts.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "SSL_CERTIFICATE_FILE", Value: "/tls/tls.crt"}) + assert.Contains(t, sts.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "SSL_PRIVATE_KEY_FILE", Value: "/tls/tls.key"}) + assert.Contains(t, sts.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "SSL_CA_FILE", Value: "/tls/ca.crt"}) } func TestAdditionalVolume(t *testing.T) { diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index 06dd979b2..c997a675a 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -19,8 +19,8 @@ var VersionMap = map[string]int{ // IsBiggerPostgresVersion Compare two Postgres version numbers func IsBiggerPostgresVersion(old string, new string) bool { - oldN, _ := VersionMap[old] - newN, _ := VersionMap[new] + oldN := VersionMap[old] + newN := VersionMap[new] return newN > oldN } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 3f08cfb4d..3036a9942 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -11,7 +11,6 @@ import ( "github.com/zalando/postgres-operator/pkg/util" "github.com/zalando/postgres-operator/pkg/util/constants" "github.com/zalando/postgres-operator/pkg/util/k8sutil" - appsv1 "k8s.io/api/apps/v1" batchv1beta1 "k8s.io/api/batch/v1beta1" v1 "k8s.io/api/core/v1" policybeta1 "k8s.io/api/policy/v1beta1" @@ -260,28 +259,6 @@ func (c *Cluster) syncPodDisruptionBudget(isUpdate bool) error { return nil } -func (c *Cluster) mustUpdatePodsAfterLazyUpdate(desiredSset *appsv1.StatefulSet) (bool, error) { - - pods, err := c.listPods() - if err != nil { - return false, fmt.Errorf("could not list pods of the statefulset: %v", err) - } - - for _, pod := range pods { - - effectivePodImage := pod.Spec.Containers[0].Image - ssImage := desiredSset.Spec.Template.Spec.Containers[0].Image - - if ssImage != effectivePodImage { - c.logger.Infof("not all pods were re-started when the lazy upgrade was enabled; forcing the rolling upgrade now") - return true, nil - } - - } - - return false, nil -} - func (c *Cluster) syncStatefulSet() error { podsToRecreate := make([]v1.Pod, 0) diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index fa8a52a1b..fabc6b216 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -227,11 +227,6 @@ func (c *Cluster) logServiceChanges(role PostgresRole, old, new *v1.Service, isU } } -func (c *Cluster) logVolumeChanges(old, new acidv1.Volume) { - c.logger.Infof("volume specification has been changed") - logNiceDiff(c.logger, old, new) -} - func (c *Cluster) getTeamMembers(teamID string) ([]string, error) { if teamID == "" { @@ -251,9 +246,7 @@ func (c *Cluster) getTeamMembers(teamID string) ([]string, error) { } } - for _, member := range additionalMembers { - members = append(members, member) - } + members = append(members, additionalMembers...) } if !c.OpConfig.EnableTeamsAPI { @@ -292,12 +285,10 @@ func (c *Cluster) annotationsSet(annotations map[string]string) map[string]strin pgCRDAnnotations := c.ObjectMeta.Annotations // allow to inherit certain labels from the 'postgres' object - if pgCRDAnnotations != nil { - for k, v := range pgCRDAnnotations { - for _, match := range c.OpConfig.InheritedAnnotations { - if k == match { - annotations[k] = v - } + for k, v := range pgCRDAnnotations { + for _, match := range c.OpConfig.InheritedAnnotations { + if k == match { + annotations[k] = v } } } diff --git a/pkg/cluster/volumes.go b/pkg/cluster/volumes.go index e07d453ec..9a41f5f05 100644 --- a/pkg/cluster/volumes.go +++ b/pkg/cluster/volumes.go @@ -74,10 +74,15 @@ func (c *Cluster) syncVolumes() error { func (c *Cluster) syncUnderlyingEBSVolume() error { c.logger.Infof("starting to sync EBS volumes: type, iops, throughput, and size") - var err error + var ( + err error + newSize resource.Quantity + ) targetValue := c.Spec.Volume - newSize, err := resource.ParseQuantity(targetValue.Size) + if newSize, err = resource.ParseQuantity(targetValue.Size); err != nil { + return fmt.Errorf("could not parse volume size: %v", err) + } targetSize := quantityToGigabyte(newSize) awsGp3 := aws.String("gp3") diff --git a/pkg/cluster/volumes_test.go b/pkg/cluster/volumes_test.go index aea7711af..204ea8aab 100644 --- a/pkg/cluster/volumes_test.go +++ b/pkg/cluster/volumes_test.go @@ -24,6 +24,20 @@ import ( "k8s.io/client-go/kubernetes/fake" ) +type testVolume struct { + size int64 + iops int64 + throughtput int64 + volType string +} + +var testVol = testVolume{ + size: 100, + iops: 300, + throughtput: 125, + volType: "gp2", +} + func newFakeK8sPVCclient() (k8sutil.KubernetesClient, *fake.Clientset) { clientSet := fake.NewSimpleClientset() @@ -189,14 +203,7 @@ func TestMigrateEBS(t *testing.T) { cluster.Namespace = namespace filterLabels := cluster.labelsSet(false) - testVolumes := []testVolume{ - { - size: 100, - }, - { - size: 100, - }, - } + testVolumes := []testVolume{testVol, testVol} initTestVolumesAndPods(cluster.KubeClient, namespace, clusterName, filterLabels, testVolumes) @@ -220,13 +227,6 @@ func TestMigrateEBS(t *testing.T) { cluster.executeEBSMigration() } -type testVolume struct { - iops int64 - throughtput int64 - size int64 - volType string -} - func initTestVolumesAndPods(client k8sutil.KubernetesClient, namespace, clustername string, labels labels.Set, volumes []testVolume) { i := 0 for _, v := range volumes { @@ -305,17 +305,7 @@ func TestMigrateGp3Support(t *testing.T) { cluster.Namespace = namespace filterLabels := cluster.labelsSet(false) - testVolumes := []testVolume{ - { - size: 100, - }, - { - size: 100, - }, - { - size: 100, - }, - } + testVolumes := []testVolume{testVol, testVol, testVol} initTestVolumesAndPods(cluster.KubeClient, namespace, clusterName, filterLabels, testVolumes) @@ -371,14 +361,7 @@ func TestManualGp2Gp3Support(t *testing.T) { cluster.Namespace = namespace filterLabels := cluster.labelsSet(false) - testVolumes := []testVolume{ - { - size: 100, - }, - { - size: 100, - }, - } + testVolumes := []testVolume{testVol, testVol} initTestVolumesAndPods(cluster.KubeClient, namespace, clusterName, filterLabels, testVolumes) From 18e2efe4e3d423ac628cdbdca2f523d797ac5270 Mon Sep 17 00:00:00 2001 From: Quan Hoang Date: Mon, 10 May 2021 17:16:47 +0700 Subject: [PATCH 04/12] Update sts when modifying additionalVolumes (#1474) --- pkg/cluster/cluster.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 273eb2932..ff3a33af9 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -446,6 +446,11 @@ func (c *Cluster) compareStatefulSetWith(statefulSet *appsv1.StatefulSet) *compa } } + if len(c.Statefulset.Spec.Template.Spec.Volumes) != len(statefulSet.Spec.Template.Spec.Volumes) { + needsReplace = true + reasons = append(reasons, fmt.Sprintf("new statefulset's Volumes contains different number of volumes to the old one")) + } + // we assume any change in priority happens by rolling out a new priority class // changing the priority value in an existing class is not supproted if c.Statefulset.Spec.Template.Spec.PriorityClassName != statefulSet.Spec.Template.Spec.PriorityClassName { @@ -503,6 +508,8 @@ func (c *Cluster) compareContainers(description string, setA, setB []v1.Containe func(a, b v1.Container) bool { return !reflect.DeepEqual(a.EnvFrom, b.EnvFrom) }), newCheck("new statefulset %s's %s (index %d) security context does not match the current one", func(a, b v1.Container) bool { return !reflect.DeepEqual(a.SecurityContext, b.SecurityContext) }), + newCheck("new statefulset %s's %s (index %d) volume mounts do not match the current one", + func(a, b v1.Container) bool { return !reflect.DeepEqual(a.VolumeMounts, b.VolumeMounts) }), } if !c.OpConfig.EnableLazySpiloUpgrade { From a993300325dc4a230d3414fab666892d04e91b8d Mon Sep 17 00:00:00 2001 From: Jan Mussler Date: Wed, 12 May 2021 10:50:16 +0200 Subject: [PATCH 05/12] Update readme to reflect major version upgrade. (#1488) --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3035e109d..fd7e731db 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ pipelines with no access to Kubernetes API directly, promoting infrastructure as * Rolling updates on Postgres cluster changes, incl. quick minor version updates * Live volume resize without pod restarts (AWS EBS, PVC) -* Database connection pooler with PGBouncer +* Database connection pooling with PGBouncer +* Support fast in place major version upgrade to PG13. Supports global upgrade of all clusters. * Restore and cloning Postgres clusters (incl. major version upgrade) * Additionally logical backups to S3 bucket can be configured * Standby cluster from S3 WAL archive @@ -24,11 +25,11 @@ pipelines with no access to Kubernetes API directly, promoting infrastructure as * Support for custom TLS certificates * UI to create and edit Postgres cluster manifests * Works well on Amazon AWS, Google Cloud, OpenShift and locally on Kind -* Support for AWS EBS gp3 migration +* Support for AWS EBS gp2 to gp3 migration, supporting iops and throughput configuration ### PostgreSQL features -* Supports PostgreSQL 13, starting from 9.5+ +* Supports PostgreSQL 13, starting from 9.6+ * Streaming replication cluster via Patroni * Point-In-Time-Recovery with [pg_basebackup](https://www.postgresql.org/docs/11/app-pgbasebackup.html) / From 7a8dc6084dc6ea324f29b9f6e5ab9b445a3d73a3 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 17 May 2021 14:14:31 +0200 Subject: [PATCH 06/12] use 0.20.6 K8s release (#1495) --- Makefile | 2 +- go.mod | 11 ++- go.sum | 200 ++++++++++++++++++++++--------------------------------- 3 files changed, 86 insertions(+), 127 deletions(-) diff --git a/Makefile b/Makefile index b9a654dd8..5d0d960f0 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,7 @@ mocks: GO111MODULE=on go generate ./... tools: - GO111MODULE=on go get k8s.io/client-go@kubernetes-1.20.2 + GO111MODULE=on go get k8s.io/client-go@kubernetes-1.20.6 GO111MODULE=on go get github.com/golang/mock/mockgen@v1.4.4 GO111MODULE=on go mod tidy diff --git a/go.mod b/go.mod index b46ebd089..1759d9573 100644 --- a/go.mod +++ b/go.mod @@ -12,11 +12,10 @@ require ( github.com/stretchr/testify v1.6.1 golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c golang.org/x/mod v0.4.0 // indirect - golang.org/x/tools v0.0.0-20201207204333-a835c872fcea // indirect gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.20.2 - k8s.io/apiextensions-apiserver v0.19.4 - k8s.io/apimachinery v0.20.2 - k8s.io/client-go v0.20.2 - k8s.io/code-generator v0.19.4 + k8s.io/api v0.20.6 + k8s.io/apiextensions-apiserver v0.20.6 + k8s.io/apimachinery v0.20.6 + k8s.io/client-go v0.20.6 + k8s.io/code-generator v0.20.6 ) diff --git a/go.sum b/go.sum index eb83c8d38..81b242b7a 100644 --- a/go.sum +++ b/go.sum @@ -6,7 +6,6 @@ cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= @@ -15,6 +14,7 @@ cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNF cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -24,44 +24,29 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.36.29 h1:lM1G3AF1+7vzFm0n7hfH8r2+750BTo+6Lo6FtPB7kzk= github.com/aws/aws-sdk-go v1.36.29/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= @@ -69,7 +54,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -79,7 +65,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -94,8 +80,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= @@ -117,8 +101,6 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -129,58 +111,22 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= -github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= -github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= -github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -236,18 +182,36 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -265,9 +229,10 @@ github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -281,20 +246,25 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -318,7 +288,7 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -327,6 +297,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= @@ -344,33 +315,37 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/r3labs/diff v1.1.0 h1:V53xhrbTHrWFWq3gI4b94AjgEJOerO1+1l0xyHOBi8M= github.com/r3labs/diff v1.1.0/go.mod h1:7WjXasNzi0vJetRcB/RqNl5dlIsmXcTTLmF5IoH6Xig= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -380,23 +355,17 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8= -go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -406,14 +375,12 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c h1:9HhBz5L/UjnK9XLtiZhYAdue5BVKep3PMmS2LuPDt8k= @@ -453,17 +420,16 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -477,7 +443,6 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -494,14 +459,15 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -525,7 +491,6 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -546,26 +511,24 @@ golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjTo golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -581,9 +544,10 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20201207204333-a835c872fcea h1:LgKM3cNs8xO6GK1ZVK0nasPn7IN39Sz9EBTwQLyishk= -golang.org/x/tools v0.0.0-20201207204333-a835c872fcea/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -624,9 +588,9 @@ google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -654,6 +618,7 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -677,41 +642,36 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.19.4/go.mod h1:SbtJ2aHCItirzdJ36YslycFNzWADYH3tgOhvBEFtZAk= -k8s.io/api v0.20.2 h1:y/HR22XDZY3pniu9hIFDLpUCPq2w5eQ6aV/VFQ7uJMw= -k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8= -k8s.io/apiextensions-apiserver v0.19.4 h1:D9ak9T012tb3vcGFWYmbQuj9SCC8YM4zhA4XZqsAQC4= -k8s.io/apiextensions-apiserver v0.19.4/go.mod h1:B9rpH/nu4JBCtuUp3zTTk8DEjZUupZTBEec7/2zNRYw= -k8s.io/apimachinery v0.19.4/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= -k8s.io/apimachinery v0.20.2 h1:hFx6Sbt1oG0n6DZ+g4bFt5f6BoMkOjKWsQFu077M3Vg= -k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apiserver v0.19.4/go.mod h1:X8WRHCR1UGZDd7HpV0QDc1h/6VbbpAeAGyxSh8yzZXw= -k8s.io/client-go v0.19.4/go.mod h1:ZrEy7+wj9PjH5VMBCuu/BDlvtUAku0oVFk4MmnW9mWA= -k8s.io/client-go v0.20.2 h1:uuf+iIAbfnCSw8IGAv/Rg0giM+2bOzHLOsbbrwrdhNQ= -k8s.io/client-go v0.20.2/go.mod h1:kH5brqWqp7HDxUFKoEgiI4v8G1xzbe9giaCenUWJzgE= -k8s.io/code-generator v0.19.4 h1:c8IL7RgTgJaYgr2bYMgjN0WikHnohbBhEgajfIkuP5I= -k8s.io/code-generator v0.19.4/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= -k8s.io/component-base v0.19.4/go.mod h1:ZzuSLlsWhajIDEkKF73j64Gz/5o0AgON08FgRbEPI70= +k8s.io/api v0.20.6 h1:bgdZrW++LqgrLikWYNruIKAtltXbSCX2l5mJu11hrVE= +k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= +k8s.io/apiextensions-apiserver v0.20.6 h1:3ZJiXMif7W/32RnfyHN8HygTAZaN7o+FvHxCyuQ7VOo= +k8s.io/apiextensions-apiserver v0.20.6/go.mod h1:qO8YMqeMmZH+lV21LUNzV41vfpoE9QVAJRA+MNqj0mo= +k8s.io/apimachinery v0.20.6 h1:R5p3SlhaABYShQSO6LpPsYHjV05Q+79eBUR0Ut/f4tk= +k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= +k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= +k8s.io/client-go v0.20.6 h1:nJZOfolnsVtDtbGJNCxzOtKUAu7zvXjB8+pMo9UNxZo= +k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= +k8s.io/code-generator v0.20.6 h1:kp65Y6kF6A4+5PvSNvXWSI5p5vuA9tUxEqEZciPw+7Q= +k8s.io/code-generator v0.20.6/go.mod h1:i6FmG+QxaLxvJsezvZp0q/gAEzzOz3U53KFibghWToU= +k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14 h1:t4L10Qfx/p7ASH3gXCdIUtPbbIuegCoUJf3TMSFekjw= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20201113003025-83324d819ded h1:JApXBKYyB7l9xx+DK7/+mFjC7A9Bt5A93FPvFD0HIFE= +k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3 h1:4oyYo8NREp49LBBhKxEqCulFjg26rawYKrnCmg+Sr6c= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= From eeb59c5bfd127020fa69bb29c58827cec905aa00 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Fri, 21 May 2021 15:49:39 +0200 Subject: [PATCH 07/12] Rename roles that are removed from PostgresTeam CRD (#1457) * rename db roles that are removed from manifests * extend PostgresTeam e2e test * make suffix configurable and add deprecated field to pgUser struct * deny LOGIN from deprecated roles * update feature documentation --- .../crds/operatorconfigurations.yaml | 6 ++ charts/postgres-operator/values-crd.yaml | 15 ++-- charts/postgres-operator/values.yaml | 28 +++---- docs/reference/operator_parameters.md | 13 ++++ docs/user.md | 17 +++++ e2e/tests/test_e2e.py | 75 +++++++++++++++---- manifests/configmap.yaml | 2 + manifests/operatorconfiguration.crd.yaml | 6 ++ ...gresql-operator-default-configuration.yaml | 2 + pkg/apis/acid.zalan.do/v1/crds.go | 6 ++ .../v1/operator_configuration_type.go | 2 + pkg/cluster/cluster.go | 18 ++++- pkg/cluster/database.go | 7 +- pkg/cluster/sync.go | 29 +++++++ pkg/cluster/util.go | 8 +- pkg/controller/operator_config.go | 2 + pkg/spec/types.go | 2 + pkg/teams/postgres_team_test.go | 2 - pkg/util/config/config.go | 2 + pkg/util/users/users.go | 49 +++++++++++- pkg/util/util.go | 12 +++ pkg/util/util_test.go | 8 ++ 22 files changed, 262 insertions(+), 49 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 0ac5651c3..0f036c299 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -443,6 +443,9 @@ spec: enable_postgres_team_crd_superusers: type: boolean default: false + enable_team_member_deprecation: + type: boolean + default: false enable_team_superuser: type: boolean default: false @@ -465,6 +468,9 @@ spec: type: string default: - admin + role_deletion_suffix: + type: string + default: "_deleted" team_admin_role: type: string default: "admin" diff --git a/charts/postgres-operator/values-crd.yaml b/charts/postgres-operator/values-crd.yaml index 6110fe8bc..bd563a636 100644 --- a/charts/postgres-operator/values-crd.yaml +++ b/charts/postgres-operator/values-crd.yaml @@ -289,13 +289,13 @@ configLogicalBackup: # automate creation of human users with teams API service configTeamsApi: # team_admin_role will have the rights to grant roles coming from PG manifests - # enable_admin_role_for_users: true - + enable_admin_role_for_users: true # operator watches for PostgresTeam CRs to assign additional teams and members to clusters enable_postgres_team_crd: false # toogle to create additional superuser teams from PostgresTeam CRs - # enable_postgres_team_crd_superusers: false - + enable_postgres_team_crd_superusers: false + # toggle to automatically rename roles of former team members and deny LOGIN + enable_team_member_deprecation: false # toggle to grant superuser to team members created from the Teams API enable_team_superuser: false # toggles usage of the Teams API by the operator @@ -306,12 +306,13 @@ configTeamsApi: # operator will add all team member roles to this group and add a pg_hba line pam_role_name: zalandos # List of teams which members need the superuser role in each Postgres cluster - # postgres_superuser_teams: - # - postgres_superusers - + postgres_superuser_teams: + - postgres_superusers # List of roles that cannot be overwritten by an application, team or infrastructure role protected_role_names: - admin + # Suffix to add if members are removed from TeamsAPI or PostgresTeam CRD + role_deletion_suffix: "_deleted" # role name to grant to team members created from the Teams API team_admin_role: admin # postgres config parameters to apply to each team member role diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 1cf84ca87..15a53a00d 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -280,36 +280,32 @@ configLogicalBackup: # automate creation of human users with teams API service configTeamsApi: # team_admin_role will have the rights to grant roles coming from PG manifests - # enable_admin_role_for_users: "true" - + enable_admin_role_for_users: "true" # operator watches for PostgresTeam CRs to assign additional teams and members to clusters enable_postgres_team_crd: "false" # toogle to create additional superuser teams from PostgresTeam CRs - # enable_postgres_team_crd_superusers: "false" - + enable_postgres_team_crd_superusers: "false" + # toggle to automatically rename roles of former team members and deny LOGIN + enable_team_member_deprecation: "false" # toggle to grant superuser to team members created from the Teams API - # enable_team_superuser: "false" - + enable_team_superuser: "false" # toggles usage of the Teams API by the operator enable_teams_api: "false" # should contain a URL to use for authentication (username and token) # pam_configuration: https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees # operator will add all team member roles to this group and add a pg_hba line - # pam_role_name: zalandos - + pam_role_name: "zalandos" # List of teams which members need the superuser role in each Postgres cluster - # postgres_superuser_teams: "postgres_superusers" - + postgres_superuser_teams: "postgres_superusers" # List of roles that cannot be overwritten by an application, team or infrastructure role - # protected_role_names: "admin" - + protected_role_names: "admin" + # Suffix to add if members are removed from TeamsAPI or PostgresTeam CRD + role_deletion_suffix: "_deleted" # role name to grant to team members created from the Teams API - # team_admin_role: "admin" - + team_admin_role: "admin" # postgres config parameters to apply to each team member role - # team_api_role_configuration: "log_statement:all" - + team_api_role_configuration: "log_statement:all" # URL of the Teams API service # teams_api_url: http://fake-teams-api.default.svc.cluster.local diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 80d0e5b8c..b0d982943 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -704,6 +704,19 @@ key. cluster to administer Postgres and maintain infrastructure built around it. The default is empty. +* **role_deletion_suffix** + defines a suffix that - when `enable_team_member_deprecation` is set to + `true` - will be appended to database role names of team members that were + removed from either the team in the Teams API or a `PostgresTeam` custom + resource (additionalMembers). When re-added, the operator will rename roles + with the defined suffix back to the original role name. + The default is `_deleted`. + +* **enable_team_member_deprecation** + if `true` database roles of former team members will be renamed by appending + the configured `role_deletion_suffix` and `LOGIN` privilege will be revoked. + The default is `false`. + * **enable_postgres_team_crd** toggle to make the operator watch for created or updated `PostgresTeam` CRDs and create roles for specified additional teams and members. diff --git a/docs/user.md b/docs/user.md index 3342913c8..8e406ec00 100644 --- a/docs/user.md +++ b/docs/user.md @@ -407,6 +407,23 @@ spec: - "briggs" ``` +#### Removed members + +The Postgres Operator does not delete database roles when users are removed +from manifests. But, using the `PostgresTeam` custom resource or Teams API it +is very easy to add roles to many clusters. Manually reverting such a change +is cumbersome. Therefore, if members are removed from a `PostgresTeam` or the +Teams API the operator can rename roles appending a configured suffix to the +name (see `role_deletion_suffix` option) and revoke the `LOGIN` privilege. +The suffix makes it easy then for a cleanup script to remove those deprecated +roles completely. Switch `enable_team_member_deprecation` to `true` to enable +this behavior. + +When a role is re-added to a `PostgresTeam` manifest (or to the source behind +the Teams API) the operator will check for roles with the configured suffix +and if found, rename the role back to the original name and grant `LOGIN` +again. + ## Prepared databases with roles and default privileges The `users` section in the manifests only allows for creating database roles diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index 114f881c4..bffb12386 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -197,13 +197,15 @@ class EndToEndTestCase(unittest.TestCase): enable_postgres_team_crd = { "data": { "enable_postgres_team_crd": "true", - "resync_period": "15s", + "enable_team_member_deprecation": "true", + "role_deletion_suffix": "_delete_me", + "resync_period": "15s" }, } self.k8s.update_config(enable_postgres_team_crd) self.eventuallyEqual(lambda: self.k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") - + self.k8s.api.custom_objects_api.patch_namespaced_custom_object( 'acid.zalan.do', 'v1', 'default', 'postgresteams', 'custom-team-membership', @@ -222,18 +224,60 @@ class EndToEndTestCase(unittest.TestCase): } }) - # make sure we let one sync pass and the new user being added - time.sleep(15) - leader = self.k8s.get_cluster_leader_pod() user_query = """ - SELECT usename - FROM pg_catalog.pg_user - WHERE usename IN ('elephant', 'kind'); + SELECT rolname + FROM pg_catalog.pg_roles + WHERE rolname IN ('elephant', 'kind'); """ - users = self.query_database(leader.metadata.name, "postgres", user_query) - self.eventuallyEqual(lambda: len(users), 2, - "Not all additional users found in database: {}".format(users)) + self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2, + "Not all additional users found in database", 10, 5) + + # replace additional member and check if the removed member's role is renamed + self.k8s.api.custom_objects_api.patch_namespaced_custom_object( + 'acid.zalan.do', 'v1', 'default', + 'postgresteams', 'custom-team-membership', + { + 'spec': { + 'additionalMembers': { + 'e2e': [ + 'tester' + ] + }, + } + }) + + user_query = """ + SELECT rolname + FROM pg_catalog.pg_roles + WHERE (rolname = 'tester' AND rolcanlogin) + OR (rolname = 'kind_delete_me' AND NOT rolcanlogin); + """ + self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2, + "Database role of replaced member in PostgresTeam not renamed", 10, 5) + + # re-add additional member and check if the role is renamed back + self.k8s.api.custom_objects_api.patch_namespaced_custom_object( + 'acid.zalan.do', 'v1', 'default', + 'postgresteams', 'custom-team-membership', + { + 'spec': { + 'additionalMembers': { + 'e2e': [ + 'kind' + ] + }, + } + }) + + user_query = """ + SELECT rolname + FROM pg_catalog.pg_roles + WHERE (rolname = 'kind' AND rolcanlogin) + OR (rolname = 'tester_delete_me' AND NOT rolcanlogin); + """ + self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2, + "Database role of recreated member in PostgresTeam not renamed back to original name", 10, 5) # revert config change revert_resync = { @@ -407,9 +451,9 @@ class EndToEndTestCase(unittest.TestCase): leader = k8s.get_cluster_leader_pod() schemas_query = """ - select schema_name - from information_schema.schemata - where schema_name = 'pooler' + SELECT schema_name + FROM information_schema.schemata + WHERE schema_name = 'pooler' """ db_list = self.list_databases(leader.metadata.name) @@ -529,6 +573,7 @@ class EndToEndTestCase(unittest.TestCase): "Parameters": None, "AdminRole": "", "Origin": 2, + "Deleted": False }) return True except: @@ -1417,7 +1462,7 @@ class EndToEndTestCase(unittest.TestCase): k8s = self.k8s result_set = [] db_list = [] - db_list_query = "select datname from pg_database" + db_list_query = "SELECT datname FROM pg_database" exec_query = r"psql -tAq -c \"{}\" -d {}" try: diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 29390a1f0..b379975eb 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -51,6 +51,7 @@ data: # enable_shm_volume: "true" # enable_sidecars: "true" enable_spilo_wal_path_compat: "true" + enable_team_member_deprecation: "false" # enable_team_superuser: "false" enable_teams_api: "false" # etcd_host: "" @@ -111,6 +112,7 @@ data: resource_check_timeout: 10m resync_period: 30m ring_log_lines: "100" + role_deletion_suffix: "_deleted" secret_name_template: "{username}.{cluster}.credentials" # sidecar_docker_images: "" # set_memory_request_to_limit: "false" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 47244e5d7..fbed0bea1 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -439,6 +439,9 @@ spec: enable_postgres_team_crd_superusers: type: boolean default: false + enable_team_member_deprecation: + type: boolean + default: false enable_team_superuser: type: boolean default: false @@ -461,6 +464,9 @@ spec: type: string default: - admin + role_deletion_suffix: + type: string + default: "_deleted" team_admin_role: type: string default: "admin" diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 3a8a79c8d..65dfd6ce4 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -141,6 +141,7 @@ configuration: # enable_admin_role_for_users: true # enable_postgres_team_crd: false # enable_postgres_team_crd_superusers: false + enable_team_member_deprecation: false enable_team_superuser: false enable_teams_api: false # pam_configuration: "" @@ -149,6 +150,7 @@ configuration: # - postgres_superusers protected_role_names: - admin + role_deletion_suffix: "_deleted" team_admin_role: admin team_api_role_configuration: log_statement: all diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 89d71eef5..83e7273e4 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1377,6 +1377,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ "enable_postgres_team_crd_superusers": { Type: "boolean", }, + "enable_team_member_deprecation": { + Type: "boolean", + }, "enable_team_superuser": { Type: "boolean", }, @@ -1405,6 +1408,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ }, }, }, + "role_deletion_suffix": { + Type: "string", + }, "team_admin_role": { Type: "string", }, diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 78b618b78..cf581431b 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -159,6 +159,8 @@ type TeamsAPIConfiguration struct { PostgresSuperuserTeams []string `json:"postgres_superuser_teams,omitempty"` EnablePostgresTeamCRD bool `json:"enable_postgres_team_crd,omitempty"` EnablePostgresTeamCRDSuperusers bool `json:"enable_postgres_team_crd_superusers,omitempty"` + EnableTeamMemberDeprecation bool `json:"enable_team_member_deprecation,omitempty"` + RoleDeletionSuffix string `json:"role_deletion_suffix,omitempty"` } // LoggingRESTAPIConfiguration defines Logging API conf diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index ff3a33af9..5b4d15ba5 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -74,6 +74,7 @@ type Cluster struct { eventRecorder record.EventRecorder patroni patroni.Interface pgUsers map[string]spec.PgUser + pgUsersCache map[string]spec.PgUser systemUsers map[string]spec.PgUser podSubscribers map[spec.NamespacedName]chan PodEvent podSubscribersMu sync.RWMutex @@ -129,7 +130,9 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec acidv1.Postgres Secrets: make(map[types.UID]*v1.Secret), Services: make(map[PostgresRole]*v1.Service), Endpoints: make(map[PostgresRole]*v1.Endpoints)}, - userSyncStrategy: users.DefaultUserSyncStrategy{PasswordEncryption: passwordEncryption}, + userSyncStrategy: users.DefaultUserSyncStrategy{ + PasswordEncryption: passwordEncryption, + RoleDeletionSuffix: cfg.OpConfig.RoleDeletionSuffix}, deleteOptions: metav1.DeleteOptions{PropagationPolicy: &deletePropagationPolicy}, podEventsQueue: podEventsQueue, KubeClient: kubeClient, @@ -190,6 +193,17 @@ func (c *Cluster) isNewCluster() bool { func (c *Cluster) initUsers() error { c.setProcessName("initializing users") + // if team member deprecation is enabled save current state of pgUsers + // to check for deleted roles + c.pgUsersCache = map[string]spec.PgUser{} + if c.OpConfig.EnableTeamMemberDeprecation { + for k, v := range c.pgUsers { + if v.Origin == spec.RoleOriginTeamsAPI { + c.pgUsersCache[k] = v + } + } + } + // clear our the previous state of the cluster users (in case we are // running a sync). c.systemUsers = map[string]spec.PgUser{} @@ -650,7 +664,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { needConnectionPooler := needMasterConnectionPoolerWorker(&newSpec.Spec) || needReplicaConnectionPoolerWorker(&newSpec.Spec) if !sameUsers || needConnectionPooler { - c.logger.Debugf("syncing secrets") + c.logger.Debugf("initialize users") if err := c.initUsers(); err != nil { c.logger.Errorf("could not init users: %v", err) updateFailed = true diff --git a/pkg/cluster/database.go b/pkg/cluster/database.go index 760b68d72..829c2e5c7 100644 --- a/pkg/cluster/database.go +++ b/pkg/cluster/database.go @@ -198,6 +198,7 @@ func (c *Cluster) readPgUsersFromDatabase(userNames []string) (users spec.PgUser rolname, rolpassword string rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin bool roloptions, memberof []string + roldeleted bool ) err := rows.Scan(&rolname, &rolpassword, &rolsuper, &rolinherit, &rolcreaterole, &rolcreatedb, &rolcanlogin, pq.Array(&roloptions), pq.Array(&memberof)) @@ -216,7 +217,11 @@ func (c *Cluster) readPgUsersFromDatabase(userNames []string) (users spec.PgUser parameters[fields[0]] = fields[1] } - users[rolname] = spec.PgUser{Name: rolname, Password: rolpassword, Flags: flags, MemberOf: memberof, Parameters: parameters} + if strings.HasSuffix(rolname, c.OpConfig.RoleDeletionSuffix) { + roldeleted = true + } + + users[rolname] = spec.PgUser{Name: rolname, Password: rolpassword, Flags: flags, MemberOf: memberof, Parameters: parameters, Deleted: roldeleted} } return users, nil diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 3036a9942..e987e744b 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -551,10 +551,29 @@ func (c *Cluster) syncRoles() (err error) { } }() + // mapping between original role name and with deletion suffix + deletedUsers := map[string]string{} + + // create list of database roles to query for _, u := range c.pgUsers { userNames = append(userNames, u.Name) + // add team member role name with rename suffix in case we need to rename it back + if u.Origin == spec.RoleOriginTeamsAPI && c.OpConfig.EnableTeamMemberDeprecation { + deletedUsers[u.Name+c.OpConfig.RoleDeletionSuffix] = u.Name + userNames = append(userNames, u.Name+c.OpConfig.RoleDeletionSuffix) + } } + // add team members that exist only in cache + // to trigger a rename of the role in ProduceSyncRequests + for _, cachedUser := range c.pgUsersCache { + if _, exists := c.pgUsers[cachedUser.Name]; !exists { + userNames = append(userNames, cachedUser.Name) + } + } + + // add pooler user to list of pgUsers, too + // to check if the pooler user exists or has to be created if needMasterConnectionPooler(&c.Spec) || needReplicaConnectionPooler(&c.Spec) { connectionPoolerUser := c.systemUsers[constants.ConnectionPoolerUserKeyName] userNames = append(userNames, connectionPoolerUser.Name) @@ -569,6 +588,16 @@ func (c *Cluster) syncRoles() (err error) { return fmt.Errorf("error getting users from the database: %v", err) } + // update pgUsers where a deleted role was found + // so that they are skipped in ProduceSyncRequests + for _, dbUser := range dbUsers { + if originalUser, exists := deletedUsers[dbUser.Name]; exists { + recreatedUser := c.pgUsers[originalUser] + recreatedUser.Deleted = true + c.pgUsers[originalUser] = recreatedUser + } + } + pgSyncRequests := c.userSyncStrategy.ProduceSyncRequests(dbUsers, c.pgUsers) if err = c.userSyncStrategy.ExecuteSyncRequests(pgSyncRequests, c.pgDb); err != nil { return fmt.Errorf("error executing sync statements: %v", err) diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index fabc6b216..6acecc38d 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -242,7 +242,7 @@ func (c *Cluster) getTeamMembers(teamID string) ([]string, error) { for team, membership := range *c.Config.PgTeamMap { if team == teamID { additionalMembers = membership.AdditionalMembers - c.logger.Debugf("found %d additional members for team %q", len(members), teamID) + c.logger.Debugf("found %d additional members for team %q", len(additionalMembers), teamID) } } @@ -256,14 +256,12 @@ func (c *Cluster) getTeamMembers(teamID string) ([]string, error) { token, err := c.oauthTokenGetter.getOAuthToken() if err != nil { - c.logger.Warnf("could not get oauth token to authenticate to team service API, only returning %d members for team %q: %v", len(members), teamID, err) - return members, nil + return nil, fmt.Errorf("could not get oauth token to authenticate to team service API: %v", err) } teamInfo, err := c.teamsAPIClient.TeamInfo(teamID, token) if err != nil { - c.logger.Warnf("could not get team info for team %q, only returning %d members: %v", teamID, len(members), err) - return members, nil + return nil, fmt.Errorf("could not get team info for team %q: %v", teamID, err) } for _, member := range teamInfo.Members { diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 3c0302cab..fbec7a462 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -180,6 +180,8 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.PostgresSuperuserTeams = fromCRD.TeamsAPI.PostgresSuperuserTeams result.EnablePostgresTeamCRD = fromCRD.TeamsAPI.EnablePostgresTeamCRD result.EnablePostgresTeamCRDSuperusers = fromCRD.TeamsAPI.EnablePostgresTeamCRDSuperusers + result.EnableTeamMemberDeprecation = fromCRD.TeamsAPI.EnableTeamMemberDeprecation + result.RoleDeletionSuffix = util.Coalesce(fromCRD.TeamsAPI.RoleDeletionSuffix, "_deleted") // logging REST API config result.APIPort = util.CoalesceInt(fromCRD.LoggingRESTAPI.APIPort, 8080) diff --git a/pkg/spec/types.go b/pkg/spec/types.go index 78c79e1b3..5d7794b42 100644 --- a/pkg/spec/types.go +++ b/pkg/spec/types.go @@ -42,6 +42,7 @@ const ( PGSyncUserAdd = iota PGsyncUserAlter PGSyncAlterSet // handle ALTER ROLE SET parameter = value + PGSyncUserRename ) // PgUser contains information about a single user. @@ -53,6 +54,7 @@ type PgUser struct { MemberOf []string `yaml:"inrole"` Parameters map[string]string `yaml:"db_parameters"` AdminRole string `yaml:"admin_role"` + Deleted bool `yaml:"deleted"` } func (user *PgUser) Valid() bool { diff --git a/pkg/teams/postgres_team_test.go b/pkg/teams/postgres_team_test.go index f138ec124..dec020c7d 100644 --- a/pkg/teams/postgres_team_test.go +++ b/pkg/teams/postgres_team_test.go @@ -9,8 +9,6 @@ import ( ) var ( - True = true - False = false pgTeamList = acidv1.PostgresTeamList{ TypeMeta: metav1.TypeMeta{ Kind: "List", diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index a5d144051..c8430e7a6 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -176,6 +176,8 @@ type Config struct { EnableTeamsAPI bool `name:"enable_teams_api" default:"true"` EnableTeamSuperuser bool `name:"enable_team_superuser" default:"false"` TeamAdminRole string `name:"team_admin_role" default:"admin"` + RoleDeletionSuffix string `name:"role_deletion_suffix" default:"_deleted"` + EnableTeamMemberDeprecation bool `name:"enable_team_member_deprecation" default:"false"` EnableAdminRoleForUsers bool `name:"enable_admin_role_for_users" default:"true"` EnablePostgresTeamCRD bool `name:"enable_postgres_team_crd" default:"false"` EnablePostgresTeamCRDSuperusers bool `name:"enable_postgres_team_crd_superusers" default:"false"` diff --git a/pkg/util/users/users.go b/pkg/util/users/users.go index 231cf2a89..3da933644 100644 --- a/pkg/util/users/users.go +++ b/pkg/util/users/users.go @@ -9,11 +9,13 @@ import ( "github.com/zalando/postgres-operator/pkg/spec" "github.com/zalando/postgres-operator/pkg/util" + "github.com/zalando/postgres-operator/pkg/util/constants" ) const ( createUserSQL = `SET LOCAL synchronous_commit = 'local'; CREATE ROLE "%s" %s %s;` alterUserSQL = `ALTER ROLE "%s" %s` + alterUserRenameSQL = `ALTER ROLE "%s" RENAME TO "%s%s"` alterRoleResetAllSQL = `ALTER ROLE "%s" RESET ALL` alterRoleSetSQL = `ALTER ROLE "%s" SET %s TO %s` grantToUserSQL = `GRANT %s TO "%s"` @@ -29,6 +31,7 @@ const ( // (except for the NOLOGIN). TODO: process other NOflags, i.e. NOSUPERUSER correctly. type DefaultUserSyncStrategy struct { PasswordEncryption string + RoleDeletionSuffix string } // ProduceSyncRequests figures out the types of changes that need to happen with the given users. @@ -36,8 +39,11 @@ func (strategy DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserM newUsers spec.PgUserMap) []spec.PgSyncUserRequest { var reqs []spec.PgSyncUserRequest - // No existing roles are deleted or stripped of role memebership/flags for name, newUser := range newUsers { + // do not create user that exists in DB with deletion suffix + if newUser.Deleted { + continue + } dbUser, exists := dbUsers[name] if !exists { reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncUserAdd, User: newUser}) @@ -70,6 +76,25 @@ func (strategy DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserM } } + // No existing roles are deleted or stripped of role membership/flags + // but team roles will be renamed and denied from LOGIN + for name, dbUser := range dbUsers { + if _, exists := newUsers[name]; !exists { + // toggle LOGIN flag based on role deletion + userFlags := make([]string, len(dbUser.Flags)) + userFlags = append(userFlags, dbUser.Flags...) + if dbUser.Deleted { + dbUser.Flags = util.StringSliceReplaceElement(dbUser.Flags, constants.RoleFlagNoLogin, constants.RoleFlagLogin) + } else { + dbUser.Flags = util.StringSliceReplaceElement(dbUser.Flags, constants.RoleFlagLogin, constants.RoleFlagNoLogin) + } + if !util.IsEqualIgnoreOrder(userFlags, dbUser.Flags) { + reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGsyncUserAlter, User: dbUser}) + } + + reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncUserRename, User: dbUser}) + } + } return reqs } @@ -94,6 +119,11 @@ func (strategy DefaultUserSyncStrategy) ExecuteSyncRequests(requests []spec.PgSy reqretries = append(reqretries, request) errors = append(errors, fmt.Sprintf("could not set custom user %q parameters: %v", request.User.Name, err)) } + case spec.PGSyncUserRename: + if err := strategy.alterPgUserRename(request.User, db); err != nil { + reqretries = append(reqretries, request) + errors = append(errors, fmt.Sprintf("could not rename custom user %q: %v", request.User.Name, err)) + } default: return fmt.Errorf("unrecognized operation: %v", request.Kind) } @@ -124,6 +154,23 @@ func (strategy DefaultUserSyncStrategy) alterPgUserSet(user spec.PgUser, db *sql return nil } +func (strategy DefaultUserSyncStrategy) alterPgUserRename(user spec.PgUser, db *sql.DB) error { + var query string + + // append or trim deletion suffix depending if the user has the suffix or not + if user.Deleted { + newName := strings.TrimSuffix(user.Name, strategy.RoleDeletionSuffix) + query = fmt.Sprintf(alterUserRenameSQL, user.Name, newName, "") + } else { + query = fmt.Sprintf(alterUserRenameSQL, user.Name, user.Name, strategy.RoleDeletionSuffix) + } + + if _, err := db.Exec(query); err != nil { + return err + } + return nil +} + func (strategy DefaultUserSyncStrategy) createPgUser(user spec.PgUser, db *sql.DB) error { var userFlags []string var userPassword string diff --git a/pkg/util/util.go b/pkg/util/util.go index bebb9f8da..a52925583 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -151,6 +151,18 @@ func IsEqualIgnoreOrder(a, b []string) bool { return reflect.DeepEqual(a_copy, b_copy) } +// SliceReplaceElement +func StringSliceReplaceElement(s []string, a, b string) (result []string) { + tmp := make([]string, 0, len(s)) + for _, str := range s { + if str == a { + str = b + } + tmp = append(tmp, str) + } + return tmp +} + // SubstractStringSlices finds elements in a that are not in b and return them as a result slice. func SubstractStringSlices(a []string, b []string) (result []string, equal bool) { // Slices are assumed to contain unique elements only diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go index c02d2c075..75853c3d6 100644 --- a/pkg/util/util_test.go +++ b/pkg/util/util_test.go @@ -166,6 +166,14 @@ func TestIsEqualIgnoreOrder(t *testing.T) { } } +func TestStringSliceReplaceElement(t *testing.T) { + testSlice := []string{"a", "b", "c"} + testSlice = StringSliceReplaceElement(testSlice, "b", "d") + if !SliceContains(testSlice, "d") { + t.Errorf("testSlide item not replaced: %v", testSlice) + } +} + func TestSubstractSlices(t *testing.T) { for _, tt := range substractTest { actualRes, actualEqual := SubstractStringSlices(tt.inA, tt.inB) From af5378eea5a8c41619fb11cfba5830532513ce08 Mon Sep 17 00:00:00 2001 From: Quan Hoang Date: Thu, 27 May 2021 19:56:14 +0700 Subject: [PATCH 08/12] Mount additional volumes to 'postgres' container when 'targetContains` is an empty list (#1475) * Mount additional volumes to 'postgres' container when 'targetContainers' is an empty list Co-authored-by: Felix Kunde Co-authored-by: Felix Kunde --- pkg/cluster/k8sres.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 9e4b045ab..6ab65612e 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -1452,7 +1452,7 @@ func (c *Cluster) addAdditionalVolumes(podSpec *v1.PodSpec, continue } - if v.TargetContainers == nil { + if len(v.TargetContainers) == 0 { spiloContainer := podSpec.Containers[0] additionalVolumes[i].TargetContainers = []string{spiloContainer.Name} } From 48cdca645df177de68de4b588f8ae9d2761792ad Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 27 May 2021 18:37:30 +0200 Subject: [PATCH 09/12] rework additional volume test (#1502) --- pkg/cluster/k8sres_test.go | 231 +++++++++++++++---------------------- 1 file changed, 92 insertions(+), 139 deletions(-) diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 29908c0e5..6dd42419c 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -413,6 +413,7 @@ func TestShmVolume(t *testing.T) { Volumes: []v1.Volume{}, Containers: []v1.Container{ { + Name: "postgres", VolumeMounts: []v1.VolumeMount{}, }, }, @@ -425,6 +426,7 @@ func TestShmVolume(t *testing.T) { Volumes: []v1.Volume{{}}, Containers: []v1.Container{ { + Name: "postgres", VolumeMounts: []v1.VolumeMount{ {}, }, @@ -1055,168 +1057,119 @@ func TestTLS(t *testing.T) { func TestAdditionalVolume(t *testing.T) { testName := "TestAdditionalVolume" - tests := []struct { - subTest string - podSpec *v1.PodSpec - volumePos int - }{ + + client, _ := newFakeK8sTestClient() + clusterName := "acid-test-cluster" + namespace := "default" + sidecarName := "sidecar" + additionalVolumes := []acidv1.AdditionalVolume{ { - subTest: "empty PodSpec", - podSpec: &v1.PodSpec{ - Volumes: []v1.Volume{}, - Containers: []v1.Container{ - { - VolumeMounts: []v1.VolumeMount{}, - }, - }, + Name: "test1", + MountPath: "/test1", + TargetContainers: []string{"all"}, + VolumeSource: v1.VolumeSource{ + EmptyDir: &v1.EmptyDirVolumeSource{}, }, - volumePos: 0, }, { - subTest: "non empty PodSpec", - podSpec: &v1.PodSpec{ - Volumes: []v1.Volume{{}}, - Containers: []v1.Container{ - { - Name: "postgres", - VolumeMounts: []v1.VolumeMount{ - { - Name: "data", - ReadOnly: false, - MountPath: "/data", - }, - }, - }, - }, + Name: "test2", + MountPath: "/test2", + TargetContainers: []string{sidecarName}, + VolumeSource: v1.VolumeSource{ + EmptyDir: &v1.EmptyDirVolumeSource{}, }, - volumePos: 1, }, { - subTest: "non empty PodSpec with sidecar", - podSpec: &v1.PodSpec{ - Volumes: []v1.Volume{{}}, - Containers: []v1.Container{ - { - Name: "postgres", - VolumeMounts: []v1.VolumeMount{ - { - Name: "data", - ReadOnly: false, - MountPath: "/data", - }, - }, - }, - { - Name: "sidecar", - VolumeMounts: []v1.VolumeMount{ - { - Name: "data", - ReadOnly: false, - MountPath: "/data", - }, - }, - }, + Name: "test3", + MountPath: "/test3", + TargetContainers: []string{}, // should mount only to postgres + VolumeSource: v1.VolumeSource{ + EmptyDir: &v1.EmptyDirVolumeSource{}, + }, + }, + { + Name: "test4", + MountPath: "/test4", + TargetContainers: nil, // should mount only to postgres + VolumeSource: v1.VolumeSource{ + EmptyDir: &v1.EmptyDirVolumeSource{}, + }, + }, + } + + pg := acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + }, + Spec: acidv1.PostgresSpec{ + TeamID: "myapp", NumberOfInstances: 1, + Resources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + }, + Volume: acidv1.Volume{ + Size: "1G", + }, + AdditionalVolumes: additionalVolumes, + Sidecars: []acidv1.Sidecar{ + { + Name: sidecarName, }, }, - volumePos: 1, }, } var cluster = New( Config{ OpConfig: config.Config{ - ProtectedRoles: []string{"admin"}, - Auth: config.Auth{ - SuperUsername: superUserName, - ReplicationUsername: replicationUserName, + PodManagementPolicy: "ordered_ready", + Resources: config.Resources{ + ClusterLabels: map[string]string{"application": "spilo"}, + ClusterNameLabel: "cluster-name", + DefaultCPURequest: "300m", + DefaultCPULimit: "300m", + DefaultMemoryRequest: "300Mi", + DefaultMemoryLimit: "300Mi", + PodRoleLabel: "spilo-role", }, }, - }, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder) + }, client, pg, logger, eventRecorder) - for _, tt := range tests { - // Test with additional volume mounted in all containers - additionalVolumeMount := []acidv1.AdditionalVolume{ - { - Name: "test", - MountPath: "/test", - TargetContainers: []string{"all"}, - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, - }, - }, - } + // create a statefulset + sts, err := cluster.createStatefulSet() + assert.NoError(t, err) - numMounts := len(tt.podSpec.Containers[0].VolumeMounts) - - cluster.addAdditionalVolumes(tt.podSpec, additionalVolumeMount) - volumeName := tt.podSpec.Volumes[tt.volumePos].Name - - if volumeName != additionalVolumeMount[0].Name { - t.Errorf("%s %s: Expected volume %v was not created, have %s instead", - testName, tt.subTest, additionalVolumeMount, volumeName) - } - - for i := range tt.podSpec.Containers { - volumeMountName := tt.podSpec.Containers[i].VolumeMounts[tt.volumePos].Name - - if volumeMountName != additionalVolumeMount[0].Name { - t.Errorf("%s %s: Expected mount %v was not created, have %s instead", - testName, tt.subTest, additionalVolumeMount, volumeMountName) - } - - } - - numMountsCheck := len(tt.podSpec.Containers[0].VolumeMounts) - - if numMountsCheck != numMounts+1 { - t.Errorf("Unexpected number of VolumeMounts: got %v instead of %v", - numMountsCheck, numMounts+1) - } + tests := []struct { + subTest string + container string + expectedMounts []string + }{ + { + subTest: "checking volume mounts of postgres container", + container: cluster.containerName(), + expectedMounts: []string{"pgdata", "test1", "test3", "test4"}, + }, + { + subTest: "checking volume mounts of sidecar container", + container: "sidecar", + expectedMounts: []string{"pgdata", "test1", "test2"}, + }, } for _, tt := range tests { - // Test with additional volume mounted only in first container - additionalVolumeMount := []acidv1.AdditionalVolume{ - { - Name: "test", - MountPath: "/test", - TargetContainers: []string{"postgres"}, - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, - }, - }, - } + for _, container := range sts.Spec.Template.Spec.Containers { + if container.Name != tt.container { + continue + } + mounts := []string{} + for _, volumeMounts := range container.VolumeMounts { + mounts = append(mounts, volumeMounts.Name) + } - numMounts := len(tt.podSpec.Containers[0].VolumeMounts) - - cluster.addAdditionalVolumes(tt.podSpec, additionalVolumeMount) - volumeName := tt.podSpec.Volumes[tt.volumePos].Name - - if volumeName != additionalVolumeMount[0].Name { - t.Errorf("%s %s: Expected volume %v was not created, have %s instead", - testName, tt.subTest, additionalVolumeMount, volumeName) - } - - for _, container := range tt.podSpec.Containers { - if container.Name == "postgres" { - volumeMountName := container.VolumeMounts[tt.volumePos].Name - - if volumeMountName != additionalVolumeMount[0].Name { - t.Errorf("%s %s: Expected mount %v was not created, have %s instead", - testName, tt.subTest, additionalVolumeMount, volumeMountName) - } - - numMountsCheck := len(container.VolumeMounts) - if numMountsCheck != numMounts+1 { - t.Errorf("Unexpected number of VolumeMounts: got %v instead of %v", - numMountsCheck, numMounts+1) - } - } else { - numMountsCheck := len(container.VolumeMounts) - if numMountsCheck == numMounts+1 { - t.Errorf("Unexpected number of VolumeMounts: got %v instead of %v", - numMountsCheck, numMounts) - } + if !util.IsEqualIgnoreOrder(mounts, tt.expectedMounts) { + t.Errorf("%s %s: different volume mounts: got %v, epxected %v", + testName, tt.subTest, mounts, tt.expectedMounts) } } } From 7884af2d597e4f203c1f12407920a9a76e61d826 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 27 May 2021 18:56:58 +0200 Subject: [PATCH 10/12] get postgres container by name, not index (#1504) --- pkg/cluster/exec.go | 5 ++- pkg/cluster/k8sres.go | 62 ++++++++++++++++++-------------- pkg/cluster/sync.go | 4 +-- pkg/cluster/util.go | 10 ++++++ pkg/util/constants/kubernetes.go | 1 - 5 files changed, 49 insertions(+), 33 deletions(-) diff --git a/pkg/cluster/exec.go b/pkg/cluster/exec.go index 8b5089b4e..462f702d7 100644 --- a/pkg/cluster/exec.go +++ b/pkg/cluster/exec.go @@ -12,7 +12,6 @@ import ( "k8s.io/client-go/tools/remotecommand" "github.com/zalando/postgres-operator/pkg/spec" - "github.com/zalando/postgres-operator/pkg/util/constants" ) //ExecCommand executes arbitrary command inside the pod @@ -32,14 +31,14 @@ func (c *Cluster) ExecCommand(podName *spec.NamespacedName, command ...string) ( // iterate through all containers looking for the one running PostgreSQL. targetContainer := -1 for i, cr := range pod.Spec.Containers { - if cr.Name == constants.PostgresContainerName { + if cr.Name == c.containerName() { targetContainer = i break } } if targetContainer < 0 { - return "", fmt.Errorf("could not find %s container to exec to", constants.PostgresContainerName) + return "", fmt.Errorf("could not find %s container to exec to", c.containerName()) } req := c.KubeClient.RESTClient.Post(). diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 6ab65612e..2bab55bc8 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -67,7 +67,7 @@ type spiloConfiguration struct { } func (c *Cluster) containerName() string { - return "postgres" + return constants.PostgresContainerName } func (c *Cluster) statefulSetName() string { @@ -1401,14 +1401,18 @@ func addShmVolume(podSpec *v1.PodSpec) { }, }) - pgIdx := constants.PostgresContainerIdx - mounts := append(podSpec.Containers[pgIdx].VolumeMounts, - v1.VolumeMount{ - Name: constants.ShmVolumeName, - MountPath: constants.ShmVolumePath, - }) + for i, container := range podSpec.Containers { + if container.Name == constants.PostgresContainerName { + mounts := append(container.VolumeMounts, + v1.VolumeMount{ + Name: constants.ShmVolumeName, + MountPath: constants.ShmVolumePath, + }) + + podSpec.Containers[i].VolumeMounts = mounts + } + } - podSpec.Containers[0].VolumeMounts = mounts podSpec.Volumes = volumes } @@ -1439,54 +1443,58 @@ func (c *Cluster) addAdditionalVolumes(podSpec *v1.PodSpec, volumes := podSpec.Volumes mountPaths := map[string]acidv1.AdditionalVolume{} - for i, v := range additionalVolumes { - if previousVolume, exist := mountPaths[v.MountPath]; exist { + for i, additionalVolume := range additionalVolumes { + if previousVolume, exist := mountPaths[additionalVolume.MountPath]; exist { msg := "Volume %+v cannot be mounted to the same path as %+v" - c.logger.Warningf(msg, v, previousVolume) + c.logger.Warningf(msg, additionalVolume, previousVolume) continue } - if v.MountPath == constants.PostgresDataMount { + if additionalVolume.MountPath == constants.PostgresDataMount { msg := "Cannot mount volume on postgresql data directory, %+v" - c.logger.Warningf(msg, v) + c.logger.Warningf(msg, additionalVolume) continue } - if len(v.TargetContainers) == 0 { - spiloContainer := podSpec.Containers[0] - additionalVolumes[i].TargetContainers = []string{spiloContainer.Name} + // if no target container is defined assign it to postgres container + if len(additionalVolume.TargetContainers) == 0 { + for j := range podSpec.Containers { + if podSpec.Containers[j].Name == c.containerName() { + additionalVolumes[i].TargetContainers = []string{c.containerName()} + } + } } - for _, target := range v.TargetContainers { - if target == "all" && len(v.TargetContainers) != 1 { + for _, target := range additionalVolume.TargetContainers { + if target == "all" && len(additionalVolume.TargetContainers) != 1 { msg := `Target containers could be either "all" or a list of containers, mixing those is not allowed, %+v` - c.logger.Warningf(msg, v) + c.logger.Warningf(msg, additionalVolume) continue } } volumes = append(volumes, v1.Volume{ - Name: v.Name, - VolumeSource: v.VolumeSource, + Name: additionalVolume.Name, + VolumeSource: additionalVolume.VolumeSource, }, ) - mountPaths[v.MountPath] = v + mountPaths[additionalVolume.MountPath] = additionalVolume } c.logger.Infof("Mount additional volumes: %+v", additionalVolumes) for i := range podSpec.Containers { mounts := podSpec.Containers[i].VolumeMounts - for _, v := range additionalVolumes { - for _, target := range v.TargetContainers { + for _, additionalVolume := range additionalVolumes { + for _, target := range additionalVolume.TargetContainers { if podSpec.Containers[i].Name == target || target == "all" { mounts = append(mounts, v1.VolumeMount{ - Name: v.Name, - MountPath: v.MountPath, - SubPath: v.SubPath, + Name: additionalVolume.Name, + MountPath: additionalVolume.MountPath, + SubPath: additionalVolume.SubPath, }) } } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index e987e744b..6bfe0da36 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -357,8 +357,8 @@ func (c *Cluster) syncStatefulSet() error { // and // (b) some of the pods were not restarted when the lazy update was still in place for _, pod := range pods { - effectivePodImage := pod.Spec.Containers[0].Image - stsImage := desiredSts.Spec.Template.Spec.Containers[0].Image + effectivePodImage := c.getPostgresContainer(&pod.Spec).Image + stsImage := c.getPostgresContainer(&desiredSts.Spec.Template.Spec).Image if stsImage != effectivePodImage { if err = c.markRollingUpdateFlagForPod(&pod, "pod not yet restarted due to lazy update"); err != nil { diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index 6acecc38d..a4a1c37ab 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -227,6 +227,16 @@ func (c *Cluster) logServiceChanges(role PostgresRole, old, new *v1.Service, isU } } +func (c *Cluster) getPostgresContainer(podSpec *v1.PodSpec) v1.Container { + var pgContainer v1.Container + for _, container := range podSpec.Containers { + if container.Name == constants.PostgresContainerName { + pgContainer = container + } + } + return pgContainer +} + func (c *Cluster) getTeamMembers(teamID string) ([]string, error) { if teamID == "" { diff --git a/pkg/util/constants/kubernetes.go b/pkg/util/constants/kubernetes.go index be79687eb..fd2712acc 100644 --- a/pkg/util/constants/kubernetes.go +++ b/pkg/util/constants/kubernetes.go @@ -5,7 +5,6 @@ import "time" // General kubernetes-related constants const ( PostgresContainerName = "postgres" - PostgresContainerIdx = 0 K8sAPIPath = "/apis" QueueResyncPeriodPod = 5 * time.Minute From dd9c3907b77f0da438bd58906d76667ed915be4d Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Fri, 28 May 2021 11:44:10 +0200 Subject: [PATCH 11/12] pick first container if postgres is not found (#1505) * pick first container if postgres is not found * minor change --- pkg/cluster/cluster.go | 6 ++++-- pkg/cluster/exec.go | 5 +++-- pkg/cluster/k8sres.go | 34 ++++++++++++++++------------------ pkg/cluster/k8sres_test.go | 24 ++++++++++++++---------- pkg/cluster/sync.go | 4 ++-- pkg/cluster/util.go | 8 ++++++-- 6 files changed, 45 insertions(+), 36 deletions(-) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 5b4d15ba5..ff474884c 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -462,7 +462,7 @@ func (c *Cluster) compareStatefulSetWith(statefulSet *appsv1.StatefulSet) *compa if len(c.Statefulset.Spec.Template.Spec.Volumes) != len(statefulSet.Spec.Template.Spec.Volumes) { needsReplace = true - reasons = append(reasons, fmt.Sprintf("new statefulset's Volumes contains different number of volumes to the old one")) + reasons = append(reasons, "new statefulset's volumes contains different number of volumes to the old one") } // we assume any change in priority happens by rolling out a new priority class @@ -476,7 +476,9 @@ func (c *Cluster) compareStatefulSetWith(statefulSet *appsv1.StatefulSet) *compa // lazy Spilo update: modify the image in the statefulset itself but let its pods run with the old image // until they are re-created for other reasons, for example node rotation - if c.OpConfig.EnableLazySpiloUpgrade && !reflect.DeepEqual(c.Statefulset.Spec.Template.Spec.Containers[0].Image, statefulSet.Spec.Template.Spec.Containers[0].Image) { + effectivePodImage := getPostgresContainer(&c.Statefulset.Spec.Template.Spec).Image + desiredImage := getPostgresContainer(&statefulSet.Spec.Template.Spec).Image + if c.OpConfig.EnableLazySpiloUpgrade && !reflect.DeepEqual(effectivePodImage, desiredImage) { needsReplace = true reasons = append(reasons, "lazy Spilo update: new statefulset's pod image does not match the current one") } diff --git a/pkg/cluster/exec.go b/pkg/cluster/exec.go index 462f702d7..8b5089b4e 100644 --- a/pkg/cluster/exec.go +++ b/pkg/cluster/exec.go @@ -12,6 +12,7 @@ import ( "k8s.io/client-go/tools/remotecommand" "github.com/zalando/postgres-operator/pkg/spec" + "github.com/zalando/postgres-operator/pkg/util/constants" ) //ExecCommand executes arbitrary command inside the pod @@ -31,14 +32,14 @@ func (c *Cluster) ExecCommand(podName *spec.NamespacedName, command ...string) ( // iterate through all containers looking for the one running PostgreSQL. targetContainer := -1 for i, cr := range pod.Spec.Containers { - if cr.Name == c.containerName() { + if cr.Name == constants.PostgresContainerName { targetContainer = i break } } if targetContainer < 0 { - return "", fmt.Errorf("could not find %s container to exec to", c.containerName()) + return "", fmt.Errorf("could not find %s container to exec to", constants.PostgresContainerName) } req := c.KubeClient.RESTClient.Post(). diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 2bab55bc8..cb11170d6 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -66,10 +66,6 @@ type spiloConfiguration struct { Bootstrap pgBootstrap `json:"bootstrap"` } -func (c *Cluster) containerName() string { - return constants.PostgresContainerName -} - func (c *Cluster) statefulSetName() string { return c.Name } @@ -1157,10 +1153,10 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef c.logger.Debugf("Generating Spilo container, environment variables") c.logger.Debugf("%v", spiloEnvVars) - spiloContainer := generateContainer(c.containerName(), + spiloContainer := generateContainer(constants.PostgresContainerName, &effectiveDockerImage, resourceRequirements, - deduplicateEnvVars(spiloEnvVars, c.containerName(), c.logger), + deduplicateEnvVars(spiloEnvVars, constants.PostgresContainerName, c.logger), volumeMounts, c.OpConfig.Resources.SpiloPrivileged, c.OpConfig.Resources.SpiloAllowPrivilegeEscalation, @@ -1392,6 +1388,9 @@ func (c *Cluster) getNumberOfInstances(spec *acidv1.PostgresSpec) int32 { // // see https://docs.okd.io/latest/dev_guide/shared_memory.html func addShmVolume(podSpec *v1.PodSpec) { + + postgresContainerIdx := 0 + volumes := append(podSpec.Volumes, v1.Volume{ Name: constants.ShmVolumeName, VolumeSource: v1.VolumeSource{ @@ -1403,16 +1402,18 @@ func addShmVolume(podSpec *v1.PodSpec) { for i, container := range podSpec.Containers { if container.Name == constants.PostgresContainerName { - mounts := append(container.VolumeMounts, - v1.VolumeMount{ - Name: constants.ShmVolumeName, - MountPath: constants.ShmVolumePath, - }) - - podSpec.Containers[i].VolumeMounts = mounts + postgresContainerIdx = i } } + mounts := append(podSpec.Containers[postgresContainerIdx].VolumeMounts, + v1.VolumeMount{ + Name: constants.ShmVolumeName, + MountPath: constants.ShmVolumePath, + }) + + podSpec.Containers[postgresContainerIdx].VolumeMounts = mounts + podSpec.Volumes = volumes } @@ -1458,11 +1459,8 @@ func (c *Cluster) addAdditionalVolumes(podSpec *v1.PodSpec, // if no target container is defined assign it to postgres container if len(additionalVolume.TargetContainers) == 0 { - for j := range podSpec.Containers { - if podSpec.Containers[j].Name == c.containerName() { - additionalVolumes[i].TargetContainers = []string{c.containerName()} - } - } + postgresContainer := getPostgresContainer(podSpec) + additionalVolumes[i].TargetContainers = []string{postgresContainer.Name} } for _, target := range additionalVolume.TargetContainers { diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 6dd42419c..5acd4a159 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -413,7 +413,6 @@ func TestShmVolume(t *testing.T) { Volumes: []v1.Volume{}, Containers: []v1.Container{ { - Name: "postgres", VolumeMounts: []v1.VolumeMount{}, }, }, @@ -438,9 +437,10 @@ func TestShmVolume(t *testing.T) { } for _, tt := range tests { addShmVolume(tt.podSpec) + postgresContainer := getPostgresContainer(tt.podSpec) volumeName := tt.podSpec.Volumes[tt.shmPos].Name - volumeMountName := tt.podSpec.Containers[0].VolumeMounts[tt.shmPos].Name + volumeMountName := postgresContainer.VolumeMounts[tt.shmPos].Name if volumeName != constants.ShmVolumeName { t.Errorf("%s %s: Expected volume %s was not created, have %s instead", @@ -612,8 +612,9 @@ func TestSecretVolume(t *testing.T) { for _, tt := range tests { additionalSecretMount := "aws-iam-s3-role" additionalSecretMountPath := "/meta/credentials" + postgresContainer := getPostgresContainer(tt.podSpec) - numMounts := len(tt.podSpec.Containers[0].VolumeMounts) + numMounts := len(postgresContainer.VolumeMounts) addSecretVolume(tt.podSpec, additionalSecretMount, additionalSecretMountPath) @@ -633,7 +634,8 @@ func TestSecretVolume(t *testing.T) { } } - numMountsCheck := len(tt.podSpec.Containers[0].VolumeMounts) + postgresContainer = getPostgresContainer(tt.podSpec) + numMountsCheck := len(postgresContainer.VolumeMounts) if numMountsCheck != numMounts+1 { t.Errorf("Unexpected number of VolumeMounts: got %v instead of %v", @@ -865,7 +867,8 @@ func testEnvs(cluster *Cluster, podSpec *v1.PodTemplateSpec, role PostgresRole) "CONNECTION_POOLER_PORT": false, } - envs := podSpec.Spec.Containers[0].Env + container := getPostgresContainer(&podSpec.Spec) + envs := container.Env for _, env := range envs { required[env.Name] = true } @@ -1045,14 +1048,15 @@ func TestTLS(t *testing.T) { } assert.Contains(t, sts.Spec.Template.Spec.Volumes, volume, "the pod gets a secret volume") - assert.Contains(t, sts.Spec.Template.Spec.Containers[0].VolumeMounts, v1.VolumeMount{ + postgresContainer := getPostgresContainer(&sts.Spec.Template.Spec) + assert.Contains(t, postgresContainer.VolumeMounts, v1.VolumeMount{ MountPath: "/tls", Name: "my-secret", }, "the volume gets mounted in /tls") - assert.Contains(t, sts.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "SSL_CERTIFICATE_FILE", Value: "/tls/tls.crt"}) - assert.Contains(t, sts.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "SSL_PRIVATE_KEY_FILE", Value: "/tls/tls.key"}) - assert.Contains(t, sts.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "SSL_CA_FILE", Value: "/tls/ca.crt"}) + assert.Contains(t, postgresContainer.Env, v1.EnvVar{Name: "SSL_CERTIFICATE_FILE", Value: "/tls/tls.crt"}) + assert.Contains(t, postgresContainer.Env, v1.EnvVar{Name: "SSL_PRIVATE_KEY_FILE", Value: "/tls/tls.key"}) + assert.Contains(t, postgresContainer.Env, v1.EnvVar{Name: "SSL_CA_FILE", Value: "/tls/ca.crt"}) } func TestAdditionalVolume(t *testing.T) { @@ -1147,7 +1151,7 @@ func TestAdditionalVolume(t *testing.T) { }{ { subTest: "checking volume mounts of postgres container", - container: cluster.containerName(), + container: constants.PostgresContainerName, expectedMounts: []string{"pgdata", "test1", "test3", "test4"}, }, { diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 6bfe0da36..94e930290 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -357,8 +357,8 @@ func (c *Cluster) syncStatefulSet() error { // and // (b) some of the pods were not restarted when the lazy update was still in place for _, pod := range pods { - effectivePodImage := c.getPostgresContainer(&pod.Spec).Image - stsImage := c.getPostgresContainer(&desiredSts.Spec.Template.Spec).Image + effectivePodImage := getPostgresContainer(&pod.Spec).Image + stsImage := getPostgresContainer(&desiredSts.Spec.Template.Spec).Image if stsImage != effectivePodImage { if err = c.markRollingUpdateFlagForPod(&pod, "pod not yet restarted due to lazy update"); err != nil { diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index a4a1c37ab..4350f9d56 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -227,13 +227,17 @@ func (c *Cluster) logServiceChanges(role PostgresRole, old, new *v1.Service, isU } } -func (c *Cluster) getPostgresContainer(podSpec *v1.PodSpec) v1.Container { - var pgContainer v1.Container +func getPostgresContainer(podSpec *v1.PodSpec) (pgContainer v1.Container) { for _, container := range podSpec.Containers { if container.Name == constants.PostgresContainerName { pgContainer = container } } + + // if no postgres container was found, take the first one in the podSpec + if reflect.DeepEqual(pgContainer, v1.Container{}) && len(podSpec.Containers) > 0 { + pgContainer = podSpec.Containers[0] + } return pgContainer } From a37e78bd9a7f64534c9e2710c6f25f4fa14ee28e Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Fri, 28 May 2021 11:53:10 +0200 Subject: [PATCH 12/12] bump operator to v1.6.3 (#1503) --- .../postgres-operator-issue-template.md | 2 +- README.md | 2 +- charts/postgres-operator-ui/Chart.yaml | 4 +-- charts/postgres-operator-ui/index.yaml | 32 +++++++++++++++--- .../postgres-operator-ui-1.6.3.tgz | Bin 0 -> 4050 bytes charts/postgres-operator-ui/values.yaml | 2 +- charts/postgres-operator/Chart.yaml | 4 +-- .../crds/operatorconfigurations.yaml | 4 +-- .../postgres-operator/crds/postgresqls.yaml | 2 +- charts/postgres-operator/index.yaml | 31 ++++++++++++++--- .../postgres-operator-1.6.3.tgz | Bin 0 -> 20124 bytes charts/postgres-operator/values-crd.yaml | 6 ++-- charts/postgres-operator/values.yaml | 6 ++-- docs/administrator.md | 2 +- docs/reference/operator_parameters.md | 2 +- e2e/tests/test_e2e.py | 6 ++-- manifests/complete-postgres-manifest.yaml | 2 +- manifests/configmap.yaml | 4 +-- manifests/operatorconfiguration.crd.yaml | 4 +-- manifests/postgres-operator.yaml | 2 +- ...gresql-operator-default-configuration.yaml | 4 +-- manifests/postgresql.crd.yaml | 2 +- pkg/controller/operator_config.go | 4 +-- pkg/util/config/config.go | 8 ++--- 24 files changed, 89 insertions(+), 46 deletions(-) create mode 100644 charts/postgres-operator-ui/postgres-operator-ui-1.6.3.tgz create mode 100644 charts/postgres-operator/postgres-operator-1.6.3.tgz diff --git a/.github/ISSUE_TEMPLATE/postgres-operator-issue-template.md b/.github/ISSUE_TEMPLATE/postgres-operator-issue-template.md index f53be1319..93392a68e 100644 --- a/.github/ISSUE_TEMPLATE/postgres-operator-issue-template.md +++ b/.github/ISSUE_TEMPLATE/postgres-operator-issue-template.md @@ -9,7 +9,7 @@ assignees: '' Please, answer some short questions which should help us to understand your problem / question better? -- **Which image of the operator are you using?** e.g. registry.opensource.zalan.do/acid/postgres-operator:v1.6.2 +- **Which image of the operator are you using?** e.g. registry.opensource.zalan.do/acid/postgres-operator:v1.6.3 - **Where do you run it - cloud or metal? Kubernetes or OpenShift?** [AWS K8s | GCP ... | Bare Metal K8s] - **Are you running Postgres Operator in production?** [yes | no] - **Type of issue?** [Bug report, question, feature request, etc.] diff --git a/README.md b/README.md index fd7e731db..ebe84bf5f 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ We introduce the major version into the backup path to smoothen the [major versi The new operator configuration can set a compatibility flag *enable_spilo_wal_path_compat* to make Spilo look for wal segments in the current path but also old format paths. This comes at potential performance costs and should be disabled after a few days. -The newest Spilo 13 image is: `registry.opensource.zalan.do/acid/spilo-13:2.0-p6` +The newest Spilo 13 image is: `registry.opensource.zalan.do/acid/spilo-13:2.0-p7` The last Spilo 12 image is: `registry.opensource.zalan.do/acid/spilo-12:1.6-p5` diff --git a/charts/postgres-operator-ui/Chart.yaml b/charts/postgres-operator-ui/Chart.yaml index 4ad4de5e6..e9c10868c 100644 --- a/charts/postgres-operator-ui/Chart.yaml +++ b/charts/postgres-operator-ui/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v1 name: postgres-operator-ui -version: 1.6.2 -appVersion: 1.6.2 +version: 1.6.3 +appVersion: 1.6.3 home: https://github.com/zalando/postgres-operator description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience keywords: diff --git a/charts/postgres-operator-ui/index.yaml b/charts/postgres-operator-ui/index.yaml index 19db0b74f..f76bfcbb4 100644 --- a/charts/postgres-operator-ui/index.yaml +++ b/charts/postgres-operator-ui/index.yaml @@ -1,9 +1,31 @@ apiVersion: v1 entries: postgres-operator-ui: + - apiVersion: v1 + appVersion: 1.6.3 + created: "2021-05-27T19:04:33.425637932+02:00" + description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience + digest: 08b810aa632dcc719e4785ef184e391267f7c460caa99677f2d00719075aac78 + home: https://github.com/zalando/postgres-operator + keywords: + - postgres + - operator + - ui + - cloud-native + - patroni + - spilo + maintainers: + - email: opensource@zalando.de + name: Zalando + name: postgres-operator-ui + sources: + - https://github.com/zalando/postgres-operator + urls: + - postgres-operator-ui-1.6.3.tgz + version: 1.6.3 - apiVersion: v1 appVersion: 1.6.2 - created: "2021-04-06T16:47:40.993908218+02:00" + created: "2021-05-27T19:04:33.422124263+02:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: 14d1559bb0bd1e1e828f2daaaa6f6ac9ffc268d79824592c3589b55dd39241f6 home: https://github.com/zalando/postgres-operator @@ -25,7 +47,7 @@ entries: version: 1.6.2 - apiVersion: v1 appVersion: 1.6.1 - created: "2021-04-06T16:47:40.993378451+02:00" + created: "2021-05-27T19:04:33.419640902+02:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: 3d321352f2f1e7bb7450aa8876e3d818aa9f9da9bd4250507386f0490f2c1969 home: https://github.com/zalando/postgres-operator @@ -47,7 +69,7 @@ entries: version: 1.6.1 - apiVersion: v1 appVersion: 1.6.0 - created: "2021-04-06T16:47:40.992871656+02:00" + created: "2021-05-27T19:04:33.41788193+02:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: 1e0aa1e7db3c1daa96927ffbf6fdbcdb434562f961833cb5241ddbe132220ee4 home: https://github.com/zalando/postgres-operator @@ -69,7 +91,7 @@ entries: version: 1.6.0 - apiVersion: v1 appVersion: 1.5.0 - created: "2021-04-06T16:47:40.992346484+02:00" + created: "2021-05-27T19:04:33.416056821+02:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: c91ea39e6d51d57f4048fb1b6ec53b40823f2690eb88e4e4f1a036367b9fdd61 home: https://github.com/zalando/postgres-operator @@ -89,4 +111,4 @@ entries: urls: - postgres-operator-ui-1.5.0.tgz version: 1.5.0 -generated: "2021-04-06T16:47:40.991668273+02:00" +generated: "2021-05-27T19:04:33.41380858+02:00" diff --git a/charts/postgres-operator-ui/postgres-operator-ui-1.6.3.tgz b/charts/postgres-operator-ui/postgres-operator-ui-1.6.3.tgz new file mode 100644 index 0000000000000000000000000000000000000000..45e3d00b2cc022508d5560eec44d630897b98d4b GIT binary patch literal 4050 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 E0Gd|!Gynhq literal 0 HcmV?d00001 diff --git a/charts/postgres-operator-ui/values.yaml b/charts/postgres-operator-ui/values.yaml index db44ce375..c9f521464 100644 --- a/charts/postgres-operator-ui/values.yaml +++ b/charts/postgres-operator-ui/values.yaml @@ -8,7 +8,7 @@ replicaCount: 1 image: registry: registry.opensource.zalan.do repository: acid/postgres-operator-ui - tag: v1.6.2 + tag: v1.6.3 pullPolicy: "IfNotPresent" # Optionally specify an array of imagePullSecrets. diff --git a/charts/postgres-operator/Chart.yaml b/charts/postgres-operator/Chart.yaml index 94dea3a15..28d0f811d 100644 --- a/charts/postgres-operator/Chart.yaml +++ b/charts/postgres-operator/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v1 name: postgres-operator -version: 1.6.2 -appVersion: 1.6.2 +version: 1.6.3 +appVersion: 1.6.3 home: https://github.com/zalando/postgres-operator description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes keywords: diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 0f036c299..82a737ae6 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -65,7 +65,7 @@ spec: properties: docker_image: type: string - default: "registry.opensource.zalan.do/acid/spilo-13:2.0-p6" + default: "registry.opensource.zalan.do/acid/spilo-13:2.0-p7" enable_crd_validation: type: boolean default: true @@ -397,7 +397,7 @@ spec: properties: logical_backup_docker_image: type: string - default: "registry.opensource.zalan.do/acid/logical-backup:v1.6.2" + default: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" logical_backup_google_application_credentials: type: string logical_backup_job_prefix: diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index ad11f6407..aead7fe69 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -27,7 +27,7 @@ spec: additionalPrinterColumns: - name: Team type: string - description: Team responsible for Postgres CLuster + description: Team responsible for Postgres cluster jsonPath: .spec.teamId - name: Version type: string diff --git a/charts/postgres-operator/index.yaml b/charts/postgres-operator/index.yaml index e59cc7629..bbd762104 100644 --- a/charts/postgres-operator/index.yaml +++ b/charts/postgres-operator/index.yaml @@ -1,9 +1,30 @@ apiVersion: v1 entries: postgres-operator: + - apiVersion: v1 + appVersion: 1.6.3 + created: "2021-05-27T19:04:25.199523943+02:00" + description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes + digest: ea08f991bf23c9ad114bca98ebcbe3e2fa15beab163061399394905eaee89b35 + home: https://github.com/zalando/postgres-operator + keywords: + - postgres + - operator + - cloud-native + - patroni + - spilo + maintainers: + - email: opensource@zalando.de + name: Zalando + name: postgres-operator + sources: + - https://github.com/zalando/postgres-operator + urls: + - postgres-operator-1.6.3.tgz + version: 1.6.3 - apiVersion: v1 appVersion: 1.6.2 - created: "2021-03-30T17:00:50.171986449+02:00" + created: "2021-05-27T19:04:25.198182197+02:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: d886f8a0879ca07d1e5246ee7bc55710e1c872f3977280fe495db6fc2057a7f4 home: https://github.com/zalando/postgres-operator @@ -24,7 +45,7 @@ entries: version: 1.6.2 - apiVersion: v1 appVersion: 1.6.1 - created: "2021-03-30T17:00:50.170294515+02:00" + created: "2021-05-27T19:04:25.19687586+02:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 4ba5972cd486dcaa2d11c5613a6f97f6b7b831822e610fe9e10a57ea1db23556 home: https://github.com/zalando/postgres-operator @@ -45,7 +66,7 @@ entries: version: 1.6.1 - apiVersion: v1 appVersion: 1.6.0 - created: "2021-03-30T17:00:50.168493689+02:00" + created: "2021-05-27T19:04:25.195600766+02:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: f52149718ea364f46b4b9eec9a65f6253ad182bb78df541d14cd5277b9c8a8c3 home: https://github.com/zalando/postgres-operator @@ -66,7 +87,7 @@ entries: version: 1.6.0 - apiVersion: v1 appVersion: 1.5.0 - created: "2021-03-30T17:00:50.166722286+02:00" + created: "2021-05-27T19:04:25.193985032+02:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 198351d5db52e65cdf383d6f3e1745d91ac1e2a01121f8476f8b1be728b09531 home: https://github.com/zalando/postgres-operator @@ -85,4 +106,4 @@ entries: urls: - postgres-operator-1.5.0.tgz version: 1.5.0 -generated: "2021-03-30T17:00:50.165166707+02:00" +generated: "2021-05-27T19:04:25.191897769+02:00" diff --git a/charts/postgres-operator/postgres-operator-1.6.3.tgz b/charts/postgres-operator/postgres-operator-1.6.3.tgz new file mode 100644 index 0000000000000000000000000000000000000000..af84bf57bc65b7418564d29b4a7169f72c545f85 GIT binary patch literal 20124 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 literal 0 HcmV?d00001 diff --git a/charts/postgres-operator/values-crd.yaml b/charts/postgres-operator/values-crd.yaml index bd563a636..b1fc43261 100644 --- a/charts/postgres-operator/values-crd.yaml +++ b/charts/postgres-operator/values-crd.yaml @@ -1,7 +1,7 @@ image: registry: registry.opensource.zalan.do repository: acid/postgres-operator - tag: v1.6.2 + tag: v1.6.3 pullPolicy: "IfNotPresent" # Optionally specify an array of imagePullSecrets. @@ -32,7 +32,7 @@ configGeneral: # Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s) # kubernetes_use_configmaps: false # Spilo docker image - docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p6 + docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p7 # min number of instances in Postgres cluster. -1 = no limit min_instances: -1 # max number of instances in Postgres cluster. -1 = no limit @@ -263,7 +263,7 @@ configAwsOrGcp: # configure K8s cron job managed by the operator configLogicalBackup: # image for pods of the logical backup job (example runs pg_dumpall) - logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.2" + logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" # path of google cloud service account json file # logical_backup_google_application_credentials: "" diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 15a53a00d..fde4e203d 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -1,7 +1,7 @@ image: registry: registry.opensource.zalan.do repository: acid/postgres-operator - tag: v1.6.2 + tag: v1.6.3 pullPolicy: "IfNotPresent" # Optionally specify an array of imagePullSecrets. @@ -35,7 +35,7 @@ configGeneral: # Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s) # kubernetes_use_configmaps: "false" # Spilo docker image - docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p6 + docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p7 # min number of instances in Postgres cluster. -1 = no limit min_instances: "-1" # max number of instances in Postgres cluster. -1 = no limit @@ -253,7 +253,7 @@ configAwsOrGcp: # configure K8s cron job managed by the operator configLogicalBackup: # image for pods of the logical backup job (example runs pg_dumpall) - logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.2" + logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" # path of google cloud service account json file # logical_backup_google_application_credentials: "" diff --git a/docs/administrator.md b/docs/administrator.md index 2b65fe3ea..db3bae6b9 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -950,7 +950,7 @@ make docker # build in image in minikube docker env eval $(minikube docker-env) -docker build -t registry.opensource.zalan.do/acid/postgres-operator-ui:v1.6.2 . +docker build -t registry.opensource.zalan.do/acid/postgres-operator-ui:v1.6.3 . # apply UI manifests next to a running Postgres Operator kubectl apply -f manifests/ diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index b0d982943..395007c91 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -593,7 +593,7 @@ grouped under the `logical_backup` key. runs `pg_dumpall` on a replica if possible and uploads compressed results to an S3 bucket under the key `/spilo/pg_cluster_name/cluster_k8s_uuid/logical_backups`. The default image is the same image built with the Zalando-internal CI - pipeline. Default: "registry.opensource.zalan.do/acid/logical-backup:v1.6.2" + pipeline. Default: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" * **logical_backup_google_application_credentials** Specifies the path of the google cloud service account json file. Default is empty. diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index bffb12386..2b9e3ad28 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -230,7 +230,7 @@ class EndToEndTestCase(unittest.TestCase): FROM pg_catalog.pg_roles WHERE rolname IN ('elephant', 'kind'); """ - self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2, + self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2, "Not all additional users found in database", 10, 5) # replace additional member and check if the removed member's role is renamed @@ -458,7 +458,7 @@ class EndToEndTestCase(unittest.TestCase): db_list = self.list_databases(leader.metadata.name) for db in db_list: - self.eventuallyNotEqual(lambda: len(self.query_database(leader.metadata.name, db, schemas_query)), 0, + self.eventuallyNotEqual(lambda: len(self.query_database(leader.metadata.name, db, schemas_query)), 0, "Pooler schema not found in database {}".format(db)) # remove config section to make test work next time @@ -789,7 +789,7 @@ class EndToEndTestCase(unittest.TestCase): } } k8s.api.custom_objects_api.patch_namespaced_custom_object( - "acid.zalan.do", "v1", "default", "postgresqls", "acid-minimal-cluster", pg_patch_resources) + "acid.zalan.do", "v1", "default", "postgresqls", "acid-minimal-cluster", pg_patch_resources) self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") self.eventuallyEqual(lambda: k8s.count_running_pods(), 2, "No two pods running after lazy rolling upgrade") diff --git a/manifests/complete-postgres-manifest.yaml b/manifests/complete-postgres-manifest.yaml index 75b873321..6e2acbdd3 100644 --- a/manifests/complete-postgres-manifest.yaml +++ b/manifests/complete-postgres-manifest.yaml @@ -9,7 +9,7 @@ metadata: # "delete-date": "2020-08-31" # can only be deleted on that day if "delete-date "key is configured # "delete-clustername": "acid-test-cluster" # can only be deleted when name matches if "delete-clustername" key is configured spec: - dockerImage: registry.opensource.zalan.do/acid/spilo-13:2.0-p6 + dockerImage: registry.opensource.zalan.do/acid/spilo-13:2.0-p7 teamId: "acid" numberOfInstances: 2 users: # Application/Robot users diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index b379975eb..7a05135ab 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -32,7 +32,7 @@ data: # default_memory_request: 100Mi # delete_annotation_date_key: delete-date # delete_annotation_name_key: delete-clustername - docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p6 + docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p7 # downscaler_annotations: "deployment-time,downscaler/*" # enable_admin_role_for_users: "true" # enable_crd_validation: "true" @@ -64,7 +64,7 @@ data: # inherited_labels: application,environment # kube_iam_role: "" # log_s3_bucket: "" - logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.2" + logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" # logical_backup_google_application_credentials: "" logical_backup_job_prefix: "logical-backup-" logical_backup_provider: "s3" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index fbed0bea1..806acc8da 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -61,7 +61,7 @@ spec: properties: docker_image: type: string - default: "registry.opensource.zalan.do/acid/spilo-13:2.0-p6" + default: "registry.opensource.zalan.do/acid/spilo-13:2.0-p7" enable_crd_validation: type: boolean default: true @@ -393,7 +393,7 @@ spec: properties: logical_backup_docker_image: type: string - default: "registry.opensource.zalan.do/acid/logical-backup:v1.6.2" + default: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" logical_backup_google_application_credentials: type: string logical_backup_job_prefix: diff --git a/manifests/postgres-operator.yaml b/manifests/postgres-operator.yaml index 5e189c819..3dbe3acb9 100644 --- a/manifests/postgres-operator.yaml +++ b/manifests/postgres-operator.yaml @@ -19,7 +19,7 @@ spec: serviceAccountName: postgres-operator containers: - name: postgres-operator - image: registry.opensource.zalan.do/acid/postgres-operator:v1.6.2 + image: registry.opensource.zalan.do/acid/postgres-operator:v1.6.3 imagePullPolicy: IfNotPresent resources: requests: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 65dfd6ce4..bd6f321dd 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -3,7 +3,7 @@ kind: OperatorConfiguration metadata: name: postgresql-operator-default-configuration configuration: - docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p6 + docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p7 # enable_crd_validation: true # enable_lazy_spilo_upgrade: false enable_pgversion_env_var: true @@ -123,7 +123,7 @@ configuration: # wal_gs_bucket: "" # wal_s3_bucket: "" logical_backup: - logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.2" + logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" # logical_backup_google_application_credentials: "" logical_backup_job_prefix: "logical-backup-" logical_backup_provider: "s3" diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index 61a04144c..30b41d392 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -23,7 +23,7 @@ spec: additionalPrinterColumns: - name: Team type: string - description: Team responsible for Postgres CLuster + description: Team responsible for Postgres cluster jsonPath: .spec.teamId - name: Version type: string diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index fbec7a462..761cf1b60 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -39,7 +39,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.EnableSpiloWalPathCompat = fromCRD.EnableSpiloWalPathCompat result.EtcdHost = fromCRD.EtcdHost result.KubernetesUseConfigMaps = fromCRD.KubernetesUseConfigMaps - result.DockerImage = util.Coalesce(fromCRD.DockerImage, "registry.opensource.zalan.do/acid/spilo-13:2.0-p6") + result.DockerImage = util.Coalesce(fromCRD.DockerImage, "registry.opensource.zalan.do/acid/spilo-13:2.0-p7") result.Workers = util.CoalesceUInt32(fromCRD.Workers, 8) result.MinInstances = fromCRD.MinInstances result.MaxInstances = fromCRD.MaxInstances @@ -152,7 +152,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur // logical backup config result.LogicalBackupSchedule = util.Coalesce(fromCRD.LogicalBackup.Schedule, "30 00 * * *") - result.LogicalBackupDockerImage = util.Coalesce(fromCRD.LogicalBackup.DockerImage, "registry.opensource.zalan.do/acid/logical-backup:v1.6.2") + result.LogicalBackupDockerImage = util.Coalesce(fromCRD.LogicalBackup.DockerImage, "registry.opensource.zalan.do/acid/logical-backup:v1.6.3") result.LogicalBackupProvider = util.Coalesce(fromCRD.LogicalBackup.BackupProvider, "s3") result.LogicalBackupS3Bucket = fromCRD.LogicalBackup.S3Bucket result.LogicalBackupS3Region = fromCRD.LogicalBackup.S3Region diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index c8430e7a6..52530fb12 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -28,8 +28,8 @@ type Resources struct { PodLabelWaitTimeout time.Duration `name:"pod_label_wait_timeout" default:"10m"` PodDeletionWaitTimeout time.Duration `name:"pod_deletion_wait_timeout" default:"10m"` PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"` - SpiloRunAsUser *int64 `name:"spilo_runasuser,omitempty"` - SpiloRunAsGroup *int64 `name:"spilo_runasgroup,omitempty"` + SpiloRunAsUser *int64 `name:"spilo_runasuser"` + SpiloRunAsGroup *int64 `name:"spilo_runasgroup"` SpiloFSGroup *int64 `name:"spilo_fsgroup"` PodPriorityClassName string `name:"pod_priority_class_name"` ClusterDomain string `name:"cluster_domain" default:"cluster.local"` @@ -114,7 +114,7 @@ type Scalyr struct { // LogicalBackup defines configuration for logical backup type LogicalBackup struct { LogicalBackupSchedule string `name:"logical_backup_schedule" default:"30 00 * * *"` - LogicalBackupDockerImage string `name:"logical_backup_docker_image" default:"registry.opensource.zalan.do/acid/logical-backup:v1.6.2"` + LogicalBackupDockerImage string `name:"logical_backup_docker_image" default:"registry.opensource.zalan.do/acid/logical-backup:v1.6.3"` LogicalBackupProvider string `name:"logical_backup_provider" default:"s3"` LogicalBackupS3Bucket string `name:"logical_backup_s3_bucket" default:""` LogicalBackupS3Region string `name:"logical_backup_s3_region" default:""` @@ -152,7 +152,7 @@ type Config struct { WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to' KubernetesUseConfigMaps bool `name:"kubernetes_use_configmaps" default:"false"` EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS - DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spilo-13:2.0-p6"` + DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spilo-13:2.0-p7"` SidecarImages map[string]string `name:"sidecar_docker_images"` // deprecated in favour of SidecarContainers SidecarContainers []v1.Container `name:"sidecars"` PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"`