cpflow 4.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/claude-code-review.yml +44 -0
  3. data/.github/workflows/claude.yml +50 -0
  4. data/.gitignore +6 -0
  5. data/CHANGELOG.md +17 -1
  6. data/Gemfile.lock +2 -2
  7. data/README.md +17 -14
  8. data/docs/ci-automation.md +28 -0
  9. data/docs/commands.md +21 -1
  10. data/docs/terraform/details.md +415 -0
  11. data/docs/terraform/example/.controlplane/controlplane.yml +29 -0
  12. data/docs/terraform/example/.controlplane/templates/app.yml +38 -0
  13. data/docs/terraform/example/.controlplane/templates/postgres.yml +30 -0
  14. data/docs/terraform/example/.controlplane/templates/rails.yml +26 -0
  15. data/docs/terraform/overview.md +105 -0
  16. data/lib/command/base.rb +29 -5
  17. data/lib/command/base_sub_command.rb +15 -0
  18. data/lib/command/generate.rb +1 -1
  19. data/lib/command/ps.rb +1 -1
  20. data/lib/command/ps_stop.rb +2 -1
  21. data/lib/command/ps_wait.rb +5 -1
  22. data/lib/command/run.rb +4 -21
  23. data/lib/command/terraform/base.rb +35 -0
  24. data/lib/command/terraform/generate.rb +99 -0
  25. data/lib/command/terraform/import.rb +79 -0
  26. data/lib/core/config.rb +1 -1
  27. data/lib/core/controlplane.rb +7 -6
  28. data/lib/core/controlplane_api_direct.rb +23 -1
  29. data/lib/core/shell.rb +9 -4
  30. data/lib/core/terraform_config/agent.rb +31 -0
  31. data/lib/core/terraform_config/audit_context.rb +31 -0
  32. data/lib/core/terraform_config/base.rb +25 -0
  33. data/lib/core/terraform_config/dsl.rb +102 -0
  34. data/lib/core/terraform_config/generator.rb +184 -0
  35. data/lib/core/terraform_config/gvc.rb +63 -0
  36. data/lib/core/terraform_config/identity.rb +35 -0
  37. data/lib/core/terraform_config/local_variable.rb +30 -0
  38. data/lib/core/terraform_config/policy.rb +151 -0
  39. data/lib/core/terraform_config/provider.rb +22 -0
  40. data/lib/core/terraform_config/required_provider.rb +23 -0
  41. data/lib/core/terraform_config/secret.rb +138 -0
  42. data/lib/core/terraform_config/volume_set.rb +155 -0
  43. data/lib/core/terraform_config/workload/main.tf +316 -0
  44. data/lib/core/terraform_config/workload/required_providers.tf +8 -0
  45. data/lib/core/terraform_config/workload/variables.tf +263 -0
  46. data/lib/core/terraform_config/workload.rb +132 -0
  47. data/lib/cpflow/version.rb +1 -1
  48. data/lib/cpflow.rb +51 -9
  49. data/lib/generator_templates/templates/postgres.yml +1 -1
  50. data/lib/patches/array.rb +8 -0
  51. data/lib/patches/hash.rb +47 -0
  52. data/lib/patches/string.rb +34 -0
  53. data/script/update_command_docs +6 -2
  54. metadata +37 -4
  55. /data/docs/{migrating.md → migrating-heroku-to-control-plane.md} +0 -0
