From 283e05d60c29041058f29e0271e6a7d7b7fab548 Mon Sep 17 00:00:00 2001 From: Zakir Dzhamaliddinov Date: Tue, 29 Oct 2024 13:41:59 +0300 Subject: [PATCH 1/7] Add documentation for terraform feature --- docs/terraform.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 docs/terraform.md diff --git a/docs/terraform.md b/docs/terraform.md new file mode 100644 index 00000000..ebc5fb83 --- /dev/null +++ b/docs/terraform.md @@ -0,0 +1,11 @@ +# Terraform + +You can manage your CPLN configuration through `cpflow` commands and later invoke the generation of Terraform configuration files by running: + +```sh +cpflow terraform generate +``` + +This command will create Terraform configurations for each application defined in `controlplane.yml`, utilizing templates from the `templates` folder. + +Each time this command is invoked, Terraform configurations will be recreated. You can continue working with CPLN configuration files in YAML format and simply transform them to Terraform format anytime. From 9e74ba2ffe32f7d04735edf64734fc02e2cf6d49 Mon Sep 17 00:00:00 2001 From: Zakir Dzhamaliddinov Date: Wed, 30 Oct 2024 20:10:54 +0300 Subject: [PATCH 2/7] Add examples --- docs/terraform.md | 348 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 347 insertions(+), 1 deletion(-) diff --git a/docs/terraform.md b/docs/terraform.md index ebc5fb83..9e5f4e4e 100644 --- a/docs/terraform.md +++ b/docs/terraform.md @@ -1,5 +1,7 @@ # Terraform +### Overview + You can manage your CPLN configuration through `cpflow` commands and later invoke the generation of Terraform configuration files by running: ```sh @@ -8,4 +10,348 @@ cpflow terraform generate This command will create Terraform configurations for each application defined in `controlplane.yml`, utilizing templates from the `templates` folder. -Each time this command is invoked, Terraform configurations will be recreated. You can continue working with CPLN configuration files in YAML format and simply transform them to Terraform format anytime. +Each time this command is invoked, Terraform configurations will be recreated. You can continue working with CPLN configuration files in YAML format and simply transform them to Terraform format at any time. + +### Project Structure + +Given the project structure below: + +``` +.controlplane/ +├── templates/ +│ ├── app.yml -- GVC config +│ ├── postgres.yml -- workload config +│ └── rails.yml -- workload config +├── controlplane.yml -- configs for overall application +├── Dockerfile +└── entrypoint.sh +``` + +Invoking `cpflow terraform generate` will generate a new `terraform` folder with subfolders containing Terraform configurations for each application described in `controlplane.yml`: + +``` +.controlplane/ +├── templates/ +│ ├── app.yml -- GVC config +│ ├── postgres.yml -- workload config +│ └── rails.yml -- workload config +├── terraform/ +│ ├── staging/ -- Terraform configurations for staging environment +│ │ ├── gvc.tf -- GVC config in HCL +│ │ ├── postgres.tf -- Postgres workload config in HCL +│ │ ├── postgres_envs.tf -- ENV variables for Postgres workload in HCL +│ │ ├── rails.tf -- Rails workload config in HCL +│ │ ├── rails_envs.tf -- ENV variables for Rails workload in HCL +│ │ ├── providers.tf -- Providers config in HCL +│ │ └── required_providers.tf -- Required providers config in HCL +│ ├── production/ -- Terraform configurations for production environment +│ │ ├── gvc.tf -- GVC config in HCL +│ │ ├── postgres.tf -- Postgres workload config in HCL +│ │ ├── postgres_envs.tf -- ENV variables for Postgres workload in HCL +│ │ ├── rails.tf -- Rails workload config in HCL +│ │ ├── rails_envs.tf -- ENV variables for Rails workload in HCL +│ │ ├── providers.tf -- Providers config in HCL +│ │ └── required_providers.tf -- Required providers config in HCL +├── controlplane.yml -- configs for overall application +├── Dockerfile +└── entrypoint.sh +``` + +### Terraform Configurations from CPLN Templates + +#### GVC + +CPLN template in YAML format: + +```yaml +kind: gvc +name: app-name +description: app-description +tags: + tag-name-1: "tag-value-1" + tag-name-2: "tag-value-2" +spec: + domain: "app.example.com" + env: + - name: DATABASE_URL + value: "postgres://the_user:the_password@postgres.app-name.cpln.local:5432/app-name" + - name: RAILS_ENV + value: production + - name: RAILS_SERVE_STATIC_FILES + value: "true" + staticPlacement: + locationLinks: + - "//location/aws-us-west-2" + pullSecretLinks: + - "/org/org-name/secret/some-secret" + loadBalancer: + dedicated: true + trustedProxies: 0 +``` + +Will transform to Terraform config: + +```terraform +resource "cpln_gvc" "app-name" { + name = "app-name" + description = "app-description" + tags = { + tag_name_1 = "tag-value-1" + tag_name_2 = "tag-value-2" + } + domain = "app.example.com" + locations = ["aws-us-west-2"] + pull_secrets = ["cpln_secret.some-secret.name"] + env = { + DATABASE_URL = "postgres://the_user:the_password@postgres.app-name.cpln.local:5432/app-name" + RAILS_ENV = "production" + RAILS_SERVE_STATIC_FILES = "true" + } + load_balancer { + dedicated = true + trusted_proxies = 0 + } +} +``` + +#### Identity + +CPLN template in YAML format: + +```yaml +kind: identity +name: postgres-poc-identity +description: postgres-poc-identity +tags: + tag-name-1: "tag-value-1" + tag-name-2: "tag-value-2" +``` + +Will transform to Terraform config: + +```terraform +resource "cpln_identity" "postgres-poc-identity" { + name = "postgres-poc-identity" + description = "postgres-poc-identity" + tags = { + tag_name_1 = "tag-value-1" + tag_name_2 = "tag-value-2" + } +} +``` + +#### Secret + +CPLN template in YAML format + +**For `aws` secret:** + +```yaml +kind: secret +name: aws +description: aws +tags: + tag1: AKIAIOSFODNN7EXAMPLE + tag2: arn:awskey +type: aws +data: + accessKey: AKIAIOSFODNN7EXAMPLE + externalId: '123' + roleArn: arn:awskey + secretKey: '123' +``` + +Will transform to Terraform config: + +```terraform +resource "cpln_secret" "aws" { + name = "aws" + description = "aws" + tags = { + tag1 = "AKIAIOSFODNN7EXAMPLE" + tag2 = "arn:awskey" + } + aws { + secret_key = "123" + access_key = "AKIAIOSFODNN7EXAMPLE" + role_arn = "arn:awskey" + external_id = "123" + } +} +``` + +**For `azure-connector` secret:** + +```yaml +kind: secret +name: azure-connector +description: azure_connector +tags: + tag1: tag-val +type: azure-connector +data: + code: '123' + url: https://sdfsfs.com +``` + +Will transform to Terraform config: + +```terraform +resource "cpln_secret" "azure-connector" { + name = "azure-connector" + description = "azure_connector" + tags = { + tag1 = "tag-val" + } + azure_connector { + url = "https://sdfsfs.com" + code = "123" + } +} +``` + +**For `azure-sdk-secret` secret:** + +```yaml +kind: secret +name: azure-sdk-secret +description: azure-sdk-secret +type: azure-sdk +data: >- + {"subscriptionId":"subscriptionId","tenantId":"tenantId","clientId":"clientId","clientSecret":"CONFIDENTIAL"} +``` + +Will transform to Terraform config: + +```terraform +resource "cpln_secret" "azure-sdk-secret" { + name = "azure-sdk-secret" + description = "azure-sdk-secret" + azure_sdk = "{"subscriptionId":"subscriptionId","tenantId":"tenantId","clientId":"clientID","clientSecret":"CONFIDENTIAL"}" +} +``` + +**For `dictionary` secret** + +```yaml +kind: secret +name: dictionary +description: dictionary +tags: {} +type: dictionary +data: + sdfdsf: '2222' +``` + +Will transform to Terraform config: + +```terraform +resource "cpln_secret" "dictionary" { + name = "dictionary" + description = "dictionary" + tags = { + } + dictionary = { + sdfdsf = "2222" + } +} +``` + +Supported all types of the secrets which can be configured in Control Plane. + +#### Policy + +CPLN template in YAML format: + +```yaml +kind: policy +name: policy-name +description: policy description +tags: + tag1: tag1_value + tag2: tag2_value +target: all +targetKind: secret +targetLinks: +- "//secret/postgres-poc-credentials" +- "//secret/postgres-poc-entrypoint-script" +bindings: + - permissions: + - reveal + - view + - use + principalLinks: + - "//gvc/{{APP_NAME}}/identity/postgres-poc-identity" + - permissions: + - view + principalLinks: + - user/fake-user@fake-email.com +``` + +Will be transformed to Terraform config: + +```terraform +resource "cpln_policy" "policy-name" { + name = "policy-name" + description = "policy description" + tags = { + tag1 = "tag1_value" + tag2 = "tag2_value" + } + target_kind = "secret" + gvc = cpln_gvc.app-name.name + target = "all" + target_links = ["postgres-poc-credentials", "postgres-poc-entrypoint-script"] + binding { + permissions = ["reveal", "view", "use"] + principal_links = ["gvc/app-name/identity/postgres-poc-identity"] + } + binding { + permissions = ["view"] + principal_links = ["user/fake-user@fake-email.com"] + } +} +``` + +#### Volumeset + +CPLN template in YAML format: + +```yaml +kind: volumeset +name: postgres-poc-vs +description: postgres-poc-vs +spec: + autoscaling: + maxCapacity: 1000 + minFreePercentage: 1 + scalingFactor: 1.1 + fileSystemType: ext4 + initialCapacity: 10 + performanceClass: general-purpose-ssd + snapshots: + createFinalSnapshot: true + retentionDuration: 7d +``` + +Will be transformed to Terraform config: + +```terraform +resource "cpln_volume_set" "postgres-poc-vs" { + gvc = cpln_gvc.app-name.name + name = "postgres-poc-vs" + description = "postgres-poc-vs" + initial_capacity = 10 + performance_class = "general-purpose-ssd" + file_system_type = "ext4" + snapshots { + create_final_snapshot = true + retention_duration = "7d" + } + autoscaling { + max_capacity = 1000 + min_free_percentage = 1 + scaling_factor = 1.1 + } +} +``` From 30ff600d31300b88316e450f71e3faa708dfa903 Mon Sep 17 00:00:00 2001 From: Zakir Dzhamaliddinov Date: Tue, 5 Nov 2024 15:03:33 +0300 Subject: [PATCH 3/7] Add docs for workload templates --- docs/terraform.md | 172 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 147 insertions(+), 25 deletions(-) diff --git a/docs/terraform.md b/docs/terraform.md index 9e5f4e4e..2fbbc8b7 100644 --- a/docs/terraform.md +++ b/docs/terraform.md @@ -10,7 +10,7 @@ cpflow terraform generate This command will create Terraform configurations for each application defined in `controlplane.yml`, utilizing templates from the `templates` folder. -Each time this command is invoked, Terraform configurations will be recreated. You can continue working with CPLN configuration files in YAML format and simply transform them to Terraform format at any time. +Each time this command is invoked, Terraform configurations will be recreated, terraform lock file will be preserved. You can continue working with CPLN configuration files in YAML format and simply transform them to Terraform format at any time. ### Project Structure @@ -23,8 +23,8 @@ Given the project structure below: │ ├── postgres.yml -- workload config │ └── rails.yml -- workload config ├── controlplane.yml -- configs for overall application -├── Dockerfile -└── entrypoint.sh +├── Dockerfile -- Docker configuration for the application +└── entrypoint.sh -- Entry point script for the application ``` Invoking `cpflow terraform generate` will generate a new `terraform` folder with subfolders containing Terraform configurations for each application described in `controlplane.yml`: @@ -52,13 +52,41 @@ Invoking `cpflow terraform generate` will generate a new `terraform` folder with │ │ ├── rails_envs.tf -- ENV variables for Rails workload in HCL │ │ ├── providers.tf -- Providers config in HCL │ │ └── required_providers.tf -- Required providers config in HCL +│ ├── workload/ -- Terraform configurations for workload module +│ │ ├── main.tf -- Main config for workload resource in HCL +│ │ └── variables.tf -- Variables used to create config for workload resource in HCL ├── controlplane.yml -- configs for overall application -├── Dockerfile -└── entrypoint.sh +├── Dockerfile -- Docker configuration for the application +└── entrypoint.sh -- Entry point script for the application ``` ### Terraform Configurations from CPLN Templates +#### Providers + +Terraform provider configurations are controlled via `required_providers.tf` and `providers.tf`: + +- **`required_providers.tf`** + +```terraform +terraform { + required_providers { + cpln = { + source = "controlplane-com/cpln" + version = "~> 1.0" + } + } +} +``` + +- **`providers.tf`** + +```terraform +provider "cpln" { + org = "org-name-example" +} +``` + #### GVC CPLN template in YAML format: @@ -150,15 +178,12 @@ CPLN template in YAML format kind: secret name: aws description: aws -tags: - tag1: AKIAIOSFODNN7EXAMPLE - tag2: arn:awskey type: aws data: - accessKey: AKIAIOSFODNN7EXAMPLE - externalId: '123' + accessKey: 'AccessKeyExample' + externalId: 'ExternalIdExample' roleArn: arn:awskey - secretKey: '123' + secretKey: 'SecretKeyExample' ``` Will transform to Terraform config: @@ -167,15 +192,11 @@ Will transform to Terraform config: resource "cpln_secret" "aws" { name = "aws" description = "aws" - tags = { - tag1 = "AKIAIOSFODNN7EXAMPLE" - tag2 = "arn:awskey" - } aws { - secret_key = "123" - access_key = "AKIAIOSFODNN7EXAMPLE" + secret_key = "SecretKeyExample" + access_key = "AccessKeyExample" role_arn = "arn:awskey" - external_id = "123" + external_id = "ExternalIdExample" } } ``` @@ -190,8 +211,8 @@ tags: tag1: tag-val type: azure-connector data: - code: '123' - url: https://sdfsfs.com + code: 'CodeExample' + url: https://example.com ``` Will transform to Terraform config: @@ -204,8 +225,8 @@ resource "cpln_secret" "azure-connector" { tag1 = "tag-val" } azure_connector { - url = "https://sdfsfs.com" - code = "123" + url = "https://example.com" + code = "CodeExample" } } ``` @@ -231,7 +252,7 @@ resource "cpln_secret" "azure-sdk-secret" { } ``` -**For `dictionary` secret** +**For `dictionary` secret:** ```yaml kind: secret @@ -240,7 +261,7 @@ description: dictionary tags: {} type: dictionary data: - sdfdsf: '2222' + example: 'value' ``` Will transform to Terraform config: @@ -252,7 +273,7 @@ resource "cpln_secret" "dictionary" { tags = { } dictionary = { - sdfdsf = "2222" + example = "value" } } ``` @@ -355,3 +376,104 @@ resource "cpln_volume_set" "postgres-poc-vs" { } } ``` + +#### Workload + +CPLN template in YAML format: + +```yaml +kind: workload +name: rails +spec: + type: standard + containers: + - name: rails + cpu: 300m + env: + - name: LOG_LEVEL + value: debug + inheritEnv: true + image: {{APP_IMAGE_LINK}} + memory: 512Mi + ports: + - number: 3000 + protocol: http + defaultOptions: + autoscaling: + maxScale: 1 + capacityAI: false + firewallConfig: + external: + # Default to allow public access to Rails server + inboundAllowCIDR: + - 0.0.0.0/0 + # Could configure outbound for more security + outboundAllowCIDR: + - 0.0.0.0/0 +``` + +Will be transformed to Terraform configs: + +- **`rails.tf`** + +```terraform +module "rails" { + source = "../workload" + type = "standard" + name = "rails" + gvc = cpln_gvc.my-app-production.name + containers = { + rails: { + image: "/org/shakacode-demo/image/my-app-production:rails", + cpu: "300m", + memory: "512Mi", + inherit_env: true, + envs: local.rails_envs, + ports: [ + { + number: 3000, + protocol: "http" + } + ] + } + } + options = { + autoscaling: { + max_scale: 1 + } + capacity_ai: false + } + firewall_spec = { + external: { + inbound_allow_cidr: [ + "0.0.0.0/0" + ], + outbound_allow_cidr: [ + "0.0.0.0/0" + ] + } + } +} +``` + +Notice `source: ../workload` line - there is common `workload` module which is used for generating Terraform configs from workload templates: +``` +workload/ +├── main.tf -- Configurable workload resource in HCL +├── variables.tf -- Variables used to configure workload resource above +``` + +- **`rails_envs.tf`** + +```terraform +locals { + rails_envs = { + LOG_LEVEL = "debug" + } +} +``` + +### References + +- [Control Plane Terraform Provider](https://registry.terraform.io/providers/controlplane-com/cpln/latest/docs) +- [Terraform Provider Plugin](https://shakadocs.controlplane.com/terraform/installation#terraform-provider-plugin) From 3f1c5a91dc9a48a033cef24d1b781c6a9794732f Mon Sep 17 00:00:00 2001 From: Zakir Dzhamaliddinov Date: Tue, 12 Nov 2024 16:11:07 +0300 Subject: [PATCH 4/7] Improve docs for terraform feature --- docs/{terraform.md => terraform/details.md} | 67 +--- .../example/.controlplane/controlplane.yml | 29 ++ .../example/.controlplane/templates/app.yml | 39 +++ .../.controlplane/templates/postgres.yml | 30 ++ .../example/.controlplane/templates/rails.yml | 26 ++ .../terraform/rails-app-production/gvc.tf | 15 + .../rails-app-production/identities.tf | 8 + .../rails-app-production/postgres.tf | 37 ++ .../rails-app-production/postgres_envs.tf | 7 + .../rails-app-production/providers.tf | 3 + .../rails-app-production/rails-app.tf | 37 ++ .../rails-app-production/rails_envs.tf | 5 + .../required_providers.tf | 8 + .../terraform/rails-app-production/secrets.tf | 8 + .../terraform/rails-app-staging/gvc.tf | 15 + .../terraform/rails-app-staging/identities.tf | 8 + .../terraform/rails-app-staging/postgres.tf | 37 ++ .../rails-app-staging/postgres_envs.tf | 7 + .../terraform/rails-app-staging/providers.tf | 3 + .../terraform/rails-app-staging/rails-app.tf | 37 ++ .../terraform/rails-app-staging/rails_envs.tf | 5 + .../rails-app-staging/required_providers.tf | 8 + .../terraform/rails-app-staging/secrets.tf | 8 + .../example/terraform/workload/main.tf | 315 ++++++++++++++++++ .../terraform/workload/required_providers.tf | 8 + .../example/terraform/workload/variables.tf | 263 +++++++++++++++ docs/terraform/overview.md | 99 ++++++ 27 files changed, 1067 insertions(+), 65 deletions(-) rename docs/{terraform.md => terraform/details.md} (70%) create mode 100644 docs/terraform/example/.controlplane/controlplane.yml create mode 100644 docs/terraform/example/.controlplane/templates/app.yml create mode 100644 docs/terraform/example/.controlplane/templates/postgres.yml create mode 100644 docs/terraform/example/.controlplane/templates/rails.yml create mode 100644 docs/terraform/example/terraform/rails-app-production/gvc.tf create mode 100644 docs/terraform/example/terraform/rails-app-production/identities.tf create mode 100644 docs/terraform/example/terraform/rails-app-production/postgres.tf create mode 100644 docs/terraform/example/terraform/rails-app-production/postgres_envs.tf create mode 100644 docs/terraform/example/terraform/rails-app-production/providers.tf create mode 100644 docs/terraform/example/terraform/rails-app-production/rails-app.tf create mode 100644 docs/terraform/example/terraform/rails-app-production/rails_envs.tf create mode 100644 docs/terraform/example/terraform/rails-app-production/required_providers.tf create mode 100644 docs/terraform/example/terraform/rails-app-production/secrets.tf create mode 100644 docs/terraform/example/terraform/rails-app-staging/gvc.tf create mode 100644 docs/terraform/example/terraform/rails-app-staging/identities.tf create mode 100644 docs/terraform/example/terraform/rails-app-staging/postgres.tf create mode 100644 docs/terraform/example/terraform/rails-app-staging/postgres_envs.tf create mode 100644 docs/terraform/example/terraform/rails-app-staging/providers.tf create mode 100644 docs/terraform/example/terraform/rails-app-staging/rails-app.tf create mode 100644 docs/terraform/example/terraform/rails-app-staging/rails_envs.tf create mode 100644 docs/terraform/example/terraform/rails-app-staging/required_providers.tf create mode 100644 docs/terraform/example/terraform/rails-app-staging/secrets.tf create mode 100644 docs/terraform/example/terraform/workload/main.tf create mode 100644 docs/terraform/example/terraform/workload/required_providers.tf create mode 100644 docs/terraform/example/terraform/workload/variables.tf create mode 100644 docs/terraform/overview.md diff --git a/docs/terraform.md b/docs/terraform/details.md similarity index 70% rename from docs/terraform.md rename to docs/terraform/details.md index 2fbbc8b7..56d42c74 100644 --- a/docs/terraform.md +++ b/docs/terraform/details.md @@ -1,65 +1,3 @@ -# Terraform - -### Overview - -You can manage your CPLN configuration through `cpflow` commands and later invoke the generation of Terraform configuration files by running: - -```sh -cpflow terraform generate -``` - -This command will create Terraform configurations for each application defined in `controlplane.yml`, utilizing templates from the `templates` folder. - -Each time this command is invoked, Terraform configurations will be recreated, terraform lock file will be preserved. You can continue working with CPLN configuration files in YAML format and simply transform them to Terraform format at any time. - -### Project Structure - -Given the project structure below: - -``` -.controlplane/ -├── templates/ -│ ├── app.yml -- GVC config -│ ├── postgres.yml -- workload config -│ └── rails.yml -- workload config -├── controlplane.yml -- configs for overall application -├── Dockerfile -- Docker configuration for the application -└── entrypoint.sh -- Entry point script for the application -``` - -Invoking `cpflow terraform generate` will generate a new `terraform` folder with subfolders containing Terraform configurations for each application described in `controlplane.yml`: - -``` -.controlplane/ -├── templates/ -│ ├── app.yml -- GVC config -│ ├── postgres.yml -- workload config -│ └── rails.yml -- workload config -├── terraform/ -│ ├── staging/ -- Terraform configurations for staging environment -│ │ ├── gvc.tf -- GVC config in HCL -│ │ ├── postgres.tf -- Postgres workload config in HCL -│ │ ├── postgres_envs.tf -- ENV variables for Postgres workload in HCL -│ │ ├── rails.tf -- Rails workload config in HCL -│ │ ├── rails_envs.tf -- ENV variables for Rails workload in HCL -│ │ ├── providers.tf -- Providers config in HCL -│ │ └── required_providers.tf -- Required providers config in HCL -│ ├── production/ -- Terraform configurations for production environment -│ │ ├── gvc.tf -- GVC config in HCL -│ │ ├── postgres.tf -- Postgres workload config in HCL -│ │ ├── postgres_envs.tf -- ENV variables for Postgres workload in HCL -│ │ ├── rails.tf -- Rails workload config in HCL -│ │ ├── rails_envs.tf -- ENV variables for Rails workload in HCL -│ │ ├── providers.tf -- Providers config in HCL -│ │ └── required_providers.tf -- Required providers config in HCL -│ ├── workload/ -- Terraform configurations for workload module -│ │ ├── main.tf -- Main config for workload resource in HCL -│ │ └── variables.tf -- Variables used to create config for workload resource in HCL -├── controlplane.yml -- configs for overall application -├── Dockerfile -- Docker configuration for the application -└── entrypoint.sh -- Entry point script for the application -``` - ### Terraform Configurations from CPLN Templates #### Providers @@ -87,7 +25,7 @@ provider "cpln" { } ``` -#### GVC +#### GVC (Global Virtual Cloud) CPLN template in YAML format: @@ -456,7 +394,7 @@ module "rails" { } ``` -Notice `source: ../workload` line - there is common `workload` module which is used for generating Terraform configs from workload templates: +Notice the `source: ../workload` line - there is a common `workload` module which is used for generating Terraform configs from workload templates: ``` workload/ ├── main.tf -- Configurable workload resource in HCL @@ -476,4 +414,3 @@ locals { ### References - [Control Plane Terraform Provider](https://registry.terraform.io/providers/controlplane-com/cpln/latest/docs) -- [Terraform Provider Plugin](https://shakadocs.controlplane.com/terraform/installation#terraform-provider-plugin) diff --git a/docs/terraform/example/.controlplane/controlplane.yml b/docs/terraform/example/.controlplane/controlplane.yml new file mode 100644 index 00000000..2f257e3f --- /dev/null +++ b/docs/terraform/example/.controlplane/controlplane.yml @@ -0,0 +1,29 @@ +allow_org_override_by_env: true +allow_app_override_by_env: true + +aliases: + common: &common + cpln_org: my-org-staging + default_location: aws-us-east-2 + setup_app_templates: + - app + - postgres + - rails + one_off_workload: rails + app_workloads: + - rails + additional_workloads: + - postgres +apps: + rails-app-staging: + <<: *common + hooks: + post_creation: bundle exec rake db:prepare + pre_deletion: bundle exec rake db:drop + + rails-app-production: + <<: *common + allow_org_override_by_env: false + allow_app_override_by_env: false + cpln_org: my-org-production + upstream: rails-app-staging diff --git a/docs/terraform/example/.controlplane/templates/app.yml b/docs/terraform/example/.controlplane/templates/app.yml new file mode 100644 index 00000000..ad456853 --- /dev/null +++ b/docs/terraform/example/.controlplane/templates/app.yml @@ -0,0 +1,39 @@ +kind: gvc +name: {{APP_NAME}} +description: Global Virtual Cloud for Rails Application +spec: + env: + - name: DATABASE_URL + value: "postgres://user:password@postgres.{{APP_NAME}}.cpln.local:5432/{{APP_NAME}}" + - name: RAILS_ENV + value: production + - name: RAILS_SERVE_STATIC_FILES + value: "true" + staticPlacement: + locationLinks: + - {{APP_LOCATION_LINK}} + pullSecretLinks: + - "/org/org-name/secret/rails-app-secret" + loadBalancer: + dedicated: true + trustedProxies: 0 + +--- + +kind: identity +name: rails-app-identity +description: Identity for Rails Application +tags: + environment: production + +--- + +kind: secret +name: rails-app-secret +description: Secret for Rails Application +type: aws +data: + accessKey: 'AccessKeyExample' + secretKey: 'SecretKeyExample' + region: 'us-west-2' + diff --git a/docs/terraform/example/.controlplane/templates/postgres.yml b/docs/terraform/example/.controlplane/templates/postgres.yml new file mode 100644 index 00000000..69371175 --- /dev/null +++ b/docs/terraform/example/.controlplane/templates/postgres.yml @@ -0,0 +1,30 @@ +kind: workload +name: postgres +spec: + type: standard + containers: + - name: postgres + cpu: 500m + env: + - name: POSTGRES_USER + value: "user" + - name: POSTGRES_PASSWORD + value: "password" + - name: POSTGRES_DB + value: "rails_app" + inheritEnv: true + image: "postgres:latest" + memory: 1Gi + ports: + - number: 5432 + protocol: tcp + defaultOptions: + autoscaling: + maxScale: 1 + capacityAI: false + firewallConfig: + external: + inboundAllowCIDR: + - 0.0.0.0/0 + outboundAllowCIDR: + - 0.0.0.0/0 diff --git a/docs/terraform/example/.controlplane/templates/rails.yml b/docs/terraform/example/.controlplane/templates/rails.yml new file mode 100644 index 00000000..4f7c0394 --- /dev/null +++ b/docs/terraform/example/.controlplane/templates/rails.yml @@ -0,0 +1,26 @@ +kind: workload +name: rails +spec: + type: standard + containers: + - name: rails + cpu: 300m + env: + - name: LOG_LEVEL + value: debug + inheritEnv: true + image: "org-name/rails:latest" + memory: 512Mi + ports: + - number: 3000 + protocol: http + defaultOptions: + autoscaling: + maxScale: 1 + capacityAI: false + firewallConfig: + external: + inboundAllowCIDR: + - 0.0.0.0/0 + outboundAllowCIDR: + - 0.0.0.0/0 diff --git a/docs/terraform/example/terraform/rails-app-production/gvc.tf b/docs/terraform/example/terraform/rails-app-production/gvc.tf new file mode 100644 index 00000000..dfec8489 --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-production/gvc.tf @@ -0,0 +1,15 @@ +resource "cpln_gvc" "rails-app-production" { + name = "rails-app-production" + description = "Global Virtual Cloud for Rails Application" + locations = ["aws-us-east-2"] + pull_secrets = [cpln_secret.rails-app-secret.name] + env = { + DATABASE_URL = "postgres://user:password@postgres.rails-app-production.cpln.local:5432/rails-app-production" + RAILS_ENV = "production" + RAILS_SERVE_STATIC_FILES = "true" + } + load_balancer { + dedicated = true + trusted_proxies = 0 + } +} diff --git a/docs/terraform/example/terraform/rails-app-production/identities.tf b/docs/terraform/example/terraform/rails-app-production/identities.tf new file mode 100644 index 00000000..c5ad5383 --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-production/identities.tf @@ -0,0 +1,8 @@ +resource "cpln_identity" "rails-app-identity" { + gvc = cpln_gvc.rails-app-production.name + name = "rails-app-identity" + description = "Identity for Rails Application" + tags = { + environment = "production" + } +} diff --git a/docs/terraform/example/terraform/rails-app-production/postgres.tf b/docs/terraform/example/terraform/rails-app-production/postgres.tf new file mode 100644 index 00000000..87603701 --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-production/postgres.tf @@ -0,0 +1,37 @@ +module "postgres" { + source = "../workload" + type = "standard" + name = "postgres" + gvc = cpln_gvc.rails-app-production.name + containers = { + postgres: { + image: "postgres:latest", + cpu: "500m", + memory: "1Gi", + inherit_env: true, + envs: local.postgres_envs, + ports: [ + { + number: 5432, + protocol: "tcp" + } + ] + } + } + options = { + autoscaling: { + max_scale: 1 + } + capacity_ai: false + } + firewall_spec = { + external: { + inbound_allow_cidr: [ + "0.0.0.0/0" + ], + outbound_allow_cidr: [ + "0.0.0.0/0" + ] + } + } +} diff --git a/docs/terraform/example/terraform/rails-app-production/postgres_envs.tf b/docs/terraform/example/terraform/rails-app-production/postgres_envs.tf new file mode 100644 index 00000000..8ca5720b --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-production/postgres_envs.tf @@ -0,0 +1,7 @@ +locals { + postgres_envs = { + POSTGRES_USER = "user" + POSTGRES_PASSWORD = "password" + POSTGRES_DB = "rails_app" + } +} diff --git a/docs/terraform/example/terraform/rails-app-production/providers.tf b/docs/terraform/example/terraform/rails-app-production/providers.tf new file mode 100644 index 00000000..485ac8d4 --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-production/providers.tf @@ -0,0 +1,3 @@ +provider "cpln" { + org = "org-name" +} diff --git a/docs/terraform/example/terraform/rails-app-production/rails-app.tf b/docs/terraform/example/terraform/rails-app-production/rails-app.tf new file mode 100644 index 00000000..6906156e --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-production/rails-app.tf @@ -0,0 +1,37 @@ +module "rails-app" { + source = "../workload" + type = "standard" + name = "rails-app" + gvc = cpln_gvc.rails-app-production.name + containers = { + rails: { + image: "org-name/rails-app:latest", + cpu: "300m", + memory: "512Mi", + inherit_env: true, + envs: local.rails_envs, + ports: [ + { + number: 3000, + protocol: "http" + } + ] + } + } + options = { + autoscaling: { + max_scale: 1 + } + capacity_ai: false + } + firewall_spec = { + external: { + inbound_allow_cidr: [ + "0.0.0.0/0" + ], + outbound_allow_cidr: [ + "0.0.0.0/0" + ] + } + } +} diff --git a/docs/terraform/example/terraform/rails-app-production/rails_envs.tf b/docs/terraform/example/terraform/rails-app-production/rails_envs.tf new file mode 100644 index 00000000..b51a979e --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-production/rails_envs.tf @@ -0,0 +1,5 @@ +locals { + rails_envs = { + LOG_LEVEL = "debug" + } +} diff --git a/docs/terraform/example/terraform/rails-app-production/required_providers.tf b/docs/terraform/example/terraform/rails-app-production/required_providers.tf new file mode 100644 index 00000000..dde72523 --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-production/required_providers.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + cpln = { + source = "controlplane-com/cpln" + version = "~> 1.0" + } + } +} diff --git a/docs/terraform/example/terraform/rails-app-production/secrets.tf b/docs/terraform/example/terraform/rails-app-production/secrets.tf new file mode 100644 index 00000000..9c8eb4fa --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-production/secrets.tf @@ -0,0 +1,8 @@ +resource "cpln_secret" "rails-app-secret" { + name = "rails-app-secret" + description = "Secret for Rails Application" + aws { + secret_key = "SecretKeyExample" + access_key = "AccessKeyExample" + } +} diff --git a/docs/terraform/example/terraform/rails-app-staging/gvc.tf b/docs/terraform/example/terraform/rails-app-staging/gvc.tf new file mode 100644 index 00000000..88ae0427 --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-staging/gvc.tf @@ -0,0 +1,15 @@ +resource "cpln_gvc" "rails-app-staging" { + name = "rails-app-staging" + description = "Global Virtual Cloud for Rails Application" + locations = ["aws-us-east-2"] + pull_secrets = [cpln_secret.rails-app-secret.name] + env = { + DATABASE_URL = "postgres://user:password@postgres.rails-app-staging.cpln.local:5432/rails-app-staging" + RAILS_ENV = "production" + RAILS_SERVE_STATIC_FILES = "true" + } + load_balancer { + dedicated = true + trusted_proxies = 0 + } +} diff --git a/docs/terraform/example/terraform/rails-app-staging/identities.tf b/docs/terraform/example/terraform/rails-app-staging/identities.tf new file mode 100644 index 00000000..483f3eb8 --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-staging/identities.tf @@ -0,0 +1,8 @@ +resource "cpln_identity" "rails-app-identity" { + gvc = cpln_gvc.rails-app-staging.name + name = "rails-app-identity" + description = "Identity for Rails Application" + tags = { + environment = "production" + } +} diff --git a/docs/terraform/example/terraform/rails-app-staging/postgres.tf b/docs/terraform/example/terraform/rails-app-staging/postgres.tf new file mode 100644 index 00000000..52e8f839 --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-staging/postgres.tf @@ -0,0 +1,37 @@ +module "postgres" { + source = "../workload" + type = "standard" + name = "postgres" + gvc = cpln_gvc.rails-app-staging.name + containers = { + postgres: { + image: "postgres:latest", + cpu: "500m", + memory: "1Gi", + inherit_env: true, + envs: local.postgres_envs, + ports: [ + { + number: 5432, + protocol: "tcp" + } + ] + } + } + options = { + autoscaling: { + max_scale: 1 + } + capacity_ai: false + } + firewall_spec = { + external: { + inbound_allow_cidr: [ + "0.0.0.0/0" + ], + outbound_allow_cidr: [ + "0.0.0.0/0" + ] + } + } +} diff --git a/docs/terraform/example/terraform/rails-app-staging/postgres_envs.tf b/docs/terraform/example/terraform/rails-app-staging/postgres_envs.tf new file mode 100644 index 00000000..8ca5720b --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-staging/postgres_envs.tf @@ -0,0 +1,7 @@ +locals { + postgres_envs = { + POSTGRES_USER = "user" + POSTGRES_PASSWORD = "password" + POSTGRES_DB = "rails_app" + } +} diff --git a/docs/terraform/example/terraform/rails-app-staging/providers.tf b/docs/terraform/example/terraform/rails-app-staging/providers.tf new file mode 100644 index 00000000..485ac8d4 --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-staging/providers.tf @@ -0,0 +1,3 @@ +provider "cpln" { + org = "org-name" +} diff --git a/docs/terraform/example/terraform/rails-app-staging/rails-app.tf b/docs/terraform/example/terraform/rails-app-staging/rails-app.tf new file mode 100644 index 00000000..78ca0891 --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-staging/rails-app.tf @@ -0,0 +1,37 @@ +module "rails-app" { + source = "../workload" + type = "standard" + name = "rails-app" + gvc = cpln_gvc.rails-app-staging.name + containers = { + rails: { + image: "org-name/rails-app:latest", + cpu: "300m", + memory: "512Mi", + inherit_env: true, + envs: local.rails_envs, + ports: [ + { + number: 3000, + protocol: "http" + } + ] + } + } + options = { + autoscaling: { + max_scale: 1 + } + capacity_ai: false + } + firewall_spec = { + external: { + inbound_allow_cidr: [ + "0.0.0.0/0" + ], + outbound_allow_cidr: [ + "0.0.0.0/0" + ] + } + } +} diff --git a/docs/terraform/example/terraform/rails-app-staging/rails_envs.tf b/docs/terraform/example/terraform/rails-app-staging/rails_envs.tf new file mode 100644 index 00000000..b51a979e --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-staging/rails_envs.tf @@ -0,0 +1,5 @@ +locals { + rails_envs = { + LOG_LEVEL = "debug" + } +} diff --git a/docs/terraform/example/terraform/rails-app-staging/required_providers.tf b/docs/terraform/example/terraform/rails-app-staging/required_providers.tf new file mode 100644 index 00000000..dde72523 --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-staging/required_providers.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + cpln = { + source = "controlplane-com/cpln" + version = "~> 1.0" + } + } +} diff --git a/docs/terraform/example/terraform/rails-app-staging/secrets.tf b/docs/terraform/example/terraform/rails-app-staging/secrets.tf new file mode 100644 index 00000000..9c8eb4fa --- /dev/null +++ b/docs/terraform/example/terraform/rails-app-staging/secrets.tf @@ -0,0 +1,8 @@ +resource "cpln_secret" "rails-app-secret" { + name = "rails-app-secret" + description = "Secret for Rails Application" + aws { + secret_key = "SecretKeyExample" + access_key = "AccessKeyExample" + } +} diff --git a/docs/terraform/example/terraform/workload/main.tf b/docs/terraform/example/terraform/workload/main.tf new file mode 100644 index 00000000..36ef508b --- /dev/null +++ b/docs/terraform/example/terraform/workload/main.tf @@ -0,0 +1,315 @@ +resource "cpln_workload" "workload" { + type = var.type + + gvc = var.gvc + identity_link = var.identity_link + + name = var.name + description = var.description + + tags = var.tags + support_dynamic_tags = var.support_dynamic_tags + + dynamic "container" { + for_each = var.containers + iterator = container + content { + name = container.key + + args = container.value.args + command = container.value.command + env = container.value.envs + image = container.value.image + + cpu = container.value.cpu + memory = container.value.memory + + dynamic "lifecycle" { + for_each = container.value.post_start_command != null || container.value.pre_stop_command != null ? [1] : [] + content { + dynamic "post_start" { + for_each = container.value.post_start_command != null ? [1] : [] + content { + exec { + command = [ + "/bin/bash", + "-c", + "[ -f ${container.value.post_start_command} ] && ${container.value.post_start_command} || true", + ] + } + } + } + dynamic "pre_stop" { + for_each = container.value.pre_stop_command != null ? [1] : [] + content { + exec { + command = [ + "/bin/bash", + "-c", + "[ -f ${container.value.pre_stop_command} ] && ${container.value.pre_stop_command} || true", + ] + } + } + } + } + } + + dynamic "liveness_probe" { + for_each = container.value.liveness_probe != null ? [container.value.liveness_probe] : [] + iterator = liveness + content { + dynamic "exec" { + for_each = liveness.value.exec != null ? [liveness.value.exec] : [] + iterator = exec + content { + command = exec.value.command + } + } + dynamic "http_get" { + for_each = liveness.value.http_get != null ? [liveness.value.http_get] : [] + iterator = http_get + content { + path = http_get.value.path + port = http_get.value.port + scheme = http_get.value.scheme + http_headers = http_get.value.http_headers + } + } + dynamic "tcp_socket" { + for_each = liveness.value.tcp_socket != null ? [liveness.value.tcp_socket] : [] + iterator = tcp_socket + content { + port = tcp_socket.value.port + } + } + dynamic "grpc" { + for_each = liveness.value.grpc != null ? [liveness.value.grpc] : [] + iterator = grpc + content { + port = grpc.value.port + } + } + failure_threshold = liveness.value.failure_threshold + initial_delay_seconds = liveness.value.initial_delay_seconds + period_seconds = liveness.value.period_seconds + success_threshold = liveness.value.success_threshold + timeout_seconds = liveness.value.timeout_seconds + } + } + + dynamic "readiness_probe" { + for_each = container.value.readiness_probe != null ? [container.value.readiness_probe] : [] + iterator = readiness + content { + dynamic "exec" { + for_each = readiness.value.exec != null ? [readiness.value.exec] : [] + iterator = exec + content { + command = exec.value.command + } + } + dynamic "http_get" { + for_each = readiness.value.http_get != null ? [readiness.value.http_get] : [] + iterator = http_get + content { + path = http_get.value.path + port = http_get.value.port + scheme = http_get.value.scheme + http_headers = http_get.value.http_headers + } + } + dynamic "tcp_socket" { + for_each = readiness.value.tcp_socket != null ? [readiness.value.tcp_socket] : [] + iterator = tcp_socket + content { + port = tcp_socket.value.port + } + } + dynamic "grpc" { + for_each = readiness.value.grpc != null ? [readiness.value.grpc] : [] + iterator = grpc + content { + port = grpc.value.port + } + } + failure_threshold = readiness.value.failure_threshold + initial_delay_seconds = readiness.value.initial_delay_seconds + period_seconds = readiness.value.period_seconds + success_threshold = readiness.value.success_threshold + timeout_seconds = readiness.value.timeout_seconds + } + } + + dynamic "ports" { + for_each = container.value.ports + iterator = port + content { + number = port.value.number + protocol = port.value.protocol + } + } + + dynamic "volume" { + for_each = container.value.volumes + iterator = volume + content { + uri = volume.value.uri + path = volume.value.path + } + } + } + } + + dynamic "options" { + for_each = var.options != null ? [var.options] : [] + iterator = options + content { + dynamic "autoscaling" { + for_each = options.value.autoscaling != null ? [options.value.autoscaling] : [] + iterator = autoscaling + content { + metric = autoscaling.value.metric + metric_percentile = autoscaling.value.metric_percentile + max_scale = autoscaling.value.max_scale + min_scale = autoscaling.value.min_scale + target = autoscaling.value.target + scale_to_zero_delay = autoscaling.value.scale_to_zero_delay + max_concurrency = autoscaling.value.max_concurrency + } + } + capacity_ai = options.value.capacity_ai + suspend = options.value.suspend + timeout_seconds = options.value.timeout_seconds + debug = options.value.debug + } + } + + dynamic "local_options" { + for_each = var.local_options != null ? [var.local_options] : [] + iterator = options + content { + dynamic "autoscaling" { + for_each = options.value.autoscaling != null ? [options.value.autoscaling] : [] + iterator = autoscaling + content { + metric = autoscaling.value.metric + metric_percentile = autoscaling.value.metric_percentile + max_scale = autoscaling.value.max_scale + min_scale = autoscaling.value.min_scale + target = autoscaling.value.target + scale_to_zero_delay = autoscaling.value.scale_to_zero_delay + max_concurrency = autoscaling.value.max_concurrency + } + } + location = options.value.location + capacity_ai = options.value.capacity_ai + suspend = options.value.suspend + timeout_seconds = options.value.timeout_seconds + debug = options.value.debug + } + } + + dynamic "rollout_options" { + for_each = var.rollout_options != null ? [var.rollout_options] : [] + iterator = rollout_options + content { + min_ready_seconds = rollout_options.value.min_ready_seconds + max_unavailable_replicas = rollout_options.value.max_unavailable_replicas + max_surge_replicas = rollout_options.value.max_surge_replicas + scaling_policy = rollout_options.value.scaling_policy + } + } + + dynamic "security_options" { + for_each = var.security_options != null ? [var.security_options] : [] + iterator = security_options + content { + file_system_group_id = security_options.value.file_system_group_id + } + } + + dynamic "firewall_spec" { + for_each = var.firewall_spec != null ? [var.firewall_spec] : [] + iterator = firewall_spec + content { + dynamic "external" { + for_each = firewall_spec.value.external != null ? [firewall_spec.value.external] : [] + iterator = external + content { + inbound_allow_cidr = external.value.inbound_allow_cidr + outbound_allow_hostname = external.value.outbound_allow_hostname + outbound_allow_cidr = external.value.outbound_allow_cidr + dynamic "outbound_allow_port" { + for_each = external.value.outbound_allow_port + iterator = outbound_allow_port + content { + protocol = outbound_allow_port.value.protocol + number = outbound_allow_port.value.number + } + } + } + } + dynamic "internal" { + for_each = firewall_spec.value.internal != null ? [firewall_spec.value.internal] : [] + iterator = internal + content { + inbound_allow_type = internal.value.inbound_allow_type + inbound_allow_workload = internal.value.inbound_allow_workload + } + } + } + } + + dynamic "load_balancer" { + for_each = var.load_balancer != null ? [var.load_balancer] : [] + iterator = load_balancer + content { + dynamic "direct" { + for_each = load_balancer.value.direct != null ? [load_balancer.value.direct] : [] + iterator = direct + content { + enabled = direct.value.enabled + dynamic "port" { + for_each = direct.value.port + iterator = port + content { + external_port = port.value.external_port + protocol = port.value.protocol + scheme = port.value.scheme + container_port = port.value.container_port + } + } + } + } + dynamic "geo_location" { + for_each = load_balancer.value.geo_location != null ? [load_balancer.value.geo_location] : [] + iterator = geo_location + content { + enabled = geo_location.value.enabled + dynamic "headers" { + for_each = geo_location.value.headers != null ? [geo_location.value.headers] : [] + iterator = headers + content { + asn = headers.value.asn + city = headers.value.city + country = headers.value.country + region = headers.value.region + } + } + } + } + } + } + + dynamic "job" { + for_each = var.job != null ? [var.job] : [] + iterator = job + content { + schedule = job.value.schedule + concurrency_policy = job.value.concurrency_policy + history_limit = job.value.history_limit + restart_policy = job.value.restart_policy + active_deadline_seconds = job.value.active_deadline_seconds + } + } +} diff --git a/docs/terraform/example/terraform/workload/required_providers.tf b/docs/terraform/example/terraform/workload/required_providers.tf new file mode 100644 index 00000000..dde72523 --- /dev/null +++ b/docs/terraform/example/terraform/workload/required_providers.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + cpln = { + source = "controlplane-com/cpln" + version = "~> 1.0" + } + } +} diff --git a/docs/terraform/example/terraform/workload/variables.tf b/docs/terraform/example/terraform/workload/variables.tf new file mode 100644 index 00000000..0556c1ed --- /dev/null +++ b/docs/terraform/example/terraform/workload/variables.tf @@ -0,0 +1,263 @@ +variable "containers" { + type = map( + object({ + args = optional(list(string)) + command = optional(string) + cpu = optional(string, "1000m") + envs = optional(map(string)) + image = string + inherit_env = optional(bool) + liveness_probe = optional( + object({ + exec = optional( + object({ + command = list(string) + }) + ) + failure_threshold = optional(number) + grpc = optional( + object({ + port = optional(number) + }) + ) + http_get = optional( + object({ + http_headers = optional(map(string)) + path = optional(string) + port = optional(number) + scheme = optional(string) + }) + ) + initial_delay_seconds = optional(number) + period_seconds = optional(number) + success_threshold = optional(number) + timeout_seconds = optional(number) + tcp_socket = optional( + object({ + port = optional(number) + }) + ) + }) + ) + memory = optional(string, "2048Mi") + ports = optional( + list( + object({ + number = number + protocol = string + }) + ), + [], + ) + post_start_command = optional(string) + pre_stop_command = optional(string) + readiness_probe = optional( + object({ + exec = optional( + object({ + command = list(string) + }) + ) + failure_threshold = optional(number) + grpc = optional( + object({ + port = optional(number) + }) + ) + http_get = optional( + object({ + http_headers = optional(map(string)) + path = optional(string) + port = optional(number) + scheme = optional(string) + }) + ) + initial_delay_seconds = optional(number) + period_seconds = optional(number) + success_threshold = optional(number) + timeout_seconds = optional(number) + tcp_socket = optional( + object({ + port = optional(number) + }) + ) + }) + ) + volumes = optional( + list( + object({ + path = string + uri = string + }) + ), + [], + ) + }) + ) +} + +variable "description" { + type = string + default = null +} + +variable "firewall_spec" { + type = object({ + external = optional( + object({ + inbound_allow_cidr = optional(list(string)) + outbound_allow_hostname = optional(list(string)) + outbound_allow_cidr = optional(list(string)) + outbound_allow_port = optional( + list( + object({ + number = number + protocol = optional(string, "tcp") + }) + ), + [] + ) + }) + ) + internal = optional( + object({ + inbound_allow_type = optional(string) + inbound_allow_workload = optional(list(string)) + }), + ) + }) + default = null +} + +variable "gvc" { + type = string +} + +variable "identity_link" { + type = string + default = null +} + +variable "job" { + type = object({ + active_deadline_seconds = optional(number) + concurrency_policy = optional(string, "Forbid") + history_limit = optional(number, 5) + restart_policy = optional(string, "Never") + schedule = string + }) + default = null +} + +variable "load_balancer" { + type = object({ + direct = optional( + object({ + enabled = number + port = optional( + list( + object({ + container_port = optional(number) + external_port = number + protocol = string + scheme = optional(string) + }) + ), + [] + ) + }) + ) + geo_location = optional( + object({ + enabled = optional(bool) + headers = optional( + object({ + asn = optional(string) + city = optional(string) + country = optional(string) + region = optional(string) + }) + ) + }) + ) + }) + default = null +} + +variable "local_options" { + type = object({ + autoscaling = optional( + object({ + metric = optional(string) + metric_percentile = optional(string) + target = optional(number) + max_scale = optional(number) + min_scale = optional(number) + scale_to_zero_delay = optional(number) + max_concurrency = optional(number) + }) + ) + location = string + capacity_ai = optional(bool, true) + debug = optional(bool, false) + suspend = optional(bool, false) + timeout_seconds = optional(number, 5) + }) + default = null +} + +variable "name" { + type = string +} + +variable "options" { + type = object({ + autoscaling = optional( + object({ + max_concurrency = optional(number) + max_scale = optional(number) + metric = optional(string) + metric_percentile = optional(string) + min_scale = optional(number) + scale_to_zero_delay = optional(number) + target = optional(number) + }) + ) + capacity_ai = optional(bool, true) + debug = optional(bool, false) + suspend = optional(bool, false) + timeout_seconds = optional(number, 5) + }) + default = null +} + +variable "rollout_options" { + type = object({ + max_surge_replicas = optional(string) + max_unavailable_replicas = optional(string) + min_ready_seconds = optional(number) + scaling_policy = optional(string, "OrderedReady") + }) + default = null +} + +variable "security_options" { + type = object({ + file_system_group_id = optional(number) + }) + default = null +} + +variable "support_dynamic_tags" { + type = bool + default = false +} + +variable "tags" { + type = map(string) + default = {} +} + +variable "type" { + type = string +} diff --git a/docs/terraform/overview.md b/docs/terraform/overview.md new file mode 100644 index 00000000..6dec49ca --- /dev/null +++ b/docs/terraform/overview.md @@ -0,0 +1,99 @@ +# Terraform + +### Overview + +You can manage your Control Plane (CPLN) configuration through `cpflow` commands and later invoke the generation of Terraform configuration files by running: + +```sh +cpflow terraform generate +``` + +This command will create Terraform configurations for each application defined in `controlplane.yml`, utilizing templates from the `templates` folder. + +Each time this command is invoked, Terraform configurations will be recreated, and the Terraform lock file will be preserved. + +You can continue working with CPLN configuration files in YAML format and simply transform them to Terraform format at any time. + +### Benefits of Using Terraform Over YAML Configs + +1. **State Management**: Terraform maintains a state file that tracks the current state of your infrastructure, making it easier to manage changes and updates. +2. **Dependency Management**: Terraform automatically handles dependencies between resources, ensuring that they are created or destroyed in the correct order. +3. **Multi-Cloud Support**: With Terraform, you can manage resources across multiple cloud providers seamlessly, allowing for a more flexible architecture. +4. **Plan and Apply**: Terraform provides a clear plan of what changes will be made before applying them, reducing the risk of unintended modifications. + +### Usage + +Suppose that you have CPLN configurations in YAML format for a Rails application with the following project structure (see the `example` folder): + +``` +.controlplane/ +├── templates/ +│ ├── app.yml -- GVC config +│ ├── postgres.yml -- Workload config for PostgreSQL +│ └── rails.yml -- Workload config for Rails +├── controlplane.yml -- Configs for overall application +``` + +To generate Terraform configurations, run the command below: + +```sh +cpflow terraform generate +``` + +Invoking this command will generate a new `terraform` folder with subfolders containing Terraform configurations for each application described in `controlplane.yml`: + +``` +.controlplane/ +├── templates/ +│ ├── app.yml -- GVC config +│ ├── postgres.yml -- Workload config for PostgreSQL +│ └── rails.yml -- Workload config for Rails +├── terraform/ +│ ├── rails-app-production/ -- Terraform configurations for production environment +│ │ ├── gvc.tf -- GVC config in HCL +│ │ ├── postgres.tf -- Postgres workload config in HCL +│ │ ├── postgres_envs.tf -- ENV variables for Postgres workload in HCL +│ │ ├── rails-app.tf -- Rails workload config in HCL +│ │ ├── rails_envs.tf -- ENV variables for Rails workload in HCL +│ │ ├── providers.tf -- Providers config in HCL +│ │ └── required_providers.tf -- Required providers config in HCL +│ ├── rails-app-staging/ -- Terraform configurations for staging environment +│ │ ├── gvc.tf -- GVC config in HCL +│ │ ├── postgres.tf -- Postgres workload config in HCL +│ │ ├── postgres_envs.tf -- ENV variables for Postgres workload in HCL +│ │ ├── rails-app.tf -- Rails workload config in HCL +│ │ ├── rails_envs.tf -- ENV variables for Rails workload in HCL +│ │ ├── providers.tf -- Providers config in HCL +│ │ └── required_providers.tf -- Required providers config in HCL +│ ├── workload/ -- Terraform configurations for workload module +│ │ ├── main.tf -- Main config for workload resource in HCL +│ │ ├── required_providers.tf -- Required providers for Terraform in HCL +│ │ └── variables.tf -- Variables used to create config for workload resource in HCL +├── controlplane.yml -- Configs for overall application +``` + +Each subfolder is a separate Terraform module, allowing you to manage the deployment of different environments of your application (e.g., `staging` and `production`). + +To deploy your application, follow these steps: + +1. Go to the application folder: + ```sh + cd terraform/rails-app-staging + ``` +2. Initialize the new Terraform working directory: + ```sh + terraform init + ``` +3. Generate the execution plan (what actions Terraform would take to apply the current configuration): + ```sh + terraform plan + ``` +4. Apply the planned changes: + ```sh + terraform apply + ``` + +### References + +- [Terraform Provider Plugin](https://shakadocs.controlplane.com/terraform/installation#terraform-provider-plugin) +- [Terraform - Control Plane Examples](https://github.com/controlplane-com/examples/tree/main/terraform) From ca901e0fc973c5b1e5a84a82db83d19d42f71f72 Mon Sep 17 00:00:00 2001 From: Zakir Dzhamaliddinov Date: Tue, 12 Nov 2024 19:06:06 +0300 Subject: [PATCH 5/7] Add description of TF generated files to TF overview docs --- docs/terraform/overview.md | 304 ++++++++++++++++++++++++++++++++++++- 1 file changed, 299 insertions(+), 5 deletions(-) diff --git a/docs/terraform/overview.md b/docs/terraform/overview.md index 6dec49ca..8dd7994d 100644 --- a/docs/terraform/overview.md +++ b/docs/terraform/overview.md @@ -1,6 +1,6 @@ # Terraform -### Overview +## Overview You can manage your Control Plane (CPLN) configuration through `cpflow` commands and later invoke the generation of Terraform configuration files by running: @@ -14,14 +14,14 @@ Each time this command is invoked, Terraform configurations will be recreated, a You can continue working with CPLN configuration files in YAML format and simply transform them to Terraform format at any time. -### Benefits of Using Terraform Over YAML Configs +## Benefits of Using Terraform Over YAML Configs 1. **State Management**: Terraform maintains a state file that tracks the current state of your infrastructure, making it easier to manage changes and updates. 2. **Dependency Management**: Terraform automatically handles dependencies between resources, ensuring that they are created or destroyed in the correct order. 3. **Multi-Cloud Support**: With Terraform, you can manage resources across multiple cloud providers seamlessly, allowing for a more flexible architecture. 4. **Plan and Apply**: Terraform provides a clear plan of what changes will be made before applying them, reducing the risk of unintended modifications. -### Usage +## Usage Suppose that you have CPLN configurations in YAML format for a Rails application with the following project structure (see the `example` folder): @@ -31,8 +31,152 @@ Suppose that you have CPLN configurations in YAML format for a Rails application │ ├── app.yml -- GVC config │ ├── postgres.yml -- Workload config for PostgreSQL │ └── rails.yml -- Workload config for Rails -├── controlplane.yml -- Configs for overall application +└── controlplane.yml -- Configs for overall application + +- **`controlplane.yml`** +```yaml +allow_org_override_by_env: true +allow_app_override_by_env: true + +aliases: + common: &common + cpln_org: my-org-staging + default_location: aws-us-east-2 + setup_app_templates: + - app + - postgres + - rails + one_off_workload: rails + app_workloads: + - rails + additional_workloads: + - postgres +apps: + rails-app-staging: + <<: *common + hooks: + post_creation: bundle exec rake db:prepare + pre_deletion: bundle exec rake db:drop + + rails-app-production: + <<: *common + allow_org_override_by_env: false + allow_app_override_by_env: false + cpln_org: my-org-production + upstream: rails-app-staging +``` +**Description**: This file defines the overall configuration for the Rails application, including organization settings, environment variables, and application-specific hooks for managing database tasks during deployment. + +- **`app.yml`** +```yaml +kind: gvc +name: {{APP_NAME}} +description: Global Virtual Cloud for Rails Application +spec: + env: + - name: DATABASE_URL + value: "postgres://user:password@postgres.{{APP_NAME}}.cpln.local:5432/{{APP_NAME}}" + - name: RAILS_ENV + value: production + - name: RAILS_SERVE_STATIC_FILES + value: "true" + staticPlacement: + locationLinks: + - {{APP_LOCATION_LINK}} + pullSecretLinks: + - "/org/org-name/secret/rails-app-secret" + loadBalancer: + dedicated: true + trustedProxies: 0 + +--- + +kind: identity +name: rails-app-identity +description: Identity for Rails Application +tags: + environment: production + +--- + +kind: secret +name: rails-app-secret +description: Secret for Rails Application +type: aws +data: + accessKey: 'AccessKeyExample' + secretKey: 'SecretKeyExample' + region: 'us-west-2' +``` +**Description**: This file defines the Global Virtual Cloud (GVC) configuration, including environment variables for the Rails application, identity settings, and AWS secrets for accessing resources. + +- **`postgres.yml`** +```yaml +kind: workload +name: postgres +spec: + type: standard + containers: + - name: postgres + cpu: 500m + env: + - name: POSTGRES_USER + value: "user" + - name: POSTGRES_PASSWORD + value: "password" + - name: POSTGRES_DB + value: "rails_app" + inheritEnv: true + image: "postgres:latest" + memory: 1Gi + ports: + - number: 5432 + protocol: tcp + defaultOptions: + autoscaling: + maxScale: 1 + capacityAI: false + firewallConfig: + external: + inboundAllowCIDR: + - 0.0.0.0/0 + outboundAllowCIDR: + - 0.0.0.0/0 ``` +**Description**: This file defines a workload resource for the PostgreSQL database, specifying the container image, CPU and memory allocation, environment variables, and firewall rules. + +- **`rails.yml`** +```yaml +kind: workload +name: rails +spec: + type: standard + containers: + - name: rails + cpu: 300m + env: + - name: LOG_LEVEL + value: debug + inheritEnv: true + image: "org-name/rails:latest" + memory: 512Mi + ports: + - number: 3000 + protocol: http + defaultOptions: + autoscaling: + maxScale: 1 + capacityAI: false + firewallConfig: + external: + inboundAllowCIDR: + - 0.0.0.0/0 + outboundAllowCIDR: + - 0.0.0.0/0 +``` +**Description**: This file defines a workload resource for the Rails application, including the container image, CPU and memory allocation, environment variables, and firewall rules. + +## Generating Terraform Configurations To generate Terraform configurations, run the command below: @@ -74,6 +218,156 @@ Invoking this command will generate a new `terraform` folder with subfolders con Each subfolder is a separate Terraform module, allowing you to manage the deployment of different environments of your application (e.g., `staging` and `production`). +Let's take a look at the Terraform configurations for the `rails-app-staging` application, along with descriptions for each generated file to help you understand their purpose. + +## Terraform Configurations for `rails-app-staging` + +- **`gvc.tf`** +```hcl +resource "cpln_gvc" "rails-app-staging" { + name = "rails-app-staging" + description = "Global Virtual Cloud for Rails Application" + locations = ["aws-us-east-2"] + pull_secrets = [cpln_secret.rails-app-secret.name] + env = { + DATABASE_URL = "postgres://user:password@postgres.rails-app-staging.cpln.local:5432/rails-app-staging" + RAILS_ENV = "production" + RAILS_SERVE_STATIC_FILES = "true" + } + load_balancer { + dedicated = true + trusted_proxies = 0 + } +} +``` +**Description**: This file defines a Global Virtual Cloud (GVC) resource for the Rails application. It specifies the name, description, and location of the GVC. The `pull_secrets` field links to the secret needed for accessing the cloud resources. The `env` block sets environment variables that the application will use, such as the database URL and Rails environment settings. The `load_balancer` block configures the load balancer settings for the application. + +- **`postgres.tf`** +```hcl +resource "cpln_workload" "postgres" { + name = "postgres" + type = "standard" + containers = { + postgres = { + image = "postgres:latest" + cpu = "500m" + memory = "1Gi" + envs = local.postgres_envs + ports = [ + { + number = 5432 + protocol = "tcp" + } + ] + } + } + options = { + autoscaling = { + max_scale = 1 + } + capacity_ai = false + } + firewall_spec = { + external = { + inbound_allow_cidr = [ + "0.0.0.0/0" + ] + outbound_allow_cidr = [ + "0.0.0.0/0" + ] + } + } +} +``` +**Description**: This file defines a workload resource for the PostgreSQL database. It specifies the container image to use, the amount of CPU and memory allocated, and the environment variables needed for the database. The `ports` section indicates that the database will listen on port 5432. The `options` block includes settings for autoscaling and capacity management. The `firewall_spec` section configures the firewall rules to allow inbound and outbound traffic. + +- **`postgres_envs.tf`** +```hcl +locals { + postgres_envs = { + POSTGRES_USER = "user" + POSTGRES_PASSWORD = "password" + POSTGRES_DB = "rails_app" + } +} +``` +**Description**: This file defines local variables for the PostgreSQL environment settings. It includes the database user, password, and the name of the database that the application will connect to. These variables are referenced in the `postgres.tf` file to configure the PostgreSQL container. + +- **`rails-app.tf`** +```hcl +module "rails" { + source = "../workload" + type = "standard" + name = "rails" + gvc = cpln_gvc.rails-app-staging.name + containers = { + rails = { + image = "org-name/rails:latest" + cpu = "300m" + memory = "512Mi" + inherit_env = true + envs = local.rails_envs + ports = [ + { + number = 3000 + protocol = "http" + } + ] + } + } + options = { + autoscaling = { + max_scale = 1 + } + capacity_ai = false + } + firewall_spec = { + external = { + inbound_allow_cidr = [ + "0.0.0.0/0" + ] + outbound_allow_cidr = [ + "0.0.0.0/0" + ] + } + } +} +``` +**Description**: This file defines a module for the Rails application workload. It specifies the source of the module, the type of workload, and the name of the application. The `gvc` field links the Rails application to the previously defined GVC. The `containers` block configures the Rails container, including the image to use, CPU and memory allocation, and environment variables. The `options` block includes settings for autoscaling and capacity management, while the `firewall_spec` section configures the firewall rules. + +- **`rails_envs.tf`** +```hcl +locals { + rails_envs = { + LOG_LEVEL = "debug" + } +} +``` +**Description**: This file defines local variables for the Rails application environment settings. It includes the log level for the application, which can be adjusted as needed. These variables are referenced in the `rails-app.tf` file to configure the Rails container. + +- **`providers.tf`** +```hcl +provider "cpln" { + org = "org-name-example" +} +``` +**Description**: This file specifies the provider configuration for the Control Plane. It includes the organization name that will be used to manage resources within the Control Plane. This is essential for authenticating and authorizing access to the resources defined in the Terraform configurations. + +- **`required_providers.tf`** +```hcl +terraform { + required_providers { + cpln = { + source = "controlplane-com/cpln" + version = "~> 1.0" + } + } +} +``` +**Description**: This file defines the required providers for the Terraform configuration. It specifies the Control Plane provider, including its source and version. This ensures that Terraform knows which provider to use when managing resources. + +## Application Deployment + To deploy your application, follow these steps: 1. Go to the application folder: @@ -93,7 +387,7 @@ To deploy your application, follow these steps: terraform apply ``` -### References +## References - [Terraform Provider Plugin](https://shakadocs.controlplane.com/terraform/installation#terraform-provider-plugin) - [Terraform - Control Plane Examples](https://github.com/controlplane-com/examples/tree/main/terraform) From 25b286d84bec7a354b338d6bca0b367ab2f80d11 Mon Sep 17 00:00:00 2001 From: Zakir Dzhamaliddinov Date: Tue, 19 Nov 2024 15:09:05 +0300 Subject: [PATCH 6/7] Add docs for terraform import command --- docs/terraform/overview.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/terraform/overview.md b/docs/terraform/overview.md index 8dd7994d..a74057b8 100644 --- a/docs/terraform/overview.md +++ b/docs/terraform/overview.md @@ -32,6 +32,7 @@ Suppose that you have CPLN configurations in YAML format for a Rails application │ ├── postgres.yml -- Workload config for PostgreSQL │ └── rails.yml -- Workload config for Rails └── controlplane.yml -- Configs for overall application +``` - **`controlplane.yml`** ```yaml @@ -387,6 +388,20 @@ To deploy your application, follow these steps: terraform apply ``` +## Importing Existing Infrastructure + +In addition to generating Terraform configurations, you can also import existing infrastructure into Terraform management using the `cpflow terraform import` command. This is useful when you have resources that were created outside of Terraform and you want to manage them using Terraform going forward. + +### Usage + +To import existing resources, run the following command: + +```sh +cpflow terraform import +``` + +This command will import resources defined in your `controlplane.yml` and `templates` folder into the Terraform state. + ## References - [Terraform Provider Plugin](https://shakadocs.controlplane.com/terraform/installation#terraform-provider-plugin) From 8e164d918965c25140efddbf2a6d5c968a5c443a Mon Sep 17 00:00:00 2001 From: Zakir Dzhamaliddinov Date: Thu, 28 Nov 2024 13:02:05 +0300 Subject: [PATCH 7/7] Simplify terraform docs --- docs/terraform/details.md | 27 +- .../example/.controlplane/templates/app.yml | 1 - .../example/.controlplane/templates/rails.yml | 2 +- .../terraform/rails-app-production/gvc.tf | 15 - .../rails-app-production/identities.tf | 8 - .../rails-app-production/postgres.tf | 37 -- .../rails-app-production/postgres_envs.tf | 7 - .../rails-app-production/providers.tf | 3 - .../rails-app-production/rails-app.tf | 37 -- .../rails-app-production/rails_envs.tf | 5 - .../required_providers.tf | 8 - .../terraform/rails-app-production/secrets.tf | 8 - .../terraform/rails-app-staging/gvc.tf | 15 - .../terraform/rails-app-staging/identities.tf | 8 - .../terraform/rails-app-staging/postgres.tf | 37 -- .../rails-app-staging/postgres_envs.tf | 7 - .../terraform/rails-app-staging/providers.tf | 3 - .../terraform/rails-app-staging/rails-app.tf | 37 -- .../terraform/rails-app-staging/rails_envs.tf | 5 - .../rails-app-staging/required_providers.tf | 8 - .../terraform/rails-app-staging/secrets.tf | 8 - .../example/terraform/workload/main.tf | 315 -------------- .../terraform/workload/required_providers.tf | 8 - .../example/terraform/workload/variables.tf | 263 ------------ docs/terraform/overview.md | 401 +++--------------- 25 files changed, 63 insertions(+), 1210 deletions(-) delete mode 100644 docs/terraform/example/terraform/rails-app-production/gvc.tf delete mode 100644 docs/terraform/example/terraform/rails-app-production/identities.tf delete mode 100644 docs/terraform/example/terraform/rails-app-production/postgres.tf delete mode 100644 docs/terraform/example/terraform/rails-app-production/postgres_envs.tf delete mode 100644 docs/terraform/example/terraform/rails-app-production/providers.tf delete mode 100644 docs/terraform/example/terraform/rails-app-production/rails-app.tf delete mode 100644 docs/terraform/example/terraform/rails-app-production/rails_envs.tf delete mode 100644 docs/terraform/example/terraform/rails-app-production/required_providers.tf delete mode 100644 docs/terraform/example/terraform/rails-app-production/secrets.tf delete mode 100644 docs/terraform/example/terraform/rails-app-staging/gvc.tf delete mode 100644 docs/terraform/example/terraform/rails-app-staging/identities.tf delete mode 100644 docs/terraform/example/terraform/rails-app-staging/postgres.tf delete mode 100644 docs/terraform/example/terraform/rails-app-staging/postgres_envs.tf delete mode 100644 docs/terraform/example/terraform/rails-app-staging/providers.tf delete mode 100644 docs/terraform/example/terraform/rails-app-staging/rails-app.tf delete mode 100644 docs/terraform/example/terraform/rails-app-staging/rails_envs.tf delete mode 100644 docs/terraform/example/terraform/rails-app-staging/required_providers.tf delete mode 100644 docs/terraform/example/terraform/rails-app-staging/secrets.tf delete mode 100644 docs/terraform/example/terraform/workload/main.tf delete mode 100644 docs/terraform/example/terraform/workload/required_providers.tf delete mode 100644 docs/terraform/example/terraform/workload/variables.tf diff --git a/docs/terraform/details.md b/docs/terraform/details.md index 56d42c74..624b3516 100644 --- a/docs/terraform/details.md +++ b/docs/terraform/details.md @@ -6,7 +6,7 @@ Terraform provider configurations are controlled via `required_providers.tf` and - **`required_providers.tf`** -```terraform +```hcl terraform { required_providers { cpln = { @@ -19,7 +19,7 @@ terraform { - **`providers.tf`** -```terraform +```hcl provider "cpln" { org = "org-name-example" } @@ -57,7 +57,7 @@ spec: Will transform to Terraform config: -```terraform +```hcl resource "cpln_gvc" "app-name" { name = "app-name" description = "app-description" @@ -95,7 +95,7 @@ tags: Will transform to Terraform config: -```terraform +```hcl resource "cpln_identity" "postgres-poc-identity" { name = "postgres-poc-identity" description = "postgres-poc-identity" @@ -126,7 +126,7 @@ data: Will transform to Terraform config: -```terraform +```hcl resource "cpln_secret" "aws" { name = "aws" description = "aws" @@ -155,7 +155,7 @@ data: Will transform to Terraform config: -```terraform +```hcl resource "cpln_secret" "azure-connector" { name = "azure-connector" description = "azure_connector" @@ -182,7 +182,7 @@ data: >- Will transform to Terraform config: -```terraform +```hcl resource "cpln_secret" "azure-sdk-secret" { name = "azure-sdk-secret" description = "azure-sdk-secret" @@ -204,7 +204,7 @@ data: Will transform to Terraform config: -```terraform +```hcl resource "cpln_secret" "dictionary" { name = "dictionary" description = "dictionary" @@ -249,7 +249,7 @@ bindings: Will be transformed to Terraform config: -```terraform +```hcl resource "cpln_policy" "policy-name" { name = "policy-name" description = "policy description" @@ -295,7 +295,7 @@ spec: Will be transformed to Terraform config: -```terraform +```hcl resource "cpln_volume_set" "postgres-poc-vs" { gvc = cpln_gvc.app-name.name name = "postgres-poc-vs" @@ -342,10 +342,8 @@ spec: capacityAI: false firewallConfig: external: - # Default to allow public access to Rails server inboundAllowCIDR: - 0.0.0.0/0 - # Could configure outbound for more security outboundAllowCIDR: - 0.0.0.0/0 ``` @@ -354,7 +352,7 @@ Will be transformed to Terraform configs: - **`rails.tf`** -```terraform +```hcl module "rails" { source = "../workload" type = "standard" @@ -398,12 +396,13 @@ Notice the `source: ../workload` line - there is a common `workload` module whic ``` workload/ ├── main.tf -- Configurable workload resource in HCL +├── required_providers.tf -- Required providers for Terraform in HCL ├── variables.tf -- Variables used to configure workload resource above ``` - **`rails_envs.tf`** -```terraform +```hcl locals { rails_envs = { LOG_LEVEL = "debug" diff --git a/docs/terraform/example/.controlplane/templates/app.yml b/docs/terraform/example/.controlplane/templates/app.yml index ad456853..132e94a6 100644 --- a/docs/terraform/example/.controlplane/templates/app.yml +++ b/docs/terraform/example/.controlplane/templates/app.yml @@ -36,4 +36,3 @@ data: accessKey: 'AccessKeyExample' secretKey: 'SecretKeyExample' region: 'us-west-2' - diff --git a/docs/terraform/example/.controlplane/templates/rails.yml b/docs/terraform/example/.controlplane/templates/rails.yml index 4f7c0394..83b56acf 100644 --- a/docs/terraform/example/.controlplane/templates/rails.yml +++ b/docs/terraform/example/.controlplane/templates/rails.yml @@ -9,7 +9,7 @@ spec: - name: LOG_LEVEL value: debug inheritEnv: true - image: "org-name/rails:latest" + image: {{APP_IMAGE_LINK}} memory: 512Mi ports: - number: 3000 diff --git a/docs/terraform/example/terraform/rails-app-production/gvc.tf b/docs/terraform/example/terraform/rails-app-production/gvc.tf deleted file mode 100644 index dfec8489..00000000 --- a/docs/terraform/example/terraform/rails-app-production/gvc.tf +++ /dev/null @@ -1,15 +0,0 @@ -resource "cpln_gvc" "rails-app-production" { - name = "rails-app-production" - description = "Global Virtual Cloud for Rails Application" - locations = ["aws-us-east-2"] - pull_secrets = [cpln_secret.rails-app-secret.name] - env = { - DATABASE_URL = "postgres://user:password@postgres.rails-app-production.cpln.local:5432/rails-app-production" - RAILS_ENV = "production" - RAILS_SERVE_STATIC_FILES = "true" - } - load_balancer { - dedicated = true - trusted_proxies = 0 - } -} diff --git a/docs/terraform/example/terraform/rails-app-production/identities.tf b/docs/terraform/example/terraform/rails-app-production/identities.tf deleted file mode 100644 index c5ad5383..00000000 --- a/docs/terraform/example/terraform/rails-app-production/identities.tf +++ /dev/null @@ -1,8 +0,0 @@ -resource "cpln_identity" "rails-app-identity" { - gvc = cpln_gvc.rails-app-production.name - name = "rails-app-identity" - description = "Identity for Rails Application" - tags = { - environment = "production" - } -} diff --git a/docs/terraform/example/terraform/rails-app-production/postgres.tf b/docs/terraform/example/terraform/rails-app-production/postgres.tf deleted file mode 100644 index 87603701..00000000 --- a/docs/terraform/example/terraform/rails-app-production/postgres.tf +++ /dev/null @@ -1,37 +0,0 @@ -module "postgres" { - source = "../workload" - type = "standard" - name = "postgres" - gvc = cpln_gvc.rails-app-production.name - containers = { - postgres: { - image: "postgres:latest", - cpu: "500m", - memory: "1Gi", - inherit_env: true, - envs: local.postgres_envs, - ports: [ - { - number: 5432, - protocol: "tcp" - } - ] - } - } - options = { - autoscaling: { - max_scale: 1 - } - capacity_ai: false - } - firewall_spec = { - external: { - inbound_allow_cidr: [ - "0.0.0.0/0" - ], - outbound_allow_cidr: [ - "0.0.0.0/0" - ] - } - } -} diff --git a/docs/terraform/example/terraform/rails-app-production/postgres_envs.tf b/docs/terraform/example/terraform/rails-app-production/postgres_envs.tf deleted file mode 100644 index 8ca5720b..00000000 --- a/docs/terraform/example/terraform/rails-app-production/postgres_envs.tf +++ /dev/null @@ -1,7 +0,0 @@ -locals { - postgres_envs = { - POSTGRES_USER = "user" - POSTGRES_PASSWORD = "password" - POSTGRES_DB = "rails_app" - } -} diff --git a/docs/terraform/example/terraform/rails-app-production/providers.tf b/docs/terraform/example/terraform/rails-app-production/providers.tf deleted file mode 100644 index 485ac8d4..00000000 --- a/docs/terraform/example/terraform/rails-app-production/providers.tf +++ /dev/null @@ -1,3 +0,0 @@ -provider "cpln" { - org = "org-name" -} diff --git a/docs/terraform/example/terraform/rails-app-production/rails-app.tf b/docs/terraform/example/terraform/rails-app-production/rails-app.tf deleted file mode 100644 index 6906156e..00000000 --- a/docs/terraform/example/terraform/rails-app-production/rails-app.tf +++ /dev/null @@ -1,37 +0,0 @@ -module "rails-app" { - source = "../workload" - type = "standard" - name = "rails-app" - gvc = cpln_gvc.rails-app-production.name - containers = { - rails: { - image: "org-name/rails-app:latest", - cpu: "300m", - memory: "512Mi", - inherit_env: true, - envs: local.rails_envs, - ports: [ - { - number: 3000, - protocol: "http" - } - ] - } - } - options = { - autoscaling: { - max_scale: 1 - } - capacity_ai: false - } - firewall_spec = { - external: { - inbound_allow_cidr: [ - "0.0.0.0/0" - ], - outbound_allow_cidr: [ - "0.0.0.0/0" - ] - } - } -} diff --git a/docs/terraform/example/terraform/rails-app-production/rails_envs.tf b/docs/terraform/example/terraform/rails-app-production/rails_envs.tf deleted file mode 100644 index b51a979e..00000000 --- a/docs/terraform/example/terraform/rails-app-production/rails_envs.tf +++ /dev/null @@ -1,5 +0,0 @@ -locals { - rails_envs = { - LOG_LEVEL = "debug" - } -} diff --git a/docs/terraform/example/terraform/rails-app-production/required_providers.tf b/docs/terraform/example/terraform/rails-app-production/required_providers.tf deleted file mode 100644 index dde72523..00000000 --- a/docs/terraform/example/terraform/rails-app-production/required_providers.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - cpln = { - source = "controlplane-com/cpln" - version = "~> 1.0" - } - } -} diff --git a/docs/terraform/example/terraform/rails-app-production/secrets.tf b/docs/terraform/example/terraform/rails-app-production/secrets.tf deleted file mode 100644 index 9c8eb4fa..00000000 --- a/docs/terraform/example/terraform/rails-app-production/secrets.tf +++ /dev/null @@ -1,8 +0,0 @@ -resource "cpln_secret" "rails-app-secret" { - name = "rails-app-secret" - description = "Secret for Rails Application" - aws { - secret_key = "SecretKeyExample" - access_key = "AccessKeyExample" - } -} diff --git a/docs/terraform/example/terraform/rails-app-staging/gvc.tf b/docs/terraform/example/terraform/rails-app-staging/gvc.tf deleted file mode 100644 index 88ae0427..00000000 --- a/docs/terraform/example/terraform/rails-app-staging/gvc.tf +++ /dev/null @@ -1,15 +0,0 @@ -resource "cpln_gvc" "rails-app-staging" { - name = "rails-app-staging" - description = "Global Virtual Cloud for Rails Application" - locations = ["aws-us-east-2"] - pull_secrets = [cpln_secret.rails-app-secret.name] - env = { - DATABASE_URL = "postgres://user:password@postgres.rails-app-staging.cpln.local:5432/rails-app-staging" - RAILS_ENV = "production" - RAILS_SERVE_STATIC_FILES = "true" - } - load_balancer { - dedicated = true - trusted_proxies = 0 - } -} diff --git a/docs/terraform/example/terraform/rails-app-staging/identities.tf b/docs/terraform/example/terraform/rails-app-staging/identities.tf deleted file mode 100644 index 483f3eb8..00000000 --- a/docs/terraform/example/terraform/rails-app-staging/identities.tf +++ /dev/null @@ -1,8 +0,0 @@ -resource "cpln_identity" "rails-app-identity" { - gvc = cpln_gvc.rails-app-staging.name - name = "rails-app-identity" - description = "Identity for Rails Application" - tags = { - environment = "production" - } -} diff --git a/docs/terraform/example/terraform/rails-app-staging/postgres.tf b/docs/terraform/example/terraform/rails-app-staging/postgres.tf deleted file mode 100644 index 52e8f839..00000000 --- a/docs/terraform/example/terraform/rails-app-staging/postgres.tf +++ /dev/null @@ -1,37 +0,0 @@ -module "postgres" { - source = "../workload" - type = "standard" - name = "postgres" - gvc = cpln_gvc.rails-app-staging.name - containers = { - postgres: { - image: "postgres:latest", - cpu: "500m", - memory: "1Gi", - inherit_env: true, - envs: local.postgres_envs, - ports: [ - { - number: 5432, - protocol: "tcp" - } - ] - } - } - options = { - autoscaling: { - max_scale: 1 - } - capacity_ai: false - } - firewall_spec = { - external: { - inbound_allow_cidr: [ - "0.0.0.0/0" - ], - outbound_allow_cidr: [ - "0.0.0.0/0" - ] - } - } -} diff --git a/docs/terraform/example/terraform/rails-app-staging/postgres_envs.tf b/docs/terraform/example/terraform/rails-app-staging/postgres_envs.tf deleted file mode 100644 index 8ca5720b..00000000 --- a/docs/terraform/example/terraform/rails-app-staging/postgres_envs.tf +++ /dev/null @@ -1,7 +0,0 @@ -locals { - postgres_envs = { - POSTGRES_USER = "user" - POSTGRES_PASSWORD = "password" - POSTGRES_DB = "rails_app" - } -} diff --git a/docs/terraform/example/terraform/rails-app-staging/providers.tf b/docs/terraform/example/terraform/rails-app-staging/providers.tf deleted file mode 100644 index 485ac8d4..00000000 --- a/docs/terraform/example/terraform/rails-app-staging/providers.tf +++ /dev/null @@ -1,3 +0,0 @@ -provider "cpln" { - org = "org-name" -} diff --git a/docs/terraform/example/terraform/rails-app-staging/rails-app.tf b/docs/terraform/example/terraform/rails-app-staging/rails-app.tf deleted file mode 100644 index 78ca0891..00000000 --- a/docs/terraform/example/terraform/rails-app-staging/rails-app.tf +++ /dev/null @@ -1,37 +0,0 @@ -module "rails-app" { - source = "../workload" - type = "standard" - name = "rails-app" - gvc = cpln_gvc.rails-app-staging.name - containers = { - rails: { - image: "org-name/rails-app:latest", - cpu: "300m", - memory: "512Mi", - inherit_env: true, - envs: local.rails_envs, - ports: [ - { - number: 3000, - protocol: "http" - } - ] - } - } - options = { - autoscaling: { - max_scale: 1 - } - capacity_ai: false - } - firewall_spec = { - external: { - inbound_allow_cidr: [ - "0.0.0.0/0" - ], - outbound_allow_cidr: [ - "0.0.0.0/0" - ] - } - } -} diff --git a/docs/terraform/example/terraform/rails-app-staging/rails_envs.tf b/docs/terraform/example/terraform/rails-app-staging/rails_envs.tf deleted file mode 100644 index b51a979e..00000000 --- a/docs/terraform/example/terraform/rails-app-staging/rails_envs.tf +++ /dev/null @@ -1,5 +0,0 @@ -locals { - rails_envs = { - LOG_LEVEL = "debug" - } -} diff --git a/docs/terraform/example/terraform/rails-app-staging/required_providers.tf b/docs/terraform/example/terraform/rails-app-staging/required_providers.tf deleted file mode 100644 index dde72523..00000000 --- a/docs/terraform/example/terraform/rails-app-staging/required_providers.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - cpln = { - source = "controlplane-com/cpln" - version = "~> 1.0" - } - } -} diff --git a/docs/terraform/example/terraform/rails-app-staging/secrets.tf b/docs/terraform/example/terraform/rails-app-staging/secrets.tf deleted file mode 100644 index 9c8eb4fa..00000000 --- a/docs/terraform/example/terraform/rails-app-staging/secrets.tf +++ /dev/null @@ -1,8 +0,0 @@ -resource "cpln_secret" "rails-app-secret" { - name = "rails-app-secret" - description = "Secret for Rails Application" - aws { - secret_key = "SecretKeyExample" - access_key = "AccessKeyExample" - } -} diff --git a/docs/terraform/example/terraform/workload/main.tf b/docs/terraform/example/terraform/workload/main.tf deleted file mode 100644 index 36ef508b..00000000 --- a/docs/terraform/example/terraform/workload/main.tf +++ /dev/null @@ -1,315 +0,0 @@ -resource "cpln_workload" "workload" { - type = var.type - - gvc = var.gvc - identity_link = var.identity_link - - name = var.name - description = var.description - - tags = var.tags - support_dynamic_tags = var.support_dynamic_tags - - dynamic "container" { - for_each = var.containers - iterator = container - content { - name = container.key - - args = container.value.args - command = container.value.command - env = container.value.envs - image = container.value.image - - cpu = container.value.cpu - memory = container.value.memory - - dynamic "lifecycle" { - for_each = container.value.post_start_command != null || container.value.pre_stop_command != null ? [1] : [] - content { - dynamic "post_start" { - for_each = container.value.post_start_command != null ? [1] : [] - content { - exec { - command = [ - "/bin/bash", - "-c", - "[ -f ${container.value.post_start_command} ] && ${container.value.post_start_command} || true", - ] - } - } - } - dynamic "pre_stop" { - for_each = container.value.pre_stop_command != null ? [1] : [] - content { - exec { - command = [ - "/bin/bash", - "-c", - "[ -f ${container.value.pre_stop_command} ] && ${container.value.pre_stop_command} || true", - ] - } - } - } - } - } - - dynamic "liveness_probe" { - for_each = container.value.liveness_probe != null ? [container.value.liveness_probe] : [] - iterator = liveness - content { - dynamic "exec" { - for_each = liveness.value.exec != null ? [liveness.value.exec] : [] - iterator = exec - content { - command = exec.value.command - } - } - dynamic "http_get" { - for_each = liveness.value.http_get != null ? [liveness.value.http_get] : [] - iterator = http_get - content { - path = http_get.value.path - port = http_get.value.port - scheme = http_get.value.scheme - http_headers = http_get.value.http_headers - } - } - dynamic "tcp_socket" { - for_each = liveness.value.tcp_socket != null ? [liveness.value.tcp_socket] : [] - iterator = tcp_socket - content { - port = tcp_socket.value.port - } - } - dynamic "grpc" { - for_each = liveness.value.grpc != null ? [liveness.value.grpc] : [] - iterator = grpc - content { - port = grpc.value.port - } - } - failure_threshold = liveness.value.failure_threshold - initial_delay_seconds = liveness.value.initial_delay_seconds - period_seconds = liveness.value.period_seconds - success_threshold = liveness.value.success_threshold - timeout_seconds = liveness.value.timeout_seconds - } - } - - dynamic "readiness_probe" { - for_each = container.value.readiness_probe != null ? [container.value.readiness_probe] : [] - iterator = readiness - content { - dynamic "exec" { - for_each = readiness.value.exec != null ? [readiness.value.exec] : [] - iterator = exec - content { - command = exec.value.command - } - } - dynamic "http_get" { - for_each = readiness.value.http_get != null ? [readiness.value.http_get] : [] - iterator = http_get - content { - path = http_get.value.path - port = http_get.value.port - scheme = http_get.value.scheme - http_headers = http_get.value.http_headers - } - } - dynamic "tcp_socket" { - for_each = readiness.value.tcp_socket != null ? [readiness.value.tcp_socket] : [] - iterator = tcp_socket - content { - port = tcp_socket.value.port - } - } - dynamic "grpc" { - for_each = readiness.value.grpc != null ? [readiness.value.grpc] : [] - iterator = grpc - content { - port = grpc.value.port - } - } - failure_threshold = readiness.value.failure_threshold - initial_delay_seconds = readiness.value.initial_delay_seconds - period_seconds = readiness.value.period_seconds - success_threshold = readiness.value.success_threshold - timeout_seconds = readiness.value.timeout_seconds - } - } - - dynamic "ports" { - for_each = container.value.ports - iterator = port - content { - number = port.value.number - protocol = port.value.protocol - } - } - - dynamic "volume" { - for_each = container.value.volumes - iterator = volume - content { - uri = volume.value.uri - path = volume.value.path - } - } - } - } - - dynamic "options" { - for_each = var.options != null ? [var.options] : [] - iterator = options - content { - dynamic "autoscaling" { - for_each = options.value.autoscaling != null ? [options.value.autoscaling] : [] - iterator = autoscaling - content { - metric = autoscaling.value.metric - metric_percentile = autoscaling.value.metric_percentile - max_scale = autoscaling.value.max_scale - min_scale = autoscaling.value.min_scale - target = autoscaling.value.target - scale_to_zero_delay = autoscaling.value.scale_to_zero_delay - max_concurrency = autoscaling.value.max_concurrency - } - } - capacity_ai = options.value.capacity_ai - suspend = options.value.suspend - timeout_seconds = options.value.timeout_seconds - debug = options.value.debug - } - } - - dynamic "local_options" { - for_each = var.local_options != null ? [var.local_options] : [] - iterator = options - content { - dynamic "autoscaling" { - for_each = options.value.autoscaling != null ? [options.value.autoscaling] : [] - iterator = autoscaling - content { - metric = autoscaling.value.metric - metric_percentile = autoscaling.value.metric_percentile - max_scale = autoscaling.value.max_scale - min_scale = autoscaling.value.min_scale - target = autoscaling.value.target - scale_to_zero_delay = autoscaling.value.scale_to_zero_delay - max_concurrency = autoscaling.value.max_concurrency - } - } - location = options.value.location - capacity_ai = options.value.capacity_ai - suspend = options.value.suspend - timeout_seconds = options.value.timeout_seconds - debug = options.value.debug - } - } - - dynamic "rollout_options" { - for_each = var.rollout_options != null ? [var.rollout_options] : [] - iterator = rollout_options - content { - min_ready_seconds = rollout_options.value.min_ready_seconds - max_unavailable_replicas = rollout_options.value.max_unavailable_replicas - max_surge_replicas = rollout_options.value.max_surge_replicas - scaling_policy = rollout_options.value.scaling_policy - } - } - - dynamic "security_options" { - for_each = var.security_options != null ? [var.security_options] : [] - iterator = security_options - content { - file_system_group_id = security_options.value.file_system_group_id - } - } - - dynamic "firewall_spec" { - for_each = var.firewall_spec != null ? [var.firewall_spec] : [] - iterator = firewall_spec - content { - dynamic "external" { - for_each = firewall_spec.value.external != null ? [firewall_spec.value.external] : [] - iterator = external - content { - inbound_allow_cidr = external.value.inbound_allow_cidr - outbound_allow_hostname = external.value.outbound_allow_hostname - outbound_allow_cidr = external.value.outbound_allow_cidr - dynamic "outbound_allow_port" { - for_each = external.value.outbound_allow_port - iterator = outbound_allow_port - content { - protocol = outbound_allow_port.value.protocol - number = outbound_allow_port.value.number - } - } - } - } - dynamic "internal" { - for_each = firewall_spec.value.internal != null ? [firewall_spec.value.internal] : [] - iterator = internal - content { - inbound_allow_type = internal.value.inbound_allow_type - inbound_allow_workload = internal.value.inbound_allow_workload - } - } - } - } - - dynamic "load_balancer" { - for_each = var.load_balancer != null ? [var.load_balancer] : [] - iterator = load_balancer - content { - dynamic "direct" { - for_each = load_balancer.value.direct != null ? [load_balancer.value.direct] : [] - iterator = direct - content { - enabled = direct.value.enabled - dynamic "port" { - for_each = direct.value.port - iterator = port - content { - external_port = port.value.external_port - protocol = port.value.protocol - scheme = port.value.scheme - container_port = port.value.container_port - } - } - } - } - dynamic "geo_location" { - for_each = load_balancer.value.geo_location != null ? [load_balancer.value.geo_location] : [] - iterator = geo_location - content { - enabled = geo_location.value.enabled - dynamic "headers" { - for_each = geo_location.value.headers != null ? [geo_location.value.headers] : [] - iterator = headers - content { - asn = headers.value.asn - city = headers.value.city - country = headers.value.country - region = headers.value.region - } - } - } - } - } - } - - dynamic "job" { - for_each = var.job != null ? [var.job] : [] - iterator = job - content { - schedule = job.value.schedule - concurrency_policy = job.value.concurrency_policy - history_limit = job.value.history_limit - restart_policy = job.value.restart_policy - active_deadline_seconds = job.value.active_deadline_seconds - } - } -} diff --git a/docs/terraform/example/terraform/workload/required_providers.tf b/docs/terraform/example/terraform/workload/required_providers.tf deleted file mode 100644 index dde72523..00000000 --- a/docs/terraform/example/terraform/workload/required_providers.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - cpln = { - source = "controlplane-com/cpln" - version = "~> 1.0" - } - } -} diff --git a/docs/terraform/example/terraform/workload/variables.tf b/docs/terraform/example/terraform/workload/variables.tf deleted file mode 100644 index 0556c1ed..00000000 --- a/docs/terraform/example/terraform/workload/variables.tf +++ /dev/null @@ -1,263 +0,0 @@ -variable "containers" { - type = map( - object({ - args = optional(list(string)) - command = optional(string) - cpu = optional(string, "1000m") - envs = optional(map(string)) - image = string - inherit_env = optional(bool) - liveness_probe = optional( - object({ - exec = optional( - object({ - command = list(string) - }) - ) - failure_threshold = optional(number) - grpc = optional( - object({ - port = optional(number) - }) - ) - http_get = optional( - object({ - http_headers = optional(map(string)) - path = optional(string) - port = optional(number) - scheme = optional(string) - }) - ) - initial_delay_seconds = optional(number) - period_seconds = optional(number) - success_threshold = optional(number) - timeout_seconds = optional(number) - tcp_socket = optional( - object({ - port = optional(number) - }) - ) - }) - ) - memory = optional(string, "2048Mi") - ports = optional( - list( - object({ - number = number - protocol = string - }) - ), - [], - ) - post_start_command = optional(string) - pre_stop_command = optional(string) - readiness_probe = optional( - object({ - exec = optional( - object({ - command = list(string) - }) - ) - failure_threshold = optional(number) - grpc = optional( - object({ - port = optional(number) - }) - ) - http_get = optional( - object({ - http_headers = optional(map(string)) - path = optional(string) - port = optional(number) - scheme = optional(string) - }) - ) - initial_delay_seconds = optional(number) - period_seconds = optional(number) - success_threshold = optional(number) - timeout_seconds = optional(number) - tcp_socket = optional( - object({ - port = optional(number) - }) - ) - }) - ) - volumes = optional( - list( - object({ - path = string - uri = string - }) - ), - [], - ) - }) - ) -} - -variable "description" { - type = string - default = null -} - -variable "firewall_spec" { - type = object({ - external = optional( - object({ - inbound_allow_cidr = optional(list(string)) - outbound_allow_hostname = optional(list(string)) - outbound_allow_cidr = optional(list(string)) - outbound_allow_port = optional( - list( - object({ - number = number - protocol = optional(string, "tcp") - }) - ), - [] - ) - }) - ) - internal = optional( - object({ - inbound_allow_type = optional(string) - inbound_allow_workload = optional(list(string)) - }), - ) - }) - default = null -} - -variable "gvc" { - type = string -} - -variable "identity_link" { - type = string - default = null -} - -variable "job" { - type = object({ - active_deadline_seconds = optional(number) - concurrency_policy = optional(string, "Forbid") - history_limit = optional(number, 5) - restart_policy = optional(string, "Never") - schedule = string - }) - default = null -} - -variable "load_balancer" { - type = object({ - direct = optional( - object({ - enabled = number - port = optional( - list( - object({ - container_port = optional(number) - external_port = number - protocol = string - scheme = optional(string) - }) - ), - [] - ) - }) - ) - geo_location = optional( - object({ - enabled = optional(bool) - headers = optional( - object({ - asn = optional(string) - city = optional(string) - country = optional(string) - region = optional(string) - }) - ) - }) - ) - }) - default = null -} - -variable "local_options" { - type = object({ - autoscaling = optional( - object({ - metric = optional(string) - metric_percentile = optional(string) - target = optional(number) - max_scale = optional(number) - min_scale = optional(number) - scale_to_zero_delay = optional(number) - max_concurrency = optional(number) - }) - ) - location = string - capacity_ai = optional(bool, true) - debug = optional(bool, false) - suspend = optional(bool, false) - timeout_seconds = optional(number, 5) - }) - default = null -} - -variable "name" { - type = string -} - -variable "options" { - type = object({ - autoscaling = optional( - object({ - max_concurrency = optional(number) - max_scale = optional(number) - metric = optional(string) - metric_percentile = optional(string) - min_scale = optional(number) - scale_to_zero_delay = optional(number) - target = optional(number) - }) - ) - capacity_ai = optional(bool, true) - debug = optional(bool, false) - suspend = optional(bool, false) - timeout_seconds = optional(number, 5) - }) - default = null -} - -variable "rollout_options" { - type = object({ - max_surge_replicas = optional(string) - max_unavailable_replicas = optional(string) - min_ready_seconds = optional(number) - scaling_policy = optional(string, "OrderedReady") - }) - default = null -} - -variable "security_options" { - type = object({ - file_system_group_id = optional(number) - }) - default = null -} - -variable "support_dynamic_tags" { - type = bool - default = false -} - -variable "tags" { - type = map(string) - default = {} -} - -variable "type" { - type = string -} diff --git a/docs/terraform/overview.md b/docs/terraform/overview.md index a74057b8..02878d94 100644 --- a/docs/terraform/overview.md +++ b/docs/terraform/overview.md @@ -2,17 +2,11 @@ ## Overview -You can manage your Control Plane (CPLN) configuration through `cpflow` commands and later invoke the generation of Terraform configuration files by running: +The Terraform feature in this project allows you to manage your Control Plane (CPLN) configurations using Terraform by: +1. Generating Terraform configuration files from existing CPLN YAML configuration files +2. Easily importing existing infrastructure into Terraform management -```sh -cpflow terraform generate -``` - -This command will create Terraform configurations for each application defined in `controlplane.yml`, utilizing templates from the `templates` folder. - -Each time this command is invoked, Terraform configurations will be recreated, and the Terraform lock file will be preserved. - -You can continue working with CPLN configuration files in YAML format and simply transform them to Terraform format at any time. +You can continue working with CPLN configuration files in YAML format and start using Terraform at any time. ## Benefits of Using Terraform Over YAML Configs @@ -23,7 +17,7 @@ You can continue working with CPLN configuration files in YAML format and simply ## Usage -Suppose that you have CPLN configurations in YAML format for a Rails application with the following project structure (see the `example` folder): +Let's take a look at how to deploy a [simple Rails application](example/.controlplane/controlplane.yml) on CPLN using Terraform: ``` .controlplane/ @@ -34,152 +28,9 @@ Suppose that you have CPLN configurations in YAML format for a Rails application └── controlplane.yml -- Configs for overall application ``` -- **`controlplane.yml`** -```yaml -allow_org_override_by_env: true -allow_app_override_by_env: true - -aliases: - common: &common - cpln_org: my-org-staging - default_location: aws-us-east-2 - setup_app_templates: - - app - - postgres - - rails - one_off_workload: rails - app_workloads: - - rails - additional_workloads: - - postgres -apps: - rails-app-staging: - <<: *common - hooks: - post_creation: bundle exec rake db:prepare - pre_deletion: bundle exec rake db:drop - - rails-app-production: - <<: *common - allow_org_override_by_env: false - allow_app_override_by_env: false - cpln_org: my-org-production - upstream: rails-app-staging -``` -**Description**: This file defines the overall configuration for the Rails application, including organization settings, environment variables, and application-specific hooks for managing database tasks during deployment. - -- **`app.yml`** -```yaml -kind: gvc -name: {{APP_NAME}} -description: Global Virtual Cloud for Rails Application -spec: - env: - - name: DATABASE_URL - value: "postgres://user:password@postgres.{{APP_NAME}}.cpln.local:5432/{{APP_NAME}}" - - name: RAILS_ENV - value: production - - name: RAILS_SERVE_STATIC_FILES - value: "true" - staticPlacement: - locationLinks: - - {{APP_LOCATION_LINK}} - pullSecretLinks: - - "/org/org-name/secret/rails-app-secret" - loadBalancer: - dedicated: true - trustedProxies: 0 - ---- +### Generating Terraform configurations -kind: identity -name: rails-app-identity -description: Identity for Rails Application -tags: - environment: production - ---- - -kind: secret -name: rails-app-secret -description: Secret for Rails Application -type: aws -data: - accessKey: 'AccessKeyExample' - secretKey: 'SecretKeyExample' - region: 'us-west-2' -``` -**Description**: This file defines the Global Virtual Cloud (GVC) configuration, including environment variables for the Rails application, identity settings, and AWS secrets for accessing resources. - -- **`postgres.yml`** -```yaml -kind: workload -name: postgres -spec: - type: standard - containers: - - name: postgres - cpu: 500m - env: - - name: POSTGRES_USER - value: "user" - - name: POSTGRES_PASSWORD - value: "password" - - name: POSTGRES_DB - value: "rails_app" - inheritEnv: true - image: "postgres:latest" - memory: 1Gi - ports: - - number: 5432 - protocol: tcp - defaultOptions: - autoscaling: - maxScale: 1 - capacityAI: false - firewallConfig: - external: - inboundAllowCIDR: - - 0.0.0.0/0 - outboundAllowCIDR: - - 0.0.0.0/0 -``` -**Description**: This file defines a workload resource for the PostgreSQL database, specifying the container image, CPU and memory allocation, environment variables, and firewall rules. - -- **`rails.yml`** -```yaml -kind: workload -name: rails -spec: - type: standard - containers: - - name: rails - cpu: 300m - env: - - name: LOG_LEVEL - value: debug - inheritEnv: true - image: "org-name/rails:latest" - memory: 512Mi - ports: - - number: 3000 - protocol: http - defaultOptions: - autoscaling: - maxScale: 1 - capacityAI: false - firewallConfig: - external: - inboundAllowCIDR: - - 0.0.0.0/0 - outboundAllowCIDR: - - 0.0.0.0/0 -``` -**Description**: This file defines a workload resource for the Rails application, including the container image, CPU and memory allocation, environment variables, and firewall rules. - -## Generating Terraform Configurations - -To generate Terraform configurations, run the command below: +To generate Terraform configurations, run the following command from the project root: ```sh cpflow terraform generate @@ -188,219 +39,65 @@ cpflow terraform generate Invoking this command will generate a new `terraform` folder with subfolders containing Terraform configurations for each application described in `controlplane.yml`: ``` -.controlplane/ -├── templates/ -│ ├── app.yml -- GVC config -│ ├── postgres.yml -- Workload config for PostgreSQL -│ └── rails.yml -- Workload config for Rails -├── terraform/ -│ ├── rails-app-production/ -- Terraform configurations for production environment -│ │ ├── gvc.tf -- GVC config in HCL -│ │ ├── postgres.tf -- Postgres workload config in HCL -│ │ ├── postgres_envs.tf -- ENV variables for Postgres workload in HCL -│ │ ├── rails-app.tf -- Rails workload config in HCL -│ │ ├── rails_envs.tf -- ENV variables for Rails workload in HCL -│ │ ├── providers.tf -- Providers config in HCL -│ │ └── required_providers.tf -- Required providers config in HCL -│ ├── rails-app-staging/ -- Terraform configurations for staging environment -│ │ ├── gvc.tf -- GVC config in HCL -│ │ ├── postgres.tf -- Postgres workload config in HCL -│ │ ├── postgres_envs.tf -- ENV variables for Postgres workload in HCL -│ │ ├── rails-app.tf -- Rails workload config in HCL -│ │ ├── rails_envs.tf -- ENV variables for Rails workload in HCL -│ │ ├── providers.tf -- Providers config in HCL -│ │ └── required_providers.tf -- Required providers config in HCL -│ ├── workload/ -- Terraform configurations for workload module -│ │ ├── main.tf -- Main config for workload resource in HCL -│ │ ├── required_providers.tf -- Required providers for Terraform in HCL -│ │ └── variables.tf -- Variables used to create config for workload resource in HCL -├── controlplane.yml -- Configs for overall application -``` - -Each subfolder is a separate Terraform module, allowing you to manage the deployment of different environments of your application (e.g., `staging` and `production`). +terraform/ +├── rails-app-production/ -- Terraform configurations for production environment +│ ├── gvc.tf -- GVC config in HCL +│ ├── identities.tf -- Identities config in HCL +│ ├── postgres.tf -- Postgres workload config in HCL +│ ├── postgres_envs.tf -- ENV variables for Postgres workload in HCL +│ ├── providers.tf -- Providers config in HCL +│ ├── rails.tf -- Rails workload config in HCL +│ ├── rails_envs.tf -- ENV variables for Rails workload in HCL +│ ├── required_providers.tf -- Required providers config in HCL +│ └── secrets.tf -- Secrets config in HCL +├── rails-app-staging/ -- Terraform configurations for staging environment +│ ├── gvc.tf -- GVC config in HCL +│ ├── identities.tf -- Identities config in HCL +│ ├── postgres.tf -- Postgres workload config in HCL +│ ├── postgres_envs.tf -- ENV variables for Postgres workload in HCL +│ ├── providers.tf -- Providers config in HCL +│ ├── rails.tf -- Rails workload config in HCL +│ ├── rails_envs.tf -- ENV variables for Rails workload in HCL +│ ├── required_providers.tf -- Required providers config in HCL +│ └── secrets.tf -- Secrets config in HCL +├── workload/ -- Terraform configurations for workload module +│ ├── main.tf -- Main config for workload resource in HCL +│ ├── required_providers.tf -- Required providers for Terraform in HCL +│ └── variables.tf -- Variables used to create config for workload resource in HCL +``` + +### Importing existing infrastructure + +Now we need to import existing infrastructure into Terraform management because some resources can already exist on CPLN and Terraform needs to know about this: -Let's take a look at the Terraform configurations for the `rails-app-staging` application, along with descriptions for each generated file to help you understand their purpose. - -## Terraform Configurations for `rails-app-staging` - -- **`gvc.tf`** -```hcl -resource "cpln_gvc" "rails-app-staging" { - name = "rails-app-staging" - description = "Global Virtual Cloud for Rails Application" - locations = ["aws-us-east-2"] - pull_secrets = [cpln_secret.rails-app-secret.name] - env = { - DATABASE_URL = "postgres://user:password@postgres.rails-app-staging.cpln.local:5432/rails-app-staging" - RAILS_ENV = "production" - RAILS_SERVE_STATIC_FILES = "true" - } - load_balancer { - dedicated = true - trusted_proxies = 0 - } -} -``` -**Description**: This file defines a Global Virtual Cloud (GVC) resource for the Rails application. It specifies the name, description, and location of the GVC. The `pull_secrets` field links to the secret needed for accessing the cloud resources. The `env` block sets environment variables that the application will use, such as the database URL and Rails environment settings. The `load_balancer` block configures the load balancer settings for the application. - -- **`postgres.tf`** -```hcl -resource "cpln_workload" "postgres" { - name = "postgres" - type = "standard" - containers = { - postgres = { - image = "postgres:latest" - cpu = "500m" - memory = "1Gi" - envs = local.postgres_envs - ports = [ - { - number = 5432 - protocol = "tcp" - } - ] - } - } - options = { - autoscaling = { - max_scale = 1 - } - capacity_ai = false - } - firewall_spec = { - external = { - inbound_allow_cidr = [ - "0.0.0.0/0" - ] - outbound_allow_cidr = [ - "0.0.0.0/0" - ] - } - } -} -``` -**Description**: This file defines a workload resource for the PostgreSQL database. It specifies the container image to use, the amount of CPU and memory allocated, and the environment variables needed for the database. The `ports` section indicates that the database will listen on port 5432. The `options` block includes settings for autoscaling and capacity management. The `firewall_spec` section configures the firewall rules to allow inbound and outbound traffic. - -- **`postgres_envs.tf`** -```hcl -locals { - postgres_envs = { - POSTGRES_USER = "user" - POSTGRES_PASSWORD = "password" - POSTGRES_DB = "rails_app" - } -} -``` -**Description**: This file defines local variables for the PostgreSQL environment settings. It includes the database user, password, and the name of the database that the application will connect to. These variables are referenced in the `postgres.tf` file to configure the PostgreSQL container. - -- **`rails-app.tf`** -```hcl -module "rails" { - source = "../workload" - type = "standard" - name = "rails" - gvc = cpln_gvc.rails-app-staging.name - containers = { - rails = { - image = "org-name/rails:latest" - cpu = "300m" - memory = "512Mi" - inherit_env = true - envs = local.rails_envs - ports = [ - { - number = 3000 - protocol = "http" - } - ] - } - } - options = { - autoscaling = { - max_scale = 1 - } - capacity_ai = false - } - firewall_spec = { - external = { - inbound_allow_cidr = [ - "0.0.0.0/0" - ] - outbound_allow_cidr = [ - "0.0.0.0/0" - ] - } - } -} -``` -**Description**: This file defines a module for the Rails application workload. It specifies the source of the module, the type of workload, and the name of the application. The `gvc` field links the Rails application to the previously defined GVC. The `containers` block configures the Rails container, including the image to use, CPU and memory allocation, and environment variables. The `options` block includes settings for autoscaling and capacity management, while the `firewall_spec` section configures the firewall rules. - -- **`rails_envs.tf`** -```hcl -locals { - rails_envs = { - LOG_LEVEL = "debug" - } -} +```sh +cpflow terraform import ``` -**Description**: This file defines local variables for the Rails application environment settings. It includes the log level for the application, which can be adjusted as needed. These variables are referenced in the `rails-app.tf` file to configure the Rails container. -- **`providers.tf`** -```hcl -provider "cpln" { - org = "org-name-example" -} -``` -**Description**: This file specifies the provider configuration for the Control Plane. It includes the organization name that will be used to manage resources within the Control Plane. This is essential for authenticating and authorizing access to the resources defined in the Terraform configurations. +This command will initialize Terraform and import resources defined in your `controlplane.yml` and `templates` folder into the Terraform state for each application. -- **`required_providers.tf`** -```hcl -terraform { - required_providers { - cpln = { - source = "controlplane-com/cpln" - version = "~> 1.0" - } - } -} -``` -**Description**: This file defines the required providers for the Terraform configuration. It specifies the Control Plane provider, including its source and version. This ensures that Terraform knows which provider to use when managing resources. +Please note that during the import process, you may encounter errors indicating that non-existing resources are being imported. This is expected behavior and can be safely ignored. -## Application Deployment +### Application deployment using Terraform -To deploy your application, follow these steps: +Preparations are complete, and now we can use Terraform commands directly to deploy our application. -1. Go to the application folder: +1. **Navigate to the Application Folder**: ```sh cd terraform/rails-app-staging ``` -2. Initialize the new Terraform working directory: - ```sh - terraform init - ``` -3. Generate the execution plan (what actions Terraform would take to apply the current configuration): + +2. **Plan the Deployment**: ```sh terraform plan ``` -4. Apply the planned changes: + +3. **Apply the Configuration**: ```sh terraform apply ``` -## Importing Existing Infrastructure - -In addition to generating Terraform configurations, you can also import existing infrastructure into Terraform management using the `cpflow terraform import` command. This is useful when you have resources that were created outside of Terraform and you want to manage them using Terraform going forward. - -### Usage - -To import existing resources, run the following command: - -```sh -cpflow terraform import -``` - -This command will import resources defined in your `controlplane.yml` and `templates` folder into the Terraform state. +You can visit [Details](details.md) to learn more about how CPLN templates in YAML format are transformed to Terraform configurations. ## References