From 6850acee5551544bddba30c369ea456572335c64 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Thu, 7 Oct 2021 10:31:17 +0200 Subject: [PATCH] feat: scaleway support managed dbs (#389) --- Cargo.lock | 41 +++ lib/scaleway/bootstrap/ks-master-cluster.tf | 2 +- lib/scaleway/bootstrap/tf-providers.j2.tf | 2 +- .../chart_values/mysql/values.j2.yaml | 6 +- .../chart_values/postgresql/values.j2.yaml | 4 +- lib/scaleway/services/common/backend.j2.tf | 8 + lib/scaleway/services/common/providers.j2.tf | 37 +++ lib/scaleway/services/mysql/main.j2.tf | 83 ++++++ lib/scaleway/services/mysql/variables.j2.tf | 202 +++++++++++++++ lib/scaleway/services/postgresql/main.j2.tf | 81 ++++++ .../services/postgresql/variables.j2.tf | 202 +++++++++++++++ src/cloud_provider/aws/application.rs | 4 +- src/cloud_provider/aws/databases/mongodb.rs | 23 +- src/cloud_provider/aws/databases/mysql.rs | 27 +- .../aws/databases/postgresql.rs | 27 +- src/cloud_provider/aws/databases/redis.rs | 23 +- src/cloud_provider/aws/databases/utilities.rs | 5 +- src/cloud_provider/aws/kubernetes/mod.rs | 4 + src/cloud_provider/aws/router.rs | 4 +- .../digitalocean/application.rs | 4 +- .../digitalocean/databases/mongodb.rs | 6 +- .../digitalocean/databases/mysql.rs | 4 +- .../digitalocean/databases/postgresql.rs | 4 +- .../digitalocean/databases/redis.rs | 4 +- .../digitalocean/kubernetes/doks_api.rs | 7 +- .../digitalocean/kubernetes/mod.rs | 4 + src/cloud_provider/digitalocean/router.rs | 4 +- src/cloud_provider/kubernetes.rs | 68 ++--- src/cloud_provider/scaleway/application.rs | 14 +- .../scaleway/databases/mongodb.rs | 4 +- .../scaleway/databases/mysql.rs | 83 ++++-- .../scaleway/databases/postgresql.rs | 97 +++++-- .../scaleway/databases/redis.rs | 4 +- .../scaleway/kubernetes/helm_charts.rs | 7 +- src/cloud_provider/scaleway/kubernetes/mod.rs | 5 + src/cloud_provider/scaleway/mod.rs | 7 +- src/cloud_provider/scaleway/router.rs | 11 +- src/cloud_provider/service.rs | 8 +- src/cloud_provider/utilities.rs | 191 +++++++++++--- src/models.rs | 92 ++++--- test_utilities/Cargo.toml | 1 + test_utilities/src/common.rs | 20 +- test_utilities/src/digitalocean.rs | 6 +- test_utilities/src/scaleway.rs | 10 +- test_utilities/src/utilities.rs | 21 +- tests/aws/aws_databases.rs | 15 ++ tests/digitalocean/do_databases.rs | 131 ++++++++-- tests/scaleway/scw_databases.rs | 240 ++++++++++++++---- 48 files changed, 1526 insertions(+), 331 deletions(-) create mode 100644 lib/scaleway/services/common/backend.j2.tf create mode 100644 lib/scaleway/services/common/providers.j2.tf create mode 100644 lib/scaleway/services/mysql/main.j2.tf create mode 100644 lib/scaleway/services/mysql/variables.j2.tf create mode 100644 lib/scaleway/services/postgresql/main.j2.tf create mode 100644 lib/scaleway/services/postgresql/variables.j2.tf diff --git a/Cargo.lock b/Cargo.lock index 2d08342d..ad03750f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1857,6 +1857,15 @@ dependencies = [ "regex", ] +[[package]] +name = "passwords" +version = "3.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03258d8822ae1e392ba9385d4f66c1eed50129fd679c13faa2ce6f62bae5b511" +dependencies = [ + "random-pick", +] + [[package]] name = "percent-encoding" version = "1.0.1" @@ -2319,6 +2328,37 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "random-number" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6c791ad2f753537ebdc0300173be0fc4821eaa628feb2c404f8acaba612d57" +dependencies = [ + "proc-macro-hack", + "rand 0.8.4", + "random-number-macro-impl", +] + +[[package]] +name = "random-number-macro-impl" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6653af545be5b0b1a2b4c19ef0f0cb288b2e5c024016a8eb0fe2efa8d9f8de" +dependencies = [ + "proc-macro-hack", + "quote 1.0.9", + "syn 1.0.74", +] + +[[package]] +name = "random-pick" +version = "1.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420e471026e6ab33d59869853453cdbfacee6bd941710ed3bd6007a9c67e4efa" +dependencies = [ + "random-number", +] + [[package]] name = "rayon" version = "1.5.1" @@ -3178,6 +3218,7 @@ dependencies = [ "gethostname", "hashicorp_vault", "maplit", + "passwords", "qovery-engine", "rand 0.7.3", "reqwest 0.10.10", diff --git a/lib/scaleway/bootstrap/ks-master-cluster.tf b/lib/scaleway/bootstrap/ks-master-cluster.tf index 7e21b922..87d41b7c 100644 --- a/lib/scaleway/bootstrap/ks-master-cluster.tf +++ b/lib/scaleway/bootstrap/ks-master-cluster.tf @@ -1,4 +1,4 @@ -resource "scaleway_k8s_cluster" "kubernetes_cluster" { +resource "scaleway_k8s_cluster" "kubernetes_cluster" { name = var.kubernetes_cluster_name version = var.scaleway_ks_version cni = "cilium" diff --git a/lib/scaleway/bootstrap/tf-providers.j2.tf b/lib/scaleway/bootstrap/tf-providers.j2.tf index f602c991..6873302b 100644 --- a/lib/scaleway/bootstrap/tf-providers.j2.tf +++ b/lib/scaleway/bootstrap/tf-providers.j2.tf @@ -35,7 +35,7 @@ terraform { provider "scaleway" { access_key = var.scaleway_access_key secret_key = var.scaleway_secret_key - project_id = var.scaleway_project_id + project_id = var.scaleway_project_id zone = var.zone region = var.region } diff --git a/lib/scaleway/chart_values/mysql/values.j2.yaml b/lib/scaleway/chart_values/mysql/values.j2.yaml index fc9b84bc..406abd06 100644 --- a/lib/scaleway/chart_values/mysql/values.j2.yaml +++ b/lib/scaleway/chart_values/mysql/values.j2.yaml @@ -90,7 +90,7 @@ root: ## MySQL admin password ## ref: https://github.com/bitnami/bitnami-docker-mysql#setting-the-root-password-on-first-run ## - password: {{ database_password }} + password: "{{ database_password }}" ## Option to force users to specify a password. That is required for 'helm upgrade' to work properly. ## If it is not force, a random password will be generated. ## @@ -107,7 +107,7 @@ db: ## Note that this user should be different from the MySQL replication user (replication.user) ## user: {{ database_login }} - password: {{ database_password }} + password: "{{ database_password }}" ## Database to create ## ref: https://github.com/bitnami/bitnami-docker-mysql#creating-a-database-on-first-run ## @@ -311,7 +311,7 @@ master: ## GKE, AWS & OpenStack) ## # storageClass: "-" - storageClass: {{ database_disk_type }} + storageClass: scw-sbv-ssd-0 ## PVC annotations ## annotations: diff --git a/lib/scaleway/chart_values/postgresql/values.j2.yaml b/lib/scaleway/chart_values/postgresql/values.j2.yaml index 0085eb95..357fbda8 100644 --- a/lib/scaleway/chart_values/postgresql/values.j2.yaml +++ b/lib/scaleway/chart_values/postgresql/values.j2.yaml @@ -141,7 +141,7 @@ postgresqlPassword: {{ database_password }} ## Create a database ## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#creating-a-database-on-first-run ## -postgresqlDatabase: {{ database_db_name }} +postgresqlDatabase: {{ sanitized_name }} ## PostgreSQL data dir ## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md @@ -323,7 +323,7 @@ persistence: ## subPath: "" - storageClass: {{ database_disk_type }} + storageClass: scw-sbv-ssd-0 accessModes: - ReadWriteOnce size: {{ database_disk_size_in_gib }}Gi diff --git a/lib/scaleway/services/common/backend.j2.tf b/lib/scaleway/services/common/backend.j2.tf new file mode 100644 index 00000000..b8ea14a2 --- /dev/null +++ b/lib/scaleway/services/common/backend.j2.tf @@ -0,0 +1,8 @@ +terraform { + backend "kubernetes" { + secret_suffix = "{{ tfstate_suffix_name }}" + load_config_file = true + config_path = "{{ kubeconfig_path }}" + namespace = "{{ namespace }}" + } +} diff --git a/lib/scaleway/services/common/providers.j2.tf b/lib/scaleway/services/common/providers.j2.tf new file mode 100644 index 00000000..82ecab6c --- /dev/null +++ b/lib/scaleway/services/common/providers.j2.tf @@ -0,0 +1,37 @@ +terraform { + required_providers { + scaleway = { + source = "scaleway/scaleway" + version = "~> 2.1.0" + } + local = { + source = "hashicorp/local" + version = "~> 1.4" + } + time = { + source = "hashicorp/time" + version = "~> 0.3" + } + } + required_version = ">= 0.13" +} + +provider "scaleway" { + access_key = "{{ scaleway_access_key }}" + secret_key = "{{ scaleway_secret_key }}" + project_id = "{{ scaleway_project_id }}" + zone = "{{ zone }}" + region = "{{ region }}" +} + +data "scaleway_k8s_cluster" "kubernetes_cluster" { + name = "{{kubernetes_cluster_name}}" +} + +provider "helm" { + kubernetes { + host = data.scaleway_k8s_cluster.kubernetes_cluster.apiserver_url + cluster_ca_certificate = base64decode(data.scaleway_k8s_cluster.kubernetes_cluster.kubeconfig.cluster_ca_certificate) + load_config_file = false + } +} diff --git a/lib/scaleway/services/mysql/main.j2.tf b/lib/scaleway/services/mysql/main.j2.tf new file mode 100644 index 00000000..3ed8b768 --- /dev/null +++ b/lib/scaleway/services/mysql/main.j2.tf @@ -0,0 +1,83 @@ +locals { + tags_mysql = { + cluster_name = var.cluster_name + cluster_id = var.kubernetes_cluster_id + region = var.region + q_client_id = var.q_customer_id + q_environment_id = var.q_environment_id + q_project_id = var.q_project_id + database_identifier = var.mysql_identifier + {% if resource_expiration_in_seconds is defined %}ttl = var.resource_expiration_in_seconds{% endif %} + {% if snapshot is defined and snapshot["snapshot_id"] %}meta_last_restored_from = var.snapshot_identifier{% endif %} + } + tags_mysql_list = [for i, v in local.tags_mysql : "${i}=${v}"] # NOTE: Scaleway doesn't support KV style tags +} + +resource "scaleway_rdb_acl" "main" { + instance_id = scaleway_rdb_instance.mysql_instance.id +{%- if publicly_accessible %} + # By default, all IPs are authorized => 0.0.0.0/0 + acl_rules { + ip = "0.0.0.0/0" + description = "accessible from any host" + } +{%- else %} + # TODO(benjaminch): Allow only Scaleway's private traffic + acl_rules { + ip = "0.0.0.0/0" + description = "accessible from any host" + } +{% endif %} + depends_on = [ + scaleway_rdb_instance.mysql_instance + ] +} + +resource "scaleway_rdb_instance" "mysql_instance" { + name = var.database_name + engine = "MySQL-${var.mysql_version_major}" + + node_type = var.instance_class + volume_type = var.storage_type + volume_size_in_gb = var.disk_size + + is_ha_cluster = var.activate_high_availability + disable_backup = !var.activate_backups + + user_name = var.username + password = var.password + + region = var.region + + tags = local.tags_mysql_list + + # TODO:(benjaminch): features to be added at some point but be discussed with Scaleway + # - port + # - instance create timeout + # - instance update timeout + # - instance delete timeout + # - snapshot id for restore: maybe should use volume ? https://registry.terraform.io/providers/scaleway/scaleway/latest/docs/resources/instance_volume => Ask them how to do it + # - db_subnet_group_name: not sure we can customize it? => Ok + # - vpc_security_group_ids: not sure we can customize it? => Ok + # - multi_az: not sure we can customize it? => Ok + # - maintenance apply_immediately: not sure we can customize it? => Ok + # - maintenance maintenance_window: not sure we can customize it? => Ok + # - monitoring_interval: not sure we can customize it? => Ok + # - monitoring_role_arn: not sure we can customize it? => Ok + # - backup backup_retention_period: not sure we can customize it? => Ok + # - backup backup_window: not sure we can customize it? => Ok + # - backup skip_final_snapshot: not sure we can customize it? => Ok + # - backup delete_automated_backups: not sure we can customize it? => Ok + + # available settings to be retrieved via API + # https://developers.scaleway.com/en/products/rdb/api/#get-1eafb7 + # https://api.scaleway.com/rdb/v1/regions/fr-par/database-engines + settings = { + slow_query_log = true + } +} + +resource "scaleway_rdb_database" "mysql_main" { + instance_id = scaleway_rdb_instance.mysql_instance.id + name = var.database_name +} diff --git a/lib/scaleway/services/mysql/variables.j2.tf b/lib/scaleway/services/mysql/variables.j2.tf new file mode 100644 index 00000000..cd2ea139 --- /dev/null +++ b/lib/scaleway/services/mysql/variables.j2.tf @@ -0,0 +1,202 @@ +# Qovery + +variable "cluster_name" { + description = "Kubernetes cluster name" + default = "{{ cluster_name }}" + type = string +} + +variable "region" { + description = "SCW region to store terraform state and lock" + default = "{{ region }}" + type = string +} + +variable "zone" { + description = "SCW zone to store terraform state and lock" + default = "{{ zone }}" + type = string +} + +variable "kubernetes_cluster_id" { + description = "Kubernetes cluster name with region" + default = "{{ kubernetes_cluster_id }}" + type = string +} + +variable "region_cluster_name" { + description = "SCW region to store terraform state and lock" + default = "{{ region }}-{{ cluster_name }}" + type = string +} + +variable "q_project_id" { + description = "Qovery project ID" + default = "{{ project_id }}" + type = string +} + +variable "q_customer_id" { + description = "Qovery customer ID" + default = "{{ owner_id }}" + type = string +} + +variable "q_environment_id" { + description = "Qovery client environment" + default = "{{ environment_id }}" + type = string +} + +# MySQL instance basics + +variable "mysql_identifier" { + description = "MySQL instance name (DB identifier)" + default = "{{ fqdn_id }}" + type = string +} + +variable "port" { + description = "MySQL instance port" + default = {{ database_port }} + type = number +} + +variable "disk_size" { + description = "disk instance size" + default = {{ database_disk_size_in_gib }} + type = number +} + +variable "mysql_version" { + description = "MySQL version" + default = "{{ version }}" + type = string +} + +variable "mysql_version_major" { + description = "MySQL version major" + default = "{{ version_major }}" + type = string +} + +variable "storage_type" { + description = "One of lssd or bssd." + default = "{{ database_disk_type }}" + type = string +} + +variable "instance_class" { + description = "Type of instance: https://www.scaleway.com/fr/tarifs/" + default = "{{database_instance_type}}" + type = string +} + +variable "username" { + description = "Admin username for the master DB user" + default = "{{ database_login }}" + type = string +} + +variable "password" { + description = "Admin password for the master DB user" + default = "{{ database_password }}" + type = string +} + +variable "database_name" { + description = "The name of the database to create when the DB instance is created. If this parameter is not specified, no database is created in the DB instance" + default = "{{ database_name }}" + type = string +} + +# Network + +variable "publicly_accessible" { + description = "Instance publicly accessible" + default = true + type = bool +} + +variable "multi_az" { + description = "Multi availability zones" + default = true + type = bool +} + +# Upgrades + +variable "upgrade_minor" { + description = "Automatic minor version upgrade during window maintenance" + default = true + type = bool +} + +variable "apply_changes_now" { + description = "Apply changes now or during the during the maintenance window" + default = false + type = bool +} + +variable "maintenance_window" { + description = "Maintenance window" + default = "Tue:02:00-Tue:04:00" + type = string +} + +# Backups + +variable "activate_backups" { + description = "Backups activated" + default = {{ activate_backups }} + type = bool +} + +variable "backup_retention_period" { + description = "Backup rentention period" + default = 7 + type = number +} + +variable "backup_window" { + description = "Maintenance window" + default = "00:00-01:00" + type = string +} + +variable "delete_automated_backups" { + description = "Delete automated backups" + default = {{ delete_automated_backups }} + type = bool +} + +variable "skip_final_snapshot" { + description = "Skip final snapshot" + default = {{ skip_final_snapshot }} + type = bool +} + +{%- if snapshot is defined %} +# Snapshots +variable "snapshot_identifier" { + description = "Snapshot ID to restore" + default = "{{ snapshot['snapshot_id']}}" + type = string +} +{% endif %} + +{%- if resource_expiration_in_seconds is defined %} +# Pleco ttl +variable "resource_expiration_in_seconds" { + description = "Resource expiration in seconds" + default = {{ resource_expiration_in_seconds }} + type = number +} +{% endif %} + +# Clustering +variable "activate_high_availability" { + description = "Define if DB should be in cluster mode" + default = {{ activate_high_availability }} + type = bool +} \ No newline at end of file diff --git a/lib/scaleway/services/postgresql/main.j2.tf b/lib/scaleway/services/postgresql/main.j2.tf new file mode 100644 index 00000000..f222feec --- /dev/null +++ b/lib/scaleway/services/postgresql/main.j2.tf @@ -0,0 +1,81 @@ +locals { + tags_postgresql = { + cluster_name = var.cluster_name + cluster_id = var.kubernetes_cluster_id + region = var.region + q_client_id = var.q_customer_id + q_environment_id = var.q_environment_id + q_project_id = var.q_project_id + database_identifier = var.postgresql_identifier + {% if resource_expiration_in_seconds is defined %}ttl = var.resource_expiration_in_seconds{% endif %} + {% if snapshot is defined and snapshot["snapshot_id"] %}meta_last_restored_from = var.snapshot_identifier{% endif %} + } + tags_postgresql_list = [for i, v in local.tags_postgresql : "${i}=${v}"] # NOTE: Scaleway doesn't support KV style tags +} + +resource "scaleway_rdb_acl" "main" { + instance_id = scaleway_rdb_instance.postgresql_instance.id +{%- if publicly_accessible %} + # By default, all IPs are authorized => 0.0.0.0/0 + acl_rules { + ip = "0.0.0.0/0" + description = "accessible from any host" + } +{%- else %} + # TODO(benjaminch): Allow only Scaleway's private traffic + acl_rules { + ip = "0.0.0.0/0" + description = "accessible from any host" + } +{% endif %} + depends_on = [ + scaleway_rdb_instance.postgresql_instance + ] +} + +resource "scaleway_rdb_instance" "postgresql_instance" { + name = var.database_name + engine = "PostgreSQL-${var.postgresql_version_major}" + + node_type = var.instance_class + volume_type = var.storage_type + volume_size_in_gb = var.disk_size + + is_ha_cluster = var.activate_high_availability + disable_backup = !var.activate_backups + + user_name = var.username + password = var.password + + region = var.region + + tags = local.tags_postgresql_list + + # TODO:(benjaminch): features to be added at some point but be discussed with Scaleway + # - port + # - instance create timeout + # - instance update timeout + # - instance delete timeout + # - snapshot id for restore: maybe should use volume ? https://registry.terraform.io/providers/scaleway/scaleway/latest/docs/resources/instance_volume => Ask them how to do it + # - db_subnet_group_name: not sure we can customize it? => Ok + # - vpc_security_group_ids: not sure we can customize it? => Ok + # - multi_az: not sure we can customize it? => Ok + # - maintenance apply_immediately: not sure we can customize it? => Ok + # - maintenance maintenance_window: not sure we can customize it? => Ok + # - monitoring_interval: not sure we can customize it? => Ok + # - monitoring_role_arn: not sure we can customize it? => Ok + # - backup backup_retention_period: not sure we can customize it? => Ok + # - backup backup_window: not sure we can customize it? => Ok + # - backup skip_final_snapshot: not sure we can customize it? => Ok + # - backup delete_automated_backups: not sure we can customize it? => Ok + + # available settings to be retrieved via API + # https://developers.scaleway.com/en/products/rdb/api/#get-1eafb7 + # https://api.scaleway.com/rdb/v1/regions/fr-par/database-engines + # settings = {} TODO(benjaminch): to activate slow queries logs, but not possible for now via `log_min_duration_statement` +} + +resource "scaleway_rdb_database" "postgresql_main" { + instance_id = scaleway_rdb_instance.postgresql_instance.id + name = var.database_name +} diff --git a/lib/scaleway/services/postgresql/variables.j2.tf b/lib/scaleway/services/postgresql/variables.j2.tf new file mode 100644 index 00000000..190e561c --- /dev/null +++ b/lib/scaleway/services/postgresql/variables.j2.tf @@ -0,0 +1,202 @@ +# Qovery + +variable "cluster_name" { + description = "Kubernetes cluster name" + default = "{{ cluster_name }}" + type = string +} + +variable "region" { + description = "SCW region to store terraform state and lock" + default = "{{ region }}" + type = string +} + +variable "zone" { + description = "SCW zone to store terraform state and lock" + default = "{{ zone }}" + type = string +} + +variable "kubernetes_cluster_id" { + description = "Kubernetes cluster name with region" + default = "{{ kubernetes_cluster_id }}" + type = string +} + +variable "region_cluster_name" { + description = "SCW region to store terraform state and lock" + default = "{{ region }}-{{ cluster_name }}" + type = string +} + +variable "q_project_id" { + description = "Qovery project ID" + default = "{{ project_id }}" + type = string +} + +variable "q_customer_id" { + description = "Qovery customer ID" + default = "{{ owner_id }}" + type = string +} + +variable "q_environment_id" { + description = "Qovery client environment" + default = "{{ environment_id }}" + type = string +} + +# PostgreSQL instance basics + +variable "postgresql_identifier" { + description = "PostgreSQL instance name (DB identifier)" + default = "{{ fqdn_id }}" + type = string +} + +variable "port" { + description = "PostgreSQL instance port" + default = {{ database_port }} + type = number +} + +variable "disk_size" { + description = "disk instance size" + default = {{ database_disk_size_in_gib }} + type = number +} + +variable "postgresql_version" { + description = "PostgreSQL version" + default = "{{ version }}" + type = string +} + +variable "postgresql_version_major" { + description = "PostgreSQL version major" + default = "{{ version_major }}" + type = string +} + +variable "storage_type" { + description = "One of lssd or bssd." + default = "{{ database_disk_type }}" + type = string +} + +variable "instance_class" { + description = "Type of instance: https://www.scaleway.com/fr/tarifs/" + default = "{{database_instance_type}}" + type = string +} + +variable "username" { + description = "Admin username for the master DB user" + default = "{{ database_login }}" + type = string +} + +variable "password" { + description = "Admin password for the master DB user" + default = "{{ database_password }}" + type = string +} + +variable "database_name" { + description = "The name of the database to create when the DB instance is created. If this parameter is not specified, no database is created in the DB instance" + default = "{{ database_name }}" + type = string +} + +# Network + +variable "publicly_accessible" { + description = "Instance publicly accessible" + default = true + type = bool +} + +variable "multi_az" { + description = "Multi availability zones" + default = true + type = bool +} + +# Upgrades + +variable "upgrade_minor" { + description = "Automatic minor version upgrade during window maintenance" + default = true + type = bool +} + +variable "apply_changes_now" { + description = "Apply changes now or during the during the maintenance window" + default = false + type = bool +} + +variable "maintenance_window" { + description = "Maintenance window" + default = "Tue:02:00-Tue:04:00" + type = string +} + +# Backups + +variable "activate_backups" { + description = "Backups activated" + default = {{ activate_backups }} + type = bool +} + +variable "backup_retention_period" { + description = "Backup rentention period" + default = 7 + type = number +} + +variable "backup_window" { + description = "Maintenance window" + default = "00:00-01:00" + type = string +} + +variable "delete_automated_backups" { + description = "Delete automated backups" + default = {{ delete_automated_backups }} + type = bool +} + +variable "skip_final_snapshot" { + description = "Skip final snapshot" + default = {{ skip_final_snapshot }} + type = bool +} + +{%- if snapshot is defined %} +# Snapshots +variable "snapshot_identifier" { + description = "Snapshot ID to restore" + default = "{{ snapshot['snapshot_id']}}" + type = string +} +{% endif %} + +{%- if resource_expiration_in_seconds is defined %} +# Pleco ttl +variable "resource_expiration_in_seconds" { + description = "Resource expiration in seconds" + default = {{ resource_expiration_in_seconds }} + type = number +} +{% endif %} + +# Clustering +variable "activate_high_availability" { + description = "Define if DB should be in cluster mode" + default = {{ activate_high_availability }} + type = bool +} \ No newline at end of file diff --git a/src/cloud_provider/aws/application.rs b/src/cloud_provider/aws/application.rs index be82fdd4..0e7d40b3 100644 --- a/src/cloud_provider/aws/application.rs +++ b/src/cloud_provider/aws/application.rs @@ -125,8 +125,8 @@ impl Service for Application { sanitize_name("app", self.name()) } - fn version(&self) -> &str { - self.image.commit_id.as_str() + fn version(&self) -> String { + self.image.commit_id.clone() } fn action(&self) -> &Action { diff --git a/src/cloud_provider/aws/databases/mongodb.rs b/src/cloud_provider/aws/databases/mongodb.rs index 00933a3f..26453ddf 100644 --- a/src/cloud_provider/aws/databases/mongodb.rs +++ b/src/cloud_provider/aws/databases/mongodb.rs @@ -100,8 +100,8 @@ impl Service for MongoDB { new_name } - fn version(&self) -> &str { - self.version.as_str() + fn version(&self) -> String { + self.version.clone() } fn action(&self) -> &Action { @@ -374,7 +374,7 @@ impl Listen for MongoDB { } } -fn get_mongodb_version(requested_version: &str, is_managed_service: bool) -> Result { +fn get_mongodb_version(requested_version: String, is_managed_service: bool) -> Result { if is_managed_service { get_managed_mongodb_version(requested_version) } else { @@ -382,7 +382,7 @@ fn get_mongodb_version(requested_version: &str, is_managed_service: bool) -> Res } } -fn get_managed_mongodb_version(requested_version: &str) -> Result { +fn get_managed_mongodb_version(requested_version: String) -> Result { let mut supported_mongodb_versions = HashMap::new(); // v3.6.0 @@ -405,17 +405,17 @@ mod tests_mongodb { #[test] fn check_mongodb_version() { // managed version - assert_eq!(get_mongodb_version("4", true).unwrap(), "4.0.0"); - assert_eq!(get_mongodb_version("4.0", true).unwrap(), "4.0.0"); + assert_eq!(get_mongodb_version("4".to_string(), true).unwrap(), "4.0.0"); + assert_eq!(get_mongodb_version("4.0".to_string(), true).unwrap(), "4.0.0"); assert_eq!( - get_mongodb_version("4.4", true).unwrap_err().as_str(), + get_mongodb_version("4.4".to_string(), true).unwrap_err().as_str(), "DocumentDB 4.4 version is not supported" ); // self-hosted version - assert_eq!(get_mongodb_version("4", false).unwrap(), "4.4.4"); - assert_eq!(get_mongodb_version("4.2", false).unwrap(), "4.2.12"); + assert_eq!(get_mongodb_version("4".to_string(), false).unwrap(), "4.4.4"); + assert_eq!(get_mongodb_version("4.2".to_string(), false).unwrap(), "4.2.12"); assert_eq!( - get_mongodb_version("3.4", false).unwrap_err().as_str(), + get_mongodb_version("3.4".to_string(), false).unwrap_err().as_str(), "MongoDB 3.4 version is not supported" ); } @@ -451,6 +451,9 @@ mod tests_mongodb { port: 5432, disk_size_in_gib: 10, database_disk_type: "gp2".to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }, vec![], ); diff --git a/src/cloud_provider/aws/databases/mysql.rs b/src/cloud_provider/aws/databases/mysql.rs index 952a7e27..b3d859b2 100644 --- a/src/cloud_provider/aws/databases/mysql.rs +++ b/src/cloud_provider/aws/databases/mysql.rs @@ -96,8 +96,8 @@ impl Service for MySQL { rds_name_sanitizer(max_size, prefix, self.name()) } - fn version(&self) -> &str { - self.version.as_str() + fn version(&self) -> String { + self.version.clone() } fn action(&self) -> &Action { @@ -386,7 +386,7 @@ impl Listen for MySQL { } } -fn get_mysql_version(requested_version: &str, is_managed_service: bool) -> Result { +fn get_mysql_version(requested_version: String, is_managed_service: bool) -> Result { if is_managed_service { get_managed_mysql_version(requested_version) } else { @@ -394,7 +394,7 @@ fn get_mysql_version(requested_version: &str, is_managed_service: bool) -> Resul } } -fn get_managed_mysql_version(requested_version: &str) -> Result { +fn get_managed_mysql_version(requested_version: String) -> Result { let mut supported_mysql_versions = HashMap::new(); // https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_MySQL.html#MySQL.Concepts.VersionMgmt @@ -425,19 +425,19 @@ mod tests_mysql { #[test] fn check_mysql_version() { // managed version - assert_eq!(get_mysql_version("8", true).unwrap(), "8.0.21"); - assert_eq!(get_mysql_version("8.0", true).unwrap(), "8.0.21"); - assert_eq!(get_mysql_version("8.0.16", true).unwrap(), "8.0.16"); + assert_eq!(get_mysql_version("8".to_string(), true).unwrap(), "8.0.21"); + assert_eq!(get_mysql_version("8.0".to_string(), true).unwrap(), "8.0.21"); + assert_eq!(get_mysql_version("8.0.16".to_string(), true).unwrap(), "8.0.16"); assert_eq!( - get_mysql_version("8.0.18", true).unwrap_err().as_str(), + get_mysql_version("8.0.18".to_string(), true).unwrap_err().as_str(), "RDS MySQL 8.0.18 version is not supported" ); // self-hosted version - assert_eq!(get_mysql_version("5", false).unwrap(), "5.7.33"); - assert_eq!(get_mysql_version("5.7", false).unwrap(), "5.7.33"); - assert_eq!(get_mysql_version("5.7.31", false).unwrap(), "5.7.31"); + assert_eq!(get_mysql_version("5".to_string(), false).unwrap(), "5.7.33"); + assert_eq!(get_mysql_version("5.7".to_string(), false).unwrap(), "5.7.33"); + assert_eq!(get_mysql_version("5.7.31".to_string(), false).unwrap(), "5.7.31"); assert_eq!( - get_mysql_version("1.0", false).unwrap_err().as_str(), + get_mysql_version("1.0".to_string(), false).unwrap_err().as_str(), "MySQL 1.0 version is not supported" ); } @@ -473,6 +473,9 @@ mod tests_mysql { port: 3306, disk_size_in_gib: 10, database_disk_type: "gp2".to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }, vec![], ); diff --git a/src/cloud_provider/aws/databases/postgresql.rs b/src/cloud_provider/aws/databases/postgresql.rs index 7ef2f6df..5f7b76b8 100644 --- a/src/cloud_provider/aws/databases/postgresql.rs +++ b/src/cloud_provider/aws/databases/postgresql.rs @@ -96,8 +96,8 @@ impl Service for PostgreSQL { rds_name_sanitizer(max_size, prefix, self.name()) } - fn version(&self) -> &str { - self.version.as_str() + fn version(&self) -> String { + self.version.clone() } fn action(&self) -> &Action { @@ -375,7 +375,7 @@ impl Listen for PostgreSQL { } } -fn get_postgres_version(requested_version: &str, is_managed_service: bool) -> Result { +fn get_postgres_version(requested_version: String, is_managed_service: bool) -> Result { if is_managed_service { get_managed_postgres_version(requested_version) } else { @@ -383,7 +383,7 @@ fn get_postgres_version(requested_version: &str, is_managed_service: bool) -> Re } } -fn get_managed_postgres_version(requested_version: &str) -> Result { +fn get_managed_postgres_version(requested_version: String) -> Result { let mut supported_postgres_versions = HashMap::new(); // https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html#PostgreSQL.Concepts @@ -415,22 +415,22 @@ mod tests_postgres { #[test] fn check_postgres_version() { // managed version - assert_eq!(get_postgres_version("12", true).unwrap(), "12.5"); - assert_eq!(get_postgres_version("12.3", true).unwrap(), "12.3"); + assert_eq!(get_postgres_version("12".to_string(), true).unwrap(), "12.5"); + assert_eq!(get_postgres_version("12.3".to_string(), true).unwrap(), "12.3"); assert_eq!( - get_postgres_version("12.3.0", true).unwrap_err().as_str(), + get_postgres_version("12.3.0".to_string(), true).unwrap_err().as_str(), "Postgresql 12.3.0 version is not supported" ); assert_eq!( - get_postgres_version("11.3", true).unwrap_err().as_str(), + get_postgres_version("11.3".to_string(), true).unwrap_err().as_str(), "Postgresql 11.3 version is not supported" ); // self-hosted version - assert_eq!(get_postgres_version("12", false).unwrap(), "12.6.0"); - assert_eq!(get_postgres_version("12.3", false).unwrap(), "12.3.0"); - assert_eq!(get_postgres_version("12.3.0", false).unwrap(), "12.3.0"); + assert_eq!(get_postgres_version("12".to_string(), false).unwrap(), "12.6.0"); + assert_eq!(get_postgres_version("12.3".to_string(), false).unwrap(), "12.3.0"); + assert_eq!(get_postgres_version("12.3.0".to_string(), false).unwrap(), "12.3.0"); assert_eq!( - get_postgres_version("1.0", false).unwrap_err().as_str(), + get_postgres_version("1.0".to_string(), false).unwrap_err().as_str(), "Postgresql 1.0 version is not supported" ); } @@ -466,6 +466,9 @@ mod tests_postgres { port: 5432, disk_size_in_gib: 10, database_disk_type: "gp2".to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }, vec![], ); diff --git a/src/cloud_provider/aws/databases/redis.rs b/src/cloud_provider/aws/databases/redis.rs index e8fe4ed4..78ab4be7 100644 --- a/src/cloud_provider/aws/databases/redis.rs +++ b/src/cloud_provider/aws/databases/redis.rs @@ -99,8 +99,8 @@ impl Service for Redis { format!("{}{}", prefix, new_name) } - fn version(&self) -> &str { - self.version.as_str() + fn version(&self) -> String { + self.version.clone() } fn action(&self) -> &Action { @@ -385,7 +385,7 @@ impl Listen for Redis { } } -fn get_redis_version(requested_version: &str, is_managed_service: bool) -> Result { +fn get_redis_version(requested_version: String, is_managed_service: bool) -> Result { if is_managed_service { get_managed_redis_version(requested_version) } else { @@ -393,7 +393,7 @@ fn get_redis_version(requested_version: &str, is_managed_service: bool) -> Resul } } -fn get_managed_redis_version(requested_version: &str) -> Result { +fn get_managed_redis_version(requested_version: String) -> Result { let mut supported_redis_versions = HashMap::with_capacity(2); // https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/supported-engine-versions.html @@ -412,18 +412,18 @@ mod tests { #[test] fn check_redis_version() { // managed version - assert_eq!(get_redis_version("6", true).unwrap(), "6.x"); - assert_eq!(get_redis_version("5", true).unwrap(), "5.0.6"); + assert_eq!(get_redis_version("6".to_string(), true).unwrap(), "6.x"); + assert_eq!(get_redis_version("5".to_string(), true).unwrap(), "5.0.6"); assert_eq!( - get_redis_version("1.0", true).unwrap_err().as_str(), + get_redis_version("1.0".to_string(), true).unwrap_err().as_str(), "Elasticache 1.0 version is not supported" ); // self-hosted version - assert_eq!(get_redis_version("6", false).unwrap(), "6.0.9"); - assert_eq!(get_redis_version("6.0", false).unwrap(), "6.0.9"); + assert_eq!(get_redis_version("6".to_string(), false).unwrap(), "6.0.9"); + assert_eq!(get_redis_version("6.0".to_string(), false).unwrap(), "6.0.9"); assert_eq!( - get_redis_version("1.0", false).unwrap_err().as_str(), + get_redis_version("1.0".to_string(), false).unwrap_err().as_str(), "Redis 1.0 version is not supported" ); } @@ -459,6 +459,9 @@ mod tests { port: 5432, disk_size_in_gib: 10, database_disk_type: "gp2".to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }, vec![], ); diff --git a/src/cloud_provider/aws/databases/utilities.rs b/src/cloud_provider/aws/databases/utilities.rs index 05062d28..f519141f 100644 --- a/src/cloud_provider/aws/databases/utilities.rs +++ b/src/cloud_provider/aws/databases/utilities.rs @@ -1,6 +1,7 @@ -use crate::cloud_provider::utilities::get_version_number; +use crate::cloud_provider::utilities::VersionsNumber; use crate::error::StringError; use crate::models::DatabaseKind; +use std::str::FromStr; pub fn rds_name_sanitizer(max_size: usize, prefix: &str, name: &str) -> String { let max_size = max_size - prefix.len(); @@ -12,7 +13,7 @@ pub fn rds_name_sanitizer(max_size: usize, prefix: &str, name: &str) -> String { } pub fn get_parameter_group_from_version(version: &str, database_kind: DatabaseKind) -> Result { - let version_number = match get_version_number(version) { + let version_number = match VersionsNumber::from_str(version) { Ok(v) => { if v.minor.is_none() { return Err(format!( diff --git a/src/cloud_provider/aws/kubernetes/mod.rs b/src/cloud_provider/aws/kubernetes/mod.rs index 0880e965..58c5a1b0 100644 --- a/src/cloud_provider/aws/kubernetes/mod.rs +++ b/src/cloud_provider/aws/kubernetes/mod.rs @@ -730,6 +730,10 @@ impl<'a> Kubernetes for EKS<'a> { self.region.name() } + fn zone(&self) -> &str { + "" + } + fn cloud_provider(&self) -> &dyn CloudProvider { self.cloud_provider } diff --git a/src/cloud_provider/aws/router.rs b/src/cloud_provider/aws/router.rs index 18c0fc14..5576cd06 100644 --- a/src/cloud_provider/aws/router.rs +++ b/src/cloud_provider/aws/router.rs @@ -68,8 +68,8 @@ impl Service for Router { sanitize_name("router", self.name()) } - fn version(&self) -> &str { - "1.0" + fn version(&self) -> String { + "1.0".to_string() } fn action(&self) -> &Action { diff --git a/src/cloud_provider/digitalocean/application.rs b/src/cloud_provider/digitalocean/application.rs index 9711c0f4..c1ed77bb 100644 --- a/src/cloud_provider/digitalocean/application.rs +++ b/src/cloud_provider/digitalocean/application.rs @@ -127,8 +127,8 @@ impl Service for Application { sanitize_name("app", self.name()) } - fn version(&self) -> &str { - self.image.commit_id.as_str() + fn version(&self) -> String { + self.image.commit_id.clone() } fn action(&self) -> &Action { diff --git a/src/cloud_provider/digitalocean/databases/mongodb.rs b/src/cloud_provider/digitalocean/databases/mongodb.rs index 6e8fbc7f..13e5c19c 100644 --- a/src/cloud_provider/digitalocean/databases/mongodb.rs +++ b/src/cloud_provider/digitalocean/databases/mongodb.rs @@ -60,7 +60,7 @@ impl MongoDB { } fn matching_correct_version(&self) -> Result { - check_service_version(get_self_hosted_mongodb_version(self.version()), self) + check_service_version(get_self_hosted_mongodb_version(self.version().clone()), self) } } @@ -87,8 +87,8 @@ impl Service for MongoDB { sanitize_name("mongodb", self.name()) } - fn version(&self) -> &str { - self.version.as_str() + fn version(&self) -> String { + self.version.clone() } fn action(&self) -> &Action { diff --git a/src/cloud_provider/digitalocean/databases/mysql.rs b/src/cloud_provider/digitalocean/databases/mysql.rs index 3c5fa5ce..49777519 100644 --- a/src/cloud_provider/digitalocean/databases/mysql.rs +++ b/src/cloud_provider/digitalocean/databases/mysql.rs @@ -87,8 +87,8 @@ impl Service for MySQL { sanitize_name("mysql", self.name()) } - fn version(&self) -> &str { - self.version.as_str() + fn version(&self) -> String { + self.version.clone() } fn action(&self) -> &Action { diff --git a/src/cloud_provider/digitalocean/databases/postgresql.rs b/src/cloud_provider/digitalocean/databases/postgresql.rs index e7471bbb..43723481 100644 --- a/src/cloud_provider/digitalocean/databases/postgresql.rs +++ b/src/cloud_provider/digitalocean/databases/postgresql.rs @@ -87,8 +87,8 @@ impl Service for PostgreSQL { sanitize_name("postgresql", self.name()) } - fn version(&self) -> &str { - self.version.as_str() + fn version(&self) -> String { + self.version.clone() } fn action(&self) -> &Action { diff --git a/src/cloud_provider/digitalocean/databases/redis.rs b/src/cloud_provider/digitalocean/databases/redis.rs index 933668b5..c61a1303 100644 --- a/src/cloud_provider/digitalocean/databases/redis.rs +++ b/src/cloud_provider/digitalocean/databases/redis.rs @@ -87,8 +87,8 @@ impl Service for Redis { sanitize_name("redis", self.name()) } - fn version(&self) -> &str { - self.version.as_str() + fn version(&self) -> String { + self.version.clone() } fn action(&self) -> &Action { diff --git a/src/cloud_provider/digitalocean/kubernetes/doks_api.rs b/src/cloud_provider/digitalocean/kubernetes/doks_api.rs index 6d818522..75de2b85 100644 --- a/src/cloud_provider/digitalocean/kubernetes/doks_api.rs +++ b/src/cloud_provider/digitalocean/kubernetes/doks_api.rs @@ -1,8 +1,9 @@ use crate::cloud_provider::digitalocean::do_api_common::{do_get_from_api, DoApiType}; use crate::cloud_provider::digitalocean::models::doks::KubernetesCluster; use crate::cloud_provider::digitalocean::models::doks::{DoksList, DoksOptions, KubernetesVersion}; -use crate::cloud_provider::utilities::get_version_number; +use crate::cloud_provider::utilities::VersionsNumber; use crate::error::{SimpleError, SimpleErrorKind, StringError}; +use std::str::FromStr; pub fn get_doks_info_from_name( json_content: &str, @@ -76,10 +77,10 @@ fn get_do_kubernetes_latest_slug_version( doks_versions: &Vec, wished_version: &str, ) -> Result, StringError> { - let wished_k8s_version = get_version_number(wished_version)?; + let wished_k8s_version = VersionsNumber::from_str(wished_version)?; for kubernetes_doks_version in doks_versions { - let current_k8s_version = get_version_number(kubernetes_doks_version.kubernetes_version.as_str())?; + let current_k8s_version = VersionsNumber::from_str(kubernetes_doks_version.kubernetes_version.as_str())?; if current_k8s_version.major == wished_k8s_version.major && current_k8s_version.minor == wished_k8s_version.minor { diff --git a/src/cloud_provider/digitalocean/kubernetes/mod.rs b/src/cloud_provider/digitalocean/kubernetes/mod.rs index 8d15697e..2810fbf8 100644 --- a/src/cloud_provider/digitalocean/kubernetes/mod.rs +++ b/src/cloud_provider/digitalocean/kubernetes/mod.rs @@ -431,6 +431,10 @@ impl<'a> Kubernetes for DOKS<'a> { self.region.as_str() } + fn zone(&self) -> &str { + "" + } + fn cloud_provider(&self) -> &dyn CloudProvider { self.cloud_provider } diff --git a/src/cloud_provider/digitalocean/router.rs b/src/cloud_provider/digitalocean/router.rs index 8e135fff..020daaf0 100644 --- a/src/cloud_provider/digitalocean/router.rs +++ b/src/cloud_provider/digitalocean/router.rs @@ -68,8 +68,8 @@ impl Service for Router { sanitize_name("router", self.name()) } - fn version(&self) -> &str { - "1.0" + fn version(&self) -> String { + "1.0".to_string() } fn action(&self) -> &Action { diff --git a/src/cloud_provider/kubernetes.rs b/src/cloud_provider/kubernetes.rs index b463897a..79c53e84 100644 --- a/src/cloud_provider/kubernetes.rs +++ b/src/cloud_provider/kubernetes.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; use crate::cloud_provider::environment::Environment; use crate::cloud_provider::service::CheckAction; -use crate::cloud_provider::utilities::{get_version_number, VersionsNumber}; +use crate::cloud_provider::utilities::VersionsNumber; use crate::cloud_provider::{service, CloudProvider, DeploymentTarget}; use crate::cmd::kubectl; use crate::cmd::kubectl::{ @@ -26,6 +26,7 @@ use crate::error::{ use crate::models::{Context, Listen, ListenersHelper, ProgressInfo, ProgressLevel, ProgressScope, StringPath}; use crate::object_storage::ObjectStorage; use crate::unit_conversion::{any_to_mi, cpu_string_to_float}; +use std::str::FromStr; pub trait Kubernetes: Listen { fn context(&self) -> &Context; @@ -40,6 +41,7 @@ pub trait Kubernetes: Listen { } fn version(&self) -> &str; fn region(&self) -> &str; + fn zone(&self) -> &str; fn cloud_provider(&self) -> &dyn CloudProvider; fn dns_provider(&self) -> &dyn DnsProvider; fn config_file_store(&self) -> &dyn ObjectStorage; @@ -618,7 +620,7 @@ where // check master versions let v = kubectl_exec_version(&kubernetes_config, envs.clone())?; let masters_version = - match get_version_number(format!("{}.{}", v.server_version.major, v.server_version.minor).as_str()) { + match VersionsNumber::from_str(format!("{}.{}", v.server_version.major, v.server_version.minor).as_str()) { Ok(vn) => Ok(vn), Err(e) => Err(SimpleError { kind: SimpleErrorKind::Other, @@ -644,7 +646,7 @@ where for node in nodes.items { // check kubelet version - match get_version_number(node.status.node_info.kubelet_version.as_str()) { + match VersionsNumber::from_str(node.status.node_info.kubelet_version.as_str()) { Ok(vn) => deployed_workers_version.push(vn), Err(e) => { return Err(SimpleError { @@ -657,7 +659,7 @@ where } } // check kube-proxy version - match get_version_number(node.status.node_info.kube_proxy_version.as_str()) { + match VersionsNumber::from_str(node.status.node_info.kube_proxy_version.as_str()) { Ok(vn) => deployed_workers_version.push(vn), Err(e) => { return Err(SimpleError { @@ -709,7 +711,7 @@ fn check_kubernetes_upgrade_status( let mut older_masters_version_detected = false; let mut older_workers_version_detected = false; - let wished_version = match get_version_number(requested_version) { + let wished_version = match VersionsNumber::from_str(requested_version) { Ok(v) => v, Err(e) => { let msg = format!( @@ -872,21 +874,14 @@ mod tests { use crate::cloud_provider::kubernetes::{ check_kubernetes_upgrade_status, compare_kubernetes_cluster_versions_for_upgrade, KubernetesNodesType, }; - use crate::cloud_provider::utilities::{get_version_number, VersionsNumber}; + use crate::cloud_provider::utilities::VersionsNumber; use crate::cmd::structs::{KubernetesList, KubernetesNode, KubernetesVersion}; + use std::str::FromStr; #[test] pub fn check_kubernetes_upgrade_method() { - let version_1_16 = VersionsNumber { - major: "1".to_string(), - minor: Some("16".to_string()), - patch: None, - }; - let version_1_17 = VersionsNumber { - major: "1".to_string(), - minor: Some("17".to_string()), - patch: None, - }; + let version_1_16 = VersionsNumber::new("1".to_string(), Some("16".to_string()), None, None); + let version_1_17 = VersionsNumber::new("1".to_string(), Some("17".to_string()), None, None); // test full cluster upgrade (masters + workers) let result = check_kubernetes_upgrade_status("1.17", version_1_16.clone(), vec![version_1_16.clone()]).unwrap(); @@ -1003,32 +998,25 @@ mod tests { let validate_providers = vec![ KubernetesVersionToCheck { json: kubectl_version_aws, - wished_version: VersionsNumber { - major: "1".to_string(), - minor: Some("16".to_string()), - patch: None, - }, + wished_version: VersionsNumber::new("1".to_string(), Some("16".to_string()), None, None), }, KubernetesVersionToCheck { json: kubectl_version_do, - wished_version: VersionsNumber { - major: "1".to_string(), - minor: Some("18".to_string()), - patch: None, - }, + wished_version: VersionsNumber::new("1".to_string(), Some("18".to_string()), None, None), }, ]; for mut provider in validate_providers { let provider_server_version: KubernetesVersion = serde_json::from_str(provider.json).unwrap(); - let provider_version = get_version_number( + let provider_version = VersionsNumber::from_str( format!( "{}", - VersionsNumber { - major: provider_server_version.server_version.major, - minor: Some(provider_server_version.server_version.minor), - patch: None - } + VersionsNumber::new( + provider_server_version.server_version.major, + Some(provider_server_version.server_version.minor), + None, + None, + ), ) .as_str(), ) @@ -1372,19 +1360,11 @@ mod tests { let validate_providers = vec![ KubernetesVersionToCheck { json: kubectl_version_aws, - wished_version: VersionsNumber { - major: "1".to_string(), - minor: Some("16".to_string()), - patch: None, - }, + wished_version: VersionsNumber::new("1".to_string(), Some("16".to_string()), None, None), }, KubernetesVersionToCheck { json: kubectl_version_do, - wished_version: VersionsNumber { - major: "1".to_string(), - minor: Some("18".to_string()), - patch: None, - }, + wished_version: VersionsNumber::new("1".to_string(), Some("18".to_string()), None, None), }, ]; @@ -1392,8 +1372,8 @@ mod tests { let provider_server_version: KubernetesList = serde_json::from_str(provider.json).expect("Can't read workers json from {} provider"); for node in provider_server_version.items { - let kubelet = get_version_number(&node.status.node_info.kubelet_version).unwrap(); - let kube_proxy = get_version_number(&node.status.node_info.kube_proxy_version).unwrap(); + let kubelet = VersionsNumber::from_str(&node.status.node_info.kubelet_version).unwrap(); + let kube_proxy = VersionsNumber::from_str(&node.status.node_info.kube_proxy_version).unwrap(); // upgrade is not required //print_kubernetes_version(&provider_version, &provider.wished_version); diff --git a/src/cloud_provider/scaleway/application.rs b/src/cloud_provider/scaleway/application.rs index f48b3e67..83f19789 100644 --- a/src/cloud_provider/scaleway/application.rs +++ b/src/cloud_provider/scaleway/application.rs @@ -128,8 +128,8 @@ impl Service for Application { sanitize_name("app", self.name()) } - fn version(&self) -> &str { - self.image.commit_id.as_str() + fn version(&self) -> String { + self.image.commit_id.clone() } fn action(&self) -> &Action { @@ -437,6 +437,16 @@ impl Zone { Zone::Warsaw1 => Region::Warsaw, } } + + // TODO(benjaminch): improve / refactor this! + pub fn region_str(&self) -> &str { + match self { + Zone::Paris1 => "fr-par", + Zone::Paris2 => "fr-par", + Zone::Amsterdam1 => "nl-ams", + Zone::Warsaw1 => "pl-waw", + } + } } impl fmt::Display for Zone { diff --git a/src/cloud_provider/scaleway/databases/mongodb.rs b/src/cloud_provider/scaleway/databases/mongodb.rs index c2f55b58..4c75d7ce 100644 --- a/src/cloud_provider/scaleway/databases/mongodb.rs +++ b/src/cloud_provider/scaleway/databases/mongodb.rs @@ -87,8 +87,8 @@ impl Service for MongoDB { sanitize_name("mongodb", self.name()) } - fn version(&self) -> &str { - self.version.as_str() + fn version(&self) -> String { + self.version.clone() } fn action(&self) -> &Action { diff --git a/src/cloud_provider/scaleway/databases/mysql.rs b/src/cloud_provider/scaleway/databases/mysql.rs index 8a328768..20812004 100644 --- a/src/cloud_provider/scaleway/databases/mysql.rs +++ b/src/cloud_provider/scaleway/databases/mysql.rs @@ -1,24 +1,29 @@ use tera::Context as TeraContext; +use crate::cloud_provider::environment::Kind; use crate::cloud_provider::service::{ check_service_version, default_tera_context, delete_stateful_service, deploy_stateful_service, get_tfstate_name, get_tfstate_suffix, scale_down_database, send_progress_on_long_task, Action, Backup, Create, Database, DatabaseOptions, DatabaseType, Delete, Downgrade, Helm, Pause, Service, ServiceType, StatefulService, Terraform, Upgrade, }; -use crate::cloud_provider::utilities::{get_self_hosted_mysql_version, sanitize_name}; +use crate::cloud_provider::utilities::{ + get_self_hosted_mysql_version, get_supported_version_to_use, sanitize_name, VersionsNumber, +}; use crate::cloud_provider::DeploymentTarget; use crate::cmd::helm::Timeout; use crate::cmd::kubectl; -use crate::error::{EngineError, EngineErrorScope}; +use crate::error::{EngineError, EngineErrorCause, EngineErrorScope, StringError}; use crate::models::{Context, Listen, Listener, Listeners}; +use std::collections::HashMap; +use std::str::FromStr; pub struct MySQL { context: Context, id: String, action: Action, name: String, - version: String, + version: VersionsNumber, fqdn: String, fqdn_id: String, total_cpus: String, @@ -34,7 +39,7 @@ impl MySQL { id: &str, action: Action, name: &str, - version: &str, + version: VersionsNumber, fqdn: &str, fqdn_id: &str, total_cpus: String, @@ -48,7 +53,7 @@ impl MySQL { action, id: id.to_string(), name: name.to_string(), - version: version.to_string(), + version, fqdn: fqdn.to_string(), fqdn_id: fqdn_id.to_string(), total_cpus, @@ -59,8 +64,35 @@ impl MySQL { } } - fn matching_correct_version(&self) -> Result { - check_service_version(get_self_hosted_mysql_version(self.version()), self) + fn matching_correct_version(&self, is_managed_services: bool) -> Result { + let version = check_service_version(Self::pick_mysql_version(self.version(), is_managed_services), self)?; + match VersionsNumber::from_str(version.as_str()) { + Ok(res) => Ok(res), + Err(e) => Err(self.engine_error( + EngineErrorCause::Internal, + format!("cannot parse database version, err: {}", e), + )), + } + } + + fn pick_mysql_version(requested_version: String, is_managed_service: bool) -> Result { + if is_managed_service { + Self::pick_managed_mysql_version(requested_version) + } else { + get_self_hosted_mysql_version(requested_version) + } + } + + fn pick_managed_mysql_version(requested_version: String) -> Result { + // Scaleway supported MySQL versions + // https://api.scaleway.com/rdb/v1/regions/fr-par/database-engines + let mut supported_mysql_versions = HashMap::new(); + + // {"name": "MySQL", "version":"8","end_of_life":"2026-04-01T00:00:00Z"} + supported_mysql_versions.insert("8".to_string(), "8".to_string()); + supported_mysql_versions.insert("8.0".to_string(), "8.0".to_string()); + + get_supported_version_to_use("RDB MySQL", supported_mysql_versions, requested_version) } } @@ -87,8 +119,8 @@ impl Service for MySQL { sanitize_name("mysql", self.name()) } - fn version(&self) -> &str { - self.version.as_str() + fn version(&self) -> String { + self.version.to_string() } fn action(&self) -> &Action { @@ -127,6 +159,11 @@ impl Service for MySQL { let mut context = default_tera_context(self, kubernetes, environment); + let is_managed_services = match environment.kind { + Kind::Production => true, + Kind::Development => false, + }; + // we need the kubernetes config file to store tfstates file in kube secrets let kube_config_file_path = kubernetes.config_file_path()?; context.insert("kubeconfig_path", &kube_config_file_path); @@ -139,8 +176,9 @@ impl Service for MySQL { context.insert("namespace", environment.namespace()); - let version = &self.matching_correct_version()?; - context.insert("version", &version); + let version = &self.matching_correct_version(is_managed_services)?; + context.insert("version_major", &version.to_major_version_string()); + context.insert("version", &version.to_string()); // Scaleway needs to have major version only for (k, v) in kubernetes.cloud_provider().tera_context_environment_variables() { context.insert(k, v); @@ -158,6 +196,7 @@ impl Service for MySQL { context.insert("database_disk_size_in_gib", &self.options.disk_size_in_gib); context.insert("database_instance_type", &self.database_instance_type); context.insert("database_disk_type", &self.options.database_disk_type); + context.insert("database_name", &self.sanitized_name()); context.insert("database_ram_size_in_mib", &self.total_ram_in_mib); context.insert("database_total_cpus", &self.total_cpus); context.insert("database_fqdn", &self.options.host.as_str()); @@ -165,7 +204,12 @@ impl Service for MySQL { context.insert("tfstate_suffix_name", &get_tfstate_suffix(self)); context.insert("tfstate_name", &get_tfstate_name(self)); + context.insert("publicly_accessible", &self.options.publicly_accessible); + context.insert("activate_high_availability", &self.options.activate_high_availability); + context.insert("activate_backups", &self.options.activate_backups); + context.insert("delete_automated_backups", &self.context().is_test_cluster()); + context.insert("skip_final_snapshot", &self.context().is_test_cluster()); if self.context.resource_expiration_in_seconds().is_some() { context.insert( "resource_expiration_in_seconds", @@ -223,15 +267,12 @@ impl Create for MySQL { fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> { info!("SCW.MySQL.on_create() called for {}", self.name()); - send_progress_on_long_task( - self, - crate::cloud_provider::service::Action::Create, - Box::new(|| deploy_stateful_service(target, self)), - ) + send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || { + deploy_stateful_service(target, self) + }) } fn on_create_check(&self) -> Result<(), EngineError> { - //FIXME : perform an actual check Ok(()) } @@ -266,11 +307,9 @@ impl Delete for MySQL { fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> { info!("SCW.MySQL.on_delete() called for {}", self.name()); - send_progress_on_long_task( - self, - crate::cloud_provider::service::Action::Delete, - Box::new(|| delete_stateful_service(target, self)), - ) + send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || { + delete_stateful_service(target, self) + }) } fn on_delete_check(&self) -> Result<(), EngineError> { diff --git a/src/cloud_provider/scaleway/databases/postgresql.rs b/src/cloud_provider/scaleway/databases/postgresql.rs index 5de3010e..918c7ea9 100644 --- a/src/cloud_provider/scaleway/databases/postgresql.rs +++ b/src/cloud_provider/scaleway/databases/postgresql.rs @@ -1,24 +1,29 @@ use tera::Context as TeraContext; +use crate::cloud_provider::environment::Kind; use crate::cloud_provider::service::{ check_service_version, default_tera_context, delete_stateful_service, deploy_stateful_service, get_tfstate_name, get_tfstate_suffix, scale_down_database, send_progress_on_long_task, Action, Backup, Create, Database, DatabaseOptions, DatabaseType, Delete, Downgrade, Helm, Pause, Service, ServiceType, StatefulService, Terraform, Upgrade, }; -use crate::cloud_provider::utilities::{get_self_hosted_postgres_version, sanitize_name}; +use crate::cloud_provider::utilities::{ + get_self_hosted_postgres_version, get_supported_version_to_use, sanitize_name, VersionsNumber, +}; use crate::cloud_provider::DeploymentTarget; use crate::cmd::helm::Timeout; use crate::cmd::kubectl; -use crate::error::{EngineError, EngineErrorScope}; +use crate::error::{EngineError, EngineErrorCause, EngineErrorScope, StringError}; use crate::models::{Context, Listen, Listener, Listeners}; +use std::collections::HashMap; +use std::str::FromStr; pub struct PostgreSQL { context: Context, id: String, action: Action, name: String, - version: String, + version: VersionsNumber, fqdn: String, fqdn_id: String, total_cpus: String, @@ -34,7 +39,7 @@ impl PostgreSQL { id: &str, action: Action, name: &str, - version: &str, + version: VersionsNumber, fqdn: &str, fqdn_id: &str, total_cpus: String, @@ -43,12 +48,12 @@ impl PostgreSQL { options: DatabaseOptions, listeners: Listeners, ) -> Self { - PostgreSQL { + Self { context, action, id: id.to_string(), name: name.to_string(), - version: version.to_string(), + version, fqdn: fqdn.to_string(), fqdn_id: fqdn_id.to_string(), total_cpus, @@ -59,8 +64,44 @@ impl PostgreSQL { } } - fn matching_correct_version(&self) -> Result { - check_service_version(get_self_hosted_postgres_version(self.version()), self) + fn matching_correct_version(&self, is_managed_services: bool) -> Result { + let version = check_service_version(Self::pick_postgres_version(self.version(), is_managed_services), self)?; + match VersionsNumber::from_str(version.as_str()) { + Ok(res) => Ok(res), + Err(e) => Err(self.engine_error( + EngineErrorCause::Internal, + format!("cannot parse database version, err: {}", e), + )), + } + } + + fn pick_postgres_version(requested_version: String, is_managed_service: bool) -> Result { + if is_managed_service { + Self::pick_managed_postgres_version(requested_version) + } else { + get_self_hosted_postgres_version(requested_version) + } + } + + fn pick_managed_postgres_version(requested_version: String) -> Result { + // Scaleway supported postgres versions + // https://api.scaleway.com/rdb/v1/regions/fr-par/database-engines + let mut supported_postgres_versions = HashMap::new(); + + // {"name":"PostgreSQL","version":"13","end_of_life":"2025-11-13T00:00:00Z"} + // {"name":"PostgreSQL","version":"12","end_of_life":"2024-11-14T00:00:00Z"} + // {"name":"PostgreSQL","version":"11","end_of_life":"2023-11-09T00:00:00Z"} + // {"name":"PostgreSQL","version":"10","end_of_life":"2022-11-10T00:00:00Z"} + supported_postgres_versions.insert("10".to_string(), "10".to_string()); + supported_postgres_versions.insert("10.0".to_string(), "10.0".to_string()); + supported_postgres_versions.insert("11".to_string(), "11".to_string()); + supported_postgres_versions.insert("11.0".to_string(), "11.0".to_string()); + supported_postgres_versions.insert("12".to_string(), "12".to_string()); + supported_postgres_versions.insert("12.0".to_string(), "12.0".to_string()); + supported_postgres_versions.insert("13".to_string(), "13".to_string()); + supported_postgres_versions.insert("13.0".to_string(), "13.0".to_string()); + + get_supported_version_to_use("RDB postgres", supported_postgres_versions, requested_version) } } @@ -87,8 +128,8 @@ impl Service for PostgreSQL { sanitize_name("postgresql", self.name()) } - fn version(&self) -> &str { - self.version.as_str() + fn version(&self) -> String { + self.version.to_string() } fn action(&self) -> &Action { @@ -127,6 +168,11 @@ impl Service for PostgreSQL { let mut context = default_tera_context(self, kubernetes, environment); + let is_managed_services = match environment.kind { + Kind::Production => true, + Kind::Development => false, + }; + // we need the kubernetes config file to store tfstates file in kube secrets let kube_config_file_path = kubernetes.config_file_path()?; context.insert("kubeconfig_path", &kube_config_file_path); @@ -139,8 +185,9 @@ impl Service for PostgreSQL { context.insert("namespace", environment.namespace()); - let version = self.matching_correct_version()?; - context.insert("version", &version); + let version = &self.matching_correct_version(is_managed_services)?; + context.insert("version_major", &version.to_major_version_string()); + context.insert("version", &version.to_string()); // Scaleway needs to have major version only for (k, v) in kubernetes.cloud_provider().tera_context_environment_variables() { context.insert(k, v); @@ -152,13 +199,13 @@ impl Service for PostgreSQL { context.insert("fqdn_id", self.fqdn_id.as_str()); context.insert("fqdn", self.fqdn.as_str()); - context.insert("database_db_name", self.name()); context.insert("database_login", self.options.login.as_str()); context.insert("database_password", self.options.password.as_str()); context.insert("database_port", &self.private_port()); context.insert("database_disk_size_in_gib", &self.options.disk_size_in_gib); context.insert("database_instance_type", &self.database_instance_type); context.insert("database_disk_type", &self.options.database_disk_type); + context.insert("database_name", &self.sanitized_name()); context.insert("database_ram_size_in_mib", &self.total_ram_in_mib); context.insert("database_total_cpus", &self.total_cpus); context.insert("database_fqdn", &self.options.host.as_str()); @@ -166,8 +213,11 @@ impl Service for PostgreSQL { context.insert("tfstate_suffix_name", &get_tfstate_suffix(self)); context.insert("tfstate_name", &get_tfstate_name(self)); + context.insert("publicly_accessible", &self.options.publicly_accessible); + context.insert("activate_high_availability", &self.options.activate_high_availability); + context.insert("activate_backups", &self.options.activate_backups); context.insert("delete_automated_backups", &self.context().is_test_cluster()); - + context.insert("skip_final_snapshot", &self.context().is_test_cluster()); if self.context.resource_expiration_in_seconds().is_some() { context.insert( "resource_expiration_in_seconds", @@ -225,11 +275,9 @@ impl Create for PostgreSQL { fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> { info!("SCW.PostgreSQL.on_create() called for {}", self.name()); - send_progress_on_long_task( - self, - crate::cloud_provider::service::Action::Create, - Box::new(|| deploy_stateful_service(target, self)), - ) + send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || { + deploy_stateful_service(target, self) + }) } fn on_create_check(&self) -> Result<(), EngineError> { @@ -267,11 +315,9 @@ impl Delete for PostgreSQL { fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> { info!("SCW.PostgreSQL.on_delete() called for {}", self.name()); - send_progress_on_long_task( - self, - crate::cloud_provider::service::Action::Delete, - Box::new(|| delete_stateful_service(target, self)), - ) + send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || { + delete_stateful_service(target, self) + }) } fn on_delete_check(&self) -> Result<(), EngineError> { @@ -279,8 +325,7 @@ impl Delete for PostgreSQL { } fn on_delete_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> { - warn!("SCW.PostgreSQL.on_create_error() called for {}", self.name()); - + warn!("SCW.MySQL.on_create_error() called for {}", self.name()); Ok(()) } } diff --git a/src/cloud_provider/scaleway/databases/redis.rs b/src/cloud_provider/scaleway/databases/redis.rs index 8205b0ec..50ba86a1 100644 --- a/src/cloud_provider/scaleway/databases/redis.rs +++ b/src/cloud_provider/scaleway/databases/redis.rs @@ -87,8 +87,8 @@ impl Service for Redis { sanitize_name("redis", self.name()) } - fn version(&self) -> &str { - self.version.as_str() + fn version(&self) -> String { + self.version.clone() } fn action(&self) -> &Action { diff --git a/src/cloud_provider/scaleway/kubernetes/helm_charts.rs b/src/cloud_provider/scaleway/kubernetes/helm_charts.rs index 931a87d7..b86f280d 100644 --- a/src/cloud_provider/scaleway/kubernetes/helm_charts.rs +++ b/src/cloud_provider/scaleway/kubernetes/helm_charts.rs @@ -3,7 +3,7 @@ use crate::cloud_provider::helm::{ HelmChart, HelmChartNamespaces, PrometheusOperatorConfigChart, }; use crate::cloud_provider::qovery::{get_qovery_app_version, QoveryAgent, QoveryAppName, QoveryEngine}; -use crate::cloud_provider::scaleway::application::Zone; +use crate::cloud_provider::scaleway::application::{Region, Zone}; use crate::cloud_provider::scaleway::kubernetes::KapsuleOptions; use crate::error::{SimpleError, SimpleErrorKind}; use semver::Version; @@ -21,11 +21,13 @@ pub struct ChartsConfigPrerequisites { pub organization_id: String, pub cluster_id: String, pub zone: Zone, + pub region: Region, pub cluster_name: String, pub cloud_provider: String, pub test_cluster: bool, pub scw_access_key: String, pub scw_secret_key: String, + pub scw_project_id: String, pub ff_log_history_enabled: bool, pub ff_metrics_history_enabled: bool, pub managed_dns_name: String, @@ -51,6 +53,7 @@ impl ChartsConfigPrerequisites { test_cluster: bool, scw_access_key: String, scw_secret_key: String, + scw_project_id: String, ff_log_history_enabled: bool, ff_metrics_history_enabled: bool, managed_dns_name: String, @@ -68,11 +71,13 @@ impl ChartsConfigPrerequisites { organization_id, cluster_id, zone, + region: zone.region(), cluster_name, cloud_provider, test_cluster, scw_access_key, scw_secret_key, + scw_project_id, ff_log_history_enabled, ff_metrics_history_enabled, managed_dns_name, diff --git a/src/cloud_provider/scaleway/kubernetes/mod.rs b/src/cloud_provider/scaleway/kubernetes/mod.rs index e1c4f069..0bdb4013 100644 --- a/src/cloud_provider/scaleway/kubernetes/mod.rs +++ b/src/cloud_provider/scaleway/kubernetes/mod.rs @@ -351,6 +351,10 @@ impl<'a> Kubernetes for Kapsule<'a> { } fn region(&self) -> &str { + self.zone.region_str() + } + + fn zone(&self) -> &str { self.zone.as_str() } @@ -531,6 +535,7 @@ impl<'a> Kubernetes for Kapsule<'a> { self.context.is_test_cluster(), self.cloud_provider.access_key.to_string(), self.cloud_provider.secret_key.to_string(), + self.options.scaleway_project_id.to_string(), self.context.is_feature_enabled(&Features::LogsHistory), self.context.is_feature_enabled(&Features::MetricsHistory), self.dns_provider.domain().to_string(), diff --git a/src/cloud_provider/scaleway/mod.rs b/src/cloud_provider/scaleway/mod.rs index 7601c2e0..19a181af 100644 --- a/src/cloud_provider/scaleway/mod.rs +++ b/src/cloud_provider/scaleway/mod.rs @@ -1,7 +1,7 @@ use std::any::Any; use crate::cloud_provider::{CloudProvider, EngineError, Kind, TerraformStateCredentials}; -use crate::constants::{SCALEWAY_ACCESS_KEY, SCALEWAY_SECRET_KEY}; +use crate::constants::{SCALEWAY_ACCESS_KEY, SCALEWAY_DEFAULT_PROJECT_ID, SCALEWAY_SECRET_KEY}; use crate::models::{Context, Listen, Listener, Listeners}; pub mod application; @@ -16,6 +16,7 @@ pub struct Scaleway { organization_id: String, pub access_key: String, pub secret_key: String, + pub project_id: String, terraform_state_credentials: TerraformStateCredentials, listeners: Listeners, } @@ -28,6 +29,7 @@ impl Scaleway { name: &str, access_key: &str, secret_key: &str, + project_id: &str, terraform_state_credentials: TerraformStateCredentials, ) -> Scaleway { Scaleway { @@ -37,6 +39,7 @@ impl Scaleway { name: name.to_string(), access_key: access_key.to_string(), secret_key: secret_key.to_string(), + project_id: project_id.to_string(), terraform_state_credentials, listeners: vec![], } @@ -73,6 +76,7 @@ impl CloudProvider for Scaleway { vec![ (SCALEWAY_ACCESS_KEY, self.access_key.as_str()), (SCALEWAY_SECRET_KEY, self.secret_key.as_str()), + (SCALEWAY_DEFAULT_PROJECT_ID, self.project_id.as_str()), ] } @@ -80,6 +84,7 @@ impl CloudProvider for Scaleway { vec![ ("scaleway_access_key", self.access_key.as_str()), ("scaleway_secret_key", self.secret_key.as_str()), + ("scaleway_project_id", self.project_id.as_str()), ] } diff --git a/src/cloud_provider/scaleway/router.rs b/src/cloud_provider/scaleway/router.rs index e50e6ebe..f6c56059 100644 --- a/src/cloud_provider/scaleway/router.rs +++ b/src/cloud_provider/scaleway/router.rs @@ -3,7 +3,7 @@ use tera::Context as TeraContext; use crate::cloud_provider::models::{CustomDomain, CustomDomainDataTemplate, Route, RouteDataTemplate}; use crate::cloud_provider::service::{ default_tera_context, delete_router, delete_stateless_service, send_progress_on_long_task, Action, Create, Delete, - Helm, Pause, Service, ServiceType, StatelessService, + Helm, Pause, Router as RRouter, Service, ServiceType, StatelessService, }; use crate::cloud_provider::utilities::{check_cname_for, sanitize_name}; use crate::cloud_provider::DeploymentTarget; @@ -67,8 +67,8 @@ impl Service for Router { sanitize_name("router", self.name()) } - fn version(&self) -> &str { - "1.0" + fn version(&self) -> String { + "1.0".to_string() } fn action(&self) -> &Action { @@ -104,6 +104,7 @@ impl Service for Router { DeploymentTarget::ManagedServices(k, env) => (*k, *env), DeploymentTarget::SelfHosted(k, env) => (*k, *env), }; + let mut context = default_tera_context(self, kubernetes, environment); let applications = environment @@ -222,7 +223,7 @@ impl StatelessService for Router {} impl Create for Router { fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> { - info!("Scaleway.router.on_create() called for {}", self.name()); + info!("SCW.router.on_create() called for {}", self.name()); let (kubernetes, environment) = match target { DeploymentTarget::ManagedServices(k, env) => (*k, *env), DeploymentTarget::SelfHosted(k, env) => (*k, *env), @@ -266,8 +267,6 @@ impl Create for Router { } fn on_create_check(&self) -> Result<(), EngineError> { - use crate::cloud_provider::service::Router; - // check non custom domains self.check_domains()?; diff --git a/src/cloud_provider/service.rs b/src/cloud_provider/service.rs index b179e7b3..c42841af 100644 --- a/src/cloud_provider/service.rs +++ b/src/cloud_provider/service.rs @@ -43,7 +43,7 @@ pub trait Service { ) .unwrap() } - fn version(&self) -> &str; + fn version(&self) -> String; fn action(&self) -> &Action; fn private_port(&self) -> Option; fn start_timeout(&self) -> Timeout; @@ -232,6 +232,9 @@ pub struct DatabaseOptions { pub port: u16, pub disk_size_in_gib: u32, pub database_disk_type: String, + pub activate_high_availability: bool, + pub activate_backups: bool, + pub publicly_accessible: bool, } #[derive(Eq, PartialEq)] @@ -300,6 +303,7 @@ pub fn default_tera_context( context.insert("organization_id", environment.organization_id.as_str()); context.insert("environment_id", environment.id.as_str()); context.insert("region", kubernetes.region()); + context.insert("zone", kubernetes.zone()); context.insert("name", service.name()); context.insert("sanitized_name", &service.sanitized_name()); context.insert("namespace", environment.namespace()); @@ -313,7 +317,7 @@ pub fn default_tera_context( context.insert("private_port", &service.private_port().unwrap()); } - context.insert("version", service.version()); + context.insert("version", &service.version()); context } diff --git a/src/cloud_provider/utilities.rs b/src/cloud_provider/utilities.rs index 42d84c2e..4cfc9e7a 100644 --- a/src/cloud_provider/utilities.rs +++ b/src/cloud_provider/utilities.rs @@ -12,11 +12,12 @@ use retry::OperationResult; use serde::{Deserialize, Serialize}; use std::fmt; use std::num::ParseFloatError; +use std::str::FromStr; use trust_dns_resolver::config::*; use trust_dns_resolver::proto::rr::{RData, RecordType}; use trust_dns_resolver::Resolver; -pub fn get_self_hosted_postgres_version(requested_version: &str) -> Result { +pub fn get_self_hosted_postgres_version(requested_version: String) -> Result { let mut supported_postgres_versions = HashMap::new(); // https://hub.docker.com/r/bitnami/postgresql/tags?page=1&ordering=last_updated @@ -36,7 +37,7 @@ pub fn get_self_hosted_postgres_version(requested_version: &str) -> Result Result { +pub fn get_self_hosted_mysql_version(requested_version: String) -> Result { let mut supported_mysql_versions = HashMap::new(); // https://hub.docker.com/r/bitnami/mysql/tags?page=1&ordering=last_updated @@ -51,7 +52,7 @@ pub fn get_self_hosted_mysql_version(requested_version: &str) -> Result Result { +pub fn get_self_hosted_mongodb_version(requested_version: String) -> Result { let mut supported_mongodb_versions = HashMap::new(); // https://hub.docker.com/r/bitnami/mongodb/tags?page=1&ordering=last_updated @@ -75,7 +76,7 @@ pub fn get_self_hosted_mongodb_version(requested_version: &str) -> Result Result { +pub fn get_self_hosted_redis_version(requested_version: String) -> Result { let mut supported_redis_versions = HashMap::with_capacity(4); // https://hub.docker.com/r/bitnami/redis/tags?page=1&ordering=last_updated @@ -90,9 +91,9 @@ pub fn get_self_hosted_redis_version(requested_version: &str) -> Result, - version_to_check: &str, + version_to_check: String, ) -> Result { - let version = match get_version_number(version_to_check) { + let version = match VersionsNumber::from_str(version_to_check.as_str()) { Ok(version) => version, Err(e) => return Err(e), }; @@ -225,44 +226,93 @@ pub struct VersionsNumber { pub(crate) major: String, pub(crate) minor: Option, pub(crate) patch: Option, + pub(crate) suffix: Option, } -impl fmt::Display for VersionsNumber { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl VersionsNumber { + pub fn new(major: String, minor: Option, patch: Option, suffix: Option) -> Self { + VersionsNumber { + major, + minor, + patch, + suffix, + } + } + + pub fn to_string(&self) -> String { let mut version = vec![self.major.to_string()]; + if self.minor.is_some() { version.push(self.minor.clone().unwrap()) } if self.patch.is_some() { version.push(self.patch.clone().unwrap()) } - write!(f, "{}", version.join(".")) + if self.suffix.is_some() { + version.push(self.suffix.clone().unwrap()) + } + + version.join(".") + } + + pub fn to_major_version_string(&self) -> String { + self.major.clone() + } + + pub fn to_major_minor_version_string(&self, default_minor: &str) -> String { + let test = format!( + "{}.{}", + self.major.clone(), + self.minor.as_ref().unwrap_or(&default_minor.to_string()) + ); + + test } } -pub fn get_version_number(version: &str) -> Result { - let mut version_split = version.split('.'); +impl FromStr for VersionsNumber { + type Err = StringError; - let major = match version_split.next() { - Some(major) => { - let major = major.to_string(); - major.replace("v", "") + fn from_str(version: &str) -> Result { + if version.trim() == "" { + return Err(StringError::from("version cannot be empty")); } - _ => { - return Err(format!( - "please check the version you've sent ({}), it can't be checked", - version - )) - } - }; - let minor = version_split.next().map(|minor| { - let minor = minor.to_string(); - minor.replace("+", "") - }); - let patch = version_split.next().map(|patch| patch.to_string()); + let mut version_split = version.splitn(4, '.').map(|v| v.trim()); - Ok(VersionsNumber { major, minor, patch }) + let major = match version_split.next() { + Some(major) => { + let major = major.to_string(); + major.replace("v", "") + } + None => { + return Err(format!( + "please check the version you've sent ({}), it can't be checked", + version + )) + } + }; + + let minor = version_split.next().map(|minor| { + let minor = minor.to_string(); + minor.replace("+", "") + }); + + let patch = version_split.next().map(|patch| patch.to_string()); + + let suffix = version_split.next().map(|suffix| suffix.to_string()); + + // TODO(benjaminch): Handle properly the case where versions are empty + // eq. 1..2 + + Ok(VersionsNumber::new(major, minor, patch, suffix)) + } +} + +impl fmt::Display for VersionsNumber { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } } fn cloudflare_dns_resolver() -> Resolver { @@ -505,9 +555,11 @@ mod tests { use crate::cloud_provider::models::CpuLimits; use crate::cloud_provider::utilities::{ cloudflare_dns_resolver, convert_k8s_cpu_value_to_f32, get_cname_record_value, - validate_k8s_required_cpu_and_burstable, + validate_k8s_required_cpu_and_burstable, VersionsNumber, }; + use crate::error::StringError; use crate::models::ListenersHelper; + use std::str::FromStr; #[test] pub fn test_k8s_milli_cpu_convert() { @@ -555,4 +607,85 @@ mod tests { assert_eq!(cname, Some(String::from("qovery.io."))); } + + #[test] + pub fn test_versions_number() { + // setup: + struct TestCase<'a> { + input: &'a str, + expected_output: Result, + description: &'a str, + } + + let test_cases = vec![ + TestCase { + input: "", + expected_output: Err(StringError::from("version cannot be empty")), + description: "empty version str", + }, + TestCase { + input: " ", + expected_output: Err(StringError::from("version cannot be empty")), + description: "version a tab str", + }, + TestCase { + input: " ", + expected_output: Err(StringError::from("version cannot be empty")), + description: "version as a space str", + }, + TestCase { + input: "-", // TODO(benjaminch): better handle this case, should trigger an error + expected_output: Ok(VersionsNumber::new("-".to_string(), None, None, None)), + description: "suffix separator only", + }, + TestCase { + input: "test", + expected_output: Ok(VersionsNumber::new("test".to_string(), None, None, None)), + description: "bad string", + }, + TestCase { + input: "1,2,3,4", // TODO(benjaminch): better handle this case, should trigger an error + expected_output: Ok(VersionsNumber::new("1,2,3,4".to_string(), None, None, None)), + description: "bad versions separator", + }, + TestCase { + input: "1", + expected_output: Ok(VersionsNumber::new("1".to_string(), None, None, None)), + description: "major only", + }, + TestCase { + input: "1.1", + expected_output: Ok(VersionsNumber::new("1".to_string(), Some("1".to_string()), None, None)), + description: "major.minor only", + }, + TestCase { + input: "1.1.1", + expected_output: Ok(VersionsNumber::new( + "1".to_string(), + Some("1".to_string()), + Some("1".to_string()), + None, + )), + description: "major.minor.update only", + }, + TestCase { + input: "1.1.1.suffix", + expected_output: Ok(VersionsNumber::new( + "1".to_string(), + Some("1".to_string()), + Some("1".to_string()), + Some("suffix".to_string()), + )), + description: "major.minor.patch-suffix", + }, + ]; + + for tc in test_cases { + // execute: + let result = VersionsNumber::from_str(tc.input); + + // verify: + assert_eq!(tc.expected_output, result, "case {} : '{}'", tc.description, tc.input); + } + } } diff --git a/src/models.rs b/src/models.rs index a9b56c16..e7268a4a 100644 --- a/src/models.rs +++ b/src/models.rs @@ -12,10 +12,12 @@ use crate::cloud_provider::aws::databases::mysql::MySQL; use crate::cloud_provider::aws::databases::postgresql::PostgreSQL; use crate::cloud_provider::aws::databases::redis::Redis; use crate::cloud_provider::service::{DatabaseOptions, StatefulService, StatelessService}; +use crate::cloud_provider::utilities::VersionsNumber; use crate::cloud_provider::CloudProvider; use crate::cloud_provider::Kind as CPKind; use crate::git::Credentials; use std::collections::BTreeMap; +use std::str::FromStr; use std::sync::Arc; #[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] @@ -546,6 +548,9 @@ pub struct Database { pub disk_size_in_gib: u32, pub database_instance_type: String, pub database_disk_type: String, + pub activate_high_availability: bool, + pub activate_backups: bool, + pub publicly_accessible: bool, } impl Database { @@ -561,6 +566,9 @@ impl Database { port: self.port, disk_size_in_gib: self.disk_size_in_gib, database_disk_type: self.database_disk_type.clone(), + activate_high_availability: self.activate_high_availability, + activate_backups: self.activate_backups, + publicly_accessible: self.publicly_accessible, }; let listeners = cloud_provider.listeners().clone(); @@ -720,44 +728,56 @@ impl Database { } }, CPKind::Scw => match self.kind { - DatabaseKind::Postgresql => { - let db: Box = - Box::new(crate::cloud_provider::scaleway::databases::postgresql::PostgreSQL::new( - context.clone(), - self.id.as_str(), - self.action.to_service_action(), - self.name.as_str(), - self.version.as_str(), - self.fqdn.as_str(), - self.fqdn_id.as_str(), - self.total_cpus.clone(), - self.total_ram_in_mib, - self.database_instance_type.as_str(), - database_options, - listeners, - )); + DatabaseKind::Postgresql => match VersionsNumber::from_str(self.version.as_str()) { + Ok(v) => { + let db: Box = + Box::new(crate::cloud_provider::scaleway::databases::postgresql::PostgreSQL::new( + context.clone(), + self.id.as_str(), + self.action.to_service_action(), + self.name.as_str(), + v, + self.fqdn.as_str(), + self.fqdn_id.as_str(), + self.total_cpus.clone(), + self.total_ram_in_mib, + self.database_instance_type.as_str(), + database_options, + listeners, + )); - Some(db) - } - DatabaseKind::Mysql => { - let db: Box = - Box::new(crate::cloud_provider::scaleway::databases::mysql::MySQL::new( - context.clone(), - self.id.as_str(), - self.action.to_service_action(), - self.name.as_str(), - self.version.as_str(), - self.fqdn.as_str(), - self.fqdn_id.as_str(), - self.total_cpus.clone(), - self.total_ram_in_mib, - self.database_instance_type.as_str(), - database_options, - listeners, - )); + Some(db) + } + Err(e) => { + error!("{}", format!("error while parsing postgres version, error: {}", e)); + None + } + }, + DatabaseKind::Mysql => match VersionsNumber::from_str(self.version.as_str()) { + Ok(v) => { + let db: Box = + Box::new(crate::cloud_provider::scaleway::databases::mysql::MySQL::new( + context.clone(), + self.id.as_str(), + self.action.to_service_action(), + self.name.as_str(), + v, + self.fqdn.as_str(), + self.fqdn_id.as_str(), + self.total_cpus.clone(), + self.total_ram_in_mib, + self.database_instance_type.as_str(), + database_options, + listeners, + )); - Some(db) - } + Some(db) + } + Err(e) => { + error!("{}", format!("error while parsing mysql version, error: {}", e)); + None + } + }, DatabaseKind::Redis => { let db: Box = Box::new(crate::cloud_provider::scaleway::databases::redis::Redis::new( diff --git a/test_utilities/Cargo.toml b/test_utilities/Cargo.toml index 13c3907c..2a76c711 100644 --- a/test_utilities/Cargo.toml +++ b/test_utilities/Cargo.toml @@ -13,6 +13,7 @@ qovery-engine = { path = "../" } chrono = "0.4.11" dirs = "3.0.1" gethostname = "0.2.1" +passwords = "3.1.7" rand = "0.7.3" serde = "1.0" serde_json = "1.0.57" diff --git a/test_utilities/src/common.rs b/test_utilities/src/common.rs index fcec1e21..9a9df5ef 100644 --- a/test_utilities/src/common.rs +++ b/test_utilities/src/common.rs @@ -9,7 +9,7 @@ use qovery_engine::models::{ StorageType, }; -use crate::utilities::generate_id; +use crate::utilities::{generate_id, generate_password}; use base64; use std::collections::BTreeMap; @@ -37,7 +37,7 @@ pub fn environment_3_apps_3_routers_3_databases( let database_port_mongo = 27017; let database_db_name_mongo = "my-mongodb".to_string(); let database_username_mongo = "superuser".to_string(); - let database_password_mongo = generate_id(); + let database_password_mongo = generate_password(false); let database_uri_mongo = format!( "mongodb://{}:{}@{}:{}/{}", database_username_mongo, @@ -53,7 +53,7 @@ pub fn environment_3_apps_3_routers_3_databases( let fqdn = format!("{}.{}", fqdn_id, &test_domain); let database_port = 5432; let database_username = "superuser".to_string(); - let database_password = generate_id(); + let database_password = generate_password(true); let database_name = "postgresql".to_string(); // pSQL 2 management part @@ -240,6 +240,9 @@ pub fn environment_3_apps_3_routers_3_databases( disk_size_in_gib: 10, database_instance_type: database_instance_type.to_string(), database_disk_type: database_disk_type.to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }, Database { kind: DatabaseKind::Postgresql, @@ -257,6 +260,9 @@ pub fn environment_3_apps_3_routers_3_databases( disk_size_in_gib: 10, database_instance_type: database_instance_type.to_string(), database_disk_type: database_disk_type.to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }, Database { kind: DatabaseKind::Mongodb, @@ -274,6 +280,9 @@ pub fn environment_3_apps_3_routers_3_databases( disk_size_in_gib: 10, database_instance_type: database_instance_type.to_string(), database_disk_type: database_disk_type.to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }, ], clone_from_environment_id: None, @@ -343,7 +352,7 @@ pub fn environnement_2_app_2_routers_1_psql( let database_port = 5432; let database_username = "superuser".to_string(); - let database_password = generate_id(); + let database_password = generate_password(true); let database_name = "postgresql".to_string(); let suffix = generate_id(); @@ -374,6 +383,9 @@ pub fn environnement_2_app_2_routers_1_psql( disk_size_in_gib: 10, database_instance_type: database_instance_type.to_string(), database_disk_type: database_disk_type.to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }], applications: vec![ Application { diff --git a/test_utilities/src/digitalocean.rs b/test_utilities/src/digitalocean.rs index c53eb630..86418dca 100644 --- a/test_utilities/src/digitalocean.rs +++ b/test_utilities/src/digitalocean.rs @@ -22,8 +22,10 @@ pub const DOCR_ID: &str = "gu9ep7t68htdu78l"; pub const DO_KUBE_TEST_CLUSTER_ID: &str = "z2a1b27a3"; pub const DO_KUBE_TEST_CLUSTER_NAME: &str = "qovery-z2a1b27a3"; pub const DO_TEST_REGION: Region = Region::NewYorkCity3; -pub const DO_DATABASE_DISK_TYPE: &str = "not-used"; -pub const DO_DATABASE_INSTANCE_TYPE: &str = "not-used"; +pub const DO_MANAGED_DATABASE_INSTANCE_TYPE: &str = "not-used"; +pub const DO_MANAGED_DATABASE_DISK_TYPE: &str = "not-used"; +pub const DO_SELF_HOSTED_DATABASE_INSTANCE_TYPE: &str = "not-used"; +pub const DO_SELF_HOSTED_DATABASE_DISK_TYPE: &str = "do-sbv-ssd-0"; pub fn container_registry_digital_ocean(context: &Context) -> DOCR { let secrets = FuncTestsSecrets::new(); diff --git a/test_utilities/src/scaleway.rs b/test_utilities/src/scaleway.rs index 8bef377e..1ea7a90e 100644 --- a/test_utilities/src/scaleway.rs +++ b/test_utilities/src/scaleway.rs @@ -22,8 +22,10 @@ pub const SCW_KUBE_TEST_CLUSTER_NAME: &str = "qovery-zb3a2b3b8"; pub const SCW_KUBE_TEST_CLUSTER_ID: &str = "zb3a2b3b8"; pub const SCW_TEST_ZONE: Zone = Zone::Paris2; pub const SCW_KUBERNETES_VERSION: &str = "1.18"; -pub const SCW_DATABASE_INSTANCE_TYPE: &str = "not-used"; -pub const SCW_DATABASE_DISK_TYPE: &str = "scw-sbv-ssd-0"; +pub const SCW_MANAGED_DATABASE_INSTANCE_TYPE: &str = "db-dev-s"; +pub const SCW_MANAGED_DATABASE_DISK_TYPE: &str = "bssd"; +pub const SCW_SELF_HOSTED_DATABASE_INSTANCE_TYPE: &str = ""; +pub const SCW_SELF_HOSTED_DATABASE_DISK_TYPE: &str = "scw-sbv-ssd-0"; pub fn container_registry_scw(context: &Context) -> ScalewayCR { let secrets = FuncTestsSecrets::new(); @@ -68,6 +70,10 @@ pub fn cloud_provider_scaleway(context: &Context) -> Scaleway { .SCALEWAY_SECRET_KEY .expect("SCALEWAY_SECRET_KEY is not set in secrets") .as_str(), + secrets + .SCALEWAY_DEFAULT_PROJECT_ID + .expect("SCALEWAY_DEFAULT_PROJECT_ID is not set in secrets") + .as_str(), TerraformStateCredentials { access_key_id: secrets .TERRAFORM_AWS_ACCESS_KEY_ID diff --git a/test_utilities/src/utilities.rs b/test_utilities/src/utilities.rs index a8ae79c3..0fb3f34d 100644 --- a/test_utilities/src/utilities.rs +++ b/test_utilities/src/utilities.rs @@ -1,5 +1,6 @@ extern crate base64; extern crate bstr; +extern crate passwords; extern crate scaleway_api_rs; use bstr::ByteSlice; @@ -11,6 +12,7 @@ use std::io::{Error, ErrorKind, Write}; use std::path::Path; use std::str::FromStr; +use passwords::PasswordGenerator; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use retry::delay::Fibonacci; @@ -355,6 +357,23 @@ pub fn generate_id() -> String { uuid } +pub fn generate_password(allow_using_symbols: bool) -> String { + let pg = PasswordGenerator::new() + .length(32) + .numbers(true) + .lowercase_letters(true) + .uppercase_letters(true) + .symbols(allow_using_symbols) + .spaces(false) + .exclude_similar_characters(true) + .strict(true); + + pg.generate_one() + .expect("error while trying to generate a password") + .to_string() + .replace("\\", "$") // most tools do not allow \ char, hence replacing it by something else +} + pub fn check_all_connections(env: &Environment) -> Vec { let mut checking: Vec = Vec::with_capacity(env.routers.len()); @@ -549,7 +568,7 @@ where type KubernetesCredentials<'a> = Vec<(&'a str, &'a str)>; -fn get_cloud_provider_credentials<'a>(provider_kind: Kind, secrets: &'a FuncTestsSecrets) -> KubernetesCredentials<'a> { +fn get_cloud_provider_credentials(provider_kind: Kind, secrets: &FuncTestsSecrets) -> KubernetesCredentials { match provider_kind { Kind::Aws => vec![ (AWS_ACCESS_KEY_ID, secrets.AWS_ACCESS_KEY_ID.as_ref().unwrap().as_str()), diff --git a/tests/aws/aws_databases.rs b/tests/aws/aws_databases.rs index 965456ca..3c50902a 100644 --- a/tests/aws/aws_databases.rs +++ b/tests/aws/aws_databases.rs @@ -351,6 +351,9 @@ fn postgresql_deploy_a_working_environment_and_redeploy() { disk_size_in_gib: 10, database_instance_type: "db.t2.micro".to_string(), database_disk_type: "gp2".to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }]; environment.applications = environment .applications @@ -461,6 +464,9 @@ fn test_postgresql_configuration( disk_size_in_gib: 10, database_instance_type: "db.t2.micro".to_string(), database_disk_type: "gp2".to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }]; environment.applications = environment .applications @@ -670,6 +676,9 @@ fn test_mongodb_configuration( disk_size_in_gib: 10, database_instance_type: "db.t3.medium".to_string(), database_disk_type: "gp2".to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }]; environment.applications = environment .applications @@ -878,6 +887,9 @@ fn test_mysql_configuration( disk_size_in_gib: 10, database_instance_type: "db.t2.micro".to_string(), database_disk_type: "gp2".to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }]; environment.applications = environment .applications @@ -1047,6 +1059,9 @@ fn test_redis_configuration( disk_size_in_gib: 10, database_instance_type: "cache.t3.micro".to_string(), database_disk_type: "gp2".to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }]; environment.applications = environment .applications diff --git a/tests/digitalocean/do_databases.rs b/tests/digitalocean/do_databases.rs index 6d41ca54..bee2bfc8 100644 --- a/tests/digitalocean/do_databases.rs +++ b/tests/digitalocean/do_databases.rs @@ -12,8 +12,9 @@ use test_utilities::utilities::{ use test_utilities::common::working_minimal_environment; use test_utilities::digitalocean::{ - clean_environments, delete_environment, deploy_environment, pause_environment, DO_DATABASE_DISK_TYPE, - DO_DATABASE_INSTANCE_TYPE, DO_KUBE_TEST_CLUSTER_ID, DO_QOVERY_ORGANIZATION_ID, DO_TEST_REGION, + clean_environments, delete_environment, deploy_environment, pause_environment, DO_KUBE_TEST_CLUSTER_ID, + DO_MANAGED_DATABASE_DISK_TYPE, DO_MANAGED_DATABASE_INSTANCE_TYPE, DO_QOVERY_ORGANIZATION_ID, + DO_SELF_HOSTED_DATABASE_DISK_TYPE, DO_SELF_HOSTED_DATABASE_INSTANCE_TYPE, DO_TEST_REGION, }; /** @@ -46,8 +47,8 @@ fn deploy_an_environment_with_3_databases_and_3_apps() { .DEFAULT_TEST_DOMAIN .expect("DEFAULT_TEST_DOMAIN is not set in secrets") .as_str(), - DO_DATABASE_INSTANCE_TYPE, - DO_DATABASE_DISK_TYPE, + DO_SELF_HOSTED_DATABASE_INSTANCE_TYPE, + DO_SELF_HOSTED_DATABASE_DISK_TYPE, ); let mut environment_delete = environment.clone(); @@ -99,8 +100,8 @@ fn deploy_an_environment_with_db_and_pause_it() { .DEFAULT_TEST_DOMAIN .expect("DEFAULT_TEST_DOMAIN is not set in secrets") .as_str(), - DO_DATABASE_INSTANCE_TYPE, - DO_DATABASE_DISK_TYPE, + DO_SELF_HOSTED_DATABASE_INSTANCE_TYPE, + DO_SELF_HOSTED_DATABASE_DISK_TYPE, ); let mut environment_delete = environment.clone(); @@ -172,8 +173,8 @@ fn postgresql_failover_dev_environment_with_all_options() { &context, DO_QOVERY_ORGANIZATION_ID, test_domain.as_str(), - DO_DATABASE_INSTANCE_TYPE, - DO_DATABASE_DISK_TYPE, + DO_SELF_HOSTED_DATABASE_INSTANCE_TYPE, + DO_SELF_HOSTED_DATABASE_DISK_TYPE, ); let environment_check = environment.clone(); let mut environment_never_up = environment.clone(); @@ -190,8 +191,8 @@ fn postgresql_failover_dev_environment_with_all_options() { &context_for_deletion, DO_QOVERY_ORGANIZATION_ID, test_domain.as_str(), - DO_DATABASE_INSTANCE_TYPE, - DO_DATABASE_DISK_TYPE, + DO_SELF_HOSTED_DATABASE_INSTANCE_TYPE, + DO_SELF_HOSTED_DATABASE_DISK_TYPE, ); environment.kind = Kind::Development; @@ -276,16 +277,16 @@ fn postgresql_deploy_a_working_development_environment_with_all_options() { &context, DO_QOVERY_ORGANIZATION_ID, test_domain.as_str(), - DO_DATABASE_INSTANCE_TYPE, - DO_DATABASE_DISK_TYPE, + DO_SELF_HOSTED_DATABASE_INSTANCE_TYPE, + DO_SELF_HOSTED_DATABASE_DISK_TYPE, ); //let env_to_check = environment.clone(); let mut environment_delete = test_utilities::common::environnement_2_app_2_routers_1_psql( &context_for_deletion, DO_QOVERY_ORGANIZATION_ID, test_domain.as_str(), - DO_DATABASE_INSTANCE_TYPE, - DO_DATABASE_DISK_TYPE, + DO_SELF_HOSTED_DATABASE_INSTANCE_TYPE, + DO_SELF_HOSTED_DATABASE_DISK_TYPE, ); environment.kind = Kind::Development; @@ -355,6 +356,11 @@ fn postgresql_deploy_a_working_environment_and_redeploy() { .as_str(), ); + let is_managed_db = match environment.kind { + Kind::Production => true, + Kind::Development => false, + }; + let app_name = format!("postgresql-app-{}", generate_id()); let database_host = format!( "postgresql-{}.{}", @@ -382,8 +388,21 @@ fn postgresql_deploy_a_working_environment_and_redeploy() { total_cpus: "500m".to_string(), total_ram_in_mib: 512, disk_size_in_gib: 10, - database_instance_type: DO_DATABASE_INSTANCE_TYPE.to_string(), - database_disk_type: DO_DATABASE_DISK_TYPE.to_string(), + database_instance_type: if is_managed_db { + DO_MANAGED_DATABASE_INSTANCE_TYPE + } else { + DO_SELF_HOSTED_DATABASE_INSTANCE_TYPE + } + .to_string(), + database_disk_type: if is_managed_db { + DO_MANAGED_DATABASE_DISK_TYPE + } else { + DO_SELF_HOSTED_DATABASE_DISK_TYPE + } + .to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }]; environment.applications = environment .applications @@ -482,7 +501,7 @@ fn test_postgresql_configuration( let database_username = "superuser".to_string(); let database_password = generate_id(); - let _is_rds = match environment.kind { + let is_managed_db = match environment.kind { Kind::Production => true, Kind::Development => false, }; @@ -501,8 +520,21 @@ fn test_postgresql_configuration( total_cpus: "100m".to_string(), total_ram_in_mib: 512, disk_size_in_gib: 10, - database_instance_type: "not-used".to_string(), - database_disk_type: "do-sbv-ssd-0".to_string(), + database_instance_type: if is_managed_db { + DO_MANAGED_DATABASE_INSTANCE_TYPE + } else { + DO_SELF_HOSTED_DATABASE_INSTANCE_TYPE + } + .to_string(), + database_disk_type: if is_managed_db { + DO_MANAGED_DATABASE_DISK_TYPE + } else { + DO_SELF_HOSTED_DATABASE_DISK_TYPE + } + .to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }]; environment.applications = environment .applications @@ -630,6 +662,11 @@ fn test_mongodb_configuration( let _enter = span.enter(); let context_for_delete = context.clone_not_same_execution_id(); + let is_managed_db = match environment.kind { + Kind::Production => true, + Kind::Development => false, + }; + let app_name = format!("mongodb-app-{}", generate_id()); let database_host = format!( "mongodb-{}.{}", @@ -659,8 +696,21 @@ fn test_mongodb_configuration( total_cpus: "500m".to_string(), total_ram_in_mib: 512, disk_size_in_gib: 10, - database_instance_type: "not-used".to_string(), - database_disk_type: "do-sbv-ssd-0".to_string(), + database_instance_type: if is_managed_db { + DO_MANAGED_DATABASE_INSTANCE_TYPE + } else { + DO_SELF_HOSTED_DATABASE_INSTANCE_TYPE + } + .to_string(), + database_disk_type: if is_managed_db { + DO_MANAGED_DATABASE_DISK_TYPE + } else { + DO_SELF_HOSTED_DATABASE_DISK_TYPE + } + .to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }]; environment.applications = environment @@ -823,7 +873,7 @@ fn test_mysql_configuration( let database_username = "superuser".to_string(); let database_password = generate_id(); - let _is_rds = match environment.kind { + let is_managed_db = match environment.kind { Kind::Production => true, Kind::Development => false, }; @@ -842,8 +892,21 @@ fn test_mysql_configuration( total_cpus: "500m".to_string(), total_ram_in_mib: 512, disk_size_in_gib: 10, - database_instance_type: "not-used".to_string(), - database_disk_type: "do-sbv-ssd-0".to_string(), + database_instance_type: if is_managed_db { + DO_MANAGED_DATABASE_INSTANCE_TYPE + } else { + DO_SELF_HOSTED_DATABASE_INSTANCE_TYPE + } + .to_string(), + database_disk_type: if is_managed_db { + DO_MANAGED_DATABASE_DISK_TYPE + } else { + DO_SELF_HOSTED_DATABASE_DISK_TYPE + } + .to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }]; environment.applications = environment .applications @@ -966,7 +1029,7 @@ fn test_redis_configuration( let database_username = "superuser".to_string(); let database_password = generate_id(); - let is_elasticache = match environment.kind { + let is_managed_db = match environment.kind { Kind::Production => true, Kind::Development => false, }; @@ -985,8 +1048,21 @@ fn test_redis_configuration( total_cpus: "500m".to_string(), total_ram_in_mib: 512, disk_size_in_gib: 10, - database_instance_type: "not-used".to_string(), - database_disk_type: "do-sbv-ssd-0".to_string(), + database_instance_type: if is_managed_db { + DO_MANAGED_DATABASE_INSTANCE_TYPE + } else { + DO_SELF_HOSTED_DATABASE_INSTANCE_TYPE + } + .to_string(), + database_disk_type: if is_managed_db { + DO_MANAGED_DATABASE_DISK_TYPE + } else { + DO_SELF_HOSTED_DATABASE_DISK_TYPE + } + .to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }]; environment.applications = environment .applications @@ -998,7 +1074,6 @@ fn test_redis_configuration( app.private_port = Some(1234); app.dockerfile_path = Some(format!("Dockerfile-{}", version)); app.environment_vars = btreemap! { - "IS_ELASTICCACHE".to_string() => base64::encode(is_elasticache.to_string()), "REDIS_HOST".to_string() => base64::encode(database_host.clone()), "REDIS_PORT".to_string() => base64::encode(database_port.clone().to_string()), "REDIS_USERNAME".to_string() => base64::encode(database_username.clone()), diff --git a/tests/scaleway/scw_databases.rs b/tests/scaleway/scw_databases.rs index 812de1b6..cd622cfa 100644 --- a/tests/scaleway/scw_databases.rs +++ b/tests/scaleway/scw_databases.rs @@ -7,13 +7,14 @@ use qovery_engine::models::{ }; use qovery_engine::transaction::TransactionResult; use test_utilities::utilities::{ - context, engine_run_test, generate_id, get_pods, init, is_pod_restarted_env, FuncTestsSecrets, + context, engine_run_test, generate_id, generate_password, get_pods, init, is_pod_restarted_env, FuncTestsSecrets, }; use test_utilities::common::working_minimal_environment; use test_utilities::scaleway::{ - clean_environments, delete_environment, deploy_environment, pause_environment, SCW_DATABASE_DISK_TYPE, - SCW_DATABASE_INSTANCE_TYPE, SCW_KUBE_TEST_CLUSTER_ID, SCW_QOVERY_ORGANIZATION_ID, SCW_TEST_ZONE, + clean_environments, delete_environment, deploy_environment, pause_environment, SCW_KUBE_TEST_CLUSTER_ID, + SCW_MANAGED_DATABASE_DISK_TYPE, SCW_MANAGED_DATABASE_INSTANCE_TYPE, SCW_QOVERY_ORGANIZATION_ID, + SCW_SELF_HOSTED_DATABASE_DISK_TYPE, SCW_SELF_HOSTED_DATABASE_INSTANCE_TYPE, SCW_TEST_ZONE, }; /** @@ -45,8 +46,8 @@ fn deploy_an_environment_with_3_databases_and_3_apps() { .DEFAULT_TEST_DOMAIN .expect("DEFAULT_TEST_DOMAIN is not set in secrets") .as_str(), - SCW_DATABASE_INSTANCE_TYPE, - SCW_DATABASE_DISK_TYPE, + SCW_SELF_HOSTED_DATABASE_INSTANCE_TYPE, + SCW_SELF_HOSTED_DATABASE_DISK_TYPE, ); let mut environment_delete = environment.clone(); @@ -97,8 +98,8 @@ fn deploy_an_environment_with_db_and_pause_it() { .DEFAULT_TEST_DOMAIN .expect("DEFAULT_TEST_DOMAIN is not set in secrets") .as_str(), - SCW_DATABASE_INSTANCE_TYPE, - SCW_DATABASE_DISK_TYPE, + SCW_SELF_HOSTED_DATABASE_INSTANCE_TYPE, + SCW_SELF_HOSTED_DATABASE_DISK_TYPE, ); let mut environment_delete = environment.clone(); @@ -169,8 +170,8 @@ fn postgresql_failover_dev_environment_with_all_options() { &context, SCW_QOVERY_ORGANIZATION_ID, test_domain.as_str(), - SCW_DATABASE_INSTANCE_TYPE, - SCW_DATABASE_DISK_TYPE, + SCW_SELF_HOSTED_DATABASE_INSTANCE_TYPE, + SCW_SELF_HOSTED_DATABASE_DISK_TYPE, ); let environment_check = environment.clone(); let mut environment_never_up = environment.clone(); @@ -187,8 +188,8 @@ fn postgresql_failover_dev_environment_with_all_options() { &context_for_deletion, SCW_QOVERY_ORGANIZATION_ID, test_domain.as_str(), - SCW_DATABASE_INSTANCE_TYPE, - SCW_DATABASE_DISK_TYPE, + SCW_SELF_HOSTED_DATABASE_INSTANCE_TYPE, + SCW_SELF_HOSTED_DATABASE_DISK_TYPE, ); environment.kind = Kind::Development; @@ -272,16 +273,15 @@ fn postgresql_deploy_a_working_development_environment_with_all_options() { &context, SCW_QOVERY_ORGANIZATION_ID, test_domain.as_str(), - SCW_DATABASE_INSTANCE_TYPE, - SCW_DATABASE_DISK_TYPE, + SCW_SELF_HOSTED_DATABASE_INSTANCE_TYPE, + SCW_SELF_HOSTED_DATABASE_DISK_TYPE, ); - //let env_to_check = environment.clone(); let mut environment_delete = test_utilities::common::environnement_2_app_2_routers_1_psql( &context_for_deletion, SCW_QOVERY_ORGANIZATION_ID, test_domain.as_str(), - SCW_DATABASE_INSTANCE_TYPE, - SCW_DATABASE_DISK_TYPE, + SCW_SELF_HOSTED_DATABASE_INSTANCE_TYPE, + SCW_SELF_HOSTED_DATABASE_DISK_TYPE, ); environment.kind = Kind::Development; @@ -296,12 +296,6 @@ fn postgresql_deploy_a_working_development_environment_with_all_options() { TransactionResult::Rollback(_) => assert!(false), TransactionResult::UnrecoverableError(_, _) => assert!(false), }; - // TODO: should be uncommented as soon as cert-manager is fixed - // for the moment this assert report a SSL issue on the second router, so it's works well - /* let connections = test_utilities::utilities::check_all_connections(&env_to_check); - for con in connections { - assert_eq!(con, true); - }*/ match delete_environment(&context_for_deletion, env_action_for_deletion, SCW_TEST_ZONE) { TransactionResult::Ok => assert!(true), @@ -362,7 +356,13 @@ fn postgresql_deploy_a_working_environment_and_redeploy() { let database_port = 5432; let database_db_name = "postgresql".to_string(); let database_username = "superuser".to_string(); - let database_password = generate_id(); + let database_password = generate_password(true); + + let is_managed_db = match environment.kind { + Kind::Development => false, + Kind::Production => true, + }; + environment.databases = vec![Database { kind: DatabaseKind::Postgresql, action: Action::Create, @@ -377,8 +377,21 @@ fn postgresql_deploy_a_working_environment_and_redeploy() { total_cpus: "500m".to_string(), total_ram_in_mib: 512, disk_size_in_gib: 10, - database_instance_type: SCW_DATABASE_INSTANCE_TYPE.to_string(), - database_disk_type: SCW_DATABASE_DISK_TYPE.to_string(), + database_instance_type: if is_managed_db { + SCW_MANAGED_DATABASE_INSTANCE_TYPE + } else { + SCW_SELF_HOSTED_DATABASE_INSTANCE_TYPE + } + .to_string(), + database_disk_type: if is_managed_db { + SCW_MANAGED_DATABASE_DISK_TYPE + } else { + SCW_SELF_HOSTED_DATABASE_DISK_TYPE + } + .to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }]; environment.applications = environment .applications @@ -475,9 +488,9 @@ fn test_postgresql_configuration( let database_port = 5432; let database_db_name = "postgres".to_string(); let database_username = "superuser".to_string(); - let database_password = generate_id(); + let database_password = generate_password(true); - let _is_rds = match environment.kind { + let is_managed_db = match environment.kind { Kind::Production => true, Kind::Development => false, }; @@ -493,11 +506,24 @@ fn test_postgresql_configuration( port: database_port.clone(), username: database_username.clone(), password: database_password.clone(), - total_cpus: "100m".to_string(), + total_cpus: "500m".to_string(), total_ram_in_mib: 512, disk_size_in_gib: 10, - database_instance_type: "not-used".to_string(), - database_disk_type: "scw-sbv-ssd-0".to_string(), + database_instance_type: if is_managed_db { + SCW_MANAGED_DATABASE_INSTANCE_TYPE + } else { + SCW_SELF_HOSTED_DATABASE_INSTANCE_TYPE + } + .to_string(), + database_disk_type: if is_managed_db { + SCW_MANAGED_DATABASE_DISK_TYPE + } else { + SCW_SELF_HOSTED_DATABASE_DISK_TYPE + } + .to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }]; environment.applications = environment .applications @@ -547,7 +573,7 @@ fn test_postgresql_configuration( }) } -// Postgres environment environment +// Postgres self hosted environment #[cfg(feature = "test-scw-self-hosted")] #[named] #[test] @@ -603,6 +629,62 @@ fn postgresql_v12_deploy_a_working_dev_environment() { } // Postgres production environment +#[cfg(feature = "test-scw-managed-services")] +#[named] +#[test] +fn postgresql_v10_deploy_a_working_prod_environment() { + let context = context(); + let secrets = FuncTestsSecrets::new(); + let mut environment = working_minimal_environment( + &context, + SCW_QOVERY_ORGANIZATION_ID, + secrets + .DEFAULT_TEST_DOMAIN + .as_ref() + .expect("DEFAULT_TEST_DOMAIN is not set in secrets") + .as_str(), + ); + environment.kind = Kind::Production; + test_postgresql_configuration(context, environment, secrets, "10", function_name!()); +} + +#[cfg(feature = "test-scw-managed-services")] +#[named] +#[test] +fn postgresql_v11_deploy_a_working_prod_environment() { + let context = context(); + let secrets = FuncTestsSecrets::new(); + let mut environment = working_minimal_environment( + &context, + SCW_QOVERY_ORGANIZATION_ID, + secrets + .DEFAULT_TEST_DOMAIN + .as_ref() + .expect("DEFAULT_TEST_DOMAIN is not set in secrets") + .as_str(), + ); + environment.kind = Kind::Production; + test_postgresql_configuration(context, environment, secrets, "11", function_name!()); +} + +#[cfg(feature = "test-scw-managed-services")] +#[named] +#[test] +fn postgresql_v12_deploy_a_working_prod_environment() { + let context = context(); + let secrets = FuncTestsSecrets::new(); + let mut environment = working_minimal_environment( + &context, + SCW_QOVERY_ORGANIZATION_ID, + secrets + .DEFAULT_TEST_DOMAIN + .as_ref() + .expect("DEFAULT_TEST_DOMAIN is not set in secrets") + .as_str(), + ); + environment.kind = Kind::Production; + test_postgresql_configuration(context, environment, secrets, "12", function_name!()); +} /** ** @@ -633,12 +715,17 @@ fn test_mongodb_configuration( let database_port = 27017; let database_db_name = "my-mongodb".to_string(); let database_username = "superuser".to_string(); - let database_password = generate_id(); + let database_password = generate_password(false); let database_uri = format!( "mongodb://{}:{}@{}:{}/{}", database_username, database_password, database_host, database_port, database_db_name ); + let is_managed_db = match environment.kind { + Kind::Development => false, + Kind::Production => true, + }; + environment.databases = vec![Database { kind: DatabaseKind::Mongodb, action: Action::Create, @@ -653,8 +740,21 @@ fn test_mongodb_configuration( total_cpus: "500m".to_string(), total_ram_in_mib: 512, disk_size_in_gib: 10, - database_instance_type: "not-used".to_string(), - database_disk_type: "scw-sbv-ssd-0".to_string(), + database_instance_type: if is_managed_db { + SCW_MANAGED_DATABASE_INSTANCE_TYPE + } else { + SCW_SELF_HOSTED_DATABASE_INSTANCE_TYPE + } + .to_string(), + database_disk_type: if is_managed_db { + SCW_MANAGED_DATABASE_DISK_TYPE + } else { + SCW_SELF_HOSTED_DATABASE_DISK_TYPE + } + .to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }]; environment.applications = environment @@ -811,9 +911,9 @@ fn test_mysql_configuration( let database_port = 3306; let database_db_name = "mysqldatabase".to_string(); let database_username = "superuser".to_string(); - let database_password = generate_id(); + let database_password = generate_password(true); - let _is_rds = match environment.kind { + let is_managed_db = match environment.kind { Kind::Production => true, Kind::Development => false, }; @@ -832,8 +932,21 @@ fn test_mysql_configuration( total_cpus: "500m".to_string(), total_ram_in_mib: 512, disk_size_in_gib: 10, - database_instance_type: "not-used".to_string(), - database_disk_type: "scw-sbv-ssd-0".to_string(), + database_instance_type: if is_managed_db { + SCW_MANAGED_DATABASE_INSTANCE_TYPE + } else { + SCW_SELF_HOSTED_DATABASE_INSTANCE_TYPE + } + .to_string(), + database_disk_type: if is_managed_db { + SCW_MANAGED_DATABASE_DISK_TYPE + } else { + SCW_SELF_HOSTED_DATABASE_DISK_TYPE + } + .to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }]; environment.applications = environment .applications @@ -920,7 +1033,25 @@ fn mysql_v8_deploy_a_working_dev_environment() { test_mysql_configuration(context, environment, secrets, "8.0", function_name!()); } -// MySQL production environment +// MySQL production environment (RDS) +#[cfg(feature = "test-scw-managed-services")] +#[named] +#[test] +fn mysql_v8_deploy_a_working_prod_environment() { + let context = context(); + let secrets = FuncTestsSecrets::new(); + let mut environment = test_utilities::common::working_minimal_environment( + &context, + SCW_QOVERY_ORGANIZATION_ID, + secrets + .DEFAULT_TEST_DOMAIN + .as_ref() + .expect("DEFAULT_TEST_DOMAIN is not set in secrets") + .as_str(), + ); + environment.kind = Kind::Production; + test_mysql_configuration(context, environment, secrets, "8.0", function_name!()); +} /** ** @@ -952,9 +1083,9 @@ fn test_redis_configuration( let database_port = 6379; let database_db_name = "my-redis".to_string(); let database_username = "superuser".to_string(); - let database_password = generate_id(); + let database_password = generate_password(true); - let is_elasticache = match environment.kind { + let is_managed_db = match environment.kind { Kind::Production => true, Kind::Development => false, }; @@ -973,8 +1104,21 @@ fn test_redis_configuration( total_cpus: "500m".to_string(), total_ram_in_mib: 512, disk_size_in_gib: 10, - database_instance_type: "not-used".to_string(), - database_disk_type: "scw-sbv-ssd-0".to_string(), + database_instance_type: if is_managed_db { + SCW_MANAGED_DATABASE_INSTANCE_TYPE + } else { + SCW_SELF_HOSTED_DATABASE_INSTANCE_TYPE + } + .to_string(), + database_disk_type: if is_managed_db { + SCW_MANAGED_DATABASE_DISK_TYPE + } else { + SCW_SELF_HOSTED_DATABASE_DISK_TYPE + } + .to_string(), + activate_high_availability: false, + activate_backups: false, + publicly_accessible: false, }]; environment.applications = environment .applications @@ -986,7 +1130,7 @@ fn test_redis_configuration( app.private_port = Some(1234); app.dockerfile_path = Some(format!("Dockerfile-{}", version)); app.environment_vars = btreemap! { - "IS_ELASTICCACHE".to_string() => base64::encode(is_elasticache.to_string()), + "IS_ELASTICCACHE".to_string() => base64::encode(is_managed_db.to_string()), "REDIS_HOST".to_string() => base64::encode(database_host.clone()), "REDIS_PORT".to_string() => base64::encode(database_port.clone().to_string()), "REDIS_USERNAME".to_string() => base64::encode(database_username.clone()), @@ -999,10 +1143,10 @@ fn test_redis_configuration( let mut environment_delete = environment.clone(); environment_delete.action = Action::Delete; - let ea = EnvironmentAction::Environment(environment.clone()); - let ea_delete = EnvironmentAction::Environment(environment_delete); + let env_action = EnvironmentAction::Environment(environment.clone()); + let env_action_delete = EnvironmentAction::Environment(environment_delete); - match deploy_environment(&context, ea, SCW_TEST_ZONE) { + match deploy_environment(&context, env_action, SCW_TEST_ZONE) { TransactionResult::Ok => assert!(true), TransactionResult::Rollback(_) => assert!(false), TransactionResult::UnrecoverableError(_, _) => assert!(false), @@ -1010,7 +1154,7 @@ fn test_redis_configuration( // todo: check the database disk is here and with correct size - match delete_environment(&context_for_delete, ea_delete, SCW_TEST_ZONE) { + match delete_environment(&context_for_delete, env_action_delete, SCW_TEST_ZONE) { TransactionResult::Ok => assert!(true), TransactionResult::Rollback(_) => assert!(false), TransactionResult::UnrecoverableError(_, _) => assert!(true),