diff --git a/PROJECT b/PROJECT index 5eb59a52..3351ebc1 100644 --- a/PROJECT +++ b/PROJECT @@ -4,4 +4,7 @@ resources: - group: database kind: Postgres version: v1 +- group: database + kind: PostgresProfile + version: v1 version: "2" diff --git a/api/v1/postgres_profile.go b/api/v1/postgres_profile.go deleted file mode 100644 index 48ea7e24..00000000 --- a/api/v1/postgres_profile.go +++ /dev/null @@ -1,10 +0,0 @@ -package v1 - -// PostgresProfile defines possible values and our defaults for the zalando operator deployment -// will be configured during postgres-controller deployment -// TODO should be a CRD as well to keep configuration identical -type PostgresProfile struct { - Versions []string - OperatorVersion string - // TODO more -} diff --git a/api/v1/postgres_types.go b/api/v1/postgres_types.go index 47d966d4..f1cd73d6 100644 --- a/api/v1/postgres_types.go +++ b/api/v1/postgres_types.go @@ -72,9 +72,11 @@ type AccessList struct { // Backup configure parametes of the database backup type Backup struct { - // Retention defines how many backups will reside. - // FIXME check zalando CRD for other options, e.g. Days + // Retention defines how many days a backup will persist Retention int32 `json:"retention,omitempty"` + + // Schedule defines how often a backup should be made, in cron format + Schedule string `json:"schedule,omitempty"` } // Size defines the size aspects of the database @@ -87,13 +89,32 @@ type Size struct { StorageSize string `json:"storage_size,omitempty"` } -// Maintenance configures automatic database maintenance +// Weekday defines a weekday or everyday +type Weekday int + +const ( + Sun Weekday = iota + Mon + Tue + Wed + Thu + Fri + Sat + All +) + +// TimeWindow defines an interval in time +type TimeWindow struct { + Start metav1.Time `json:"start,omitempty"` + End metav1.Time `json:"end,omitempty"` +} + +// Maintenance configures database maintenance type Maintenance struct { - // Auto if set to true database maintenance is done by the operator - Auto bool `json:"auto,omitempty"` - // Window defines the time window where the maintenance will happen if Auto is set to true - // FIXME format - TimeWindow string `json:"time_window,omitempty"` + // Weekday defines when the operator is allowed to do maintenance + Weekday Weekday `json:"weekday,omitempty"` + // TimeWindow defines when the maintenance should happen + TimeWindow TimeWindow `json:"time_window,omitempty"` } // PostgresStatus defines the observed state of Postgres diff --git a/api/v1/postgresprofile_types.go b/api/v1/postgresprofile_types.go new file mode 100644 index 00000000..3677f36f --- /dev/null +++ b/api/v1/postgresprofile_types.go @@ -0,0 +1,78 @@ +/* + + +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 v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// +kubebuilder:object:root=true + +// PostgresProfile is the Schema for the postgresprofiles API +type PostgresProfile struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PostgresProfileSpec `json:"spec,omitempty"` + Status PostgresProfileStatus `json:"status,omitempty"` +} + +// PostgresProfileSpec defines the desired state of PostgresProfile +type PostgresProfileSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + NumberOfInstances []int `json:"number_of_instances,omitempty"` + Operators []Operator `json:"operators,omitempty"` + Sizes []Size `json:"sizes,omitempty"` + Versions []string `json:"versions,omitempty"` + + DefaultBackup Backup `json:"default_backup,omitempty"` + DefaultMaintenance Maintenance `json:"default_maintenance,omitempty"` + DefaultNumberOfInstances int `json:"default_number_of_instances,omitempty"` + DefaultOperator Operator `json:"default_operator,omitempty"` + DefaultSize Size `json:"default_size,omitempty"` + DefaultVersion string `json:"default_version,omitempty"` +} + +// Operator defines which provider of the operator should be used and its version +type Operator struct { + Provider string `json:"provider,omitempty"` + Version []string `json:"version,omitempty"` +} + +// PostgresProfileStatus defines the observed state of PostgresProfile +type PostgresProfileStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +// +kubebuilder:object:root=true + +// PostgresProfileList contains a list of PostgresProfile +type PostgresProfileList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []PostgresProfile `json:"items"` +} + +func init() { + SchemeBuilder.Register(&PostgresProfile{}, &PostgresProfileList{}) +} diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 19f4c69a..6d5cf2d1 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -62,6 +62,7 @@ func (in *Backup) DeepCopy() *Backup { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Maintenance) DeepCopyInto(out *Maintenance) { *out = *in + in.TimeWindow.DeepCopyInto(&out.TimeWindow) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Maintenance. @@ -74,6 +75,26 @@ func (in *Maintenance) DeepCopy() *Maintenance { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Operator) DeepCopyInto(out *Operator) { + *out = *in + if in.Version != nil { + in, out := &in.Version, &out.Version + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Operator. +func (in *Operator) DeepCopy() *Operator { + if in == nil { + return nil + } + out := new(Operator) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Postgres) DeepCopyInto(out *Postgres) { *out = *in @@ -136,19 +157,114 @@ func (in *PostgresList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PostgresProfile) DeepCopyInto(out *PostgresProfile) { *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresProfile. +func (in *PostgresProfile) DeepCopy() *PostgresProfile { + if in == nil { + return nil + } + out := new(PostgresProfile) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PostgresProfile) 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 *PostgresProfileList) DeepCopyInto(out *PostgresProfileList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PostgresProfile, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresProfileList. +func (in *PostgresProfileList) DeepCopy() *PostgresProfileList { + if in == nil { + return nil + } + out := new(PostgresProfileList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PostgresProfileList) 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 *PostgresProfileSpec) DeepCopyInto(out *PostgresProfileSpec) { + *out = *in + if in.NumberOfInstances != nil { + in, out := &in.NumberOfInstances, &out.NumberOfInstances + *out = make([]int, len(*in)) + copy(*out, *in) + } + if in.Operators != nil { + in, out := &in.Operators, &out.Operators + *out = make([]Operator, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Sizes != nil { + in, out := &in.Sizes, &out.Sizes + *out = make([]Size, len(*in)) + copy(*out, *in) + } if in.Versions != nil { in, out := &in.Versions, &out.Versions *out = make([]string, len(*in)) copy(*out, *in) } + out.DefaultBackup = in.DefaultBackup + in.DefaultMaintenance.DeepCopyInto(&out.DefaultMaintenance) + in.DefaultOperator.DeepCopyInto(&out.DefaultOperator) + out.DefaultSize = in.DefaultSize } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresProfile. -func (in *PostgresProfile) DeepCopy() *PostgresProfile { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresProfileSpec. +func (in *PostgresProfileSpec) DeepCopy() *PostgresProfileSpec { if in == nil { return nil } - out := new(PostgresProfile) + out := new(PostgresProfileSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PostgresProfileStatus) DeepCopyInto(out *PostgresProfileStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresProfileStatus. +func (in *PostgresProfileStatus) DeepCopy() *PostgresProfileStatus { + if in == nil { + return nil + } + out := new(PostgresProfileStatus) in.DeepCopyInto(out) return out } @@ -157,7 +273,7 @@ func (in *PostgresProfile) DeepCopy() *PostgresProfile { func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) { *out = *in out.Size = in.Size - out.Maintenance = in.Maintenance + in.Maintenance.DeepCopyInto(&out.Maintenance) out.Backup = in.Backup in.AccessList.DeepCopyInto(&out.AccessList) } @@ -201,3 +317,20 @@ func (in *Size) DeepCopy() *Size { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TimeWindow) DeepCopyInto(out *TimeWindow) { + *out = *in + in.Start.DeepCopyInto(&out.Start) + in.End.DeepCopyInto(&out.End) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TimeWindow. +func (in *TimeWindow) DeepCopy() *TimeWindow { + if in == nil { + return nil + } + out := new(TimeWindow) + in.DeepCopyInto(out) + return out +} diff --git a/config/crd/bases/database.fits.cloud_postgres.yaml b/config/crd/bases/database.fits.cloud_postgres.yaml index 1179f981..0868ad19 100644 --- a/config/crd/bases/database.fits.cloud_postgres.yaml +++ b/config/crd/bases/database.fits.cloud_postgres.yaml @@ -58,10 +58,13 @@ spec: description: Backup parametes of the database backup properties: retention: - description: Retention defines how many backups will reside. FIXME - check zalando CRD for other options, e.g. Days + description: Retention defines how many days a backup will persist format: int32 type: integer + schedule: + description: Schedule defines how often a backup should be made, + in cron format + type: string type: object description: description: Description @@ -69,14 +72,20 @@ spec: maintenance: description: Maintenance defines automatic maintenance of the database properties: - auto: - description: Auto if set to true database maintenance is done by - the operator - type: boolean time_window: - description: Window defines the time window where the maintenance - will happen if Auto is set to true FIXME format - type: string + description: TimeWindow defines when the maintenance should happen + properties: + end: + format: date-time + type: string + start: + format: date-time + type: string + type: object + weekday: + description: Weekday defines when the operator is allowed to do + maintenance + type: integer type: object number_of_instances: description: NumberOfInstances number of replicas diff --git a/config/crd/bases/database.fits.cloud_postgresprofiles.yaml b/config/crd/bases/database.fits.cloud_postgresprofiles.yaml new file mode 100644 index 00000000..fd74840e --- /dev/null +++ b/config/crd/bases/database.fits.cloud_postgresprofiles.yaml @@ -0,0 +1,148 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.3.0 + creationTimestamp: null + name: postgresprofiles.database.fits.cloud +spec: + group: database.fits.cloud + names: + kind: PostgresProfile + listKind: PostgresProfileList + plural: postgresprofiles + singular: postgresprofile + scope: Namespaced + validation: + openAPIV3Schema: + description: PostgresProfile is the Schema for the postgresprofiles 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: PostgresProfileSpec defines the desired state of PostgresProfile + properties: + default_backup: + description: Backup configure parametes of the database backup + properties: + retention: + description: Retention defines how many days a backup will persist + format: int32 + type: integer + schedule: + description: Schedule defines how often a backup should be made, + in cron format + type: string + type: object + default_maintenance: + description: Maintenance configures database maintenance + properties: + time_window: + description: TimeWindow defines when the maintenance should happen + properties: + end: + format: date-time + type: string + start: + format: date-time + type: string + type: object + weekday: + description: Weekday defines when the operator is allowed to do + maintenance + type: integer + type: object + default_number_of_instances: + type: integer + default_operator: + description: Operator defines which provider of the operator should + be used and its version + properties: + provider: + type: string + version: + items: + type: string + type: array + type: object + default_size: + description: Size defines the size aspects of the database + properties: + cpu: + description: CPU is in the format as pod.spec.resource.request.cpu + type: string + shared_buffer: + description: SharedBuffer of the database + type: string + storage_size: + description: StorageSize the amount of Storage this database will + get + type: string + type: object + default_version: + type: string + number_of_instances: + items: + type: integer + type: array + operators: + items: + description: Operator defines which provider of the operator should + be used and its version + properties: + provider: + type: string + version: + items: + type: string + type: array + type: object + type: array + sizes: + items: + description: Size defines the size aspects of the database + properties: + cpu: + description: CPU is in the format as pod.spec.resource.request.cpu + type: string + shared_buffer: + description: SharedBuffer of the database + type: string + storage_size: + description: StorageSize the amount of Storage this database will + get + type: string + type: object + type: array + versions: + items: + type: string + type: array + type: object + status: + description: PostgresProfileStatus defines the observed state of PostgresProfile + type: object + type: object + version: v1 + versions: + - name: v1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 53243ef7..2db1a402 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -3,17 +3,20 @@ # It should be run by config/default resources: - bases/database.fits.cloud_postgres.yaml +- bases/database.fits.cloud_postgresprofiles.yaml # +kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD - patches/webhook_in_postgres.yaml +#- patches/webhook_in_postgresprofiles.yaml # +kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD - patches/cainjection_in_postgres.yaml +#- patches/cainjection_in_postgresprofiles.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_postgresprofiles.yaml b/config/crd/patches/cainjection_in_postgresprofiles.yaml new file mode 100644 index 00000000..16428876 --- /dev/null +++ b/config/crd/patches/cainjection_in_postgresprofiles.yaml @@ -0,0 +1,8 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: postgresprofiles.database.fits.cloud diff --git a/config/crd/patches/webhook_in_postgresprofiles.yaml b/config/crd/patches/webhook_in_postgresprofiles.yaml new file mode 100644 index 00000000..cb1c4fc7 --- /dev/null +++ b/config/crd/patches/webhook_in_postgresprofiles.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: postgresprofiles.database.fits.cloud +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/config/rbac/postgresprofile_editor_role.yaml b/config/rbac/postgresprofile_editor_role.yaml new file mode 100644 index 00000000..8ade46ec --- /dev/null +++ b/config/rbac/postgresprofile_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit postgresprofiles. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: postgresprofile-editor-role +rules: +- apiGroups: + - database.fits.cloud + resources: + - postgresprofiles + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.fits.cloud + resources: + - postgresprofiles/status + verbs: + - get diff --git a/config/rbac/postgresprofile_viewer_role.yaml b/config/rbac/postgresprofile_viewer_role.yaml new file mode 100644 index 00000000..d6c52ff1 --- /dev/null +++ b/config/rbac/postgresprofile_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view postgresprofiles. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: postgresprofile-viewer-role +rules: +- apiGroups: + - database.fits.cloud + resources: + - postgresprofiles + verbs: + - get + - list + - watch +- apiGroups: + - database.fits.cloud + resources: + - postgresprofiles/status + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 888b3c37..36ec8986 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -46,3 +46,23 @@ rules: - get - patch - update +- apiGroups: + - database.fits.cloud + resources: + - postgresprofiles + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.fits.cloud + resources: + - postgresprofiles/status + verbs: + - get + - patch + - update diff --git a/config/samples/database_v1_postgresprofile.yaml b/config/samples/database_v1_postgresprofile.yaml new file mode 100644 index 00000000..4570b46d --- /dev/null +++ b/config/samples/database_v1_postgresprofile.yaml @@ -0,0 +1,7 @@ +apiVersion: database.fits.cloud/v1 +kind: PostgresProfile +metadata: + name: postgresprofile-sample +spec: + # Add fields here + foo: bar diff --git a/controllers/suite_test.go b/controllers/suite_test.go index a1f4fc15..6b8d7589 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -65,6 +65,9 @@ var _ = BeforeSuite(func(done Done) { err = databasev1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) + err = databasev1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + // +kubebuilder:scaffold:scheme k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) diff --git a/main.go b/main.go index 7b810852..38e2978e 100644 --- a/main.go +++ b/main.go @@ -26,9 +26,10 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/log/zap" + zalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + databasev1 "github.com/fi-ts/postgres-controller/api/v1" "github.com/fi-ts/postgres-controller/controllers" - zalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" // +kubebuilder:scaffold:imports ) @@ -80,6 +81,7 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "Postgres") os.Exit(1) } + // +kubebuilder:scaffold:builder setupLog.Info("starting manager")