diff --git a/Makefile b/Makefile index 9092737fe..a346b8d98 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # To re-generate a bundle for another specific version without changing the standard setup, you can: # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) -VERSION ?= 0.0.2 +VERSION ?= 0.0.3 # CHANNELS define the bundle channels used in the bundle. # Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") diff --git a/PROJECT b/PROJECT index 52b0a99f8..82934d382 100644 --- a/PROJECT +++ b/PROJECT @@ -47,4 +47,12 @@ resources: kind: DatabaseClusterBackup path: github.com/percona/everest-operator/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + domain: percona.com + group: everest + kind: ObjectStorage + path: github.com/percona/everest-operator/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/api/v1alpha1/databasecluster_types.go b/api/v1alpha1/databasecluster_types.go index c377b3e63..a0326e771 100644 --- a/api/v1alpha1/databasecluster_types.go +++ b/api/v1alpha1/databasecluster_types.go @@ -16,21 +16,12 @@ package v1alpha1 import ( - "github.com/percona/percona-backup-mongodb/pbm/compress" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const ( - // LoadBalancerMongos represents mongos load balancer. - LoadBalancerMongos LoadBalancerType = "mongos" - // LoadBalancerHAProxy represents haproxy load balancer. - LoadBalancerHAProxy LoadBalancerType = "haproxy" - // LoadBalancerProxySQL represents proxySQL load balancer. - LoadBalancerProxySQL LoadBalancerType = "proxysql" - // LoadBalancerPGBouncer represents PGBouncer load balancer. - LoadBalancerPGBouncer LoadBalancerType = "pgbouncer" // AppStateUnknown is an unknown state. AppStateUnknown AppState = "unknown" // AppStateInit is a initializing state. @@ -45,6 +36,22 @@ const ( AppStateReady AppState = "ready" // AppStateError is an error state. AppStateError AppState = "error" + // AppStateRestoring is a restoring state. + AppStateRestoring AppState = "restoring" + + // ExposeTypeInternal is an internal expose type. + ExposeTypeInternal ExposeType = "internal" + // ExposeTypeExternal is an external expose type. + ExposeTypeExternal ExposeType = "external" + + // ProxyTypeMongos is a mongos proxy type. + ProxyTypeMongos ProxyType = "mongos" + // ProxyTypeHAProxy is a HAProxy proxy type. + ProxyTypeHAProxy ProxyType = "haproxy" + // ProxyTypeProxySQL is a ProxySQL proxy type. + ProxyTypeProxySQL ProxyType = "proxysql" + // ProxyTypePGBouncer is a PGBouncer proxy type. + ProxyTypePGBouncer ProxyType = "pgbouncer" ) type ( @@ -53,97 +60,6 @@ type ( LoadBalancerType string // AppState is used to represent cluster's state. AppState string - // DatabaseSpec defines the desired state of Database. - DatabaseSpec struct { - // Database type stands for supported databases by the PMM API - // Now it's pxc or psmdb types but we can extend it. - Database EngineType `json:"databaseType"` - // DatabaseVersion sets from version service and uses the recommended version - // by default. - DatabaseImage string `json:"databaseImage"` - // DatabaseConfig contains a config settings for the specified database. - DatabaseConfig string `json:"databaseConfig"` - // SecretsName contains name of a secrets file for a database cluster. - SecretsName string `json:"secretsName,omitempty"` - // Pause represents is a cluster paused or not. - Pause bool `json:"pause,omitempty"` - // ClusterSize is amount of nodes that required for the cluster. - // A database starts in cluster mode if clusterSize >= 3. - ClusterSize int32 `json:"clusterSize"` - // LoadBalancer contains a load balancer settings. For PXC it's haproxy - // or proxysql. For PSMDB it's mongos. - LoadBalancer LoadBalancerSpec `json:"loadBalancer,omitempty"` - // Monitoring contains a monitoring settings. - Monitoring MonitoringSpec `json:"monitoring,omitempty"` - // DBInstance represents resource requests for a database cluster. - DBInstance DBInstanceSpec `json:"dbInstance"` - // Backup contains backup settings. - Backup *BackupSpec `json:"backup,omitempty"` - // DataSource defines a data source for a new cluster - DataSource *BackupSource `json:"dataSource,omitempty"` - } - // LoadBalancerSpec contains a load balancer settings. For PXC it's haproxy - // or proxysql. For PSMDB it's mongos. - LoadBalancerSpec struct { - Type LoadBalancerType `json:"type,omitempty"` - ExposeType corev1.ServiceType `json:"exposeType,omitempty"` - Image string `json:"image,omitempty"` - Size int32 `json:"size,omitempty"` - Configuration string `json:"configuration,omitempty"` - LoadBalancerSourceRanges []string `json:"loadBalancerSourceRanges,omitempty"` - Annotations map[string]string `json:"annotations,omitempty"` - TrafficPolicy corev1.ServiceExternalTrafficPolicyType `json:"trafficPolicy,omitempty"` - Resources corev1.ResourceRequirements `json:"resources,omitempty"` - } - // MonitoringSpec contains monitoring settings. - MonitoringSpec struct { - PMM *PMMSpec `json:"pmm,omitempty"` - ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"` - Resources corev1.ResourceRequirements `json:"resources,omitempty"` - RuntimeClassName *string `json:"runtimeClassName,omitempty"` - ContainerSecurityContext *corev1.SecurityContext `json:"containerSecurityContext,omitempty"` - } - // PMMSpec contains PMM settings. - PMMSpec struct { - Image string `json:"image,omitempty"` - ServerHost string `json:"serverHost,omitempty"` - ServerUser string `json:"serverUser,omitempty"` - PublicAddress string `json:"publicAddress,omitempty"` - Login string `json:"login,omitempty"` - Password string `json:"password,omitempty"` - } - // DBInstanceSpec represents resource requests for database cluster. - DBInstanceSpec struct { - CPU resource.Quantity `json:"cpu,omitempty"` - Memory resource.Quantity `json:"memory,omitempty"` - DiskSize resource.Quantity `json:"diskSize,omitempty"` - StorageClassName *string `json:"storageClassName,omitempty"` - } - // BackupSpec contains backup settings. - BackupSpec struct { - Enabled bool `json:"enabled,omitempty"` - Image string `json:"image,omitempty"` - InitImage string `json:"initImage,omitempty"` - ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` - ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"` - Schedule []BackupSchedule `json:"schedule,omitempty"` - ServiceAccountName string `json:"serviceAccountName,omitempty"` - ContainerSecurityContext *corev1.SecurityContext `json:"containerSecurityContext,omitempty"` - Resources corev1.ResourceRequirements `json:"resources,omitempty"` - Storages map[string]*BackupStorageSpec `json:"storages,omitempty"` - Annotations map[string]string `json:"annotations,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - } - // BackupSchedule represents set of settings to configure backup schedule. - BackupSchedule struct { - Name string `json:"name,omitempty"` - Enabled bool `json:"enabled,omitempty"` - Schedule string `json:"schedule,omitempty"` - Keep int `json:"keep,omitempty"` - StorageName string `json:"storageName,omitempty"` - CompressionType compress.CompressionType `json:"compressionType,omitempty"` - CompressionLevel *int `json:"compressionLevel,omitempty"` - } // BackupStorageProviderSpec represents set of settings to configure cloud provider. BackupStorageProviderSpec struct { // A container name is a valid DNS name that conforms to the Azure naming rules. @@ -159,52 +75,168 @@ type ( // Hot (Frequently accessed or modified data), Cool (Infrequently accessed or modified data), Archive (Rarely accessed or modified data) for Azure. StorageClass string `json:"storageClass,omitempty"` } - // BackupStorageSpec represents set of settings to configure backup storage. - BackupStorageSpec struct { - Type BackupStorageType `json:"type"` - Volume *VolumeSpec `json:"volumeSpec,omitempty"` - StorageProvider *BackupStorageProviderSpec `json:"storageProvider,omitempty"` - NodeSelector map[string]string `json:"nodeSelector,omitempty"` - Resources corev1.ResourceRequirements `json:"resources,omitempty"` - Affinity *corev1.Affinity `json:"affinity,omitempty"` - Tolerations []corev1.Toleration `json:"tolerations,omitempty"` - Annotations map[string]string `json:"annotations,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - SchedulerName string `json:"schedulerName,omitempty"` - PriorityClassName string `json:"priorityClassName,omitempty"` - PodSecurityContext *corev1.PodSecurityContext `json:"podSecurityContext,omitempty"` - ContainerSecurityContext *corev1.SecurityContext `json:"containerSecurityContext,omitempty"` - RuntimeClassName *string `json:"runtimeClassName,omitempty"` - VerifyTLS *bool `json:"verifyTLS,omitempty"` - } - // VolumeSpec represents a specification to configure volume for underlying database. - VolumeSpec struct { - // EmptyDir to use as data volume for mysql. EmptyDir represents a temporary - // directory that shares a pod's lifetime. - // +optional - EmptyDir *corev1.EmptyDirVolumeSource `json:"emptyDir,omitempty"` - - // HostPath to use as data volume for mysql. HostPath represents a - // pre-existing file or directory on the host machine that is directly - // exposed to the container. - // +optional - HostPath *corev1.HostPathVolumeSource `json:"hostPath,omitempty"` - - // PersistentVolumeClaim to specify PVC spec for the volume for mysql data. - // It has the highest level of precedence, followed by HostPath and - // EmptyDir. And represents the PVC specification. - // +optional - PersistentVolumeClaim *corev1.PersistentVolumeClaimSpec `json:"persistentVolumeClaim,omitempty"` - } ) -// DatabaseClusterStatus defines the observed state of Database. +// Storage is the storage configuration. +type Storage struct { + // Size is the size of the persistent volume claim + Size resource.Quantity `json:"size"` + // Class is the storage class to use for the persistent volume claim + Class *string `json:"class,omitempty"` +} + +// Resources are the resource requirements. +type Resources struct { + // CPU is the CPU resource requirements + CPU resource.Quantity `json:"cpu,omitempty"` + // Memory is the memory resource requirements + Memory resource.Quantity `json:"memory,omitempty"` +} + +// Engine is the engine configuration. +type Engine struct { + // Type is the engine type + Type EngineType `json:"type"` + // Version is the engine version + Version string `json:"version,omitempty"` + // Replicas is the number of engine replicas + Replicas int32 `json:"replicas,omitempty"` + // Storage is the engine storage configuration + Storage Storage `json:"storage"` + // Resources are the resource limits for each engine replica. + // If not set, resource limits are not imposed + Resources Resources `json:"resources,omitempty"` + // Config is the engine configuration + Config string `json:"config,omitempty"` +} + +// ExposeType is the expose type. +type ExposeType string + +// Expose is the expose configuration. +type Expose struct { + // Type is the expose type, can be internal or external + // +kubebuilder:validation:Enum:=internal;external + // +kubebuilder:default:=internal + Type ExposeType `json:"type,omitempty"` + // IPSourceRanges is the list of IP source ranges (CIDR notation) + // to allow access from. If not set, there is no limitations + IPSourceRanges []string `json:"ipSourceRanges,omitempty"` +} + +// ProxyType is the proxy type. +type ProxyType string + +// Proxy is the proxy configuration. +type Proxy struct { + // Type is the proxy type + // +kubebuilder:validation:Enum:=mongos;haproxy;proxysql;pgbouncer + Type ProxyType `json:"type,omitempty"` + // Replicas is the number of proxy replicas + Replicas *int32 `json:"replicas,omitempty"` + // Config is the proxy configuration + Config string `json:"config,omitempty"` + // Expose is the proxy expose configuration + Expose Expose `json:"expose,omitempty"` + // Resources are the resource limits for each proxy replica. + // If not set, resource limits are not imposed + Resources Resources `json:"resources,omitempty"` +} + +// DataSource is the data source configuration. +type DataSource struct { + // BackupName is the name of the backup from backup location to use + BackupName string `json:"backupName"` + // ObjectStorageName is the name of the ObjectStorage CR that defines the + // storage location + ObjectStorageName string `json:"objectStorageName"` +} + +// BackupSchedule is the backup schedule configuration. +type BackupSchedule struct { + // Enabled is a flag to enable the schedule + Enabled bool `json:"enabled"` + // Name is the name of the schedule + Name string `json:"name"` + // RetentionCopies is the number of backup copies to retain + RetentionCopies int32 `json:"retentionCopies,omitempty"` + // Schedule is the cron schedule + Schedule string `json:"schedule"` + // ObjectStorageName is the name of the ObjectStorage CR that defines the + // storage location + ObjectStorageName string `json:"objectStorageName"` +} + +// Backup is the backup configuration. +type Backup struct { + // Enabled is a flag to enable backups + Enabled bool `json:"enabled"` + // Schedules is a list of backup schedules + Schedules []BackupSchedule `json:"schedules,omitempty"` +} + +// PMMSpec contains PMM settings. +type PMMSpec struct { + Image string `json:"image,omitempty"` + ServerHost string `json:"serverHost,omitempty"` + ServerUser string `json:"serverUser,omitempty"` + PublicAddress string `json:"publicAddress,omitempty"` + Login string `json:"login,omitempty"` + Password string `json:"password,omitempty"` +} + +// Monitoring contains monitoring settings. +type Monitoring struct { + // Enabled is a flag to enable monitoring + Enabled bool `json:"enabled"` + PMM *PMMSpec `json:"pmm,omitempty"` + Resources corev1.ResourceRequirements `json:"resources,omitempty"` + //nolint:godox,dupword,gocritic + // TODO migrate to the MonitoringConfig CR approach. + //// MonitoringConfigName is the name of the MonitoringConfig CR that defines + //// the monitoring configuration + //MonitoringConfigName string `json:"monitoringConfigName,omitempty"` + //// Resources are the resource requirements for the monitoring container. + //// If not set, resource limits are not imposed + //Resources Resources `json:"resources,omitempty"` +} + +// DatabaseClusterSpec defines the desired state of DatabaseCluster. +type DatabaseClusterSpec struct { + // Paused is a flag to stop the cluster + Paused bool `json:"paused,omitempty"` + // Engine is the database engine specification + Engine Engine `json:"engine"` + // Proxy is the proxy specification. If not set, an appropriate + // proxy specification will be applied for the given engine. A + // common use case for setting this field is to control the + // external access to the database cluster. + Proxy Proxy `json:"proxy,omitempty"` + // AdminUserSecretName is the name of the secret that contains the admin + // user credentials + AdminUserSecretName string `json:"adminSecretName,omitempty"` + // DataSource defines a data source for bootstraping a new cluster + DataSource *DataSource `json:"dataSource,omitempty"` + // Backup is the backup specification + Backup Backup `json:"backup,omitempty"` + // Monitoring is the monitoring specification + Monitoring Monitoring `json:"monitoring,omitempty"` +} + +// DatabaseClusterStatus defines the observed state of DatabaseCluster. type DatabaseClusterStatus struct { - Ready int32 `json:"ready,omitempty"` - Size int32 `json:"size,omitempty"` - State AppState `json:"status,omitempty"` - Host string `json:"host,omitempty"` - Message string `json:"message,omitempty"` + // Status is the status of the cluster + Status AppState `json:"status,omitempty"` + // Hostname is the hostname where the cluster can be reached + Hostname string `json:"hostname,omitempty"` + // Port is the port where the cluster can be reached + Port int32 `json:"port,omitempty"` + // Ready is the number of ready pods + Ready int32 `json:"ready,omitempty"` + // Size is the total number of pods + Size int32 `json:"size,omitempty"` + // Message is extra information about the cluster + Message string `json:"message,omitempty"` } //+kubebuilder:object:root=true @@ -213,21 +245,21 @@ type DatabaseClusterStatus struct { // +kubebuilder:printcolumn:name="Size",type="string",JSONPath=".status.size" // +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.ready" // +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status" -// +kubebuilder:printcolumn:name="ENDPOINT",type="string",JSONPath=".status.host" +// +kubebuilder:printcolumn:name="Hostname",type="string",JSONPath=".status.hostname" // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" -// DatabaseCluster is the Schema for the databases API. +// DatabaseCluster is the Schema for the databaseclusters API. type DatabaseCluster struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec DatabaseSpec `json:"spec,omitempty"` + Spec DatabaseClusterSpec `json:"spec,omitempty"` Status DatabaseClusterStatus `json:"status,omitempty"` } //+kubebuilder:object:root=true -// DatabaseClusterList contains a list of Database. +// DatabaseClusterList contains a list of DatabaseCluster. type DatabaseClusterList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` diff --git a/api/v1alpha1/databaseengine_types.go b/api/v1alpha1/databaseengine_types.go index 259264aa1..1c1effff8 100644 --- a/api/v1alpha1/databaseengine_types.go +++ b/api/v1alpha1/databaseengine_types.go @@ -16,6 +16,9 @@ package v1alpha1 import ( + "sort" + + goversion "github.com/hashicorp/go-version" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -26,12 +29,22 @@ const ( DBEngineStateInstalling EngineState = "installing" // DBEngineStateInstalled represents the state of engine when underlying operator is installed. DBEngineStateInstalled EngineState = "installed" + // DatabaseEnginePXC represents engine type for PXC clusters. DatabaseEnginePXC EngineType = "pxc" // DatabaseEnginePSMDB represents engine type for PSMDB clusters. DatabaseEnginePSMDB EngineType = "psmdb" // DatabaseEnginePostgresql represents engine type for Postgresql clusters. DatabaseEnginePostgresql EngineType = "postgresql" + + // DBEngineComponentRecommended represents recommended component status. + DBEngineComponentRecommended ComponentStatus = "recommended" + // DBEngineComponentAvailable represents available component status. + DBEngineComponentAvailable ComponentStatus = "available" + // DBEngineComponentUnavailable represents unavailable component status. + DBEngineComponentUnavailable ComponentStatus = "unavailable" + // DBEngineComponentUnsupported represents unsupported component status. + DBEngineComponentUnsupported ComponentStatus = "unsupported" ) type ( @@ -61,7 +74,7 @@ type DatabaseEngineStatus struct { //+kubebuilder:resource:shortName=dbengine; //+kubebuilder:printcolumn:name="Type",type="string",JSONPath=".spec.type" //+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status" -//+kubebuilder:printcolumn:name="Version",type="string",JSONPath=".status.version" +//+kubebuilder:printcolumn:name="Operator Version",type="string",JSONPath=".status.operatorVersion" // DatabaseEngine is the Schema for the databaseengines API. type DatabaseEngine struct { @@ -83,29 +96,128 @@ type DatabaseEngineList struct { // Versions struct represents available versions of database engine components. type Versions struct { - Engine map[string]*Component `json:"engine,omitempty"` - Backup map[string]*Component `json:"backup,omitempty"` - Proxy map[string]map[string]*Component `json:"proxy,omitempty"` - Tools map[string]map[string]*Component `json:"tools,omitempty"` + Engine ComponentsMap `json:"engine,omitempty"` + Backup ComponentsMap `json:"backup,omitempty"` + Proxy map[ProxyType]ComponentsMap `json:"proxy,omitempty"` + Tools map[string]ComponentsMap `json:"tools,omitempty"` } +// ComponentsMap is a map of database engine components. +type ComponentsMap map[string]*Component + +// ComponentStatus represents status of the database engine component. +type ComponentStatus string + // Component contains information of the database engine component. // Database Engine component can be database engine, database proxy or tools image path. type Component struct { - Critical bool `json:"critical,omitempty"` - ImageHash string `json:"imageHash,omitempty"` - ImagePath string `json:"imagePath,omitempty"` - Status string `json:"status,omitempty"` + Critical bool `json:"critical,omitempty"` + ImageHash string `json:"imageHash,omitempty"` + ImagePath string `json:"imagePath,omitempty"` + Status ComponentStatus `json:"status,omitempty"` } -// RecommendedBackupImage returns the recommended image for a backup component. -func (d DatabaseEngine) RecommendedBackupImage() string { - for _, component := range d.Status.AvailableVersions.Backup { - if component.Status == "recommended" { - return component.ImagePath +// FilterStatus returns a new ComponentsMap with components filtered by status. +func (c ComponentsMap) FilterStatus(statuses ...ComponentStatus) ComponentsMap { + result := make(ComponentsMap) + for version, component := range c { + for _, status := range statuses { + if component.Status == status { + result[version] = component + } + } + } + return result +} + +// GetSortedVersions returns a sorted slice of versions. Versions are sorted in +// descending order. Most recent version is first. +func (c ComponentsMap) GetSortedVersions() []string { + versions := make(goversion.Collection, 0, len(c)) + for version := range c { + v, err := goversion.NewVersion(version) + if err != nil { + continue + } + versions = append(versions, v) + } + sort.Sort(versions) + + // Reverse order and return the original version strings. + result := make([]string, 0, len(versions)) + for i := len(versions) - 1; i >= 0; i-- { + result = append(result, versions[i].Original()) + } + + return result +} + +// GetAllowedVersionsSorted returns a sorted slice of allowed versions. +// An allowed version is a version whose status is either recommended or +// available. Allowed versions are sorted by status, with recommended versions +// first, followed by available versions. Versions with the same status are +// sorted by version in descending order. Most recent version is first. +func (c ComponentsMap) GetAllowedVersionsSorted() []string { + recommendedComponents := c.FilterStatus(DBEngineComponentRecommended) + recommendedVersions := recommendedComponents.GetSortedVersions() + + availableComponents := c.FilterStatus(DBEngineComponentAvailable) + availableVersions := availableComponents.GetSortedVersions() + + return append(recommendedVersions, availableVersions...) +} + +// BestVersion returns the best version for the components map. +func (c ComponentsMap) BestVersion() string { + allowedVersions := c.GetAllowedVersionsSorted() + return allowedVersions[0] +} + +// BestEngineVersion returns the best engine version for the database engine. +func (d DatabaseEngine) BestEngineVersion() string { + return d.Status.AvailableVersions.Engine.BestVersion() +} + +// BestBackupVersion returns the best backup version for a given engine version. +func (d DatabaseEngine) BestBackupVersion(engineVersion string) string { + switch d.Spec.Type { + case DatabaseEnginePXC: + engineGoVersion, err := goversion.NewVersion(engineVersion) + if err != nil { + return "" + } + + v8, err := goversion.NewVersion("8.0.0") + if err != nil { + return "" + } + + engineIsV8 := engineGoVersion.GreaterThanOrEqual(v8) + allowedVersions := d.Status.AvailableVersions.Backup.GetAllowedVersionsSorted() + for _, version := range allowedVersions { + v, err := goversion.NewVersion(version) + if err != nil { + continue + } + if !engineIsV8 && v.GreaterThanOrEqual(v8) { + continue + } + if engineIsV8 && v.LessThan(v8) { + continue + } + return version + } + return "" + case DatabaseEnginePSMDB: + return d.Status.AvailableVersions.Backup.BestVersion() + case DatabaseEnginePostgresql: + if d.Status.AvailableVersions.Backup[engineVersion] == nil { + return "" } + return engineVersion + default: + return "" } - return "" } func init() { diff --git a/api/v1alpha1/objectstorage_types.go b/api/v1alpha1/objectstorage_types.go new file mode 100644 index 000000000..1dd43ae9c --- /dev/null +++ b/api/v1alpha1/objectstorage_types.go @@ -0,0 +1,71 @@ +// everest-operator +// Copyright (C) 2022 Percona LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + // ObjectStorageTypeS3 is a type of S3 object storage. + ObjectStorageTypeS3 ObjectStorageType = "s3" +) + +// ObjectStorageType is a type of object storage. +type ObjectStorageType string + +// ObjectStorageSpec defines the desired state of ObjectStorage. +type ObjectStorageSpec struct { + // Type is a type of object storage. Currently only S3 is supported. + // +kubebuilder:validation:Enum=s3 + Type ObjectStorageType `json:"type"` + // Bucket is a name of bucket. + Bucket string `json:"bucket"` + // Region is a region where the bucket is located. + Region string `json:"region"` + // EndpointURL is an endpoint URL of object storage. + EndpointURL string `json:"endpointURL"` + // CredentialsSecretName is the name of the secret with credentials. + CredentialsSecretName string `json:"credentialsSecretName"` +} + +// ObjectStorageStatus defines the observed state of ObjectStorage. +type ObjectStorageStatus struct{} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// ObjectStorage is the Schema for the objectstorages API. +type ObjectStorage struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ObjectStorageSpec `json:"spec,omitempty"` + Status ObjectStorageStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// ObjectStorageList contains a list of ObjectStorage. +type ObjectStorageList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ObjectStorage `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ObjectStorage{}, &ObjectStorageList{}) +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 1c43a21ad..5912032e7 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -21,21 +21,35 @@ package v1alpha1 import ( - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/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 *BackupSchedule) DeepCopyInto(out *BackupSchedule) { +func (in *Backup) DeepCopyInto(out *Backup) { *out = *in - if in.CompressionLevel != nil { - in, out := &in.CompressionLevel, &out.CompressionLevel - *out = new(int) - **out = **in + if in.Schedules != nil { + in, out := &in.Schedules, &out.Schedules + *out = make([]BackupSchedule, len(*in)) + copy(*out, *in) } } +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Backup. +func (in *Backup) DeepCopy() *Backup { + if in == nil { + return nil + } + out := new(Backup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupSchedule) DeepCopyInto(out *BackupSchedule) { + *out = *in +} + // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupSchedule. func (in *BackupSchedule) DeepCopy() *BackupSchedule { if in == nil { @@ -71,68 +85,6 @@ func (in *BackupSource) DeepCopy() *BackupSource { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackupSpec) DeepCopyInto(out *BackupSpec) { - *out = *in - if in.ImagePullSecrets != nil { - in, out := &in.ImagePullSecrets, &out.ImagePullSecrets - *out = make([]v1.LocalObjectReference, len(*in)) - copy(*out, *in) - } - if in.Schedule != nil { - in, out := &in.Schedule, &out.Schedule - *out = make([]BackupSchedule, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.ContainerSecurityContext != nil { - in, out := &in.ContainerSecurityContext, &out.ContainerSecurityContext - *out = new(v1.SecurityContext) - (*in).DeepCopyInto(*out) - } - in.Resources.DeepCopyInto(&out.Resources) - if in.Storages != nil { - in, out := &in.Storages, &out.Storages - *out = make(map[string]*BackupStorageSpec, len(*in)) - for key, val := range *in { - var outVal *BackupStorageSpec - if val == nil { - (*out)[key] = nil - } else { - in, out := &val, &outVal - *out = new(BackupStorageSpec) - (*in).DeepCopyInto(*out) - } - (*out)[key] = outVal - } - } - if in.Annotations != nil { - in, out := &in.Annotations, &out.Annotations - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupSpec. -func (in *BackupSpec) DeepCopy() *BackupSpec { - if in == nil { - return nil - } - out := new(BackupSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BackupStorageProviderSpec) DeepCopyInto(out *BackupStorageProviderSpec) { *out = *in @@ -149,118 +101,60 @@ func (in *BackupStorageProviderSpec) DeepCopy() *BackupStorageProviderSpec { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackupStorageSpec) DeepCopyInto(out *BackupStorageSpec) { +func (in *Component) DeepCopyInto(out *Component) { *out = *in - if in.Volume != nil { - in, out := &in.Volume, &out.Volume - *out = new(VolumeSpec) - (*in).DeepCopyInto(*out) - } - if in.StorageProvider != nil { - in, out := &in.StorageProvider, &out.StorageProvider - *out = new(BackupStorageProviderSpec) - **out = **in - } - if in.NodeSelector != nil { - in, out := &in.NodeSelector, &out.NodeSelector - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - in.Resources.DeepCopyInto(&out.Resources) - if in.Affinity != nil { - in, out := &in.Affinity, &out.Affinity - *out = new(v1.Affinity) - (*in).DeepCopyInto(*out) - } - if in.Tolerations != nil { - in, out := &in.Tolerations, &out.Tolerations - *out = make([]v1.Toleration, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Annotations != nil { - in, out := &in.Annotations, &out.Annotations - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.PodSecurityContext != nil { - in, out := &in.PodSecurityContext, &out.PodSecurityContext - *out = new(v1.PodSecurityContext) - (*in).DeepCopyInto(*out) - } - if in.ContainerSecurityContext != nil { - in, out := &in.ContainerSecurityContext, &out.ContainerSecurityContext - *out = new(v1.SecurityContext) - (*in).DeepCopyInto(*out) - } - if in.RuntimeClassName != nil { - in, out := &in.RuntimeClassName, &out.RuntimeClassName - *out = new(string) - **out = **in - } - if in.VerifyTLS != nil { - in, out := &in.VerifyTLS, &out.VerifyTLS - *out = new(bool) - **out = **in - } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageSpec. -func (in *BackupStorageSpec) DeepCopy() *BackupStorageSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Component. +func (in *Component) DeepCopy() *Component { if in == nil { return nil } - out := new(BackupStorageSpec) + out := new(Component) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Component) DeepCopyInto(out *Component) { - *out = *in +func (in ComponentsMap) DeepCopyInto(out *ComponentsMap) { + { + in := &in + *out = make(ComponentsMap, len(*in)) + for key, val := range *in { + var outVal *Component + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = new(Component) + **out = **in + } + (*out)[key] = outVal + } + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Component. -func (in *Component) DeepCopy() *Component { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComponentsMap. +func (in ComponentsMap) DeepCopy() ComponentsMap { if in == nil { return nil } - out := new(Component) + out := new(ComponentsMap) in.DeepCopyInto(out) - return out + return *out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DBInstanceSpec) DeepCopyInto(out *DBInstanceSpec) { +func (in *DataSource) DeepCopyInto(out *DataSource) { *out = *in - out.CPU = in.CPU.DeepCopy() - out.Memory = in.Memory.DeepCopy() - out.DiskSize = in.DiskSize.DeepCopy() - if in.StorageClassName != nil { - in, out := &in.StorageClassName, &out.StorageClassName - *out = new(string) - **out = **in - } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DBInstanceSpec. -func (in *DBInstanceSpec) DeepCopy() *DBInstanceSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataSource. +func (in *DataSource) DeepCopy() *DataSource { if in == nil { return nil } - out := new(DBInstanceSpec) + out := new(DataSource) in.DeepCopyInto(out) return out } @@ -510,7 +404,7 @@ func (in *DatabaseClusterRestoreStatus) DeepCopyInto(out *DatabaseClusterRestore } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -527,6 +421,30 @@ func (in *DatabaseClusterRestoreStatus) DeepCopy() *DatabaseClusterRestoreStatus return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DatabaseClusterSpec) DeepCopyInto(out *DatabaseClusterSpec) { + *out = *in + in.Engine.DeepCopyInto(&out.Engine) + in.Proxy.DeepCopyInto(&out.Proxy) + if in.DataSource != nil { + in, out := &in.DataSource, &out.DataSource + *out = new(DataSource) + **out = **in + } + in.Backup.DeepCopyInto(&out.Backup) + in.Monitoring.DeepCopyInto(&out.Monitoring) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseClusterSpec. +func (in *DatabaseClusterSpec) DeepCopy() *DatabaseClusterSpec { + if in == nil { + return nil + } + out := new(DatabaseClusterSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DatabaseClusterStatus) DeepCopyInto(out *DatabaseClusterStatus) { *out = *in @@ -638,63 +556,44 @@ func (in *DatabaseEngineStatus) DeepCopy() *DatabaseEngineStatus { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DatabaseSpec) DeepCopyInto(out *DatabaseSpec) { +func (in *Engine) DeepCopyInto(out *Engine) { *out = *in - in.LoadBalancer.DeepCopyInto(&out.LoadBalancer) - in.Monitoring.DeepCopyInto(&out.Monitoring) - in.DBInstance.DeepCopyInto(&out.DBInstance) - if in.Backup != nil { - in, out := &in.Backup, &out.Backup - *out = new(BackupSpec) - (*in).DeepCopyInto(*out) - } - if in.DataSource != nil { - in, out := &in.DataSource, &out.DataSource - *out = new(BackupSource) - (*in).DeepCopyInto(*out) - } + in.Storage.DeepCopyInto(&out.Storage) + in.Resources.DeepCopyInto(&out.Resources) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseSpec. -func (in *DatabaseSpec) DeepCopy() *DatabaseSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Engine. +func (in *Engine) DeepCopy() *Engine { if in == nil { return nil } - out := new(DatabaseSpec) + out := new(Engine) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LoadBalancerSpec) DeepCopyInto(out *LoadBalancerSpec) { +func (in *Expose) DeepCopyInto(out *Expose) { *out = *in - if in.LoadBalancerSourceRanges != nil { - in, out := &in.LoadBalancerSourceRanges, &out.LoadBalancerSourceRanges + if in.IPSourceRanges != nil { + in, out := &in.IPSourceRanges, &out.IPSourceRanges *out = make([]string, len(*in)) copy(*out, *in) } - if in.Annotations != nil { - in, out := &in.Annotations, &out.Annotations - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - in.Resources.DeepCopyInto(&out.Resources) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancerSpec. -func (in *LoadBalancerSpec) DeepCopy() *LoadBalancerSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Expose. +func (in *Expose) DeepCopy() *Expose { if in == nil { return nil } - out := new(LoadBalancerSpec) + out := new(Expose) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *MonitoringSpec) DeepCopyInto(out *MonitoringSpec) { +func (in *Monitoring) DeepCopyInto(out *Monitoring) { *out = *in if in.PMM != nil { in, out := &in.PMM, &out.PMM @@ -702,24 +601,103 @@ func (in *MonitoringSpec) DeepCopyInto(out *MonitoringSpec) { **out = **in } in.Resources.DeepCopyInto(&out.Resources) - if in.RuntimeClassName != nil { - in, out := &in.RuntimeClassName, &out.RuntimeClassName - *out = new(string) - **out = **in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Monitoring. +func (in *Monitoring) DeepCopy() *Monitoring { + if in == nil { + return nil } - if in.ContainerSecurityContext != nil { - in, out := &in.ContainerSecurityContext, &out.ContainerSecurityContext - *out = new(v1.SecurityContext) - (*in).DeepCopyInto(*out) + out := new(Monitoring) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectStorage) DeepCopyInto(out *ObjectStorage) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStorage. +func (in *ObjectStorage) DeepCopy() *ObjectStorage { + if in == nil { + return nil + } + out := new(ObjectStorage) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ObjectStorage) 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 *ObjectStorageList) DeepCopyInto(out *ObjectStorageList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ObjectStorage, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStorageList. +func (in *ObjectStorageList) DeepCopy() *ObjectStorageList { + if in == nil { + return nil + } + out := new(ObjectStorageList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ObjectStorageList) 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 *ObjectStorageSpec) DeepCopyInto(out *ObjectStorageSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStorageSpec. +func (in *ObjectStorageSpec) DeepCopy() *ObjectStorageSpec { + if in == nil { + return nil } + out := new(ObjectStorageSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectStorageStatus) DeepCopyInto(out *ObjectStorageStatus) { + *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MonitoringSpec. -func (in *MonitoringSpec) DeepCopy() *MonitoringSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStorageStatus. +func (in *ObjectStorageStatus) DeepCopy() *ObjectStorageStatus { if in == nil { return nil } - out := new(MonitoringSpec) + out := new(ObjectStorageStatus) in.DeepCopyInto(out) return out } @@ -739,12 +717,72 @@ func (in *PMMSpec) DeepCopy() *PMMSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Proxy) DeepCopyInto(out *Proxy) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + in.Expose.DeepCopyInto(&out.Expose) + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Proxy. +func (in *Proxy) DeepCopy() *Proxy { + if in == nil { + return nil + } + out := new(Proxy) + 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.CPU = in.CPU.DeepCopy() + out.Memory = in.Memory.DeepCopy() +} + +// 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 *Storage) DeepCopyInto(out *Storage) { + *out = *in + out.Size = in.Size.DeepCopy() + if in.Class != nil { + in, out := &in.Class, &out.Class + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Storage. +func (in *Storage) DeepCopy() *Storage { + if in == nil { + return nil + } + out := new(Storage) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Versions) DeepCopyInto(out *Versions) { *out = *in if in.Engine != nil { in, out := &in.Engine, &out.Engine - *out = make(map[string]*Component, len(*in)) + *out = make(ComponentsMap, len(*in)) for key, val := range *in { var outVal *Component if val == nil { @@ -759,7 +797,7 @@ func (in *Versions) DeepCopyInto(out *Versions) { } if in.Backup != nil { in, out := &in.Backup, &out.Backup - *out = make(map[string]*Component, len(*in)) + *out = make(ComponentsMap, len(*in)) for key, val := range *in { var outVal *Component if val == nil { @@ -774,14 +812,14 @@ func (in *Versions) DeepCopyInto(out *Versions) { } if in.Proxy != nil { in, out := &in.Proxy, &out.Proxy - *out = make(map[string]map[string]*Component, len(*in)) + *out = make(map[ProxyType]ComponentsMap, len(*in)) for key, val := range *in { var outVal map[string]*Component if val == nil { (*out)[key] = nil } else { in, out := &val, &outVal - *out = make(map[string]*Component, len(*in)) + *out = make(ComponentsMap, len(*in)) for key, val := range *in { var outVal *Component if val == nil { @@ -799,14 +837,14 @@ func (in *Versions) DeepCopyInto(out *Versions) { } if in.Tools != nil { in, out := &in.Tools, &out.Tools - *out = make(map[string]map[string]*Component, len(*in)) + *out = make(map[string]ComponentsMap, len(*in)) for key, val := range *in { var outVal map[string]*Component if val == nil { (*out)[key] = nil } else { in, out := &val, &outVal - *out = make(map[string]*Component, len(*in)) + *out = make(ComponentsMap, len(*in)) for key, val := range *in { var outVal *Component if val == nil { @@ -833,33 +871,3 @@ func (in *Versions) DeepCopy() *Versions { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeSpec) DeepCopyInto(out *VolumeSpec) { - *out = *in - if in.EmptyDir != nil { - in, out := &in.EmptyDir, &out.EmptyDir - *out = new(v1.EmptyDirVolumeSource) - (*in).DeepCopyInto(*out) - } - if in.HostPath != nil { - in, out := &in.HostPath, &out.HostPath - *out = new(v1.HostPathVolumeSource) - (*in).DeepCopyInto(*out) - } - if in.PersistentVolumeClaim != nil { - in, out := &in.PersistentVolumeClaim, &out.PersistentVolumeClaim - *out = new(v1.PersistentVolumeClaimSpec) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSpec. -func (in *VolumeSpec) DeepCopy() *VolumeSpec { - if in == nil { - return nil - } - out := new(VolumeSpec) - in.DeepCopyInto(out) - return out -} diff --git a/bundle.Dockerfile b/bundle.Dockerfile index 457b2056b..5a3f80fb8 100644 --- a/bundle.Dockerfile +++ b/bundle.Dockerfile @@ -6,7 +6,7 @@ LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ LABEL operators.operatorframework.io.bundle.package.v1=everest-operator LABEL operators.operatorframework.io.bundle.channels.v1=alpha -LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.27.0 +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.30.0 LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v3 diff --git a/bundle/manifests/everest-operator.clusterserviceversion.yaml b/bundle/manifests/everest-operator.clusterserviceversion.yaml index d925bf54f..9ac27f477 100644 --- a/bundle/manifests/everest-operator.clusterserviceversion.yaml +++ b/bundle/manifests/everest-operator.clusterserviceversion.yaml @@ -63,13 +63,28 @@ metadata: "name": "databaseengine-sample" }, "spec": null + }, + { + "apiVersion": "everest.percona.com/v1alpha1", + "kind": "ObjectStorage", + "metadata": { + "labels": { + "app.kubernetes.io/created-by": "everest-operator", + "app.kubernetes.io/instance": "objectstorage-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "objectstorage", + "app.kubernetes.io/part-of": "everest-operator" + }, + "name": "objectstorage-sample" + }, + "spec": null } ] capabilities: Basic Install - createdAt: "2023-07-12T12:08:29Z" - operators.operatorframework.io/builder: operator-sdk-v1.27.0 + createdAt: "2023-07-17T08:27:52Z" + operators.operatorframework.io/builder: operator-sdk-v1.30.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 - name: everest-operator.v0.0.2 + name: everest-operator.v0.0.3 namespace: placeholder spec: apiservicedefinitions: {} @@ -87,7 +102,7 @@ spec: kind: DatabaseClusterRestore name: databaseclusterrestores.everest.percona.com version: v1alpha1 - - description: DatabaseCluster is the Schema for the databases API. + - description: DatabaseCluster is the Schema for the databaseclusters API. displayName: Database Cluster kind: DatabaseCluster name: databaseclusters.everest.percona.com @@ -97,6 +112,11 @@ spec: kind: DatabaseEngine name: databaseengines.everest.percona.com version: v1alpha1 + - description: ObjectStorage is the Schema for the objectstorages API. + displayName: Object Storage + kind: ObjectStorage + name: objectstorages.everest.percona.com + version: v1alpha1 description: Deploy database clusters easily with Everest operator displayName: Everest operator icon: @@ -239,7 +259,19 @@ spec: - patch - update - apiGroups: - - pg.percona.com + - everest.percona.com + resources: + - objectstorages + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - pgv2.percona.com resources: - perconapgclusters verbs: @@ -393,7 +425,7 @@ spec: valueFrom: fieldRef: fieldPath: metadata.annotations['olm.targetNamespaces'] - image: docker.io/percona/everest-operator:0.0.2 + image: docker.io/percona/everest-operator:0.0.3 livenessProbe: httpGet: path: /healthz @@ -487,4 +519,4 @@ spec: provider: name: Percona url: https://percona.com - version: 0.0.2 + version: 0.0.3 diff --git a/bundle/manifests/everest.percona.com_databaseclusters.yaml b/bundle/manifests/everest.percona.com_databaseclusters.yaml index 75dac66c5..af2e5eca6 100644 --- a/bundle/manifests/everest.percona.com_databaseclusters.yaml +++ b/bundle/manifests/everest.percona.com_databaseclusters.yaml @@ -27,8 +27,8 @@ spec: - jsonPath: .status.status name: Status type: string - - jsonPath: .status.host - name: ENDPOINT + - jsonPath: .status.hostname + name: Hostname type: string - jsonPath: .metadata.creationTimestamp name: Age @@ -36,7 +36,7 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: DatabaseCluster is the Schema for the databases API. + description: DatabaseCluster is the Schema for the databaseclusters API. properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -51,2397 +51,129 @@ spec: metadata: type: object spec: - description: DatabaseSpec defines the desired state of Database. + description: DatabaseClusterSpec defines the desired state of DatabaseCluster. properties: + adminSecretName: + description: AdminSecretName is the name of the secret that contains + the admin credentials + type: string backup: - description: Backup contains backup settings. + description: Backup is the backup specification properties: - annotations: - additionalProperties: - type: string - type: object - containerSecurityContext: - description: SecurityContext holds security configuration that - will be applied to a container. Some fields are present in both - SecurityContext and PodSecurityContext. When both are set, - the values in SecurityContext take precedence. - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls whether a - process can gain more privileges than its parent process. - This bool directly controls if the no_new_privs flag will - be set on the container process. AllowPrivilegeEscalation - is true always when the container is: 1) run as Privileged - 2) has CAP_SYS_ADMIN Note that this field cannot be set - when spec.os.name is windows.' - type: boolean - capabilities: - description: The capabilities to add/drop when running containers. - Defaults to the default set of capabilities granted by the - container runtime. Note that this field cannot be set when - spec.os.name is windows. - properties: - add: - description: Added capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - type: object - privileged: - description: Run container in privileged mode. Processes in - privileged containers are essentially equivalent to root - on the host. Defaults to false. Note that this field cannot - be set when spec.os.name is windows. - type: boolean - procMount: - description: procMount denotes the type of proc mount to use - for the containers. The default is DefaultProcMount which - uses the container runtime defaults for readonly paths and - masked paths. This requires the ProcMountType feature flag - to be enabled. Note that this field cannot be set when spec.os.name - is windows. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only root filesystem. - Default is false. Note that this field cannot be set when - spec.os.name is windows. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be set - in PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as a non-root - user. If true, the Kubelet will validate the image at runtime - to ensure that it does not run as UID 0 (root) and fail - to start the container if it does. If unset or false, no - such validation will be performed. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata if - unspecified. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to the container. - If unspecified, the container runtime will allocate a random - SELinux context for each container. May also be set in - PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that applies - to the container. - type: string - role: - description: Role is a SELinux role label that applies - to the container. - type: string - type: - description: Type is a SELinux type label that applies - to the container. - type: string - user: - description: User is a SELinux user label that applies - to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by this container. - If seccomp options are provided at both the pod & container - level, the container options override the pod options. Note - that this field cannot be set when spec.os.name is windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile must - be preconfigured on the node to work. Must be a descending - path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - a - profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile - should be used. Unconfined - no profile should be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings applied to all - containers. If unspecified, the options from the PodSecurityContext - will be used. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name is - linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named - by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name of the - GMSA credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is - alpha-level and will only be honored by components that - enable the WindowsHostProcessContainers feature flag. - Setting this field without the feature flag will result - in errors when validating the Pod. All of a Pod's containers - must have the same effective HostProcess value (it is - not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, if HostProcess - is true then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set in - PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. - type: string - type: object - type: object enabled: + description: Enabled is a flag to enable backups type: boolean - image: - type: string - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image - type: string - imagePullSecrets: - items: - description: LocalObjectReference contains enough information - to let you locate the referenced object inside the same namespace. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - x-kubernetes-map-type: atomic - type: array - initImage: - type: string - labels: - additionalProperties: - type: string - type: object - resources: - description: ResourceRequirements describes the compute resource - requirements. - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only be - set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry in - pod.spec.resourceClaims of the Pod where this field - is used. It makes that resource available inside a - container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. Requests cannot exceed - Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - schedule: + schedules: + description: Schedules is a list of backup schedules items: - description: BackupSchedule represents set of settings to configure - backup schedule. + description: BackupSchedule is the backup schedule configuration. properties: - compressionLevel: - type: integer - compressionType: - type: string enabled: + description: Enabled is a flag to enable the schedule type: boolean - keep: - type: integer name: + description: Name is the name of the schedule type: string - schedule: - type: string - storageName: - type: string - type: object - type: array - serviceAccountName: - type: string - storages: - additionalProperties: - description: BackupStorageSpec represents set of settings to - configure backup storage. - properties: - affinity: - description: Affinity is a group of affinity scheduling - rules. - properties: - nodeAffinity: - description: Describes node affinity scheduling rules - for the pod. - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. - The node that is most preferred is the one with - the greatest sum of weights, i.e. for each node - that meets all of the scheduling requirements - (resource request, requiredDuringScheduling affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and adding - "weight" to the sum if the node matches the corresponding - matchExpressions; the node(s) with the highest - sum are the most preferred. - items: - description: An empty preferred scheduling term - matches all objects with implicit weight 0 (i.e. - it's a no-op). A null preferred scheduling term - matches no objects (i.e. is also a no-op). - properties: - preference: - description: A node selector term, associated - with the corresponding weight. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching - the corresponding nodeSelectorTerm, in the - range 1-100. - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, - the pod will not be scheduled onto the node. If - the affinity requirements specified by this field - cease to be met at some point during pod execution - (e.g. due to an update), the system may or may - not try to eventually evict the pod from its node. - properties: - nodeSelectorTerms: - description: Required. A list of node selector - terms. The terms are ORed. - items: - description: A null or empty node selector - term matches no objects. The requirements - of them are ANDed. The TopologySelectorTerm - type implements a subset of the NodeSelectorTerm. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - x-kubernetes-map-type: atomic - type: array - required: - - nodeSelectorTerms - type: object - x-kubernetes-map-type: atomic - type: object - podAffinity: - description: Describes pod affinity scheduling rules - (e.g. co-locate this pod in the same node, zone, etc. - as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. - The node that is most preferred is the one with - the greatest sum of weights, i.e. for each node - that meets all of the scheduling requirements - (resource request, requiredDuringScheduling affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and adding - "weight" to the sum if the node has pods which - matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched - WeightedPodAffinityTerm fields are added per-node - to find the most preferred node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, - associated with the corresponding weight. - properties: - labelSelector: - description: A label query over a set - of resources, in this case pods. - properties: - matchExpressions: - description: matchExpressions is a - list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set - of namespaces that the term applies - to. The term is applied to the union - of the namespaces selected by this field - and the ones listed in the namespaces - field. null selector and null or empty - namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a - list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's - namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, - the pod will not be scheduled onto the node. If - the affinity requirements specified by this field - cease to be met at some point during pod execution - (e.g. due to a pod label update), the system may - or may not try to eventually evict the pod from - its node. When there are multiple elements, the - lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. - items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) - with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which - a pod of the set of pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set of - namespaces that the term applies to. The - term is applied to the union of the namespaces - selected by this field and the ones listed - in the namespaces field. null selector and - null or empty namespaces list means "this - pod's namespace". An empty selector ({}) - matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static - list of namespace names that the term applies - to. The term is applied to the union of - the namespaces listed in this field and - the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector - means "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose value - of the label with key topologyKey matches - that of any node on which any of the selected - pods is running. Empty topologyKey is not - allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - description: Describes pod anti-affinity scheduling - rules (e.g. avoid putting this pod in the same node, - zone, etc. as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the anti-affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. - The node that is most preferred is the one with - the greatest sum of weights, i.e. for each node - that meets all of the scheduling requirements - (resource request, requiredDuringScheduling anti-affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and adding - "weight" to the sum if the node has pods which - matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched - WeightedPodAffinityTerm fields are added per-node - to find the most preferred node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, - associated with the corresponding weight. - properties: - labelSelector: - description: A label query over a set - of resources, in this case pods. - properties: - matchExpressions: - description: matchExpressions is a - list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set - of namespaces that the term applies - to. The term is applied to the union - of the namespaces selected by this field - and the ones listed in the namespaces - field. null selector and null or empty - namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a - list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's - namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified - by this field are not met at scheduling time, - the pod will not be scheduled onto the node. If - the anti-affinity requirements specified by this - field cease to be met at some point during pod - execution (e.g. due to a pod label update), the - system may or may not try to eventually evict - the pod from its node. When there are multiple - elements, the lists of nodes corresponding to - each podAffinityTerm are intersected, i.e. all - terms must be satisfied. - items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) - with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which - a pod of the set of pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set of - namespaces that the term applies to. The - term is applied to the union of the namespaces - selected by this field and the ones listed - in the namespaces field. null selector and - null or empty namespaces list means "this - pod's namespace". An empty selector ({}) - matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static - list of namespace names that the term applies - to. The term is applied to the union of - the namespaces listed in this field and - the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector - means "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose value - of the label with key topologyKey matches - that of any node on which any of the selected - pods is running. Empty topologyKey is not - allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - annotations: - additionalProperties: - type: string - type: object - containerSecurityContext: - description: SecurityContext holds security configuration - that will be applied to a container. Some fields are present - in both SecurityContext and PodSecurityContext. When - both are set, the values in SecurityContext take precedence. - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls whether - a process can gain more privileges than its parent - process. This bool directly controls if the no_new_privs - flag will be set on the container process. AllowPrivilegeEscalation - is true always when the container is: 1) run as Privileged - 2) has CAP_SYS_ADMIN Note that this field cannot be - set when spec.os.name is windows.' - type: boolean - capabilities: - description: The capabilities to add/drop when running - containers. Defaults to the default set of capabilities - granted by the container runtime. Note that this field - cannot be set when spec.os.name is windows. - properties: - add: - description: Added capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - type: object - privileged: - description: Run container in privileged mode. Processes - in privileged containers are essentially equivalent - to root on the host. Defaults to false. Note that - this field cannot be set when spec.os.name is windows. - type: boolean - procMount: - description: procMount denotes the type of proc mount - to use for the containers. The default is DefaultProcMount - which uses the container runtime defaults for readonly - paths and masked paths. This requires the ProcMountType - feature flag to be enabled. Note that this field cannot - be set when spec.os.name is windows. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only - root filesystem. Default is false. Note that this - field cannot be set when spec.os.name is windows. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be - set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set - when spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as - a non-root user. If true, the Kubelet will validate - the image at runtime to ensure that it does not run - as UID 0 (root) and fail to start the container if - it does. If unset or false, no such validation will - be performed. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata - if unspecified. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name - is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to the - container. If unspecified, the container runtime will - allocate a random SELinux context for each container. May - also be set in PodSecurityContext. If set in both - SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is - windows. - properties: - level: - description: Level is SELinux level label that applies - to the container. - type: string - role: - description: Role is a SELinux role label that applies - to the container. - type: string - type: - description: Type is a SELinux type label that applies - to the container. - type: string - user: - description: User is a SELinux user label that applies - to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by this container. - If seccomp options are provided at both the pod & - container level, the container options override the - pod options. Note that this field cannot be set when - spec.os.name is windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile - defined in a file on the node should be used. - The profile must be preconfigured on the node - to work. Must be a descending path, relative to - the kubelet's configured seccomp profile location. - Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp - profile will be applied. Valid options are: \n - Localhost - a profile defined in a file on the - node should be used. RuntimeDefault - the container - runtime default profile should be used. Unconfined - - no profile should be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings applied to - all containers. If unspecified, the options from the - PodSecurityContext will be used. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set - when spec.os.name is linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA - admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec - named by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name - of the GMSA credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container - should be run as a 'Host Process' container. This - field is alpha-level and will only be honored - by components that enable the WindowsHostProcessContainers - feature flag. Setting this field without the feature - flag will result in errors when validating the - Pod. All of a Pod's containers must have the same - effective HostProcess value (it is not allowed - to have a mix of HostProcess containers and non-HostProcess - containers). In addition, if HostProcess is true - then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the - entrypoint of the container process. Defaults - to the user specified in image metadata if unspecified. - May also be set in PodSecurityContext. If set - in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - type: string - type: object - type: object - labels: - additionalProperties: - type: string - type: object - nodeSelector: - additionalProperties: - type: string - type: object - podSecurityContext: - description: PodSecurityContext holds pod-level security - attributes and common container settings. Some fields - are also present in container.securityContext. Field - values of container.securityContext take precedence over - field values of PodSecurityContext. - properties: - fsGroup: - description: "A special supplemental group that applies - to all containers in a pod. Some volume types allow - the Kubelet to change the ownership of that volume - to be owned by the pod: \n 1. The owning GID will - be the FSGroup 2. The setgid bit is set (new files - created in the volume will be owned by FSGroup) 3. - The permission bits are OR'd with rw-rw---- \n If - unset, the Kubelet will not modify the ownership and - permissions of any volume. Note that this field cannot - be set when spec.os.name is windows." - format: int64 - type: integer - fsGroupChangePolicy: - description: 'fsGroupChangePolicy defines behavior of - changing ownership and permission of the volume before - being exposed inside Pod. This field will only apply - to volume types which support fsGroup based ownership(and - permissions). It will have no effect on ephemeral - volume types such as: secret, configmaps and emptydir. - Valid values are "OnRootMismatch" and "Always". If - not specified, "Always" is used. Note that this field - cannot be set when spec.os.name is windows.' - type: string - runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be - set in SecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. Note that this - field cannot be set when spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as - a non-root user. If true, the Kubelet will validate - the image at runtime to ensure that it does not run - as UID 0 (root) and fail to start the container if - it does. If unset or false, no such validation will - be performed. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata - if unspecified. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence - for that container. Note that this field cannot be - set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to all - containers. If unspecified, the container runtime - will allocate a random SELinux context for each container. May - also be set in SecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. Note that this - field cannot be set when spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that applies - to the container. - type: string - role: - description: Role is a SELinux role label that applies - to the container. - type: string - type: - description: Type is a SELinux type label that applies - to the container. - type: string - user: - description: User is a SELinux user label that applies - to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by the containers - in this pod. Note that this field cannot be set when - spec.os.name is windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile - defined in a file on the node should be used. - The profile must be preconfigured on the node - to work. Must be a descending path, relative to - the kubelet's configured seccomp profile location. - Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp - profile will be applied. Valid options are: \n - Localhost - a profile defined in a file on the - node should be used. RuntimeDefault - the container - runtime default profile should be used. Unconfined - - no profile should be applied." - type: string - required: - - type - type: object - supplementalGroups: - description: A list of groups applied to the first process - run in each container, in addition to the container's - primary GID, the fsGroup (if specified), and group - memberships defined in the container image for the - uid of the container process. If unspecified, no additional - groups are added to any container. Note that group - memberships defined in the container image for the - uid of the container process are still effective, - even if they are not included in this list. Note that - this field cannot be set when spec.os.name is windows. - items: - format: int64 - type: integer - type: array - sysctls: - description: Sysctls hold a list of namespaced sysctls - used for the pod. Pods with unsupported sysctls (by - the container runtime) might fail to launch. Note - that this field cannot be set when spec.os.name is - windows. - items: - description: Sysctl defines a kernel parameter to - be set - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - description: The Windows specific settings applied to - all containers. If unspecified, the options within - a container's SecurityContext will be used. If set - in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name - is linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA - admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec - named by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name - of the GMSA credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container - should be run as a 'Host Process' container. This - field is alpha-level and will only be honored - by components that enable the WindowsHostProcessContainers - feature flag. Setting this field without the feature - flag will result in errors when validating the - Pod. All of a Pod's containers must have the same - effective HostProcess value (it is not allowed - to have a mix of HostProcess containers and non-HostProcess - containers). In addition, if HostProcess is true - then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the - entrypoint of the container process. Defaults - to the user specified in image metadata if unspecified. - May also be set in PodSecurityContext. If set - in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - type: string - type: object - type: object - priorityClassName: + objectStorageName: + description: ObjectStorageName is the name of the ObjectStorage + CR that defines the storage location type: string - resources: - description: ResourceRequirements describes the compute - resource requirements. - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the - DynamicResourceAllocation feature gate. \n This field - is immutable. It can only be set for containers." - items: - description: ResourceClaim references one entry in - PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where - this field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of - compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount - of compute resources required. If Requests is omitted - for a container, it defaults to Limits if that is - explicitly specified, otherwise to an implementation-defined - value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - runtimeClassName: - type: string - schedulerName: - type: string - storageProvider: - description: BackupStorageProviderSpec represents set of - settings to configure cloud provider. - properties: - bucket: - type: string - containerName: - description: A container name is a valid DNS name that - conforms to the Azure naming rules. - type: string - credentialsSecret: - type: string - endpointUrl: - type: string - prefix: - type: string - region: - type: string - storageClass: - description: STANDARD, NEARLINE, COLDLINE, ARCHIVE for - GCP Hot (Frequently accessed or modified data), Cool - (Infrequently accessed or modified data), Archive - (Rarely accessed or modified data) for Azure. - type: string - required: - - credentialsSecret - type: object - tolerations: - items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple - using the matching operator . - properties: - effect: - description: Effect indicates the taint effect to - match. Empty means match all taint effects. When - specified, allowed values are NoSchedule, PreferNoSchedule - and NoExecute. - type: string - key: - description: Key is the taint key that the toleration - applies to. Empty means match all taint keys. If - the key is empty, operator must be Exists; this - combination means to match all values and all keys. - type: string - operator: - description: Operator represents a key's relationship - to the value. Valid operators are Exists and Equal. - Defaults to Equal. Exists is equivalent to wildcard - for value, so that a pod can tolerate all taints - of a particular category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period - of time the toleration (which must be of effect - NoExecute, otherwise this field is ignored) tolerates - the taint. By default, it is not set, which means - tolerate the taint forever (do not evict). Zero - and negative values will be treated as 0 (evict - immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the toleration - matches to. If the operator is Exists, the value - should be empty, otherwise just a regular string. - type: string - type: object - type: array - type: - description: BackupStorageType represents backup storage - type. + retentionCopies: + description: RetentionCopies is the number of backup copies + to retain + format: int32 + type: integer + schedule: + description: Schedule is the cron schedule type: string - verifyTLS: - type: boolean - volumeSpec: - description: VolumeSpec represents a specification to configure - volume for underlying database. - properties: - emptyDir: - description: EmptyDir to use as data volume for mysql. - EmptyDir represents a temporary directory that shares - a pod's lifetime. - properties: - medium: - description: 'medium represents what type of storage - medium should back this directory. The default - is "" which means to use the node''s default medium. - Must be an empty string (default) or Memory. More - info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - description: 'sizeLimit is the total amount of local - storage required for this EmptyDir volume. The - size limit is also applicable for memory medium. - The maximum usage on memory medium EmptyDir would - be the minimum value between the SizeLimit specified - here and the sum of memory limits of all containers - in a pod. The default is nil which means that - the limit is undefined. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - description: HostPath to use as data volume for mysql. - HostPath represents a pre-existing file or directory - on the host machine that is directly exposed to the - container. - properties: - path: - description: 'path of the directory on the host. - If the path is a symlink, it will follow the link - to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - type: - description: 'type for HostPath Volume Defaults - to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - required: - - path - type: object - persistentVolumeClaim: - description: PersistentVolumeClaim to specify PVC spec - for the volume for mysql data. It has the highest - level of precedence, followed by HostPath and EmptyDir. - And represents the PVC specification. - properties: - accessModes: - description: 'accessModes contains the desired access - modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' - items: - type: string - type: array - dataSource: - description: 'dataSource field can be used to specify - either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) If the - provisioner or an external controller can support - the specified data source, it will create a new - volume based on the contents of the specified - data source. When the AnyVolumeDataSource feature - gate is enabled, dataSource contents will be copied - to dataSourceRef, and dataSourceRef contents will - be copied to dataSource when dataSourceRef.namespace - is not specified. If the namespace is specified, - then dataSourceRef will not be copied to dataSource.' - properties: - apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API - group. For any other third-party types, APIGroup - is required. - type: string - kind: - description: Kind is the type of resource being - referenced - type: string - name: - description: Name is the name of resource being - referenced - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - description: 'dataSourceRef specifies the object - from which to populate the volume with data, if - a non-empty volume is desired. This may be any - object from a non-empty API group (non core object) - or a PersistentVolumeClaim object. When this field - is specified, volume binding will only succeed - if the type of the specified object matches some - installed volume populator or dynamic provisioner. - This field will replace the functionality of the - dataSource field and as such if both fields are - non-empty, they must have the same value. For - backwards compatibility, when namespace isn''t - specified in dataSourceRef, both fields (dataSource - and dataSourceRef) will be set to the same value - automatically if one of them is empty and the - other is non-empty. When namespace is specified - in dataSourceRef, dataSource isn''t set to the - same value and must be empty. There are three - important differences between dataSource and dataSourceRef: - * While dataSource only allows two specific types - of objects, dataSourceRef allows any non-core - object, as well as PersistentVolumeClaim objects. - * While dataSource ignores disallowed values (dropping - them), dataSourceRef preserves all values, and - generates an error if a disallowed value is specified. - * While dataSource only allows local objects, - dataSourceRef allows objects in any namespaces. - (Beta) Using this field requires the AnyVolumeDataSource - feature gate to be enabled. (Alpha) Using the - namespace field of dataSourceRef requires the - CrossNamespaceVolumeDataSource feature gate to - be enabled.' - properties: - apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API - group. For any other third-party types, APIGroup - is required. - type: string - kind: - description: Kind is the type of resource being - referenced - type: string - name: - description: Name is the name of resource being - referenced - type: string - namespace: - description: Namespace is the namespace of resource - being referenced Note that when a namespace - is specified, a gateway.networking.k8s.io/ReferenceGrant - object is required in the referent namespace - to allow that namespace's owner to accept - the reference. See the ReferenceGrant documentation - for details. (Alpha) This field requires the - CrossNamespaceVolumeDataSource feature gate - to be enabled. - type: string - required: - - kind - - name - type: object - resources: - description: 'resources represents the minimum resources - the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to specify - resource requirements that are lower than previous - value but must still be higher than capacity recorded - in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' - properties: - claims: - description: "Claims lists the names of resources, - defined in spec.resourceClaims, that are used - by this container. \n This is an alpha field - and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. - It can only be set for containers." - items: - description: ResourceClaim references one - entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name - of one entry in pod.spec.resourceClaims - of the Pod where this field is used. - It makes that resource available inside - a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount - of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum - amount of compute resources required. If Requests - is omitted for a container, it defaults to - Limits if that is explicitly specified, otherwise - to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - selector: - description: selector is a label query over volumes - to consider for binding. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - description: 'storageClassName is the name of the - StorageClass required by the claim. More info: - https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' - type: string - volumeMode: - description: volumeMode defines what type of volume - is required by the claim. Value of Filesystem - is implied when not included in claim spec. - type: string - volumeName: - description: volumeName is the binding reference - to the PersistentVolume backing this claim. - type: string - type: object - type: object required: - - type + - enabled + - name + - objectStorageName + - schedule type: object - type: object + type: array + required: + - enabled type: object - clusterSize: - description: ClusterSize is amount of nodes that required for the - cluster. A database starts in cluster mode if clusterSize >= 3. - format: int32 - type: integer dataSource: - description: DataSource defines a data source for a new cluster + description: DataSource defines a data source for bootstraping a new + cluster properties: - azure: - description: BackupStorageProviderSpec represents set of settings - to configure cloud provider. - properties: - bucket: - type: string - containerName: - description: A container name is a valid DNS name that conforms - to the Azure naming rules. - type: string - credentialsSecret: - type: string - endpointUrl: - type: string - prefix: - type: string - region: - type: string - storageClass: - description: STANDARD, NEARLINE, COLDLINE, ARCHIVE for GCP - Hot (Frequently accessed or modified data), Cool (Infrequently - accessed or modified data), Archive (Rarely accessed or - modified data) for Azure. - type: string - required: - - credentialsSecret - type: object - destination: - type: string - image: + backupName: + description: BackupName is the name of the backup from backup + location to use type: string - s3: - description: BackupStorageProviderSpec represents set of settings - to configure cloud provider. - properties: - bucket: - type: string - containerName: - description: A container name is a valid DNS name that conforms - to the Azure naming rules. - type: string - credentialsSecret: - type: string - endpointUrl: - type: string - prefix: - type: string - region: - type: string - storageClass: - description: STANDARD, NEARLINE, COLDLINE, ARCHIVE for GCP - Hot (Frequently accessed or modified data), Cool (Infrequently - accessed or modified data), Archive (Rarely accessed or - modified data) for Azure. - type: string - required: - - credentialsSecret - type: object - sslInternalSecretName: - type: string - sslSecretName: - type: string - storage_type: - description: BackupStorageType represents backup storage type. - type: string - storageName: - type: string - vaultSecretName: + objectStorageName: + description: ObjectStorageName is the name of the ObjectStorage + CR that defines the storage location type: string required: - - storage_type + - backupName + - objectStorageName type: object - databaseConfig: - description: DatabaseConfig contains a config settings for the specified - database. - type: string - databaseImage: - description: DatabaseVersion sets from version service and uses the - recommended version by default. - type: string - databaseType: - description: Database type stands for supported databases by the PMM - API Now it's pxc or psmdb types but we can extend it. - type: string - dbInstance: - description: DBInstance represents resource requests for a database - cluster. + engine: + description: Engine is the database engine specification properties: - cpu: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - diskSize: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - memory: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - storageClassName: + config: + description: Config is the engine configuration type: string - type: object - loadBalancer: - description: LoadBalancer contains a load balancer settings. For PXC - it's haproxy or proxysql. For PSMDB it's mongos. - properties: - annotations: - additionalProperties: - type: string - type: object - configuration: - type: string - exposeType: - description: Service Type string describes ingress methods for - a service - type: string - image: - type: string - loadBalancerSourceRanges: - items: - type: string - type: array + replicas: + description: Replicas is the number of engine replicas + format: int32 + type: integer resources: - description: ResourceRequirements describes the compute resource - requirements. + description: Resources are the resource limits for each engine + replica. If not set, resource limits are not imposed properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only be - set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry in - pod.spec.resourceClaims of the Pod where this field - is used. It makes that resource available inside a - container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. Requests cannot exceed - Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object + cpu: + anyOf: + - type: integer + - type: string + description: CPU is the CPU resource requirements + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory is the memory resource requirements + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + storage: + description: Storage is the engine storage configuration + properties: + class: + description: Class is the storage class to use for the persistent + volume claim + type: string + size: + anyOf: + - type: integer + - type: string + description: Size is the size of the persistent volume claim + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - size type: object - size: - format: int32 - type: integer - trafficPolicy: - description: ServiceExternalTrafficPolicy describes how nodes - distribute service traffic they receive on one of the Service's - "externally-facing" addresses (NodePorts, ExternalIPs, and LoadBalancer - IPs. - type: string type: - description: LoadBalancerType contains supported loadbalancers. - It can be proxysql or haproxy for PXC clusters, mongos for PSMDB - clusters or pgbouncer for Postgresql clusters. + description: Type is the engine type type: string + version: + description: Version is the engine version + type: string + required: + - storage + - type type: object monitoring: - description: Monitoring contains a monitoring settings. + description: Monitoring is the monitoring specification properties: - containerSecurityContext: - description: SecurityContext holds security configuration that - will be applied to a container. Some fields are present in both - SecurityContext and PodSecurityContext. When both are set, - the values in SecurityContext take precedence. - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls whether a - process can gain more privileges than its parent process. - This bool directly controls if the no_new_privs flag will - be set on the container process. AllowPrivilegeEscalation - is true always when the container is: 1) run as Privileged - 2) has CAP_SYS_ADMIN Note that this field cannot be set - when spec.os.name is windows.' - type: boolean - capabilities: - description: The capabilities to add/drop when running containers. - Defaults to the default set of capabilities granted by the - container runtime. Note that this field cannot be set when - spec.os.name is windows. - properties: - add: - description: Added capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - type: object - privileged: - description: Run container in privileged mode. Processes in - privileged containers are essentially equivalent to root - on the host. Defaults to false. Note that this field cannot - be set when spec.os.name is windows. - type: boolean - procMount: - description: procMount denotes the type of proc mount to use - for the containers. The default is DefaultProcMount which - uses the container runtime defaults for readonly paths and - masked paths. This requires the ProcMountType feature flag - to be enabled. Note that this field cannot be set when spec.os.name - is windows. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only root filesystem. - Default is false. Note that this field cannot be set when - spec.os.name is windows. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be set - in PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as a non-root - user. If true, the Kubelet will validate the image at runtime - to ensure that it does not run as UID 0 (root) and fail - to start the container if it does. If unset or false, no - such validation will be performed. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata if - unspecified. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to the container. - If unspecified, the container runtime will allocate a random - SELinux context for each container. May also be set in - PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that applies - to the container. - type: string - role: - description: Role is a SELinux role label that applies - to the container. - type: string - type: - description: Type is a SELinux type label that applies - to the container. - type: string - user: - description: User is a SELinux user label that applies - to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by this container. - If seccomp options are provided at both the pod & container - level, the container options override the pod options. Note - that this field cannot be set when spec.os.name is windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile must - be preconfigured on the node to work. Must be a descending - path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - a - profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile - should be used. Unconfined - no profile should be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings applied to all - containers. If unspecified, the options from the PodSecurityContext - will be used. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name is - linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named - by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name of the - GMSA credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is - alpha-level and will only be honored by components that - enable the WindowsHostProcessContainers feature flag. - Setting this field without the feature flag will result - in errors when validating the Pod. All of a Pod's containers - must have the same effective HostProcess value (it is - not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, if HostProcess - is true then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set in - PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. - type: string - type: object - type: object - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image - type: string + enabled: + description: Enabled is a flag to enable monitoring + type: boolean pmm: description: PMMSpec contains PMM settings. properties: @@ -2508,38 +240,97 @@ spec: Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object - runtimeClassName: - type: string + required: + - enabled type: object - pause: - description: Pause represents is a cluster paused or not. + paused: + description: Paused is a flag to stop the cluster type: boolean - secretsName: - description: SecretsName contains name of a secrets file for a database - cluster. - type: string + proxy: + description: Proxy is the proxy specification. If not set, an appropriate + proxy specification will be applied for the given engine. A common + use case for setting this field is to control the external access + to the database cluster. + properties: + config: + description: Config is the proxy configuration + type: string + expose: + description: Expose is the proxy expose configuration + properties: + ipSourceRanges: + description: IPSourceRanges is the list of IP source ranges + (CIDR notation) to allow access from. If not set, there + is no limitations + items: + type: string + type: array + type: + default: internal + description: Type is the expose type, can be internal or external + enum: + - internal + - external + type: string + type: object + replicas: + description: Replicas is the number of proxy replicas + format: int32 + type: integer + resources: + description: Resources are the resource limits for each proxy + replica. If not set, resource limits are not imposed + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU is the CPU resource requirements + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory is the memory resource requirements + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: + description: Type is the proxy type + enum: + - mongos + - haproxy + - proxysql + - pgbouncer + type: string + type: object required: - - clusterSize - - databaseConfig - - databaseImage - - databaseType - - dbInstance + - engine type: object status: - description: DatabaseClusterStatus defines the observed state of Database. + description: DatabaseClusterStatus defines the observed state of DatabaseCluster. properties: - host: + hostname: + description: Hostname is the hostname where the cluster can be reached type: string message: + description: Message is extra information about the cluster type: string + port: + description: Port is the port where the cluster can be reached + format: int32 + type: integer ready: + description: Ready is the number of ready pods format: int32 type: integer size: + description: Size is the total number of pods format: int32 type: integer status: - description: AppState is used to represent cluster's state. + description: Status is the status of the cluster type: string type: object type: object diff --git a/bundle/manifests/everest.percona.com_databaseengines.yaml b/bundle/manifests/everest.percona.com_databaseengines.yaml index d7f96c5cc..8aa1420a1 100644 --- a/bundle/manifests/everest.percona.com_databaseengines.yaml +++ b/bundle/manifests/everest.percona.com_databaseengines.yaml @@ -23,8 +23,8 @@ spec: - jsonPath: .status.status name: Status type: string - - jsonPath: .status.version - name: Version + - jsonPath: .status.operatorVersion + name: Operator Version type: string name: v1alpha1 schema: @@ -78,8 +78,11 @@ spec: imagePath: type: string status: + description: ComponentStatus represents status of the database + engine component. type: string type: object + description: ComponentsMap is a map of database engine components. type: object engine: additionalProperties: @@ -94,8 +97,11 @@ spec: imagePath: type: string status: + description: ComponentStatus represents status of the database + engine component. type: string type: object + description: ComponentsMap is a map of database engine components. type: object proxy: additionalProperties: @@ -111,8 +117,11 @@ spec: imagePath: type: string status: + description: ComponentStatus represents status of the + database engine component. type: string type: object + description: ComponentsMap is a map of database engine components. type: object type: object tools: @@ -129,8 +138,11 @@ spec: imagePath: type: string status: + description: ComponentStatus represents status of the + database engine component. type: string type: object + description: ComponentsMap is a map of database engine components. type: object type: object type: object diff --git a/bundle/manifests/everest.percona.com_objectstorages.yaml b/bundle/manifests/everest.percona.com_objectstorages.yaml new file mode 100644 index 000000000..26f89b741 --- /dev/null +++ b/bundle/manifests/everest.percona.com_objectstorages.yaml @@ -0,0 +1,76 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.1 + creationTimestamp: null + name: objectstorages.everest.percona.com +spec: + group: everest.percona.com + names: + kind: ObjectStorage + listKind: ObjectStorageList + plural: objectstorages + singular: objectstorage + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ObjectStorage is the Schema for the objectstorages API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ObjectStorageSpec defines the desired state of ObjectStorage. + properties: + bucket: + description: Bucket is a name of bucket. + type: string + credentialsSecretName: + description: CredentialsSecretName is the name of the secret with + credentials. + type: string + endpointURL: + description: EndpointURL is an endpoint URL of object storage. + type: string + region: + description: Region is a region where the bucket is located. + type: string + type: + description: Type is a type of object storage. Currently only S3 is + supported. + enum: + - s3 + type: string + required: + - bucket + - credentialsSecretName + - endpointURL + - region + - type + type: object + status: + description: ObjectStorageStatus defines the observed state of ObjectStorage. + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/metadata/annotations.yaml b/bundle/metadata/annotations.yaml index ee2482f97..2e815d56a 100644 --- a/bundle/metadata/annotations.yaml +++ b/bundle/metadata/annotations.yaml @@ -5,7 +5,7 @@ annotations: operators.operatorframework.io.bundle.metadata.v1: metadata/ operators.operatorframework.io.bundle.package.v1: everest-operator operators.operatorframework.io.bundle.channels.v1: alpha - operators.operatorframework.io.metrics.builder: operator-sdk-v1.27.0 + operators.operatorframework.io.metrics.builder: operator-sdk-v1.30.0 operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v3 diff --git a/config/crd/bases/everest.percona.com_databaseclusters.yaml b/config/crd/bases/everest.percona.com_databaseclusters.yaml index 3ef831f25..1cbddd973 100644 --- a/config/crd/bases/everest.percona.com_databaseclusters.yaml +++ b/config/crd/bases/everest.percona.com_databaseclusters.yaml @@ -28,8 +28,8 @@ spec: - jsonPath: .status.status name: Status type: string - - jsonPath: .status.host - name: ENDPOINT + - jsonPath: .status.hostname + name: Hostname type: string - jsonPath: .metadata.creationTimestamp name: Age @@ -37,7 +37,7 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: DatabaseCluster is the Schema for the databases API. + description: DatabaseCluster is the Schema for the databaseclusters API. properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -52,2397 +52,129 @@ spec: metadata: type: object spec: - description: DatabaseSpec defines the desired state of Database. + description: DatabaseClusterSpec defines the desired state of DatabaseCluster. properties: + adminSecretName: + description: AdminUserSecretName is the name of the secret that contains + the admin user credentials + type: string backup: - description: Backup contains backup settings. + description: Backup is the backup specification properties: - annotations: - additionalProperties: - type: string - type: object - containerSecurityContext: - description: SecurityContext holds security configuration that - will be applied to a container. Some fields are present in both - SecurityContext and PodSecurityContext. When both are set, - the values in SecurityContext take precedence. - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls whether a - process can gain more privileges than its parent process. - This bool directly controls if the no_new_privs flag will - be set on the container process. AllowPrivilegeEscalation - is true always when the container is: 1) run as Privileged - 2) has CAP_SYS_ADMIN Note that this field cannot be set - when spec.os.name is windows.' - type: boolean - capabilities: - description: The capabilities to add/drop when running containers. - Defaults to the default set of capabilities granted by the - container runtime. Note that this field cannot be set when - spec.os.name is windows. - properties: - add: - description: Added capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - type: object - privileged: - description: Run container in privileged mode. Processes in - privileged containers are essentially equivalent to root - on the host. Defaults to false. Note that this field cannot - be set when spec.os.name is windows. - type: boolean - procMount: - description: procMount denotes the type of proc mount to use - for the containers. The default is DefaultProcMount which - uses the container runtime defaults for readonly paths and - masked paths. This requires the ProcMountType feature flag - to be enabled. Note that this field cannot be set when spec.os.name - is windows. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only root filesystem. - Default is false. Note that this field cannot be set when - spec.os.name is windows. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be set - in PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as a non-root - user. If true, the Kubelet will validate the image at runtime - to ensure that it does not run as UID 0 (root) and fail - to start the container if it does. If unset or false, no - such validation will be performed. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata if - unspecified. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to the container. - If unspecified, the container runtime will allocate a random - SELinux context for each container. May also be set in - PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that applies - to the container. - type: string - role: - description: Role is a SELinux role label that applies - to the container. - type: string - type: - description: Type is a SELinux type label that applies - to the container. - type: string - user: - description: User is a SELinux user label that applies - to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by this container. - If seccomp options are provided at both the pod & container - level, the container options override the pod options. Note - that this field cannot be set when spec.os.name is windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile must - be preconfigured on the node to work. Must be a descending - path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - a - profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile - should be used. Unconfined - no profile should be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings applied to all - containers. If unspecified, the options from the PodSecurityContext - will be used. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name is - linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named - by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name of the - GMSA credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is - alpha-level and will only be honored by components that - enable the WindowsHostProcessContainers feature flag. - Setting this field without the feature flag will result - in errors when validating the Pod. All of a Pod's containers - must have the same effective HostProcess value (it is - not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, if HostProcess - is true then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set in - PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. - type: string - type: object - type: object enabled: + description: Enabled is a flag to enable backups type: boolean - image: - type: string - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image - type: string - imagePullSecrets: - items: - description: LocalObjectReference contains enough information - to let you locate the referenced object inside the same namespace. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - x-kubernetes-map-type: atomic - type: array - initImage: - type: string - labels: - additionalProperties: - type: string - type: object - resources: - description: ResourceRequirements describes the compute resource - requirements. - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only be - set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry in - pod.spec.resourceClaims of the Pod where this field - is used. It makes that resource available inside a - container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. Requests cannot exceed - Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - schedule: + schedules: + description: Schedules is a list of backup schedules items: - description: BackupSchedule represents set of settings to configure - backup schedule. + description: BackupSchedule is the backup schedule configuration. properties: - compressionLevel: - type: integer - compressionType: - type: string enabled: + description: Enabled is a flag to enable the schedule type: boolean - keep: - type: integer name: + description: Name is the name of the schedule type: string - schedule: - type: string - storageName: - type: string - type: object - type: array - serviceAccountName: - type: string - storages: - additionalProperties: - description: BackupStorageSpec represents set of settings to - configure backup storage. - properties: - affinity: - description: Affinity is a group of affinity scheduling - rules. - properties: - nodeAffinity: - description: Describes node affinity scheduling rules - for the pod. - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. - The node that is most preferred is the one with - the greatest sum of weights, i.e. for each node - that meets all of the scheduling requirements - (resource request, requiredDuringScheduling affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and adding - "weight" to the sum if the node matches the corresponding - matchExpressions; the node(s) with the highest - sum are the most preferred. - items: - description: An empty preferred scheduling term - matches all objects with implicit weight 0 (i.e. - it's a no-op). A null preferred scheduling term - matches no objects (i.e. is also a no-op). - properties: - preference: - description: A node selector term, associated - with the corresponding weight. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - x-kubernetes-map-type: atomic - weight: - description: Weight associated with matching - the corresponding nodeSelectorTerm, in the - range 1-100. - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, - the pod will not be scheduled onto the node. If - the affinity requirements specified by this field - cease to be met at some point during pod execution - (e.g. due to an update), the system may or may - not try to eventually evict the pod from its node. - properties: - nodeSelectorTerms: - description: Required. A list of node selector - terms. The terms are ORed. - items: - description: A null or empty node selector - term matches no objects. The requirements - of them are ANDed. The TopologySelectorTerm - type implements a subset of the NodeSelectorTerm. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: The label key that - the selector applies to. - type: string - operator: - description: Represents a key's - relationship to a set of values. - Valid operators are In, NotIn, - Exists, DoesNotExist. Gt, and - Lt. - type: string - values: - description: An array of string - values. If the operator is In - or NotIn, the values array must - be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. If - the operator is Gt or Lt, the - values array must have a single - element, which will be interpreted - as an integer. This array is replaced - during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - x-kubernetes-map-type: atomic - type: array - required: - - nodeSelectorTerms - type: object - x-kubernetes-map-type: atomic - type: object - podAffinity: - description: Describes pod affinity scheduling rules - (e.g. co-locate this pod in the same node, zone, etc. - as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. - The node that is most preferred is the one with - the greatest sum of weights, i.e. for each node - that meets all of the scheduling requirements - (resource request, requiredDuringScheduling affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and adding - "weight" to the sum if the node has pods which - matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched - WeightedPodAffinityTerm fields are added per-node - to find the most preferred node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, - associated with the corresponding weight. - properties: - labelSelector: - description: A label query over a set - of resources, in this case pods. - properties: - matchExpressions: - description: matchExpressions is a - list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set - of namespaces that the term applies - to. The term is applied to the union - of the namespaces selected by this field - and the ones listed in the namespaces - field. null selector and null or empty - namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a - list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's - namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified - by this field are not met at scheduling time, - the pod will not be scheduled onto the node. If - the affinity requirements specified by this field - cease to be met at some point during pod execution - (e.g. due to a pod label update), the system may - or may not try to eventually evict the pod from - its node. When there are multiple elements, the - lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. - items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) - with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which - a pod of the set of pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set of - namespaces that the term applies to. The - term is applied to the union of the namespaces - selected by this field and the ones listed - in the namespaces field. null selector and - null or empty namespaces list means "this - pod's namespace". An empty selector ({}) - matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static - list of namespace names that the term applies - to. The term is applied to the union of - the namespaces listed in this field and - the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector - means "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose value - of the label with key topologyKey matches - that of any node on which any of the selected - pods is running. Empty topologyKey is not - allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - description: Describes pod anti-affinity scheduling - rules (e.g. avoid putting this pod in the same node, - zone, etc. as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule - pods to nodes that satisfy the anti-affinity expressions - specified by this field, but it may choose a node - that violates one or more of the expressions. - The node that is most preferred is the one with - the greatest sum of weights, i.e. for each node - that meets all of the scheduling requirements - (resource request, requiredDuringScheduling anti-affinity - expressions, etc.), compute a sum by iterating - through the elements of this field and adding - "weight" to the sum if the node has pods which - matches the corresponding podAffinityTerm; the - node(s) with the highest sum are the most preferred. - items: - description: The weights of all of the matched - WeightedPodAffinityTerm fields are added per-node - to find the most preferred node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, - associated with the corresponding weight. - properties: - labelSelector: - description: A label query over a set - of resources, in this case pods. - properties: - matchExpressions: - description: matchExpressions is a - list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set - of namespaces that the term applies - to. The term is applied to the union - of the namespaces selected by this field - and the ones listed in the namespaces - field. null selector and null or empty - namespaces list means "this pod's namespace". - An empty selector ({}) matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a - list of label selector requirements. - The requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label - key that the selector applies - to. - type: string - operator: - description: operator represents - a key's relationship to a - set of values. Valid operators - are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values - array must be non-empty. If - the operator is Exists or - DoesNotExist, the values array - must be empty. This array - is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map - of {key,value} pairs. A single {key,value} - in the matchLabels map is equivalent - to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are - ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static - list of namespace names that the term - applies to. The term is applied to the - union of the namespaces listed in this - field and the ones selected by namespaceSelector. - null or empty namespaces list and null - namespaceSelector means "this pod's - namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose - value of the label with key topologyKey - matches that of any node on which any - of the selected pods is running. Empty - topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching - the corresponding podAffinityTerm, in the - range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified - by this field are not met at scheduling time, - the pod will not be scheduled onto the node. If - the anti-affinity requirements specified by this - field cease to be met at some point during pod - execution (e.g. due to a pod label update), the - system may or may not try to eventually evict - the pod from its node. When there are multiple - elements, the lists of nodes corresponding to - each podAffinityTerm are intersected, i.e. all - terms must be satisfied. - items: - description: Defines a set of pods (namely those - matching the labelSelector relative to the given - namespace(s)) that this pod should be co-located - (affinity) or not co-located (anti-affinity) - with, where co-located is defined as running - on a node whose value of the label with key - matches that of any node on which - a pod of the set of pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaceSelector: - description: A label query over the set of - namespaces that the term applies to. The - term is applied to the union of the namespaces - selected by this field and the ones listed - in the namespaces field. null selector and - null or empty namespaces list means "this - pod's namespace". An empty selector ({}) - matches all namespaces. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The - requirements are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents - a key's relationship to a set - of values. Valid operators are - In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array - of string values. If the operator - is In or NotIn, the values array - must be non-empty. If the operator - is Exists or DoesNotExist, the - values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: namespaces specifies a static - list of namespace names that the term applies - to. The term is applied to the union of - the namespaces listed in this field and - the ones selected by namespaceSelector. - null or empty namespaces list and null namespaceSelector - means "this pod's namespace". - items: - type: string - type: array - topologyKey: - description: This pod should be co-located - (affinity) or not co-located (anti-affinity) - with the pods matching the labelSelector - in the specified namespaces, where co-located - is defined as running on a node whose value - of the label with key topologyKey matches - that of any node on which any of the selected - pods is running. Empty topologyKey is not - allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - annotations: - additionalProperties: - type: string - type: object - containerSecurityContext: - description: SecurityContext holds security configuration - that will be applied to a container. Some fields are present - in both SecurityContext and PodSecurityContext. When - both are set, the values in SecurityContext take precedence. - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls whether - a process can gain more privileges than its parent - process. This bool directly controls if the no_new_privs - flag will be set on the container process. AllowPrivilegeEscalation - is true always when the container is: 1) run as Privileged - 2) has CAP_SYS_ADMIN Note that this field cannot be - set when spec.os.name is windows.' - type: boolean - capabilities: - description: The capabilities to add/drop when running - containers. Defaults to the default set of capabilities - granted by the container runtime. Note that this field - cannot be set when spec.os.name is windows. - properties: - add: - description: Added capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - type: object - privileged: - description: Run container in privileged mode. Processes - in privileged containers are essentially equivalent - to root on the host. Defaults to false. Note that - this field cannot be set when spec.os.name is windows. - type: boolean - procMount: - description: procMount denotes the type of proc mount - to use for the containers. The default is DefaultProcMount - which uses the container runtime defaults for readonly - paths and masked paths. This requires the ProcMountType - feature flag to be enabled. Note that this field cannot - be set when spec.os.name is windows. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only - root filesystem. Default is false. Note that this - field cannot be set when spec.os.name is windows. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be - set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set - when spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as - a non-root user. If true, the Kubelet will validate - the image at runtime to ensure that it does not run - as UID 0 (root) and fail to start the container if - it does. If unset or false, no such validation will - be performed. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata - if unspecified. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name - is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to the - container. If unspecified, the container runtime will - allocate a random SELinux context for each container. May - also be set in PodSecurityContext. If set in both - SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is - windows. - properties: - level: - description: Level is SELinux level label that applies - to the container. - type: string - role: - description: Role is a SELinux role label that applies - to the container. - type: string - type: - description: Type is a SELinux type label that applies - to the container. - type: string - user: - description: User is a SELinux user label that applies - to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by this container. - If seccomp options are provided at both the pod & - container level, the container options override the - pod options. Note that this field cannot be set when - spec.os.name is windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile - defined in a file on the node should be used. - The profile must be preconfigured on the node - to work. Must be a descending path, relative to - the kubelet's configured seccomp profile location. - Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp - profile will be applied. Valid options are: \n - Localhost - a profile defined in a file on the - node should be used. RuntimeDefault - the container - runtime default profile should be used. Unconfined - - no profile should be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings applied to - all containers. If unspecified, the options from the - PodSecurityContext will be used. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set - when spec.os.name is linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA - admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec - named by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name - of the GMSA credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container - should be run as a 'Host Process' container. This - field is alpha-level and will only be honored - by components that enable the WindowsHostProcessContainers - feature flag. Setting this field without the feature - flag will result in errors when validating the - Pod. All of a Pod's containers must have the same - effective HostProcess value (it is not allowed - to have a mix of HostProcess containers and non-HostProcess - containers). In addition, if HostProcess is true - then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the - entrypoint of the container process. Defaults - to the user specified in image metadata if unspecified. - May also be set in PodSecurityContext. If set - in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - type: string - type: object - type: object - labels: - additionalProperties: - type: string - type: object - nodeSelector: - additionalProperties: - type: string - type: object - podSecurityContext: - description: PodSecurityContext holds pod-level security - attributes and common container settings. Some fields - are also present in container.securityContext. Field - values of container.securityContext take precedence over - field values of PodSecurityContext. - properties: - fsGroup: - description: "A special supplemental group that applies - to all containers in a pod. Some volume types allow - the Kubelet to change the ownership of that volume - to be owned by the pod: \n 1. The owning GID will - be the FSGroup 2. The setgid bit is set (new files - created in the volume will be owned by FSGroup) 3. - The permission bits are OR'd with rw-rw---- \n If - unset, the Kubelet will not modify the ownership and - permissions of any volume. Note that this field cannot - be set when spec.os.name is windows." - format: int64 - type: integer - fsGroupChangePolicy: - description: 'fsGroupChangePolicy defines behavior of - changing ownership and permission of the volume before - being exposed inside Pod. This field will only apply - to volume types which support fsGroup based ownership(and - permissions). It will have no effect on ephemeral - volume types such as: secret, configmaps and emptydir. - Valid values are "OnRootMismatch" and "Always". If - not specified, "Always" is used. Note that this field - cannot be set when spec.os.name is windows.' - type: string - runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be - set in SecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. Note that this - field cannot be set when spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as - a non-root user. If true, the Kubelet will validate - the image at runtime to ensure that it does not run - as UID 0 (root) and fail to start the container if - it does. If unset or false, no such validation will - be performed. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata - if unspecified. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence - for that container. Note that this field cannot be - set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to all - containers. If unspecified, the container runtime - will allocate a random SELinux context for each container. May - also be set in SecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. Note that this - field cannot be set when spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that applies - to the container. - type: string - role: - description: Role is a SELinux role label that applies - to the container. - type: string - type: - description: Type is a SELinux type label that applies - to the container. - type: string - user: - description: User is a SELinux user label that applies - to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by the containers - in this pod. Note that this field cannot be set when - spec.os.name is windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile - defined in a file on the node should be used. - The profile must be preconfigured on the node - to work. Must be a descending path, relative to - the kubelet's configured seccomp profile location. - Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp - profile will be applied. Valid options are: \n - Localhost - a profile defined in a file on the - node should be used. RuntimeDefault - the container - runtime default profile should be used. Unconfined - - no profile should be applied." - type: string - required: - - type - type: object - supplementalGroups: - description: A list of groups applied to the first process - run in each container, in addition to the container's - primary GID, the fsGroup (if specified), and group - memberships defined in the container image for the - uid of the container process. If unspecified, no additional - groups are added to any container. Note that group - memberships defined in the container image for the - uid of the container process are still effective, - even if they are not included in this list. Note that - this field cannot be set when spec.os.name is windows. - items: - format: int64 - type: integer - type: array - sysctls: - description: Sysctls hold a list of namespaced sysctls - used for the pod. Pods with unsupported sysctls (by - the container runtime) might fail to launch. Note - that this field cannot be set when spec.os.name is - windows. - items: - description: Sysctl defines a kernel parameter to - be set - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - description: The Windows specific settings applied to - all containers. If unspecified, the options within - a container's SecurityContext will be used. If set - in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name - is linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA - admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec - named by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name - of the GMSA credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container - should be run as a 'Host Process' container. This - field is alpha-level and will only be honored - by components that enable the WindowsHostProcessContainers - feature flag. Setting this field without the feature - flag will result in errors when validating the - Pod. All of a Pod's containers must have the same - effective HostProcess value (it is not allowed - to have a mix of HostProcess containers and non-HostProcess - containers). In addition, if HostProcess is true - then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the - entrypoint of the container process. Defaults - to the user specified in image metadata if unspecified. - May also be set in PodSecurityContext. If set - in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - type: string - type: object - type: object - priorityClassName: + objectStorageName: + description: ObjectStorageName is the name of the ObjectStorage + CR that defines the storage location type: string - resources: - description: ResourceRequirements describes the compute - resource requirements. - properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the - DynamicResourceAllocation feature gate. \n This field - is immutable. It can only be set for containers." - items: - description: ResourceClaim references one entry in - PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry - in pod.spec.resourceClaims of the Pod where - this field is used. It makes that resource available - inside a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of - compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount - of compute resources required. If Requests is omitted - for a container, it defaults to Limits if that is - explicitly specified, otherwise to an implementation-defined - value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - runtimeClassName: - type: string - schedulerName: - type: string - storageProvider: - description: BackupStorageProviderSpec represents set of - settings to configure cloud provider. - properties: - bucket: - type: string - containerName: - description: A container name is a valid DNS name that - conforms to the Azure naming rules. - type: string - credentialsSecret: - type: string - endpointUrl: - type: string - prefix: - type: string - region: - type: string - storageClass: - description: STANDARD, NEARLINE, COLDLINE, ARCHIVE for - GCP Hot (Frequently accessed or modified data), Cool - (Infrequently accessed or modified data), Archive - (Rarely accessed or modified data) for Azure. - type: string - required: - - credentialsSecret - type: object - tolerations: - items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple - using the matching operator . - properties: - effect: - description: Effect indicates the taint effect to - match. Empty means match all taint effects. When - specified, allowed values are NoSchedule, PreferNoSchedule - and NoExecute. - type: string - key: - description: Key is the taint key that the toleration - applies to. Empty means match all taint keys. If - the key is empty, operator must be Exists; this - combination means to match all values and all keys. - type: string - operator: - description: Operator represents a key's relationship - to the value. Valid operators are Exists and Equal. - Defaults to Equal. Exists is equivalent to wildcard - for value, so that a pod can tolerate all taints - of a particular category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period - of time the toleration (which must be of effect - NoExecute, otherwise this field is ignored) tolerates - the taint. By default, it is not set, which means - tolerate the taint forever (do not evict). Zero - and negative values will be treated as 0 (evict - immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the toleration - matches to. If the operator is Exists, the value - should be empty, otherwise just a regular string. - type: string - type: object - type: array - type: - description: BackupStorageType represents backup storage - type. + retentionCopies: + description: RetentionCopies is the number of backup copies + to retain + format: int32 + type: integer + schedule: + description: Schedule is the cron schedule type: string - verifyTLS: - type: boolean - volumeSpec: - description: VolumeSpec represents a specification to configure - volume for underlying database. - properties: - emptyDir: - description: EmptyDir to use as data volume for mysql. - EmptyDir represents a temporary directory that shares - a pod's lifetime. - properties: - medium: - description: 'medium represents what type of storage - medium should back this directory. The default - is "" which means to use the node''s default medium. - Must be an empty string (default) or Memory. More - info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - description: 'sizeLimit is the total amount of local - storage required for this EmptyDir volume. The - size limit is also applicable for memory medium. - The maximum usage on memory medium EmptyDir would - be the minimum value between the SizeLimit specified - here and the sum of memory limits of all containers - in a pod. The default is nil which means that - the limit is undefined. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - hostPath: - description: HostPath to use as data volume for mysql. - HostPath represents a pre-existing file or directory - on the host machine that is directly exposed to the - container. - properties: - path: - description: 'path of the directory on the host. - If the path is a symlink, it will follow the link - to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - type: - description: 'type for HostPath Volume Defaults - to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - required: - - path - type: object - persistentVolumeClaim: - description: PersistentVolumeClaim to specify PVC spec - for the volume for mysql data. It has the highest - level of precedence, followed by HostPath and EmptyDir. - And represents the PVC specification. - properties: - accessModes: - description: 'accessModes contains the desired access - modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' - items: - type: string - type: array - dataSource: - description: 'dataSource field can be used to specify - either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) If the - provisioner or an external controller can support - the specified data source, it will create a new - volume based on the contents of the specified - data source. When the AnyVolumeDataSource feature - gate is enabled, dataSource contents will be copied - to dataSourceRef, and dataSourceRef contents will - be copied to dataSource when dataSourceRef.namespace - is not specified. If the namespace is specified, - then dataSourceRef will not be copied to dataSource.' - properties: - apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API - group. For any other third-party types, APIGroup - is required. - type: string - kind: - description: Kind is the type of resource being - referenced - type: string - name: - description: Name is the name of resource being - referenced - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - dataSourceRef: - description: 'dataSourceRef specifies the object - from which to populate the volume with data, if - a non-empty volume is desired. This may be any - object from a non-empty API group (non core object) - or a PersistentVolumeClaim object. When this field - is specified, volume binding will only succeed - if the type of the specified object matches some - installed volume populator or dynamic provisioner. - This field will replace the functionality of the - dataSource field and as such if both fields are - non-empty, they must have the same value. For - backwards compatibility, when namespace isn''t - specified in dataSourceRef, both fields (dataSource - and dataSourceRef) will be set to the same value - automatically if one of them is empty and the - other is non-empty. When namespace is specified - in dataSourceRef, dataSource isn''t set to the - same value and must be empty. There are three - important differences between dataSource and dataSourceRef: - * While dataSource only allows two specific types - of objects, dataSourceRef allows any non-core - object, as well as PersistentVolumeClaim objects. - * While dataSource ignores disallowed values (dropping - them), dataSourceRef preserves all values, and - generates an error if a disallowed value is specified. - * While dataSource only allows local objects, - dataSourceRef allows objects in any namespaces. - (Beta) Using this field requires the AnyVolumeDataSource - feature gate to be enabled. (Alpha) Using the - namespace field of dataSourceRef requires the - CrossNamespaceVolumeDataSource feature gate to - be enabled.' - properties: - apiGroup: - description: APIGroup is the group for the resource - being referenced. If APIGroup is not specified, - the specified Kind must be in the core API - group. For any other third-party types, APIGroup - is required. - type: string - kind: - description: Kind is the type of resource being - referenced - type: string - name: - description: Name is the name of resource being - referenced - type: string - namespace: - description: Namespace is the namespace of resource - being referenced Note that when a namespace - is specified, a gateway.networking.k8s.io/ReferenceGrant - object is required in the referent namespace - to allow that namespace's owner to accept - the reference. See the ReferenceGrant documentation - for details. (Alpha) This field requires the - CrossNamespaceVolumeDataSource feature gate - to be enabled. - type: string - required: - - kind - - name - type: object - resources: - description: 'resources represents the minimum resources - the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to specify - resource requirements that are lower than previous - value but must still be higher than capacity recorded - in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' - properties: - claims: - description: "Claims lists the names of resources, - defined in spec.resourceClaims, that are used - by this container. \n This is an alpha field - and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. - It can only be set for containers." - items: - description: ResourceClaim references one - entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name - of one entry in pod.spec.resourceClaims - of the Pod where this field is used. - It makes that resource available inside - a container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount - of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum - amount of compute resources required. If Requests - is omitted for a container, it defaults to - Limits if that is explicitly specified, otherwise - to an implementation-defined value. Requests - cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - selector: - description: selector is a label query over volumes - to consider for binding. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - storageClassName: - description: 'storageClassName is the name of the - StorageClass required by the claim. More info: - https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' - type: string - volumeMode: - description: volumeMode defines what type of volume - is required by the claim. Value of Filesystem - is implied when not included in claim spec. - type: string - volumeName: - description: volumeName is the binding reference - to the PersistentVolume backing this claim. - type: string - type: object - type: object required: - - type + - enabled + - name + - objectStorageName + - schedule type: object - type: object + type: array + required: + - enabled type: object - clusterSize: - description: ClusterSize is amount of nodes that required for the - cluster. A database starts in cluster mode if clusterSize >= 3. - format: int32 - type: integer dataSource: - description: DataSource defines a data source for a new cluster + description: DataSource defines a data source for bootstraping a new + cluster properties: - azure: - description: BackupStorageProviderSpec represents set of settings - to configure cloud provider. - properties: - bucket: - type: string - containerName: - description: A container name is a valid DNS name that conforms - to the Azure naming rules. - type: string - credentialsSecret: - type: string - endpointUrl: - type: string - prefix: - type: string - region: - type: string - storageClass: - description: STANDARD, NEARLINE, COLDLINE, ARCHIVE for GCP - Hot (Frequently accessed or modified data), Cool (Infrequently - accessed or modified data), Archive (Rarely accessed or - modified data) for Azure. - type: string - required: - - credentialsSecret - type: object - destination: - type: string - image: + backupName: + description: BackupName is the name of the backup from backup + location to use type: string - s3: - description: BackupStorageProviderSpec represents set of settings - to configure cloud provider. - properties: - bucket: - type: string - containerName: - description: A container name is a valid DNS name that conforms - to the Azure naming rules. - type: string - credentialsSecret: - type: string - endpointUrl: - type: string - prefix: - type: string - region: - type: string - storageClass: - description: STANDARD, NEARLINE, COLDLINE, ARCHIVE for GCP - Hot (Frequently accessed or modified data), Cool (Infrequently - accessed or modified data), Archive (Rarely accessed or - modified data) for Azure. - type: string - required: - - credentialsSecret - type: object - sslInternalSecretName: - type: string - sslSecretName: - type: string - storage_type: - description: BackupStorageType represents backup storage type. - type: string - storageName: - type: string - vaultSecretName: + objectStorageName: + description: ObjectStorageName is the name of the ObjectStorage + CR that defines the storage location type: string required: - - storage_type + - backupName + - objectStorageName type: object - databaseConfig: - description: DatabaseConfig contains a config settings for the specified - database. - type: string - databaseImage: - description: DatabaseVersion sets from version service and uses the - recommended version by default. - type: string - databaseType: - description: Database type stands for supported databases by the PMM - API Now it's pxc or psmdb types but we can extend it. - type: string - dbInstance: - description: DBInstance represents resource requests for a database - cluster. + engine: + description: Engine is the database engine specification properties: - cpu: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - diskSize: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - memory: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - storageClassName: + config: + description: Config is the engine configuration type: string - type: object - loadBalancer: - description: LoadBalancer contains a load balancer settings. For PXC - it's haproxy or proxysql. For PSMDB it's mongos. - properties: - annotations: - additionalProperties: - type: string - type: object - configuration: - type: string - exposeType: - description: Service Type string describes ingress methods for - a service - type: string - image: - type: string - loadBalancerSourceRanges: - items: - type: string - type: array + replicas: + description: Replicas is the number of engine replicas + format: int32 + type: integer resources: - description: ResourceRequirements describes the compute resource - requirements. + description: Resources are the resource limits for each engine + replica. If not set, resource limits are not imposed properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only be - set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry in - pod.spec.resourceClaims of the Pod where this field - is used. It makes that resource available inside a - container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. Requests cannot exceed - Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object + cpu: + anyOf: + - type: integer + - type: string + description: CPU is the CPU resource requirements + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory is the memory resource requirements + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + storage: + description: Storage is the engine storage configuration + properties: + class: + description: Class is the storage class to use for the persistent + volume claim + type: string + size: + anyOf: + - type: integer + - type: string + description: Size is the size of the persistent volume claim + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - size type: object - size: - format: int32 - type: integer - trafficPolicy: - description: ServiceExternalTrafficPolicy describes how nodes - distribute service traffic they receive on one of the Service's - "externally-facing" addresses (NodePorts, ExternalIPs, and LoadBalancer - IPs. - type: string type: - description: LoadBalancerType contains supported loadbalancers. - It can be proxysql or haproxy for PXC clusters, mongos for PSMDB - clusters or pgbouncer for Postgresql clusters. + description: Type is the engine type type: string + version: + description: Version is the engine version + type: string + required: + - storage + - type type: object monitoring: - description: Monitoring contains a monitoring settings. + description: Monitoring is the monitoring specification properties: - containerSecurityContext: - description: SecurityContext holds security configuration that - will be applied to a container. Some fields are present in both - SecurityContext and PodSecurityContext. When both are set, - the values in SecurityContext take precedence. - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls whether a - process can gain more privileges than its parent process. - This bool directly controls if the no_new_privs flag will - be set on the container process. AllowPrivilegeEscalation - is true always when the container is: 1) run as Privileged - 2) has CAP_SYS_ADMIN Note that this field cannot be set - when spec.os.name is windows.' - type: boolean - capabilities: - description: The capabilities to add/drop when running containers. - Defaults to the default set of capabilities granted by the - container runtime. Note that this field cannot be set when - spec.os.name is windows. - properties: - add: - description: Added capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent POSIX capabilities - type - type: string - type: array - type: object - privileged: - description: Run container in privileged mode. Processes in - privileged containers are essentially equivalent to root - on the host. Defaults to false. Note that this field cannot - be set when spec.os.name is windows. - type: boolean - procMount: - description: procMount denotes the type of proc mount to use - for the containers. The default is DefaultProcMount which - uses the container runtime defaults for readonly paths and - masked paths. This requires the ProcMountType feature flag - to be enabled. Note that this field cannot be set when spec.os.name - is windows. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only root filesystem. - Default is false. Note that this field cannot be set when - spec.os.name is windows. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of the container - process. Uses runtime default if unset. May also be set - in PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as a non-root - user. If true, the Kubelet will validate the image at runtime - to ensure that it does not run as UID 0 (root) and fail - to start the container if it does. If unset or false, no - such validation will be performed. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container - process. Defaults to user specified in image metadata if - unspecified. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to the container. - If unspecified, the container runtime will allocate a random - SELinux context for each container. May also be set in - PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when - spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that applies - to the container. - type: string - role: - description: Role is a SELinux role label that applies - to the container. - type: string - type: - description: Type is a SELinux type label that applies - to the container. - type: string - user: - description: User is a SELinux user label that applies - to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by this container. - If seccomp options are provided at both the pod & container - level, the container options override the pod options. Note - that this field cannot be set when spec.os.name is windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile must - be preconfigured on the node to work. Must be a descending - path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - a - profile defined in a file on the node should be used. - RuntimeDefault - the container runtime default profile - should be used. Unconfined - no profile should be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings applied to all - containers. If unspecified, the options from the PodSecurityContext - will be used. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. - Note that this field cannot be set when spec.os.name is - linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named - by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name of the - GMSA credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is - alpha-level and will only be honored by components that - enable the WindowsHostProcessContainers feature flag. - Setting this field without the feature flag will result - in errors when validating the Pod. All of a Pod's containers - must have the same effective HostProcess value (it is - not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, if HostProcess - is true then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set in - PodSecurityContext. If set in both SecurityContext and - PodSecurityContext, the value specified in SecurityContext - takes precedence. - type: string - type: object - type: object - imagePullPolicy: - description: PullPolicy describes a policy for if/when to pull - a container image - type: string + enabled: + description: Enabled is a flag to enable monitoring + type: boolean pmm: description: PMMSpec contains PMM settings. properties: @@ -2509,38 +241,97 @@ spec: Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' type: object type: object - runtimeClassName: - type: string + required: + - enabled type: object - pause: - description: Pause represents is a cluster paused or not. + paused: + description: Paused is a flag to stop the cluster type: boolean - secretsName: - description: SecretsName contains name of a secrets file for a database - cluster. - type: string + proxy: + description: Proxy is the proxy specification. If not set, an appropriate + proxy specification will be applied for the given engine. A common + use case for setting this field is to control the external access + to the database cluster. + properties: + config: + description: Config is the proxy configuration + type: string + expose: + description: Expose is the proxy expose configuration + properties: + ipSourceRanges: + description: IPSourceRanges is the list of IP source ranges + (CIDR notation) to allow access from. If not set, there + is no limitations + items: + type: string + type: array + type: + default: internal + description: Type is the expose type, can be internal or external + enum: + - internal + - external + type: string + type: object + replicas: + description: Replicas is the number of proxy replicas + format: int32 + type: integer + resources: + description: Resources are the resource limits for each proxy + replica. If not set, resource limits are not imposed + properties: + cpu: + anyOf: + - type: integer + - type: string + description: CPU is the CPU resource requirements + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + memory: + anyOf: + - type: integer + - type: string + description: Memory is the memory resource requirements + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: + description: Type is the proxy type + enum: + - mongos + - haproxy + - proxysql + - pgbouncer + type: string + type: object required: - - clusterSize - - databaseConfig - - databaseImage - - databaseType - - dbInstance + - engine type: object status: - description: DatabaseClusterStatus defines the observed state of Database. + description: DatabaseClusterStatus defines the observed state of DatabaseCluster. properties: - host: + hostname: + description: Hostname is the hostname where the cluster can be reached type: string message: + description: Message is extra information about the cluster type: string + port: + description: Port is the port where the cluster can be reached + format: int32 + type: integer ready: + description: Ready is the number of ready pods format: int32 type: integer size: + description: Size is the total number of pods format: int32 type: integer status: - description: AppState is used to represent cluster's state. + description: Status is the status of the cluster type: string type: object type: object diff --git a/config/crd/bases/everest.percona.com_databaseengines.yaml b/config/crd/bases/everest.percona.com_databaseengines.yaml index 4657c1ffd..db0da3687 100644 --- a/config/crd/bases/everest.percona.com_databaseengines.yaml +++ b/config/crd/bases/everest.percona.com_databaseengines.yaml @@ -24,8 +24,8 @@ spec: - jsonPath: .status.status name: Status type: string - - jsonPath: .status.version - name: Version + - jsonPath: .status.operatorVersion + name: Operator Version type: string name: v1alpha1 schema: @@ -79,8 +79,11 @@ spec: imagePath: type: string status: + description: ComponentStatus represents status of the database + engine component. type: string type: object + description: ComponentsMap is a map of database engine components. type: object engine: additionalProperties: @@ -95,8 +98,11 @@ spec: imagePath: type: string status: + description: ComponentStatus represents status of the database + engine component. type: string type: object + description: ComponentsMap is a map of database engine components. type: object proxy: additionalProperties: @@ -112,8 +118,11 @@ spec: imagePath: type: string status: + description: ComponentStatus represents status of the + database engine component. type: string type: object + description: ComponentsMap is a map of database engine components. type: object type: object tools: @@ -130,8 +139,11 @@ spec: imagePath: type: string status: + description: ComponentStatus represents status of the + database engine component. type: string type: object + description: ComponentsMap is a map of database engine components. type: object type: object type: object diff --git a/config/crd/bases/everest.percona.com_objectstorages.yaml b/config/crd/bases/everest.percona.com_objectstorages.yaml new file mode 100644 index 000000000..e56daa7fa --- /dev/null +++ b/config/crd/bases/everest.percona.com_objectstorages.yaml @@ -0,0 +1,71 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.1 + creationTimestamp: null + name: objectstorages.everest.percona.com +spec: + group: everest.percona.com + names: + kind: ObjectStorage + listKind: ObjectStorageList + plural: objectstorages + singular: objectstorage + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ObjectStorage is the Schema for the objectstorages API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ObjectStorageSpec defines the desired state of ObjectStorage. + properties: + bucket: + description: Bucket is a name of bucket. + type: string + credentialsSecretName: + description: CredentialsSecretName is the name of the secret with + credentials. + type: string + endpointURL: + description: EndpointURL is an endpoint URL of object storage. + type: string + region: + description: Region is a region where the bucket is located. + type: string + type: + description: Type is a type of object storage. Currently only S3 is + supported. + enum: + - s3 + type: string + required: + - bucket + - credentialsSecretName + - endpointURL + - region + - type + type: object + status: + description: ObjectStorageStatus defines the observed state of ObjectStorage. + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 68202b4b4..8355d2050 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -6,6 +6,7 @@ resources: - bases/everest.percona.com_databaseclusterbackups.yaml - bases/everest.percona.com_databaseclusterrestores.yaml - bases/everest.percona.com_databaseengines.yaml +- bases/everest.percona.com_objectstorages.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: @@ -15,6 +16,7 @@ patchesStrategicMerge: #- patches/webhook_in_databaseclusterbackups.yaml #- patches/webhook_in_databaseclusterrestores.yaml #- patches/webhook_in_databaseengines.yaml +#- patches/webhook_in_objectstorages.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. @@ -23,6 +25,7 @@ patchesStrategicMerge: #- patches/cainjection_in_databaseclusterbackups.yaml #- patches/cainjection_in_databaseclusterrestores.yaml #- patches/cainjection_in_databaseengines.yaml +#- patches/cainjection_in_objectstorages.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_objectstorages.yaml b/config/crd/patches/cainjection_in_objectstorages.yaml new file mode 100644 index 000000000..8173000c2 --- /dev/null +++ b/config/crd/patches/cainjection_in_objectstorages.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: objectstorages.everest.percona.com diff --git a/config/crd/patches/webhook_in_objectstorages.yaml b/config/crd/patches/webhook_in_objectstorages.yaml new file mode 100644 index 000000000..6cbf82d33 --- /dev/null +++ b/config/crd/patches/webhook_in_objectstorages.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: objectstorages.everest.percona.com +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 342d8c3a2..eaa7f689b 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -5,4 +5,4 @@ kind: Kustomization images: - name: controller newName: docker.io/percona/everest-operator - newTag: 0.0.2 + newTag: 0.0.3 diff --git a/config/manifests/bases/everest-operator.clusterserviceversion.yaml b/config/manifests/bases/everest-operator.clusterserviceversion.yaml index 1032278e7..7c2c50cf2 100644 --- a/config/manifests/bases/everest-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/everest-operator.clusterserviceversion.yaml @@ -22,7 +22,7 @@ spec: kind: DatabaseClusterRestore name: databaseclusterrestores.everest.percona.com version: v1alpha1 - - description: DatabaseCluster is the Schema for the databases API. + - description: DatabaseCluster is the Schema for the databaseclusters API. displayName: Database Cluster kind: DatabaseCluster name: databaseclusters.everest.percona.com @@ -32,6 +32,11 @@ spec: kind: DatabaseEngine name: databaseengines.everest.percona.com version: v1alpha1 + - description: ObjectStorage is the Schema for the objectstorages API. + displayName: Object Storage + kind: ObjectStorage + name: objectstorages.everest.percona.com + version: v1alpha1 description: Deploy database clusters easily with Everest operator displayName: Everest operator icon: diff --git a/config/rbac/objectstorage_editor_role.yaml b/config/rbac/objectstorage_editor_role.yaml new file mode 100644 index 000000000..da92bc9d9 --- /dev/null +++ b/config/rbac/objectstorage_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit objectstorages. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: objectstorage-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: everest-operator + app.kubernetes.io/part-of: everest-operator + app.kubernetes.io/managed-by: kustomize + name: objectstorage-editor-role +rules: +- apiGroups: + - everest.percona.com + resources: + - objectstorages + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - everest.percona.com + resources: + - objectstorages/status + verbs: + - get diff --git a/config/rbac/objectstorage_viewer_role.yaml b/config/rbac/objectstorage_viewer_role.yaml new file mode 100644 index 000000000..b19601499 --- /dev/null +++ b/config/rbac/objectstorage_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view objectstorages. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: objectstorage-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: everest-operator + app.kubernetes.io/part-of: everest-operator + app.kubernetes.io/managed-by: kustomize + name: objectstorage-viewer-role +rules: +- apiGroups: + - everest.percona.com + resources: + - objectstorages + verbs: + - get + - list + - watch +- apiGroups: + - everest.percona.com + resources: + - objectstorages/status + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 2445679dc..6406e8b8d 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -138,7 +138,19 @@ rules: - patch - update - apiGroups: - - pg.percona.com + - everest.percona.com + resources: + - objectstorages + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - pgv2.percona.com resources: - perconapgclusters verbs: diff --git a/config/samples/everest_v1alpha1_objectstorage.yaml b/config/samples/everest_v1alpha1_objectstorage.yaml new file mode 100644 index 000000000..e21dd96db --- /dev/null +++ b/config/samples/everest_v1alpha1_objectstorage.yaml @@ -0,0 +1,12 @@ +apiVersion: everest.percona.com/v1alpha1 +kind: ObjectStorage +metadata: + labels: + app.kubernetes.io/name: objectstorage + app.kubernetes.io/instance: objectstorage-sample + app.kubernetes.io/part-of: everest-operator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: everest-operator + name: objectstorage-sample +spec: + # TODO(user): Add fields here diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 0397dffac..0a298daa9 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -4,4 +4,5 @@ resources: - everest_v1alpha1_databaseclusterbackup.yaml - everest_v1alpha1_databaseclusterrestore.yaml - everest_v1alpha1_databaseengine.yaml +- everest_v1alpha1_objectstorage.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/controllers/databasecluster_controller.go b/controllers/databasecluster_controller.go index 9321d8dd6..28e3a14bc 100644 --- a/controllers/databasecluster_controller.go +++ b/controllers/databasecluster_controller.go @@ -18,9 +18,11 @@ package controllers import ( "context" + "crypto/rand" "encoding/base64" "encoding/json" "fmt" + "math/big" "os" "reflect" "regexp" @@ -30,7 +32,7 @@ import ( "github.com/AlekSi/pointer" goversion "github.com/hashicorp/go-version" - pgv2beta1 "github.com/percona/percona-postgresql-operator/pkg/apis/pg.percona.com/v2beta1" + pgv2 "github.com/percona/percona-postgresql-operator/pkg/apis/pgv2.percona.com/v2" crunchyv1beta1 "github.com/percona/percona-postgresql-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" psmdbv1 "github.com/percona/percona-server-mongodb-operator/pkg/apis/psmdb/v1" pxcv1 "github.com/percona/percona-xtradb-cluster-operator/pkg/apis/pxc/v1" @@ -73,14 +75,13 @@ const ( pxcDeploymentName = "percona-xtradb-cluster-operator" psmdbDeploymentName = "percona-server-mongodb-operator" pgDeploymentName = "percona-postgresql-operator" - pxcBackupImageTmpl = "percona/percona-xtradb-cluster-operator:%s-pxc8.0-backup" psmdbCRDName = "perconaservermongodbs.psmdb.percona.com" pxcCRDName = "perconaxtradbclusters.pxc.percona.com" - pgCRDName = "perconapgclusters.pg.percona.com" + pgCRDName = "perconapgclusters.pgv2.percona.com" pxcAPIGroup = "pxc.percona.com" psmdbAPIGroup = "psmdb.percona.com" - pgAPIGroup = "pg.percona.com" + pgAPIGroup = "pgv2.percona.com" haProxyTemplate = "percona/percona-xtradb-cluster-operator:%s-haproxy" restartAnnotationKey = "everest.percona.com/restart" dbTemplateKindAnnotationKey = "everest.percona.com/dbtemplate-kind" @@ -110,7 +111,11 @@ timeout server 28800s operationProfiling: mode: slowOp ` - backupStorageCredentialSecretName = ".spec.backup.storages.storageProvider.credentialsSecret" //nolint:gosec + objectStorageNameField = ".spec.backup.schedules.objectStorageName" + credentialsSecretNameField = ".spec.credentialsSecretName" //nolint:gosec + adminSecretNameField = ".spec.adminSecretName" //nolint:gosec + + passwordLength = 24 ) var operatorDeployment = map[everestv1alpha1.EngineType]string{ @@ -134,6 +139,9 @@ var defaultPXCSpec = pxcv1.PerconaXtraDBClusterSpec{ PodDisruptionBudget: &pxcv1.PodDisruptionBudgetSpec{ MaxUnavailable: &maxUnavailable, }, + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{}, + }, }, }, PMM: &pxcv1.PMMSpec{ @@ -151,6 +159,9 @@ var defaultPXCSpec = pxcv1.PerconaXtraDBClusterSpec{ Affinity: &pxcv1.PodAffinity{ TopologyKey: pointer.ToString(pxcv1.AffinityTopologyKeyOff), }, + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{}, + }, }, }, ProxySQL: &pxcv1.PodSpec{ @@ -158,6 +169,9 @@ var defaultPXCSpec = pxcv1.PerconaXtraDBClusterSpec{ Affinity: &pxcv1.PodAffinity{ TopologyKey: pointer.ToString(pxcv1.AffinityTopologyKeyOff), }, + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{}, + }, }, } @@ -225,6 +239,9 @@ var ( Affinity: &psmdbv1.PodAffinity{ TopologyKey: pointer.ToString(psmdbv1.AffinityOff), }, + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{}, + }, }, }, }, @@ -250,19 +267,25 @@ var ( Affinity: &psmdbv1.PodAffinity{ TopologyKey: pointer.ToString(psmdbv1.AffinityOff), }, + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{}, + }, }, }, }, } ) -var defaultPGSpec = pgv2beta1.PerconaPGClusterSpec{ - InstanceSets: pgv2beta1.PGInstanceSets{ +var defaultPGSpec = pgv2.PerconaPGClusterSpec{ + InstanceSets: pgv2.PGInstanceSets{ { Name: "instance1", + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{}, + }, }, }, - PMM: &pgv2beta1.PMMSpec{ + PMM: &pgv2.PMMSpec{ Enabled: false, Resources: corev1.ResourceRequirements{ Limits: corev1.ResourceList{ @@ -271,8 +294,12 @@ var defaultPGSpec = pgv2beta1.PerconaPGClusterSpec{ }, }, }, - Proxy: &pgv2beta1.PGProxySpec{ - PGBouncer: &pgv2beta1.PGBouncerSpec{}, + Proxy: &pgv2.PGProxySpec{ + PGBouncer: &pgv2.PGBouncerSpec{ + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{}, + }, + }, }, } @@ -290,8 +317,9 @@ type DatabaseClusterReconciler struct { //+kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions,verbs=get;list;watch //+kubebuilder:rbac:groups=pxc.percona.com,resources=perconaxtradbclusters,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=psmdb.percona.com,resources=perconaservermongodbs,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=pg.percona.com,resources=perconapgclusters,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=pgv2.percona.com,resources=perconapgclusters,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=everest.percona.com,resources=objectstorages,verbs=get;list;watch;create;update;patch;delete // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -316,28 +344,28 @@ func (r *DatabaseClusterReconciler) Reconcile(ctx context.Context, req ctrl.Requ logger.Info("Reconciled", "request", req) _, ok := database.ObjectMeta.Annotations[restartAnnotationKey] - if ok && !database.Spec.Pause { - database.Spec.Pause = true + if ok && !database.Spec.Paused { + database.Spec.Paused = true } - if ok && database.Status.State == everestv1alpha1.AppStatePaused { - database.Spec.Pause = false + if ok && database.Status.Status == everestv1alpha1.AppStatePaused { + database.Spec.Paused = false delete(database.ObjectMeta.Annotations, restartAnnotationKey) if err := r.Update(ctx, database); err != nil { return reconcile.Result{}, err } } - if database.Spec.Database == everestv1alpha1.DatabaseEnginePXC { + if database.Spec.Engine.Type == everestv1alpha1.DatabaseEnginePXC { err := r.reconcilePXC(ctx, req, database) return reconcile.Result{}, err } - if database.Spec.Database == everestv1alpha1.DatabaseEnginePSMDB { + if database.Spec.Engine.Type == everestv1alpha1.DatabaseEnginePSMDB { err := r.reconcilePSMDB(ctx, req, database) if err != nil { logger.Error(err, "unable to reconcile psmdb") } return reconcile.Result{}, err } - if database.Spec.Database == "postgresql" { + if database.Spec.Engine.Type == everestv1alpha1.DatabaseEnginePostgresql { err := r.reconcilePG(ctx, req, database) return reconcile.Result{}, err } @@ -371,6 +399,116 @@ func (r *DatabaseClusterReconciler) getClusterType(ctx context.Context) (Cluster return clusterType, nil } +func generatePassword(n int) (string, error) { + // PSMDB does not support all special characters in password https://jira.percona.com/browse/K8SPSMDB-364 + symbols := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + symbolsLen := len(symbols) + b := make([]rune, n) + for i := range b { + randomIndex, err := rand.Int(rand.Reader, big.NewInt(int64(symbolsLen))) + if err != nil { + return "", err + } + b[i] = symbols[randomIndex.Uint64()] + } + return string(b), nil +} + +func (r *DatabaseClusterReconciler) reconcileAdminSecret( + ctx context.Context, + database *everestv1alpha1.DatabaseCluster, + adminSecretName, + internalSecretName string, +) error { + adminSecret := &corev1.Secret{} + err := r.Get(ctx, types.NamespacedName{Name: adminSecretName, Namespace: database.Namespace}, adminSecret) + if err != nil && !k8serrors.IsNotFound(err) { + return err + } + + if k8serrors.IsNotFound(err) { + password, err := generatePassword(passwordLength) + if err != nil { + return errors.Wrapf(err, "unable to generate password for %s", adminSecretName) + } + + var username string + switch database.Spec.Engine.Type { + case everestv1alpha1.DatabaseEnginePXC: + username = "root" + case everestv1alpha1.DatabaseEnginePSMDB: + username = "userAdmin" + case everestv1alpha1.DatabaseEnginePostgresql: + username = "postgres" + default: + return errors.Errorf("unknown database engine %s", database.Spec.Engine.Type) + } + + adminSecret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: adminSecretName, + Namespace: database.Namespace, + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "username": []byte(username), + "password": []byte(password), + }, + } + err = r.Create(ctx, adminSecret) + if err != nil { + return errors.Wrapf(err, "unable to create secret %s", adminSecretName) + } + } + + internalSecret := &corev1.Secret{} + err = r.Get(ctx, types.NamespacedName{Name: internalSecretName, Namespace: database.Namespace}, internalSecret) + if err != nil && !k8serrors.IsNotFound(err) { + return err + } + + if k8serrors.IsNotFound(err) { + internalSecret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: internalSecretName, + Namespace: database.Namespace, + }, + Type: corev1.SecretTypeOpaque, + } + } + + if internalSecret.Data == nil { + internalSecret.Data = map[string][]byte{} + } + + switch database.Spec.Engine.Type { + case everestv1alpha1.DatabaseEnginePXC: + if string(adminSecret.Data["username"]) != "root" { + return errors.Errorf("username for %s is not root", adminSecretName) + } + internalSecret.Data["root"] = adminSecret.Data["password"] + case everestv1alpha1.DatabaseEnginePSMDB: + internalSecret.Data["MONGODB_USER_ADMIN_USER"] = adminSecret.Data["username"] + internalSecret.Data["MONGODB_USER_ADMIN_PASSWORD"] = adminSecret.Data["password"] + case everestv1alpha1.DatabaseEnginePostgresql: + if string(adminSecret.Data["username"]) != "postgres" { + return errors.Errorf("username for %s is not postgres", adminSecretName) + } + internalSecret.Data["user"] = adminSecret.Data["username"] + internalSecret.Data["password"] = adminSecret.Data["password"] + internalSecret.Data["verifier"] = []byte("") + default: + return errors.Errorf("unknown database engine %s", database.Spec.Engine.Type) + } + + err = r.createOrUpdate(ctx, internalSecret) + if err != nil { + return err + } + + return nil +} + func (r *DatabaseClusterReconciler) reconcileDBRestoreFromDataSource(ctx context.Context, database *everestv1alpha1.DatabaseCluster) error { dbRestore := &everestv1alpha1.DatabaseClusterRestore{ ObjectMeta: metav1.ObjectMeta{ @@ -382,23 +520,29 @@ func (r *DatabaseClusterReconciler) reconcileDBRestoreFromDataSource(ctx context return err } _, err := controllerutil.CreateOrUpdate(ctx, r.Client, dbRestore, func() error { + objectStorage := &everestv1alpha1.ObjectStorage{} + err := r.Get(ctx, types.NamespacedName{Name: database.Spec.DataSource.ObjectStorageName, Namespace: database.Namespace}, objectStorage) + if err != nil { + return errors.Wrapf(err, "failed to get object storage %s", database.Spec.DataSource.ObjectStorageName) + } + dbRestore.Spec.DatabaseCluster = database.Name - dbRestore.Spec.DatabaseType = database.Spec.Database + dbRestore.Spec.DatabaseType = database.Spec.Engine.Type dbRestore.Spec.BackupSource = &everestv1alpha1.BackupSource{ - Destination: database.Spec.DataSource.Destination, - StorageName: database.Spec.DataSource.StorageName, - StorageType: database.Spec.DataSource.StorageType, + Destination: fmt.Sprintf("s3://%s/%s", objectStorage.Spec.Bucket, database.Spec.DataSource.BackupName), + StorageName: database.Spec.DataSource.ObjectStorageName, + StorageType: everestv1alpha1.BackupStorageType(objectStorage.Spec.Type), } - switch database.Spec.DataSource.StorageType { - case everestv1alpha1.BackupStorageS3: + switch objectStorage.Spec.Type { + case everestv1alpha1.ObjectStorageTypeS3: dbRestore.Spec.BackupSource.S3 = &everestv1alpha1.BackupStorageProviderSpec{ - Bucket: database.Spec.DataSource.S3.Bucket, - CredentialsSecret: database.Spec.DataSource.S3.CredentialsSecret, - Region: database.Spec.DataSource.S3.Region, - EndpointURL: database.Spec.DataSource.S3.EndpointURL, + Bucket: objectStorage.Spec.Bucket, + CredentialsSecret: objectStorage.Spec.CredentialsSecretName, + Region: objectStorage.Spec.Region, + EndpointURL: objectStorage.Spec.EndpointURL, } default: - return errors.Errorf("unsupported data source storage type %s", database.Spec.DataSource.StorageType) + return errors.Errorf("unsupported object storage type %s for %s", objectStorage.Spec.Type, objectStorage.Name) } return nil }) @@ -406,6 +550,63 @@ func (r *DatabaseClusterReconciler) reconcileDBRestoreFromDataSource(ctx context return err } +func (r *DatabaseClusterReconciler) genPSMDBBackupSpec( + ctx context.Context, + database *everestv1alpha1.DatabaseCluster, + engine *everestv1alpha1.DatabaseEngine, +) (psmdbv1.BackupSpec, error) { + bestBackupVersion := engine.BestBackupVersion(database.Spec.Engine.Version) + backupVersion, ok := engine.Status.AvailableVersions.Backup[bestBackupVersion] + if !ok { + return psmdbv1.BackupSpec{Enabled: false}, errors.Errorf("backup version %s not available", bestBackupVersion) + } + + psmdbBackupSpec := psmdbv1.BackupSpec{ + Enabled: true, + Image: backupVersion.ImagePath, + } + storages := make(map[string]psmdbv1.BackupStorageSpec) + var tasks []psmdbv1.BackupTaskSpec //nolint:prealloc + for _, schedule := range database.Spec.Backup.Schedules { + if !schedule.Enabled { + continue + } + + objectStorage := &everestv1alpha1.ObjectStorage{} + err := r.Get(ctx, types.NamespacedName{Name: schedule.ObjectStorageName, Namespace: database.Namespace}, objectStorage) + if err != nil { + return psmdbv1.BackupSpec{Enabled: false}, errors.Wrapf(err, "failed to get object storage %s", schedule.ObjectStorageName) + } + + switch objectStorage.Spec.Type { + case everestv1alpha1.ObjectStorageTypeS3: + storages[schedule.ObjectStorageName] = psmdbv1.BackupStorageSpec{ + Type: psmdbv1.BackupStorageType(objectStorage.Spec.Type), + S3: psmdbv1.BackupStorageS3Spec{ + Bucket: objectStorage.Spec.Bucket, + CredentialsSecret: objectStorage.Spec.CredentialsSecretName, + Region: objectStorage.Spec.Region, + EndpointURL: objectStorage.Spec.EndpointURL, + }, + } + default: + return psmdbv1.BackupSpec{Enabled: false}, errors.Errorf("unsupported object storage type %s for %s", objectStorage.Spec.Type, objectStorage.Name) + } + + tasks = append(tasks, psmdbv1.BackupTaskSpec{ + Name: schedule.Name, + Enabled: true, + Schedule: schedule.Schedule, + Keep: int(schedule.RetentionCopies), + StorageName: schedule.ObjectStorageName, + }) + } + psmdbBackupSpec.Storages = storages + psmdbBackupSpec.Tasks = tasks + + return psmdbBackupSpec, nil +} + func (r *DatabaseClusterReconciler) reconcilePSMDB(ctx context.Context, req ctrl.Request, database *everestv1alpha1.DatabaseCluster) error { //nolint:gocognit,maintidx,gocyclo,lll,cyclop version, err := r.getOperatorVersion(ctx, types.NamespacedName{ Namespace: req.NamespacedName.Namespace, @@ -431,9 +632,9 @@ func (r *DatabaseClusterReconciler) reconcilePSMDB(ctx context.Context, req ctrl return err } engine := &everestv1alpha1.DatabaseEngine{} - err = r.Get(ctx, types.NamespacedName{Namespace: database.Namespace, Name: operatorDeployment[database.Spec.Database]}, engine) + err = r.Get(ctx, types.NamespacedName{Namespace: database.Namespace, Name: operatorDeployment[database.Spec.Engine.Type]}, engine) if err != nil { - return err + return errors.Wrapf(err, "failed to get database engine %s", operatorDeployment[database.Spec.Engine.Type]) } if err := controllerutil.SetControllerReference(database, psmdb, r.Client.Scheme()); err != nil { @@ -480,127 +681,131 @@ func (r *DatabaseClusterReconciler) reconcilePSMDB(ctx context.Context, req ctrl } psmdb.Spec.CRVersion = version.ToCRVersion() - psmdb.Spec.UnsafeConf = database.Spec.ClusterSize == 1 - psmdb.Spec.Pause = database.Spec.Pause - psmdb.Spec.Image = database.Spec.DatabaseImage + psmdb.Spec.UnsafeConf = database.Spec.Engine.Replicas == 1 + psmdb.Spec.Pause = database.Spec.Paused + + if database.Spec.Engine.Version == "" { + database.Spec.Engine.Version = engine.BestEngineVersion() + } + engineVersion, ok := engine.Status.AvailableVersions.Engine[database.Spec.Engine.Version] + if !ok { + return errors.Errorf("engine version %s not available", database.Spec.Engine.Version) + } + + psmdb.Spec.Image = engineVersion.ImagePath + + if database.Spec.AdminUserSecretName == "" { + database.Spec.AdminUserSecretName = database.Name + "-adminuser" + } psmdb.Spec.Secrets = &psmdbv1.SecretsSpec{ - Users: database.Spec.SecretsName, + Users: database.Name + "-secrets", + } + err = r.reconcileAdminSecret(ctx, database, database.Spec.AdminUserSecretName, psmdb.Spec.Secrets.Users) + if err != nil { + return err } psmdb.Spec.Mongod.Security.EncryptionKeySecret = fmt.Sprintf("%s-mongodb-encryption-key", database.Name) - if database.Spec.DatabaseConfig != "" { - psmdb.Spec.Replsets[0].Configuration = psmdbv1.MongoConfiguration(database.Spec.DatabaseConfig) + if database.Spec.Engine.Config != "" { + psmdb.Spec.Replsets[0].Configuration = psmdbv1.MongoConfiguration(database.Spec.Engine.Config) } if psmdb.Spec.Replsets[0].Configuration == "" { // Config missing from the DatabaseCluster CR and the template (if any), apply the default one psmdb.Spec.Replsets[0].Configuration = psmdbv1.MongoConfiguration(psmdbDefaultConfigurationTemplate) } - psmdb.Spec.Replsets[0].Size = database.Spec.ClusterSize + if database.Spec.Engine.Replicas == 0 { + database.Spec.Engine.Replicas = 3 + } + psmdb.Spec.Replsets[0].Size = database.Spec.Engine.Replicas psmdb.Spec.Replsets[0].VolumeSpec = &psmdbv1.VolumeSpec{ PersistentVolumeClaim: psmdbv1.PVCSpec{ PersistentVolumeClaimSpec: &corev1.PersistentVolumeClaimSpec{ - StorageClassName: database.Spec.DBInstance.StorageClassName, + StorageClassName: database.Spec.Engine.Storage.Class, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ - corev1.ResourceStorage: database.Spec.DBInstance.DiskSize, + corev1.ResourceStorage: database.Spec.Engine.Storage.Size, }, }, }, }, } - psmdb.Spec.Replsets[0].MultiAZ.Resources = corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceCPU: database.Spec.DBInstance.CPU, - corev1.ResourceMemory: database.Spec.DBInstance.Memory, - }, + if !database.Spec.Engine.Resources.CPU.IsZero() { + psmdb.Spec.Replsets[0].MultiAZ.Resources.Limits[corev1.ResourceCPU] = database.Spec.Engine.Resources.CPU + } + if !database.Spec.Engine.Resources.Memory.IsZero() { + psmdb.Spec.Replsets[0].MultiAZ.Resources.Limits[corev1.ResourceMemory] = database.Spec.Engine.Resources.Memory } - psmdb.Spec.Sharding.ConfigsvrReplSet.Size = database.Spec.ClusterSize + psmdb.Spec.Sharding.ConfigsvrReplSet.Size = database.Spec.Engine.Replicas psmdb.Spec.Sharding.ConfigsvrReplSet.VolumeSpec = &psmdbv1.VolumeSpec{ PersistentVolumeClaim: psmdbv1.PVCSpec{ PersistentVolumeClaimSpec: &corev1.PersistentVolumeClaimSpec{ - StorageClassName: database.Spec.DBInstance.StorageClassName, + StorageClassName: database.Spec.Engine.Storage.Class, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ - corev1.ResourceStorage: database.Spec.DBInstance.DiskSize, + corev1.ResourceStorage: database.Spec.Engine.Storage.Size, }, }, }, }, } - psmdb.Spec.Sharding.Mongos.Size = database.Spec.LoadBalancer.Size - psmdb.Spec.Sharding.Mongos.Expose = psmdbv1.MongosExpose{ - Expose: psmdbv1.Expose{ - ExposeType: database.Spec.LoadBalancer.ExposeType, - LoadBalancerSourceRanges: database.Spec.LoadBalancer.LoadBalancerSourceRanges, - ServiceAnnotations: database.Spec.LoadBalancer.Annotations, - }, + if database.Spec.Proxy.Replicas == nil { + // By default we set the same number of replicas as the engine + psmdb.Spec.Sharding.Mongos.Size = database.Spec.Engine.Replicas + } else { + psmdb.Spec.Sharding.Mongos.Size = *database.Spec.Proxy.Replicas + } + switch database.Spec.Proxy.Expose.Type { + case everestv1alpha1.ExposeTypeInternal: + psmdb.Spec.Sharding.Mongos.Expose = psmdbv1.MongosExpose{ + Expose: psmdbv1.Expose{ + ExposeType: corev1.ServiceTypeClusterIP, + }, + } + case everestv1alpha1.ExposeTypeExternal: + psmdb.Spec.Sharding.Mongos.Expose = psmdbv1.MongosExpose{ + Expose: psmdbv1.Expose{ + ExposeType: corev1.ServiceTypeLoadBalancer, + LoadBalancerSourceRanges: database.Spec.Proxy.Expose.IPSourceRanges, + }, + } + default: + return errors.Errorf("invalid expose type %s", database.Spec.Proxy.Expose.Type) + } + + psmdb.Spec.Sharding.Mongos.Configuration = psmdbv1.MongoConfiguration(database.Spec.Proxy.Config) + if !database.Spec.Proxy.Resources.CPU.IsZero() { + psmdb.Spec.Sharding.Mongos.MultiAZ.Resources.Limits[corev1.ResourceCPU] = database.Spec.Proxy.Resources.CPU } - psmdb.Spec.Sharding.Mongos.Configuration = psmdbv1.MongoConfiguration(database.Spec.LoadBalancer.Configuration) - psmdb.Spec.Sharding.Mongos.MultiAZ.Resources = database.Spec.LoadBalancer.Resources - if database.Spec.ClusterSize == 1 { + if !database.Spec.Proxy.Resources.Memory.IsZero() { + psmdb.Spec.Sharding.Mongos.MultiAZ.Resources.Limits[corev1.ResourceMemory] = database.Spec.Proxy.Resources.Memory + } + if database.Spec.Engine.Replicas == 1 { psmdb.Spec.Sharding.Enabled = false psmdb.Spec.Replsets[0].Expose.Enabled = true - psmdb.Spec.Replsets[0].Expose.ExposeType = database.Spec.LoadBalancer.ExposeType + switch database.Spec.Proxy.Expose.Type { + case everestv1alpha1.ExposeTypeInternal: + psmdb.Spec.Replsets[0].Expose.ExposeType = corev1.ServiceTypeClusterIP + case everestv1alpha1.ExposeTypeExternal: + psmdb.Spec.Replsets[0].Expose.ExposeType = corev1.ServiceTypeLoadBalancer + default: + return errors.Errorf("invalid expose type %s", database.Spec.Proxy.Expose.Type) + } + psmdb.Spec.Replsets[0].Expose.ExposeType = corev1.ServiceTypeClusterIP psmdb.Spec.Sharding.Mongos.Expose.ExposeType = corev1.ServiceTypeClusterIP } + if database.Spec.Monitoring.PMM != nil && database.Spec.Monitoring.PMM.Image != "" { psmdb.Spec.PMM.Enabled = true psmdb.Spec.PMM.ServerHost = database.Spec.Monitoring.PMM.PublicAddress psmdb.Spec.PMM.Image = database.Spec.Monitoring.PMM.Image } - if database.Spec.Backup != nil { - if database.Spec.Backup.Image == "" { - database.Spec.Backup.Image = engine.RecommendedBackupImage() - } - psmdb.Spec.Backup = psmdbv1.BackupSpec{ - Enabled: true, - Image: database.Spec.Backup.Image, - ServiceAccountName: database.Spec.Backup.ServiceAccountName, - ContainerSecurityContext: database.Spec.Backup.ContainerSecurityContext, - Resources: database.Spec.Backup.Resources, - Annotations: database.Spec.Backup.Annotations, - Labels: database.Spec.Backup.Labels, - } - storages := make(map[string]psmdbv1.BackupStorageSpec) - var tasks []psmdbv1.BackupTaskSpec - for k, v := range database.Spec.Backup.Storages { - switch v.Type { - case everestv1alpha1.BackupStorageS3: - storages[k] = psmdbv1.BackupStorageSpec{ - Type: psmdbv1.BackupStorageType(v.Type), - S3: psmdbv1.BackupStorageS3Spec{ - Bucket: v.StorageProvider.Bucket, - CredentialsSecret: v.StorageProvider.CredentialsSecret, - Region: v.StorageProvider.Region, - EndpointURL: v.StorageProvider.EndpointURL, - StorageClass: v.StorageProvider.StorageClass, - }, - } - case everestv1alpha1.BackupStorageAzure: - storages[k] = psmdbv1.BackupStorageSpec{ - Type: psmdbv1.BackupStorageType(v.Type), - Azure: psmdbv1.BackupStorageAzureSpec{ - Container: v.StorageProvider.ContainerName, - CredentialsSecret: v.StorageProvider.CredentialsSecret, - Prefix: v.StorageProvider.Prefix, - }, - } - } - } - for _, v := range database.Spec.Backup.Schedule { - tasks = append(tasks, psmdbv1.BackupTaskSpec{ - Name: v.Name, - Enabled: v.Enabled, - Keep: v.Keep, - Schedule: v.Schedule, - StorageName: v.StorageName, - CompressionType: v.CompressionType, - CompressionLevel: v.CompressionLevel, - }) + + if database.Spec.Backup.Enabled { + psmdb.Spec.Backup, err = r.genPSMDBBackupSpec(ctx, database, engine) + if err != nil { + return err } - psmdb.Spec.Backup.Storages = storages - psmdb.Spec.Backup.Tasks = tasks } return nil }) @@ -615,10 +820,10 @@ func (r *DatabaseClusterReconciler) reconcilePSMDB(ctx context.Context, req ctrl } } - database.Status.Host = psmdb.Status.Host + database.Status.Hostname = psmdb.Status.Host database.Status.Ready = psmdb.Status.Ready database.Status.Size = psmdb.Status.Size - database.Status.State = everestv1alpha1.AppState(psmdb.Status.State) + database.Status.Status = everestv1alpha1.AppState(psmdb.Status.State) message := psmdb.Status.Message conditions := psmdb.Status.Conditions if message == "" && len(conditions) != 0 { @@ -628,6 +833,161 @@ func (r *DatabaseClusterReconciler) reconcilePSMDB(ctx context.Context, req ctrl return r.Status().Update(ctx, database) } +func (r *DatabaseClusterReconciler) genPXCHAProxySpec(database *everestv1alpha1.DatabaseCluster, engine *everestv1alpha1.DatabaseEngine) (*pxcv1.HAProxySpec, error) { + haProxy := defaultPXCSpec.HAProxy + + haProxy.PodSpec.Enabled = true + + if database.Spec.Proxy.Replicas == nil { + // By default we set the same number of replicas as the engine + haProxy.PodSpec.Size = database.Spec.Engine.Replicas + } else { + haProxy.PodSpec.Size = *database.Spec.Proxy.Replicas + } + + switch database.Spec.Proxy.Expose.Type { + case everestv1alpha1.ExposeTypeInternal: + haProxy.PodSpec.ServiceType = corev1.ServiceTypeClusterIP + haProxy.PodSpec.ReplicasServiceType = corev1.ServiceTypeClusterIP + case everestv1alpha1.ExposeTypeExternal: + haProxy.PodSpec.ServiceType = corev1.ServiceTypeLoadBalancer + haProxy.PodSpec.ReplicasServiceType = corev1.ServiceTypeLoadBalancer + haProxy.PodSpec.LoadBalancerSourceRanges = database.Spec.Proxy.Expose.IPSourceRanges + default: + return nil, errors.Errorf("invalid expose type %s", database.Spec.Proxy.Expose.Type) + } + + haProxy.PodSpec.Configuration = database.Spec.Proxy.Config + + haProxyAvailVersions, ok := engine.Status.AvailableVersions.Proxy[everestv1alpha1.ProxyTypeHAProxy] + if !ok { + return nil, errors.Errorf("haproxy version not available") + } + + bestHAProxyVersion := haProxyAvailVersions.BestVersion() + haProxyVersion, ok := haProxyAvailVersions[bestHAProxyVersion] + if !ok { + return nil, errors.Errorf("haproxy version %s not available", bestHAProxyVersion) + } + + haProxy.PodSpec.Image = haProxyVersion.ImagePath + + if !database.Spec.Proxy.Resources.CPU.IsZero() { + haProxy.PodSpec.Resources.Limits[corev1.ResourceCPU] = database.Spec.Proxy.Resources.CPU + } + if !database.Spec.Proxy.Resources.Memory.IsZero() { + haProxy.PodSpec.Resources.Limits[corev1.ResourceMemory] = database.Spec.Proxy.Resources.Memory + } + + return haProxy, nil +} + +func (r *DatabaseClusterReconciler) genPXCProxySQLSpec(database *everestv1alpha1.DatabaseCluster, engine *everestv1alpha1.DatabaseEngine) (*pxcv1.PodSpec, error) { + proxySQL := defaultPXCSpec.ProxySQL + + proxySQL.Enabled = true + + if database.Spec.Proxy.Replicas == nil { + // By default we set the same number of replicas as the engine + proxySQL.Size = database.Spec.Engine.Replicas + } else { + proxySQL.Size = *database.Spec.Proxy.Replicas + } + + switch database.Spec.Proxy.Expose.Type { + case everestv1alpha1.ExposeTypeInternal: + proxySQL.ServiceType = corev1.ServiceTypeClusterIP + proxySQL.ReplicasServiceType = corev1.ServiceTypeClusterIP + case everestv1alpha1.ExposeTypeExternal: + proxySQL.ServiceType = corev1.ServiceTypeLoadBalancer + proxySQL.ReplicasServiceType = corev1.ServiceTypeLoadBalancer + proxySQL.LoadBalancerSourceRanges = database.Spec.Proxy.Expose.IPSourceRanges + default: + return nil, errors.Errorf("invalid expose type %s", database.Spec.Proxy.Expose.Type) + } + + proxySQL.Configuration = database.Spec.Proxy.Config + + proxySQLAvailVersions, ok := engine.Status.AvailableVersions.Proxy[everestv1alpha1.ProxyTypeProxySQL] + if !ok { + return nil, errors.Errorf("proxysql version not available") + } + + bestProxySQLVersion := proxySQLAvailVersions.BestVersion() + proxySQLVersion, ok := proxySQLAvailVersions[bestProxySQLVersion] + if !ok { + return nil, errors.Errorf("proxysql version %s not available", bestProxySQLVersion) + } + + proxySQL.Image = proxySQLVersion.ImagePath + + if !database.Spec.Proxy.Resources.CPU.IsZero() { + proxySQL.Resources.Limits[corev1.ResourceCPU] = database.Spec.Proxy.Resources.CPU + } + if !database.Spec.Proxy.Resources.Memory.IsZero() { + proxySQL.Resources.Limits[corev1.ResourceMemory] = database.Spec.Proxy.Resources.Memory + } + + return proxySQL, nil +} + +func (r *DatabaseClusterReconciler) genPXCBackupSpec( + ctx context.Context, + database *everestv1alpha1.DatabaseCluster, + engine *everestv1alpha1.DatabaseEngine, +) (*pxcv1.PXCScheduledBackup, error) { + bestBackupVersion := engine.BestBackupVersion(database.Spec.Engine.Version) + backupVersion, ok := engine.Status.AvailableVersions.Backup[bestBackupVersion] + if !ok { + return nil, errors.Errorf("backup version %s not available", bestBackupVersion) + } + + pxcBackupSpec := &pxcv1.PXCScheduledBackup{ + Image: backupVersion.ImagePath, + } + + storages := make(map[string]*pxcv1.BackupStorageSpec) + var pxcSchedules []pxcv1.PXCScheduledBackupSchedule //nolint:prealloc + for _, schedule := range database.Spec.Backup.Schedules { + if !schedule.Enabled { + continue + } + + objectStorage := &everestv1alpha1.ObjectStorage{} + err := r.Get(ctx, types.NamespacedName{Name: schedule.ObjectStorageName, Namespace: database.Namespace}, objectStorage) + if err != nil { + return nil, errors.Wrapf(err, "failed to get object storage %s", schedule.ObjectStorageName) + } + + storages[schedule.ObjectStorageName] = &pxcv1.BackupStorageSpec{ + Type: pxcv1.BackupStorageType(objectStorage.Spec.Type), + } + switch objectStorage.Spec.Type { + case everestv1alpha1.ObjectStorageTypeS3: + storages[schedule.ObjectStorageName].S3 = &pxcv1.BackupStorageS3Spec{ + Bucket: objectStorage.Spec.Bucket, + CredentialsSecret: objectStorage.Spec.CredentialsSecretName, + Region: objectStorage.Spec.Region, + EndpointURL: objectStorage.Spec.EndpointURL, + } + default: + return nil, errors.Errorf("unsupported object storage type %s for %s", objectStorage.Spec.Type, objectStorage.Name) + } + + pxcSchedules = append(pxcSchedules, pxcv1.PXCScheduledBackupSchedule{ + Name: schedule.Name, + Schedule: schedule.Schedule, + Keep: int(schedule.RetentionCopies), + StorageName: schedule.ObjectStorageName, + }) + } + + pxcBackupSpec.Storages = storages + pxcBackupSpec.Schedule = pxcSchedules + + return pxcBackupSpec, nil +} + func (r *DatabaseClusterReconciler) reconcilePXC(ctx context.Context, req ctrl.Request, database *everestv1alpha1.DatabaseCluster) error { //nolint:lll,gocognit,gocyclo,cyclop,maintidx version, err := r.getOperatorVersion(ctx, types.NamespacedName{ Namespace: req.NamespacedName.Namespace, @@ -644,7 +1004,7 @@ func (r *DatabaseClusterReconciler) reconcilePXC(ctx context.Context, req ctrl.R return err } } - if current.Spec.Pause != database.Spec.Pause { + if current.Spec.Pause != database.Spec.Paused { // During the restoration of PXC clusters // They need to be shutted down // @@ -677,7 +1037,7 @@ func (r *DatabaseClusterReconciler) reconcilePXC(ctx context.Context, req ctrl.R } } if jobRunning { - database.Spec.Pause = current.Spec.Pause + database.Spec.Paused = current.Spec.Pause } } @@ -689,17 +1049,28 @@ func (r *DatabaseClusterReconciler) reconcilePXC(ctx context.Context, req ctrl.R }, Spec: defaultPXCSpec, } + if len(database.Finalizers) != 0 { pxc.Finalizers = database.Finalizers database.Finalizers = []string{} } - if database.Spec.LoadBalancer.Type == "haproxy" && database.Spec.LoadBalancer.Configuration == "" { - database.Spec.LoadBalancer.Configuration = haProxyDefaultConfigurationTemplate + + if database.Spec.Proxy.Type == "" { + database.Spec.Proxy.Type = everestv1alpha1.ProxyTypeHAProxy + } + if database.Spec.Proxy.Type == everestv1alpha1.ProxyTypeHAProxy && database.Spec.Proxy.Config == "" { + database.Spec.Proxy.Config = haProxyDefaultConfigurationTemplate } if err := r.Update(ctx, database); err != nil { return err } + engine := &everestv1alpha1.DatabaseEngine{} + err = r.Get(ctx, types.NamespacedName{Name: pxcDeploymentName, Namespace: database.Namespace}, engine) + if err != nil { + return errors.Wrapf(err, "failed to get database engine %s", pxcDeploymentName) + } + if err := controllerutil.SetControllerReference(database, pxc, r.Client.Scheme()); err != nil { return err } @@ -743,24 +1114,32 @@ func (r *DatabaseClusterReconciler) reconcilePXC(ctx context.Context, req ctrl.R } pxc.Spec.CRVersion = version.ToCRVersion() - pxc.Spec.AllowUnsafeConfig = database.Spec.ClusterSize == 1 - pxc.Spec.Pause = database.Spec.Pause - pxc.Spec.SecretsName = database.Spec.SecretsName + pxc.Spec.AllowUnsafeConfig = database.Spec.Engine.Replicas == 1 + pxc.Spec.Pause = database.Spec.Paused - if database.Spec.DatabaseConfig != "" { - pxc.Spec.PXC.PodSpec.Configuration = database.Spec.DatabaseConfig + if database.Spec.AdminUserSecretName == "" { + database.Spec.AdminUserSecretName = database.Name + "-adminuser" + } + pxc.Spec.SecretsName = database.Name + "-secrets" + err = r.reconcileAdminSecret(ctx, database, database.Spec.AdminUserSecretName, pxc.Spec.SecretsName) + if err != nil { + return err + } + + if database.Spec.Engine.Config != "" { + pxc.Spec.PXC.PodSpec.Configuration = database.Spec.Engine.Config } if pxc.Spec.PXC.PodSpec.Configuration == "" { // Config missing from the DatabaseCluster CR and the template (if any), apply the default one gCacheSize := "600M" - if database.Spec.DBInstance.Memory.CmpInt64(memorySmallSize) > 0 && database.Spec.DBInstance.Memory.CmpInt64(memoryMediumSize) <= 0 { + if database.Spec.Engine.Resources.Memory.CmpInt64(memorySmallSize) > 0 && database.Spec.Engine.Resources.Memory.CmpInt64(memoryMediumSize) <= 0 { gCacheSize = "2457M" } - if database.Spec.DBInstance.Memory.CmpInt64(memoryMediumSize) > 0 && database.Spec.DBInstance.Memory.CmpInt64(memoryLargeSize) <= 0 { + if database.Spec.Engine.Resources.Memory.CmpInt64(memoryMediumSize) > 0 && database.Spec.Engine.Resources.Memory.CmpInt64(memoryLargeSize) <= 0 { gCacheSize = "9830M" } - if database.Spec.DBInstance.Memory.CmpInt64(memoryLargeSize) >= 0 { + if database.Spec.Engine.Resources.Memory.CmpInt64(memoryLargeSize) >= 0 { gCacheSize = "9830M" } ver, _ := goversion.NewVersion("v1.11.0") @@ -770,116 +1149,68 @@ func (r *DatabaseClusterReconciler) reconcilePXC(ctx context.Context, req ctrl.R } } - pxc.Spec.PXC.PodSpec.Size = database.Spec.ClusterSize - pxc.Spec.PXC.PodSpec.Image = database.Spec.DatabaseImage + if database.Spec.Engine.Replicas == 0 { + database.Spec.Engine.Replicas = 3 + } + pxc.Spec.PXC.PodSpec.Size = database.Spec.Engine.Replicas + + if database.Spec.Engine.Version == "" { + database.Spec.Engine.Version = engine.BestEngineVersion() + } + pxcEngineVersion, ok := engine.Status.AvailableVersions.Engine[database.Spec.Engine.Version] + if !ok { + return errors.Errorf("engine version %s not available", database.Spec.Engine.Version) + } + + pxc.Spec.PXC.PodSpec.Image = pxcEngineVersion.ImagePath + pxc.Spec.PXC.PodSpec.VolumeSpec = &pxcv1.VolumeSpec{ PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{ - StorageClassName: database.Spec.DBInstance.StorageClassName, + StorageClassName: database.Spec.Engine.Storage.Class, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ - corev1.ResourceStorage: database.Spec.DBInstance.DiskSize, + corev1.ResourceStorage: database.Spec.Engine.Storage.Size, }, }, }, } - pxc.Spec.PXC.PodSpec.Resources = corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceCPU: database.Spec.DBInstance.CPU, - corev1.ResourceMemory: database.Spec.DBInstance.Memory, - }, + + if !database.Spec.Engine.Resources.CPU.IsZero() { + pxc.Spec.PXC.PodSpec.Resources.Limits[corev1.ResourceCPU] = database.Spec.Engine.Resources.CPU + } + if !database.Spec.Engine.Resources.Memory.IsZero() { + pxc.Spec.PXC.PodSpec.Resources.Limits[corev1.ResourceMemory] = database.Spec.Engine.Resources.Memory } - if database.Spec.LoadBalancer.Type == "haproxy" { - pxc.Spec.ProxySQL.Enabled = false - if database.Spec.LoadBalancer.Image == "" { - database.Spec.LoadBalancer.Image = fmt.Sprintf(haProxyTemplate, version.String()) + switch database.Spec.Proxy.Type { + case everestv1alpha1.ProxyTypeHAProxy: + pxc.Spec.ProxySQL.Enabled = false + pxc.Spec.HAProxy, err = r.genPXCHAProxySpec(database, engine) + if err != nil { + return err } - pxc.Spec.HAProxy.PodSpec.Size = database.Spec.LoadBalancer.Size - pxc.Spec.HAProxy.PodSpec.ServiceType = database.Spec.LoadBalancer.ExposeType - pxc.Spec.HAProxy.PodSpec.ReplicasServiceType = database.Spec.LoadBalancer.ExposeType - pxc.Spec.HAProxy.PodSpec.Configuration = database.Spec.LoadBalancer.Configuration - pxc.Spec.HAProxy.PodSpec.LoadBalancerSourceRanges = database.Spec.LoadBalancer.LoadBalancerSourceRanges - pxc.Spec.HAProxy.PodSpec.Annotations = database.Spec.LoadBalancer.Annotations - pxc.Spec.HAProxy.PodSpec.ExternalTrafficPolicy = database.Spec.LoadBalancer.TrafficPolicy - pxc.Spec.HAProxy.PodSpec.ReplicasExternalTrafficPolicy = database.Spec.LoadBalancer.TrafficPolicy - pxc.Spec.HAProxy.PodSpec.Resources = database.Spec.LoadBalancer.Resources - pxc.Spec.HAProxy.PodSpec.Enabled = true - pxc.Spec.HAProxy.PodSpec.Image = database.Spec.LoadBalancer.Image - } - if database.Spec.LoadBalancer.Type == "proxysql" { + case everestv1alpha1.ProxyTypeProxySQL: pxc.Spec.HAProxy.PodSpec.Enabled = false - - pxc.Spec.ProxySQL.Size = database.Spec.LoadBalancer.Size - pxc.Spec.ProxySQL.ServiceType = database.Spec.LoadBalancer.ExposeType - pxc.Spec.ProxySQL.Configuration = database.Spec.LoadBalancer.Configuration - pxc.Spec.ProxySQL.LoadBalancerSourceRanges = database.Spec.LoadBalancer.LoadBalancerSourceRanges - pxc.Spec.ProxySQL.Annotations = database.Spec.LoadBalancer.Annotations - pxc.Spec.ProxySQL.ExternalTrafficPolicy = database.Spec.LoadBalancer.TrafficPolicy - pxc.Spec.ProxySQL.Resources = database.Spec.LoadBalancer.Resources - pxc.Spec.ProxySQL.Enabled = true - pxc.Spec.ProxySQL.Image = database.Spec.LoadBalancer.Image + pxc.Spec.ProxySQL, err = r.genPXCProxySQLSpec(database, engine) + if err != nil { + return err + } + default: + return errors.Errorf("invalid proxy type %s", database.Spec.Proxy.Type) } + if database.Spec.Monitoring.PMM != nil { pxc.Spec.PMM.Enabled = true pxc.Spec.PMM.ServerHost = database.Spec.Monitoring.PMM.PublicAddress pxc.Spec.PMM.ServerUser = database.Spec.Monitoring.PMM.Login pxc.Spec.PMM.Image = database.Spec.Monitoring.PMM.Image } - if database.Spec.Backup != nil { - if database.Spec.Backup.Image == "" { - database.Spec.Backup.Image = fmt.Sprintf(pxcBackupImageTmpl, pxc.Spec.CRVersion) - } - pxc.Spec.Backup = &pxcv1.PXCScheduledBackup{ - Image: database.Spec.Backup.Image, - ImagePullSecrets: database.Spec.Backup.ImagePullSecrets, - ImagePullPolicy: database.Spec.Backup.ImagePullPolicy, - ServiceAccountName: database.Spec.Backup.ServiceAccountName, - } - storages := make(map[string]*pxcv1.BackupStorageSpec) - var schedules []pxcv1.PXCScheduledBackupSchedule - for k, v := range database.Spec.Backup.Storages { - storages[k] = &pxcv1.BackupStorageSpec{ - Type: pxcv1.BackupStorageType(v.Type), - NodeSelector: v.NodeSelector, - Resources: v.Resources, - Affinity: v.Affinity, - Tolerations: v.Tolerations, - Annotations: v.Annotations, - Labels: v.Labels, - SchedulerName: v.SchedulerName, - PriorityClassName: v.PriorityClassName, - PodSecurityContext: v.PodSecurityContext, - ContainerSecurityContext: v.ContainerSecurityContext, - RuntimeClassName: v.RuntimeClassName, - VerifyTLS: v.VerifyTLS, - } - switch v.Type { - case everestv1alpha1.BackupStorageS3: - storages[k].S3 = &pxcv1.BackupStorageS3Spec{ - Bucket: v.StorageProvider.Bucket, - CredentialsSecret: v.StorageProvider.CredentialsSecret, - Region: v.StorageProvider.Region, - EndpointURL: v.StorageProvider.EndpointURL, - } - case everestv1alpha1.BackupStorageAzure: - storages[k].Azure = &pxcv1.BackupStorageAzureSpec{ - ContainerPath: v.StorageProvider.ContainerName, - CredentialsSecret: v.StorageProvider.CredentialsSecret, - StorageClass: v.StorageProvider.StorageClass, - Endpoint: v.StorageProvider.EndpointURL, - } - } - } - for _, v := range database.Spec.Backup.Schedule { - schedules = append(schedules, pxcv1.PXCScheduledBackupSchedule{ - Name: v.Name, - Schedule: v.Schedule, - Keep: v.Keep, - StorageName: v.StorageName, - }) + + if database.Spec.Backup.Enabled { + pxc.Spec.Backup, err = r.genPXCBackupSpec(ctx, database, engine) + if err != nil { + return err } - pxc.Spec.Backup.Storages = storages - pxc.Spec.Backup.Schedule = schedules } return nil }) @@ -894,8 +1225,8 @@ func (r *DatabaseClusterReconciler) reconcilePXC(ctx context.Context, req ctrl.R } } - database.Status.Host = pxc.Status.Host - database.Status.State = everestv1alpha1.AppState(pxc.Status.Status) + database.Status.Hostname = pxc.Status.Host + database.Status.Status = everestv1alpha1.AppState(pxc.Status.Status) database.Status.Ready = pxc.Status.Ready database.Status.Size = pxc.Status.Size database.Status.Message = strings.Join(pxc.Status.Messages, ";") @@ -941,43 +1272,58 @@ func (r *DatabaseClusterReconciler) createPGBackrestSecret( return pgBackrestSecret, nil } -func (r *DatabaseClusterReconciler) genPGBackupsSpec(ctx context.Context, database *everestv1alpha1.DatabaseCluster) (crunchyv1beta1.Backups, error) { +func (r *DatabaseClusterReconciler) genPGBackupsSpec( + ctx context.Context, + database *everestv1alpha1.DatabaseCluster, + engine *everestv1alpha1.DatabaseEngine, +) (crunchyv1beta1.Backups, error) { + pgbackrestVersion, ok := engine.Status.AvailableVersions.Backup[database.Spec.Engine.Version] + if !ok { + return crunchyv1beta1.Backups{}, errors.Errorf("pgbackrest version %s not available", database.Spec.Engine.Version) + } + backups := crunchyv1beta1.Backups{ PGBackRest: crunchyv1beta1.PGBackRestArchive{ Global: map[string]string{}, - Image: database.Spec.Backup.Image, + Image: pgbackrestVersion.ImagePath, }, } - if len(database.Spec.Backup.Schedule) > 4 { + if len(database.Spec.Backup.Schedules) > 4 { return crunchyv1beta1.Backups{}, errors.Errorf("number of backup schedules for postgresql cannot exceed 4") } - repos := make([]crunchyv1beta1.PGBackRestRepo, len(database.Spec.Backup.Schedule)) - for idx, v := range database.Spec.Backup.Schedule { - storage, ok := database.Spec.Backup.Storages[v.StorageName] - if !ok { - return crunchyv1beta1.Backups{}, errors.Errorf("unknown backup storage %s", v.StorageName) + repos := make([]crunchyv1beta1.PGBackRestRepo, len(database.Spec.Backup.Schedules)) + for idx, schedule := range database.Spec.Backup.Schedules { + if !schedule.Enabled { + continue } + + objectStorage := &everestv1alpha1.ObjectStorage{} + err := r.Get(ctx, types.NamespacedName{Name: schedule.ObjectStorageName, Namespace: database.Namespace}, objectStorage) + if err != nil { + return crunchyv1beta1.Backups{}, errors.Wrapf(err, "failed to get object storage %s", schedule.ObjectStorageName) + } + repos[idx] = crunchyv1beta1.PGBackRestRepo{ Name: fmt.Sprintf("repo%d", idx+1), BackupSchedules: &crunchyv1beta1.PGBackRestBackupSchedules{ - Full: &database.Spec.Backup.Schedule[idx].Schedule, + Full: &database.Spec.Backup.Schedules[idx].Schedule, }, } - backups.PGBackRest.Global[repos[idx].Name+"-retention-full"] = fmt.Sprintf("%d", database.Spec.Backup.Schedule[idx].Keep) + backups.PGBackRest.Global[repos[idx].Name+"-retention-full"] = fmt.Sprintf("%d", database.Spec.Backup.Schedules[idx].RetentionCopies) - switch storage.Type { - case everestv1alpha1.BackupStorageS3: + switch objectStorage.Spec.Type { + case everestv1alpha1.ObjectStorageTypeS3: repos[idx].S3 = &crunchyv1beta1.RepoS3{ - Bucket: storage.StorageProvider.Bucket, - Endpoint: storage.StorageProvider.EndpointURL, - Region: storage.StorageProvider.Region, + Bucket: objectStorage.Spec.Bucket, + Region: objectStorage.Spec.Region, + Endpoint: objectStorage.Spec.EndpointURL, } pgBackrestSecret, err := r.createPGBackrestSecret( ctx, database, - storage.StorageProvider.CredentialsSecret, + objectStorage.Spec.CredentialsSecretName, repos[idx].Name, database.Name+"-pgbackrest-secrets", ) @@ -995,7 +1341,7 @@ func (r *DatabaseClusterReconciler) genPGBackupsSpec(ctx context.Context, databa }, } default: - return crunchyv1beta1.Backups{}, errors.Errorf("unsupported backup storage type %s for %s", storage.Type, v.StorageName) + return crunchyv1beta1.Backups{}, errors.Errorf("unsupported object storage type %s for %s", objectStorage.Spec.Type, objectStorage.Name) } } backups.PGBackRest.Repos = repos @@ -1003,9 +1349,9 @@ func (r *DatabaseClusterReconciler) genPGBackupsSpec(ctx context.Context, databa } func (r *DatabaseClusterReconciler) genPGDataSourceSpec(ctx context.Context, database *everestv1alpha1.DatabaseCluster) (*crunchyv1beta1.DataSource, error) { - destMatch := regexp.MustCompile(`/pgbackrest/(repo\d+)/backup/db/(.*)$`).FindStringSubmatch(database.Spec.DataSource.Destination) + destMatch := regexp.MustCompile(`/pgbackrest/(repo\d+)/backup/db/(.*)$`).FindStringSubmatch(database.Spec.DataSource.BackupName) if len(destMatch) < 3 { - return nil, errors.Errorf("failed to extract the pgbackrest repo and backup names from %s", database.Spec.DataSource.Destination) + return nil, errors.Errorf("failed to extract the pgbackrest repo and backup names from %s", database.Spec.DataSource.BackupName) } repoName := destMatch[1] backupName := destMatch[2] @@ -1023,16 +1369,18 @@ func (r *DatabaseClusterReconciler) genPGDataSourceSpec(ctx context.Context, dat }, } - switch database.Spec.DataSource.StorageType { - case everestv1alpha1.BackupStorageS3: - if database.Spec.DataSource.S3 == nil { - return nil, errors.Errorf("data source storage is of type %s but is missing s3 field", everestv1alpha1.BackupStorageS3) - } + objectStorage := &everestv1alpha1.ObjectStorage{} + err := r.Get(ctx, types.NamespacedName{Name: database.Spec.DataSource.ObjectStorageName, Namespace: database.Namespace}, objectStorage) + if err != nil { + return nil, errors.Wrapf(err, "failed to get object storage %s", database.Spec.DataSource.ObjectStorageName) + } + switch objectStorage.Spec.Type { + case everestv1alpha1.ObjectStorageTypeS3: pgBackrestSecret, err := r.createPGBackrestSecret( ctx, database, - database.Spec.DataSource.S3.CredentialsSecret, + objectStorage.Spec.CredentialsSecretName, repoName, database.Name+"-pgbackrest-datasource-secrets", ) @@ -1052,29 +1400,26 @@ func (r *DatabaseClusterReconciler) genPGDataSourceSpec(ctx context.Context, dat pgDataSource.PGBackRest.Repo = crunchyv1beta1.PGBackRestRepo{ Name: repoName, S3: &crunchyv1beta1.RepoS3{ - Bucket: database.Spec.DataSource.S3.Bucket, - Endpoint: database.Spec.DataSource.S3.EndpointURL, - Region: database.Spec.DataSource.S3.Region, + Bucket: objectStorage.Spec.Bucket, + Endpoint: objectStorage.Spec.EndpointURL, + Region: objectStorage.Spec.Region, }, } default: - return nil, errors.Errorf("unsupported data source storage type \"%s\"", database.Spec.DataSource.StorageType) + return nil, errors.Errorf("unsupported object storage type %s for %s", objectStorage.Spec.Type, objectStorage.Name) } return pgDataSource, nil } -func (r *DatabaseClusterReconciler) reconcilePG(ctx context.Context, _ ctrl.Request, database *everestv1alpha1.DatabaseCluster) error { - opVersion, err := r.getOperatorVersion(ctx, types.NamespacedName{ - Namespace: database.Namespace, +//nolint:gocognit,maintidx,gocyclo,cyclop +func (r *DatabaseClusterReconciler) reconcilePG(ctx context.Context, req ctrl.Request, database *everestv1alpha1.DatabaseCluster) error { + version, err := r.getOperatorVersion(ctx, types.NamespacedName{ + Namespace: req.NamespacedName.Namespace, Name: pgDeploymentName, }) if err != nil { return err } - version, err := NewVersion("v2beta1") - if err != nil { - return err - } clusterType, err := r.getClusterType(ctx) if err != nil { return err @@ -1095,7 +1440,7 @@ func (r *DatabaseClusterReconciler) reconcilePG(ctx context.Context, _ ctrl.Requ pgSpec.Proxy.PGBouncer.Affinity = affinity } - pg := &pgv2beta1.PerconaPGCluster{ + pg := &pgv2.PerconaPGCluster{ ObjectMeta: metav1.ObjectMeta{ Name: database.Name, Namespace: database.Namespace, @@ -1111,6 +1456,12 @@ func (r *DatabaseClusterReconciler) reconcilePG(ctx context.Context, _ ctrl.Requ return err } + engine := &everestv1alpha1.DatabaseEngine{} + err = r.Get(ctx, types.NamespacedName{Name: pgDeploymentName, Namespace: database.Namespace}, engine) + if err != nil { + return errors.Wrapf(err, "failed to get database engine %s", pgDeploymentName) + } + if err := controllerutil.SetControllerReference(database, pg, r.Client.Scheme()); err != nil { return err } @@ -1120,53 +1471,103 @@ func (r *DatabaseClusterReconciler) reconcilePG(ctx context.Context, _ ctrl.Requ Kind: PerconaPGClusterKind, } - //nolint:godox - // FIXME add the secrets name when - // https://jira.percona.com/browse/K8SPG-309 is fixed - // pg.Spec.SecretsName = database.Spec.SecretsName - pg.Spec.Pause = &database.Spec.Pause - pg.Spec.Image = database.Spec.DatabaseImage - pgVersionMatch := regexp.MustCompile(`-ppg(\d+)-`).FindStringSubmatch(database.Spec.DatabaseImage) - if len(pgVersionMatch) < 2 { - return errors.Errorf("failed to extract the PostgresVersion from %s", database.Spec.DatabaseImage) - } - pgVersion, err := strconv.Atoi(pgVersionMatch[1]) + if database.Spec.AdminUserSecretName == "" { + database.Spec.AdminUserSecretName = database.Name + "-adminuser" + } + pg.Spec.Proxy.PGBouncer.ExposeSuperusers = true + pg.Spec.Users = []crunchyv1beta1.PostgresUserSpec{ + { + Name: "postgres", + }, + } + err = r.reconcileAdminSecret(ctx, database, database.Spec.AdminUserSecretName, database.Name+"-pguser-"+string(pg.Spec.Users[0].Name)) if err != nil { return err } - pg.Spec.PostgresVersion = pgVersion - pg.Spec.InstanceSets[0].Replicas = &database.Spec.ClusterSize - pg.Spec.InstanceSets[0].Resources = corev1.ResourceRequirements{ - Limits: corev1.ResourceList{ - corev1.ResourceCPU: database.Spec.DBInstance.CPU, - corev1.ResourceMemory: database.Spec.DBInstance.Memory, - }, + pg.Spec.Pause = &database.Spec.Paused + if database.Spec.Engine.Version == "" { + database.Spec.Engine.Version = engine.BestEngineVersion() + } + pgEngineVersion, ok := engine.Status.AvailableVersions.Engine[database.Spec.Engine.Version] + if !ok { + return errors.Errorf("engine version %s not available", database.Spec.Engine.Version) + } + + pg.Spec.Image = pgEngineVersion.ImagePath + + pgMajorVersionMatch := regexp.MustCompile(`^(\d+)`).FindStringSubmatch(database.Spec.Engine.Version) + if len(pgMajorVersionMatch) < 2 { + return errors.Errorf("failed to extract the major version from %s", database.Spec.Engine.Version) + } + pgMajorVersion, err := strconv.Atoi(pgMajorVersionMatch[1]) + if err != nil { + return err + } + pg.Spec.PostgresVersion = pgMajorVersion + + if database.Spec.Engine.Replicas == 0 { + database.Spec.Engine.Replicas = 3 + } + pg.Spec.InstanceSets[0].Replicas = &database.Spec.Engine.Replicas + if !database.Spec.Engine.Resources.CPU.IsZero() { + pg.Spec.InstanceSets[0].Resources.Limits[corev1.ResourceCPU] = database.Spec.Engine.Resources.CPU + } + if !database.Spec.Engine.Resources.Memory.IsZero() { + pg.Spec.InstanceSets[0].Resources.Limits[corev1.ResourceMemory] = database.Spec.Engine.Resources.Memory } pg.Spec.InstanceSets[0].DataVolumeClaimSpec = corev1.PersistentVolumeClaimSpec{ AccessModes: []corev1.PersistentVolumeAccessMode{ corev1.ReadWriteOnce, }, - StorageClassName: database.Spec.DBInstance.StorageClassName, + StorageClassName: database.Spec.Engine.Storage.Class, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ - corev1.ResourceStorage: database.Spec.DBInstance.DiskSize, + corev1.ResourceStorage: database.Spec.Engine.Storage.Size, }, }, } - pg.Spec.Proxy.PGBouncer.Image = database.Spec.LoadBalancer.Image - pg.Spec.Proxy.PGBouncer.Replicas = &database.Spec.LoadBalancer.Size + pgbouncerAvailVersions, ok := engine.Status.AvailableVersions.Proxy["pgbouncer"] + if !ok { + return errors.Errorf("pgbouncer version not available") + } + + pgbouncerVersion, ok := pgbouncerAvailVersions[database.Spec.Engine.Version] + if !ok { + return errors.Errorf("pgbouncer version %s not available", database.Spec.Engine.Version) + } + + pg.Spec.Proxy.PGBouncer.Image = pgbouncerVersion.ImagePath + + if database.Spec.Proxy.Replicas == nil { + // By default we set the same number of replicas as the engine + pg.Spec.Proxy.PGBouncer.Replicas = &database.Spec.Engine.Replicas + } else { + pg.Spec.Proxy.PGBouncer.Replicas = database.Spec.Proxy.Replicas + } //nolint:godox // TODO add support for database.Spec.LoadBalancer.LoadBalancerSourceRanges // https://jira.percona.com/browse/K8SPG-311 - pg.Spec.Proxy.PGBouncer.ServiceExpose = &pgv2beta1.ServiceExpose{ - Metadata: crunchyv1beta1.Metadata{ - Annotations: database.Spec.LoadBalancer.Annotations, - }, - Type: string(database.Spec.LoadBalancer.ExposeType), + switch database.Spec.Proxy.Expose.Type { + case everestv1alpha1.ExposeTypeInternal: + pg.Spec.Proxy.PGBouncer.ServiceExpose = &pgv2.ServiceExpose{ + Type: string(corev1.ServiceTypeClusterIP), + } + case everestv1alpha1.ExposeTypeExternal: + pg.Spec.Proxy.PGBouncer.ServiceExpose = &pgv2.ServiceExpose{ + Type: string(corev1.ServiceTypeLoadBalancer), + } + default: + return errors.Errorf("invalid expose type %s", database.Spec.Proxy.Expose.Type) + } + + if !database.Spec.Proxy.Resources.CPU.IsZero() { + pg.Spec.Proxy.PGBouncer.Resources.Limits[corev1.ResourceCPU] = database.Spec.Proxy.Resources.CPU + } + if !database.Spec.Proxy.Resources.Memory.IsZero() { + pg.Spec.Proxy.PGBouncer.Resources.Limits[corev1.ResourceMemory] = database.Spec.Proxy.Resources.Memory } - pg.Spec.Proxy.PGBouncer.Resources = database.Spec.LoadBalancer.Resources if database.Spec.Monitoring.PMM != nil { pg.Spec.PMM.Enabled = true @@ -1178,9 +1579,13 @@ func (r *DatabaseClusterReconciler) reconcilePG(ctx context.Context, _ ctrl.Requ // Without credentials need to define a PVC-backed repo because the // pg-operator requires a backup to be set up in order to create // replicas. + pgbackrestVersion, ok := engine.Status.AvailableVersions.Backup[database.Spec.Engine.Version] + if !ok { + return errors.Errorf("pgbackrest version %s not available", database.Spec.Engine.Version) + } pg.Spec.Backups = crunchyv1beta1.Backups{ PGBackRest: crunchyv1beta1.PGBackRestArchive{ - Image: fmt.Sprintf("percona/percona-postgresql-operator:%s-ppg%d-pgbackrest", opVersion.String(), pgVersion), + Image: pgbackrestVersion.ImagePath, Repos: []crunchyv1beta1.PGBackRestRepo{ { Name: "repo1", @@ -1189,10 +1594,10 @@ func (r *DatabaseClusterReconciler) reconcilePG(ctx context.Context, _ ctrl.Requ AccessModes: []corev1.PersistentVolumeAccessMode{ corev1.ReadWriteOnce, }, - StorageClassName: database.Spec.DBInstance.StorageClassName, + StorageClassName: database.Spec.Engine.Storage.Class, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ - corev1.ResourceStorage: database.Spec.DBInstance.DiskSize, + corev1.ResourceStorage: database.Spec.Engine.Storage.Size, }, }, }, @@ -1201,11 +1606,9 @@ func (r *DatabaseClusterReconciler) reconcilePG(ctx context.Context, _ ctrl.Requ }, }, } - if database.Spec.Backup != nil { - if database.Spec.Backup.Image == "" { - database.Spec.Backup.Image = fmt.Sprintf("percona/percona-postgresql-operator:%s-ppg%d-pgbackrest", opVersion.String(), pgVersion) - } - pg.Spec.Backups, err = r.genPGBackupsSpec(ctx, database) + + if database.Spec.Backup.Enabled { + pg.Spec.Backups, err = r.genPGBackupsSpec(ctx, database, engine) if err != nil { return err } @@ -1224,8 +1627,8 @@ func (r *DatabaseClusterReconciler) reconcilePG(ctx context.Context, _ ctrl.Requ return err } - database.Status.Host = pg.Status.Host - database.Status.State = everestv1alpha1.AppState(pg.Status.State) + database.Status.Hostname = pg.Status.Host + database.Status.Status = everestv1alpha1.AppState(pg.Status.State) database.Status.Ready = pg.Status.Postgres.Ready + pg.Status.PGBouncer.Ready database.Status.Size = pg.Status.Postgres.Size + pg.Status.PGBouncer.Size return r.Status().Update(ctx, database) @@ -1259,10 +1662,10 @@ func (r *DatabaseClusterReconciler) addPXCKnownTypes(scheme *runtime.Scheme) err if err != nil { return err } - pxcSchemeGroupVersion := schema.GroupVersion{Group: "pxc.percona.com", Version: strings.ReplaceAll("v"+version.String(), ".", "-")} + pxcSchemeGroupVersion := schema.GroupVersion{Group: pxcAPIGroup, Version: strings.ReplaceAll("v"+version.String(), ".", "-")} ver, _ := goversion.NewVersion("v1.11.0") if version.version.GreaterThan(ver) { - pxcSchemeGroupVersion = schema.GroupVersion{Group: "pxc.percona.com", Version: "v1"} + pxcSchemeGroupVersion = schema.GroupVersion{Group: pxcAPIGroup, Version: "v1"} } scheme.AddKnownTypes(pxcSchemeGroupVersion, @@ -1280,10 +1683,10 @@ func (r *DatabaseClusterReconciler) addPSMDBKnownTypes(scheme *runtime.Scheme) e if err != nil { return err } - psmdbSchemeGroupVersion := schema.GroupVersion{Group: "psmdb.percona.com", Version: strings.ReplaceAll("v"+version.String(), ".", "-")} + psmdbSchemeGroupVersion := schema.GroupVersion{Group: psmdbAPIGroup, Version: strings.ReplaceAll("v"+version.String(), ".", "-")} ver, _ := goversion.NewVersion("v1.12.0") if version.version.GreaterThan(ver) { - psmdbSchemeGroupVersion = schema.GroupVersion{Group: "psmdb.percona.com", Version: "v1"} + psmdbSchemeGroupVersion = schema.GroupVersion{Group: psmdbAPIGroup, Version: "v1"} } scheme.AddKnownTypes(psmdbSchemeGroupVersion, &psmdbv1.PerconaServerMongoDB{}, &psmdbv1.PerconaServerMongoDBList{}) @@ -1293,9 +1696,9 @@ func (r *DatabaseClusterReconciler) addPSMDBKnownTypes(scheme *runtime.Scheme) e } func (r *DatabaseClusterReconciler) addPGKnownTypes(scheme *runtime.Scheme) error { - pgSchemeGroupVersion := schema.GroupVersion{Group: "pg.percona.com", Version: "v2beta1"} + pgSchemeGroupVersion := schema.GroupVersion{Group: pgAPIGroup, Version: "v2"} scheme.AddKnownTypes(pgSchemeGroupVersion, - &pgv2beta1.PerconaPGCluster{}, &pgv2beta1.PerconaPGClusterList{}) + &pgv2.PerconaPGCluster{}, &pgv2.PerconaPGClusterList{}) metav1.AddToGroupVersion(scheme, pgSchemeGroupVersion) return nil @@ -1318,20 +1721,59 @@ func (r *DatabaseClusterReconciler) addPGToScheme(scheme *runtime.Scheme) error // SetupWithManager sets up the controller with the Manager. func (r *DatabaseClusterReconciler) SetupWithManager(mgr ctrl.Manager) error { - err := mgr.GetFieldIndexer().IndexField(context.Background(), &everestv1alpha1.DatabaseCluster{}, backupStorageCredentialSecretName, func(o client.Object) []string { + // Index the ObjectStorage's CredentialsSecretName field so that it can be + // used by the databaseClustersThatReferenceCredentialsSecret function to + // find all DatabaseClusters that reference a specific secret through the + // ObjectStorage's CredentialsSecretName field + err := mgr.GetFieldIndexer().IndexField(context.Background(), &everestv1alpha1.ObjectStorage{}, credentialsSecretNameField, func(o client.Object) []string { + var res []string + objectStorage, ok := o.(*everestv1alpha1.ObjectStorage) + if !ok { + return res + } + res = append(res, objectStorage.Spec.CredentialsSecretName) + return res + }) + if err != nil { + return err + } + + // Index the ObjectStorageName so that it can be used by the + // databaseClustersThatReferenceObjectStorage function to find all + // DatabaseClusters that reference a specific ObjectStorage through the + // ObjectStorageName field + err = mgr.GetFieldIndexer().IndexField(context.Background(), &everestv1alpha1.DatabaseCluster{}, objectStorageNameField, func(o client.Object) []string { var res []string database, ok := o.(*everestv1alpha1.DatabaseCluster) - if !ok || database.Spec.Backup == nil { + if !ok || !database.Spec.Backup.Enabled { return res } - for _, storage := range database.Spec.Backup.Storages { - res = append(res, storage.StorageProvider.CredentialsSecret) + for _, storage := range database.Spec.Backup.Schedules { + res = append(res, storage.ObjectStorageName) + } + return res + }) + if err != nil { + return err + } + + // Index the AdminUserSecretName field so that it can be used by the + // databaseClustersThatReferenceAdminUserSecret function to find all + // DatabaseClusters that reference a specific secret through the + // AdminUserSecretName field + err = mgr.GetFieldIndexer().IndexField(context.Background(), &everestv1alpha1.DatabaseCluster{}, adminSecretNameField, func(o client.Object) []string { + var res []string + database, ok := o.(*everestv1alpha1.DatabaseCluster) + if !ok { + return res } + res = append(res, database.Spec.AdminUserSecretName) return res }) if err != nil { return err } + unstructuredResource := &unstructured.Unstructured{} unstructuredResource.SetGroupVersionKind(schema.GroupVersionKind{ Group: "apiextensions.k8s.io", @@ -1355,7 +1797,7 @@ func (r *DatabaseClusterReconciler) SetupWithManager(mgr ctrl.Manager) error { err = r.Get(context.Background(), types.NamespacedName{Name: pgCRDName}, unstructuredResource) if err == nil { if err := r.addPGToScheme(r.Scheme); err == nil { - controller.Owns(&pgv2beta1.PerconaPGCluster{}) + controller.Owns(&pgv2.PerconaPGCluster{}) } } // In PG reconciliation we create a backup credentials secret because the @@ -1364,19 +1806,97 @@ func (r *DatabaseClusterReconciler) SetupWithManager(mgr ctrl.Manager) error { // secrets, specifically the ones that are referenced in DatabaseCluster // CRs, and trigger a reconciliation if these change so that we can // reenconde the secret required by PG. + controller.Owns(&everestv1alpha1.ObjectStorage{}) + controller.Watches( + &everestv1alpha1.ObjectStorage{}, + handler.EnqueueRequestsFromMapFunc(r.databaseClustersThatReferenceObjectStorage), + builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), + ) controller.Owns(&corev1.Secret{}) controller.Watches( &corev1.Secret{}, - handler.EnqueueRequestsFromMapFunc(r.findObjectsForBackupSecretsName), + handler.EnqueueRequestsFromMapFunc(r.databaseClustersThatReferenceCredentialsSecret), + builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), + ) + controller.Watches( + &corev1.Secret{}, + handler.EnqueueRequestsFromMapFunc(r.databaseClustersThatReferenceAdminUserSecret), builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), ) return controller.Complete(r) } -func (r *DatabaseClusterReconciler) findObjectsForBackupSecretsName(ctx context.Context, secret client.Object) []reconcile.Request { +// databaseClustersThatReferenceObjectStorage returns a list of reconcile +// requests for all DatabaseClusters that reference the given ObjectStorage. +func (r *DatabaseClusterReconciler) databaseClustersThatReferenceObjectStorage(ctx context.Context, objectStorage client.Object) []reconcile.Request { attachedDatabaseClusters := &everestv1alpha1.DatabaseClusterList{} listOps := &client.ListOptions{ - FieldSelector: fields.OneTermEqualSelector(backupStorageCredentialSecretName, secret.GetName()), + FieldSelector: fields.OneTermEqualSelector(objectStorageNameField, objectStorage.GetName()), + Namespace: objectStorage.GetNamespace(), + } + err := r.List(ctx, attachedDatabaseClusters, listOps) + if err != nil { + return []reconcile.Request{} + } + + requests := make([]reconcile.Request, len(attachedDatabaseClusters.Items)) + for i, item := range attachedDatabaseClusters.Items { + requests[i] = reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: item.GetName(), + Namespace: item.GetNamespace(), + }, + } + } + + return requests +} + +// databaseClustersThatReferenceCredentialsSecret returns a list of reconcile +// requests for all DatabaseClusters that reference the given secret. +func (r *DatabaseClusterReconciler) databaseClustersThatReferenceCredentialsSecret(ctx context.Context, secret client.Object) []reconcile.Request { + attachedObjectStorage := &everestv1alpha1.ObjectStorageList{} + listOps := &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(credentialsSecretNameField, secret.GetName()), + Namespace: secret.GetNamespace(), + } + err := r.List(ctx, attachedObjectStorage, listOps) + if err != nil { + return []reconcile.Request{} + } + + var requests []reconcile.Request + for _, objectStorage := range attachedObjectStorage.Items { + attachedDatabaseClusters := &everestv1alpha1.DatabaseClusterList{} + listOps := &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(objectStorageNameField, objectStorage.GetName()), + Namespace: secret.GetNamespace(), + } + err = r.List(ctx, attachedDatabaseClusters, listOps) + if err != nil { + return []reconcile.Request{} + } + + for _, item := range attachedDatabaseClusters.Items { + request := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: item.GetName(), + Namespace: item.GetNamespace(), + }, + } + requests = append(requests, request) + } + } + + return requests +} + +// databaseClustersThatReferenceAdminUserSecret returns a list of reconcile +// requests for all DatabaseClusters that reference the given admin secret. +func (r *DatabaseClusterReconciler) databaseClustersThatReferenceAdminUserSecret(ctx context.Context, secret client.Object) []reconcile.Request { + attachedDatabaseClusters := &everestv1alpha1.DatabaseClusterList{} + listOps := &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(adminSecretNameField, secret.GetName()), Namespace: secret.GetNamespace(), } err := r.List(ctx, attachedDatabaseClusters, listOps) @@ -1393,6 +1913,7 @@ func (r *DatabaseClusterReconciler) findObjectsForBackupSecretsName(ctx context. }, } } + return requests } diff --git a/controllers/databaseclusterrestore_controller.go b/controllers/databaseclusterrestore_controller.go index 5debec8fe..b37b9f3a3 100644 --- a/controllers/databaseclusterrestore_controller.go +++ b/controllers/databaseclusterrestore_controller.go @@ -106,7 +106,7 @@ func (r *DatabaseClusterRestoreReconciler) ensureClusterIsReady(restore *everest if err != nil { return err } - if cluster.Status.State == everestv1alpha1.AppStateReady { + if cluster.Status.Status == everestv1alpha1.AppStateReady { return nil } } diff --git a/controllers/databaseengine_controller.go b/controllers/databaseengine_controller.go index f65311ef5..9108506ae 100644 --- a/controllers/databaseengine_controller.go +++ b/controllers/databaseengine_controller.go @@ -100,11 +100,11 @@ func (r *DatabaseEngineReconciler) Reconcile(ctx context.Context, req ctrl.Reque } if dbEngine.Spec.Type == everestv1alpha1.DatabaseEnginePXC { versions.Engine = matrix.PXC - versions.Proxy = map[string]map[string]*everestv1alpha1.Component{ - "haproxy": matrix.HAProxy, - "proxysql": matrix.ProxySQL, + versions.Proxy = map[everestv1alpha1.ProxyType]everestv1alpha1.ComponentsMap{ + everestv1alpha1.ProxyTypeHAProxy: matrix.HAProxy, + everestv1alpha1.ProxyTypeProxySQL: matrix.ProxySQL, } - versions.Tools = map[string]map[string]*everestv1alpha1.Component{ + versions.Tools = map[string]everestv1alpha1.ComponentsMap{ "logCollector": matrix.LogCollector, } } @@ -114,8 +114,8 @@ func (r *DatabaseEngineReconciler) Reconcile(ctx context.Context, req ctrl.Reque if dbEngine.Spec.Type == everestv1alpha1.DatabaseEnginePostgresql { versions.Engine = matrix.Postgresql versions.Backup = matrix.PGBackRest - versions.Proxy = map[string]map[string]*everestv1alpha1.Component{ - "pgbouncer": matrix.PGBouncer, + versions.Proxy = map[everestv1alpha1.ProxyType]everestv1alpha1.ComponentsMap{ + everestv1alpha1.ProxyTypePGBouncer: matrix.PGBouncer, } } dbEngine.Status.AvailableVersions = versions diff --git a/go.mod b/go.mod index 437f2adb0..0261a2464 100644 --- a/go.mod +++ b/go.mod @@ -7,8 +7,7 @@ require ( github.com/hashicorp/go-version v1.6.0 github.com/onsi/ginkgo/v2 v2.11.0 github.com/onsi/gomega v1.27.8 - github.com/percona/percona-backup-mongodb v1.8.1-0.20221024072933-3ec38a5fc670 - github.com/percona/percona-postgresql-operator v0.0.0-20230504183427-1e192bc639d4 + github.com/percona/percona-postgresql-operator v0.0.0-20230629061704-21f8d7d89b98 github.com/percona/percona-server-mongodb-operator v1.14.0 github.com/percona/percona-xtradb-cluster-operator v1.13.0 github.com/pkg/errors v0.9.1 @@ -68,6 +67,7 @@ require ( github.com/mongodb/mongo-tools v0.0.0-20220803145531-1d46e6e7021f // indirect github.com/montanaflynn/stats v0.6.6 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/percona/percona-backup-mongodb v1.8.1-0.20221024072933-3ec38a5fc670 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.15.1 // indirect diff --git a/go.sum b/go.sum index f9a31e2bc..004e3e61b 100644 --- a/go.sum +++ b/go.sum @@ -406,8 +406,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/percona/percona-backup-mongodb v1.8.1-0.20221024072933-3ec38a5fc670 h1:QJ+ID/EDB/E/Eo6wGlDi6K+iKoi9bt5ntiO0HM5DD2U= github.com/percona/percona-backup-mongodb v1.8.1-0.20221024072933-3ec38a5fc670/go.mod h1:vq130+8euuK+U03alsLdmJyiK/iIhq7Qs3ApSACwFDc= -github.com/percona/percona-postgresql-operator v0.0.0-20230504183427-1e192bc639d4 h1:7L/tWpYoYiWQGTu8xiC4mE0jZQRP6dWYwSasktHzisM= -github.com/percona/percona-postgresql-operator v0.0.0-20230504183427-1e192bc639d4/go.mod h1:0bE55H+B5BQY/lURgM1tJ3ZSDtvX0uxlI/otBP40aSQ= +github.com/percona/percona-postgresql-operator v0.0.0-20230629061704-21f8d7d89b98 h1:TBc6YKTrJm8rg1Z43vXLMniyi7hwMZqBaGdKN/fbXLI= +github.com/percona/percona-postgresql-operator v0.0.0-20230629061704-21f8d7d89b98/go.mod h1:Wbw7DyZtHKWJpy2kELRuRTlG9dW0CVAn8faT7POBjC4= github.com/percona/percona-server-mongodb-operator v1.14.0 h1:m0Vjr8cnXWT/Wq0AeQ9RjtRNJQQ0qOc1a0V7ycAv+R0= github.com/percona/percona-server-mongodb-operator v1.14.0/go.mod h1:JL0hpVCrvL+a/+FHOAzy906WEY51mauEXtYqmJgR5NI= github.com/percona/percona-xtradb-cluster-operator v1.13.0 h1:KhXkjK3hRCLdEtbcuc9ynKiY7fJ1IxRqZTLgd6R6xz0=