resolve conflicts with master
This commit is contained in:
		
						commit
						dde4ba8aca
					
				|  | @ -13,6 +13,7 @@ rules: | |||
|   - acid.zalan.do | ||||
|   resources: | ||||
|   - postgresqls | ||||
|   - postgresqls/status | ||||
|   - operatorconfigurations | ||||
|   verbs: | ||||
|   - "*" | ||||
|  |  | |||
|  | @ -32,16 +32,19 @@ kubectl create -f manifests/postgres-operator.yaml  # deployment | |||
| 
 | ||||
| ## Helm chart | ||||
| 
 | ||||
| Another possibility is using a provided [Helm](https://helm.sh/) chart which | ||||
| saves you these steps. Therefore, you would need to install the helm CLI on your | ||||
| machine. After initializing helm (and its server component Tiller) in your local | ||||
| cluster you can install the operator chart. | ||||
| Alternatively, the operator can be installed by using the provided [Helm](https://helm.sh/) | ||||
| chart which saves you the manual steps. Therefore, you would need to install | ||||
| the helm CLI on your machine. After initializing helm (and its server | ||||
| component Tiller) in your local cluster you can install the operator chart. | ||||
| You can define a release name that is prepended to the operator resource's | ||||
| names. Use `--name zalando` to match with the default service account name | ||||
| as older operator versions do not support custom names for service accounts. | ||||
| 
 | ||||
| ```bash | ||||
| # 1) initialize helm | ||||
| helm init | ||||
| # 2) install postgres-operator chart | ||||
| helm install --name postgres-operator ./charts/postgres-operator | ||||
| helm install --name zalando ./charts/postgres-operator | ||||
| ``` | ||||
| 
 | ||||
| ## Create a Postgres cluster | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ data: | |||
| 
 | ||||
|   debug_logging: "true" | ||||
|   workers: "4" | ||||
|   docker_image: registry.opensource.zalan.do/acid/spilo-11:1.5-p4 | ||||
|   docker_image: registry.opensource.zalan.do/acid/spilo-cdp-11:1.5-p70 | ||||
|   pod_service_account_name: "zalando-postgres-operator" | ||||
|   secret_name_template: '{username}.{cluster}.credentials' | ||||
|   super_username: postgres | ||||
|  | @ -30,6 +30,7 @@ data: | |||
|   # pam_role_name: zalandos | ||||
|   # pam_configuration: | | ||||
|   #  https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees | ||||
|   # inherited_labels: "" | ||||
|   aws_region: eu-central-1 | ||||
|   db_hosted_zone: db.example.com | ||||
|   master_dns_name_format: '{cluster}.{team}.staging.{hostedzone}' | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ rules: | |||
|   - acid.zalan.do | ||||
|   resources: | ||||
|   - postgresqls | ||||
|   - postgresqls/status | ||||
|   - operatorconfigurations | ||||
|   verbs: | ||||
|   - "*" | ||||
|  | @ -137,7 +138,7 @@ rules: | |||
|   - clusterroles | ||||
|   verbs: | ||||
|   - bind | ||||
|   resourceNames:  | ||||
|   resourceNames: | ||||
|   - zalando-postgres-operator | ||||
| 
 | ||||
| --- | ||||
|  |  | |||
|  | @ -35,6 +35,9 @@ configuration: | |||
|     # toleration: {} | ||||
|     # infrastructure_roles_secret_name: "" | ||||
|     # pod_environment_configmap: "" | ||||
|     pod_management_policy: "ordered_ready" | ||||
|     enable_pod_antiaffinity: "false" | ||||
|     pod_antiaffinity_topology_key: "kubernetes.io/hostname" | ||||
|   postgres_pod_resources: | ||||
|     default_cpu_request: 100m | ||||
|     default_memory_request: 100Mi | ||||
|  |  | |||
|  | @ -2,14 +2,14 @@ package v1 | |||
| 
 | ||||
| // 	ClusterStatusUnknown etc : status of a Postgres cluster known to the operator
 | ||||
| const ( | ||||
| 	ClusterStatusUnknown      PostgresStatus = "" | ||||
| 	ClusterStatusCreating     PostgresStatus = "Creating" | ||||
| 	ClusterStatusUpdating     PostgresStatus = "Updating" | ||||
| 	ClusterStatusUpdateFailed PostgresStatus = "UpdateFailed" | ||||
| 	ClusterStatusSyncFailed   PostgresStatus = "SyncFailed" | ||||
| 	ClusterStatusAddFailed    PostgresStatus = "CreateFailed" | ||||
| 	ClusterStatusRunning      PostgresStatus = "Running" | ||||
| 	ClusterStatusInvalid      PostgresStatus = "Invalid" | ||||
| 	ClusterStatusUnknown      = "" | ||||
| 	ClusterStatusCreating     = "Creating" | ||||
| 	ClusterStatusUpdating     = "Updating" | ||||
| 	ClusterStatusUpdateFailed = "UpdateFailed" | ||||
| 	ClusterStatusSyncFailed   = "SyncFailed" | ||||
| 	ClusterStatusAddFailed    = "CreateFailed" | ||||
| 	ClusterStatusRunning      = "Running" | ||||
| 	ClusterStatusInvalid      = "Invalid" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ import ( | |||
| ) | ||||
| 
 | ||||
| type postgresqlCopy Postgresql | ||||
| type postgresStatusCopy PostgresStatus | ||||
| 
 | ||||
| // MarshalJSON converts a maintenance window definition to JSON.
 | ||||
| func (m *MaintenanceWindow) MarshalJSON() ([]byte, error) { | ||||
|  | @ -69,6 +70,26 @@ func (m *MaintenanceWindow) UnmarshalJSON(data []byte) error { | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // UnmarshalJSON converts a JSON to the status subresource definition.
 | ||||
| func (ps *PostgresStatus) UnmarshalJSON(data []byte) error { | ||||
| 	var ( | ||||
| 		tmp    postgresStatusCopy | ||||
| 		status string | ||||
| 	) | ||||
| 
 | ||||
| 	err := json.Unmarshal(data, &tmp) | ||||
| 	if err != nil { | ||||
| 		metaErr := json.Unmarshal(data, &status) | ||||
| 		if metaErr != nil { | ||||
| 			return fmt.Errorf("Could not parse status: %v; err %v", string(data), metaErr) | ||||
| 		} | ||||
| 		tmp.PostgresClusterStatus = status | ||||
| 	} | ||||
| 	*ps = PostgresStatus(tmp) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // UnmarshalJSON converts a JSON into the PostgreSQL object.
 | ||||
| func (p *Postgresql) UnmarshalJSON(data []byte) error { | ||||
| 	var tmp postgresqlCopy | ||||
|  | @ -81,7 +102,7 @@ func (p *Postgresql) UnmarshalJSON(data []byte) error { | |||
| 		} | ||||
| 
 | ||||
| 		tmp.Error = err.Error() | ||||
| 		tmp.Status = ClusterStatusInvalid | ||||
| 		tmp.Status = PostgresStatus{PostgresClusterStatus: ClusterStatusInvalid} | ||||
| 
 | ||||
| 		*p = Postgresql(tmp) | ||||
| 
 | ||||
|  | @ -91,10 +112,10 @@ func (p *Postgresql) UnmarshalJSON(data []byte) error { | |||
| 
 | ||||
| 	if clusterName, err := extractClusterName(tmp2.ObjectMeta.Name, tmp2.Spec.TeamID); err != nil { | ||||
| 		tmp2.Error = err.Error() | ||||
| 		tmp2.Status = ClusterStatusInvalid | ||||
| 		tmp2.Status = PostgresStatus{PostgresClusterStatus: ClusterStatusInvalid} | ||||
| 	} else if err := validateCloneClusterDescription(&tmp2.Spec.Clone); err != nil { | ||||
| 		tmp2.Error = err.Error() | ||||
| 		tmp2.Status = ClusterStatusInvalid | ||||
| 		tmp2.Status = PostgresStatus{PostgresClusterStatus: ClusterStatusInvalid} | ||||
| 	} else { | ||||
| 		tmp2.Spec.ClusterName = clusterName | ||||
| 	} | ||||
|  |  | |||
|  | @ -62,8 +62,9 @@ type KubernetesMetaConfiguration struct { | |||
| 	PodEnvironmentConfigMap    string        `json:"pod_environment_configmap,omitempty"` | ||||
| 	PodPriorityClassName       string        `json:"pod_priority_class_name,omitempty"` | ||||
| 	MasterPodMoveTimeout       time.Duration `json:"master_pod_move_timeout,omitempty"` | ||||
| 	EnablePodAntiAffinity      bool          `json:"enable_pod_antiaffinity" default:"false"` | ||||
| 	PodAntiAffinityTopologyKey string        `name:"pod_antiaffinity_topology_key" default:"kubernetes.io/hostname"` | ||||
| 	EnablePodAntiAffinity      bool          `json:"enable_pod_antiaffinity,omitempty"` | ||||
| 	PodAntiAffinityTopologyKey string        `json:"pod_antiaffinity_topology_key,omitempty"` | ||||
| 	PodManagementPolicy        string        `json:"pod_management_policy,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // PostgresPodResourcesDefaults defines the spec of default resources
 | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ type Postgresql struct { | |||
| 	metav1.ObjectMeta `json:"metadata,omitempty"` | ||||
| 
 | ||||
| 	Spec   PostgresSpec   `json:"spec"` | ||||
| 	Status PostgresStatus `json:"status,omitempty"` | ||||
| 	Status PostgresStatus `json:"status"` | ||||
| 	Error  string         `json:"-"` | ||||
| } | ||||
| 
 | ||||
|  | @ -129,4 +129,6 @@ type Sidecar struct { | |||
| type UserFlags []string | ||||
| 
 | ||||
| // PostgresStatus contains status of the PostgreSQL cluster (running, creation failed etc.)
 | ||||
| type PostgresStatus string | ||||
| type PostgresStatus struct { | ||||
| 	PostgresClusterStatus string `json:"PostgresClusterStatus"` | ||||
| } | ||||
|  |  | |||
|  | @ -85,12 +85,22 @@ func validateCloneClusterDescription(clone *CloneDescription) error { | |||
| } | ||||
| 
 | ||||
| // Success of the current Status
 | ||||
| func (status PostgresStatus) Success() bool { | ||||
| 	return status != ClusterStatusAddFailed && | ||||
| 		status != ClusterStatusUpdateFailed && | ||||
| 		status != ClusterStatusSyncFailed | ||||
| func (postgresStatus PostgresStatus) Success() bool { | ||||
| 	return postgresStatus.PostgresClusterStatus != ClusterStatusAddFailed && | ||||
| 		postgresStatus.PostgresClusterStatus != ClusterStatusUpdateFailed && | ||||
| 		postgresStatus.PostgresClusterStatus != ClusterStatusSyncFailed | ||||
| } | ||||
| 
 | ||||
| func (status PostgresStatus) String() string { | ||||
| 	return string(status) | ||||
| // Running status of cluster
 | ||||
| func (postgresStatus PostgresStatus) Running() bool { | ||||
| 	return postgresStatus.PostgresClusterStatus == ClusterStatusRunning | ||||
| } | ||||
| 
 | ||||
| // Creating status of cluster
 | ||||
| func (postgresStatus PostgresStatus) Creating() bool { | ||||
| 	return postgresStatus.PostgresClusterStatus == ClusterStatusCreating | ||||
| } | ||||
| 
 | ||||
| func (postgresStatus PostgresStatus) String() string { | ||||
| 	return postgresStatus.PostgresClusterStatus | ||||
| } | ||||
|  |  | |||
|  | @ -111,101 +111,139 @@ var maintenanceWindows = []struct { | |||
| 	{[]byte(`"Mon:00:00"`), MaintenanceWindow{}, errors.New("incorrect maintenance window format")}, | ||||
| 	{[]byte(`"Mon:00:00-00:00:00"`), MaintenanceWindow{}, errors.New("could not parse end time: incorrect time format")}} | ||||
| 
 | ||||
| var postgresStatus = []struct { | ||||
| 	in  []byte | ||||
| 	out PostgresStatus | ||||
| 	err error | ||||
| }{ | ||||
| 	{[]byte(`{"PostgresClusterStatus":"Running"}`), | ||||
| 		PostgresStatus{PostgresClusterStatus: ClusterStatusRunning}, nil}, | ||||
| 	{[]byte(`{"PostgresClusterStatus":""}`), | ||||
| 		PostgresStatus{PostgresClusterStatus: ClusterStatusUnknown}, nil}, | ||||
| 	{[]byte(`"Running"`), | ||||
| 		PostgresStatus{PostgresClusterStatus: ClusterStatusRunning}, nil}, | ||||
| 	{[]byte(`""`), | ||||
| 		PostgresStatus{PostgresClusterStatus: ClusterStatusUnknown}, nil}} | ||||
| 
 | ||||
| var unmarshalCluster = []struct { | ||||
| 	in      []byte | ||||
| 	out     Postgresql | ||||
| 	marshal []byte | ||||
| 	err     error | ||||
| }{{ | ||||
| 	[]byte(`{ | ||||
|   "kind": "Postgresql","apiVersion": "acid.zalan.do/v1", | ||||
|   "metadata": {"name": "acid-testcluster1"}, "spec": {"teamId": 100}}`), | ||||
| 	Postgresql{ | ||||
| 		TypeMeta: metav1.TypeMeta{ | ||||
| 			Kind:       "Postgresql", | ||||
| 			APIVersion: "acid.zalan.do/v1", | ||||
| }{ | ||||
| 	// example with simple status field
 | ||||
| 	{ | ||||
| 		in: []byte(`{ | ||||
| 	  "kind": "Postgresql","apiVersion": "acid.zalan.do/v1", | ||||
| 	  "metadata": {"name": "acid-testcluster1"}, "spec": {"teamId": 100}}`), | ||||
| 		out: Postgresql{ | ||||
| 			TypeMeta: metav1.TypeMeta{ | ||||
| 				Kind:       "Postgresql", | ||||
| 				APIVersion: "acid.zalan.do/v1", | ||||
| 			}, | ||||
| 			ObjectMeta: metav1.ObjectMeta{ | ||||
| 				Name: "acid-testcluster1", | ||||
| 			}, | ||||
| 			Status: PostgresStatus{PostgresClusterStatus: ClusterStatusInvalid}, | ||||
| 			// This error message can vary between Go versions, so compute it for the current version.
 | ||||
| 			Error: json.Unmarshal([]byte(`{"teamId": 0}`), &PostgresSpec{}).Error(), | ||||
| 		}, | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name: "acid-testcluster1", | ||||
| 		marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-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},"resources":{"requests":{"cpu":"","memory":""},"limits":{"cpu":"","memory":""}},"teamId":"","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":{}},"status":"Invalid"}`), | ||||
| 		err:     nil}, | ||||
| 	// example with /status subresource
 | ||||
| 	{ | ||||
| 		in: []byte(`{ | ||||
| 	  "kind": "Postgresql","apiVersion": "acid.zalan.do/v1", | ||||
| 	  "metadata": {"name": "acid-testcluster1"}, "spec": {"teamId": 100}}`), | ||||
| 		out: Postgresql{ | ||||
| 			TypeMeta: metav1.TypeMeta{ | ||||
| 				Kind:       "Postgresql", | ||||
| 				APIVersion: "acid.zalan.do/v1", | ||||
| 			}, | ||||
| 			ObjectMeta: metav1.ObjectMeta{ | ||||
| 				Name: "acid-testcluster1", | ||||
| 			}, | ||||
| 			Status: PostgresStatus{PostgresClusterStatus: ClusterStatusInvalid}, | ||||
| 			// This error message can vary between Go versions, so compute it for the current version.
 | ||||
| 			Error: json.Unmarshal([]byte(`{"teamId": 0}`), &PostgresSpec{}).Error(), | ||||
| 		}, | ||||
| 		Status: ClusterStatusInvalid, | ||||
| 		// This error message can vary between Go versions, so compute it for the current version.
 | ||||
| 		Error: json.Unmarshal([]byte(`{"teamId": 0}`), &PostgresSpec{}).Error(), | ||||
| 	}, | ||||
| 	[]byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-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},"resources":{"requests":{"cpu":"","memory":""},"limits":{"cpu":"","memory":""}},"teamId":"","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":{}},"status":"Invalid"}`), nil}, | ||||
| 	{[]byte(`{ | ||||
|   "kind": "Postgresql", | ||||
|   "apiVersion": "acid.zalan.do/v1", | ||||
|   "metadata": { | ||||
|     "name": "acid-testcluster1" | ||||
|   }, | ||||
|   "spec": { | ||||
|     "teamId": "ACID", | ||||
|     "volume": { | ||||
|       "size": "5Gi", | ||||
|       "storageClass": "SSD" | ||||
|     }, | ||||
|     "numberOfInstances": 2, | ||||
|     "users": { | ||||
|       "zalando": [ | ||||
|         "superuser", | ||||
|         "createdb" | ||||
|       ] | ||||
|     }, | ||||
|     "allowedSourceRanges": [ | ||||
|       "127.0.0.1/32" | ||||
|     ], | ||||
|     "postgresql": { | ||||
|       "version": "9.6", | ||||
|       "parameters": { | ||||
|         "shared_buffers": "32MB", | ||||
|         "max_connections": "10", | ||||
|         "log_statement": "all" | ||||
|       } | ||||
|     }, | ||||
|     "resources": { | ||||
|       "requests": { | ||||
|         "cpu": "10m", | ||||
|         "memory": "50Mi" | ||||
|       }, | ||||
|       "limits": { | ||||
|         "cpu": "300m", | ||||
|         "memory": "3000Mi" | ||||
|       } | ||||
|     }, | ||||
|     "clone" : { | ||||
|      "cluster": "acid-batman" | ||||
|      }, | ||||
|     "patroni": { | ||||
|       "initdb": { | ||||
|         "encoding": "UTF8", | ||||
|         "locale": "en_US.UTF-8", | ||||
|         "data-checksums": "true" | ||||
|       }, | ||||
|       "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" : { | ||||
| 			  "type"     : "logical", | ||||
| 			  "database" : "foo", | ||||
| 			  "plugin"   : "pgoutput" | ||||
| 	       } | ||||
| 		marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-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},"resources":{"requests":{"cpu":"","memory":""},"limits":{"cpu":"","memory":""}},"teamId":"","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":{}},"status":{"PostgresClusterStatus":"Invalid"}}`), | ||||
| 		err:     nil}, | ||||
| 	// example with detailed input manifest
 | ||||
| 	{ | ||||
| 		in: []byte(`{ | ||||
| 	  "kind": "Postgresql", | ||||
| 	  "apiVersion": "acid.zalan.do/v1", | ||||
| 	  "metadata": { | ||||
| 	    "name": "acid-testcluster1" | ||||
| 	  }, | ||||
| 	  "spec": { | ||||
| 	    "teamId": "ACID", | ||||
| 	    "volume": { | ||||
| 	      "size": "5Gi", | ||||
| 	      "storageClass": "SSD" | ||||
| 	    }, | ||||
| 	    "numberOfInstances": 2, | ||||
| 	    "users": { | ||||
| 	      "zalando": [ | ||||
| 	        "superuser", | ||||
| 	        "createdb" | ||||
| 	      ] | ||||
| 	    }, | ||||
| 	    "allowedSourceRanges": [ | ||||
| 	      "127.0.0.1/32" | ||||
| 	    ], | ||||
| 	    "postgresql": { | ||||
| 	      "version": "9.6", | ||||
| 	      "parameters": { | ||||
| 	        "shared_buffers": "32MB", | ||||
| 	        "max_connections": "10", | ||||
| 	        "log_statement": "all" | ||||
| 	      } | ||||
| 	    }, | ||||
| 	    "resources": { | ||||
| 	      "requests": { | ||||
| 	        "cpu": "10m", | ||||
| 	        "memory": "50Mi" | ||||
| 	      }, | ||||
| 	      "limits": { | ||||
| 	        "cpu": "300m", | ||||
| 	        "memory": "3000Mi" | ||||
| 	      } | ||||
| 	    }, | ||||
| 	    "clone" : { | ||||
| 	     "cluster": "acid-batman" | ||||
| 	     }, | ||||
| 	    "patroni": { | ||||
| 	      "initdb": { | ||||
| 	        "encoding": "UTF8", | ||||
| 	        "locale": "en_US.UTF-8", | ||||
| 	        "data-checksums": "true" | ||||
| 	      }, | ||||
| 	      "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" : { | ||||
| 					  "type"     : "logical", | ||||
| 					  "database" : "foo", | ||||
| 					  "plugin"   : "pgoutput" | ||||
| 			       } | ||||
| 			  } | ||||
| 	  	}, | ||||
| 	  	"maintenanceWindows": [ | ||||
| 	    	"Mon:01:00-06:00", | ||||
| 	    	"Sat:00:00-04:00", | ||||
| 	    	"05:00-05:15" | ||||
| 	  	] | ||||
| 	  } | ||||
|     }, | ||||
|     "maintenanceWindows": [ | ||||
|       "Mon:01:00-06:00", | ||||
|       "Sat:00:00-04:00", | ||||
|       "05:00-05:15" | ||||
|     ] | ||||
|   } | ||||
| }`), | ||||
| 		Postgresql{ | ||||
| 		}`), | ||||
| 		out: Postgresql{ | ||||
| 			TypeMeta: metav1.TypeMeta{ | ||||
| 				Kind:       "Postgresql", | ||||
| 				APIVersion: "acid.zalan.do/v1", | ||||
|  | @ -273,10 +311,12 @@ var unmarshalCluster = []struct { | |||
| 			}, | ||||
| 			Error: "", | ||||
| 		}, | ||||
| 		[]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"}},"volume":{"size":"5Gi","storageClass":"SSD"},"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"}}}`), nil}, | ||||
| 		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"}},"volume":{"size":"5Gi","storageClass":"SSD"},"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}, | ||||
| 	// example with teamId set in input
 | ||||
