Skip to content

Commit e1edff4

Browse files
authored
feat: add backend state http (#509)
## Changes - Added support for http backend state. For example to integrate with GitLab Terraform state. - Added missing token field to terraform_remote state
2 parents 1e5cab2 + 3cf6193 commit e1edff4

File tree

13 files changed

+441
-47
lines changed

13 files changed

+441
-47
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
kind: Added
2+
body: Added support for `http` backend state
3+
time: 2025-05-06T18:41:08.657888+02:00
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
kind: Fixed
2+
body: Added missing token field to terraform_remote state
3+
time: 2025-05-06T21:30:35.199691+02:00

docs/src/reference/cli/mach-composer_cloud.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ mach-composer cloud [flags]
2727
* [mach-composer cloud add-organization-user](mach-composer_cloud_add-organization-user.md) - Invite a user to a specific organization
2828
* [mach-composer cloud config](mach-composer_cloud_config.md) - Configure mach composer cloud
2929
* [mach-composer cloud create-api-client](mach-composer_cloud_create-api-client.md) - Manage your components
30-
* [mach-composer cloud create-component](mach-composer_cloud_create-component.md) - Register a new component
30+
* [mach-composer cloud create-component](mach-composer_cloud_create-component.md) - Register a new component. If key is not provided it will be generated from the name
3131
* [mach-composer cloud create-organization](mach-composer_cloud_create-organization.md) - Create a new organization
3232
* [mach-composer cloud create-project](mach-composer_cloud_create-project.md) - Create a new Project
3333
* [mach-composer cloud describe-component-versions](mach-composer_cloud_describe-component-versions.md) - List all changes for a component version

docs/src/reference/cli/mach-composer_cloud_create-component.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
## mach-composer cloud create-component
22

3-
Register a new component
3+
Register a new component. If key is not provided it will be generated from the name
44

55
```
6-
mach-composer cloud create-component [name] [flags]
6+
mach-composer cloud create-component [name] [key] [flags]
77
```
88

99
### Options

docs/src/reference/syntax/global.md

Lines changed: 88 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,54 @@ and Terraform provider versions.
4242

4343
### Required
4444

45-
- `plugin` (String) The plugin to use. One of `aws`, `gcp`, `azure` or `local`.
45+
- `plugin` (String) The plugin to use. One of `aws`, `azure`, `gcp`, `http`, `local` or `terraform_cloud`.
4646
This will determine what remote state backend configs will be available
4747

4848
### Dynamic
4949

5050
Depending on the `plugin` value, the following blocks will be merged into
5151
the `remote_state` block:
5252

53-
- `azure` (Block) [Azure](#nested-schema-for-azure) state configuration for
54-
Azure backend
5553
- `aws` (Block) [AWS](#nested-schema-for-aws) state configuration for AWS
5654
backend
55+
- `azure` (Block) [Azure](#nested-schema-for-azure) state configuration for
56+
Azure backend
5757
- `gcp` (Block) [GCP](#nested-schema-for-gcp) state configuration for GCP
5858
backend
59+
- `http` (Block) [HTTP](#nested-schema-for-http) state configuration for
60+
HTTP backend
5961
- `local` (Block) [Local](#nested-schema-for-local) state configuration for
6062
local backend
63+
- `terraform_cloud` (Block) [Terraform Cloud](#nested-schema-for-terraform_cloud)
64+
state configuration for Terraform Cloud backend
65+
66+
## Nested schema for `aws`
67+
68+
An AWS S3 state backend can be configured with the following options
69+
70+
### Example
71+
72+
```yaml
73+
remote_state:
74+
plugin: aws
75+
bucket: <your bucket>
76+
region: <your region>
77+
key_prefix: <your key prefix>
78+
role_arn: <your role arn>
79+
```
80+
81+
### Required
82+
83+
- `bucket` (String) S3 bucket name
84+
- `region` (String) AWS region
85+
- `key_prefix` (String) Key prefix for each individual Terraform state
86+
87+
### Optional
88+
89+
- `role_arn` - Role ARN to access S3 bucket with
90+
- `lock_table` - DynamoDB lock table
91+
- `encrypt` - Enable server side encryption of the state file. Defaults
92+
to `True`
6193

6294
## Nested schema for `azure`
6395

@@ -89,55 +121,60 @@ as the environment
89121
- `state_folder` (String) Folder name for each individual Terraform state.
90122
If left empty the site identifier will be used
91123

92-
## Nested schema for `aws`
124+
## Nested schema for `gcp`
93125

94-
An AWS S3 state backend can be configured with the following options
126+
A GCP state backend can be configured with the following options
95127

96128
### Example
97129

98130
```yaml
99131
remote_state:
100-
plugin: aws
132+
plugin: gcp
101133
bucket: <your bucket>
102-
region: <your region>
103-
key_prefix: <your key prefix>
104-
role_arn: <your role arn>
134+
prefix: <your prefix>
105135
```
106136

107137
### Required
108138

109-
- `bucket` (String) S3 bucket name
110-
- `region` (String) AWS region
111-
- `key_prefix` (String) Key prefix for each individual Terraform state
112-
113-
### Optional
114-
115-
- `role_arn` - Role ARN to access S3 bucket with
116-
- `lock_table` - DynamoDB lock table
117-
- `encrypt` - Enable server side encryption of the state file. Defaults
118-
to `True`
139+
- `bucket` (String) GCS bucket name
140+
- `prefix` (String) Prefix for each individual Terraform state
119141

120-
## Nested schema for `gcp`
142+
## Nested schema for `http`
121143

122-
A GCP state backend can be configured with the following options
144+
An HTTP state backend can be configured with the following options.
123145

124146
### Example
125147

126148
```yaml
127149
remote_state:
128-
plugin: gcp
129-
bucket: <your bucket>
130-
prefix: <your prefix>
150+
plugin: http
151+
address: https://example.com/state
131152
```
132153

133154
### Required
134155

135-
- `bucket` (String) GCS bucket name
136-
- `prefix` (String) Prefix for each individual Terraform state
156+
- `address` (String) The address of the REST endpoint.
157+
158+
### Optional
159+
160+
- `update_method` (String) HTTP method to use when updating state. Defaults to `POST`.
161+
- `lock_address` (String) The address of the lock REST endpoint. Defaults to disabled.
162+
- `lock_method` (String) The HTTP method to use when locking. Defaults to `LOCK`.
163+
- `unlock_address` (String) The address of the unlock REST endpoint. Defaults to disabled.
164+
- `unlock_method` (String) The HTTP method to use when unlocking. Defaults to `UNLOCK`.
165+
- `username` (String) The username for HTTP basic authentication.
166+
- `password` (String) The password for HTTP basic authentication.
167+
- `skip_cert_verification` (Boolean) Whether to skip TLS verification. Defaults to `false`.
168+
- `retry_max` (Integer) The number of HTTP request retries. Defaults to `2`.
169+
- `retry_wait_min` (Integer) The minimum time in seconds to wait between HTTP request attempts. Defaults to `1`.
170+
- `retry_wait_max` (Integer) The maximum time in seconds to wait between HTTP request attempts. Defaults to `30`.
171+
- `client_certificate_pem` (String) A PEM-encoded certificate used by the server to verify the client during mutual TLS (mTLS) authentication.
172+
- `client_private_key_pem` (String) A PEM-encoded private key, required if `client_certificate_pem` is specified.
173+
- `client_ca_certificate_pem` (String) A PEM-encoded CA certificate chain used by the client to verify server certificates during TLS authentication.
137174

138175
## Nested schema for `local`
139176

140-
A GCP state backend can be configured with the following options
177+
A local state backend can be configured with the following options
141178

142179
### Example
143180

@@ -151,3 +188,27 @@ remote_state:
151188

152189
- `path` (String) Local path to store state files. Defaults to
153190
`./terraform.tfstate`
191+
192+
## Nested schema for `terraform_cloud`
193+
194+
A Terraform Cloud state backend can be configured with the following options.
195+
196+
### Example
197+
198+
```yaml
199+
remote_state:
200+
plugin: terraform_cloud
201+
organization: <your organization>
202+
```
203+
204+
### Required
205+
206+
- `organization` (String) The name of the Terraform Cloud organization.
207+
208+
### Optional
209+
210+
- `hostname` (String) The hostname of the Terraform Cloud instance. Defaults to `app.terraform.io`.
211+
- `token` (String) The token used to authenticate with the Terraform Cloud backend. It is recommended to omit this field and use `terraform login` or configure credentials in the CLI config file instead.
212+
- `workspaces` (Block) Configuration for workspaces:
213+
- `name` (String) The name of the workspace.
214+
- `prefix` (String) A prefix for dynamically created workspaces.

internal/config/schemas/schema-1.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,11 @@ definitions:
112112
type: string
113113
enum:
114114
- aws
115-
- gcp
116115
- azure
117-
- terraform_cloud
116+
- gcp
117+
- http
118118
- local
119+
- terraform_cloud
119120
- $ref: "#/definitions/RemoteState"
120121

121122
RemoteState:

internal/state/http.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package state
2+
3+
import (
4+
"github.com/mach-composer/mach-composer-cli/internal/utils"
5+
)
6+
7+
// HttpState HTTP backend configuration.
8+
type HttpState struct {
9+
Address string `mapstructure:"address"`
10+
UpdateMethod string `mapstructure:"update_method"`
11+
LockAddress string `mapstructure:"lock_address"`
12+
LockMethod string `mapstructure:"lock_method"`
13+
UnlockAddress string `mapstructure:"unlock_address"`
14+
UnlockMethod string `mapstructure:"unlock_method"`
15+
Username string `mapstructure:"username"`
16+
Password string `mapstructure:"password"`
17+
SkipCertVerification bool `mapstructure:"skip_cert_verification" default:"false"`
18+
RetryMax int `mapstructure:"retry_max"`
19+
RetryWaitMin int `mapstructure:"retry_wait_min"`
20+
RetryWaitMax int `mapstructure:"retry_wait_max"`
21+
ClientCertificatePEM string `mapstructure:"client_certificate_pem"`
22+
ClientPrivateKeyPEM string `mapstructure:"client_private_key_pem"`
23+
ClientCACertificatePEM string `mapstructure:"client_ca_certificate_pem"`
24+
}
25+
26+
type HttpRenderer struct {
27+
BaseRenderer
28+
state *HttpState
29+
}
30+
31+
func (hr *HttpRenderer) Backend() (string, error) {
32+
templateContext := struct {
33+
State *HttpState
34+
Identifier string
35+
}{
36+
State: hr.state,
37+
Identifier: hr.identifier,
38+
}
39+
40+
tpl := `
41+
backend "http" {
42+
address = "{{ .State.Address }}"
43+
{{- if .State.UpdateMethod }}
44+
update_method = "{{ .State.UpdateMethod }}"
45+
{{- end }}
46+
{{- if .State.LockAddress }}
47+
lock_address = "{{ .State.LockAddress }}"
48+
{{- end }}
49+
{{- if .State.LockMethod }}
50+
lock_method = "{{ .State.LockMethod }}"
51+
{{- end }}
52+
{{- if .State.UnlockAddress }}
53+
unlock_address = "{{ .State.UnlockAddress }}"
54+
{{- end }}
55+
{{- if .State.UnlockMethod }}
56+
unlock_method = "{{ .State.UnlockMethod }}"
57+
{{- end }}
58+
{{- if .State.Username }}
59+
username = "{{ .State.Username }}"
60+
{{- end }}
61+
{{- if .State.Password }}
62+
password = "{{ .State.Password }}"
63+
{{- end }}
64+
{{- if .State.SkipCertVerification }}
65+
skip_cert_verification = {{ .State.SkipCertVerification }}
66+
{{- end }}
67+
{{- if gt .State.RetryMax 0 }}
68+
retry_max = {{ .State.RetryMax }}
69+
{{- end }}
70+
{{- if gt .State.RetryWaitMin 0 }}
71+
retry_wait_min = {{ .State.RetryWaitMin }}
72+
{{- end }}
73+
{{- if gt .State.RetryWaitMax 0 }}
74+
retry_wait_max = {{ .State.RetryWaitMax }}
75+
{{- end }}
76+
{{- if .State.ClientCertificatePEM }}
77+
client_certificate_pem = <<EOT
78+
{{ .State.ClientCertificatePEM }}
79+
EOT
80+
{{- end }}
81+
{{- if .State.ClientPrivateKeyPEM }}
82+
client_private_key_pem = <<EOT
83+
{{ .State.ClientPrivateKeyPEM }}
84+
EOT
85+
{{- end }}
86+
{{- if .State.ClientCACertificatePEM }}
87+
client_ca_certificate_pem = <<EOT
88+
{{ .State.ClientCACertificatePEM }}
89+
EOT
90+
{{- end }}
91+
}
92+
`
93+
return utils.RenderGoTemplate(tpl, templateContext)
94+
}
95+
96+
func (hr *HttpRenderer) RemoteState() (string, error) {
97+
templateContext := struct {
98+
State *HttpState
99+
Identifier string
100+
Key string
101+
}{
102+
State: hr.state,
103+
Identifier: hr.identifier,
104+
Key: hr.stateKey,
105+
}
106+
107+
tpl := `
108+
data "terraform_remote_state" "{{ .Key }}" {
109+
backend = "http"
110+
111+
config = {
112+
address = "{{ .State.Address }}"
113+
{{- if .State.UpdateMethod }}
114+
update_method = "{{ .State.UpdateMethod }}"
115+
{{- end }}
116+
}
117+
}
118+
`
119+
return utils.RenderGoTemplate(tpl, templateContext)
120+
}

0 commit comments

Comments
 (0)