From b616df164840e71de346f17f4a566f8bd3585fcd Mon Sep 17 00:00:00 2001 From: Payam Qorbanpour Date: Mon, 23 Feb 2026 21:03:57 +0330 Subject: [PATCH 1/4] Add validation in crds to avoid unrelated schema --- api/v1/database_types.go | 15 ++++++++ .../crds/gobackup.io_databases.yaml | 34 +++++++++++++++++++ config/crd/bases/gobackup.io_databases.yaml | 34 +++++++++++++++++++ config/crd/bases/gobackup.io_storages.yaml | 28 +++++++++++++++ pkg/k8sutil/secret.go | 9 +++-- 5 files changed, 118 insertions(+), 2 deletions(-) diff --git a/api/v1/database_types.go b/api/v1/database_types.go index f08f13b..8874ecf 100644 --- a/api/v1/database_types.go +++ b/api/v1/database_types.go @@ -21,6 +21,21 @@ import ( ) // DatabaseSpec defines the desired state of Database +// +kubebuilder:validation:XValidation:rule="self.type == 'redis' || !has(self.config.mode)",message="config.mode is only valid when spec.type is redis" +// +kubebuilder:validation:XValidation:rule="self.type == 'redis' || !has(self.config.sync)",message="config.sync is only valid when spec.type is redis" +// +kubebuilder:validation:XValidation:rule="self.type == 'redis' || !has(self.config.copy)",message="config.copy is only valid when spec.type is redis" +// +kubebuilder:validation:XValidation:rule="self.type == 'redis' || !has(self.config.invoke_save)",message="config.invoke_save is only valid when spec.type is redis" +// +kubebuilder:validation:XValidation:rule="self.type == 'redis' || !has(self.config.rdb_path)",message="config.rdb_path is only valid when spec.type is redis" +// +kubebuilder:validation:XValidation:rule="self.type == 'redis' || !has(self.config.args_redis)",message="config.args_redis is only valid when spec.type is redis" +// +kubebuilder:validation:XValidation:rule="self.type == 'mongodb' || !has(self.config.auth_db)",message="config.auth_db is only valid when spec.type is mongodb" +// +kubebuilder:validation:XValidation:rule="self.type == 'mongodb' || !has(self.config.oplog)",message="config.oplog is only valid when spec.type is mongodb" +// +kubebuilder:validation:XValidation:rule="self.type == 'mssql' || !has(self.config.trust_server_certificate)",message="config.trust_server_certificate is only valid when spec.type is mssql" +// +kubebuilder:validation:XValidation:rule="self.type == 'influxdb' || !has(self.config.token)",message="config.token is only valid when spec.type is influxdb" +// +kubebuilder:validation:XValidation:rule="self.type == 'influxdb' || !has(self.config.bucket)",message="config.bucket is only valid when spec.type is influxdb" +// +kubebuilder:validation:XValidation:rule="self.type == 'influxdb' || !has(self.config.org)",message="config.org is only valid when spec.type is influxdb" +// +kubebuilder:validation:XValidation:rule="self.type == 'etcd' || !has(self.config.endpoints)",message="config.endpoints is only valid when spec.type is etcd" +// +kubebuilder:validation:XValidation:rule="self.type in ['postgresql', 'mysql', 'mariadb', 'mssql'] || !has(self.config.tables)",message="config.tables is only valid for SQL databases (postgresql, mysql, mariadb, mssql)" +// +kubebuilder:validation:XValidation:rule="self.type in ['postgresql', 'mysql', 'mariadb', 'mssql'] || !has(self.config.exclude_tables)",message="config.exclude_tables is only valid for SQL databases (postgresql, mysql, mariadb, mssql)" type DatabaseSpec struct { // Type is the database backend type // +kubebuilder:validation:Enum=postgresql;mysql;mariadb;mongodb;redis;mssql;influxdb;etcd diff --git a/charts/gobackup-operator/crds/gobackup.io_databases.yaml b/charts/gobackup-operator/crds/gobackup.io_databases.yaml index 830c96a..f2cba93 100644 --- a/charts/gobackup-operator/crds/gobackup.io_databases.yaml +++ b/charts/gobackup-operator/crds/gobackup.io_databases.yaml @@ -155,6 +155,40 @@ spec: - config - type type: object + x-kubernetes-validations: + - message: config.mode is only valid when spec.type is redis + rule: self.type == 'redis' || !has(self.config.mode) + - message: config.sync is only valid when spec.type is redis + rule: self.type == 'redis' || !has(self.config.sync) + - message: config.copy is only valid when spec.type is redis + rule: self.type == 'redis' || !has(self.config.copy) + - message: config.invoke_save is only valid when spec.type is redis + rule: self.type == 'redis' || !has(self.config.invoke_save) + - message: config.rdb_path is only valid when spec.type is redis + rule: self.type == 'redis' || !has(self.config.rdb_path) + - message: config.args_redis is only valid when spec.type is redis + rule: self.type == 'redis' || !has(self.config.args_redis) + - message: config.auth_db is only valid when spec.type is mongodb + rule: self.type == 'mongodb' || !has(self.config.auth_db) + - message: config.oplog is only valid when spec.type is mongodb + rule: self.type == 'mongodb' || !has(self.config.oplog) + - message: config.trust_server_certificate is only valid when spec.type + is mssql + rule: self.type == 'mssql' || !has(self.config.trust_server_certificate) + - message: config.token is only valid when spec.type is influxdb + rule: self.type == 'influxdb' || !has(self.config.token) + - message: config.bucket is only valid when spec.type is influxdb + rule: self.type == 'influxdb' || !has(self.config.bucket) + - message: config.org is only valid when spec.type is influxdb + rule: self.type == 'influxdb' || !has(self.config.org) + - message: config.endpoints is only valid when spec.type is etcd + rule: self.type == 'etcd' || !has(self.config.endpoints) + - message: config.tables is only valid for SQL databases (postgresql, + mysql, mariadb, mssql) + rule: self.type in ['postgresql', 'mysql', 'mariadb', 'mssql'] || !has(self.config.tables) + - message: config.exclude_tables is only valid for SQL databases (postgresql, + mysql, mariadb, mssql) + rule: self.type in ['postgresql', 'mysql', 'mariadb', 'mssql'] || !has(self.config.exclude_tables) status: description: DatabaseStatus defines the observed state of Database type: object diff --git a/config/crd/bases/gobackup.io_databases.yaml b/config/crd/bases/gobackup.io_databases.yaml index 744fe2f..8a8a022 100644 --- a/config/crd/bases/gobackup.io_databases.yaml +++ b/config/crd/bases/gobackup.io_databases.yaml @@ -156,6 +156,40 @@ spec: - config - type type: object + x-kubernetes-validations: + - message: config.mode is only valid when spec.type is redis + rule: self.type == 'redis' || !has(self.config.mode) + - message: config.sync is only valid when spec.type is redis + rule: self.type == 'redis' || !has(self.config.sync) + - message: config.copy is only valid when spec.type is redis + rule: self.type == 'redis' || !has(self.config.copy) + - message: config.invoke_save is only valid when spec.type is redis + rule: self.type == 'redis' || !has(self.config.invoke_save) + - message: config.rdb_path is only valid when spec.type is redis + rule: self.type == 'redis' || !has(self.config.rdb_path) + - message: config.args_redis is only valid when spec.type is redis + rule: self.type == 'redis' || !has(self.config.args_redis) + - message: config.auth_db is only valid when spec.type is mongodb + rule: self.type == 'mongodb' || !has(self.config.auth_db) + - message: config.oplog is only valid when spec.type is mongodb + rule: self.type == 'mongodb' || !has(self.config.oplog) + - message: config.trust_server_certificate is only valid when spec.type + is mssql + rule: self.type == 'mssql' || !has(self.config.trust_server_certificate) + - message: config.token is only valid when spec.type is influxdb + rule: self.type == 'influxdb' || !has(self.config.token) + - message: config.bucket is only valid when spec.type is influxdb + rule: self.type == 'influxdb' || !has(self.config.bucket) + - message: config.org is only valid when spec.type is influxdb + rule: self.type == 'influxdb' || !has(self.config.org) + - message: config.endpoints is only valid when spec.type is etcd + rule: self.type == 'etcd' || !has(self.config.endpoints) + - message: config.tables is only valid for SQL databases (postgresql, + mysql, mariadb, mssql) + rule: self.type in ['postgresql', 'mysql', 'mariadb', 'mssql'] || !has(self.config.tables) + - message: config.exclude_tables is only valid for SQL databases (postgresql, + mysql, mariadb, mssql) + rule: self.type in ['postgresql', 'mysql', 'mariadb', 'mssql'] || !has(self.config.exclude_tables) status: description: DatabaseStatus defines the observed state of Database type: object diff --git a/config/crd/bases/gobackup.io_storages.yaml b/config/crd/bases/gobackup.io_storages.yaml index 6e2ddd4..78fe7ea 100644 --- a/config/crd/bases/gobackup.io_storages.yaml +++ b/config/crd/bases/gobackup.io_storages.yaml @@ -59,8 +59,12 @@ spec: a valid secret key. type: string name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: @@ -106,8 +110,12 @@ spec: a valid secret key. type: string name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: @@ -143,8 +151,12 @@ spec: a valid secret key. type: string name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: @@ -195,8 +207,12 @@ spec: a valid secret key. type: string name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: @@ -222,8 +238,12 @@ spec: a valid secret key. type: string name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: @@ -259,8 +279,12 @@ spec: a valid secret key. type: string name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: @@ -296,8 +320,12 @@ spec: a valid secret key. type: string name: + default: "" description: |- Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names type: string optional: diff --git a/pkg/k8sutil/secret.go b/pkg/k8sutil/secret.go index d080669..3730485 100644 --- a/pkg/k8sutil/secret.go +++ b/pkg/k8sutil/secret.go @@ -47,7 +47,6 @@ func (k *K8s) CreateSecret(ctx context.Context, backup *backupv1.Backup) error { // Process database references for _, database := range model.DatabaseRefs { - dbType := strings.ToLower(database.Type) // Resource name is always "databases" (plural of Database kind), not type-specific resource := "databases" @@ -60,7 +59,7 @@ func (k *K8s) CreateSecret(ctx context.Context, backup *backupv1.Backup) error { // Fetch the database CRD databaseCRD, err := k.GetCRD(ctx, apiGroup, "v1", resource, namespace, database.Name) if err != nil { - return fmt.Errorf("failed to get %s database: %w", dbType, err) + return fmt.Errorf("failed to get database %s: %w", database.Name, err) } // Extract the database spec @@ -69,6 +68,12 @@ func (k *K8s) CreateSecret(ctx context.Context, backup *backupv1.Backup) error { return fmt.Errorf("database spec for %s is not a valid map", database.Name) } + rawType, ok := specMap["type"].(string) + if !ok || strings.TrimSpace(rawType) == "" { + return fmt.Errorf("database type for %s is missing or invalid", database.Name) + } + dbType := strings.ToLower(strings.TrimSpace(rawType)) + // Extract config if it exists configMap, ok := specMap["config"].(map[string]interface{}) if !ok { From 6a3f59ba24b9ecbad039bf1befa3dec3f31a9ee9 Mon Sep 17 00:00:00 2001 From: Payam Qorbanpour Date: Tue, 12 May 2026 21:43:37 +0330 Subject: [PATCH 2/4] Add CLAUDE.md to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 304dc02..e5c59e6 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,5 @@ charts/gobackup-operator/values.*.local.yaml # Claude .claude +CLAUDE.md .serena \ No newline at end of file From b788e44f069aaca24ab73bcd89b445c90807162d Mon Sep 17 00:00:00 2001 From: Payam Qorbanpour Date: Tue, 12 May 2026 22:45:53 +0330 Subject: [PATCH 3/4] Add database credential ref --- api/v1/database_types.go | 18 +++- api/v1/zz_generated.deepcopy.go | 15 ++++ config/crd/bases/gobackup.io_databases.yaml | 85 ++++++++++++++++++- config/samples/database/etcd.yaml | 2 +- config/samples/database/influxdb-direct.yaml | 12 +++ config/samples/database/influxdb.yaml | 6 +- config/samples/database/mariadb-direct.yaml | 13 +++ config/samples/database/mariadb.yaml | 10 ++- config/samples/database/mongodb-direct.yaml | 15 ++++ config/samples/database/mongodb.yaml | 10 ++- config/samples/database/mssql-direct.yaml | 14 +++ config/samples/database/mssql.yaml | 10 ++- config/samples/database/mysql-direct.yaml | 15 ++++ config/samples/database/mysql.yaml | 10 ++- .../samples/database/postgresql-direct.yaml | 20 +++++ config/samples/database/postgresql.yaml | 10 ++- config/samples/database/redis-direct.yaml | 12 +++ config/samples/database/redis.yaml | 6 +- .../samples/secrets/database-credentials.yaml | 16 ++++ 19 files changed, 273 insertions(+), 26 deletions(-) create mode 100644 config/samples/database/influxdb-direct.yaml create mode 100644 config/samples/database/mariadb-direct.yaml create mode 100644 config/samples/database/mongodb-direct.yaml create mode 100644 config/samples/database/mssql-direct.yaml create mode 100644 config/samples/database/mysql-direct.yaml create mode 100644 config/samples/database/postgresql-direct.yaml create mode 100644 config/samples/database/redis-direct.yaml create mode 100644 config/samples/secrets/database-credentials.yaml diff --git a/api/v1/database_types.go b/api/v1/database_types.go index 8874ecf..e8a98b4 100644 --- a/api/v1/database_types.go +++ b/api/v1/database_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1 import ( + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -31,6 +32,7 @@ import ( // +kubebuilder:validation:XValidation:rule="self.type == 'mongodb' || !has(self.config.oplog)",message="config.oplog is only valid when spec.type is mongodb" // +kubebuilder:validation:XValidation:rule="self.type == 'mssql' || !has(self.config.trust_server_certificate)",message="config.trust_server_certificate is only valid when spec.type is mssql" // +kubebuilder:validation:XValidation:rule="self.type == 'influxdb' || !has(self.config.token)",message="config.token is only valid when spec.type is influxdb" +// +kubebuilder:validation:XValidation:rule="self.type == 'influxdb' || !has(self.config.token_ref)",message="config.token_ref is only valid when spec.type is influxdb" // +kubebuilder:validation:XValidation:rule="self.type == 'influxdb' || !has(self.config.bucket)",message="config.bucket is only valid when spec.type is influxdb" // +kubebuilder:validation:XValidation:rule="self.type == 'influxdb' || !has(self.config.org)",message="config.org is only valid when spec.type is influxdb" // +kubebuilder:validation:XValidation:rule="self.type == 'etcd' || !has(self.config.endpoints)",message="config.endpoints is only valid when spec.type is etcd" @@ -62,10 +64,14 @@ type DatabaseConfig struct { // For Redis: e.g. /var/run/redis/redis.sock Socket *string `json:"socket,omitempty"` - // Password is the password for the database or Redis server + // Password is the password for the database or Redis server. Use password_ref to reference a Secret instead. // Default for Redis: "" Password *string `json:"password,omitempty"` + // PasswordRef references a Secret containing the database password. + // Set either Password or PasswordRef, not both. + PasswordRef *corev1.SecretKeySelector `json:"password_ref,omitempty"` + // Args are additional arguments for pg_dump (PostgreSQL), mysqldump (MySQL) or redis-cli utility (Redis) // For Redis, e.g.: --tls --cacert redis_ca.pem // For MySQL, e.g.: --skip-ssl or --ssl-ca=/path/to/ca.pem @@ -76,10 +82,13 @@ type DatabaseConfig struct { // Database is the database name (PostgreSQL) Database *string `json:"database,omitempty"` - // Username is the username for the database (PostgreSQL) + // Username is the username for the database (PostgreSQL). Use username_ref to reference a Secret instead. // Default: root Username *string `json:"username,omitempty"` + // UsernameRef references a Secret containing the database username. + UsernameRef *corev1.SecretKeySelector `json:"username_ref,omitempty"` + // Tables is an array of tables to backup (PostgreSQL) Tables []string `json:"tables,omitempty"` @@ -101,9 +110,12 @@ type DatabaseConfig struct { // InfluxDB-specific fields - // Token is the authentication token (InfluxDB) + // Token is the authentication token (InfluxDB). Use token_ref to reference a Secret instead. Token *string `json:"token,omitempty"` + // TokenRef references a Secret containing the InfluxDB authentication token. + TokenRef *corev1.SecretKeySelector `json:"token_ref,omitempty"` + // Bucket is the bucket name (InfluxDB) Bucket *string `json:"bucket,omitempty"` diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index acaf4ef..7f98802 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -290,6 +290,11 @@ func (in *DatabaseConfig) DeepCopyInto(out *DatabaseConfig) { *out = new(string) **out = **in } + if in.PasswordRef != nil { + in, out := &in.PasswordRef, &out.PasswordRef + *out = new(corev1.SecretKeySelector) + (*in).DeepCopyInto(*out) + } if in.Args != nil { in, out := &in.Args, &out.Args *out = new(string) @@ -305,6 +310,11 @@ func (in *DatabaseConfig) DeepCopyInto(out *DatabaseConfig) { *out = new(string) **out = **in } + if in.UsernameRef != nil { + in, out := &in.UsernameRef, &out.UsernameRef + *out = new(corev1.SecretKeySelector) + (*in).DeepCopyInto(*out) + } if in.Tables != nil { in, out := &in.Tables, &out.Tables *out = make([]string, len(*in)) @@ -335,6 +345,11 @@ func (in *DatabaseConfig) DeepCopyInto(out *DatabaseConfig) { *out = new(string) **out = **in } + if in.TokenRef != nil { + in, out := &in.TokenRef, &out.TokenRef + *out = new(corev1.SecretKeySelector) + (*in).DeepCopyInto(*out) + } if in.Bucket != nil { in, out := &in.Bucket, &out.Bucket *out = new(string) diff --git a/config/crd/bases/gobackup.io_databases.yaml b/config/crd/bases/gobackup.io_databases.yaml index 8a8a022..2c3a0de 100644 --- a/config/crd/bases/gobackup.io_databases.yaml +++ b/config/crd/bases/gobackup.io_databases.yaml @@ -101,9 +101,35 @@ spec: type: string password: description: |- - Password is the password for the database or Redis server + Password is the password for the database or Redis server. Use password_ref to reference a Secret instead. Default for Redis: "" type: string + password_ref: + description: |- + PasswordRef references a Secret containing the database password. + Set either Password or PasswordRef, not both. + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must be + defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic port: description: |- Port is the database server port @@ -128,17 +154,68 @@ spec: type: string type: array token: - description: Token is the authentication token (InfluxDB) + description: Token is the authentication token (InfluxDB). Use + token_ref to reference a Secret instead. type: string + token_ref: + description: TokenRef references a Secret containing the InfluxDB + authentication token. + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must be + defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic trust_server_certificate: description: TrustServerCertificate is used to trust the server certificate (MSSQL) type: boolean username: description: |- - Username is the username for the database (PostgreSQL) + Username is the username for the database (PostgreSQL). Use username_ref to reference a Secret instead. Default: root type: string + username_ref: + description: UsernameRef references a Secret containing the database + username. + properties: + key: + description: The key of the secret to select from. Must be + a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must be + defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic type: object type: description: Type is the database backend type @@ -178,6 +255,8 @@ spec: rule: self.type == 'mssql' || !has(self.config.trust_server_certificate) - message: config.token is only valid when spec.type is influxdb rule: self.type == 'influxdb' || !has(self.config.token) + - message: config.token_ref is only valid when spec.type is influxdb + rule: self.type == 'influxdb' || !has(self.config.token_ref) - message: config.bucket is only valid when spec.type is influxdb rule: self.type == 'influxdb' || !has(self.config.bucket) - message: config.org is only valid when spec.type is influxdb diff --git a/config/samples/database/etcd.yaml b/config/samples/database/etcd.yaml index bf2a6c9..dfa8194 100644 --- a/config/samples/database/etcd.yaml +++ b/config/samples/database/etcd.yaml @@ -2,7 +2,7 @@ apiVersion: gobackup.io/v1 kind: Database metadata: name: example-etcd - namespace: gobackup-operator-system + namespace: default spec: type: etcd config: diff --git a/config/samples/database/influxdb-direct.yaml b/config/samples/database/influxdb-direct.yaml new file mode 100644 index 0000000..3e0ed80 --- /dev/null +++ b/config/samples/database/influxdb-direct.yaml @@ -0,0 +1,12 @@ +apiVersion: gobackup.io/v1 +kind: Database +metadata: + name: example-influxdb-direct + namespace: default +spec: + type: influxdb + config: + host: influxdb.example.svc.cluster.local + token: mytoken + bucket: mybucket + org: myorg diff --git a/config/samples/database/influxdb.yaml b/config/samples/database/influxdb.yaml index a2dbf5f..3c8a131 100644 --- a/config/samples/database/influxdb.yaml +++ b/config/samples/database/influxdb.yaml @@ -2,11 +2,13 @@ apiVersion: gobackup.io/v1 kind: Database metadata: name: example-influxdb - namespace: gobackup-operator-system + namespace: default spec: type: influxdb config: host: influxdb.example.svc.cluster.local - token: mytoken + token_ref: + name: database-credentials + key: token bucket: mybucket org: myorg diff --git a/config/samples/database/mariadb-direct.yaml b/config/samples/database/mariadb-direct.yaml new file mode 100644 index 0000000..efc4680 --- /dev/null +++ b/config/samples/database/mariadb-direct.yaml @@ -0,0 +1,13 @@ +apiVersion: gobackup.io/v1 +kind: Database +metadata: + name: example-mariadb-direct + namespace: default +spec: + type: mariadb + config: + host: mariadb.example.svc.cluster.local + port: 3306 + database: exampledb + username: root + password: examplepassword diff --git a/config/samples/database/mariadb.yaml b/config/samples/database/mariadb.yaml index 3d4b907..a645e71 100644 --- a/config/samples/database/mariadb.yaml +++ b/config/samples/database/mariadb.yaml @@ -2,12 +2,16 @@ apiVersion: gobackup.io/v1 kind: Database metadata: name: example-mariadb - namespace: gobackup-operator-system + namespace: default spec: type: mariadb config: host: mariadb.example.svc.cluster.local port: 3306 database: exampledb - username: root - password: examplepassword + username_ref: + name: database-credentials + key: username + password_ref: + name: database-credentials + key: password diff --git a/config/samples/database/mongodb-direct.yaml b/config/samples/database/mongodb-direct.yaml new file mode 100644 index 0000000..b169e80 --- /dev/null +++ b/config/samples/database/mongodb-direct.yaml @@ -0,0 +1,15 @@ +apiVersion: gobackup.io/v1 +kind: Database +metadata: + name: example-mongodb-direct + namespace: default +spec: + type: mongodb + config: + host: mongodb.example.svc.cluster.local + port: 27017 + database: exampledb + username: root + password: examplepassword + auth_db: admin + oplog: true diff --git a/config/samples/database/mongodb.yaml b/config/samples/database/mongodb.yaml index 29c308f..7ae164a 100644 --- a/config/samples/database/mongodb.yaml +++ b/config/samples/database/mongodb.yaml @@ -2,14 +2,18 @@ apiVersion: gobackup.io/v1 kind: Database metadata: name: example-mongodb - namespace: gobackup-operator-system + namespace: default spec: type: mongodb config: host: mongodb.example.svc.cluster.local port: 27017 database: exampledb - username: root - password: examplepassword + username_ref: + name: database-credentials + key: username + password_ref: + name: database-credentials + key: password auth_db: admin oplog: true diff --git a/config/samples/database/mssql-direct.yaml b/config/samples/database/mssql-direct.yaml new file mode 100644 index 0000000..e9a2dba --- /dev/null +++ b/config/samples/database/mssql-direct.yaml @@ -0,0 +1,14 @@ +apiVersion: gobackup.io/v1 +kind: Database +metadata: + name: example-mssql-direct + namespace: default +spec: + type: mssql + config: + host: mssql.example.svc.cluster.local + port: 1433 + database: exampledb + username: sa + password: examplepassword + trust_server_certificate: true diff --git a/config/samples/database/mssql.yaml b/config/samples/database/mssql.yaml index e043532..7e5ba02 100644 --- a/config/samples/database/mssql.yaml +++ b/config/samples/database/mssql.yaml @@ -2,13 +2,17 @@ apiVersion: gobackup.io/v1 kind: Database metadata: name: example-mssql - namespace: gobackup-operator-system + namespace: default spec: type: mssql config: host: mssql.example.svc.cluster.local port: 1433 database: exampledb - username: sa - password: examplepassword + username_ref: + name: database-credentials + key: username + password_ref: + name: database-credentials + key: password trust_server_certificate: true diff --git a/config/samples/database/mysql-direct.yaml b/config/samples/database/mysql-direct.yaml new file mode 100644 index 0000000..8e8bff6 --- /dev/null +++ b/config/samples/database/mysql-direct.yaml @@ -0,0 +1,15 @@ +apiVersion: gobackup.io/v1 +kind: Database +metadata: + name: example-mysql-direct + namespace: default +spec: + type: mysql + config: + host: mysql.example.svc.cluster.local + port: 3306 + socket: "" + database: exampledb + username: root + password: examplepassword + args: "--skip-ssl" diff --git a/config/samples/database/mysql.yaml b/config/samples/database/mysql.yaml index 300116e..fd74fe2 100644 --- a/config/samples/database/mysql.yaml +++ b/config/samples/database/mysql.yaml @@ -2,7 +2,7 @@ apiVersion: gobackup.io/v1 kind: Database metadata: name: example-mysql - namespace: gobackup-operator-system + namespace: default spec: type: mysql config: @@ -10,6 +10,10 @@ spec: port: 3306 socket: "" database: exampledb - username: root - password: examplepassword + username_ref: + name: database-credentials + key: username + password_ref: + name: database-credentials + key: password args: "--skip-ssl" diff --git a/config/samples/database/postgresql-direct.yaml b/config/samples/database/postgresql-direct.yaml new file mode 100644 index 0000000..c25a330 --- /dev/null +++ b/config/samples/database/postgresql-direct.yaml @@ -0,0 +1,20 @@ +apiVersion: gobackup.io/v1 +kind: Database +metadata: + name: example-postgresql-direct + namespace: default +spec: + type: postgresql + config: + host: localhost + port: 5432 + socket: "" + database: exampledb + username: root + password: examplepassword + tables: + - table1 + - table2 + exclude_tables: + - table3 + args: "--no-owner" diff --git a/config/samples/database/postgresql.yaml b/config/samples/database/postgresql.yaml index fadfa1f..3b91e35 100644 --- a/config/samples/database/postgresql.yaml +++ b/config/samples/database/postgresql.yaml @@ -2,7 +2,7 @@ apiVersion: gobackup.io/v1 kind: Database metadata: name: example-postgresql - namespace: gobackup-operator-system + namespace: default spec: type: postgresql config: @@ -10,8 +10,12 @@ spec: port: 5432 socket: "" database: exampledb - username: root - password: examplepassword + username_ref: + name: database-credentials + key: username + password_ref: + name: database-credentials + key: password tables: - table1 - table2 diff --git a/config/samples/database/redis-direct.yaml b/config/samples/database/redis-direct.yaml new file mode 100644 index 0000000..a2ef11a --- /dev/null +++ b/config/samples/database/redis-direct.yaml @@ -0,0 +1,12 @@ +apiVersion: gobackup.io/v1 +kind: Database +metadata: + name: example-redis-direct + namespace: default +spec: + type: redis + config: + host: redis.example.svc.cluster.local + port: 6379 + password: examplepassword + mode: sync diff --git a/config/samples/database/redis.yaml b/config/samples/database/redis.yaml index 5a468fa..02f8d6f 100644 --- a/config/samples/database/redis.yaml +++ b/config/samples/database/redis.yaml @@ -2,11 +2,13 @@ apiVersion: gobackup.io/v1 kind: Database metadata: name: example-redis - namespace: gobackup-operator-system + namespace: default spec: type: redis config: host: redis.example.svc.cluster.local port: 6379 - password: examplepassword + password_ref: + name: database-credentials + key: password mode: sync diff --git a/config/samples/secrets/database-credentials.yaml b/config/samples/secrets/database-credentials.yaml new file mode 100644 index 0000000..7674916 --- /dev/null +++ b/config/samples/secrets/database-credentials.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: database-credentials + namespace: default +type: Opaque +stringData: + # Shared credentials(username & password) for SQL-shaped databases + # (postgresql, mysql, mariadb, mongodb, mssql) + username: "YOUR_DATABASE_USERNAME" + # Redis: password-only auth, kept separate + password: "YOUR_DATABASE_PASSWORD" + + # InfluxDB: token-based auth + token: "YOUR_INFLUXDB_TOKEN" From 53658b65b92a70dfe5f77ff027ac4ae3f2b9ab30 Mon Sep 17 00:00:00 2001 From: Payam Qorbanpour Date: Tue, 12 May 2026 22:58:24 +0330 Subject: [PATCH 4/4] Merge helm releaser workflows --- .github/workflows/helm-chart.yml | 102 --------------------- .github/workflows/helm-publish.yml | 140 ----------------------------- .github/workflows/release.yml | 64 +++++++++++++ 3 files changed, 64 insertions(+), 242 deletions(-) delete mode 100644 .github/workflows/helm-chart.yml delete mode 100644 .github/workflows/helm-publish.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/helm-chart.yml b/.github/workflows/helm-chart.yml deleted file mode 100644 index ea67178..0000000 --- a/.github/workflows/helm-chart.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: Helm Chart - -on: - push: - branches: - - main - paths: - - 'charts/**' - pull_request: - paths: - - 'charts/**' - -jobs: - lint: - name: Lint Helm Chart - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Set up Helm - uses: azure/setup-helm@v4 - with: - version: v3.14.0 - - - name: Lint chart - run: helm lint charts/gobackup-operator - - - name: Template chart - run: helm template gobackup-operator charts/gobackup-operator --debug - - release: - name: Release Helm Chart - runs-on: ubuntu-latest - needs: lint - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - permissions: - contents: write - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Configure Git - run: | - git config user.name "$GITHUB_ACTOR" - git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - - - name: Install Helm - uses: azure/setup-helm@v4 - with: - version: v3.14.0 - - - name: Get chart version - id: chart - run: | - VERSION=$(grep '^version:' charts/gobackup-operator/Chart.yaml | awk '{print $2}') - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "Chart version: $VERSION" - - - name: Package Helm chart - run: | - helm package charts/gobackup-operator --destination .cr-release-packages - - - name: Create GitHub Release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - RELEASE_NAME="v${{ steps.chart.outputs.version }}" - TAG_NAME="v${{ steps.chart.outputs.version }}" - CHART_PACKAGE=".cr-release-packages/gobackup-operator-${{ steps.chart.outputs.version }}.tgz" - - # Check if release already exists - if gh release view "$TAG_NAME" &>/dev/null; then - echo "Release $TAG_NAME already exists, skipping..." - else - gh release create "$TAG_NAME" "$CHART_PACKAGE" \ - --title "$RELEASE_NAME" \ - --notes "Helm chart for GoBackup Operator v${{ steps.chart.outputs.version }}" - fi - - - name: Update Helm repo index - run: | - # Checkout gh-pages branch - git fetch origin gh-pages:gh-pages || true - git checkout gh-pages || git checkout --orphan gh-pages - - # Download existing index if it exists - if [ -f index.yaml ]; then - helm repo index .cr-release-packages --url https://github.com/gobackup/gobackup-operator/releases/download/v${{ steps.chart.outputs.version }} --merge index.yaml - else - helm repo index .cr-release-packages --url https://github.com/gobackup/gobackup-operator/releases/download/v${{ steps.chart.outputs.version }} - fi - - cp .cr-release-packages/index.yaml . - git add index.yaml - git commit -m "Update Helm repo index for v${{ steps.chart.outputs.version }}" || true - git push origin gh-pages || true - diff --git a/.github/workflows/helm-publish.yml b/.github/workflows/helm-publish.yml deleted file mode 100644 index 56e085f..0000000 --- a/.github/workflows/helm-publish.yml +++ /dev/null @@ -1,140 +0,0 @@ -name: Publish Helm Chart -on: - push: - branches: - - "release-helm" - release: - types: [created] - workflow_dispatch: - inputs: - version: - description: 'Chart version to publish (leave empty to use Chart.yaml version)' - required: false - type: string - -env: - REGISTRY: ghcr.io - CHART_NAME: gobackup-operator - -jobs: - publish-ghcr: - name: Publish to GitHub Container Registry - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Set up Helm - uses: azure/setup-helm@v4 - with: - version: v3.14.0 - - - name: Lint chart - run: | - helm lint charts/${{ env.CHART_NAME }} - helm template test-release charts/${{ env.CHART_NAME }} --debug - - - name: Configure Helm OCI registry - run: | - helm registry login -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }} ${{ env.REGISTRY }} - - - name: Get chart version - id: chart - run: | - if [ -n "${{ github.event.inputs.version }}" ]; then - VERSION="${{ github.event.inputs.version }}" - elif [ "${{ github.event_name }}" == "release" ]; then - VERSION="${{ github.event.release.tag_name }}" - # Remove 'v' prefix if present - VERSION=${VERSION#v} - else - VERSION=$(grep '^version:' charts/${{ env.CHART_NAME }}/Chart.yaml | awk '{print $2}') - fi - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "Chart version: $VERSION" - - - name: Package Helm chart - run: | - helm package charts/${{ env.CHART_NAME }} --destination .cr-release-packages - - - name: Push to GitHub Container Registry - run: | - CHART_PACKAGE=".cr-release-packages/${{ env.CHART_NAME }}-${{ steps.chart.outputs.version }}.tgz" - helm push $CHART_PACKAGE oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/helm-charts - echo "Chart pushed to: oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/helm-charts/${{ env.CHART_NAME }}:${{ steps.chart.outputs.version }}" - - - name: Upload chart package artifact - uses: actions/upload-artifact@v4 - with: - name: chart-package - path: .cr-release-packages/${{ env.CHART_NAME }}-${{ steps.chart.outputs.version }}.tgz - retention-days: 1 - - - name: Output chart location - run: | - echo "Chart available at:" - echo " oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/helm-charts/${{ env.CHART_NAME }}:${{ steps.chart.outputs.version }}" - echo "" - echo "To install:" - echo " helm install my-release oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/helm-charts/${{ env.CHART_NAME }} --version ${{ steps.chart.outputs.version }}" - - publish-artifacthub: - name: Publish to ArtifactHub - runs-on: ubuntu-latest - needs: publish-ghcr - permissions: - contents: write - steps: - - name: Download chart package artifact - uses: actions/download-artifact@v4 - with: - name: chart-package - path: .cr-release-packages - - - name: Get chart version - id: chart - run: | - if [ -n "${{ github.event.inputs.version }}" ]; then - VERSION="${{ github.event.inputs.version }}" - elif [ "${{ github.event_name }}" == "release" ]; then - VERSION="${{ github.event.release.tag_name }}" - # Remove 'v' prefix if present - VERSION=${VERSION#v} - else - VERSION=$(grep '^version:' charts/${{ env.CHART_NAME }}/Chart.yaml | awk '{print $2}') - fi - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "Chart version: $VERSION" - - - name: Upload chart to release - if: github.event_name == 'release' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - CHART_PACKAGE=".cr-release-packages/${{ env.CHART_NAME }}-${{ steps.chart.outputs.version }}.tgz" - gh release upload ${{ github.event.release.tag_name }} "$CHART_PACKAGE" --clobber - echo "Chart uploaded to GitHub release: ${{ github.event.release.tag_name }}" - - - name: Create GitHub Release (for manual dispatch) - if: github.event_name == 'workflow_dispatch' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - RELEASE_NAME="v${{ steps.chart.outputs.version }}" - TAG_NAME="v${{ steps.chart.outputs.version }}" - CHART_PACKAGE=".cr-release-packages/${{ env.CHART_NAME }}-${{ steps.chart.outputs.version }}.tgz" - - # Check if release already exists - if gh release view "$TAG_NAME" &>/dev/null; then - echo "Release $TAG_NAME already exists, uploading chart..." - gh release upload "$TAG_NAME" "$CHART_PACKAGE" --clobber - else - gh release create "$TAG_NAME" "$CHART_PACKAGE" \ - --title "$RELEASE_NAME" \ - --notes "Helm chart for GoBackup Operator v${{ steps.chart.outputs.version }}" - fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..67aa8b2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,64 @@ +name: release + +on: + push: + branches: [main] + paths: + - 'charts/**' + workflow_dispatch: + +permissions: + contents: read + packages: write + +env: + REGISTRY: ghcr.io + CHART_NAME: gobackup-operator + +concurrency: + group: release-helm-${{ github.ref }} + cancel-in-progress: false + +jobs: + helm-release: + name: Publish Helm chart to GHCR + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Set up Helm + uses: azure/setup-helm@v4 + with: + version: v3.14.0 + + - name: Lint chart + run: | + helm lint charts/${{ env.CHART_NAME }} + helm template test-release charts/${{ env.CHART_NAME }} --debug + + - name: Get chart version + id: chart + run: | + VERSION=$(grep '^version:' charts/${{ env.CHART_NAME }}/Chart.yaml | awk '{print $2}') + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Chart version: $VERSION" + + - name: Package chart + run: helm package charts/${{ env.CHART_NAME }} --destination .cr-release-packages + + - name: Login to GHCR + run: | + helm registry login -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }} ${{ env.REGISTRY }} + + - name: Push chart + run: | + CHART_PACKAGE=".cr-release-packages/${{ env.CHART_NAME }}-${{ steps.chart.outputs.version }}.tgz" + helm push "$CHART_PACKAGE" oci://${{ env.REGISTRY }}/${{ github.repository_owner }} + echo "Pushed: oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.CHART_NAME }}:${{ steps.chart.outputs.version }}" + + - name: Install hint + run: | + echo "helm install my-release oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.CHART_NAME }} --version ${{ steps.chart.outputs.version }}"