| 	{ | ||||
| 		[]byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1","metadata": {"name": "teapot-testcluster1"}, "spec": {"teamId": "acid"}}`), | ||||
| 		Postgresql{ | ||||
| 		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", | ||||
|  | @ -285,10 +325,12 @@ var unmarshalCluster = []struct { | |||
| 				Name: "teapot-testcluster1", | ||||
| 			}, | ||||
| 			Spec:   PostgresSpec{TeamID: "acid"}, | ||||
| 			Status: ClusterStatusInvalid, | ||||
| 			Status: PostgresStatus{PostgresClusterStatus: ClusterStatusInvalid}, | ||||
| 			Error:  errors.New("name must match {TEAM}-{NAME} format").Error(), | ||||
| 		}, | ||||
| 		[]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},"resources":{"requests":{"cpu":"","memory":""},"limits":{"cpu":"","memory":""}},"teamId":"acid","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":{}},"status":"Invalid"}`), nil}, | ||||
| 		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},"resources":{"requests":{"cpu":"","memory":""},"limits":{"cpu":"","memory":""}},"teamId":"acid","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":{}},"status":{"PostgresClusterStatus":"Invalid"}}`), | ||||
| 		err:     nil}, | ||||
| 	// clone example
 | ||||
| 	{ | ||||
| 		in: []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1","metadata": {"name": "acid-testcluster1"}, "spec": {"teamId": "acid", "clone": {"cluster": "team-batman"}}}`), | ||||
| 		out: Postgresql{ | ||||
|  | @ -308,22 +350,26 @@ var unmarshalCluster = []struct { | |||
| 			}, | ||||
| 			Error: "", | ||||
| 		}, | ||||
| 		marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-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},"resources":{"requests":{"cpu":"","memory":""},"limits":{"cpu":"","memory":""}},"teamId":"acid","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":{"cluster":"team-batman"}}}`), err: nil}, | ||||
| 	{[]byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1"`), | ||||
| 		Postgresql{}, | ||||
| 		[]byte{}, | ||||
| 		errors.New("unexpected end of JSON input")}, | ||||
| 	{[]byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster","creationTimestamp":qaz},"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},"resources":{"requests":{"cpu":"","memory":""},"limits":{"cpu":"","memory":""}},"teamId":"acid","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":{}},"status":"Invalid"}`), | ||||
| 		Postgresql{}, | ||||
| 		[]byte{}, | ||||
| 		errors.New("invalid character 'q' looking for beginning of value")}} | ||||
| 		marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-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},"resources":{"requests":{"cpu":"","memory":""},"limits":{"cpu":"","memory":""}},"teamId":"acid","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":{"cluster":"team-batman"}},"status":{"PostgresClusterStatus":""}}`), | ||||
| 		err:     nil}, | ||||
| 	// erroneous examples
 | ||||
