diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index d14d3d9d7..76ddb9ff9 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -12,7 +12,7 @@ configuration. * CRD-based configuration. The configuration is stored in the custom YAML manifest, an instance of the custom resource definition (CRD) called - `postgresql-operator-configuration`. This CRD is registered by the operator + `OperatorConfiguration`. This CRD is registered by the operator during the start when `POSTGRES_OPERATOR_CONFIGURATION_OBJECT` variable is set to a non-empty value. The CRD-based configuration is a regular YAML document; non-scalar keys are simply represented in the usual YAML way. The diff --git a/glide.lock b/glide.lock index f87d10955..19cb6c41f 100644 --- a/glide.lock +++ b/glide.lock @@ -1,8 +1,8 @@ -hash: ff2f80192f85899fb70880aabc4851672673f8ac3be257c6d9ff46ad33db94ca -updated: 2018-08-06T15:28:34.096941+02:00 +hash: bd5394acf101795aac9da20c104a57344a6c4fd71080bf1b16845367e6360578 +updated: 2018-08-14T15:18:08.144086+02:00 imports: - name: github.com/aws/aws-sdk-go - version: f70339bb6af843c8ab1974381b3f4fcaee2b1a41 + version: f831d5a0822a1ad72420ab18c6269bca1ddaf490 subpackages: - aws - aws/awserr @@ -103,7 +103,7 @@ imports: - name: github.com/Sirupsen/logrus version: 3e01752db0189b9157070a0e1668a620f9a85da2 - name: github.com/spf13/pflag - version: 9ff6c6923cfffbcd502984b8e0c80539a94968b7 + version: 583c0c0531f06d5278b7d917446061adc344b5cd - name: golang.org/x/crypto version: c126467f60eb25f8f27e5a981f32a87e3965053f subpackages: @@ -124,17 +124,10 @@ imports: - name: golang.org/x/text version: b19bf474d317b857955b12035d2c5acb57ce8b01 subpackages: - - cases - - internal - - internal/tag - - language - - runes - secure/bidirule - - secure/precis - transform - unicode/bidi - unicode/norm - - width - name: golang.org/x/time version: f51c12702a4d776e4c1fa9b0fabab841babae631 subpackages: @@ -144,7 +137,7 @@ imports: - name: gopkg.in/yaml.v2 version: 5420a8b6744d3b0345ab293f6fcba19c978f1183 - name: k8s.io/api - version: 072894a440bdee3a891dea811fe42902311cd2a3 + version: 2d6f90ab1293a1fb871cf149423ebb72aa7423aa subpackages: - admissionregistration/v1alpha1 - admissionregistration/v1beta1 @@ -164,6 +157,7 @@ imports: - core/v1 - events/v1beta1 - extensions/v1beta1 + - imagepolicy/v1alpha1 - networking/v1 - policy/v1beta1 - rbac/v1 @@ -176,7 +170,7 @@ imports: - storage/v1alpha1 - storage/v1beta1 - name: k8s.io/apiextensions-apiserver - version: 06dfdaae5c2bd89e1243151ff65b9bf8ee050f28 + version: cc9cd5d998df84cc405d398e9030d29c95acff18 subpackages: - pkg/apis/apiextensions - pkg/apis/apiextensions/v1beta1 @@ -216,22 +210,26 @@ imports: - pkg/util/httpstream/spdy - pkg/util/intstr - pkg/util/json + - pkg/util/mergepatch - pkg/util/net - pkg/util/remotecommand - pkg/util/runtime - pkg/util/sets + - pkg/util/strategicpatch - pkg/util/validation - pkg/util/validation/field - pkg/util/wait - pkg/util/yaml - pkg/version - pkg/watch + - third_party/forked/golang/json - third_party/forked/golang/netutil - third_party/forked/golang/reflect - name: k8s.io/client-go - version: 7d04d0e2a0a1a4d4a1cd6baa432a2301492e4e65 + version: 1f13a808da65775f22cbf47862c4e5898d8f4ca1 subpackages: - discovery + - discovery/fake - kubernetes - kubernetes/scheme - kubernetes/typed/admissionregistration/v1alpha1 @@ -270,6 +268,7 @@ imports: - plugin/pkg/client/auth/exec - rest - rest/watch + - testing - tools/auth - tools/cache - tools/clientcmd @@ -294,4 +293,8 @@ imports: version: 6702109cc68eb6fe6350b83e14407c8d7309fd1a - name: k8s.io/gengo version: 906d99f89cd644eecf75ab547b29bf9f876f0b59 +- name: k8s.io/kube-openapi + version: 91cfa479c814065e420cee7ed227db0f63a5854e + subpackages: + - pkg/util/proto testImports: [] diff --git a/glide.yaml b/glide.yaml index 625a06d62..74e837877 100644 --- a/glide.yaml +++ b/glide.yaml @@ -11,13 +11,13 @@ import: - package: github.com/lib/pq - package: github.com/motomux/pretty - package: k8s.io/apimachinery - version: kubernetes-1.11.1 + version: kubernetes-1.11.3-beta.0 - package: k8s.io/apiextensions-apiserver - version: kubernetes-1.11.1 + version: kubernetes-1.11.3-beta.0 - package: k8s.io/client-go - version: ^8.0.0 + version: kubernetes-1.11.3-beta.0 - package: k8s.io/code-generator - version: kubernetes-1.11.1 + version: kubernetes-1.11.3-beta.0 - package: k8s.io/gengo - package: gopkg.in/yaml.v2 - package: github.com/mohae/deepcopy diff --git a/hack/custom-boilerplate.go.txt b/hack/custom-boilerplate.go.txt new file mode 100644 index 000000000..8fca2b20f --- /dev/null +++ b/hack/custom-boilerplate.go.txt @@ -0,0 +1,21 @@ +/* +Copyright YEAR Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh new file mode 100755 index 000000000..a1c1555c9 --- /dev/null +++ b/hack/update-codegen.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -o errexit +set -o nounset +set -o pipefail + +SCRIPT_ROOT=$(dirname ${BASH_SOURCE})/.. +CODEGEN_PKG=${CODEGEN_PKG:-$(cd ${SCRIPT_ROOT}; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ${GOPATH}/src/k8s.io/code-generator)} + +vendor/k8s.io/code-generator/generate-groups.sh all \ + github.com/zalando-incubator/postgres-operator/pkg/generated github.com/zalando-incubator/postgres-operator/pkg/apis \ + acid.zalan.do:v1 \ + --go-header-file ${SCRIPT_ROOT}/hack/custom-boilerplate.go.txt diff --git a/hack/verify-codegen.sh b/hack/verify-codegen.sh new file mode 100755 index 000000000..904586d05 --- /dev/null +++ b/hack/verify-codegen.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -o errexit +set -o nounset +set -o pipefail + +SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")/.. +DIFFROOT="${SCRIPT_ROOT}/pkg" +TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/pkg" +_tmp="${SCRIPT_ROOT}/_tmp" + +cleanup() { + rm -rf "${_tmp}" +} +trap "cleanup" EXIT SIGINT + +cleanup + +mkdir -p "${TMP_DIFFROOT}" +cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}" + +"${SCRIPT_ROOT}/hack/update-codegen.sh" +echo "diffing ${DIFFROOT} against freshly generated codegen" +ret=0 +diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$? +cp -a "${TMP_DIFFROOT}"/* "${DIFFROOT}" +if [[ $ret -eq 0 ]] +then + echo "${DIFFROOT} up to date." +else + echo "${DIFFROOT} is out of date. Please run hack/update-codegen.sh" + exit 1 +fi diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 05fa935e9..d2a1307f8 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -1,5 +1,5 @@ apiVersion: "acid.zalan.do/v1" -kind: postgresql-operator-configuration +kind: OperatorConfiguration metadata: name: postgresql-operator-default-configuration configuration: diff --git a/pkg/apis/acid.zalan.do/register.go b/pkg/apis/acid.zalan.do/register.go new file mode 100644 index 000000000..f99396656 --- /dev/null +++ b/pkg/apis/acid.zalan.do/register.go @@ -0,0 +1,6 @@ +package acidzalando + +const ( + // GroupName is the group name for the operator CRDs + GroupName = "acid.zalan.do" +) diff --git a/pkg/apis/acid.zalan.do/v1/const.go b/pkg/apis/acid.zalan.do/v1/const.go new file mode 100644 index 000000000..4592a2d68 --- /dev/null +++ b/pkg/apis/acid.zalan.do/v1/const.go @@ -0,0 +1,16 @@ +package v1 + +const ( + serviceNameMaxLength = 63 + clusterNameMaxLength = serviceNameMaxLength - len("-repl") + serviceNameRegexString = `^[a-z]([-a-z0-9]*[a-z0-9])?$` + + ClusterStatusUnknown PostgresStatus = "" + ClusterStatusCreating PostgresStatus = "Creating" + ClusterStatusUpdating PostgresStatus = "Updating" + ClusterStatusUpdateFailed PostgresStatus = "UpdateFailed" + ClusterStatusSyncFailed PostgresStatus = "SyncFailed" + ClusterStatusAddFailed PostgresStatus = "CreateFailed" + ClusterStatusRunning PostgresStatus = "Running" + ClusterStatusInvalid PostgresStatus = "Invalid" +) diff --git a/pkg/apis/acid.zalan.do/v1/doc.go b/pkg/apis/acid.zalan.do/v1/doc.go new file mode 100644 index 000000000..5accd806d --- /dev/null +++ b/pkg/apis/acid.zalan.do/v1/doc.go @@ -0,0 +1,6 @@ +// +k8s:deepcopy-gen=package,register + +// Package v1 is the v1 version of the API. +// +groupName=acid.zalan.do + +package v1 diff --git a/pkg/apis/acid.zalan.do/v1/marshal.go b/pkg/apis/acid.zalan.do/v1/marshal.go new file mode 100644 index 000000000..b24c4e49d --- /dev/null +++ b/pkg/apis/acid.zalan.do/v1/marshal.go @@ -0,0 +1,130 @@ +package v1 + +import ( + "encoding/json" + "fmt" + "strings" + "time" +) + +type postgresqlCopy Postgresql + +// MarshalJSON converts a maintenance window definition to JSON. +func (m *MaintenanceWindow) MarshalJSON() ([]byte, error) { + if m.Everyday { + return []byte(fmt.Sprintf("\"%s-%s\"", + m.StartTime.Format("15:04"), + m.EndTime.Format("15:04"))), nil + } + + return []byte(fmt.Sprintf("\"%s:%s-%s\"", + m.Weekday.String()[:3], + m.StartTime.Format("15:04"), + m.EndTime.Format("15:04"))), nil +} + +// UnmarshalJSON converts a JSON to the maintenance window definition. +func (m *MaintenanceWindow) UnmarshalJSON(data []byte) error { + var ( + got MaintenanceWindow + err error + ) + + parts := strings.Split(string(data[1:len(data)-1]), "-") + if len(parts) != 2 { + return fmt.Errorf("incorrect maintenance window format") + } + + fromParts := strings.Split(parts[0], ":") + switch len(fromParts) { + case 3: + got.Everyday = false + got.Weekday, err = parseWeekday(fromParts[0]) + if err != nil { + return fmt.Errorf("could not parse weekday: %v", err) + } + + got.StartTime, err = parseTime(fromParts[1] + ":" + fromParts[2]) + case 2: + got.Everyday = true + got.StartTime, err = parseTime(fromParts[0] + ":" + fromParts[1]) + default: + return fmt.Errorf("incorrect maintenance window format") + } + if err != nil { + return fmt.Errorf("could not parse start time: %v", err) + } + + got.EndTime, err = parseTime(parts[1]) + if err != nil { + return fmt.Errorf("could not parse end time: %v", err) + } + + if got.EndTime.Before(&got.StartTime) { + return fmt.Errorf("'From' time must be prior to the 'To' time") + } + + *m = got + + return nil +} + +// UnmarshalJSON converts a JSON into the PostgreSQL object. +func (p *Postgresql) UnmarshalJSON(data []byte) error { + var tmp postgresqlCopy + + err := json.Unmarshal(data, &tmp) + if err != nil { + metaErr := json.Unmarshal(data, &tmp.ObjectMeta) + if metaErr != nil { + return err + } + + tmp.Error = err.Error() + tmp.Status = ClusterStatusInvalid + + *p = Postgresql(tmp) + + return nil + } + tmp2 := Postgresql(tmp) + + if clusterName, err := extractClusterName(tmp2.ObjectMeta.Name, tmp2.Spec.TeamID); err != nil { + tmp2.Error = err.Error() + tmp2.Status = ClusterStatusInvalid + } else if err := validateCloneClusterDescription(&tmp2.Spec.Clone); err != nil { + tmp2.Error = err.Error() + tmp2.Status = ClusterStatusInvalid + } else { + tmp2.Spec.ClusterName = clusterName + } + + *p = tmp2 + + return nil +} + +func (d *Duration) UnmarshalJSON(b []byte) error { + var ( + v interface{} + err error + ) + if err = json.Unmarshal(b, &v); err != nil { + return err + } + switch val := v.(type) { + case string: + t, err := time.ParseDuration(val) + if err != nil { + return err + } + *d = Duration(t) + return nil + case float64: + t := time.Duration(val) + *d = Duration(t) + return nil + default: + return fmt.Errorf("could not recognize type %T as a valid type to unmarshal to Duration", val) + } +} diff --git a/pkg/util/config/crd_config.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go similarity index 56% rename from pkg/util/config/crd_config.go rename to pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 2a9090514..cd70d76d9 100644 --- a/pkg/util/config/crd_config.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -1,23 +1,25 @@ -package config +package v1 import ( - "encoding/json" + "github.com/zalando-incubator/postgres-operator/pkg/util/config" "github.com/zalando-incubator/postgres-operator/pkg/spec" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - - "github.com/mohae/deepcopy" + "time" ) +// +genclient +// +genclient:onlyVerbs=get +// +genclient:noStatus +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type OperatorConfiguration struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata"` Configuration OperatorConfigurationData `json:"configuration"` - Error error `json:"-"` } +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type OperatorConfigurationList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata"` @@ -33,18 +35,18 @@ type PostgresUsersConfiguration struct { type KubernetesMetaConfiguration struct { PodServiceAccountName string `json:"pod_service_account_name,omitempty"` // TODO: change it to the proper json - PodServiceAccountDefinition string `json:"pod_service_account_definition,omitempty"` - PodServiceAccountRoleBindingDefinition string `json:"pod_service_account_role_binding_definition,omitempty"` - PodTerminateGracePeriod spec.Duration `json:"pod_terminate_grace_period,omitempty"` - WatchedNamespace string `json:"watched_namespace,omitempty"` - PDBNameFormat stringTemplate `json:"pdb_name_format,omitempty"` - SecretNameTemplate stringTemplate `json:"secret_name_template,omitempty"` - OAuthTokenSecretName spec.NamespacedName `json:"oauth_token_secret_name,omitempty"` - InfrastructureRolesSecretName spec.NamespacedName `json:"infrastructure_roles_secret_name,omitempty"` - PodRoleLabel string `json:"pod_role_label,omitempty"` - ClusterLabels map[string]string `json:"cluster_labels,omitempty"` - ClusterNameLabel string `json:"cluster_name_label,omitempty"` - NodeReadinessLabel map[string]string `json:"node_readiness_label,omitempty"` + PodServiceAccountDefinition string `json:"pod_service_account_definition,omitempty"` + PodServiceAccountRoleBindingDefinition string `json:"pod_service_account_role_binding_definition,omitempty"` + PodTerminateGracePeriod Duration `json:"pod_terminate_grace_period,omitempty"` + WatchedNamespace string `json:"watched_namespace,omitempty"` + PDBNameFormat config.StringTemplate `json:"pdb_name_format,omitempty"` + SecretNameTemplate config.StringTemplate `json:"secret_name_template,omitempty"` + OAuthTokenSecretName spec.NamespacedName `json:"oauth_token_secret_name,omitempty"` + InfrastructureRolesSecretName spec.NamespacedName `json:"infrastructure_roles_secret_name,omitempty"` + PodRoleLabel string `json:"pod_role_label,omitempty"` + ClusterLabels map[string]string `json:"cluster_labels,omitempty"` + ClusterNameLabel string `json:"cluster_name_label,omitempty"` + NodeReadinessLabel map[string]string `json:"node_readiness_label,omitempty"` // TODO: use a proper toleration structure? PodToleration map[string]string `json:"toleration,omitempty"` // TODO: use namespacedname @@ -60,20 +62,20 @@ type PostgresPodResourcesDefaults struct { } type OperatorTimeouts struct { - ResourceCheckInterval spec.Duration `json:"resource_check_interval,omitempty"` - ResourceCheckTimeout spec.Duration `json:"resource_check_timeout,omitempty"` - PodLabelWaitTimeout spec.Duration `json:"pod_label_wait_timeout,omitempty"` - PodDeletionWaitTimeout spec.Duration `json:"pod_deletion_wait_timeout,omitempty"` - ReadyWaitInterval spec.Duration `json:"ready_wait_interval,omitempty"` - ReadyWaitTimeout spec.Duration `json:"ready_wait_timeout,omitempty"` + ResourceCheckInterval Duration `json:"resource_check_interval,omitempty"` + ResourceCheckTimeout Duration `json:"resource_check_timeout,omitempty"` + PodLabelWaitTimeout Duration `json:"pod_label_wait_timeout,omitempty"` + PodDeletionWaitTimeout Duration `json:"pod_deletion_wait_timeout,omitempty"` + ReadyWaitInterval Duration `json:"ready_wait_interval,omitempty"` + ReadyWaitTimeout Duration `json:"ready_wait_timeout,omitempty"` } type LoadBalancerConfiguration struct { - DbHostedZone string `json:"db_hosted_zone,omitempty"` - EnableMasterLoadBalancer bool `json:"enable_master_load_balancer,omitempty"` - EnableReplicaLoadBalancer bool `json:"enable_replica_load_balancer,omitempty"` - MasterDNSNameFormat stringTemplate `json:"master_dns_name_format,omitempty"` - ReplicaDNSNameFormat stringTemplate `json:"replica_dns_name_format,omitempty"` + DbHostedZone string `json:"db_hosted_zone,omitempty"` + EnableMasterLoadBalancer bool `json:"enable_master_load_balancer,omitempty"` + EnableReplicaLoadBalancer bool `json:"enable_replica_load_balancer,omitempty"` + MasterDNSNameFormat config.StringTemplate `json:"master_dns_name_format,omitempty"` + ReplicaDNSNameFormat config.StringTemplate `json:"replica_dns_name_format,omitempty"` } type AWSGCPConfiguration struct { @@ -121,8 +123,8 @@ type OperatorConfigurationData struct { Workers uint32 `json:"workers,omitempty"` MinInstances int32 `json:"min_instances,omitempty"` MaxInstances int32 `json:"max_instances,omitempty"` - ResyncPeriod spec.Duration `json:"resync_period,omitempty"` - RepairPeriod spec.Duration `json:"repair_period,omitempty"` + ResyncPeriod Duration `json:"resync_period,omitempty"` + RepairPeriod Duration `json:"repair_period,omitempty"` Sidecars map[string]string `json:"sidecar_docker_images,omitempty"` PostgresUsersConfiguration PostgresUsersConfiguration `json:"users"` Kubernetes KubernetesMetaConfiguration `json:"kubernetes"` @@ -143,67 +145,4 @@ type OperatorConfigurationUsers struct { TeamAPIRoleConfiguration map[string]string `json:"team_api_role_configuration,omitempty"` } -type OperatorConfigurationCopy OperatorConfiguration -type OperatorConfigurationListCopy OperatorConfigurationList - -func (opc *OperatorConfiguration) UnmarshalJSON(data []byte) error { - var ref OperatorConfigurationCopy - if err := json.Unmarshal(data, &ref); err != nil { - return err - } - *opc = OperatorConfiguration(ref) - return nil -} - -func (opc *OperatorConfiguration) DeepCopyInto(out *OperatorConfiguration) { - if opc != nil { - *out = deepcopy.Copy(*opc).(OperatorConfiguration) - } -} - -func (opc *OperatorConfiguration) DeepCopy() *OperatorConfiguration { - if opc == nil { - return nil - } - out := new(OperatorConfiguration) - opc.DeepCopyInto(out) - return out -} - -func (opc *OperatorConfiguration) DeepCopyObject() runtime.Object { - if c := opc.DeepCopy(); c != nil { - return c - } - return nil -} - -func (opcl *OperatorConfigurationList) UnmarshalJSON(data []byte) error { - var ref OperatorConfigurationListCopy - if err := json.Unmarshal(data, &ref); err != nil { - return nil - } - *opcl = OperatorConfigurationList(ref) - return nil -} - -func (opcl *OperatorConfigurationList) DeepCopyInto(out *OperatorConfigurationList) { - if opcl != nil { - *out = deepcopy.Copy(*opcl).(OperatorConfigurationList) - } -} - -func (opcl *OperatorConfigurationList) DeepCopy() *OperatorConfigurationList { - if opcl == nil { - return nil - } - out := new(OperatorConfigurationList) - opcl.DeepCopyInto(out) - return out -} - -func (opcl *OperatorConfigurationList) DeepCopyObject() runtime.Object { - if c := opcl.DeepCopy(); c != nil { - return c - } - return nil -} +type Duration time.Duration diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go new file mode 100644 index 000000000..3f6d34165 --- /dev/null +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -0,0 +1,127 @@ +package v1 + +import ( + "time" + + "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +//Postgresql defines PostgreSQL Custom Resource Definition Object. +type Postgresql struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PostgresSpec `json:"spec"` + Status PostgresStatus `json:"status,omitempty"` + Error string `json:"-"` +} + +// PostgresSpec defines the specification for the PostgreSQL TPR. +type PostgresSpec struct { + PostgresqlParam `json:"postgresql"` + Volume `json:"volume,omitempty"` + Patroni `json:"patroni,omitempty"` + Resources `json:"resources,omitempty"` + + TeamID string `json:"teamId"` + DockerImage string `json:"dockerImage,omitempty"` + + // vars that enable load balancers are pointers because it is important to know if any of them is omitted from the Postgres manifest + // in that case the var evaluates to nil and the value is taken from the operator config + EnableMasterLoadBalancer *bool `json:"enableMasterLoadBalancer,omitempty"` + EnableReplicaLoadBalancer *bool `json:"enableReplicaLoadBalancer,omitempty"` + + // deprecated load balancer settings maintained for backward compatibility + // see "Load balancers" operator docs + UseLoadBalancer *bool `json:"useLoadBalancer,omitempty"` + ReplicaLoadBalancer *bool `json:"replicaLoadBalancer,omitempty"` + + // load balancers' source ranges are the same for master and replica services + AllowedSourceRanges []string `json:"allowedSourceRanges"` + + NumberOfInstances int32 `json:"numberOfInstances"` + Users map[string]UserFlags `json:"users"` + MaintenanceWindows []MaintenanceWindow `json:"maintenanceWindows,omitempty"` + Clone CloneDescription `json:"clone"` + ClusterName string `json:"-"` + Databases map[string]string `json:"databases,omitempty"` + Tolerations []v1.Toleration `json:"tolerations,omitempty"` + Sidecars []Sidecar `json:"sidecars,omitempty"` + PodPriorityClassName string `json:"pod_priority_class_name,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// PostgresqlList defines a list of PostgreSQL clusters. +type PostgresqlList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + Items []Postgresql `json:"items"` +} + +// MaintenanceWindow describes the time window when the operator is allowed to do maintenance on a cluster. +type MaintenanceWindow struct { + Everyday bool + Weekday time.Weekday + StartTime metav1.Time // Start time + EndTime metav1.Time // End time +} + +// Volume describes a single volume in the manifest. +type Volume struct { + Size string `json:"size"` + StorageClass string `json:"storageClass"` +} + +// PostgresqlParam describes PostgreSQL version and pairs of configuration parameter name - values. +type PostgresqlParam struct { + PgVersion string `json:"version"` + Parameters map[string]string `json:"parameters"` +} + +// ResourceDescription describes CPU and memory resources defined for a cluster. +type ResourceDescription struct { + CPU string `json:"cpu"` + Memory string `json:"memory"` +} + +// Resources describes requests and limits for the cluster resouces. +type Resources struct { + ResourceRequest ResourceDescription `json:"requests,omitempty"` + ResourceLimits ResourceDescription `json:"limits,omitempty"` +} + +// Patroni contains Patroni-specific configuration +type Patroni struct { + InitDB map[string]string `json:"initdb"` + PgHba []string `json:"pg_hba"` + TTL uint32 `json:"ttl"` + LoopWait uint32 `json:"loop_wait"` + RetryTimeout uint32 `json:"retry_timeout"` + MaximumLagOnFailover float32 `json:"maximum_lag_on_failover"` // float32 because https://github.com/kubernetes/kubernetes/issues/30213 +} + +// CloneDescription describes which cluster the new should clone and up to which point in time +type CloneDescription struct { + ClusterName string `json:"cluster,omitempty"` + UID string `json:"uid,omitempty"` + EndTimestamp string `json:"timestamp,omitempty"` +} + +// Sidecar defines a container to be run in the same pod as the Postgres container. +type Sidecar struct { + Resources `json:"resources,omitempty"` + Name string `json:"name,omitempty"` + DockerImage string `json:"image,omitempty"` + Ports []v1.ContainerPort `json:"ports,omitempty"` + Env []v1.EnvVar `json:"env,omitempty"` +} + +// UserFlags defines flags (such as superuser, nologin) that could be assigned to individual users +type UserFlags []string + +// PostgresStatus contains status of the PostgreSQL cluster (running, creation failed etc.) +type PostgresStatus string diff --git a/pkg/apis/acid.zalan.do/v1/register.go b/pkg/apis/acid.zalan.do/v1/register.go new file mode 100644 index 000000000..210bf782e --- /dev/null +++ b/pkg/apis/acid.zalan.do/v1/register.go @@ -0,0 +1,58 @@ +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do" +) + +const ( + PostgresCRDResourceKind = "postgresql" + PostgresCRDResourcePlural = "postgresqls" + PostgresCRDResouceName = PostgresCRDResourcePlural + "." + acidzalando.GroupName + PostgresCRDResourceShort = "pg" + + OperatorConfigCRDResouceKind = "OperatorConfiguration" + OperatorConfigCRDResourcePlural = "operatorconfigurations" + OperatorConfigCRDResourceName = OperatorConfigCRDResourcePlural + "." + acidzalando.GroupName + OperatorConfigCRDResourceShort = "opconfig" + + APIVersion = "v1" +) + +var ( + // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. + SchemeBuilder runtime.SchemeBuilder + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme + SchemeGroupVersion = schema.GroupVersion{Group: acidzalando.GroupName, Version: APIVersion} +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addKnownTypes) +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + // AddKnownType assumes derives the type kind from the type name, which is always uppercase. + // For our CRDs we use lowercase names historically, therefore we have to supply the name separately. + // TODO: User uppercase CRDResourceKind of our types in the next major API version + scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("postgresql"), &Postgresql{}) + scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("postgresqlList"), &PostgresqlList{}) + scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("OperatorConfiguration"), + &OperatorConfiguration{}) + scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("OperatorConfigurationList"), + &OperatorConfigurationList{}) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/acid.zalan.do/v1/util.go b/pkg/apis/acid.zalan.do/v1/util.go new file mode 100644 index 000000000..7d071ce22 --- /dev/null +++ b/pkg/apis/acid.zalan.do/v1/util.go @@ -0,0 +1,93 @@ +package v1 + +import ( + "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "regexp" + "strings" + "time" +) + +var ( + weekdays = map[string]int{"Sun": 0, "Mon": 1, "Tue": 2, "Wed": 3, "Thu": 4, "Fri": 5, "Sat": 6} + serviceNameRegex = regexp.MustCompile(serviceNameRegexString) +) + +func (p *Postgresql) Clone() *Postgresql { + if p == nil { + return nil + } + return p.DeepCopy() +} + +func parseTime(s string) (metav1.Time, error) { + parts := strings.Split(s, ":") + if len(parts) != 2 { + return metav1.Time{}, fmt.Errorf("incorrect time format") + } + timeLayout := "15:04" + + tp, err := time.Parse(timeLayout, s) + if err != nil { + return metav1.Time{}, err + } + + return metav1.Time{Time: tp.UTC()}, nil +} + +func parseWeekday(s string) (time.Weekday, error) { + weekday, ok := weekdays[s] + if !ok { + return time.Weekday(0), fmt.Errorf("incorrect weekday") + } + + return time.Weekday(weekday), nil +} + +func extractClusterName(clusterName string, teamName string) (string, error) { + teamNameLen := len(teamName) + if len(clusterName) < teamNameLen+2 { + return "", fmt.Errorf("name is too short") + } + + if teamNameLen == 0 { + return "", fmt.Errorf("team name is empty") + } + + if strings.ToLower(clusterName[:teamNameLen+1]) != strings.ToLower(teamName)+"-" { + return "", fmt.Errorf("name must match {TEAM}-{NAME} format") + } + if len(clusterName) > clusterNameMaxLength { + return "", fmt.Errorf("name cannot be longer than %d characters", clusterNameMaxLength) + } + if !serviceNameRegex.MatchString(clusterName) { + return "", fmt.Errorf("name must confirm to DNS-1035, regex used for validation is %q", + serviceNameRegexString) + } + + return clusterName[teamNameLen+1:], nil +} + +func validateCloneClusterDescription(clone *CloneDescription) error { + // when cloning from the basebackup (no end timestamp) check that the cluster name is a valid service name + if clone.ClusterName != "" && clone.EndTimestamp == "" { + if !serviceNameRegex.MatchString(clone.ClusterName) { + return fmt.Errorf("clone cluster name must confirm to DNS-1035, regex used for validation is %q", + serviceNameRegexString) + } + if len(clone.ClusterName) > serviceNameMaxLength { + return fmt.Errorf("clone cluster name must be no longer than %d characters", serviceNameMaxLength) + } + } + return nil +} + +func (status PostgresStatus) Success() bool { + return status != ClusterStatusAddFailed && + status != ClusterStatusUpdateFailed && + status != ClusterStatusSyncFailed +} + +func (status PostgresStatus) String() string { + return string(status) +} diff --git a/pkg/spec/postgresql_test.go b/pkg/apis/acid.zalan.do/v1/util_test.go similarity index 95% rename from pkg/spec/postgresql_test.go rename to pkg/apis/acid.zalan.do/v1/util_test.go index aef698782..d1f06a2cc 100644 --- a/pkg/spec/postgresql_test.go +++ b/pkg/apis/acid.zalan.do/v1/util_test.go @@ -1,4 +1,4 @@ -package spec +package v1 import ( "bytes" @@ -12,15 +12,15 @@ import ( var parseTimeTests = []struct { in string - out time.Time + out metav1.Time err error }{ {"16:08", mustParseTime("16:08"), nil}, {"11:00", mustParseTime("11:00"), nil}, {"23:59", mustParseTime("23:59"), nil}, - {"26:09", time.Now(), errors.New(`parsing time "26:09": hour out of range`)}, - {"23:69", time.Now(), errors.New(`parsing time "23:69": minute out of range`)}, + {"26:09", metav1.Now(), errors.New(`parsing time "26:09": hour out of range`)}, + {"23:69", metav1.Now(), errors.New(`parsing time "23:69": minute out of range`)}, } var parseWeekdayTests = []struct { @@ -125,13 +125,13 @@ var unmarshalCluster = []struct { Name: "acid-testcluster1", }, Status: ClusterStatusInvalid, - Error: &json.UnmarshalTypeError{ + Error: (&json.UnmarshalTypeError{ Value: "number", Type: reflect.TypeOf(""), Offset: 126, Struct: "PostgresSpec", Field: "teamId", - }, + }).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},"resources":{"requests":{"cpu":"","memory":""},"limits":{"cpu":"","memory":""}},"teamId":"","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":{}},"status":"Invalid"}`), nil}, {[]byte(`{ @@ -264,7 +264,7 @@ var unmarshalCluster = []struct { }, ClusterName: "testcluster1", }, - Error: nil, + 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},"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}, { @@ -279,7 +279,7 @@ var unmarshalCluster = []struct { }, Spec: PostgresSpec{TeamID: "acid"}, Status: ClusterStatusInvalid, - Error: errors.New("name must match {TEAM}-{NAME} format"), + 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},"resources":{"requests":{"cpu":"","memory":""},"limits":{"cpu":"","memory":""}},"teamId":"acid","allowedSourceRanges":null,"numberOfInstances":0,"users":null,"clone":{}},"status":"Invalid"}`), nil}, { @@ -299,7 +299,7 @@ var unmarshalCluster = []struct { }, ClusterName: "testcluster1", }, - Error: nil, + 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},"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"`), @@ -344,7 +344,7 @@ var postgresqlList = []struct { NumberOfInstances: 1, }, Status: ClusterStatusRunning, - Error: nil, + Error: "", }}, }, nil}, @@ -352,13 +352,13 @@ var postgresqlList = []struct { PostgresqlList{}, errors.New("unexpected end of JSON input")}} -func mustParseTime(s string) time.Time { +func mustParseTime(s string) metav1.Time { v, err := time.Parse("15:04", s) if err != nil { panic(err) } - return v.UTC() + return metav1.Time{Time: v.UTC()} } func TestParseTime(t *testing.T) { @@ -509,25 +509,6 @@ func TestPostgresMeta(t *testing.T) { } } -func TestUnmarshalPostgresList(t *testing.T) { - for _, tt := range postgresqlList { - var list PostgresqlList - err := list.UnmarshalJSON(tt.in) - if err != nil { - if tt.err == nil || err.Error() != tt.err.Error() { - t.Errorf("PostgresqlList 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(list, tt.out) { - t.Errorf("Postgresql list unmarshall expected: %#v, got: %#v", tt.out, list) - } - } -} - func TestPostgresListMeta(t *testing.T) { for _, tt := range postgresqlList { if tt.err != nil { @@ -549,7 +530,7 @@ func TestPostgresListMeta(t *testing.T) { func TestPostgresqlClone(t *testing.T) { for _, tt := range unmarshalCluster { cp := &tt.out - cp.Error = nil + cp.Error = "" clone := cp.Clone() if !reflect.DeepEqual(clone, cp) { t.Errorf("TestPostgresqlClone expected: \n%#v\n, got \n%#v", cp, clone) diff --git a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go new file mode 100644 index 000000000..01280e548 --- /dev/null +++ b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go @@ -0,0 +1,681 @@ +// +build !ignore_autogenerated + +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1 + +import ( + corev1 "k8s.io/api/core/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AWSGCPConfiguration) DeepCopyInto(out *AWSGCPConfiguration) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSGCPConfiguration. +func (in *AWSGCPConfiguration) DeepCopy() *AWSGCPConfiguration { + if in == nil { + return nil + } + out := new(AWSGCPConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloneDescription) DeepCopyInto(out *CloneDescription) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloneDescription. +func (in *CloneDescription) DeepCopy() *CloneDescription { + if in == nil { + return nil + } + out := new(CloneDescription) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesMetaConfiguration) DeepCopyInto(out *KubernetesMetaConfiguration) { + *out = *in + out.OAuthTokenSecretName = in.OAuthTokenSecretName + out.InfrastructureRolesSecretName = in.InfrastructureRolesSecretName + if in.ClusterLabels != nil { + in, out := &in.ClusterLabels, &out.ClusterLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.NodeReadinessLabel != nil { + in, out := &in.NodeReadinessLabel, &out.NodeReadinessLabel + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.PodToleration != nil { + in, out := &in.PodToleration, &out.PodToleration + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesMetaConfiguration. +func (in *KubernetesMetaConfiguration) DeepCopy() *KubernetesMetaConfiguration { + if in == nil { + return nil + } + out := new(KubernetesMetaConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LoadBalancerConfiguration) DeepCopyInto(out *LoadBalancerConfiguration) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancerConfiguration. +func (in *LoadBalancerConfiguration) DeepCopy() *LoadBalancerConfiguration { + if in == nil { + return nil + } + out := new(LoadBalancerConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LoggingRESTAPIConfiguration) DeepCopyInto(out *LoggingRESTAPIConfiguration) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoggingRESTAPIConfiguration. +func (in *LoggingRESTAPIConfiguration) DeepCopy() *LoggingRESTAPIConfiguration { + if in == nil { + return nil + } + out := new(LoggingRESTAPIConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MaintenanceWindow) DeepCopyInto(out *MaintenanceWindow) { + *out = *in + in.StartTime.DeepCopyInto(&out.StartTime) + in.EndTime.DeepCopyInto(&out.EndTime) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MaintenanceWindow. +func (in *MaintenanceWindow) DeepCopy() *MaintenanceWindow { + if in == nil { + return nil + } + out := new(MaintenanceWindow) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OperatorConfiguration) DeepCopyInto(out *OperatorConfiguration) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Configuration.DeepCopyInto(&out.Configuration) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorConfiguration. +func (in *OperatorConfiguration) DeepCopy() *OperatorConfiguration { + if in == nil { + return nil + } + out := new(OperatorConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OperatorConfiguration) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// 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.Sidecars != nil { + in, out := &in.Sidecars, &out.Sidecars + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + out.PostgresUsersConfiguration = in.PostgresUsersConfiguration + in.Kubernetes.DeepCopyInto(&out.Kubernetes) + out.PostgresPodResources = in.PostgresPodResources + out.Timeouts = in.Timeouts + out.LoadBalancer = in.LoadBalancer + out.AWSGCP = in.AWSGCP + out.OperatorDebug = in.OperatorDebug + in.TeamsAPI.DeepCopyInto(&out.TeamsAPI) + out.LoggingRESTAPI = in.LoggingRESTAPI + out.Scalyr = in.Scalyr + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorConfigurationData. +func (in *OperatorConfigurationData) DeepCopy() *OperatorConfigurationData { + if in == nil { + return nil + } + out := new(OperatorConfigurationData) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OperatorConfigurationList) DeepCopyInto(out *OperatorConfigurationList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]OperatorConfiguration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorConfigurationList. +func (in *OperatorConfigurationList) DeepCopy() *OperatorConfigurationList { + if in == nil { + return nil + } + out := new(OperatorConfigurationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OperatorConfigurationList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OperatorConfigurationUsers) DeepCopyInto(out *OperatorConfigurationUsers) { + *out = *in + if in.ProtectedRoles != nil { + in, out := &in.ProtectedRoles, &out.ProtectedRoles + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.TeamAPIRoleConfiguration != nil { + in, out := &in.TeamAPIRoleConfiguration, &out.TeamAPIRoleConfiguration + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorConfigurationUsers. +func (in *OperatorConfigurationUsers) DeepCopy() *OperatorConfigurationUsers { + if in == nil { + return nil + } + out := new(OperatorConfigurationUsers) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OperatorDebugConfiguration) DeepCopyInto(out *OperatorDebugConfiguration) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorDebugConfiguration. +func (in *OperatorDebugConfiguration) DeepCopy() *OperatorDebugConfiguration { + if in == nil { + return nil + } + out := new(OperatorDebugConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OperatorTimeouts) DeepCopyInto(out *OperatorTimeouts) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorTimeouts. +func (in *OperatorTimeouts) DeepCopy() *OperatorTimeouts { + if in == nil { + return nil + } + out := new(OperatorTimeouts) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Patroni) DeepCopyInto(out *Patroni) { + *out = *in + if in.InitDB != nil { + in, out := &in.InitDB, &out.InitDB + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.PgHba != nil { + in, out := &in.PgHba, &out.PgHba + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Patroni. +func (in *Patroni) DeepCopy() *Patroni { + if in == nil { + return nil + } + out := new(Patroni) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgresPodResourcesDefaults) DeepCopyInto(out *PostgresPodResourcesDefaults) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresPodResourcesDefaults. +func (in *PostgresPodResourcesDefaults) DeepCopy() *PostgresPodResourcesDefaults { + if in == nil { + return nil + } + out := new(PostgresPodResourcesDefaults) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) { + *out = *in + in.PostgresqlParam.DeepCopyInto(&out.PostgresqlParam) + out.Volume = in.Volume + in.Patroni.DeepCopyInto(&out.Patroni) + out.Resources = in.Resources + if in.EnableMasterLoadBalancer != nil { + in, out := &in.EnableMasterLoadBalancer, &out.EnableMasterLoadBalancer + *out = new(bool) + **out = **in + } + if in.EnableReplicaLoadBalancer != nil { + in, out := &in.EnableReplicaLoadBalancer, &out.EnableReplicaLoadBalancer + *out = new(bool) + **out = **in + } + if in.UseLoadBalancer != nil { + in, out := &in.UseLoadBalancer, &out.UseLoadBalancer + *out = new(bool) + **out = **in + } + if in.ReplicaLoadBalancer != nil { + in, out := &in.ReplicaLoadBalancer, &out.ReplicaLoadBalancer + *out = new(bool) + **out = **in + } + if in.AllowedSourceRanges != nil { + in, out := &in.AllowedSourceRanges, &out.AllowedSourceRanges + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Users != nil { + in, out := &in.Users, &out.Users + *out = make(map[string]UserFlags, len(*in)) + for key, val := range *in { + var outVal []string + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = make(UserFlags, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } + } + if in.MaintenanceWindows != nil { + in, out := &in.MaintenanceWindows, &out.MaintenanceWindows + *out = make([]MaintenanceWindow, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + out.Clone = in.Clone + if in.Databases != nil { + in, out := &in.Databases, &out.Databases + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]corev1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Sidecars != nil { + in, out := &in.Sidecars, &out.Sidecars + *out = make([]Sidecar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresSpec. +func (in *PostgresSpec) DeepCopy() *PostgresSpec { + if in == nil { + return nil + } + out := new(PostgresSpec) + 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 + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresUsersConfiguration. +func (in *PostgresUsersConfiguration) DeepCopy() *PostgresUsersConfiguration { + if in == nil { + return nil + } + out := new(PostgresUsersConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Postgresql) DeepCopyInto(out *Postgresql) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Postgresql. +func (in *Postgresql) DeepCopy() *Postgresql { + if in == nil { + return nil + } + out := new(Postgresql) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Postgresql) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgresqlList) DeepCopyInto(out *PostgresqlList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Postgresql, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresqlList. +func (in *PostgresqlList) DeepCopy() *PostgresqlList { + if in == nil { + return nil + } + out := new(PostgresqlList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PostgresqlList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgresqlParam) DeepCopyInto(out *PostgresqlParam) { + *out = *in + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresqlParam. +func (in *PostgresqlParam) DeepCopy() *PostgresqlParam { + if in == nil { + return nil + } + out := new(PostgresqlParam) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourceDescription) DeepCopyInto(out *ResourceDescription) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceDescription. +func (in *ResourceDescription) DeepCopy() *ResourceDescription { + if in == nil { + return nil + } + out := new(ResourceDescription) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + out.ResourceRequest = in.ResourceRequest + out.ResourceLimits = in.ResourceLimits + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScalyrConfiguration) DeepCopyInto(out *ScalyrConfiguration) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScalyrConfiguration. +func (in *ScalyrConfiguration) DeepCopy() *ScalyrConfiguration { + if in == nil { + return nil + } + out := new(ScalyrConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Sidecar) DeepCopyInto(out *Sidecar) { + *out = *in + out.Resources = in.Resources + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]corev1.ContainerPort, len(*in)) + copy(*out, *in) + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]corev1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sidecar. +func (in *Sidecar) DeepCopy() *Sidecar { + if in == nil { + return nil + } + out := new(Sidecar) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TeamsAPIConfiguration) DeepCopyInto(out *TeamsAPIConfiguration) { + *out = *in + if in.TeamAPIRoleConfiguration != nil { + in, out := &in.TeamAPIRoleConfiguration, &out.TeamAPIRoleConfiguration + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.ProtectedRoles != nil { + in, out := &in.ProtectedRoles, &out.ProtectedRoles + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TeamsAPIConfiguration. +func (in *TeamsAPIConfiguration) DeepCopy() *TeamsAPIConfiguration { + if in == nil { + return nil + } + out := new(TeamsAPIConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in UserFlags) DeepCopyInto(out *UserFlags) { + { + in := &in + *out = make(UserFlags, len(*in)) + copy(*out, *in) + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserFlags. +func (in UserFlags) DeepCopy() UserFlags { + if in == nil { + return nil + } + out := new(UserFlags) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Volume) DeepCopyInto(out *Volume) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Volume. +func (in *Volume) DeepCopy() *Volume { + if in == nil { + return nil + } + out := new(Volume) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index ac26a9114..54c870287 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -13,6 +13,7 @@ import ( "github.com/Sirupsen/logrus" + "github.com/zalando-incubator/postgres-operator/pkg/cluster" "github.com/zalando-incubator/postgres-operator/pkg/spec" "github.com/zalando-incubator/postgres-operator/pkg/util" "github.com/zalando-incubator/postgres-operator/pkg/util/config" @@ -30,14 +31,14 @@ type controllerInformer interface { GetOperatorConfig() *config.Config GetStatus() *spec.ControllerStatus TeamClusterList() map[string][]spec.NamespacedName - ClusterStatus(team, namespace, cluster string) (*spec.ClusterStatus, error) + ClusterStatus(team, namespace, cluster string) (*cluster.ClusterStatus, error) ClusterLogs(team, namespace, cluster string) ([]*spec.LogEntry, error) ClusterHistory(team, namespace, cluster string) ([]*spec.Diff, error) ClusterDatabasesMap() map[string][]string WorkerLogs(workerID uint32) ([]*spec.LogEntry, error) ListQueue(workerID uint32) (*spec.QueueDump, error) GetWorkersCnt() uint32 - WorkerStatus(workerID uint32) (*spec.WorkerStatus, error) + WorkerStatus(workerID uint32) (*cluster.WorkerStatus, error) } // Server describes HTTP API server @@ -228,7 +229,7 @@ func (s *Server) workers(w http.ResponseWriter, req *http.Request) { resp, err = s.controller.ListQueue(workerID) } else if matches := util.FindNamedStringSubmatch(workerStatusURL, req.URL.Path); matches != nil { - var workerStatus *spec.WorkerStatus + var workerStatus *cluster.WorkerStatus workerID := mustConvertToUint32(matches["id"]) resp = "idle" diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 49bab8599..da266041e 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -4,7 +4,6 @@ package cluster import ( "database/sql" - "encoding/json" "fmt" "reflect" "regexp" @@ -20,6 +19,8 @@ import ( "k8s.io/client-go/rest" "k8s.io/client-go/tools/cache" + "encoding/json" + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" "github.com/zalando-incubator/postgres-operator/pkg/spec" "github.com/zalando-incubator/postgres-operator/pkg/util" "github.com/zalando-incubator/postgres-operator/pkg/util/config" @@ -60,13 +61,13 @@ type kubeResources struct { // Cluster describes postgresql cluster type Cluster struct { kubeResources - spec.Postgresql + acidv1.Postgresql Config logger *logrus.Entry patroni patroni.Interface pgUsers map[string]spec.PgUser systemUsers map[string]spec.PgUser - podSubscribers map[spec.NamespacedName]chan spec.PodEvent + podSubscribers map[spec.NamespacedName]chan PodEvent podSubscribersMu sync.RWMutex pgDb *sql.DB mu sync.Mutex @@ -77,7 +78,7 @@ type Cluster struct { teamsAPIClient teams.Interface oauthTokenGetter OAuthTokenGetter KubeClient k8sutil.KubernetesClient //TODO: move clients to the better place? - currentProcess spec.Process + currentProcess Process processMu sync.RWMutex // protects the current operation for reporting, no need to hold the master mutex specMu sync.RWMutex // protects the spec for reporting, no need to hold the master mutex } @@ -90,11 +91,11 @@ type compareStatefulsetResult struct { } // New creates a new cluster. This function should be called from a controller. -func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec spec.Postgresql, logger *logrus.Entry) *Cluster { +func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec acidv1.Postgresql, logger *logrus.Entry) *Cluster { deletePropagationPolicy := metav1.DeletePropagationOrphan podEventsQueue := cache.NewFIFO(func(obj interface{}) (string, error) { - e, ok := obj.(spec.PodEvent) + e, ok := obj.(PodEvent) if !ok { return "", fmt.Errorf("could not cast to PodEvent") } @@ -107,7 +108,7 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec spec.Postgresql Postgresql: pgSpec, pgUsers: make(map[string]spec.PgUser), systemUsers: make(map[string]spec.PgUser), - podSubscribers: make(map[spec.NamespacedName]chan spec.PodEvent), + podSubscribers: make(map[spec.NamespacedName]chan PodEvent), kubeResources: kubeResources{ Secrets: make(map[types.UID]*v1.Secret), Services: make(map[PostgresRole]*v1.Service), @@ -141,39 +142,36 @@ func (c *Cluster) teamName() string { func (c *Cluster) setProcessName(procName string, args ...interface{}) { c.processMu.Lock() defer c.processMu.Unlock() - c.currentProcess = spec.Process{ + c.currentProcess = Process{ Name: fmt.Sprintf(procName, args...), StartTime: time.Now(), } } -func (c *Cluster) setStatus(status spec.PostgresStatus) { - c.Status = status - b, err := json.Marshal(status) +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 { + 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.OpConfig.WatchedNamespace).Patch(c.Name, types.MergePatchType, patch) if err != nil { - c.logger.Fatalf("could not marshal status: %v", err) - } - request := []byte(fmt.Sprintf(`{"status": %s}`, string(b))) //TODO: Look into/wait for k8s go client methods - - _, err = c.KubeClient.CRDREST.Patch(types.MergePatchType). - Namespace(c.Namespace). - Resource(constants.PostgresCRDResource). - Name(c.Name). - Body(request). - DoRaw() - - if k8sutil.ResourceNotFound(err) { - c.logger.Warningf("could not set %q status for the non-existing cluster", status) - return - } - - if err != nil { - c.logger.Warningf("could not set %q status for the cluster: %v", status, err) + c.logger.Errorf("could not update status: %v", err) } + // update the spec, maintaining the new resourceVersion. + c.setSpec(newspec) } func (c *Cluster) isNewCluster() bool { - return c.Status == spec.ClusterStatusCreating + return c.Status == acidv1.ClusterStatusCreating } // initUsers populates c.systemUsers and c.pgUsers maps. @@ -215,13 +213,13 @@ func (c *Cluster) Create() error { defer func() { if err == nil { - c.setStatus(spec.ClusterStatusRunning) //TODO: are you sure it's running? + c.setStatus(acidv1.ClusterStatusRunning) //TODO: are you sure it's running? } else { - c.setStatus(spec.ClusterStatusAddFailed) + c.setStatus(acidv1.ClusterStatusAddFailed) } }() - c.setStatus(spec.ClusterStatusCreating) + c.setStatus(acidv1.ClusterStatusCreating) for _, role := range []PostgresRole{Master, Replica} { @@ -482,20 +480,20 @@ func compareResoucesAssumeFirstNotNil(a *v1.ResourceRequirements, b *v1.Resource // Update changes Kubernetes objects according to the new specification. Unlike the sync case, the missing object. // (i.e. service) is treated as an error. -func (c *Cluster) Update(oldSpec, newSpec *spec.Postgresql) error { +func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { updateFailed := false c.mu.Lock() defer c.mu.Unlock() - c.setStatus(spec.ClusterStatusUpdating) + c.setStatus(acidv1.ClusterStatusUpdating) c.setSpec(newSpec) defer func() { if updateFailed { - c.setStatus(spec.ClusterStatusUpdateFailed) - } else if c.Status != spec.ClusterStatusRunning { - c.setStatus(spec.ClusterStatusRunning) + c.setStatus(acidv1.ClusterStatusUpdateFailed) + } else if c.Status != acidv1.ClusterStatusRunning { + c.setStatus(acidv1.ClusterStatusRunning) } }() @@ -631,7 +629,7 @@ func (c *Cluster) Delete() { } //NeedsRepair returns true if the cluster should be included in the repair scan (based on its in-memory status). -func (c *Cluster) NeedsRepair() (bool, spec.PostgresStatus) { +func (c *Cluster) NeedsRepair() (bool, acidv1.PostgresStatus) { c.specMu.RLock() defer c.specMu.RUnlock() return !c.Status.Success(), c.Status @@ -639,20 +637,20 @@ func (c *Cluster) NeedsRepair() (bool, spec.PostgresStatus) { } // ReceivePodEvent is called back by the controller in order to add the cluster's pod event to the queue. -func (c *Cluster) ReceivePodEvent(event spec.PodEvent) { +func (c *Cluster) ReceivePodEvent(event PodEvent) { if err := c.podEventsQueue.Add(event); err != nil { c.logger.Errorf("error when receiving pod events: %v", err) } } func (c *Cluster) processPodEvent(obj interface{}) error { - event, ok := obj.(spec.PodEvent) + event, ok := obj.(PodEvent) if !ok { return fmt.Errorf("could not cast to PodEvent") } c.podSubscribersMu.RLock() - subscriber, ok := c.podSubscribers[event.PodName] + subscriber, ok := c.podSubscribers[spec.NamespacedName(event.PodName)] c.podSubscribersMu.RUnlock() if ok { subscriber <- event @@ -812,7 +810,7 @@ func (c *Cluster) shouldAvoidProtectedOrSystemRole(username, purpose string) boo } // GetCurrentProcess provides name of the last process of the cluster -func (c *Cluster) GetCurrentProcess() spec.Process { +func (c *Cluster) GetCurrentProcess() Process { c.processMu.RLock() defer c.processMu.RUnlock() @@ -820,8 +818,8 @@ func (c *Cluster) GetCurrentProcess() spec.Process { } // GetStatus provides status of the cluster -func (c *Cluster) GetStatus() *spec.ClusterStatus { - return &spec.ClusterStatus{ +func (c *Cluster) GetStatus() *ClusterStatus { + return &ClusterStatus{ Cluster: c.Spec.ClusterName, Team: c.Spec.TeamID, Status: c.Status, @@ -835,7 +833,7 @@ func (c *Cluster) GetStatus() *spec.ClusterStatus { PodDisruptionBudget: c.GetPodDisruptionBudget(), CurrentProcess: c.GetCurrentProcess(), - Error: c.Error, + Error: fmt.Errorf("error: %s", c.Error), } } diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index 7786e4563..82400344f 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -3,6 +3,7 @@ package cluster import ( "fmt" "github.com/Sirupsen/logrus" + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" "github.com/zalando-incubator/postgres-operator/pkg/spec" "github.com/zalando-incubator/postgres-operator/pkg/util/config" "github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil" @@ -21,43 +22,43 @@ 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{}, spec.Postgresql{}, logger) + k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger) func TestInitRobotUsers(t *testing.T) { testName := "TestInitRobotUsers" tests := []struct { - manifestUsers map[string]spec.UserFlags + manifestUsers map[string]acidv1.UserFlags infraRoles map[string]spec.PgUser result map[string]spec.PgUser err error }{ { - manifestUsers: map[string]spec.UserFlags{"foo": {"superuser", "createdb"}}, + manifestUsers: map[string]acidv1.UserFlags{"foo": {"superuser", "createdb"}}, infraRoles: map[string]spec.PgUser{"foo": {Origin: spec.RoleOriginInfrastructure, Name: "foo", Password: "bar"}}, result: map[string]spec.PgUser{"foo": {Origin: spec.RoleOriginInfrastructure, Name: "foo", Password: "bar"}}, err: nil, }, { - manifestUsers: map[string]spec.UserFlags{"!fooBar": {"superuser", "createdb"}}, + manifestUsers: map[string]acidv1.UserFlags{"!fooBar": {"superuser", "createdb"}}, err: fmt.Errorf(`invalid username: "!fooBar"`), }, { - manifestUsers: map[string]spec.UserFlags{"foobar": {"!superuser", "createdb"}}, + manifestUsers: map[string]acidv1.UserFlags{"foobar": {"!superuser", "createdb"}}, err: fmt.Errorf(`invalid flags for user "foobar": ` + `user flag "!superuser" is not alphanumeric`), }, { - manifestUsers: map[string]spec.UserFlags{"foobar": {"superuser1", "createdb"}}, + manifestUsers: map[string]acidv1.UserFlags{"foobar": {"superuser1", "createdb"}}, err: fmt.Errorf(`invalid flags for user "foobar": ` + `user flag "SUPERUSER1" is not valid`), }, { - manifestUsers: map[string]spec.UserFlags{"foobar": {"inherit", "noinherit"}}, + manifestUsers: map[string]acidv1.UserFlags{"foobar": {"inherit", "noinherit"}}, err: fmt.Errorf(`invalid flags for user "foobar": ` + `conflicting user flags: "NOINHERIT" and "INHERIT"`), }, { - manifestUsers: map[string]spec.UserFlags{"admin": {"superuser"}, superUserName: {"createdb"}}, + manifestUsers: map[string]acidv1.UserFlags{"admin": {"superuser"}, superUserName: {"createdb"}}, infraRoles: map[string]spec.PgUser{}, result: map[string]spec.PgUser{}, err: nil, diff --git a/pkg/cluster/pg.go b/pkg/cluster/database.go similarity index 100% rename from pkg/cluster/pg.go rename to pkg/cluster/database.go diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index b620082b5..c2049ff29 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -15,6 +15,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" "github.com/zalando-incubator/postgres-operator/pkg/spec" "github.com/zalando-incubator/postgres-operator/pkg/util" "github.com/zalando-incubator/postgres-operator/pkg/util/constants" @@ -83,17 +84,17 @@ func (c *Cluster) podDisruptionBudgetName() string { return c.OpConfig.PDBNameFormat.Format("cluster", c.Name) } -func (c *Cluster) makeDefaultResources() spec.Resources { +func (c *Cluster) makeDefaultResources() acidv1.Resources { config := c.OpConfig - defaultRequests := spec.ResourceDescription{CPU: config.DefaultCPURequest, Memory: config.DefaultMemoryRequest} - defaultLimits := spec.ResourceDescription{CPU: config.DefaultCPULimit, Memory: config.DefaultMemoryLimit} + defaultRequests := acidv1.ResourceDescription{CPU: config.DefaultCPURequest, Memory: config.DefaultMemoryRequest} + defaultLimits := acidv1.ResourceDescription{CPU: config.DefaultCPULimit, Memory: config.DefaultMemoryLimit} - return spec.Resources{ResourceRequest: defaultRequests, ResourceLimits: defaultLimits} + return acidv1.Resources{ResourceRequest: defaultRequests, ResourceLimits: defaultLimits} } -func generateResourceRequirements(resources spec.Resources, defaultResources spec.Resources) (*v1.ResourceRequirements, error) { +func generateResourceRequirements(resources acidv1.Resources, defaultResources acidv1.Resources) (*v1.ResourceRequirements, error) { var err error specRequests := resources.ResourceRequest @@ -114,7 +115,7 @@ func generateResourceRequirements(resources spec.Resources, defaultResources spe return &result, nil } -func fillResourceList(spec spec.ResourceDescription, defaults spec.ResourceDescription) (v1.ResourceList, error) { +func fillResourceList(spec acidv1.ResourceDescription, defaults acidv1.ResourceDescription) (v1.ResourceList, error) { var err error requests := v1.ResourceList{} @@ -144,7 +145,7 @@ func fillResourceList(spec spec.ResourceDescription, defaults spec.ResourceDescr return requests, nil } -func generateSpiloJSONConfiguration(pg *spec.PostgresqlParam, patroni *spec.Patroni, pamRoleName string, logger *logrus.Entry) string { +func generateSpiloJSONConfiguration(pg *acidv1.PostgresqlParam, patroni *acidv1.Patroni, pamRoleName string, logger *logrus.Entry) string { config := spiloConfiguration{} config.Bootstrap = pgBootstrap{} @@ -362,8 +363,8 @@ func generateSpiloContainer( } } -func generateSidecarContainers(sidecars []spec.Sidecar, - volumeMounts []v1.VolumeMount, defaultResources spec.Resources, +func generateSidecarContainers(sidecars []acidv1.Sidecar, + volumeMounts []v1.VolumeMount, defaultResources acidv1.Resources, superUserName string, credentialsSecretName string, logger *logrus.Entry) ([]v1.Container, error) { if len(sidecars) > 0 { @@ -438,7 +439,7 @@ func generatePodTemplate( } // generatePodEnvVars generates environment variables for the Spilo Pod -func (c *Cluster) generateSpiloPodEnvVars(uid types.UID, spiloConfiguration string, cloneDescription *spec.CloneDescription, customPodEnvVarsList []v1.EnvVar) []v1.EnvVar { +func (c *Cluster) generateSpiloPodEnvVars(uid types.UID, spiloConfiguration string, cloneDescription *acidv1.CloneDescription, customPodEnvVarsList []v1.EnvVar) []v1.EnvVar { envVars := []v1.EnvVar{ { Name: "SCOPE", @@ -555,7 +556,7 @@ func deduplicateEnvVars(input []v1.EnvVar, containerName string, logger *logrus. return result } -func getSidecarContainer(sidecar spec.Sidecar, index int, volumeMounts []v1.VolumeMount, +func getSidecarContainer(sidecar acidv1.Sidecar, index int, volumeMounts []v1.VolumeMount, resources *v1.ResourceRequirements, superUserName string, credentialsSecretName string, logger *logrus.Entry) *v1.Container { name := sidecar.Name if name == "" { @@ -618,20 +619,20 @@ func getBucketScopeSuffix(uid string) string { return "" } -func makeResources(cpuRequest, memoryRequest, cpuLimit, memoryLimit string) spec.Resources { - return spec.Resources{ - ResourceRequest: spec.ResourceDescription{ +func makeResources(cpuRequest, memoryRequest, cpuLimit, memoryLimit string) acidv1.Resources { + return acidv1.Resources{ + ResourceRequest: acidv1.ResourceDescription{ CPU: cpuRequest, Memory: memoryRequest, }, - ResourceLimits: spec.ResourceDescription{ + ResourceLimits: acidv1.ResourceDescription{ CPU: cpuLimit, Memory: memoryLimit, }, } } -func (c *Cluster) generateStatefulSet(spec *spec.PostgresSpec) (*v1beta1.StatefulSet, error) { +func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*v1beta1.StatefulSet, error) { var ( err error @@ -751,14 +752,14 @@ func (c *Cluster) generateStatefulSet(spec *spec.PostgresSpec) (*v1beta1.Statefu } func generateScalyrSidecarSpec(clusterName, APIKey, serverURL, dockerImage string, - containerResources *spec.Resources, logger *logrus.Entry) *spec.Sidecar { + containerResources *acidv1.Resources, logger *logrus.Entry) *acidv1.Sidecar { if APIKey == "" || dockerImage == "" { if APIKey == "" && dockerImage != "" { logger.Warning("Not running Scalyr sidecar: SCALYR_API_KEY must be defined") } return nil } - scalarSpec := &spec.Sidecar{ + scalarSpec := &acidv1.Sidecar{ Name: "scalyr-sidecar", DockerImage: dockerImage, Env: []v1.EnvVar{ @@ -780,9 +781,9 @@ func generateScalyrSidecarSpec(clusterName, APIKey, serverURL, dockerImage strin } // mergeSidecar merges globally-defined sidecars with those defined in the cluster manifest -func (c *Cluster) mergeSidecars(sidecars []spec.Sidecar) []spec.Sidecar { +func (c *Cluster) mergeSidecars(sidecars []acidv1.Sidecar) []acidv1.Sidecar { globalSidecarsToSkip := map[string]bool{} - result := make([]spec.Sidecar, 0) + result := make([]acidv1.Sidecar, 0) for i, sidecar := range sidecars { dockerImage, ok := c.OpConfig.Sidecars[sidecar.Name] @@ -798,13 +799,13 @@ func (c *Cluster) mergeSidecars(sidecars []spec.Sidecar) []spec.Sidecar { } for name, dockerImage := range c.OpConfig.Sidecars { if !globalSidecarsToSkip[name] { - result = append(result, spec.Sidecar{Name: name, DockerImage: dockerImage}) + result = append(result, acidv1.Sidecar{Name: name, DockerImage: dockerImage}) } } return result } -func (c *Cluster) getNumberOfInstances(spec *spec.PostgresSpec) int32 { +func (c *Cluster) getNumberOfInstances(spec *acidv1.PostgresSpec) int32 { min := c.OpConfig.MinInstances max := c.OpConfig.MaxInstances cur := spec.NumberOfInstances @@ -907,7 +908,7 @@ func (c *Cluster) generateSingleUserSecret(namespace string, pgUser spec.PgUser) return &secret } -func (c *Cluster) shouldCreateLoadBalancerForService(role PostgresRole, spec *spec.PostgresSpec) bool { +func (c *Cluster) shouldCreateLoadBalancerForService(role PostgresRole, spec *acidv1.PostgresSpec) bool { switch role { @@ -935,7 +936,7 @@ func (c *Cluster) shouldCreateLoadBalancerForService(role PostgresRole, spec *sp } -func (c *Cluster) generateService(role PostgresRole, spec *spec.PostgresSpec) *v1.Service { +func (c *Cluster) generateService(role PostgresRole, spec *acidv1.PostgresSpec) *v1.Service { var dnsName string if role == Master { @@ -1006,7 +1007,7 @@ func (c *Cluster) generateEndpoint(role PostgresRole, subsets []v1.EndpointSubse return endpoints } -func (c *Cluster) generateCloneEnvironment(description *spec.CloneDescription) []v1.EnvVar { +func (c *Cluster) generateCloneEnvironment(description *acidv1.CloneDescription) []v1.EnvVar { result := make([]v1.EnvVar, 0) if description.ClusterName == "" { diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 54890660c..12e145c04 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -1,7 +1,7 @@ package cluster import ( - "github.com/zalando-incubator/postgres-operator/pkg/spec" + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" "github.com/zalando-incubator/postgres-operator/pkg/util/config" "github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil" "testing" @@ -27,41 +27,41 @@ func TestCreateLoadBalancerLogic(t *testing.T) { ReplicationUsername: replicationUserName, }, }, - }, k8sutil.KubernetesClient{}, spec.Postgresql{}, logger) + }, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger) testName := "TestCreateLoadBalancerLogic" tests := []struct { subtest string role PostgresRole - spec *spec.PostgresSpec + spec *acidv1.PostgresSpec opConfig config.Config result bool }{ { subtest: "new format, load balancer is enabled for replica", role: Replica, - spec: &spec.PostgresSpec{EnableReplicaLoadBalancer: True()}, + spec: &acidv1.PostgresSpec{EnableReplicaLoadBalancer: True()}, opConfig: config.Config{}, result: true, }, { subtest: "new format, load balancer is disabled for replica", role: Replica, - spec: &spec.PostgresSpec{EnableReplicaLoadBalancer: False()}, + spec: &acidv1.PostgresSpec{EnableReplicaLoadBalancer: False()}, opConfig: config.Config{}, result: false, }, { subtest: "new format, load balancer isn't specified for replica", role: Replica, - spec: &spec.PostgresSpec{EnableReplicaLoadBalancer: nil}, + spec: &acidv1.PostgresSpec{EnableReplicaLoadBalancer: nil}, opConfig: config.Config{EnableReplicaLoadBalancer: true}, result: true, }, { subtest: "new format, load balancer isn't specified for replica", role: Replica, - spec: &spec.PostgresSpec{EnableReplicaLoadBalancer: nil}, + spec: &acidv1.PostgresSpec{EnableReplicaLoadBalancer: nil}, opConfig: config.Config{EnableReplicaLoadBalancer: false}, result: false, }, diff --git a/pkg/cluster/pod.go b/pkg/cluster/pod.go index beb433fa0..ab282b6b9 100644 --- a/pkg/cluster/pod.go +++ b/pkg/cluster/pod.go @@ -97,12 +97,12 @@ func (c *Cluster) unregisterPodSubscriber(podName spec.NamespacedName) { delete(c.podSubscribers, podName) } -func (c *Cluster) registerPodSubscriber(podName spec.NamespacedName) chan spec.PodEvent { +func (c *Cluster) registerPodSubscriber(podName spec.NamespacedName) chan PodEvent { c.logger.Debugf("subscribing to pod %q", podName) c.podSubscribersMu.Lock() defer c.podSubscribersMu.Unlock() - ch := make(chan spec.PodEvent) + ch := make(chan PodEvent) if _, ok := c.podSubscribers[podName]; ok { panic("pod '" + podName.String() + "' is already subscribed") } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index ad42eeac5..bbe158dd5 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -6,6 +6,7 @@ import ( policybeta1 "k8s.io/api/policy/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" "github.com/zalando-incubator/postgres-operator/pkg/spec" "github.com/zalando-incubator/postgres-operator/pkg/util" "github.com/zalando-incubator/postgres-operator/pkg/util/constants" @@ -15,7 +16,7 @@ import ( // Sync syncs the cluster, making sure the actual Kubernetes objects correspond to what is defined in the manifest. // Unlike the update, sync does not error out if some objects do not exist and takes care of creating them. -func (c *Cluster) Sync(newSpec *spec.Postgresql) error { +func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error { var err error c.mu.Lock() defer c.mu.Unlock() @@ -25,9 +26,9 @@ func (c *Cluster) Sync(newSpec *spec.Postgresql) error { defer func() { if err != nil { c.logger.Warningf("error while syncing cluster state: %v", err) - c.setStatus(spec.ClusterStatusSyncFailed) - } else if c.Status != spec.ClusterStatusRunning { - c.setStatus(spec.ClusterStatusRunning) + c.setStatus(acidv1.ClusterStatusSyncFailed) + } else if c.Status != acidv1.ClusterStatusRunning { + c.setStatus(acidv1.ClusterStatusRunning) } }() diff --git a/pkg/cluster/types.go b/pkg/cluster/types.go index fbaa39fb7..83b7e73fb 100644 --- a/pkg/cluster/types.go +++ b/pkg/cluster/types.go @@ -1,5 +1,14 @@ package cluster +import ( + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" + "k8s.io/api/apps/v1beta1" + "k8s.io/api/core/v1" + policybeta1 "k8s.io/api/policy/v1beta1" + "k8s.io/apimachinery/pkg/types" + "time" +) + // PostgresRole describes role of the node type PostgresRole string @@ -10,3 +19,51 @@ const ( // Replica role Replica PostgresRole = "replica" ) + +type PodEventType string + +// Possible values for the EventType +const ( + PodEventAdd PodEventType = "ADD" + PodEventUpdate PodEventType = "UPDATE" + PodEventDelete PodEventType = "DELETE" +) + +// PodEvent describes the event for a single Pod +type PodEvent struct { + ResourceVersion string + PodName types.NamespacedName + PrevPod *v1.Pod + CurPod *v1.Pod + EventType PodEventType +} + +// Process describes process of the cluster +type Process struct { + Name string + StartTime time.Time +} + +// WorkerStatus describes status of the worker +type WorkerStatus struct { + CurrentCluster types.NamespacedName + CurrentProcess Process +} + +// ClusterStatus describes status of the cluster +type ClusterStatus struct { + Team string + Cluster string + MasterService *v1.Service + ReplicaService *v1.Service + MasterEndpoint *v1.Endpoints + ReplicaEndpoint *v1.Endpoints + StatefulSet *v1beta1.StatefulSet + PodDisruptionBudget *policybeta1.PodDisruptionBudget + + CurrentProcess Process + Worker uint32 + Status acidv1.PostgresStatus + Spec acidv1.PostgresSpec + Error error +} diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index 85a61d969..911bf74f8 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -17,6 +17,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + acidzalando "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do" + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" "github.com/zalando-incubator/postgres-operator/pkg/spec" "github.com/zalando-incubator/postgres-operator/pkg/util" "github.com/zalando-incubator/postgres-operator/pkg/util/constants" @@ -203,7 +205,7 @@ func (c *Cluster) logServiceChanges(role PostgresRole, old, new *v1.Service, isU } } -func (c *Cluster) logVolumeChanges(old, new spec.Volume) { +func (c *Cluster) logVolumeChanges(old, new acidv1.Volume) { c.logger.Infof("volume specification has been changed") c.logger.Debugf("diff\n%s\n", util.PrettyDiff(old, new)) } @@ -232,7 +234,7 @@ func (c *Cluster) getTeamMembers() ([]string, error) { return teamInfo.Members, nil } -func (c *Cluster) waitForPodLabel(podEvents chan spec.PodEvent, stopChan chan struct{}, role *PostgresRole) (*v1.Pod, error) { +func (c *Cluster) waitForPodLabel(podEvents chan PodEvent, stopChan chan struct{}, role *PostgresRole) (*v1.Pod, error) { timeout := time.After(c.OpConfig.PodLabelWaitTimeout) for { select { @@ -254,12 +256,12 @@ func (c *Cluster) waitForPodLabel(podEvents chan spec.PodEvent, stopChan chan st } } -func (c *Cluster) waitForPodDeletion(podEvents chan spec.PodEvent) error { +func (c *Cluster) waitForPodDeletion(podEvents chan PodEvent) error { timeout := time.After(c.OpConfig.PodDeletionWaitTimeout) for { select { case podEvent := <-podEvents: - if podEvent.EventType == spec.EventDelete { + if podEvent.EventType == PodEventDelete { return nil } case <-timeout: @@ -425,18 +427,18 @@ func (c *Cluster) credentialSecretNameForCluster(username string, clusterName st return c.OpConfig.SecretNameTemplate.Format( "username", strings.Replace(username, "_", "-", -1), "cluster", clusterName, - "tprkind", constants.PostgresCRDKind, - "tprgroup", constants.CRDGroup) + "tprkind", acidv1.PostgresCRDResourceKind, + "tprgroup", acidzalando.GroupName) } func masterCandidate(replicas []spec.NamespacedName) spec.NamespacedName { return replicas[rand.Intn(len(replicas))] } -func cloneSpec(from *spec.Postgresql) (*spec.Postgresql, error) { +func cloneSpec(from *acidv1.Postgresql) (*acidv1.Postgresql, error) { var ( buf bytes.Buffer - result *spec.Postgresql + result *acidv1.Postgresql err error ) enc := gob.NewEncoder(&buf) @@ -450,13 +452,13 @@ func cloneSpec(from *spec.Postgresql) (*spec.Postgresql, error) { return result, nil } -func (c *Cluster) setSpec(newSpec *spec.Postgresql) { +func (c *Cluster) setSpec(newSpec *acidv1.Postgresql) { c.specMu.Lock() c.Postgresql = *newSpec c.specMu.Unlock() } -func (c *Cluster) GetSpec() (*spec.Postgresql, error) { +func (c *Cluster) GetSpec() (*acidv1.Postgresql, error) { c.specMu.RLock() defer c.specMu.RUnlock() return cloneSpec(&c.Postgresql) diff --git a/pkg/cluster/volumes.go b/pkg/cluster/volumes.go index c44ac4a03..9816116c2 100644 --- a/pkg/cluster/volumes.go +++ b/pkg/cluster/volumes.go @@ -9,6 +9,7 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" "github.com/zalando-incubator/postgres-operator/pkg/spec" "github.com/zalando-incubator/postgres-operator/pkg/util" "github.com/zalando-incubator/postgres-operator/pkg/util/constants" @@ -88,7 +89,7 @@ func (c *Cluster) listPersistentVolumes() ([]*v1.PersistentVolume, error) { } // resizeVolumes resize persistent volumes compatible with the given resizer interface -func (c *Cluster) resizeVolumes(newVolume spec.Volume, resizers []volumes.VolumeResizer) error { +func (c *Cluster) resizeVolumes(newVolume acidv1.Volume, resizers []volumes.VolumeResizer) error { c.setProcessName("resizing volumes") var totalIncompatible int @@ -158,7 +159,7 @@ func (c *Cluster) resizeVolumes(newVolume spec.Volume, resizers []volumes.Volume return nil } -func (c *Cluster) volumesNeedResizing(newVolume spec.Volume) (bool, error) { +func (c *Cluster) volumesNeedResizing(newVolume acidv1.Volume) (bool, error) { vols, manifestSize, err := c.listVolumesWithManifestSize(newVolume) if err != nil { return false, err @@ -172,7 +173,7 @@ func (c *Cluster) volumesNeedResizing(newVolume spec.Volume) (bool, error) { return false, nil } -func (c *Cluster) listVolumesWithManifestSize(newVolume spec.Volume) ([]*v1.PersistentVolume, int64, error) { +func (c *Cluster) listVolumesWithManifestSize(newVolume acidv1.Volume) ([]*v1.PersistentVolume, int64, error) { newSize, err := resource.ParseQuantity(newVolume.Size) if err != nil { return nil, 0, fmt.Errorf("could not parse volume size from the manifest: %v", err) diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 7e340abb3..f99d836b8 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -21,6 +21,8 @@ import ( "github.com/zalando-incubator/postgres-operator/pkg/util/constants" "github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil" "github.com/zalando-incubator/postgres-operator/pkg/util/ringlog" + + acidv1informer "github.com/zalando-incubator/postgres-operator/pkg/generated/informers/externalversions/acid.zalan.do/v1" ) // Controller represents operator controller @@ -46,7 +48,7 @@ type Controller struct { postgresqlInformer cache.SharedIndexInformer podInformer cache.SharedIndexInformer nodesInformer cache.SharedIndexInformer - podCh chan spec.PodEvent + podCh chan cluster.PodEvent clusterEventQueues []*cache.FIFO // [workerID]Queue lastClusterSyncTime int64 @@ -74,7 +76,7 @@ func NewController(controllerConfig *spec.ControllerConfig) *Controller { clusterHistory: make(map[spec.NamespacedName]ringlog.RingLogger), teamClusters: make(map[string][]spec.NamespacedName), stopCh: make(chan struct{}), - podCh: make(chan spec.PodEvent), + podCh: make(chan cluster.PodEvent), } logger.Hooks.Add(c) @@ -227,9 +229,9 @@ func (c *Controller) initController() { } } else { c.initOperatorConfig() - c.initPodServiceAccount() - c.initRoleBinding() } + c.initPodServiceAccount() + c.initRoleBinding() c.modifyConfigFromEnvironment() @@ -256,7 +258,7 @@ func (c *Controller) initController() { c.workerLogs = make(map[uint32]ringlog.RingLogger, c.opConfig.Workers) for i := range c.clusterEventQueues { c.clusterEventQueues[i] = cache.NewFIFO(func(obj interface{}) (string, error) { - e, ok := obj.(spec.ClusterEvent) + e, ok := obj.(ClusterEvent) if !ok { return "", fmt.Errorf("could not cast to ClusterEvent") } @@ -269,13 +271,10 @@ func (c *Controller) initController() { } func (c *Controller) initSharedInformers() { - // Postgresqls - c.postgresqlInformer = cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: c.clusterListFunc, - WatchFunc: c.clusterWatchFunc, - }, - &spec.Postgresql{}, + + c.postgresqlInformer = acidv1informer.NewPostgresqlInformer( + c.KubeClient.AcidV1ClientSet, + c.opConfig.WatchedNamespace, constants.QueueResyncPeriodTPR, cache.Indexers{}) @@ -345,7 +344,6 @@ func (c *Controller) Run(stopCh <-chan struct{}, wg *sync.WaitGroup) { go c.apiserver.Run(stopCh, wg) go c.kubeNodesInformer(stopCh, wg) - c.logger.Info("started working in background") } @@ -361,7 +359,7 @@ func (c *Controller) runPostgresqlInformer(stopCh <-chan struct{}, wg *sync.Wait c.postgresqlInformer.Run(stopCh) } -func queueClusterKey(eventType spec.EventType, uid types.UID) string { +func queueClusterKey(eventType EventType, uid types.UID) string { return fmt.Sprintf("%s-%s", eventType, uid) } diff --git a/pkg/controller/status.go b/pkg/controller/logs_and_api.go similarity index 95% rename from pkg/controller/status.go rename to pkg/controller/logs_and_api.go index 73f25f697..7a6619203 100644 --- a/pkg/controller/status.go +++ b/pkg/controller/logs_and_api.go @@ -11,10 +11,11 @@ import ( "github.com/zalando-incubator/postgres-operator/pkg/spec" "github.com/zalando-incubator/postgres-operator/pkg/util" "github.com/zalando-incubator/postgres-operator/pkg/util/config" + "k8s.io/apimachinery/pkg/types" ) // ClusterStatus provides status of the cluster -func (c *Controller) ClusterStatus(team, namespace, cluster string) (*spec.ClusterStatus, error) { +func (c *Controller) ClusterStatus(team, namespace, cluster string) (*cluster.ClusterStatus, error) { clusterName := spec.NamespacedName{ Namespace: namespace, @@ -196,7 +197,7 @@ func (c *Controller) GetWorkersCnt() uint32 { } //WorkerStatus provides status of the worker -func (c *Controller) WorkerStatus(workerID uint32) (*spec.WorkerStatus, error) { +func (c *Controller) WorkerStatus(workerID uint32) (*cluster.WorkerStatus, error) { obj, ok := c.curWorkerCluster.Load(workerID) if !ok || obj == nil { return nil, nil @@ -207,8 +208,8 @@ func (c *Controller) WorkerStatus(workerID uint32) (*spec.WorkerStatus, error) { return nil, fmt.Errorf("could not cast to Cluster struct") } - return &spec.WorkerStatus{ - CurrentCluster: util.NameFromMeta(cl.ObjectMeta), + return &cluster.WorkerStatus{ + CurrentCluster: types.NamespacedName(util.NameFromMeta(cl.ObjectMeta)), CurrentProcess: cl.GetCurrentProcess(), }, nil } diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index bdac26a19..251828b32 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -1,40 +1,27 @@ package controller import ( - "encoding/json" "fmt" "time" + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" "github.com/zalando-incubator/postgres-operator/pkg/util/config" - "github.com/zalando-incubator/postgres-operator/pkg/util/constants" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func (c *Controller) readOperatorConfigurationFromCRD(configObjectNamespace, configObjectName string) (*config.OperatorConfiguration, error) { - var ( - opConfig config.OperatorConfiguration - ) +func (c *Controller) readOperatorConfigurationFromCRD(configObjectNamespace, configObjectName string) (*acidv1.OperatorConfiguration, error) { - req := c.KubeClient.CRDREST.Get(). - Name(configObjectName). - Namespace(configObjectNamespace). - Resource(constants.OperatorConfigCRDResource). - VersionedParams(&metav1.ListOptions{ResourceVersion: "0"}, metav1.ParameterCodec) - - data, err := req.DoRaw() + config, err := c.KubeClient.AcidV1ClientSet.AcidV1().OperatorConfigurations(configObjectNamespace).Get(configObjectName, metav1.GetOptions{}) if err != nil { - return nil, fmt.Errorf("could not get operator configuration object %s: %v", configObjectName, err) - } - if err = json.Unmarshal(data, &opConfig); err != nil { - return nil, fmt.Errorf("could not unmarshal operator configuration object %s, %v", configObjectName, err) + return nil, fmt.Errorf("could not get operator configuration object %q: %v", configObjectName, err) } - return &opConfig, nil + return config, nil } // importConfigurationFromCRD is a transitional function that converts CRD configuration to the one based on the configmap -func (c *Controller) importConfigurationFromCRD(fromCRD *config.OperatorConfigurationData) *config.Config { +func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigurationData) *config.Config { result := &config.Config{} result.EtcdHost = fromCRD.EtcdHost diff --git a/pkg/controller/pod.go b/pkg/controller/pod.go index d3634ff27..d2e12eb30 100644 --- a/pkg/controller/pod.go +++ b/pkg/controller/pod.go @@ -6,8 +6,10 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" + "github.com/zalando-incubator/postgres-operator/pkg/cluster" "github.com/zalando-incubator/postgres-operator/pkg/spec" "github.com/zalando-incubator/postgres-operator/pkg/util" + "k8s.io/apimachinery/pkg/types" ) func (c *Controller) podListFunc(options metav1.ListOptions) (runtime.Object, error) { @@ -30,7 +32,7 @@ func (c *Controller) podWatchFunc(options metav1.ListOptions) (watch.Interface, return c.KubeClient.Pods(c.opConfig.WatchedNamespace).Watch(opts) } -func (c *Controller) dispatchPodEvent(clusterName spec.NamespacedName, event spec.PodEvent) { +func (c *Controller) dispatchPodEvent(clusterName spec.NamespacedName, event cluster.PodEvent) { c.clustersMu.RLock() cluster, ok := c.clusters[clusterName] c.clustersMu.RUnlock() @@ -41,7 +43,7 @@ func (c *Controller) dispatchPodEvent(clusterName spec.NamespacedName, event spe func (c *Controller) podAdd(obj interface{}) { if pod, ok := obj.(*v1.Pod); ok { - c.preparePodEventForDispatch(pod, nil, spec.EventAdd) + c.preparePodEventForDispatch(pod, nil, cluster.PodEventAdd) } } @@ -56,19 +58,19 @@ func (c *Controller) podUpdate(prev, cur interface{}) { return } - c.preparePodEventForDispatch(curPod, prevPod, spec.EventUpdate) + c.preparePodEventForDispatch(curPod, prevPod, cluster.PodEventUpdate) } func (c *Controller) podDelete(obj interface{}) { if pod, ok := obj.(*v1.Pod); ok { - c.preparePodEventForDispatch(pod, nil, spec.EventDelete) + c.preparePodEventForDispatch(pod, nil, cluster.PodEventDelete) } } -func (c *Controller) preparePodEventForDispatch(curPod, prevPod *v1.Pod, event spec.EventType) { - podEvent := spec.PodEvent{ - PodName: util.NameFromMeta(curPod.ObjectMeta), +func (c *Controller) preparePodEventForDispatch(curPod, prevPod *v1.Pod, event cluster.PodEventType) { + podEvent := cluster.PodEvent{ + PodName: types.NamespacedName(util.NameFromMeta(curPod.ObjectMeta)), CurPod: curPod, PrevPod: prevPod, EventType: event, diff --git a/pkg/controller/postgresql.go b/pkg/controller/postgresql.go index 4e5df42a7..379cec842 100644 --- a/pkg/controller/postgresql.go +++ b/pkg/controller/postgresql.go @@ -1,7 +1,6 @@ package controller import ( - "encoding/json" "fmt" "reflect" "strings" @@ -12,15 +11,13 @@ import ( "github.com/Sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/tools/cache" + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" "github.com/zalando-incubator/postgres-operator/pkg/cluster" "github.com/zalando-incubator/postgres-operator/pkg/spec" "github.com/zalando-incubator/postgres-operator/pkg/util" - "github.com/zalando-incubator/postgres-operator/pkg/util/constants" "github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil" "github.com/zalando-incubator/postgres-operator/pkg/util/ringlog" ) @@ -42,40 +39,20 @@ func (c *Controller) clusterResync(stopCh <-chan struct{}, wg *sync.WaitGroup) { } // clusterListFunc obtains a list of all PostgreSQL clusters -func (c *Controller) listClusters(options metav1.ListOptions) (*spec.PostgresqlList, error) { - var ( - list spec.PostgresqlList - ) - - req := c.KubeClient.CRDREST. - Get(). - Namespace(c.opConfig.WatchedNamespace). - Resource(constants.PostgresCRDResource). - VersionedParams(&options, metav1.ParameterCodec) - - b, err := req.DoRaw() +func (c *Controller) listClusters(options metav1.ListOptions) (*acidv1.PostgresqlList, error) { + // TODO: use the SharedInformer cache instead of quering Kubernetes API directly. + list, err := c.KubeClient.AcidV1ClientSet.AcidV1().Postgresqls(c.opConfig.WatchedNamespace).List(options) if err != nil { - c.logger.Errorf("could not get the list of postgresql CRD objects: %v", err) - return nil, err + c.logger.Errorf("could not list postgresql objects: %v", err) } - if err = json.Unmarshal(b, &list); err != nil { - c.logger.Warningf("could not unmarshal list of clusters: %v", err) - } - - return &list, err - -} - -// A separate function to be called from InitSharedInformers -func (c *Controller) clusterListFunc(options metav1.ListOptions) (runtime.Object, error) { - return c.listClusters(options) + return list, err } // clusterListAndSync lists all manifests and decides whether to run the sync or repair. func (c *Controller) clusterListAndSync() error { var ( err error - event spec.EventType + event EventType ) currentTime := time.Now().Unix() @@ -83,12 +60,12 @@ func (c *Controller) clusterListAndSync() error { timeFromPreviousRepair := currentTime - atomic.LoadInt64(&c.lastClusterRepairTime) if timeFromPreviousSync >= int64(c.opConfig.ResyncPeriod.Seconds()) { - event = spec.EventSync + event = EventSync } else if timeFromPreviousRepair >= int64(c.opConfig.RepairPeriod.Seconds()) { - event = spec.EventRepair + event = EventRepair } if event != "" { - var list *spec.PostgresqlList + var list *acidv1.PostgresqlList if list, err = c.listClusters(metav1.ListOptions{ResourceVersion: "0"}); err != nil { return err } @@ -101,16 +78,17 @@ func (c *Controller) clusterListAndSync() error { } // queueEvents queues a sync or repair event for every cluster with a valid manifest -func (c *Controller) queueEvents(list *spec.PostgresqlList, event spec.EventType) { +func (c *Controller) queueEvents(list *acidv1.PostgresqlList, event EventType) { var activeClustersCnt, failedClustersCnt, clustersToRepair int for i, pg := range list.Items { - if pg.Error != nil { + // XXX: check the cluster status field instead + if pg.Error != "" { failedClustersCnt++ continue } activeClustersCnt++ // check if that cluster needs repair - if event == spec.EventRepair { + if event == EventRepair { if pg.Status.Success() { continue } else { @@ -133,9 +111,9 @@ func (c *Controller) queueEvents(list *spec.PostgresqlList, event spec.EventType } else { c.logger.Infof("no clusters running") } - if event == spec.EventRepair || event == spec.EventSync { + if event == EventRepair || event == EventSync { atomic.StoreInt64(&c.lastClusterRepairTime, time.Now().Unix()) - if event == spec.EventSync { + if event == EventSync { atomic.StoreInt64(&c.lastClusterSyncTime, time.Now().Unix()) } } @@ -143,7 +121,7 @@ func (c *Controller) queueEvents(list *spec.PostgresqlList, event spec.EventType func (c *Controller) acquireInitialListOfClusters() error { var ( - list *spec.PostgresqlList + list *acidv1.PostgresqlList err error clusterName spec.NamespacedName ) @@ -153,7 +131,8 @@ func (c *Controller) acquireInitialListOfClusters() error { } c.logger.Debugf("acquiring initial list of clusters") for _, pg := range list.Items { - if pg.Error != nil { + // XXX: check the cluster status field instead + if pg.Error != "" { continue } clusterName = util.NameFromMeta(pg.ObjectMeta) @@ -161,54 +140,11 @@ func (c *Controller) acquireInitialListOfClusters() error { c.logger.Debugf("added new cluster: %q", clusterName) } // initiate initial sync of all clusters. - c.queueEvents(list, spec.EventSync) + c.queueEvents(list, EventSync) return nil } -type crdDecoder struct { - dec *json.Decoder - close func() error -} - -func (d *crdDecoder) Close() { - if err := d.close(); err != nil { - fmt.Printf("error when closing CRDDecorer: %v\n", err) - } -} - -func (d *crdDecoder) Decode() (action watch.EventType, object runtime.Object, err error) { - var e struct { - Type watch.EventType - Object spec.Postgresql - } - if err := d.dec.Decode(&e); err != nil { - return watch.Error, nil, err - } - - return e.Type, &e.Object, nil -} - -func (c *Controller) clusterWatchFunc(options metav1.ListOptions) (watch.Interface, error) { - options.Watch = true - // MIGRATION: FieldsSelectorParam(nil) - r, err := c.KubeClient.CRDREST. - Get(). - Namespace(c.opConfig.WatchedNamespace). - Resource(constants.PostgresCRDResource). - VersionedParams(&options, metav1.ParameterCodec). - Stream() - - if err != nil { - return nil, err - } - - return watch.NewStreamWatcher(&crdDecoder{ - dec: json.NewDecoder(r), - close: r.Close, - }), nil -} - -func (c *Controller) addCluster(lg *logrus.Entry, clusterName spec.NamespacedName, pgSpec *spec.Postgresql) *cluster.Cluster { +func (c *Controller) addCluster(lg *logrus.Entry, clusterName spec.NamespacedName, pgSpec *acidv1.Postgresql) *cluster.Cluster { cl := cluster.New(c.makeClusterConfig(), c.KubeClient, *pgSpec, lg) cl.Run(c.stopCh) teamName := strings.ToLower(cl.Spec.TeamID) @@ -224,13 +160,13 @@ func (c *Controller) addCluster(lg *logrus.Entry, clusterName spec.NamespacedNam return cl } -func (c *Controller) processEvent(event spec.ClusterEvent) { +func (c *Controller) processEvent(event ClusterEvent) { var clusterName spec.NamespacedName var clHistory ringlog.RingLogger lg := c.logger.WithField("worker", event.WorkerID) - if event.EventType == spec.EventAdd || event.EventType == spec.EventSync || event.EventType == spec.EventRepair { + if event.EventType == EventAdd || event.EventType == EventSync || event.EventType == EventRepair { clusterName = util.NameFromMeta(event.NewSpec.ObjectMeta) } else { clusterName = util.NameFromMeta(event.OldSpec.ObjectMeta) @@ -246,17 +182,17 @@ func (c *Controller) processEvent(event spec.ClusterEvent) { defer c.curWorkerCluster.Store(event.WorkerID, nil) - if event.EventType == spec.EventRepair { + if event.EventType == EventRepair { runRepair, lastOperationStatus := cl.NeedsRepair() if !runRepair { lg.Debugf("Observed cluster status %s, repair is not required", lastOperationStatus) return } lg.Debugf("Observed cluster status %s, running sync scan to repair the cluster", lastOperationStatus) - event.EventType = spec.EventSync + event.EventType = EventSync } - if event.EventType == spec.EventAdd || event.EventType == spec.EventUpdate || event.EventType == spec.EventSync { + if event.EventType == EventAdd || event.EventType == EventUpdate || event.EventType == EventSync { // handle deprecated parameters by possibly assigning their values to the new ones. if event.OldSpec != nil { c.mergeDeprecatedPostgreSQLSpecParameters(&event.OldSpec.Spec) @@ -273,7 +209,7 @@ func (c *Controller) processEvent(event spec.ClusterEvent) { } switch event.EventType { - case spec.EventAdd: + case EventAdd: if clusterFound { lg.Debugf("cluster already exists") return @@ -286,14 +222,14 @@ func (c *Controller) processEvent(event spec.ClusterEvent) { c.curWorkerCluster.Store(event.WorkerID, cl) if err := cl.Create(); err != nil { - cl.Error = fmt.Errorf("could not create cluster: %v", err) + cl.Error = fmt.Sprintf("could not create cluster: %v", err) lg.Error(cl.Error) return } lg.Infoln("cluster has been created") - case spec.EventUpdate: + case EventUpdate: lg.Infoln("update of the cluster started") if !clusterFound { @@ -302,12 +238,12 @@ func (c *Controller) processEvent(event spec.ClusterEvent) { } c.curWorkerCluster.Store(event.WorkerID, cl) if err := cl.Update(event.OldSpec, event.NewSpec); err != nil { - cl.Error = fmt.Errorf("could not update cluster: %v", err) + cl.Error = fmt.Sprintf("could not update cluster: %v", err) lg.Error(cl.Error) return } - cl.Error = nil + cl.Error = "" lg.Infoln("cluster has been updated") clHistory.Insert(&spec.Diff{ @@ -315,7 +251,7 @@ func (c *Controller) processEvent(event spec.ClusterEvent) { ProcessTime: time.Now(), Diff: util.Diff(event.OldSpec, event.NewSpec), }) - case spec.EventDelete: + case EventDelete: if !clusterFound { lg.Errorf("unknown cluster: %q", clusterName) return @@ -345,7 +281,7 @@ func (c *Controller) processEvent(event spec.ClusterEvent) { }() lg.Infof("cluster has been deleted") - case spec.EventSync: + case EventSync: lg.Infof("syncing of the cluster started") // no race condition because a cluster is always processed by single worker @@ -355,11 +291,11 @@ func (c *Controller) processEvent(event spec.ClusterEvent) { c.curWorkerCluster.Store(event.WorkerID, cl) if err := cl.Sync(event.NewSpec); err != nil { - cl.Error = fmt.Errorf("could not sync cluster: %v", err) + cl.Error = fmt.Sprintf("could not sync cluster: %v", err) lg.Error(cl.Error) return } - cl.Error = nil + cl.Error = "" lg.Infof("cluster has been synced") } @@ -382,7 +318,7 @@ func (c *Controller) processClusterEventsQueue(idx int, stopCh <-chan struct{}, c.logger.Errorf("error when processing cluster events queue: %v", err) continue } - event, ok := obj.(spec.ClusterEvent) + event, ok := obj.(ClusterEvent) if !ok { c.logger.Errorf("could not cast to ClusterEvent") } @@ -391,7 +327,7 @@ func (c *Controller) processClusterEventsQueue(idx int, stopCh <-chan struct{}, } } -func (c *Controller) warnOnDeprecatedPostgreSQLSpecParameters(spec *spec.PostgresSpec) { +func (c *Controller) warnOnDeprecatedPostgreSQLSpecParameters(spec *acidv1.PostgresSpec) { deprecate := func(deprecated, replacement string) { c.logger.Warningf("Parameter %q is deprecated. Consider setting %q instead", deprecated, replacement) @@ -421,7 +357,7 @@ func (c *Controller) warnOnDeprecatedPostgreSQLSpecParameters(spec *spec.Postgre // mergeDeprecatedPostgreSQLSpecParameters modifies the spec passed to the cluster by setting current parameter // values from the obsolete ones. Note: while the spec that is modified is a copy made in queueClusterEvent, it is // still a shallow copy, so be extra careful not to modify values pointer fields point to, but copy them instead. -func (c *Controller) mergeDeprecatedPostgreSQLSpecParameters(spec *spec.PostgresSpec) *spec.PostgresSpec { +func (c *Controller) mergeDeprecatedPostgreSQLSpecParameters(spec *acidv1.PostgresSpec) *acidv1.PostgresSpec { if (spec.UseLoadBalancer != nil || spec.ReplicaLoadBalancer != nil) && (spec.EnableReplicaLoadBalancer == nil && spec.EnableMasterLoadBalancer == nil) { if spec.UseLoadBalancer != nil { @@ -439,18 +375,18 @@ func (c *Controller) mergeDeprecatedPostgreSQLSpecParameters(spec *spec.Postgres return spec } -func (c *Controller) queueClusterEvent(informerOldSpec, informerNewSpec *spec.Postgresql, eventType spec.EventType) { +func (c *Controller) queueClusterEvent(informerOldSpec, informerNewSpec *acidv1.Postgresql, eventType EventType) { var ( uid types.UID clusterName spec.NamespacedName - clusterError error + clusterError string ) if informerOldSpec != nil { //update, delete uid = informerOldSpec.GetUID() clusterName = util.NameFromMeta(informerOldSpec.ObjectMeta) - if eventType == spec.EventUpdate && informerNewSpec.Error == nil && informerOldSpec.Error != nil { - eventType = spec.EventSync + if eventType == EventUpdate && informerNewSpec.Error == "" && informerOldSpec.Error != "" { + eventType = EventSync clusterError = informerNewSpec.Error } else { clusterError = informerOldSpec.Error @@ -461,10 +397,10 @@ func (c *Controller) queueClusterEvent(informerOldSpec, informerNewSpec *spec.Po clusterError = informerNewSpec.Error } - if clusterError != nil && eventType != spec.EventDelete { + if clusterError != "" && eventType != EventDelete { c.logger. WithField("cluster-name", clusterName). - Debugf("skipping %q event for the invalid cluster: %v", eventType, clusterError) + Debugf("skipping %q event for the invalid cluster: %s", eventType, clusterError) return } @@ -473,7 +409,7 @@ func (c *Controller) queueClusterEvent(informerOldSpec, informerNewSpec *spec.Po // effect, the modified state will be returned together with subsequent events). workerID := c.clusterWorkerID(clusterName) - clusterEvent := spec.ClusterEvent{ + clusterEvent := ClusterEvent{ EventTime: time.Now(), EventType: eventType, UID: uid, @@ -488,11 +424,11 @@ func (c *Controller) queueClusterEvent(informerOldSpec, informerNewSpec *spec.Po } lg.Infof("%q event has been queued", eventType) - if eventType != spec.EventDelete { + if eventType != EventDelete { return } // A delete event discards all prior requests for that cluster. - for _, evType := range []spec.EventType{spec.EventAdd, spec.EventSync, spec.EventUpdate, spec.EventRepair} { + for _, evType := range []EventType{EventAdd, EventSync, EventUpdate, EventRepair} { obj, exists, err := c.clusterEventQueues[workerID].GetByKey(queueClusterKey(evType, uid)) if err != nil { lg.Warningf("could not get event from the queue: %v", err) @@ -513,40 +449,41 @@ func (c *Controller) queueClusterEvent(informerOldSpec, informerNewSpec *spec.Po } func (c *Controller) postgresqlAdd(obj interface{}) { - pg, ok := obj.(*spec.Postgresql) + pg, ok := obj.(*acidv1.Postgresql) if !ok { c.logger.Errorf("could not cast to postgresql spec") return } // We will not get multiple Add events for the same cluster - c.queueClusterEvent(nil, pg, spec.EventAdd) + c.queueClusterEvent(nil, pg, EventAdd) } func (c *Controller) postgresqlUpdate(prev, cur interface{}) { - pgOld, ok := prev.(*spec.Postgresql) + pgOld, ok := prev.(*acidv1.Postgresql) if !ok { c.logger.Errorf("could not cast to postgresql spec") } - pgNew, ok := cur.(*spec.Postgresql) + pgNew, ok := cur.(*acidv1.Postgresql) if !ok { c.logger.Errorf("could not cast to postgresql spec") } + // Avoid the inifinite recursion for status updates if reflect.DeepEqual(pgOld.Spec, pgNew.Spec) { return } - c.queueClusterEvent(pgOld, pgNew, spec.EventUpdate) + c.queueClusterEvent(pgOld, pgNew, EventUpdate) } func (c *Controller) postgresqlDelete(obj interface{}) { - pg, ok := obj.(*spec.Postgresql) + pg, ok := obj.(*acidv1.Postgresql) if !ok { c.logger.Errorf("could not cast to postgresql spec") return } - c.queueClusterEvent(pg, nil, spec.EventDelete) + c.queueClusterEvent(pg, nil, EventDelete) } /* @@ -555,7 +492,7 @@ func (c *Controller) postgresqlDelete(obj interface{}) { The operator does not sync accounts/role bindings after creation. */ -func (c *Controller) submitRBACCredentials(event spec.ClusterEvent) error { +func (c *Controller) submitRBACCredentials(event ClusterEvent) error { namespace := event.NewSpec.GetNamespace() if _, ok := c.namespacesWithDefinedRBAC.Load(namespace); ok { diff --git a/pkg/controller/postgresql_test.go b/pkg/controller/postgresql_test.go index d5d5669af..c3a74be74 100644 --- a/pkg/controller/postgresql_test.go +++ b/pkg/controller/postgresql_test.go @@ -1,6 +1,7 @@ package controller import ( + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" "github.com/zalando-incubator/postgres-operator/pkg/spec" "reflect" "testing" @@ -16,21 +17,21 @@ func TestMergeDeprecatedPostgreSQLSpecParameters(t *testing.T) { tests := []struct { name string - in *spec.PostgresSpec - out *spec.PostgresSpec + in *acidv1.PostgresSpec + out *acidv1.PostgresSpec error string }{ { "Check that old parameters propagate values to the new ones", - &spec.PostgresSpec{UseLoadBalancer: &True, ReplicaLoadBalancer: &True}, - &spec.PostgresSpec{UseLoadBalancer: nil, ReplicaLoadBalancer: nil, + &acidv1.PostgresSpec{UseLoadBalancer: &True, ReplicaLoadBalancer: &True}, + &acidv1.PostgresSpec{UseLoadBalancer: nil, ReplicaLoadBalancer: nil, EnableMasterLoadBalancer: &True, EnableReplicaLoadBalancer: &True}, "New parameters should be set from the values of old ones", }, { "Check that new parameters are not set when both old and new ones are present", - &spec.PostgresSpec{UseLoadBalancer: &True, EnableMasterLoadBalancer: &False}, - &spec.PostgresSpec{UseLoadBalancer: nil, EnableMasterLoadBalancer: &False}, + &acidv1.PostgresSpec{UseLoadBalancer: &True, EnableMasterLoadBalancer: &False}, + &acidv1.PostgresSpec{UseLoadBalancer: nil, EnableMasterLoadBalancer: &False}, "New parameters should remain unchanged when both old and new are present", }, } diff --git a/pkg/controller/types.go b/pkg/controller/types.go new file mode 100644 index 000000000..c412eb66e --- /dev/null +++ b/pkg/controller/types.go @@ -0,0 +1,30 @@ +package controller + +import ( + "k8s.io/apimachinery/pkg/types" + "time" + + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" +) + +// EventType contains type of the events for the TPRs and Pods received from Kubernetes +type EventType string + +// Possible values for the EventType +const ( + EventAdd EventType = "ADD" + EventUpdate EventType = "UPDATE" + EventDelete EventType = "DELETE" + EventSync EventType = "SYNC" + EventRepair EventType = "REPAIR" +) + +// ClusterEvent carries the payload of the Cluster TPR events. +type ClusterEvent struct { + EventTime time.Time + UID types.UID + EventType EventType + OldSpec *acidv1.Postgresql + NewSpec *acidv1.Postgresql + WorkerID uint32 +} diff --git a/pkg/controller/util.go b/pkg/controller/util.go index 46c369fdd..e6d7f972e 100644 --- a/pkg/controller/util.go +++ b/pkg/controller/util.go @@ -8,10 +8,10 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" "github.com/zalando-incubator/postgres-operator/pkg/cluster" "github.com/zalando-incubator/postgres-operator/pkg/spec" "github.com/zalando-incubator/postgres-operator/pkg/util/config" - "github.com/zalando-incubator/postgres-operator/pkg/util/constants" "github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil" "gopkg.in/yaml.v2" ) @@ -47,22 +47,24 @@ func (c *Controller) clusterWorkerID(clusterName spec.NamespacedName) uint32 { return c.clusterWorkers[clusterName] } -func (c *Controller) createOperatorCRD(plural, singular, short string) error { +func (c *Controller) createOperatorCRD(name, kind, plural, short string) error { + subResourceStatus := apiextv1beta1.CustomResourceSubresourceStatus{} crd := &apiextv1beta1.CustomResourceDefinition{ ObjectMeta: metav1.ObjectMeta{ - Name: plural + "." + constants.CRDGroup, + Name: name, }, Spec: apiextv1beta1.CustomResourceDefinitionSpec{ - Group: constants.CRDGroup, - Version: constants.CRDApiVersion, + Group: acidv1.SchemeGroupVersion.Group, + Version: acidv1.SchemeGroupVersion.Version, Names: apiextv1beta1.CustomResourceDefinitionNames{ Plural: plural, - Singular: singular, ShortNames: []string{short}, - Kind: singular, - ListKind: singular + "List", + Kind: kind, }, Scope: apiextv1beta1.NamespaceScoped, + Subresources: &apiextv1beta1.CustomResourceSubresources{ + Status: &subResourceStatus, + }, }, } @@ -99,11 +101,17 @@ func (c *Controller) createOperatorCRD(plural, singular, short string) error { } func (c *Controller) createPostgresCRD() error { - return c.createOperatorCRD(constants.PostgresCRDResource, constants.PostgresCRDKind, constants.PostgresCRDShort) + return c.createOperatorCRD(acidv1.PostgresCRDResouceName, + acidv1.PostgresCRDResourceKind, + acidv1.PostgresCRDResourcePlural, + acidv1.PostgresCRDResourceShort) } func (c *Controller) createConfigurationCRD() error { - return c.createOperatorCRD(constants.OperatorConfigCRDResource, constants.OperatorConfigCRDKind, constants.OperatorConfigCRDShort) + return c.createOperatorCRD(acidv1.OperatorConfigCRDResourceName, + acidv1.OperatorConfigCRDResouceKind, + acidv1.OperatorConfigCRDResourcePlural, + acidv1.OperatorConfigCRDResourceShort) } func readDecodedRole(s string) (*spec.PgUser, error) { diff --git a/pkg/generated/clientset/versioned/clientset.go b/pkg/generated/clientset/versioned/clientset.go new file mode 100644 index 000000000..d42fa3b21 --- /dev/null +++ b/pkg/generated/clientset/versioned/clientset.go @@ -0,0 +1,104 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1" + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + AcidV1() acidv1.AcidV1Interface + // Deprecated: please explicitly pick a version if possible. + Acid() acidv1.AcidV1Interface +} + +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *discovery.DiscoveryClient + acidV1 *acidv1.AcidV1Client +} + +// AcidV1 retrieves the AcidV1Client +func (c *Clientset) AcidV1() acidv1.AcidV1Interface { + return c.acidV1 +} + +// Deprecated: Acid retrieves the default version of AcidClient. +// Please explicitly pick a version. +func (c *Clientset) Acid() acidv1.AcidV1Interface { + return c.acidV1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error + cs.acidV1, err = acidv1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + var cs Clientset + cs.acidV1 = acidv1.NewForConfigOrDie(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) + return &cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.acidV1 = acidv1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/pkg/generated/clientset/versioned/doc.go b/pkg/generated/clientset/versioned/doc.go new file mode 100644 index 000000000..4ef8c1bb8 --- /dev/null +++ b/pkg/generated/clientset/versioned/doc.go @@ -0,0 +1,26 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated clientset. +package versioned diff --git a/pkg/generated/clientset/versioned/fake/clientset_generated.go b/pkg/generated/clientset/versioned/fake/clientset_generated.go new file mode 100644 index 000000000..19d9ab805 --- /dev/null +++ b/pkg/generated/clientset/versioned/fake/clientset_generated.go @@ -0,0 +1,88 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + clientset "github.com/zalando-incubator/postgres-operator/pkg/generated/clientset/versioned" + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1" + fakeacidv1 "github.com/zalando-incubator/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/discovery" + fakediscovery "k8s.io/client-go/discovery/fake" + "k8s.io/client-go/testing" +) + +// NewSimpleClientset returns a clientset that will respond with the provided objects. +// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, +// without applying any validations and/or defaults. It shouldn't be considered a replacement +// for a real clientset and is mostly useful in simple unit tests. +func NewSimpleClientset(objects ...runtime.Object) *Clientset { + o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &Clientset{} + cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + +// Clientset implements clientset.Interface. Meant to be embedded into a +// struct to get a default implementation. This makes faking out just the method +// you want to test easier. +type Clientset struct { + testing.Fake + discovery *fakediscovery.FakeDiscovery +} + +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + return c.discovery +} + +var _ clientset.Interface = &Clientset{} + +// AcidV1 retrieves the AcidV1Client +func (c *Clientset) AcidV1() acidv1.AcidV1Interface { + return &fakeacidv1.FakeAcidV1{Fake: &c.Fake} +} + +// Acid retrieves the AcidV1Client +func (c *Clientset) Acid() acidv1.AcidV1Interface { + return &fakeacidv1.FakeAcidV1{Fake: &c.Fake} +} diff --git a/pkg/generated/clientset/versioned/fake/doc.go b/pkg/generated/clientset/versioned/fake/doc.go new file mode 100644 index 000000000..c249c43fa --- /dev/null +++ b/pkg/generated/clientset/versioned/fake/doc.go @@ -0,0 +1,26 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated fake clientset. +package fake diff --git a/pkg/generated/clientset/versioned/fake/register.go b/pkg/generated/clientset/versioned/fake/register.go new file mode 100644 index 000000000..5269b757a --- /dev/null +++ b/pkg/generated/clientset/versioned/fake/register.go @@ -0,0 +1,60 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" +) + +var scheme = runtime.NewScheme() +var codecs = serializer.NewCodecFactory(scheme) +var parameterCodec = runtime.NewParameterCodec(scheme) + +func init() { + v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) + AddToScheme(scheme) +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +func AddToScheme(scheme *runtime.Scheme) { + acidv1.AddToScheme(scheme) +} diff --git a/pkg/generated/clientset/versioned/scheme/doc.go b/pkg/generated/clientset/versioned/scheme/doc.go new file mode 100644 index 000000000..d17209947 --- /dev/null +++ b/pkg/generated/clientset/versioned/scheme/doc.go @@ -0,0 +1,26 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/pkg/generated/clientset/versioned/scheme/register.go b/pkg/generated/clientset/versioned/scheme/register.go new file mode 100644 index 000000000..346cd4b16 --- /dev/null +++ b/pkg/generated/clientset/versioned/scheme/register.go @@ -0,0 +1,60 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + acidv1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + AddToScheme(Scheme) +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +func AddToScheme(scheme *runtime.Scheme) { + acidv1.AddToScheme(scheme) +} diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go new file mode 100644 index 000000000..4e73a425f --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/acid.zalan.do_client.go @@ -0,0 +1,101 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + v1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" + "github.com/zalando-incubator/postgres-operator/pkg/generated/clientset/versioned/scheme" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + rest "k8s.io/client-go/rest" +) + +type AcidV1Interface interface { + RESTClient() rest.Interface + OperatorConfigurationsGetter + PostgresqlsGetter +} + +// AcidV1Client is used to interact with features provided by the acid.zalan.do group. +type AcidV1Client struct { + restClient rest.Interface +} + +func (c *AcidV1Client) OperatorConfigurations(namespace string) OperatorConfigurationInterface { + return newOperatorConfigurations(c, namespace) +} + +func (c *AcidV1Client) Postgresqls(namespace string) PostgresqlInterface { + return newPostgresqls(c, namespace) +} + +// NewForConfig creates a new AcidV1Client for the given config. +func NewForConfig(c *rest.Config) (*AcidV1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &AcidV1Client{client}, nil +} + +// NewForConfigOrDie creates a new AcidV1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *AcidV1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new AcidV1Client for the given RESTClient. +func New(c rest.Interface) *AcidV1Client { + return &AcidV1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *AcidV1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/doc.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/doc.go new file mode 100644 index 000000000..97d91a36a --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/doc.go @@ -0,0 +1,26 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1 diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/doc.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/doc.go new file mode 100644 index 000000000..58640649f --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/doc.go @@ -0,0 +1,26 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go new file mode 100644 index 000000000..9d401ef7c --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go @@ -0,0 +1,50 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "github.com/zalando-incubator/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeAcidV1 struct { + *testing.Fake +} + +func (c *FakeAcidV1) OperatorConfigurations(namespace string) v1.OperatorConfigurationInterface { + return &FakeOperatorConfigurations{c, namespace} +} + +func (c *FakeAcidV1) Postgresqls(namespace string) v1.PostgresqlInterface { + return &FakePostgresqls{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeAcidV1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go new file mode 100644 index 000000000..b2fada626 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go @@ -0,0 +1,53 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + acidzalandov1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + testing "k8s.io/client-go/testing" +) + +// FakeOperatorConfigurations implements OperatorConfigurationInterface +type FakeOperatorConfigurations struct { + Fake *FakeAcidV1 + ns string +} + +var operatorconfigurationsResource = schema.GroupVersionResource{Group: "acid.zalan.do", Version: "v1", Resource: "operatorconfigurations"} + +var operatorconfigurationsKind = schema.GroupVersionKind{Group: "acid.zalan.do", Version: "v1", Kind: "OperatorConfiguration"} + +// Get takes name of the operatorConfiguration, and returns the corresponding operatorConfiguration object, and an error if there is any. +func (c *FakeOperatorConfigurations) Get(name string, options v1.GetOptions) (result *acidzalandov1.OperatorConfiguration, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(operatorconfigurationsResource, c.ns, name), &acidzalandov1.OperatorConfiguration{}) + + if obj == nil { + return nil, err + } + return obj.(*acidzalandov1.OperatorConfiguration), err +} diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go new file mode 100644 index 000000000..6feb72eb8 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_postgresql.go @@ -0,0 +1,146 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + acidzalandov1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakePostgresqls implements PostgresqlInterface +type FakePostgresqls struct { + Fake *FakeAcidV1 + ns string +} + +var postgresqlsResource = schema.GroupVersionResource{Group: "acid.zalan.do", Version: "v1", Resource: "postgresqls"} + +var postgresqlsKind = schema.GroupVersionKind{Group: "acid.zalan.do", Version: "v1", Kind: "Postgresql"} + +// Get takes name of the postgresql, and returns the corresponding postgresql object, and an error if there is any. +func (c *FakePostgresqls) Get(name string, options v1.GetOptions) (result *acidzalandov1.Postgresql, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(postgresqlsResource, c.ns, name), &acidzalandov1.Postgresql{}) + + if obj == nil { + return nil, err + } + return obj.(*acidzalandov1.Postgresql), err +} + +// List takes label and field selectors, and returns the list of Postgresqls that match those selectors. +func (c *FakePostgresqls) List(opts v1.ListOptions) (result *acidzalandov1.PostgresqlList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(postgresqlsResource, postgresqlsKind, c.ns, opts), &acidzalandov1.PostgresqlList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &acidzalandov1.PostgresqlList{ListMeta: obj.(*acidzalandov1.PostgresqlList).ListMeta} + for _, item := range obj.(*acidzalandov1.PostgresqlList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested postgresqls. +func (c *FakePostgresqls) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(postgresqlsResource, c.ns, opts)) + +} + +// Create takes the representation of a postgresql and creates it. Returns the server's representation of the postgresql, and an error, if there is any. +func (c *FakePostgresqls) Create(postgresql *acidzalandov1.Postgresql) (result *acidzalandov1.Postgresql, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(postgresqlsResource, c.ns, postgresql), &acidzalandov1.Postgresql{}) + + if obj == nil { + return nil, err + } + return obj.(*acidzalandov1.Postgresql), err +} + +// Update takes the representation of a postgresql and updates it. Returns the server's representation of the postgresql, and an error, if there is any. +func (c *FakePostgresqls) Update(postgresql *acidzalandov1.Postgresql) (result *acidzalandov1.Postgresql, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(postgresqlsResource, c.ns, postgresql), &acidzalandov1.Postgresql{}) + + if obj == nil { + return nil, err + } + return obj.(*acidzalandov1.Postgresql), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakePostgresqls) UpdateStatus(postgresql *acidzalandov1.Postgresql) (*acidzalandov1.Postgresql, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(postgresqlsResource, "status", c.ns, postgresql), &acidzalandov1.Postgresql{}) + + if obj == nil { + return nil, err + } + return obj.(*acidzalandov1.Postgresql), err +} + +// Delete takes name of the postgresql and deletes it. Returns an error if one occurs. +func (c *FakePostgresqls) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(postgresqlsResource, c.ns, name), &acidzalandov1.Postgresql{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakePostgresqls) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(postgresqlsResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &acidzalandov1.PostgresqlList{}) + return err +} + +// Patch applies the patch and returns the patched postgresql. +func (c *FakePostgresqls) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *acidzalandov1.Postgresql, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(postgresqlsResource, c.ns, name, data, subresources...), &acidzalandov1.Postgresql{}) + + if obj == nil { + return nil, err + } + return obj.(*acidzalandov1.Postgresql), err +} diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/generated_expansion.go new file mode 100644 index 000000000..775a4b21f --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/generated_expansion.go @@ -0,0 +1,29 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +type OperatorConfigurationExpansion interface{} + +type PostgresqlExpansion interface{} diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go new file mode 100644 index 000000000..2541f0e3f --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go @@ -0,0 +1,71 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + acidzalandov1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" + scheme "github.com/zalando-incubator/postgres-operator/pkg/generated/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + rest "k8s.io/client-go/rest" +) + +// OperatorConfigurationsGetter has a method to return a OperatorConfigurationInterface. +// A group's client should implement this interface. +type OperatorConfigurationsGetter interface { + OperatorConfigurations(namespace string) OperatorConfigurationInterface +} + +// OperatorConfigurationInterface has methods to work with OperatorConfiguration resources. +type OperatorConfigurationInterface interface { + Get(name string, options v1.GetOptions) (*acidzalandov1.OperatorConfiguration, error) + OperatorConfigurationExpansion +} + +// operatorConfigurations implements OperatorConfigurationInterface +type operatorConfigurations struct { + client rest.Interface + ns string +} + +// newOperatorConfigurations returns a OperatorConfigurations +func newOperatorConfigurations(c *AcidV1Client, namespace string) *operatorConfigurations { + return &operatorConfigurations{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the operatorConfiguration, and returns the corresponding operatorConfiguration object, and an error if there is any. +func (c *operatorConfigurations) Get(name string, options v1.GetOptions) (result *acidzalandov1.OperatorConfiguration, err error) { + result = &acidzalandov1.OperatorConfiguration{} + err = c.client.Get(). + Namespace(c.ns). + Resource("operatorconfigurations"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} diff --git a/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresql.go b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresql.go new file mode 100644 index 000000000..df1045ee3 --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/postgresql.go @@ -0,0 +1,180 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + v1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" + scheme "github.com/zalando-incubator/postgres-operator/pkg/generated/clientset/versioned/scheme" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// PostgresqlsGetter has a method to return a PostgresqlInterface. +// A group's client should implement this interface. +type PostgresqlsGetter interface { + Postgresqls(namespace string) PostgresqlInterface +} + +// PostgresqlInterface has methods to work with Postgresql resources. +type PostgresqlInterface interface { + Create(*v1.Postgresql) (*v1.Postgresql, error) + Update(*v1.Postgresql) (*v1.Postgresql, error) + UpdateStatus(*v1.Postgresql) (*v1.Postgresql, error) + Delete(name string, options *metav1.DeleteOptions) error + DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error + Get(name string, options metav1.GetOptions) (*v1.Postgresql, error) + List(opts metav1.ListOptions) (*v1.PostgresqlList, error) + Watch(opts metav1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Postgresql, err error) + PostgresqlExpansion +} + +// postgresqls implements PostgresqlInterface +type postgresqls struct { + client rest.Interface + ns string +} + +// newPostgresqls returns a Postgresqls +func newPostgresqls(c *AcidV1Client, namespace string) *postgresqls { + return &postgresqls{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the postgresql, and returns the corresponding postgresql object, and an error if there is any. +func (c *postgresqls) Get(name string, options metav1.GetOptions) (result *v1.Postgresql, err error) { + result = &v1.Postgresql{} + err = c.client.Get(). + Namespace(c.ns). + Resource("postgresqls"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Postgresqls that match those selectors. +func (c *postgresqls) List(opts metav1.ListOptions) (result *v1.PostgresqlList, err error) { + result = &v1.PostgresqlList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("postgresqls"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested postgresqls. +func (c *postgresqls) Watch(opts metav1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("postgresqls"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Create takes the representation of a postgresql and creates it. Returns the server's representation of the postgresql, and an error, if there is any. +func (c *postgresqls) Create(postgresql *v1.Postgresql) (result *v1.Postgresql, err error) { + result = &v1.Postgresql{} + err = c.client.Post(). + Namespace(c.ns). + Resource("postgresqls"). + Body(postgresql). + Do(). + Into(result) + return +} + +// Update takes the representation of a postgresql and updates it. Returns the server's representation of the postgresql, and an error, if there is any. +func (c *postgresqls) Update(postgresql *v1.Postgresql) (result *v1.Postgresql, err error) { + result = &v1.Postgresql{} + err = c.client.Put(). + Namespace(c.ns). + Resource("postgresqls"). + Name(postgresql.Name). + Body(postgresql). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *postgresqls) UpdateStatus(postgresql *v1.Postgresql) (result *v1.Postgresql, err error) { + result = &v1.Postgresql{} + err = c.client.Put(). + Namespace(c.ns). + Resource("postgresqls"). + Name(postgresql.Name). + SubResource("status"). + Body(postgresql). + Do(). + Into(result) + return +} + +// Delete takes name of the postgresql and deletes it. Returns an error if one occurs. +func (c *postgresqls) Delete(name string, options *metav1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("postgresqls"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *postgresqls) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("postgresqls"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched postgresql. +func (c *postgresqls) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Postgresql, err error) { + result = &v1.Postgresql{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("postgresqls"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/interface.go b/pkg/generated/informers/externalversions/acid.zalan.do/interface.go new file mode 100644 index 000000000..9dfa60021 --- /dev/null +++ b/pkg/generated/informers/externalversions/acid.zalan.do/interface.go @@ -0,0 +1,52 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package acid + +import ( + v1 "github.com/zalando-incubator/postgres-operator/pkg/generated/informers/externalversions/acid.zalan.do/v1" + internalinterfaces "github.com/zalando-incubator/postgres-operator/pkg/generated/informers/externalversions/internalinterfaces" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V1 provides access to shared informers for resources in V1. + V1() v1.Interface +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// V1 returns a new v1.Interface. +func (g *group) V1() v1.Interface { + return v1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go b/pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go new file mode 100644 index 000000000..f0f35b65c --- /dev/null +++ b/pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go @@ -0,0 +1,51 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1 + +import ( + internalinterfaces "github.com/zalando-incubator/postgres-operator/pkg/generated/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // Postgresqls returns a PostgresqlInformer. + Postgresqls() PostgresqlInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// Postgresqls returns a PostgresqlInformer. +func (v *version) Postgresqls() PostgresqlInformer { + return &postgresqlInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go new file mode 100644 index 000000000..50f3126cf --- /dev/null +++ b/pkg/generated/informers/externalversions/acid.zalan.do/v1/postgresql.go @@ -0,0 +1,95 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1 + +import ( + time "time" + + acidzalandov1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" + versioned "github.com/zalando-incubator/postgres-operator/pkg/generated/clientset/versioned" + internalinterfaces "github.com/zalando-incubator/postgres-operator/pkg/generated/informers/externalversions/internalinterfaces" + v1 "github.com/zalando-incubator/postgres-operator/pkg/generated/listers/acid.zalan.do/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// PostgresqlInformer provides access to a shared informer and lister for +// Postgresqls. +type PostgresqlInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1.PostgresqlLister +} + +type postgresqlInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewPostgresqlInformer constructs a new informer for Postgresql type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewPostgresqlInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredPostgresqlInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredPostgresqlInformer constructs a new informer for Postgresql type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredPostgresqlInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AcidV1().Postgresqls(namespace).List(options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AcidV1().Postgresqls(namespace).Watch(options) + }, + }, + &acidzalandov1.Postgresql{}, + resyncPeriod, + indexers, + ) +} + +func (f *postgresqlInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredPostgresqlInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *postgresqlInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&acidzalandov1.Postgresql{}, f.defaultInformer) +} + +func (f *postgresqlInformer) Lister() v1.PostgresqlLister { + return v1.NewPostgresqlLister(f.Informer().GetIndexer()) +} diff --git a/pkg/generated/informers/externalversions/factory.go b/pkg/generated/informers/externalversions/factory.go new file mode 100644 index 000000000..395bc25b5 --- /dev/null +++ b/pkg/generated/informers/externalversions/factory.go @@ -0,0 +1,186 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + reflect "reflect" + sync "sync" + time "time" + + versioned "github.com/zalando-incubator/postgres-operator/pkg/generated/clientset/versioned" + acidzalando "github.com/zalando-incubator/postgres-operator/pkg/generated/informers/externalversions/acid.zalan.do" + internalinterfaces "github.com/zalando-incubator/postgres-operator/pkg/generated/informers/externalversions/internalinterfaces" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// SharedInformerOption defines the functional option type for SharedInformerFactory. +type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory + +type sharedInformerFactory struct { + client versioned.Interface + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc + lock sync.Mutex + defaultResync time.Duration + customResync map[reflect.Type]time.Duration + + informers map[reflect.Type]cache.SharedIndexInformer + // startedInformers is used for tracking which informers have been started. + // This allows Start() to be called multiple times safely. + startedInformers map[reflect.Type]bool +} + +// WithCustomResyncConfig sets a custom resync period for the specified informer types. +func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + for k, v := range resyncConfig { + factory.customResync[reflect.TypeOf(k)] = v + } + return factory + } +} + +// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. +func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.tweakListOptions = tweakListOptions + return factory + } +} + +// WithNamespace limits the SharedInformerFactory to the specified namespace. +func WithNamespace(namespace string) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.namespace = namespace + return factory + } +} + +// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. +func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync) +} + +// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. +// Listers obtained via this SharedInformerFactory will be subject to the same filters +// as specified here. +// Deprecated: Please use NewSharedInformerFactoryWithOptions instead +func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) +} + +// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. +func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { + factory := &sharedInformerFactory{ + client: client, + namespace: v1.NamespaceAll, + defaultResync: defaultResync, + informers: make(map[reflect.Type]cache.SharedIndexInformer), + startedInformers: make(map[reflect.Type]bool), + customResync: make(map[reflect.Type]time.Duration), + } + + // Apply all options + for _, opt := range options { + factory = opt(factory) + } + + return factory +} + +// Start initializes all requested informers. +func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { + f.lock.Lock() + defer f.lock.Unlock() + + for informerType, informer := range f.informers { + if !f.startedInformers[informerType] { + go informer.Run(stopCh) + f.startedInformers[informerType] = true + } + } +} + +// WaitForCacheSync waits for all started informers' cache were synced. +func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { + informers := func() map[reflect.Type]cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informers := map[reflect.Type]cache.SharedIndexInformer{} + for informerType, informer := range f.informers { + if f.startedInformers[informerType] { + informers[informerType] = informer + } + } + return informers + }() + + res := map[reflect.Type]bool{} + for informType, informer := range informers { + res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) + } + return res +} + +// InternalInformerFor returns the SharedIndexInformer for obj using an internal +// client. +func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informerType := reflect.TypeOf(obj) + informer, exists := f.informers[informerType] + if exists { + return informer + } + + resyncPeriod, exists := f.customResync[informerType] + if !exists { + resyncPeriod = f.defaultResync + } + + informer = newFunc(f.client, resyncPeriod) + f.informers[informerType] = informer + + return informer +} + +// SharedInformerFactory provides shared informers for resources in all known +// API group versions. +type SharedInformerFactory interface { + internalinterfaces.SharedInformerFactory + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + + Acid() acidzalando.Interface +} + +func (f *sharedInformerFactory) Acid() acidzalando.Interface { + return acidzalando.New(f, f.namespace, f.tweakListOptions) +} diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go new file mode 100644 index 000000000..1b1988212 --- /dev/null +++ b/pkg/generated/informers/externalversions/generic.go @@ -0,0 +1,68 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + "fmt" + + v1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// GenericInformer is type of SharedIndexInformer which will locate and delegate to other +// sharedInformers based on type +type GenericInformer interface { + Informer() cache.SharedIndexInformer + Lister() cache.GenericLister +} + +type genericInformer struct { + informer cache.SharedIndexInformer + resource schema.GroupResource +} + +// Informer returns the SharedIndexInformer. +func (f *genericInformer) Informer() cache.SharedIndexInformer { + return f.informer +} + +// Lister returns the GenericLister. +func (f *genericInformer) Lister() cache.GenericLister { + return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) +} + +// ForResource gives generic access to a shared informer of the matching type +// TODO extend this to unknown resources with a client pool +func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { + switch resource { + // Group=acid.zalan.do, Version=v1 + case v1.SchemeGroupVersion.WithResource("postgresqls"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Acid().V1().Postgresqls().Informer()}, nil + + } + + return nil, fmt.Errorf("no informer found for %v", resource) +} diff --git a/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go new file mode 100644 index 000000000..f3b4ab9fa --- /dev/null +++ b/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -0,0 +1,44 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package internalinterfaces + +import ( + time "time" + + versioned "github.com/zalando-incubator/postgres-operator/pkg/generated/clientset/versioned" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + cache "k8s.io/client-go/tools/cache" +) + +type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer + +// SharedInformerFactory a small interface to allow for adding an informer without an import cycle +type SharedInformerFactory interface { + Start(stopCh <-chan struct{}) + InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer +} + +type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go b/pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go new file mode 100644 index 000000000..071a413d6 --- /dev/null +++ b/pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go @@ -0,0 +1,33 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1 + +// PostgresqlListerExpansion allows custom methods to be added to +// PostgresqlLister. +type PostgresqlListerExpansion interface{} + +// PostgresqlNamespaceListerExpansion allows custom methods to be added to +// PostgresqlNamespaceLister. +type PostgresqlNamespaceListerExpansion interface{} diff --git a/pkg/generated/listers/acid.zalan.do/v1/postgresql.go b/pkg/generated/listers/acid.zalan.do/v1/postgresql.go new file mode 100644 index 000000000..c8603bc79 --- /dev/null +++ b/pkg/generated/listers/acid.zalan.do/v1/postgresql.go @@ -0,0 +1,100 @@ +/* +Copyright 2018 Compose, Zalando SE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1 + +import ( + v1 "github.com/zalando-incubator/postgres-operator/pkg/apis/acid.zalan.do/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// PostgresqlLister helps list Postgresqls. +type PostgresqlLister interface { + // List lists all Postgresqls in the indexer. + List(selector labels.Selector) (ret []*v1.Postgresql, err error) + // Postgresqls returns an object that can list and get Postgresqls. + Postgresqls(namespace string) PostgresqlNamespaceLister + PostgresqlListerExpansion +} + +// postgresqlLister implements the PostgresqlLister interface. +type postgresqlLister struct { + indexer cache.Indexer +} + +// NewPostgresqlLister returns a new PostgresqlLister. +func NewPostgresqlLister(indexer cache.Indexer) PostgresqlLister { + return &postgresqlLister{indexer: indexer} +} + +// List lists all Postgresqls in the indexer. +func (s *postgresqlLister) List(selector labels.Selector) (ret []*v1.Postgresql, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1.Postgresql)) + }) + return ret, err +} + +// Postgresqls returns an object that can list and get Postgresqls. +func (s *postgresqlLister) Postgresqls(namespace string) PostgresqlNamespaceLister { + return postgresqlNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// PostgresqlNamespaceLister helps list and get Postgresqls. +type PostgresqlNamespaceLister interface { + // List lists all Postgresqls in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1.Postgresql, err error) + // Get retrieves the Postgresql from the indexer for a given namespace and name. + Get(name string) (*v1.Postgresql, error) + PostgresqlNamespaceListerExpansion +} + +// postgresqlNamespaceLister implements the PostgresqlNamespaceLister +// interface. +type postgresqlNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Postgresqls in the indexer for a given namespace. +func (s postgresqlNamespaceLister) List(selector labels.Selector) (ret []*v1.Postgresql, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1.Postgresql)) + }) + return ret, err +} + +// Get retrieves the Postgresql from the indexer for a given namespace and name. +func (s postgresqlNamespaceLister) Get(name string) (*v1.Postgresql, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1.Resource("postgresql"), name) + } + return obj.(*v1.Postgresql), nil +} diff --git a/pkg/spec/postgresql.go b/pkg/spec/postgresql.go deleted file mode 100644 index 889ebe5bc..000000000 --- a/pkg/spec/postgresql.go +++ /dev/null @@ -1,394 +0,0 @@ -package spec - -import ( - "encoding/json" - "fmt" - "github.com/mohae/deepcopy" - "regexp" - "strings" - "time" - - "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// MaintenanceWindow describes the time window when the operator is allowed to do maintenance on a cluster. -type MaintenanceWindow struct { - Everyday bool - Weekday time.Weekday - StartTime time.Time // Start time - EndTime time.Time // End time -} - -// Volume describes a single volume in the manifest. -type Volume struct { - Size string `json:"size"` - StorageClass string `json:"storageClass"` -} - -// PostgresqlParam describes PostgreSQL version and pairs of configuration parameter name - values. -type PostgresqlParam struct { - PgVersion string `json:"version"` - Parameters map[string]string `json:"parameters"` -} - -// ResourceDescription describes CPU and memory resources defined for a cluster. -type ResourceDescription struct { - CPU string `json:"cpu"` - Memory string `json:"memory"` -} - -// Resources describes requests and limits for the cluster resouces. -type Resources struct { - ResourceRequest ResourceDescription `json:"requests,omitempty"` - ResourceLimits ResourceDescription `json:"limits,omitempty"` -} - -// Patroni contains Patroni-specific configuration -type Patroni struct { - InitDB map[string]string `json:"initdb"` - PgHba []string `json:"pg_hba"` - TTL uint32 `json:"ttl"` - LoopWait uint32 `json:"loop_wait"` - RetryTimeout uint32 `json:"retry_timeout"` - MaximumLagOnFailover float32 `json:"maximum_lag_on_failover"` // float32 because https://github.com/kubernetes/kubernetes/issues/30213 -} - -// CloneDescription describes which cluster the new should clone and up to which point in time -type CloneDescription struct { - ClusterName string `json:"cluster,omitempty"` - UID string `json:"uid,omitempty"` - EndTimestamp string `json:"timestamp,omitempty"` -} - -// Sidecar defines a container to be run in the same pod as the Postgres container. -type Sidecar struct { - Resources `json:"resources,omitempty"` - Name string `json:"name,omitempty"` - DockerImage string `json:"image,omitempty"` - Ports []v1.ContainerPort `json:"ports,omitempty"` - Env []v1.EnvVar `json:"env,omitempty"` -} - -// UserFlags defines flags (such as superuser, nologin) that could be assigned to individual users -type UserFlags []string - -// PostgresStatus contains status of the PostgreSQL cluster (running, creation failed etc.) -type PostgresStatus string - -// possible values for PostgreSQL cluster statuses -const ( - ClusterStatusUnknown PostgresStatus = "" - ClusterStatusCreating PostgresStatus = "Creating" - ClusterStatusUpdating PostgresStatus = "Updating" - ClusterStatusUpdateFailed PostgresStatus = "UpdateFailed" - ClusterStatusSyncFailed PostgresStatus = "SyncFailed" - ClusterStatusAddFailed PostgresStatus = "CreateFailed" - ClusterStatusRunning PostgresStatus = "Running" - ClusterStatusInvalid PostgresStatus = "Invalid" -) - -const ( - serviceNameMaxLength = 63 - clusterNameMaxLength = serviceNameMaxLength - len("-repl") - serviceNameRegexString = `^[a-z]([-a-z0-9]*[a-z0-9])?$` -) - -// Postgresql defines PostgreSQL Custom Resource Definition Object. -type Postgresql struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata"` - - Spec PostgresSpec `json:"spec"` - Status PostgresStatus `json:"status,omitempty"` - Error error `json:"-"` -} - -// PostgresSpec defines the specification for the PostgreSQL TPR. -type PostgresSpec struct { - PostgresqlParam `json:"postgresql"` - Volume `json:"volume,omitempty"` - Patroni `json:"patroni,omitempty"` - Resources `json:"resources,omitempty"` - - TeamID string `json:"teamId"` - DockerImage string `json:"dockerImage,omitempty"` - - // vars that enable load balancers are pointers because it is important to know if any of them is omitted from the Postgres manifest - // in that case the var evaluates to nil and the value is taken from the operator config - EnableMasterLoadBalancer *bool `json:"enableMasterLoadBalancer,omitempty"` - EnableReplicaLoadBalancer *bool `json:"enableReplicaLoadBalancer,omitempty"` - - // deprecated load balancer settings maintained for backward compatibility - // see "Load balancers" operator docs - UseLoadBalancer *bool `json:"useLoadBalancer,omitempty"` - ReplicaLoadBalancer *bool `json:"replicaLoadBalancer,omitempty"` - - // load balancers' source ranges are the same for master and replica services - AllowedSourceRanges []string `json:"allowedSourceRanges"` - - NumberOfInstances int32 `json:"numberOfInstances"` - Users map[string]UserFlags `json:"users"` - MaintenanceWindows []MaintenanceWindow `json:"maintenanceWindows,omitempty"` - Clone CloneDescription `json:"clone"` - ClusterName string `json:"-"` - Databases map[string]string `json:"databases,omitempty"` - Tolerations []v1.Toleration `json:"tolerations,omitempty"` - Sidecars []Sidecar `json:"sidecars,omitempty"` - PodPriorityClassName string `json:"pod_priority_class_name,omitempty"` -} - -// PostgresqlList defines a list of PostgreSQL clusters. -type PostgresqlList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []Postgresql `json:"items"` -} - -var ( - weekdays = map[string]int{"Sun": 0, "Mon": 1, "Tue": 2, "Wed": 3, "Thu": 4, "Fri": 5, "Sat": 6} - serviceNameRegex = regexp.MustCompile(serviceNameRegexString) -) - -// Clone makes a deepcopy of the Postgresql structure. The Error field is nulled-out, -// as there is no guarantee that the actual implementation of the error interface -// will not contain any private fields not-reachable to deepcopy. This should be ok, -// since Error is never read from a Kubernetes object. -func (p *Postgresql) Clone() *Postgresql { - if p == nil { - return nil - } - c := deepcopy.Copy(p).(*Postgresql) - c.Error = nil - return c -} - -func (p *Postgresql) DeepCopyInto(out *Postgresql) { - if p != nil { - *out = deepcopy.Copy(*p).(Postgresql) - } -} - -func (p *Postgresql) DeepCopy() *Postgresql { - if p == nil { - return nil - } - out := new(Postgresql) - p.DeepCopyInto(out) - return out -} - -func (p *Postgresql) DeepCopyObject() runtime.Object { - if c := p.DeepCopy(); c != nil { - return c - } - return nil -} - -func parseTime(s string) (time.Time, error) { - parts := strings.Split(s, ":") - if len(parts) != 2 { - return time.Time{}, fmt.Errorf("incorrect time format") - } - timeLayout := "15:04" - - tp, err := time.Parse(timeLayout, s) - if err != nil { - return time.Time{}, err - } - - return tp.UTC(), nil -} - -func parseWeekday(s string) (time.Weekday, error) { - weekday, ok := weekdays[s] - if !ok { - return time.Weekday(0), fmt.Errorf("incorrect weekday") - } - - return time.Weekday(weekday), nil -} - -// MarshalJSON converts a maintenance window definition to JSON. -func (m *MaintenanceWindow) MarshalJSON() ([]byte, error) { - if m.Everyday { - return []byte(fmt.Sprintf("\"%s-%s\"", - m.StartTime.Format("15:04"), - m.EndTime.Format("15:04"))), nil - } - - return []byte(fmt.Sprintf("\"%s:%s-%s\"", - m.Weekday.String()[:3], - m.StartTime.Format("15:04"), - m.EndTime.Format("15:04"))), nil -} - -// UnmarshalJSON converts a JSON to the maintenance window definition. -func (m *MaintenanceWindow) UnmarshalJSON(data []byte) error { - var ( - got MaintenanceWindow - err error - ) - - parts := strings.Split(string(data[1:len(data)-1]), "-") - if len(parts) != 2 { - return fmt.Errorf("incorrect maintenance window format") - } - - fromParts := strings.Split(parts[0], ":") - switch len(fromParts) { - case 3: - got.Everyday = false - got.Weekday, err = parseWeekday(fromParts[0]) - if err != nil { - return fmt.Errorf("could not parse weekday: %v", err) - } - - got.StartTime, err = parseTime(fromParts[1] + ":" + fromParts[2]) - case 2: - got.Everyday = true - got.StartTime, err = parseTime(fromParts[0] + ":" + fromParts[1]) - default: - return fmt.Errorf("incorrect maintenance window format") - } - if err != nil { - return fmt.Errorf("could not parse start time: %v", err) - } - - got.EndTime, err = parseTime(parts[1]) - if err != nil { - return fmt.Errorf("could not parse end time: %v", err) - } - - if got.EndTime.Before(got.StartTime) { - return fmt.Errorf("'From' time must be prior to the 'To' time") - } - - *m = got - - return nil -} - -func extractClusterName(clusterName string, teamName string) (string, error) { - teamNameLen := len(teamName) - if len(clusterName) < teamNameLen+2 { - return "", fmt.Errorf("name is too short") - } - - if teamNameLen == 0 { - return "", fmt.Errorf("team name is empty") - } - - if strings.ToLower(clusterName[:teamNameLen+1]) != strings.ToLower(teamName)+"-" { - return "", fmt.Errorf("name must match {TEAM}-{NAME} format") - } - if len(clusterName) > clusterNameMaxLength { - return "", fmt.Errorf("name cannot be longer than %d characters", clusterNameMaxLength) - } - if !serviceNameRegex.MatchString(clusterName) { - return "", fmt.Errorf("name must confirm to DNS-1035, regex used for validation is %q", - serviceNameRegexString) - } - - return clusterName[teamNameLen+1:], nil -} - -func validateCloneClusterDescription(clone *CloneDescription) error { - // when cloning from the basebackup (no end timestamp) check that the cluster name is a valid service name - if clone.ClusterName != "" && clone.EndTimestamp == "" { - if !serviceNameRegex.MatchString(clone.ClusterName) { - return fmt.Errorf("clone cluster name must confirm to DNS-1035, regex used for validation is %q", - serviceNameRegexString) - } - if len(clone.ClusterName) > serviceNameMaxLength { - return fmt.Errorf("clone cluster name must be no longer than %d characters", serviceNameMaxLength) - } - } - return nil -} - -type postgresqlListCopy PostgresqlList -type postgresqlCopy Postgresql - -// UnmarshalJSON converts a JSON into the PostgreSQL object. -func (p *Postgresql) UnmarshalJSON(data []byte) error { - var tmp postgresqlCopy - - err := json.Unmarshal(data, &tmp) - if err != nil { - metaErr := json.Unmarshal(data, &tmp.ObjectMeta) - if metaErr != nil { - return err - } - - tmp.Error = err - tmp.Status = ClusterStatusInvalid - - *p = Postgresql(tmp) - - return nil - } - tmp2 := Postgresql(tmp) - - if clusterName, err := extractClusterName(tmp2.ObjectMeta.Name, tmp2.Spec.TeamID); err != nil { - tmp2.Error = err - tmp2.Status = ClusterStatusInvalid - } else if err := validateCloneClusterDescription(&tmp2.Spec.Clone); err != nil { - tmp2.Error = err - tmp2.Status = ClusterStatusInvalid - } else { - tmp2.Spec.ClusterName = clusterName - } - - *p = tmp2 - - return nil -} - -// UnmarshalJSON converts a JSON into the PostgreSQL List object. -func (pl *PostgresqlList) UnmarshalJSON(data []byte) error { - var tmp postgresqlListCopy - - err := json.Unmarshal(data, &tmp) - if err != nil { - return err - } - tmp2 := PostgresqlList(tmp) - *pl = tmp2 - - return nil -} - -func (pl *PostgresqlList) DeepCopy() *PostgresqlList { - if pl == nil { - return nil - } - out := new(PostgresqlList) - pl.DeepCopyInto(out) - return out -} - -func (pl *PostgresqlList) DeepCopyInto(out *PostgresqlList) { - if pl != nil { - *out = deepcopy.Copy(*pl).(PostgresqlList) - } -} - -func (pl *PostgresqlList) DeepCopyObject() runtime.Object { - if c := pl.DeepCopy(); c != nil { - return c - } - return nil -} - -func (status PostgresStatus) Success() bool { - return status != ClusterStatusAddFailed && - status != ClusterStatusUpdateFailed && - status != ClusterStatusSyncFailed -} - -func (status PostgresStatus) String() string { - return string(status) -} diff --git a/pkg/spec/types.go b/pkg/spec/types.go index 8e11f34ac..c683a1cef 100644 --- a/pkg/spec/types.go +++ b/pkg/spec/types.go @@ -11,29 +11,14 @@ import ( "time" "github.com/Sirupsen/logrus" - "k8s.io/api/apps/v1beta1" - "k8s.io/api/core/v1" - policyv1beta1 "k8s.io/api/policy/v1beta1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" ) -// EventType contains type of the events for the TPRs and Pods received from Kubernetes -type EventType string - // NamespacedName describes the namespace/name pairs used in Kubernetes names. type NamespacedName types.NamespacedName -// Possible values for the EventType -const ( - EventAdd EventType = "ADD" - EventUpdate EventType = "UPDATE" - EventDelete EventType = "DELETE" - EventSync EventType = "SYNC" - EventRepair EventType = "REPAIR" - - fileWithNamespace = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" -) +const fileWithNamespace = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" // RoleOrigin contains the code of the origin of a role type RoleOrigin int @@ -47,16 +32,6 @@ const ( RoleOriginSystem ) -// ClusterEvent carries the payload of the Cluster TPR events. -type ClusterEvent struct { - EventTime time.Time - UID types.UID - EventType EventType - OldSpec *Postgresql - NewSpec *Postgresql - WorkerID uint32 -} - type syncUserOperation int // Possible values for the sync user operation (removal of users is not supported yet) @@ -66,15 +41,6 @@ const ( PGSyncAlterSet // handle ALTER ROLE SET parameter = value ) -// PodEvent describes the event for a single Pod -type PodEvent struct { - ResourceVersion string - PodName NamespacedName - PrevPod *v1.Pod - CurPod *v1.Pod - EventType EventType -} - // PgUser contains information about a single user. type PgUser struct { Origin RoleOrigin `yaml:"-"` @@ -109,36 +75,6 @@ type LogEntry struct { Message string } -// Process describes process of the cluster -type Process struct { - Name string - StartTime time.Time -} - -// ClusterStatus describes status of the cluster -type ClusterStatus struct { - Team string - Cluster string - MasterService *v1.Service - ReplicaService *v1.Service - MasterEndpoint *v1.Endpoints - ReplicaEndpoint *v1.Endpoints - StatefulSet *v1beta1.StatefulSet - PodDisruptionBudget *policyv1beta1.PodDisruptionBudget - - CurrentProcess Process - Worker uint32 - Status PostgresStatus - Spec PostgresSpec - Error error -} - -// WorkerStatus describes status of the worker -type WorkerStatus struct { - CurrentCluster NamespacedName - CurrentProcess Process -} - // Diff describes diff type Diff struct { EventTime time.Time @@ -260,30 +196,3 @@ func GetOperatorNamespace() string { } return operatorNamespace } - -type Duration time.Duration - -func (d *Duration) UnmarshalJSON(b []byte) error { - var ( - v interface{} - err error - ) - if err = json.Unmarshal(b, &v); err != nil { - return err - } - switch val := v.(type) { - case string: - t, err := time.ParseDuration(val) - if err != nil { - return err - } - *d = Duration(t) - return nil - case float64: - t := time.Duration(val) - *d = Duration(t) - return nil - default: - return fmt.Errorf("could not recognize type %T as a valid type to unmarshal to Duration", val) - } -} diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 34d282a96..bcfea0647 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -42,7 +42,7 @@ type Resources struct { // Auth describes authentication specific configuration parameters type Auth struct { - SecretNameTemplate stringTemplate `name:"secret_name_template" default:"{username}.{cluster}.credentials.{tprkind}.{tprgroup}"` + SecretNameTemplate StringTemplate `name:"secret_name_template" default:"{username}.{cluster}.credentials.{tprkind}.{tprgroup}"` PamRoleName string `name:"pam_role_name" default:"zalandos"` PamConfiguration string `name:"pam_configuration" default:"https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees"` TeamsAPIUrl string `name:"teams_api_url" default:"https://teams.example.com/api/"` @@ -93,9 +93,9 @@ type Config struct { EnableReplicaLoadBalancer bool `name:"enable_replica_load_balancer" default:"false"` // deprecated and kept for backward compatibility EnableLoadBalancer *bool `name:"enable_load_balancer"` - MasterDNSNameFormat stringTemplate `name:"master_dns_name_format" default:"{cluster}.{team}.{hostedzone}"` - ReplicaDNSNameFormat stringTemplate `name:"replica_dns_name_format" default:"{cluster}-repl.{team}.{hostedzone}"` - PDBNameFormat stringTemplate `name:"pdb_name_format" default:"postgres-{cluster}-pdb"` + MasterDNSNameFormat StringTemplate `name:"master_dns_name_format" default:"{cluster}.{team}.{hostedzone}"` + ReplicaDNSNameFormat StringTemplate `name:"replica_dns_name_format" default:"{cluster}-repl.{team}.{hostedzone}"` + PDBNameFormat StringTemplate `name:"pdb_name_format" default:"postgres-{cluster}-pdb"` Workers uint32 `name:"workers" default:"4"` APIPort int `name:"api_port" default:"8080"` RingLogLines int `name:"ring_log_lines" default:"100"` diff --git a/pkg/util/config/util.go b/pkg/util/config/util.go index aef333ce7..498810bb7 100644 --- a/pkg/util/config/util.go +++ b/pkg/util/config/util.go @@ -19,7 +19,7 @@ type fieldInfo struct { Field reflect.Value } -type stringTemplate string +type StringTemplate string func decoderFrom(field reflect.Value) (d decoder) { // it may be impossible for a struct field to fail this check @@ -172,7 +172,7 @@ func processField(value string, field reflect.Value) error { type parserState int const ( - plain parserState = iota + plain parserState = iota doubleQuoted singleQuoted ) @@ -221,13 +221,13 @@ func getMapPairsFromString(value string) (pairs []string, err error) { return } -func (f *stringTemplate) Decode(value string) error { - *f = stringTemplate(value) +func (f *StringTemplate) Decode(value string) error { + *f = StringTemplate(value) return nil } -func (f *stringTemplate) Format(a ...string) string { +func (f *StringTemplate) Format(a ...string) string { res := string(*f) for i := 0; i < len(a); i += 2 { @@ -237,6 +237,6 @@ func (f *stringTemplate) Format(a ...string) string { return res } -func (f stringTemplate) MarshalJSON() ([]byte, error) { +func (f StringTemplate) MarshalJSON() ([]byte, error) { return json.Marshal(string(f)) } diff --git a/pkg/util/constants/crd.go b/pkg/util/constants/crd.go deleted file mode 100644 index 113264f01..000000000 --- a/pkg/util/constants/crd.go +++ /dev/null @@ -1,13 +0,0 @@ -package constants - -// Different properties of the PostgreSQL Custom Resource Definition -const ( - PostgresCRDKind = "postgresql" - PostgresCRDResource = "postgresqls" - PostgresCRDShort = "pg" - CRDGroup = "acid.zalan.do" - CRDApiVersion = "v1" - OperatorConfigCRDKind = "postgresql-operator-configuration" - OperatorConfigCRDResource = "postgresql-operator-configurations" - OperatorConfigCRDShort = "pgopconfig" -) diff --git a/pkg/util/k8sutil/k8sutil.go b/pkg/util/k8sutil/k8sutil.go index f4af5fea1..9ab5cb876 100644 --- a/pkg/util/k8sutil/k8sutil.go +++ b/pkg/util/k8sutil/k8sutil.go @@ -2,25 +2,22 @@ package k8sutil import ( "fmt" - "reflect" - + "github.com/zalando-incubator/postgres-operator/pkg/util/constants" "k8s.io/api/core/v1" policybeta1 "k8s.io/api/policy/v1beta1" apiextclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" apiextbeta1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1" apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/kubernetes/typed/apps/v1beta1" v1core "k8s.io/client-go/kubernetes/typed/core/v1" policyv1beta1 "k8s.io/client-go/kubernetes/typed/policy/v1beta1" rbacv1beta1 "k8s.io/client-go/kubernetes/typed/rbac/v1beta1" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + "reflect" - "github.com/zalando-incubator/postgres-operator/pkg/util/constants" + acidv1client "github.com/zalando-incubator/postgres-operator/pkg/generated/clientset/versioned" ) // KubernetesClient describes getters for Kubernetes objects @@ -40,8 +37,8 @@ type KubernetesClient struct { policyv1beta1.PodDisruptionBudgetsGetter apiextbeta1.CustomResourceDefinitionsGetter - RESTClient rest.Interface - CRDREST rest.Interface + RESTClient rest.Interface + AcidV1ClientSet *acidv1client.Clientset } // RestConfig creates REST config @@ -87,27 +84,13 @@ func NewFromConfig(cfg *rest.Config) (KubernetesClient, error) { kubeClient.RESTClient = client.CoreV1().RESTClient() kubeClient.RoleBindingsGetter = client.RbacV1beta1() - cfg2 := *cfg - cfg2.GroupVersion = &schema.GroupVersion{ - Group: constants.CRDGroup, - Version: constants.CRDApiVersion, - } - cfg2.APIPath = constants.K8sAPIPath - // MIGRATION: api.codecs -> scheme.Codecs? - cfg2.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} - - crd, err := rest.RESTClientFor(&cfg2) - if err != nil { - return kubeClient, fmt.Errorf("could not get rest client: %v", err) - } - kubeClient.CRDREST = crd - apiextClient, err := apiextclient.NewForConfig(cfg) if err != nil { return kubeClient, fmt.Errorf("could not create api client:%v", err) } kubeClient.CustomResourceDefinitionsGetter = apiextClient.ApiextensionsV1beta1() + kubeClient.AcidV1ClientSet = acidv1client.NewForConfigOrDie(cfg) return kubeClient, nil }