Make teamId in cluster name optional (#2001)

* making teamId in clustername optional
* move teamId check to addCluster function
This commit is contained in:
Felix Kunde 2022-08-24 10:12:50 +02:00 committed by GitHub
parent b91b69c736
commit 3bfd63cbe6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 96 additions and 76 deletions

View File

@ -88,6 +88,9 @@ spec:
enable_spilo_wal_path_compat: enable_spilo_wal_path_compat:
type: boolean type: boolean
default: false default: false
enable_team_id_clustername_prefix:
type: boolean
default: false
etcd_host: etcd_host:
type: string type: string
default: "" default: ""

View File

@ -33,6 +33,8 @@ configGeneral:
enable_shm_volume: true enable_shm_volume: true
# enables backwards compatible path between Spilo 12 and Spilo 13+ images # enables backwards compatible path between Spilo 12 and Spilo 13+ images
enable_spilo_wal_path_compat: false enable_spilo_wal_path_compat: false
# operator will sync only clusters where name starts with teamId prefix
enable_team_id_clustername_prefix: false
# etcd connection string for Patroni. Empty uses K8s-native DCS. # etcd connection string for Patroni. Empty uses K8s-native DCS.
etcd_host: "" etcd_host: ""
# Spilo docker image # Spilo docker image

View File

@ -53,8 +53,7 @@ Those parameters are grouped under the `metadata` top-level key.
These parameters are grouped directly under the `spec` key in the manifest. These parameters are grouped directly under the `spec` key in the manifest.
* **teamId** * **teamId**
name of the team the cluster belongs to. Changing it after the cluster name of the team the cluster belongs to. Required field.
creation is not supported. Required field.
* **numberOfInstances** * **numberOfInstances**
total number of instances for a given cluster. The operator parameters total number of instances for a given cluster. The operator parameters

View File

@ -92,6 +92,11 @@ Those are top-level keys, containing both leaf keys and groups.
* **enable_spilo_wal_path_compat** * **enable_spilo_wal_path_compat**
enables backwards compatible path between Spilo 12 and Spilo 13+ images. The default is `false`. enables backwards compatible path between Spilo 12 and Spilo 13+ images. The default is `false`.
* **enable_team_id_clustername_prefix**
To lower the risk of name clashes between clusters of different teams you
can turn on this flag and the operator will sync only clusters where the
name starts with the `teamId` (from `spec`) plus `-`. Default is `false`.
* **etcd_host** * **etcd_host**
Etcd connection string for Patroni defined as `host:port`. Not required when Etcd connection string for Patroni defined as `host:port`. Not required when
Patroni native Kubernetes support is used. The default is empty (use Patroni native Kubernetes support is used. The default is empty (use

View File

@ -45,11 +45,12 @@ Make sure, the `spec` section of the manifest contains at least a `teamId`, the
The minimum volume size to run the `postgresql` resource on Elastic Block The minimum volume size to run the `postgresql` resource on Elastic Block
Storage (EBS) is `1Gi`. Storage (EBS) is `1Gi`.
Note, that the name of the cluster must start with the `teamId` and `-`. At Note, that when `enable_team_id_clustername_prefix` is set to `true` the name
Zalando we use team IDs (nicknames) to lower the chance of duplicate cluster of the cluster must start with the `teamId` and `-`. At Zalando we use team IDs
names and colliding entities. The team ID would also be used to query an API to (nicknames) to lower chances of duplicate cluster names and colliding entities.
get all members of a team and create [database roles](#teams-api-roles) for The team ID would also be used to query an API to get all members of a team
them. Besides, the maximum cluster name length is 53 characters. and create [database roles](#teams-api-roles) for them. Besides, the maximum
cluster name length is 53 characters.
## Watch pods being created ## Watch pods being created

View File

@ -57,6 +57,7 @@ data:
# enable_shm_volume: "true" # enable_shm_volume: "true"
# enable_sidecars: "true" # enable_sidecars: "true"
enable_spilo_wal_path_compat: "true" enable_spilo_wal_path_compat: "true"
enable_team_id_clustername_prefix: "false"
enable_team_member_deprecation: "false" enable_team_member_deprecation: "false"
# enable_team_superuser: "false" # enable_team_superuser: "false"
enable_teams_api: "false" enable_teams_api: "false"

View File

@ -86,6 +86,9 @@ spec:
enable_spilo_wal_path_compat: enable_spilo_wal_path_compat:
type: boolean type: boolean
default: false default: false
enable_team_id_clustername_prefix:
type: boolean
default: false
etcd_host: etcd_host:
type: string type: string
default: "" default: ""

View File

@ -11,6 +11,7 @@ configuration:
enable_pgversion_env_var: true enable_pgversion_env_var: true
# enable_shm_volume: true # enable_shm_volume: true
enable_spilo_wal_path_compat: false enable_spilo_wal_path_compat: false
enable_team_id_clustername_prefix: false
etcd_host: "" etcd_host: ""
# ignore_instance_limits_annotation_key: "" # ignore_instance_limits_annotation_key: ""
# kubernetes_use_configmaps: false # kubernetes_use_configmaps: false

View File

@ -1112,6 +1112,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{
"enable_spilo_wal_path_compat": { "enable_spilo_wal_path_compat": {
Type: "boolean", Type: "boolean",
}, },
"enable_team_id_clustername_prefix": {
Type: "boolean",
},
"etcd_host": { "etcd_host": {
Type: "string", Type: "string",
}, },

View File

@ -110,15 +110,9 @@ func (p *Postgresql) UnmarshalJSON(data []byte) error {
} }
tmp2 := Postgresql(tmp) tmp2 := Postgresql(tmp)
if clusterName, err := extractClusterName(tmp2.ObjectMeta.Name, tmp2.Spec.TeamID); err != nil { if err := validateCloneClusterDescription(tmp2.Spec.Clone); err != nil {
tmp2.Error = err.Error()
tmp2.Status = PostgresStatus{PostgresClusterStatus: ClusterStatusInvalid}
} else if err := validateCloneClusterDescription(tmp2.Spec.Clone); err != nil {
tmp2.Error = err.Error() tmp2.Error = err.Error()
tmp2.Status.PostgresClusterStatus = ClusterStatusInvalid tmp2.Status.PostgresClusterStatus = ClusterStatusInvalid
} else {
tmp2.Spec.ClusterName = clusterName
} }
*p = tmp2 *p = tmp2

View File

@ -228,35 +228,36 @@ type OperatorLogicalBackupConfiguration struct {
// OperatorConfigurationData defines the operation config // OperatorConfigurationData defines the operation config
type OperatorConfigurationData struct { type OperatorConfigurationData struct {
EnableCRDRegistration *bool `json:"enable_crd_registration,omitempty"` EnableCRDRegistration *bool `json:"enable_crd_registration,omitempty"`
EnableCRDValidation *bool `json:"enable_crd_validation,omitempty"` EnableCRDValidation *bool `json:"enable_crd_validation,omitempty"`
CRDCategories []string `json:"crd_categories,omitempty"` CRDCategories []string `json:"crd_categories,omitempty"`
EnableLazySpiloUpgrade bool `json:"enable_lazy_spilo_upgrade,omitempty"` EnableLazySpiloUpgrade bool `json:"enable_lazy_spilo_upgrade,omitempty"`
EnablePgVersionEnvVar bool `json:"enable_pgversion_env_var,omitempty"` EnablePgVersionEnvVar bool `json:"enable_pgversion_env_var,omitempty"`
EnableSpiloWalPathCompat bool `json:"enable_spilo_wal_path_compat,omitempty"` EnableSpiloWalPathCompat bool `json:"enable_spilo_wal_path_compat,omitempty"`
EtcdHost string `json:"etcd_host,omitempty"` EnableTeamIdClusternamePrefix bool `json:"enable_team_id_clustername_prefix,omitempty"`
KubernetesUseConfigMaps bool `json:"kubernetes_use_configmaps,omitempty"` EtcdHost string `json:"etcd_host,omitempty"`
DockerImage string `json:"docker_image,omitempty"` KubernetesUseConfigMaps bool `json:"kubernetes_use_configmaps,omitempty"`
Workers uint32 `json:"workers,omitempty"` DockerImage string `json:"docker_image,omitempty"`
ResyncPeriod Duration `json:"resync_period,omitempty"` Workers uint32 `json:"workers,omitempty"`
RepairPeriod Duration `json:"repair_period,omitempty"` ResyncPeriod Duration `json:"resync_period,omitempty"`
SetMemoryRequestToLimit bool `json:"set_memory_request_to_limit,omitempty"` RepairPeriod Duration `json:"repair_period,omitempty"`
ShmVolume *bool `json:"enable_shm_volume,omitempty"` SetMemoryRequestToLimit bool `json:"set_memory_request_to_limit,omitempty"`
SidecarImages map[string]string `json:"sidecar_docker_images,omitempty"` // deprecated in favour of SidecarContainers ShmVolume *bool `json:"enable_shm_volume,omitempty"`
SidecarContainers []v1.Container `json:"sidecars,omitempty"` SidecarImages map[string]string `json:"sidecar_docker_images,omitempty"` // deprecated in favour of SidecarContainers
PostgresUsersConfiguration PostgresUsersConfiguration `json:"users"` SidecarContainers []v1.Container `json:"sidecars,omitempty"`
MajorVersionUpgrade MajorVersionUpgradeConfiguration `json:"major_version_upgrade"` PostgresUsersConfiguration PostgresUsersConfiguration `json:"users"`
Kubernetes KubernetesMetaConfiguration `json:"kubernetes"` MajorVersionUpgrade MajorVersionUpgradeConfiguration `json:"major_version_upgrade"`
PostgresPodResources PostgresPodResourcesDefaults `json:"postgres_pod_resources"` Kubernetes KubernetesMetaConfiguration `json:"kubernetes"`
Timeouts OperatorTimeouts `json:"timeouts"` PostgresPodResources PostgresPodResourcesDefaults `json:"postgres_pod_resources"`
LoadBalancer LoadBalancerConfiguration `json:"load_balancer"` Timeouts OperatorTimeouts `json:"timeouts"`
AWSGCP AWSGCPConfiguration `json:"aws_or_gcp"` LoadBalancer LoadBalancerConfiguration `json:"load_balancer"`
OperatorDebug OperatorDebugConfiguration `json:"debug"` AWSGCP AWSGCPConfiguration `json:"aws_or_gcp"`
TeamsAPI TeamsAPIConfiguration `json:"teams_api"` OperatorDebug OperatorDebugConfiguration `json:"debug"`
LoggingRESTAPI LoggingRESTAPIConfiguration `json:"logging_rest_api"` TeamsAPI TeamsAPIConfiguration `json:"teams_api"`
Scalyr ScalyrConfiguration `json:"scalyr"` LoggingRESTAPI LoggingRESTAPIConfiguration `json:"logging_rest_api"`
LogicalBackup OperatorLogicalBackupConfiguration `json:"logical_backup"` Scalyr ScalyrConfiguration `json:"scalyr"`
ConnectionPooler ConnectionPoolerConfiguration `json:"connection_pooler"` LogicalBackup OperatorLogicalBackupConfiguration `json:"logical_backup"`
ConnectionPooler ConnectionPoolerConfiguration `json:"connection_pooler"`
MinInstances int32 `json:"min_instances,omitempty"` MinInstances int32 `json:"min_instances,omitempty"`
MaxInstances int32 `json:"max_instances,omitempty"` MaxInstances int32 `json:"max_instances,omitempty"`

View File

@ -46,7 +46,7 @@ func parseWeekday(s string) (time.Weekday, error) {
return time.Weekday(weekday), nil return time.Weekday(weekday), nil
} }
func extractClusterName(clusterName string, teamName string) (string, error) { func ExtractClusterName(clusterName string, teamName string) (string, error) {
teamNameLen := len(teamName) teamNameLen := len(teamName)
if len(clusterName) < teamNameLen+2 { if len(clusterName) < teamNameLen+2 {
return "", fmt.Errorf("cluster name must match {TEAM}-{NAME} format. Got cluster name '%v', team name '%v'", clusterName, teamName) return "", fmt.Errorf("cluster name must match {TEAM}-{NAME} format. Got cluster name '%v', team name '%v'", clusterName, teamName)

View File

@ -330,29 +330,11 @@ var unmarshalCluster = []struct {
Clone: &CloneDescription{ Clone: &CloneDescription{
ClusterName: "acid-batman", ClusterName: "acid-batman",
}, },
ClusterName: "testcluster1",
}, },
Error: "", Error: "",
}, },
marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"9.6","parameters":{"log_statement":"all","max_connections":"10","shared_buffers":"32MB"}},"pod_priority_class_name":"spilo-pod-priority","volume":{"size":"5Gi","storageClass":"SSD", "subPath": "subdir"},"enableShmVolume":false,"patroni":{"initdb":{"data-checksums":"true","encoding":"UTF8","locale":"en_US.UTF-8"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"],"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}},"resources":{"requests":{"cpu":"10m","memory":"50Mi"},"limits":{"cpu":"300m","memory":"3000Mi"}},"teamId":"acid","allowedSourceRanges":["127.0.0.1/32"],"numberOfInstances":2,"users":{"zalando":["superuser","createdb"]},"maintenanceWindows":["Mon:01:00-06:00","Sat:00:00-04:00","05:00-05:15"],"clone":{"cluster":"acid-batman"}},"status":{"PostgresClusterStatus":""}}`), marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"9.6","parameters":{"log_statement":"all","max_connections":"10","shared_buffers":"32MB"}},"pod_priority_class_name":"spilo-pod-priority","volume":{"size":"5Gi","storageClass":"SSD", "subPath": "subdir"},"enableShmVolume":false,"patroni":{"initdb":{"data-checksums":"true","encoding":"UTF8","locale":"en_US.UTF-8"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"],"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}},"resources":{"requests":{"cpu":"10m","memory":"50Mi"},"limits":{"cpu":"300m","memory":"3000Mi"}},"teamId":"acid","allowedSourceRanges":["127.0.0.1/32"],"numberOfInstances":2,"users":{"zalando":["superuser","createdb"]},"maintenanceWindows":["Mon:01:00-06:00","Sat:00:00-04:00","05:00-05:15"],"clone":{"cluster":"acid-batman"}},"status":{"PostgresClusterStatus":""}}`),
err: nil}, err: nil},
{
about: "example with teamId set in input",
in: []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1","metadata": {"name": "teapot-testcluster1"}, "spec": {"teamId": "acid"}}`),
out: Postgresql{
TypeMeta: metav1.TypeMeta{
Kind: "Postgresql",
APIVersion: "acid.zalan.do/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "teapot-testcluster1",
},
Spec: PostgresSpec{TeamID: "acid"},
Status: PostgresStatus{PostgresClusterStatus: ClusterStatusInvalid},
Error: errors.New("name must match {TEAM}-{NAME} format").Error(),
},
marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"teapot-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"","parameters":null},"volume":{"size":"","storageClass":""},"patroni":{"initdb":null,"pg_hba":null,"ttl":0,"loop_wait":0,"retry_timeout":0,"maximum_lag_on_failover":0,"slots":null},"teamId":"acid","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":null},"status":{"PostgresClusterStatus":"Invalid"}}`),
err: nil},
{ {
about: "example with clone", about: "example with clone",
in: []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1","metadata": {"name": "acid-testcluster1"}, "spec": {"teamId": "acid", "clone": {"cluster": "team-batman"}}}`), in: []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1","metadata": {"name": "acid-testcluster1"}, "spec": {"teamId": "acid", "clone": {"cluster": "team-batman"}}}`),
@ -369,7 +351,6 @@ var unmarshalCluster = []struct {
Clone: &CloneDescription{ Clone: &CloneDescription{
ClusterName: "team-batman", ClusterName: "team-batman",
}, },
ClusterName: "testcluster1",
}, },
Error: "", Error: "",
}, },
@ -391,7 +372,6 @@ var unmarshalCluster = []struct {
StandbyCluster: &StandbyDescription{ StandbyCluster: &StandbyDescription{
S3WalPath: "s3://custom/path/to/bucket/", S3WalPath: "s3://custom/path/to/bucket/",
}, },
ClusterName: "testcluster1",
}, },
Error: "", Error: "",
}, },
@ -628,10 +608,10 @@ func TestServiceAnnotations(t *testing.T) {
func TestClusterName(t *testing.T) { func TestClusterName(t *testing.T) {
for _, tt := range clusterNames { for _, tt := range clusterNames {
t.Run(tt.about, func(t *testing.T) { t.Run(tt.about, func(t *testing.T) {
name, err := extractClusterName(tt.in, tt.inTeam) name, err := ExtractClusterName(tt.in, tt.inTeam)
if err != nil { if err != nil {
if tt.err == nil || err.Error() != tt.err.Error() { if tt.err == nil || err.Error() != tt.err.Error() {
t.Errorf("extractClusterName expected error: %v, got: %v", tt.err, err) t.Errorf("ExtractClusterName expected error: %v, got: %v", tt.err, err)
} }
return return
} else if tt.err != nil { } else if tt.err != nil {

View File

@ -244,7 +244,12 @@ func getPostgresContainer(podSpec *v1.PodSpec) (pgContainer v1.Container) {
func (c *Cluster) getTeamMembers(teamID string) ([]string, error) { func (c *Cluster) getTeamMembers(teamID string) ([]string, error) {
if teamID == "" { if teamID == "" {
return nil, fmt.Errorf("no teamId specified") msg := "no teamId specified"
if c.OpConfig.EnableTeamIdClusternamePrefix {
return nil, fmt.Errorf(msg)
}
c.logger.Warnf(msg)
return nil, nil
} }
members := []string{} members := []string{}

View File

@ -36,6 +36,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.EnableLazySpiloUpgrade = fromCRD.EnableLazySpiloUpgrade result.EnableLazySpiloUpgrade = fromCRD.EnableLazySpiloUpgrade
result.EnablePgVersionEnvVar = fromCRD.EnablePgVersionEnvVar result.EnablePgVersionEnvVar = fromCRD.EnablePgVersionEnvVar
result.EnableSpiloWalPathCompat = fromCRD.EnableSpiloWalPathCompat result.EnableSpiloWalPathCompat = fromCRD.EnableSpiloWalPathCompat
result.EnableTeamIdClusternamePrefix = fromCRD.EnableTeamIdClusternamePrefix
result.EtcdHost = fromCRD.EtcdHost result.EtcdHost = fromCRD.EtcdHost
result.KubernetesUseConfigMaps = fromCRD.KubernetesUseConfigMaps result.KubernetesUseConfigMaps = fromCRD.KubernetesUseConfigMaps
result.DockerImage = util.Coalesce(fromCRD.DockerImage, "registry.opensource.zalan.do/acid/spilo-14:2.1-p6") result.DockerImage = util.Coalesce(fromCRD.DockerImage, "registry.opensource.zalan.do/acid/spilo-14:2.1-p6")

View File

@ -158,7 +158,15 @@ func (c *Controller) acquireInitialListOfClusters() error {
return nil return nil
} }
func (c *Controller) addCluster(lg *logrus.Entry, clusterName spec.NamespacedName, pgSpec *acidv1.Postgresql) *cluster.Cluster { func (c *Controller) addCluster(lg *logrus.Entry, clusterName spec.NamespacedName, pgSpec *acidv1.Postgresql) (*cluster.Cluster, error) {
if c.opConfig.EnableTeamIdClusternamePrefix {
if _, err := acidv1.ExtractClusterName(clusterName.Name, pgSpec.Spec.TeamID); err != nil {
c.KubeClient.SetPostgresCRDStatus(clusterName, acidv1.ClusterStatusInvalid)
return nil, err
}
}
cl := cluster.New(c.makeClusterConfig(), c.KubeClient, *pgSpec, lg, c.eventRecorder) cl := cluster.New(c.makeClusterConfig(), c.KubeClient, *pgSpec, lg, c.eventRecorder)
cl.Run(c.stopCh) cl.Run(c.stopCh)
teamName := strings.ToLower(cl.Spec.TeamID) teamName := strings.ToLower(cl.Spec.TeamID)
@ -171,12 +179,13 @@ func (c *Controller) addCluster(lg *logrus.Entry, clusterName spec.NamespacedNam
c.clusterLogs[clusterName] = ringlog.New(c.opConfig.RingLogLines) c.clusterLogs[clusterName] = ringlog.New(c.opConfig.RingLogLines)
c.clusterHistory[clusterName] = ringlog.New(c.opConfig.ClusterHistoryEntries) c.clusterHistory[clusterName] = ringlog.New(c.opConfig.ClusterHistoryEntries)
return cl return cl, nil
} }
func (c *Controller) processEvent(event ClusterEvent) { func (c *Controller) processEvent(event ClusterEvent) {
var clusterName spec.NamespacedName var clusterName spec.NamespacedName
var clHistory ringlog.RingLogger var clHistory ringlog.RingLogger
var err error
lg := c.logger.WithField("worker", event.WorkerID) lg := c.logger.WithField("worker", event.WorkerID)
@ -216,7 +225,7 @@ func (c *Controller) processEvent(event ClusterEvent) {
c.mergeDeprecatedPostgreSQLSpecParameters(&event.NewSpec.Spec) c.mergeDeprecatedPostgreSQLSpecParameters(&event.NewSpec.Spec)
} }
if err := c.submitRBACCredentials(event); err != nil { if err = c.submitRBACCredentials(event); err != nil {
c.logger.Warnf("pods and/or Patroni may misfunction due to the lack of permissions: %v", err) c.logger.Warnf("pods and/or Patroni may misfunction due to the lack of permissions: %v", err)
} }
@ -231,15 +240,20 @@ func (c *Controller) processEvent(event ClusterEvent) {
lg.Infof("creating a new Postgres cluster") lg.Infof("creating a new Postgres cluster")
cl = c.addCluster(lg, clusterName, event.NewSpec) cl, err = c.addCluster(lg, clusterName, event.NewSpec)
if err != nil {
lg.Errorf("creation of cluster is blocked: %v", err)
return
}
c.curWorkerCluster.Store(event.WorkerID, cl) c.curWorkerCluster.Store(event.WorkerID, cl)
if err := cl.Create(); err != nil { err = cl.Create()
if err != nil {
cl.Status = acidv1.PostgresStatus{PostgresClusterStatus: acidv1.ClusterStatusInvalid}
cl.Error = fmt.Sprintf("could not create cluster: %v", err) cl.Error = fmt.Sprintf("could not create cluster: %v", err)
lg.Error(cl.Error) lg.Error(cl.Error)
c.eventRecorder.Eventf(cl.GetReference(), v1.EventTypeWarning, "Create", "%v", cl.Error) c.eventRecorder.Eventf(cl.GetReference(), v1.EventTypeWarning, "Create", "%v", cl.Error)
return return
} }
@ -252,7 +266,8 @@ func (c *Controller) processEvent(event ClusterEvent) {
return return
} }
c.curWorkerCluster.Store(event.WorkerID, cl) c.curWorkerCluster.Store(event.WorkerID, cl)
if err := cl.Update(event.OldSpec, event.NewSpec); err != nil { err = cl.Update(event.OldSpec, event.NewSpec)
if err != nil {
cl.Error = fmt.Sprintf("could not update cluster: %v", err) cl.Error = fmt.Sprintf("could not update cluster: %v", err)
lg.Error(cl.Error) lg.Error(cl.Error)
@ -303,11 +318,16 @@ func (c *Controller) processEvent(event ClusterEvent) {
// no race condition because a cluster is always processed by single worker // no race condition because a cluster is always processed by single worker
if !clusterFound { if !clusterFound {
cl = c.addCluster(lg, clusterName, event.NewSpec) cl, err = c.addCluster(lg, clusterName, event.NewSpec)
if err != nil {
lg.Errorf("syncing of cluster is blocked: %v", err)
return
}
} }
c.curWorkerCluster.Store(event.WorkerID, cl) c.curWorkerCluster.Store(event.WorkerID, cl)
if err := cl.Sync(event.NewSpec); err != nil { err = cl.Sync(event.NewSpec)
if err != nil {
cl.Error = fmt.Sprintf("could not sync cluster: %v", err) cl.Error = fmt.Sprintf("could not sync cluster: %v", err)
c.eventRecorder.Eventf(cl.GetReference(), v1.EventTypeWarning, "Sync", "%v", cl.Error) c.eventRecorder.Eventf(cl.GetReference(), v1.EventTypeWarning, "Sync", "%v", cl.Error)
lg.Error(cl.Error) lg.Error(cl.Error)

View File

@ -226,6 +226,7 @@ type Config struct {
EnableCrossNamespaceSecret bool `name:"enable_cross_namespace_secret" default:"false"` EnableCrossNamespaceSecret bool `name:"enable_cross_namespace_secret" default:"false"`
EnablePgVersionEnvVar bool `name:"enable_pgversion_env_var" default:"true"` EnablePgVersionEnvVar bool `name:"enable_pgversion_env_var" default:"true"`
EnableSpiloWalPathCompat bool `name:"enable_spilo_wal_path_compat" default:"false"` EnableSpiloWalPathCompat bool `name:"enable_spilo_wal_path_compat" default:"false"`
EnableTeamIdClusternamePrefix bool `name:"enable_team_id_clustername_prefix" default:"false"`
MajorVersionUpgradeMode string `name:"major_version_upgrade_mode" default:"off"` MajorVersionUpgradeMode string `name:"major_version_upgrade_mode" default:"off"`
MajorVersionUpgradeTeamAllowList []string `name:"major_version_upgrade_team_allow_list" default:""` MajorVersionUpgradeTeamAllowList []string `name:"major_version_upgrade_team_allow_list" default:""`
MinimalMajorVersion string `name:"minimal_major_version" default:"9.6"` MinimalMajorVersion string `name:"minimal_major_version" default:"9.6"`