| 	{ | ||||
| 		in:      []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1"`), | ||||
| 		out:     Postgresql{}, | ||||
| 		marshal: []byte{}, | ||||
| 		err:     errors.New("unexpected end of JSON input")}, | ||||
| 	{ | ||||
| 		in:      []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster","creationTimestamp":qaz},"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},"resources":{"requests":{"cpu":"","memory":""},"limits":{"cpu":"","memory":""}},"teamId":"acid","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":{}},"status":{"PostgresClusterStatus":"Invalid"}}`), | ||||
| 		out:     Postgresql{}, | ||||
| 		marshal: []byte{}, | ||||
| 		err:     errors.New("invalid character 'q' looking for beginning of value")}} | ||||
| 
 | ||||
| var postgresqlList = []struct { | ||||
| 	in  []byte | ||||
| 	out PostgresqlList | ||||
| 	err error | ||||
| }{ | ||||
| 	{[]byte(`{"apiVersion":"v1","items":[{"apiVersion":"acid.zalan.do/v1","kind":"Postgresql","metadata":{"labels":{"team":"acid"},"name":"acid-testcluster42","namespace":"default","resourceVersion":"30446957","selfLink":"/apis/acid.zalan.do/v1/namespaces/default/postgresqls/acid-testcluster42","uid":"857cd208-33dc-11e7-b20a-0699041e4b03"},"spec":{"allowedSourceRanges":["185.85.220.0/22"],"numberOfInstances":1,"postgresql":{"version":"9.6"},"teamId":"acid","volume":{"size":"10Gi"}},"status":"Running"}],"kind":"List","metadata":{},"resourceVersion":"","selfLink":""}`), | ||||
| 	{[]byte(`{"apiVersion":"v1","items":[{"apiVersion":"acid.zalan.do/v1","kind":"Postgresql","metadata":{"labels":{"team":"acid"},"name":"acid-testcluster42","namespace":"default","resourceVersion":"30446957","selfLink":"/apis/acid.zalan.do/v1/namespaces/default/postgresqls/acid-testcluster42","uid":"857cd208-33dc-11e7-b20a-0699041e4b03"},"spec":{"allowedSourceRanges":["185.85.220.0/22"],"numberOfInstances":1,"postgresql":{"version":"9.6"},"teamId":"acid","volume":{"size":"10Gi"}},"status":{"PostgresClusterStatus":"Running"}}],"kind":"List","metadata":{},"resourceVersion":"","selfLink":""}`), | ||||
| 		PostgresqlList{ | ||||
| 			TypeMeta: metav1.TypeMeta{ | ||||
| 				Kind:       "List", | ||||
|  | @ -350,8 +396,10 @@ var postgresqlList = []struct { | |||
| 					AllowedSourceRanges: []string{"185.85.220.0/22"}, | ||||
| 					NumberOfInstances:   1, | ||||
| 				}, | ||||
| 				Status: ClusterStatusRunning, | ||||
| 				Error:  "", | ||||
| 				Status: PostgresStatus{ | ||||
| 					PostgresClusterStatus: ClusterStatusRunning, | ||||
| 				}, | ||||
| 				Error: "", | ||||
| 			}}, | ||||
| 		}, | ||||
| 		nil}, | ||||
|  | @ -469,6 +517,25 @@ func TestMarshalMaintenanceWindow(t *testing.T) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestUnmarshalPostgresStatus(t *testing.T) { | ||||
| 	for _, tt := range postgresStatus { | ||||
| 		var ps PostgresStatus | ||||
| 		err := ps.UnmarshalJSON(tt.in) | ||||
| 		if err != nil { | ||||
| 			if tt.err == nil || err.Error() != tt.err.Error() { | ||||
| 				t.Errorf("CR status unmarshal expected error: %v, got %v", tt.err, err) | ||||
| 			} | ||||
| 			continue | ||||
| 			//} else if tt.err != nil {
 | ||||
| 			//t.Errorf("Expected error: %v", tt.err)
 | ||||
| 		} | ||||
| 
 | ||||
| 		if !reflect.DeepEqual(ps, tt.out) { | ||||
| 			t.Errorf("Expected status: %#v, got: %#v", tt.out, ps) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestPostgresUnmarshal(t *testing.T) { | ||||
| 	for _, tt := range unmarshalCluster { | ||||
| 		var cluster Postgresql | ||||
|  | @ -494,12 +561,26 @@ func TestMarshal(t *testing.T) { | |||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// Unmarshal and marshal example to capture api changes
 | ||||
| 		var cluster Postgresql | ||||
| 		err := cluster.UnmarshalJSON(tt.marshal) | ||||
| 		if err != nil { | ||||
| 			if tt.err == nil || err.Error() != tt.err.Error() { | ||||
| 				t.Errorf("Backwards compatibility unmarshal expected error: %v, got: %v", tt.err, err) | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
| 		expected, err := json.Marshal(cluster) | ||||
| 		if err != nil { | ||||
| 			t.Errorf("Backwards compatibility marshal error: %v", err) | ||||
| 		} | ||||
| 
 | ||||
| 		m, err := json.Marshal(tt.out) | ||||
| 		if err != nil { | ||||
| 			t.Errorf("Marshal error: %v", err) | ||||
| 		} | ||||
| 		if !bytes.Equal(m, tt.marshal) { | ||||
| 			t.Errorf("Marshal Postgresql \nexpected: %q, \ngot:      %q", string(tt.marshal), string(m)) | ||||
| 		if !bytes.Equal(m, expected) { | ||||
| 			t.Errorf("Marshal Postgresql \nexpected: %q, \ngot:      %q", string(expected), string(m)) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -479,6 +479,22 @@ func (in *PostgresSpec) DeepCopy() *PostgresSpec { | |||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | ||||
| func (in *PostgresStatus) DeepCopyInto(out *PostgresStatus) { | ||||
| 	*out = *in | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresStatus.
 | ||||
| func (in *PostgresStatus) DeepCopy() *PostgresStatus { | ||||
| 	if in == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	out := new(PostgresStatus) | ||||
| 	in.DeepCopyInto(out) | ||||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | ||||
| func (in *PostgresUsersConfiguration) DeepCopyInto(out *PostgresUsersConfiguration) { | ||||
| 	*out = *in | ||||
|  | @ -501,6 +517,7 @@ func (in *Postgresql) DeepCopyInto(out *Postgresql) { | |||
| 	out.TypeMeta = in.TypeMeta | ||||
| 	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) | ||||
| 	in.Spec.DeepCopyInto(&out.Spec) | ||||
| 	out.Status = in.Status | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ package cluster | |||
| 
 | ||||
| import ( | ||||
| 	"database/sql" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"regexp" | ||||
|  | @ -19,8 +20,6 @@ import ( | |||
| 	"k8s.io/client-go/rest" | ||||
| 	"k8s.io/client-go/tools/cache" | ||||
| 
 | ||||
| 	"encoding/json" | ||||
| 
 | ||||
| 	acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" | ||||
| 	"github.com/zalando/postgres-operator/pkg/spec" | ||||
| 	"github.com/zalando/postgres-operator/pkg/util" | ||||
|  | @ -149,21 +148,24 @@ func (c *Cluster) setProcessName(procName string, args ...interface{}) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *Cluster) setStatus(status acidv1.PostgresStatus) { | ||||
| 	// TODO: eventually switch to updateStatus() for kubernetes 1.11 and above
 | ||||
| 	var ( | ||||
| 		err error | ||||
| 		b   []byte | ||||
| 	) | ||||
| 	if b, err = json.Marshal(status); err != nil { | ||||
| // SetStatus of Postgres cluster
 | ||||
| // TODO: eventually switch to updateStatus() for kubernetes 1.11 and above
 | ||||
| func (c *Cluster) setStatus(status string) { | ||||
| 	var pgStatus acidv1.PostgresStatus | ||||
| 	pgStatus.PostgresClusterStatus = status | ||||
| 
 | ||||
| 	patch, err := json.Marshal(struct { | ||||
| 		PgStatus interface{} `json:"status"` | ||||
| 	}{&pgStatus}) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		c.logger.Errorf("could not marshal status: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	patch := []byte(fmt.Sprintf(`{"status": %s}`, string(b))) | ||||
| 	// we cannot do a full scale update here without fetching the previous manifest (as the resourceVersion may differ),
 | ||||
| 	// however, we could do patch without it. In the future, once /status subresource is there (starting Kubernets 1.11)
 | ||||
| 	// we should take advantage of it.
 | ||||
| 	newspec, err := c.KubeClient.AcidV1ClientSet.AcidV1().Postgresqls(c.clusterNamespace()).Patch(c.Name, types.MergePatchType, patch) | ||||
| 	newspec, err := c.KubeClient.AcidV1ClientSet.AcidV1().Postgresqls(c.clusterNamespace()).Patch(c.Name, types.MergePatchType, patch, "status") | ||||
| 	if err != nil { | ||||
| 		c.logger.Errorf("could not update status: %v", err) | ||||
| 	} | ||||
|  | @ -172,7 +174,7 @@ func (c *Cluster) setStatus(status acidv1.PostgresStatus) { | |||
| } | ||||
| 
 | ||||
| func (c *Cluster) isNewCluster() bool { | ||||
| 	return c.Status == acidv1.ClusterStatusCreating | ||||
| 	return c.Status.Creating() | ||||
| } | ||||
| 
 | ||||
| // initUsers populates c.systemUsers and c.pgUsers maps.
 | ||||
|  |  | |||
|  | @ -20,10 +20,20 @@ const ( | |||
| ) | ||||
| 
 | ||||
| var logger = logrus.New().WithField("test", "cluster") | ||||
| var cl = New(Config{OpConfig: config.Config{ProtectedRoles: []string{"admin"}, | ||||
| 	Auth: config.Auth{SuperUsername: superUserName, | ||||
| 		ReplicationUsername: replicationUserName}}}, | ||||
| 	k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger) | ||||
| var cl = New( | ||||
| 	Config{ | ||||
| 		OpConfig: config.Config{ | ||||
| 			ProtectedRoles: []string{"admin"}, | ||||
| 			Auth: config.Auth{ | ||||
| 				SuperUsername:       superUserName, | ||||
| 				ReplicationUsername: replicationUserName, | ||||
| 			}, | ||||
| 		}, | ||||
| 	}, | ||||
| 	k8sutil.NewMockKubernetesClient(), | ||||
| 	acidv1.Postgresql{}, | ||||
| 	logger, | ||||
| ) | ||||
| 
 | ||||
| func TestInitRobotUsers(t *testing.T) { | ||||
| 	testName := "TestInitRobotUsers" | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error { | |||
| 		if err != nil { | ||||
| 			c.logger.Warningf("error while syncing cluster state: %v", err) | ||||
| 			c.setStatus(acidv1.ClusterStatusSyncFailed) | ||||
| 		} else if c.Status != acidv1.ClusterStatusRunning { | ||||
| 		} else if !c.Status.Running() { | ||||
| 			c.setStatus(acidv1.ClusterStatusRunning) | ||||
| 		} | ||||
| 	}() | ||||
|  |  | |||
|  | @ -53,6 +53,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur | |||
| 	result.ClusterNameLabel = fromCRD.Kubernetes.ClusterNameLabel | ||||
| 	result.NodeReadinessLabel = fromCRD.Kubernetes.NodeReadinessLabel | ||||
| 	result.PodPriorityClassName = fromCRD.Kubernetes.PodPriorityClassName | ||||
| 	result.PodManagementPolicy = fromCRD.Kubernetes.PodManagementPolicy | ||||
| 	result.MasterPodMoveTimeout = fromCRD.Kubernetes.MasterPodMoveTimeout | ||||
| 
 | ||||
| 	result.EnablePodAntiAffinity = fromCRD.Kubernetes.EnablePodAntiAffinity | ||||
|  |  | |||
|  | @ -6,82 +6,24 @@ import ( | |||
| 	"testing" | ||||
| 
 | ||||
| 	b64 "encoding/base64" | ||||
| 	"k8s.io/api/core/v1" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| 	v1core "k8s.io/client-go/kubernetes/typed/core/v1" | ||||
| 
 | ||||
| 	"github.com/zalando/postgres-operator/pkg/spec" | ||||
| 	"github.com/zalando/postgres-operator/pkg/util/k8sutil" | ||||
| 	"k8s.io/api/core/v1" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	testInfrastructureRolesSecretName = "infrastructureroles-test" | ||||
| ) | ||||
| 
 | ||||
| type mockSecret struct { | ||||
| 	v1core.SecretInterface | ||||
| } | ||||
| 
 | ||||
| type mockConfigMap struct { | ||||
| 	v1core.ConfigMapInterface | ||||
| } | ||||
| 
 | ||||
| func (c *mockSecret) Get(name string, options metav1.GetOptions) (*v1.Secret, error) { | ||||
| 	if name != testInfrastructureRolesSecretName { | ||||
| 		return nil, fmt.Errorf("NotFound") | ||||
| 	} | ||||
| 	secret := &v1.Secret{} | ||||
| 	secret.Name = mockController.opConfig.ClusterNameLabel | ||||
| 	secret.Data = map[string][]byte{ | ||||
| 		"user1":     []byte("testrole"), | ||||
| 		"password1": []byte("testpassword"), | ||||
| 		"inrole1":   []byte("testinrole"), | ||||
| 		"foobar":    []byte(b64.StdEncoding.EncodeToString([]byte("password"))), | ||||
| 	} | ||||
| 	return secret, nil | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func (c *mockConfigMap) Get(name string, options metav1.GetOptions) (*v1.ConfigMap, error) { | ||||
| 	if name != testInfrastructureRolesSecretName { | ||||
| 		return nil, fmt.Errorf("NotFound") | ||||
| 	} | ||||
| 	configmap := &v1.ConfigMap{} | ||||
| 	configmap.Name = mockController.opConfig.ClusterNameLabel | ||||
| 	configmap.Data = map[string]string{ | ||||
| 		"foobar": "{}", | ||||
| 	} | ||||
| 	return configmap, nil | ||||
| } | ||||
| 
 | ||||
| type MockSecretGetter struct { | ||||
| } | ||||
| 
 | ||||
| type MockConfigMapsGetter struct { | ||||
| } | ||||
| 
 | ||||
| func (c *MockSecretGetter) Secrets(namespace string) v1core.SecretInterface { | ||||
| 	return &mockSecret{} | ||||
| } | ||||
| 
 | ||||
| func (c *MockConfigMapsGetter) ConfigMaps(namespace string) v1core.ConfigMapInterface { | ||||
| 	return &mockConfigMap{} | ||||
| } | ||||
| 
 | ||||
| func newMockKubernetesClient() k8sutil.KubernetesClient { | ||||
| 	return k8sutil.KubernetesClient{ | ||||
| 		SecretsGetter:    &MockSecretGetter{}, | ||||
| 		ConfigMapsGetter: &MockConfigMapsGetter{}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func newMockController() *Controller { | ||||
| 	controller := NewController(&spec.ControllerConfig{}) | ||||
| 	controller.opConfig.ClusterNameLabel = "cluster-name" | ||||
| 	controller.opConfig.InfrastructureRolesSecretName = | ||||
| 		spec.NamespacedName{Namespace: v1.NamespaceDefault, Name: testInfrastructureRolesSecretName} | ||||
| 	controller.opConfig.Workers = 4 | ||||
| 	controller.KubeClient = newMockKubernetesClient() | ||||
| 	controller.KubeClient = k8sutil.NewMockKubernetesClient() | ||||
| 	return controller | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,6 +2,10 @@ package k8sutil | |||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 
 | ||||
| 	b64 "encoding/base64" | ||||
| 
 | ||||
| 	"github.com/zalando/postgres-operator/pkg/util/constants" | ||||
| 	"k8s.io/api/core/v1" | ||||
| 	policybeta1 "k8s.io/api/policy/v1beta1" | ||||
|  | @ -15,9 +19,9 @@ import ( | |||
| 	rbacv1beta1 "k8s.io/client-go/kubernetes/typed/rbac/v1beta1" | ||||
| 	"k8s.io/client-go/rest" | ||||
| 	"k8s.io/client-go/tools/clientcmd" | ||||
| 	"reflect" | ||||
| 
 | ||||
| 	acidv1client "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned" | ||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||
| ) | ||||
| 
 | ||||
| // KubernetesClient describes getters for Kubernetes objects
 | ||||
|  | @ -41,6 +45,20 @@ type KubernetesClient struct { | |||
| 	AcidV1ClientSet *acidv1client.Clientset | ||||
| } | ||||
| 
 | ||||
| type mockSecret struct { | ||||
| 	v1core.SecretInterface | ||||
| } | ||||
| 
 | ||||
| type MockSecretGetter struct { | ||||
| } | ||||
| 
 | ||||
| type mockConfigMap struct { | ||||
| 	v1core.ConfigMapInterface | ||||
| } | ||||
| 
 | ||||
| type MockConfigMapsGetter struct { | ||||
| } | ||||
| 
 | ||||
| // RestConfig creates REST config
 | ||||
| func RestConfig(kubeConfig string, outOfCluster bool) (*rest.Config, error) { | ||||
| 	if outOfCluster { | ||||
|  | @ -140,3 +158,49 @@ func SamePDB(cur, new *policybeta1.PodDisruptionBudget) (match bool, reason stri | |||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (c *mockSecret) Get(name string, options metav1.GetOptions) (*v1.Secret, error) { | ||||
| 	if name != "infrastructureroles-test" { | ||||
| 		return nil, fmt.Errorf("NotFound") | ||||
| 	} | ||||
| 	secret := &v1.Secret{} | ||||
| 	secret.Name = "testcluster" | ||||
| 	secret.Data = map[string][]byte{ | ||||
| 		"user1":     []byte("testrole"), | ||||
| 		"password1": []byte("testpassword"), | ||||
| 		"inrole1":   []byte("testinrole"), | ||||
| 		"foobar":    []byte(b64.StdEncoding.EncodeToString([]byte("password"))), | ||||
| 	} | ||||
| 	return secret, nil | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func (c *mockConfigMap) Get(name string, options metav1.GetOptions) (*v1.ConfigMap, error) { | ||||
| 	if name != "infrastructureroles-test" { | ||||
| 		return nil, fmt.Errorf("NotFound") | ||||
| 	} | ||||
| 	configmap := &v1.ConfigMap{} | ||||
| 	configmap.Name = "testcluster" | ||||
| 	configmap.Data = map[string]string{ | ||||
| 		"foobar": "{}", | ||||
| 	} | ||||
| 	return configmap, nil | ||||
| } | ||||
| 
 | ||||
| // Secrets to be mocked
 | ||||
| func (c *MockSecretGetter) Secrets(namespace string) v1core.SecretInterface { | ||||
| 	return &mockSecret{} | ||||
| } | ||||
| 
 | ||||
| // ConfigMaps to be mocked
 | ||||
| func (c *MockConfigMapsGetter) ConfigMaps(namespace string) v1core.ConfigMapInterface { | ||||
| 	return &mockConfigMap{} | ||||
| } | ||||
| 
 | ||||
| // NewMockKubernetesClient for other tests
 | ||||
| func NewMockKubernetesClient() KubernetesClient { | ||||
| 	return KubernetesClient{ | ||||
| 		SecretsGetter:    &MockSecretGetter{}, | ||||
| 		ConfigMapsGetter: &MockConfigMapsGetter{}, | ||||
| 	} | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue