Merge branch 'master' into validate-resources

This commit is contained in:
Felix Kunde
2019-11-28 14:18:20 +01:00
30 changed files with 2499 additions and 248 deletions
+916 -6
View File
@@ -1,7 +1,7 @@
package v1
import (
"github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do"
acidzalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@@ -103,7 +103,902 @@ var OperatorConfigCRDResourceColumns = []apiextv1beta1.CustomResourceColumnDefin
},
}
func buildCRD(name, kind, plural, short string, columns []apiextv1beta1.CustomResourceColumnDefinition) *apiextv1beta1.CustomResourceDefinition {
var min0 = 0.0
var min1 = 1.0
var minDisable = -1.0
// PostgresCRDResourceValidation to check applied manifest parameters
var PostgresCRDResourceValidation = apiextv1beta1.CustomResourceValidation{
OpenAPIV3Schema: &apiextv1beta1.JSONSchemaProps{
Type: "object",
Required: []string{"kind", "apiVersion", "spec"},
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"kind": {
Type: "string",
Enum: []apiextv1beta1.JSON{
{
Raw: []byte(`"postgresql"`),
},
},
},
"apiVersion": {
Type: "string",
Enum: []apiextv1beta1.JSON{
{
Raw: []byte(`"acid.zalan.do/v1"`),
},
},
},
"spec": {
Type: "object",
Required: []string{"numberOfInstances", "teamId", "postgresql"},
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"allowedSourceRanges": {
Type: "array",
Nullable: true,
Items: &apiextv1beta1.JSONSchemaPropsOrArray{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
Pattern: "^(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\/(\\d|[1-2]\\d|3[0-2])$",
},
},
},
"clone": {
Type: "object",
Required: []string{"cluster"},
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"cluster": {
Type: "string",
},
"s3_endpoint": {
Type: "string",
},
"s3_access_key_id": {
Type: "string",
},
"s3_secret_access_key": {
Type: "string",
},
"s3_force_path_style": {
Type: "string",
},
"s3_wal_path": {
Type: "string",
},
"timestamp": {
Type: "string",
Description: "Date-time format that specifies a timezone as an offset relative to UTC e.g. 1996-12-19T16:39:57-08:00",
Pattern: "^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\\.[0-9]+)?(([Zz])|([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$",
},
"uid": {
Type: "string",
Format: "uuid",
},
},
},
"databases": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
Description: "User names specified here as database owners must be declared in the users key of the spec key",
},
},
},
"dockerImage": {
Type: "string",
},
"enableLogicalBackup": {
Type: "boolean",
},
"enableMasterLoadBalancer": {
Type: "boolean",
},
"enableReplicaLoadBalancer": {
Type: "boolean",
},
"enableShmVolume": {
Type: "boolean",
},
"init_containers": {
Type: "array",
Description: "Deprecated",
Items: &apiextv1beta1.JSONSchemaPropsOrArray{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Allows: true,
},
},
},
},
"initContainers": {
Type: "array",
Items: &apiextv1beta1.JSONSchemaPropsOrArray{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Allows: true,
},
},
},
},
"logicalBackupSchedule": {
Type: "string",
Pattern: "^(\\d+|\\*)(/\\d+)?(\\s+(\\d+|\\*)(/\\d+)?){4}$",
},
"maintenanceWindows": {
Type: "array",
Items: &apiextv1beta1.JSONSchemaPropsOrArray{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
Pattern: "^\\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))-((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))\\ *$",
},
},
},
"numberOfInstances": {
Type: "integer",
Minimum: &min0,
},
"patroni": {
Type: "object",
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"initdb": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
"pg_hba": {
Type: "array",
Items: &apiextv1beta1.JSONSchemaPropsOrArray{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
"slots": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
},
},
"ttl": {
Type: "integer",
},
"loop_wait": {
Type: "integer",
},
"retry_timeout": {
Type: "integer",
},
"maximum_lag_on_failover": {
Type: "integer",
},
},
},
"podAnnotations": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
"pod_priority_class_name": {
Type: "string",
Description: "Deprecated",
},
"podPriorityClassName": {
Type: "string",
},
"postgresql": {
Type: "object",
Required: []string{"version"},
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"version": {
Type: "string",
Enum: []apiextv1beta1.JSON{
{
Raw: []byte(`"9.3"`),
},
{
Raw: []byte(`"9.4"`),
},
{
Raw: []byte(`"9.5"`),
},
{
Raw: []byte(`"9.6"`),
},
{
Raw: []byte(`"10"`),
},
{
Raw: []byte(`"11"`),
},
{
Raw: []byte(`"12"`),
},
},
},
"parameters": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
},
},
"replicaLoadBalancer": {
Type: "boolean",
Description: "Deprecated",
},
"resources": {
Type: "object",
Required: []string{"requests", "limits"},
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"limits": {
Type: "object",
Required: []string{"cpu", "memory"},
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"cpu": {
Type: "string",
Description: "Decimal natural followed by m, or decimal natural followed by dot followed by up to three decimal digits (precision used by Kubernetes). Must be greater than 0",
Pattern: "^(\\d+m|\\d+\\.\\d{1,3})$",
},
"memory": {
Type: "string",
Description: "Plain integer or fixed-point integer using one of these suffixes: E, P, T, G, M, k (with or without a tailing i). Must be greater than 0",
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
},
},
},
"requests": {
Type: "object",
Required: []string{"cpu", "memory"},
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"cpu": {
Type: "string",
Description: "Decimal natural followed by m, or decimal natural followed by dot followed by up to three decimal digits (precision used by Kubernetes). Must be greater than 0",
Pattern: "^(\\d+m|\\d+\\.\\d{1,3})$",
},
"memory": {
Type: "string",
Description: "Plain integer or fixed-point integer using one of these suffixes: E, P, T, G, M, k (with or without a tailing i). Must be greater than 0",
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
},
},
},
},
},
"sidecars": {
Type: "array",
Items: &apiextv1beta1.JSONSchemaPropsOrArray{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Allows: true,
},
},
},
},
"spiloFSGroup": {
Type: "integer",
},
"standby": {
Type: "object",
Required: []string{"s3_wal_path"},
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"s3_wal_path": {
Type: "string",
},
},
},
"teamId": {
Type: "string",
},
"tolerations": {
Type: "array",
Items: &apiextv1beta1.JSONSchemaPropsOrArray{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "object",
Required: []string{"key", "operator", "effect"},
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"key": {
Type: "string",
},
"operator": {
Type: "string",
Enum: []apiextv1beta1.JSON{
{
Raw: []byte(`"Equal"`),
},
{
Raw: []byte(`"Exists"`),
},
},
},
"value": {
Type: "string",
},
"effect": {
Type: "string",
Enum: []apiextv1beta1.JSON{
{
Raw: []byte(`"NoExecute"`),
},
{
Raw: []byte(`"NoSchedule"`),
},
{
Raw: []byte(`"PreferNoSchedule"`),
},
},
},
"tolerationSeconds": {
Type: "integer",
},
},
},
},
},
"useLoadBalancer": {
Type: "boolean",
Description: "Deprecated",
},
"users": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "array",
Description: "Role flags specified here must not contradict each other",
Nullable: true,
Items: &apiextv1beta1.JSONSchemaPropsOrArray{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
Enum: []apiextv1beta1.JSON{
{
Raw: []byte(`"bypassrls"`),
},
{
Raw: []byte(`"BYPASSRLS"`),
},
{
Raw: []byte(`"nobypassrls"`),
},
{
Raw: []byte(`"NOBYPASSRLS"`),
},
{
Raw: []byte(`"createdb"`),
},
{
Raw: []byte(`"CREATEDB"`),
},
{
Raw: []byte(`"nocreatedb"`),
},
{
Raw: []byte(`"NOCREATEDB"`),
},
{
Raw: []byte(`"createrole"`),
},
{
Raw: []byte(`"CREATEROLE"`),
},
{
Raw: []byte(`"nocreaterole"`),
},
{
Raw: []byte(`"NOCREATEROLE"`),
},
{
Raw: []byte(`"inherit"`),
},
{
Raw: []byte(`"INHERIT"`),
},
{
Raw: []byte(`"noinherit"`),
},
{
Raw: []byte(`"NOINHERIT"`),
},
{
Raw: []byte(`"login"`),
},
{
Raw: []byte(`"LOGIN"`),
},
{
Raw: []byte(`"nologin"`),
},
{
Raw: []byte(`"NOLOGIN"`),
},
{
Raw: []byte(`"replication"`),
},
{
Raw: []byte(`"REPLICATION"`),
},
{
Raw: []byte(`"noreplication"`),
},
{
Raw: []byte(`"NOREPLICATION"`),
},
{
Raw: []byte(`"superuser"`),
},
{
Raw: []byte(`"SUPERUSER"`),
},
{
Raw: []byte(`"nosuperuser"`),
},
{
Raw: []byte(`"NOSUPERUSER"`),
},
},
},
},
},
},
},
"volume": {
Type: "object",
Required: []string{"size"},
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"size": {
Type: "string",
Description: "Value must not be zero",
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
},
"storageClass": {
Type: "string",
},
"subPath": {
Type: "string",
},
},
},
},
},
},
},
}
// OperatorConfigCRDResourceValidation to check applied manifest parameters
var OperatorConfigCRDResourceValidation = apiextv1beta1.CustomResourceValidation{
OpenAPIV3Schema: &apiextv1beta1.JSONSchemaProps{
Type: "object",
Required: []string{"kind", "apiVersion", "configuration"},
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"kind": {
Type: "string",
Enum: []apiextv1beta1.JSON{
{
Raw: []byte(`"OperatorConfiguration"`),
},
},
},
"apiVersion": {
Type: "string",
Enum: []apiextv1beta1.JSON{
{
Raw: []byte(`"acid.zalan.do/v1"`),
},
},
},
"configuration": {
Type: "object",
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"docker_image": {
Type: "string",
},
"enable_crd_validation": {
Type: "boolean",
},
"enable_shm_volume": {
Type: "boolean",
},
"etcd_host": {
Type: "string",
},
"max_instances": {
Type: "integer",
Description: "-1 = disabled",
Minimum: &minDisable,
},
"min_instances": {
Type: "integer",
Description: "-1 = disabled",
Minimum: &minDisable,
},
"resync_period": {
Type: "string",
},
"repair_period": {
Type: "string",
},
"set_memory_request_to_limit": {
Type: "boolean",
},
"sidecar_docker_images": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
"workers": {
Type: "integer",
Minimum: &min1,
},
"users": {
Type: "object",
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"replication_username": {
Type: "string",
},
"super_username": {
Type: "string",
},
},
},
"kubernetes": {
Type: "object",
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"cluster_domain": {
Type: "string",
},
"cluster_labels": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
"cluster_name_label": {
Type: "string",
},
"custom_pod_annotations": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
"enable_pod_antiaffinity": {
Type: "boolean",
},
"enable_pod_disruption_budget": {
Type: "boolean",
},
"infrastructure_roles_secret_name": {
Type: "string",
},
"inherited_labels": {
Type: "array",
Items: &apiextv1beta1.JSONSchemaPropsOrArray{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
"node_readiness_label": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
"oauth_token_secret_name": {
Type: "string",
},
"pdb_name_format": {
Type: "string",
},
"pod_antiaffinity_topology_key": {
Type: "string",
},
"pod_environment_configmap": {
Type: "string",
},
"pod_management_policy": {
Type: "string",
Enum: []apiextv1beta1.JSON{
{
Raw: []byte(`"ordered_ready"`),
},
{
Raw: []byte(`"parallel"`),
},
},
},
"pod_role_label": {
Type: "string",
},
"pod_service_account_name": {
Type: "string",
},
"pod_terminate_grace_period": {
Type: "string",
},
"secret_name_template": {
Type: "string",
},
"spilo_fsgroup": {
Type: "integer",
},
"spilo_privileged": {
Type: "boolean",
},
"toleration": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
"watched_namespace": {
Type: "string",
},
},
},
"postgres_pod_resources": {
Type: "object",
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"default_cpu_limit": {
Type: "string",
Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
},
"default_cpu_request": {
Type: "string",
Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
},
"default_memory_limit": {
Type: "string",
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
},
"default_memory_request": {
Type: "string",
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
},
},
},
"timeouts": {
Type: "object",
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"pod_label_wait_timeout": {
Type: "string",
},
"pod_deletion_wait_timeout": {
Type: "string",
},
"ready_wait_interval": {
Type: "string",
},
"ready_wait_timeout": {
Type: "string",
},
"resource_check_interval": {
Type: "string",
},
"resource_check_timeout": {
Type: "string",
},
},
},
"load_balancer": {
Type: "object",
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"db_hosted_zone": {
Type: "string",
},
"enable_master_load_balancer": {
Type: "boolean",
},
"enable_replica_load_balancer": {
Type: "boolean",
},
"custom_service_annotations": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
"master_dns_name_format": {
Type: "string",
},
"replica_dns_name_format": {
Type: "string",
},
},
},
"aws_or_gcp": {
Type: "object",
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"additional_secret_mount": {
Type: "string",
},
"additional_secret_mount_path": {
Type: "string",
},
"aws_region": {
Type: "string",
},
"kube_iam_role": {
Type: "string",
},
"log_s3_bucket": {
Type: "string",
},
"wal_s3_bucket": {
Type: "string",
},
},
},
"logical_backup": {
Type: "object",
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"logical_backup_schedule": {
Type: "string",
Pattern: "^(\\d+|\\*)(/\\d+)?(\\s+(\\d+|\\*)(/\\d+)?){4}$",
},
"logical_backup_docker_image": {
Type: "string",
},
"logical_backup_s3_bucket": {
Type: "string",
},
"logical_backup_s3_endpoint": {
Type: "string",
},
"logical_backup_s3_sse": {
Type: "string",
},
"logical_backup_s3_access_key_id": {
Type: "string",
},
"logical_backup_s3_secret_access_key": {
Type: "string",
},
},
},
"debug": {
Type: "object",
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"debug_logging": {
Type: "boolean",
},
"enable_database_access": {
Type: "boolean",
},
},
},
"teams_api": {
Type: "object",
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"enable_admin_role_for_users": {
Type: "boolean",
},
"enable_team_superuser": {
Type: "boolean",
},
"enable_teams_api": {
Type: "boolean",
},
"pam_configuration": {
Type: "string",
},
"pam_role_name": {
Type: "string",
},
"postgres_superuser_teams": {
Type: "array",
Items: &apiextv1beta1.JSONSchemaPropsOrArray{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
"protected_role_names": {
Type: "array",
Items: &apiextv1beta1.JSONSchemaPropsOrArray{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
"team_admin_role": {
Type: "string",
},
"team_api_role_configuration": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
"teams_api_url": {
Type: "string",
},
},
},
"logging_rest_api": {
Type: "object",
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"api_port": {
Type: "integer",
},
"cluster_history_entries": {
Type: "integer",
},
"ring_log_lines": {
Type: "integer",
},
},
},
"scalyr": {
Type: "object",
Properties: map[string]apiextv1beta1.JSONSchemaProps{
"scalyr_api_key": {
Type: "string",
},
"scalyr_cpu_limit": {
Type: "string",
Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
},
"scalyr_cpu_request": {
Type: "string",
Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
},
"scalyr_image": {
Type: "string",
},
"scalyr_memory_limit": {
Type: "string",
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
},
"scalyr_memory_request": {
Type: "string",
Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
},
"scalyr_server_url": {
Type: "string",
},
},
},
},
},
},
},
}
func buildCRD(name, kind, plural, short string, columns []apiextv1beta1.CustomResourceColumnDefinition, validation apiextv1beta1.CustomResourceValidation) *apiextv1beta1.CustomResourceDefinition {
return &apiextv1beta1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: name,
@@ -121,24 +1016,39 @@ func buildCRD(name, kind, plural, short string, columns []apiextv1beta1.CustomRe
Status: &apiextv1beta1.CustomResourceSubresourceStatus{},
},
AdditionalPrinterColumns: columns,
Validation: &validation,
},
}
}
// PostgresCRD returns CustomResourceDefinition built from PostgresCRDResource
func PostgresCRD() *apiextv1beta1.CustomResourceDefinition {
func PostgresCRD(enableValidation *bool) *apiextv1beta1.CustomResourceDefinition {
postgresCRDvalidation := apiextv1beta1.CustomResourceValidation{}
if enableValidation != nil && *enableValidation {
postgresCRDvalidation = PostgresCRDResourceValidation
}
return buildCRD(PostgresCRDResouceName,
PostgresCRDResourceKind,
PostgresCRDResourcePlural,
PostgresCRDResourceShort,
PostgresCRDResourceColumns)
PostgresCRDResourceColumns,
postgresCRDvalidation)
}
// ConfigurationCRD returns CustomResourceDefinition built from OperatorConfigCRDResource
func ConfigurationCRD() *apiextv1beta1.CustomResourceDefinition {
func ConfigurationCRD(enableValidation *bool) *apiextv1beta1.CustomResourceDefinition {
opconfigCRDvalidation := apiextv1beta1.CustomResourceValidation{}
if enableValidation != nil && *enableValidation {
opconfigCRDvalidation = OperatorConfigCRDResourceValidation
}
return buildCRD(OperatorConfigCRDResourceName,
OperatorConfigCRDResouceKind,
OperatorConfigCRDResourcePlural,
OperatorConfigCRDResourceShort,
OperatorConfigCRDResourceColumns)
OperatorConfigCRDResourceColumns,
opconfigCRDvalidation)
}
@@ -150,6 +150,7 @@ type ScalyrConfiguration struct {
// OperatorConfigurationData defines the operation config
type OperatorConfigurationData struct {
EnableCRDValidation *bool `json:"enable_crd_validation,omitempty"`
EtcdHost string `json:"etcd_host,omitempty"`
DockerImage string `json:"docker_image,omitempty"`
Workers uint32 `json:"workers,omitempty"`
@@ -184,8 +185,13 @@ type OperatorConfigurationUsers struct {
//Duration shortens this frequently used name
type Duration time.Duration
// OperatorLogicalBackupConfiguration defines configration for logical backup
type OperatorLogicalBackupConfiguration struct {
Schedule string `json:"logical_backup_schedule,omitempty"`
DockerImage string `json:"logical_backup_docker_image,omitempty"`
S3Bucket string `json:"logical_backup_s3_bucket,omitempty"`
Schedule string `json:"logical_backup_schedule,omitempty"`
DockerImage string `json:"logical_backup_docker_image,omitempty"`
S3Bucket string `json:"logical_backup_s3_bucket,omitempty"`
S3Endpoint string `json:"logical_backup_s3_endpoint,omitempty"`
S3AccessKeyID string `json:"logical_backup_s3_access_key_id,omitempty"`
S3SecretAccessKey string `json:"logical_backup_s3_secret_access_key,omitempty"`
S3SSE string `json:"logical_backup_s3_sse,omitempty"`
}
@@ -216,6 +216,11 @@ func (in *OperatorConfiguration) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData) {
*out = *in
if in.EnableCRDValidation != nil {
in, out := &in.EnableCRDValidation, &out.EnableCRDValidation
*out = new(bool)
**out = **in
}
if in.ShmVolume != nil {
in, out := &in.ShmVolume, &out.ShmVolume
*out = new(bool)
+16 -1
View File
@@ -1575,6 +1575,14 @@ func (c *Cluster) generateLogicalBackupPodEnvVars() []v1.EnvVar {
Name: "LOGICAL_BACKUP_S3_BUCKET",
Value: c.OpConfig.LogicalBackup.LogicalBackupS3Bucket,
},
{
Name: "LOGICAL_BACKUP_S3_ENDPOINT",
Value: c.OpConfig.LogicalBackup.LogicalBackupS3Endpoint,
},
{
Name: "LOGICAL_BACKUP_S3_SSE",
Value: c.OpConfig.LogicalBackup.LogicalBackupS3SSE,
},
{
Name: "LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX",
Value: getBucketScopeSuffix(string(c.Postgresql.GetUID())),
@@ -1613,8 +1621,15 @@ func (c *Cluster) generateLogicalBackupPodEnvVars() []v1.EnvVar {
},
}
c.logger.Debugf("Generated logical backup env vars %v", envVars)
if c.OpConfig.LogicalBackup.LogicalBackupS3AccessKeyID != "" {
envVars = append(envVars, v1.EnvVar{Name: "AWS_ACCESS_KEY_ID", Value: c.OpConfig.LogicalBackup.LogicalBackupS3AccessKeyID})
}
if c.OpConfig.LogicalBackup.LogicalBackupS3SecretAccessKey != "" {
envVars = append(envVars, v1.EnvVar{Name: "AWS_SECRET_ACCESS_KEY", Value: c.OpConfig.LogicalBackup.LogicalBackupS3SecretAccessKey})
}
c.logger.Debugf("Generated logical backup env vars %v", envVars)
return envVars
}
+36 -10
View File
@@ -13,7 +13,6 @@ import (
"k8s.io/apimachinery/pkg/types"
"github.com/zalando/postgres-operator/pkg/util"
"github.com/zalando/postgres-operator/pkg/util/constants"
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
"github.com/zalando/postgres-operator/pkg/util/retryutil"
)
@@ -278,7 +277,8 @@ func (c *Cluster) replaceStatefulSet(newStatefulSet *appsv1.StatefulSet) error {
oldStatefulset := c.Statefulset
options := metav1.DeleteOptions{PropagationPolicy: &deletePropagationPolicy}
if err := c.KubeClient.StatefulSets(oldStatefulset.Namespace).Delete(oldStatefulset.Name, &options); err != nil {
err := c.KubeClient.StatefulSets(oldStatefulset.Namespace).Delete(oldStatefulset.Name, &options)
if err != nil {
return fmt.Errorf("could not delete statefulset %q: %v", statefulSetName, err)
}
// make sure we clear the stored statefulset status if the subsequent create fails.
@@ -286,11 +286,16 @@ func (c *Cluster) replaceStatefulSet(newStatefulSet *appsv1.StatefulSet) error {
// wait until the statefulset is truly deleted
c.logger.Debugf("waiting for the statefulset to be deleted")
err := retryutil.Retry(constants.StatefulsetDeletionInterval, constants.StatefulsetDeletionTimeout,
err = retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
func() (bool, error) {
_, err := c.KubeClient.StatefulSets(oldStatefulset.Namespace).Get(oldStatefulset.Name, metav1.GetOptions{})
return err != nil, nil
_, err2 := c.KubeClient.StatefulSets(oldStatefulset.Namespace).Get(oldStatefulset.Name, metav1.GetOptions{})
if err2 == nil {
return false, nil
}
if k8sutil.ResourceNotFound(err2) {
return true, nil
}
return false, err2
})
if err != nil {
return fmt.Errorf("could not delete statefulset: %v", err)
@@ -380,13 +385,27 @@ func (c *Cluster) updateService(role PostgresRole, newService *v1.Service) error
return fmt.Errorf("could not delete service %q: %v", serviceName, err)
}
c.Endpoints[role] = nil
svc, err := c.KubeClient.Services(serviceName.Namespace).Create(newService)
// wait until the service is truly deleted
c.logger.Debugf("waiting for service to be deleted")
err = retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
func() (bool, error) {
_, err2 := c.KubeClient.Services(serviceName.Namespace).Get(serviceName.Name, metav1.GetOptions{})
if err2 == nil {
return false, nil
}
if k8sutil.ResourceNotFound(err2) {
return true, nil
}
return false, err2
})
if err != nil {
return fmt.Errorf("could not create service %q: %v", serviceName, err)
return fmt.Errorf("could not delete service %q: %v", serviceName, err)
}
c.Services[role] = svc
// make sure we clear the stored service and endpoint status if the subsequent create fails.
c.Services[role] = nil
c.Endpoints[role] = nil
if role == Master {
// create the new endpoint using the addresses obtained from the previous one
endpointSpec := c.generateEndpoint(role, currentEndpoint.Subsets)
@@ -398,6 +417,13 @@ func (c *Cluster) updateService(role PostgresRole, newService *v1.Service) error
c.Endpoints[role] = ep
}
svc, err := c.KubeClient.Services(serviceName.Namespace).Create(newService)
if err != nil {
return fmt.Errorf("could not create service %q: %v", serviceName, err)
}
c.Services[role] = svc
return nil
}
+2 -2
View File
@@ -240,7 +240,7 @@ func (c *Controller) initController() {
c.initClients()
if configObjectName := os.Getenv("POSTGRES_OPERATOR_CONFIGURATION_OBJECT"); configObjectName != "" {
if err := c.createConfigurationCRD(); err != nil {
if err := c.createConfigurationCRD(c.opConfig.EnableCRDValidation); err != nil {
c.logger.Fatalf("could not register Operator Configuration CustomResourceDefinition: %v", err)
}
if cfg, err := c.readOperatorConfigurationFromCRD(spec.GetOperatorNamespace(), configObjectName); err != nil {
@@ -256,7 +256,7 @@ func (c *Controller) initController() {
c.modifyConfigFromEnvironment()
if err := c.createPostgresCRD(); err != nil {
if err := c.createPostgresCRD(c.opConfig.EnableCRDValidation); err != nil {
c.logger.Fatalf("could not register Postgres CustomResourceDefinition: %v", err)
}
+5
View File
@@ -25,6 +25,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result := &config.Config{}
// general config
result.EnableCRDValidation = fromCRD.EnableCRDValidation
result.EtcdHost = fromCRD.EtcdHost
result.DockerImage = fromCRD.DockerImage
result.Workers = fromCRD.Workers
@@ -101,6 +102,10 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.LogicalBackupSchedule = fromCRD.LogicalBackup.Schedule
result.LogicalBackupDockerImage = fromCRD.LogicalBackup.DockerImage
result.LogicalBackupS3Bucket = fromCRD.LogicalBackup.S3Bucket
result.LogicalBackupS3Endpoint = fromCRD.LogicalBackup.S3Endpoint
result.LogicalBackupS3AccessKeyID = fromCRD.LogicalBackup.S3AccessKeyID
result.LogicalBackupS3SecretAccessKey = fromCRD.LogicalBackup.S3SecretAccessKey
result.LogicalBackupS3SSE = fromCRD.LogicalBackup.S3SSE
// debug config
result.DebugLogging = fromCRD.OperatorDebug.DebugLogging
+5 -5
View File
@@ -4,7 +4,7 @@ import (
"encoding/json"
"fmt"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
@@ -91,12 +91,12 @@ func (c *Controller) createOperatorCRD(crd *apiextv1beta1.CustomResourceDefiniti
})
}
func (c *Controller) createPostgresCRD() error {
return c.createOperatorCRD(acidv1.PostgresCRD())
func (c *Controller) createPostgresCRD(enableValidation *bool) error {
return c.createOperatorCRD(acidv1.PostgresCRD(enableValidation))
}
func (c *Controller) createConfigurationCRD() error {
return c.createOperatorCRD(acidv1.ConfigurationCRD())
func (c *Controller) createConfigurationCRD(enableValidation *bool) error {
return c.createOperatorCRD(acidv1.ConfigurationCRD(enableValidation))
}
func readDecodedRole(s string) (*spec.PgUser, error) {
+13 -8
View File
@@ -12,10 +12,11 @@ import (
// CRD describes CustomResourceDefinition specific configuration parameters
type CRD struct {
ReadyWaitInterval time.Duration `name:"ready_wait_interval" default:"4s"`
ReadyWaitTimeout time.Duration `name:"ready_wait_timeout" default:"30s"`
ResyncPeriod time.Duration `name:"resync_period" default:"30m"`
RepairPeriod time.Duration `name:"repair_period" default:"5m"`
ReadyWaitInterval time.Duration `name:"ready_wait_interval" default:"4s"`
ReadyWaitTimeout time.Duration `name:"ready_wait_timeout" default:"30s"`
ResyncPeriod time.Duration `name:"resync_period" default:"30m"`
RepairPeriod time.Duration `name:"repair_period" default:"5m"`
EnableCRDValidation *bool `name:"enable_crd_validation" default:"true"`
}
// Resources describes kubernetes resource specific configuration parameters
@@ -68,11 +69,15 @@ type Scalyr struct {
ScalyrMemoryLimit string `name:"scalyr_memory_limit" default:"1Gi"`
}
// LogicalBackup
// LogicalBackup defines configration 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"`
LogicalBackupS3Bucket string `name:"logical_backup_s3_bucket" default:""`
LogicalBackupSchedule string `name:"logical_backup_schedule" default:"30 00 * * *"`
LogicalBackupDockerImage string `name:"logical_backup_docker_image" default:"registry.opensource.zalan.do/acid/logical-backup"`
LogicalBackupS3Bucket string `name:"logical_backup_s3_bucket" default:""`
LogicalBackupS3Endpoint string `name:"logical_backup_s3_endpoint" default:""`
LogicalBackupS3AccessKeyID string `name:"logical_backup_s3_access_key_id" default:""`
LogicalBackupS3SecretAccessKey string `name:"logical_backup_s3_secret_access_key" default:""`
LogicalBackupS3SSE string `name:"logical_backup_s3_sse" default:"AES256"`
}
// Config describes operator config
+3 -5
View File
@@ -4,11 +4,9 @@ import "time"
// General kubernetes-related constants
const (
PostgresContainerName = "postgres"
PostgresContainerIdx = 0
K8sAPIPath = "/apis"
StatefulsetDeletionInterval = 1 * time.Second
StatefulsetDeletionTimeout = 30 * time.Second
PostgresContainerName = "postgres"
PostgresContainerIdx = 0
K8sAPIPath = "/apis"
QueueResyncPeriodPod = 5 * time.Minute
QueueResyncPeriodTPR = 5 * time.Minute