@@ -0,0 +1,415 @@
1
+ ### Terraform Configurations from CPLN Templates
2
+
3
+ #### Providers
4
+
5
+ Terraform provider configurations are controlled via `required_providers.tf` and `providers.tf`:
6
+
7
+ - **`required_providers.tf`**
8
+
9
+ ```hcl
10
+ terraform {
11
+ required_providers {
12
+ cpln = {
13
+ source = "controlplane-com/cpln"
14
+ version = "~> 1.0"
15
+ }
16
+ }
17
+ }
18
+ ```
19
+
20
+ - **`providers.tf`**
21
+
22
+ ```hcl
23
+ provider "cpln" {
24
+ org = "org-name-example"
25
+ }
26
+ ```
27
+
28
+ #### GVC (Global Virtual Cloud)
29
+
30
+ CPLN template in YAML format:
31
+
32
+ ```yaml
33
+ kind: gvc
34
+ name: app-name
35
+ description: app-description
36
+ tags:
37
+ tag-name-1: "tag-value-1"
38
+ tag-name-2: "tag-value-2"
39
+ spec:
40
+ domain: "app.example.com"
41
+ env:
42
+ - name: DATABASE_URL
43
+ value: "postgres://the_user:the_password@postgres.app-name.cpln.local:5432/app-name"
44
+ - name: RAILS_ENV
45
+ value: production
46
+ - name: RAILS_SERVE_STATIC_FILES
47
+ value: "true"
48
+ staticPlacement:
49
+ locationLinks:
50
+ - "//location/aws-us-west-2"
51
+ pullSecretLinks:
52
+ - "/org/org-name/secret/some-secret"
53
+ loadBalancer:
54
+ dedicated: true
55
+ trustedProxies: 0
56
+ ```
57
+
58
+ Will transform to Terraform config:
59
+
60
+ ```hcl
61
+ resource "cpln_gvc" "app-name" {
62
+ name = "app-name"
63
+ description = "app-description"
64
+ tags = {
65
+ tag_name_1 = "tag-value-1"
66
+ tag_name_2 = "tag-value-2"
67
+ }
68
+ domain = "app.example.com"
69
+ locations = ["aws-us-west-2"]
70
+ pull_secrets = ["cpln_secret.some-secret.name"]
71
+ env = {
72
+ DATABASE_URL = "postgres://the_user:the_password@postgres.app-name.cpln.local:5432/app-name"
73
+ RAILS_ENV = "production"
74
+ RAILS_SERVE_STATIC_FILES = "true"
75
+ }
76
+ load_balancer {
77
+ dedicated = true
78
+ trusted_proxies = 0
79
+ }
80
+ }
81
+ ```
82
+
83
+ #### Identity
84
+
85
+ CPLN template in YAML format:
86
+
87
+ ```yaml
88
+ kind: identity
89
+ name: postgres-poc-identity
90
+ description: postgres-poc-identity
91
+ tags:
92
+ tag-name-1: "tag-value-1"
93
+ tag-name-2: "tag-value-2"
94
+ ```
95
+
96
+ Will transform to Terraform config:
97
+
98
+ ```hcl
99
+ resource "cpln_identity" "postgres-poc-identity" {
100
+ name = "postgres-poc-identity"
101
+ description = "postgres-poc-identity"
102
+ tags = {
103
+ tag_name_1 = "tag-value-1"
104
+ tag_name_2 = "tag-value-2"
105
+ }
106
+ }
107
+ ```
108
+
109
+ #### Secret
110
+
111
+ CPLN template in YAML format
112
+
113
+ **For `aws` secret:**
114
+
115
+ ```yaml
116
+ kind: secret
117
+ name: aws
118
+ description: aws
119
+ type: aws
120
+ data:
121
+ accessKey: 'AccessKeyExample'
122
+ externalId: 'ExternalIdExample'
123
+ roleArn: arn:awskey
124
+ secretKey: 'SecretKeyExample'
125
+ ```
126
+
127
+ Will transform to Terraform config:
128
+
129
+ ```hcl
130
+ resource "cpln_secret" "aws" {
131
+ name = "aws"
132
+ description = "aws"
133
+ aws {
134
+ secret_key = "SecretKeyExample"
135
+ access_key = "AccessKeyExample"
136
+ role_arn = "arn:awskey"
137
+ external_id = "ExternalIdExample"
138
+ }
139
+ }
140
+ ```
141
+
142
+ **For `azure-connector` secret:**
143
+
144
+ ```yaml
145
+ kind: secret
146
+ name: azure-connector
147
+ description: azure_connector
148
+ tags:
149
+ tag1: tag-val
150
+ type: azure-connector
151
+ data:
152
+ code: 'CodeExample'
153
+ url: https://example.com
154
+ ```
155
+
156
+ Will transform to Terraform config:
157
+
158
+ ```hcl
159
+ resource "cpln_secret" "azure-connector" {
160
+ name = "azure-connector"
161
+ description = "azure_connector"
162
+ tags = {
163
+ tag1 = "tag-val"
164
+ }
165
+ azure_connector {
166
+ url = "https://example.com"
167
+ code = "CodeExample"
168
+ }
169
+ }
170
+ ```
171
+
172
+ **For `azure-sdk-secret` secret:**
173
+
174
+ ```yaml
175
+ kind: secret
176
+ name: azure-sdk-secret
177
+ description: azure-sdk-secret
178
+ type: azure-sdk
179
+ data: >-
180
+ {"subscriptionId":"subscriptionId","tenantId":"tenantId","clientId":"clientId","clientSecret":"CONFIDENTIAL"}
181
+ ```
182
+
183
+ Will transform to Terraform config:
184
+
185
+ ```hcl
186
+ resource "cpln_secret" "azure-sdk-secret" {
187
+ name = "azure-sdk-secret"
188
+ description = "azure-sdk-secret"
189
+ azure_sdk = "{"subscriptionId":"subscriptionId","tenantId":"tenantId","clientId":"clientID","clientSecret":"CONFIDENTIAL"}"
190
+ }
191
+ ```
192
+
193
+ **For `dictionary` secret:**
194
+
195
+ ```yaml
196
+ kind: secret
197
+ name: dictionary
198
+ description: dictionary
199
+ tags: {}
200
+ type: dictionary
201
+ data:
202
+ example: 'value'
203
+ ```
204
+
205
+ Will transform to Terraform config:
206
+
207
+ ```hcl
208
+ resource "cpln_secret" "dictionary" {
209
+ name = "dictionary"
210
+ description = "dictionary"
211
+ tags = {
212
+ }
213
+ dictionary = {
214
+ example = "value"
215
+ }
216
+ }
217
+ ```
218
+
219
+ Supported all types of the secrets which can be configured in Control Plane.
220
+
221
+ #### Policy
222
+
223
+ CPLN template in YAML format:
224
+
225
+ ```yaml
226
+ kind: policy
227
+ name: policy-name
228
+ description: policy description
229
+ tags:
230
+ tag1: tag1_value
231
+ tag2: tag2_value
232
+ target: all
233
+ targetKind: secret
234
+ targetLinks:
235
+ - "//secret/postgres-poc-credentials"
236
+ - "//secret/postgres-poc-entrypoint-script"
237
+ bindings:
238
+ - permissions:
239
+ - reveal
240
+ - view
241
+ - use
242
+ principalLinks:
243
+ - "//gvc/{{APP_NAME}}/identity/postgres-poc-identity"
244
+ - permissions:
245
+ - view
246
+ principalLinks:
247
+ - user/fake-user@fake-email.com
248
+ ```
249
+
250
+ Will be transformed to Terraform config:
251
+
252
+ ```hcl
253
+ resource "cpln_policy" "policy-name" {
254
+ name = "policy-name"
255
+ description = "policy description"
256
+ tags = {
257
+ tag1 = "tag1_value"
258
+ tag2 = "tag2_value"
259
+ }
260
+ target_kind = "secret"
261
+ gvc = cpln_gvc.app-name.name
262
+ target = "all"
263
+ target_links = ["postgres-poc-credentials", "postgres-poc-entrypoint-script"]
264
+ binding {
265
+ permissions = ["reveal", "view", "use"]
266
+ principal_links = ["gvc/app-name/identity/postgres-poc-identity"]
267
+ }
268
+ binding {
269
+ permissions = ["view"]
270
+ principal_links = ["user/fake-user@fake-email.com"]
271
+ }
272
+ }
273
+ ```
274
+
275
+ #### Volumeset
276
+
277
+ CPLN template in YAML format:
278
+
279
+ ```yaml
280
+ kind: volumeset
281
+ name: postgres-poc-vs
282
+ description: postgres-poc-vs
283
+ spec:
284
+ autoscaling:
285
+ maxCapacity: 1000
286
+ minFreePercentage: 1
287
+ scalingFactor: 1.1
288
+ fileSystemType: ext4
289
+ initialCapacity: 10
290
+ performanceClass: general-purpose-ssd
291
+ snapshots:
292
+ createFinalSnapshot: true
293
+ retentionDuration: 7d
294
+ ```
295
+
296
+ Will be transformed to Terraform config:
297
+
298
+ ```hcl
299
+ resource "cpln_volume_set" "postgres-poc-vs" {
300
+ gvc = cpln_gvc.app-name.name
301
+ name = "postgres-poc-vs"
302
+ description = "postgres-poc-vs"
303
+ initial_capacity = 10
304
+ performance_class = "general-purpose-ssd"
305
+ file_system_type = "ext4"
306
+ snapshots {
307
+ create_final_snapshot = true
308
+ retention_duration = "7d"
309
+ }
310
+ autoscaling {
311
+ max_capacity = 1000
312
+ min_free_percentage = 1
313
+ scaling_factor = 1.1
314
+ }
315
+ }
316
+ ```
317
+
318
+ #### Workload
319
+
320
+ CPLN template in YAML format:
321
+
322
+ ```yaml
323
+ kind: workload
324
+ name: rails
325
+ spec:
326
+ type: standard
327
+ containers:
328
+ - name: rails
329
+ cpu: 300m
330
+ env:
331
+ - name: LOG_LEVEL
332
+ value: debug
333
+ inheritEnv: true
334
+ image: {{APP_IMAGE_LINK}}
335
+ memory: 512Mi
336
+ ports:
337
+ - number: 3000
338
+ protocol: http
339
+ defaultOptions:
340
+ autoscaling:
341
+ maxScale: 1
342
+ capacityAI: false
343
+ firewallConfig:
344
+ external:
345
+ inboundAllowCIDR:
346
+ - 0.0.0.0/0
347
+ outboundAllowCIDR:
348
+ - 0.0.0.0/0
349
+ ```
350
+
351
+ Will be transformed to Terraform configs:
352
+
353
+ - **`rails.tf`**
354
+
355
+ ```hcl
356
+ module "rails" {
357
+ source = "../workload"
358
+ type = "standard"
359
+ name = "rails"
360
+ gvc = cpln_gvc.my-app-production.name
361
+ containers = {
362
+ rails: {
363
+ image: "/org/shakacode-demo/image/my-app-production:rails",
364
+ cpu: "300m",
365
+ memory: "512Mi",
366
+ inherit_env: true,
367
+ envs: local.rails_envs,
368
+ ports: [
369
+ {
370
+ number: 3000,
371
+ protocol: "http"
372
+ }
373
+ ]
374
+ }
375
+ }
376
+ options = {
377
+ autoscaling: {
378
+ max_scale: 1
379
+ }
380
+ capacity_ai: false
381
+ }
382
+ firewall_spec = {
383
+ external: {
384
+ inbound_allow_cidr: [
385
+ "0.0.0.0/0"
386
+ ],
387
+ outbound_allow_cidr: [
388
+ "0.0.0.0/0"
389
+ ]
390
+ }
391
+ }
392
+ }
393
+ ```
394
+
395
+ Notice the `source: ../workload` line - there is a common `workload` module which is used for generating Terraform configs from workload templates:
396
+ ```
397
+ workload/
398
+ ├── main.tf -- Configurable workload resource in HCL
399
+ ├── required_providers.tf -- Required providers for Terraform in HCL
400
+ ├── variables.tf -- Variables used to configure workload resource above
401
+ ```
402
+
403
+ - **`rails_envs.tf`**
404
+
405
+ ```hcl
406
+ locals {
407
+ rails_envs = {
408
+ LOG_LEVEL = "debug"
409
+ }
410
+ }
411
+ ```
412
+
413
+ ### References
414
+
415
+ - [Control Plane Terraform Provider](https://registry.terraform.io/providers/controlplane-com/cpln/latest/docs)
@@ -0,0 +1,29 @@
1
+ allow_org_override_by_env: true
2
+ allow_app_override_by_env: true
3
+
4
+ aliases:
5
+ common: &common
6
+ cpln_org: my-org-staging
7
+ default_location: aws-us-east-2
8
+ setup_app_templates:
9
+ - app
10
+ - postgres
11
+ - rails
12
+ one_off_workload: rails
13
+ app_workloads:
14
+ - rails
15
+ additional_workloads:
16
+ - postgres
17
+ apps:
18
+ rails-app-staging:
19
+ <<: *common
20
+ hooks:
21
+ post_creation: bundle exec rake db:prepare
22
+ pre_deletion: bundle exec rake db:drop
23
+
24
+ rails-app-production:
25
+ <<: *common
26
+ allow_org_override_by_env: false
27
+ allow_app_override_by_env: false
28
+ cpln_org: my-org-production
29
+ upstream: rails-app-staging
@@ -0,0 +1,38 @@
1
+ kind: gvc
2
+ name: {{APP_NAME}}
3
+ description: Global Virtual Cloud for Rails Application
4
+ spec:
5
+ env:
6
+ - name: DATABASE_URL
7
+ value: "postgres://user:password@postgres.{{APP_NAME}}.cpln.local:5432/{{APP_NAME}}"
8
+ - name: RAILS_ENV
9
+ value: production
10
+ - name: RAILS_SERVE_STATIC_FILES
11
+ value: "true"
12
+ staticPlacement:
13
+ locationLinks:
14
+ - {{APP_LOCATION_LINK}}
15
+ pullSecretLinks:
16
+ - "/org/org-name/secret/rails-app-secret"
17
+ loadBalancer:
18
+ dedicated: true
19
+ trustedProxies: 0
20
+
21
+ ---
22
+
23
+ kind: identity
24
+ name: rails-app-identity
25
+ description: Identity for Rails Application
26
+ tags:
27
+ environment: production
28
+
29
+ ---
30
+
31
+ kind: secret
32
+ name: rails-app-secret
33
+ description: Secret for Rails Application
34
+ type: aws
35
+ data:
36
+ accessKey: 'AccessKeyExample'
37
+ secretKey: 'SecretKeyExample'
38
+ region: 'us-west-2'
@@ -0,0 +1,30 @@
1
+ kind: workload
2
+ name: postgres
3
+ spec:
4
+ type: standard
5
+ containers:
6
+ - name: postgres
7
+ cpu: 500m
8
+ env:
9
+ - name: POSTGRES_USER
10
+ value: "user"
11
+ - name: POSTGRES_PASSWORD
12
+ value: "password"
13
+ - name: POSTGRES_DB
14
+ value: "rails_app"
15
+ inheritEnv: true
16
+ image: "postgres:latest"
17
+ memory: 1Gi
18
+ ports:
19
+ - number: 5432
20
+ protocol: tcp
21
+ defaultOptions:
22
+ autoscaling:
23
+ maxScale: 1
24
+ capacityAI: false
25
+ firewallConfig:
26
+ external:
27
+ inboundAllowCIDR:
28
+ - 0.0.0.0/0
29
+ outboundAllowCIDR:
30
+ - 0.0.0.0/0
@@ -0,0 +1,26 @@
1
+ kind: workload
2
+ name: rails
3
+ spec:
4
+ type: standard
5
+ containers:
6
+ - name: rails
7
+ cpu: 300m
8
+ env:
9
+ - name: LOG_LEVEL
10
+ value: debug
11
+ inheritEnv: true
12
+ image: {{APP_IMAGE_LINK}}
13
+ memory: 512Mi
14
+ ports:
15
+ - number: 3000
16
+ protocol: http
17
+ defaultOptions:
18
+ autoscaling:
19
+ maxScale: 1
20
+ capacityAI: false
21
+ firewallConfig:
22
+ external:
23
+ inboundAllowCIDR:
24
+ - 0.0.0.0/0
25
+ outboundAllowCIDR:
26
+ - 0.0.0.0/0
@@ -0,0 +1,105 @@
1
+ # Terraform
2
+
3
+ ## Overview
4
+
5
+ The Terraform feature in this project allows you to manage your Control Plane (CPLN) configurations using Terraform by:
6
+ 1. Generating Terraform configuration files from existing CPLN YAML configuration files
7
+ 2. Easily importing existing infrastructure into Terraform management
8
+
9
+ You can continue working with CPLN configuration files in YAML format and start using Terraform at any time.
10
+
11
+ ## Benefits of Using Terraform Over YAML Configs
12
+
13
+ 1. **State Management**: Terraform maintains a state file that tracks the current state of your infrastructure, making it easier to manage changes and updates.
14
+ 2. **Dependency Management**: Terraform automatically handles dependencies between resources, ensuring that they are created or destroyed in the correct order.
15
+ 3. **Multi-Cloud Support**: With Terraform, you can manage resources across multiple cloud providers seamlessly, allowing for a more flexible architecture.
16
+ 4. **Plan and Apply**: Terraform provides a clear plan of what changes will be made before applying them, reducing the risk of unintended modifications.
17
+
18
+ ## Usage
19
+
20
+ Let's take a look at how to deploy a [simple Rails application](https://github.com/shakacode/control-plane-flow/tree/main/docs/terraform/example/.controlplane/controlplane.yml) on CPLN using Terraform:
21
+
22
+ ```
23
+ .controlplane/
24
+ ├── templates/
25
+ │ ├── app.yml -- GVC config
26
+ │ ├── postgres.yml -- Workload config for PostgreSQL
27
+ │ └── rails.yml -- Workload config for Rails
28
+ └── controlplane.yml -- Configs for overall application
29
+ ```
30
+
31
+ ### Generating Terraform configurations
32
+
33
+ To generate Terraform configurations, run the following command from the project root:
34
+
35
+ ```sh
36
+ cpflow terraform generate
37
+ ```
38
+
39
+ Invoking this command will generate a new `terraform` folder with subfolders containing Terraform configurations for each application described in `controlplane.yml`:
40
+
41
+ ```
42
+ terraform/
43
+ ├── rails-app-production/ -- Terraform configurations for production environment
44
+ │ ├── gvc.tf -- GVC config in HCL
45
+ │ ├── identities.tf -- Identities config in HCL
46
+ │ ├── postgres.tf -- Postgres workload config in HCL
47
+ │ ├── postgres_envs.tf -- ENV variables for Postgres workload in HCL
48
+ │ ├── providers.tf -- Providers config in HCL
49
+ │ ├── rails.tf -- Rails workload config in HCL
50
+ │ ├── rails_envs.tf -- ENV variables for Rails workload in HCL
51
+ │ ├── required_providers.tf -- Required providers config in HCL
52
+ │ └── secrets.tf -- Secrets config in HCL
53
+ ├── rails-app-staging/ -- Terraform configurations for staging environment
54
+ │ ├── gvc.tf -- GVC config in HCL
55
+ │ ├── identities.tf -- Identities config in HCL
56
+ │ ├── postgres.tf -- Postgres workload config in HCL
57
+ │ ├── postgres_envs.tf -- ENV variables for Postgres workload in HCL
58
+ │ ├── providers.tf -- Providers config in HCL
59
+ │ ├── rails.tf -- Rails workload config in HCL
60
+ │ ├── rails_envs.tf -- ENV variables for Rails workload in HCL
61
+ │ ├── required_providers.tf -- Required providers config in HCL
62
+ │ └── secrets.tf -- Secrets config in HCL
63
+ ├── workload/ -- Terraform configurations for workload module
64
+ │ ├── main.tf -- Main config for workload resource in HCL
65
+ │ ├── required_providers.tf -- Required providers for Terraform in HCL
66
+ │ └── variables.tf -- Variables used to create config for workload resource in HCL
67
+ ```
68
+
69
+ ### Importing existing infrastructure
70
+
71
+ 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:
72
+
73
+ ```sh
74
+ cpflow terraform import
75
+ ```
76
+
77
+ This command will initialize Terraform and import resources defined in your `controlplane.yml` and `templates` folder into the Terraform state for each application.
78
+
79
+ 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.
80
+
81
+ ### Application deployment using Terraform
82
+
83
+ Preparations are complete, and now we can use Terraform commands directly to deploy our application.
84
+
85
+ 1. **Navigate to the Application Folder**:
86
+ ```sh
87
+ cd terraform/rails-app-staging
88
+ ```
89
+
90
+ 2. **Plan the Deployment**:
91
+ ```sh
92
+ terraform plan
93
+ ```
94
+
95
+ 3. **Apply the Configuration**:
96
+ ```sh
97
+ terraform apply
98
+ ```
99
+
100
+ You can visit [Details](https://github.com/shakacode/control-plane-flow/tree/main/docs/terraform/details.md) to learn more about how CPLN templates in YAML format are transformed to Terraform configurations.
101
+
102
+ ## References
103
+
104
+ - [Terraform Provider Plugin](https://shakadocs.controlplane.com/terraform/installation#terraform-provider-plugin)
105
+ - [Terraform - Control Plane Examples](https://github.com/controlplane-com/examples/tree/main/terraform)
data/lib/command/base.rb CHANGED
@@ -12,6 +12,8 @@ module Command
12
12
  VALIDATIONS_WITH_ADDITIONAL_OPTIONS = %w[templates].freeze
13
13
  ALL_VALIDATIONS = VALIDATIONS_WITHOUT_ADDITIONAL_OPTIONS + VALIDATIONS_WITH_ADDITIONAL_OPTIONS
14
14
 
15
+ # Used to call the command (`cpflow SUBCOMMAND_NAME NAME`)
16
+ SUBCOMMAND_NAME = nil
15
17
  # Used to call the command (`cpflow NAME`)
16
18
  # NAME = ""
17
19
  # Displayed when running `cpflow help` or `cpflow help NAME` (defaults to `NAME`)
@@ -43,11 +45,21 @@ module Command
43
45
  @config = config
44
46
  end
45
47
 
46
- def self.all_commands
47
- Dir["#{__dir__}/*.rb"].each_with_object({}) do |file, result|
48
- filename = File.basename(file, ".rb")
49
- classname = File.read(file).match(/^\s+class (\w+) < Base($| .*$)/)&.captures&.first
50
- result[filename.to_sym] = Object.const_get("::Command::#{classname}") if classname
48
+ def self.all_commands # rubocop:disable Metrics/MethodLength
49
+ Dir["#{__dir__}/**/*.rb"].each_with_object({}) do |file, result|
50
+ content = File.read(file)
51
+
52
+ classname = content.match(/^\s+class (?!Base\b)(\w+) < (?:.*(?!Command::)Base)(?:$| .*$)/)&.captures&.first
53
+ next unless classname
54
+
55
+ namespaces = content.scan(/^\s+module (\w+)/).flatten
56
+ full_classname = [*namespaces, classname].join("::").prepend("::")
57
+
58
+ command_key = File.basename(file, ".rb")
59
+ prefix = namespaces[1..].map(&:downcase).join("_")
60
+ command_key.prepend(prefix.concat("_")) unless prefix.empty?
61
+
62
+ result[command_key.to_sym] = Object.const_get(full_classname)
51
63
  end
52
64
  end
53
65
 
@@ -453,6 +465,18 @@ module Command
453
465
  }
454
466
  }
455
467
  end
468
+
469
+ def self.dir_option(required: false)
470
+ {
471
+ name: :dir,
472
+ params: {
473
+ banner: "DIR",
474
+ desc: "Output directory",
475
+ type: :string,
476
+ required: required
477
+ }
478
+ }
479
+ end
456
480
  # rubocop:enable Metrics/MethodLength
457
481
 
458
482
  def self.all_options