From 6e96e1f61de0640dafa30f654cbd2eb4fc64f205 Mon Sep 17 00:00:00 2001 From: patoarvizu Date: Sat, 19 Feb 2022 11:59:21 -0500 Subject: [PATCH 1/8] Add 'etcdv3' backend, configuration, tests, and documentation --- .circleci/config.yml | 2 + README.md | 12 +++++ api/v1/terraformstate_types.go | 13 +++++ ...raform.patoarvizu.dev_terraformstates.yaml | 25 +++++++++ .../templates/crds/terraformstate.yaml | 25 +++++++++ test/e2e/terraformstate_test.go | 52 +++++++++++++++++++ test/etcdv3-state/outputs.tf | 14 +++++ test/etcdv3-state/remote_state.tf | 7 +++ test/etcdv3/helmfile.yaml | 18 +++++++ test/etcdv3/values.yaml | 8 +++ 10 files changed, 176 insertions(+) create mode 100644 test/etcdv3-state/outputs.tf create mode 100644 test/etcdv3-state/remote_state.tf create mode 100644 test/etcdv3/helmfile.yaml create mode 100644 test/etcdv3/values.yaml diff --git a/.circleci/config.yml b/.circleci/config.yml index 4df4229..9386fea 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -107,11 +107,13 @@ jobs: export CONSUL_HTTP_TOKEN=$(kubectl -n consul get secret consul-bootstrap-acl-token -o json | jq -r '.data.token' | base64 -d) cd ../postgres && helmfile sync cd ../artifactory && helmfile sync + cd ../etcdv3 && helmfile sync sleep 30 cd ../consul-state && terraform init && terraform apply -auto-approve cd ../kubernetes-state && terraform init && terraform apply -auto-approve cd ../postgres-state && terraform init && terraform apply -auto-approve cd ../artifactory-state && terraform init && terraform apply -auto-approve + cd ../etcdv3-state && terraform init && terraform apply -auto-approve export AMP_CONSUL_TOKEN=${CONSUL_HTTP_TOKEN} cd ../secrets && helmfile sync cd ../amphibian && helmfile sync diff --git a/README.md b/README.md index 237c1bb..c6f0863 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ - [Kubernetes](#kubernetes) - [Postgres](#postgres) - [Artifactory](#artifactory) + - [Etcd V3](#etcd-v3) - [Target](#target) - [Values](#values) - [For security nerds](#for-security-nerds) @@ -158,6 +159,17 @@ The following fields can be alternatively be set as environment variables (as do - `password` (`ARTIFACTORY_PASSWORD`) - `url` (`ARTIFACTORY_URL`) +#### Etcd V3 + +- [Documentation](https://www.terraform.io/language/settings/backends/etcdv3) +- `type: etcdv3` +- Configuration block name: `etcdv3Config` + +The following fields can be alternatively be set as environment variables (as documented in the link above): + +- `username` (`ETCDV3_USERNAME`) +- `password` (`ETCDV3_PASSWORD`) + ### Target The `target` field represents the location and type of object where the outputs from the upstream state will be projected. diff --git a/api/v1/terraformstate_types.go b/api/v1/terraformstate_types.go index dba3f20..12c78c7 100644 --- a/api/v1/terraformstate_types.go +++ b/api/v1/terraformstate_types.go @@ -110,6 +110,18 @@ type ArtifactoryConfig struct { Subpath string `json:"subpath"` } +type EtcdV3Config struct { + Endpoints []string `json:"endpoints"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Prefix string `json:"prefix,omitempty"` + Lock bool `json:"lock,omitempty"` + CACertPath string `json:"caCertPath,omitempty"` + CertPath string `json:"certPath,omitempty"` + KeyPath string `json:"keyPath,omitempty"` + MaxRequestBytes string `json:"maxRequestBytes,omitempty"` +} + type TerraformStateSpec struct { Type string `json:"type"` RemoteConfig RemoteConfig `json:"remoteConfig,omitempty"` @@ -119,6 +131,7 @@ type TerraformStateSpec struct { GCSConfig GCSConfig `json:"gcsConfig,omitempty"` PostgresConfig PostgresConfig `json:"postgresConfig,omitempty"` ArtifactoryConfig ArtifactoryConfig `json:"artifactoryConfig,omitempty"` + EtcdV3Config EtcdV3Config `json:"etcdv3Config,omitempty"` Target Target `json:"target"` } diff --git a/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml b/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml index 056f251..3bd8a49 100644 --- a/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml +++ b/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml @@ -75,6 +75,31 @@ spec: required: - path type: object + etcdv3Config: + properties: + caCertPath: + type: string + certPath: + type: string + endpoints: + items: + type: string + type: array + keyPath: + type: string + lock: + type: boolean + maxRequestBytes: + type: string + password: + type: string + prefix: + type: string + username: + type: string + required: + - endpoints + type: object gcsConfig: properties: accessToken: diff --git a/helm/amphibian/templates/crds/terraformstate.yaml b/helm/amphibian/templates/crds/terraformstate.yaml index 056f251..3bd8a49 100644 --- a/helm/amphibian/templates/crds/terraformstate.yaml +++ b/helm/amphibian/templates/crds/terraformstate.yaml @@ -75,6 +75,31 @@ spec: required: - path type: object + etcdv3Config: + properties: + caCertPath: + type: string + certPath: + type: string + endpoints: + items: + type: string + type: array + keyPath: + type: string + lock: + type: boolean + maxRequestBytes: + type: string + password: + type: string + prefix: + type: string + username: + type: string + required: + - endpoints + type: object gcsConfig: properties: accessToken: diff --git a/test/e2e/terraformstate_test.go b/test/e2e/terraformstate_test.go index 8242a60..05b5ec5 100644 --- a/test/e2e/terraformstate_test.go +++ b/test/e2e/terraformstate_test.go @@ -249,6 +249,36 @@ func createArtifactoryStateConfig(targetType string) (*terraformv1.TerraformStat return s, nil } +func createEtcdV3StateConfig(targetType string) (*terraformv1.TerraformState, error) { + s := &terraformv1.TerraformState{ + TypeMeta: metav1.TypeMeta{ + Kind: "TerraformState", + APIVersion: "terraform.patoarvizu.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-etcdv3", + Namespace: "default", + }, + Spec: terraformv1.TerraformStateSpec{ + Type: "etcdv3", + EtcdV3Config: terraformv1.EtcdV3Config{ + Endpoints: []string{"etcdv3.etcdv3:2379"}, + Username: "root", + Password: "root123", + }, + Target: terraformv1.Target{ + Type: targetType, + Name: "test-etcdv3", + }, + }, + } + err := k8sClient.Create(context.TODO(), s) + if err != nil { + return nil, err + } + return s, nil +} + // These tests use Ginkgo (BDD-style Go testing framework). Refer to // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. @@ -427,6 +457,28 @@ var _ = Describe("With the controller running", func() { Expect(err).ToNot(HaveOccurred()) }) }) + When("Deploying a TerraformState object with 'etcdv3' config and target type 'configmap'", func() { + It("Should create the target ConfigMap", func() { + state, err = createEtcdV3StateConfig("configmap") + Expect(err).ToNot(HaveOccurred()) + Expect(state).ToNot(BeNil()) + err = validateStateTargetConfigMap(state) + Expect(err).ToNot(HaveOccurred()) + err = k8sClient.Delete(context.TODO(), state) + Expect(err).ToNot(HaveOccurred()) + }) + }) + When("Deploying a TerraformState object with 'etcdv3' config and target type 'secret'", func() { + It("Should create the target Secret", func() { + state, err = createEtcdV3StateConfig("secret") + Expect(err).ToNot(HaveOccurred()) + Expect(state).ToNot(BeNil()) + err = validateStateTargetSecret(state) + Expect(err).ToNot(HaveOccurred()) + err = k8sClient.Delete(context.TODO(), state) + Expect(err).ToNot(HaveOccurred()) + }) + }) }) var _ = AfterSuite(func() { diff --git a/test/etcdv3-state/outputs.tf b/test/etcdv3-state/outputs.tf new file mode 100644 index 0000000..7d0c56c --- /dev/null +++ b/test/etcdv3-state/outputs.tf @@ -0,0 +1,14 @@ +output hello { + value = "world" +} + +output map { + value = { + a = "b" + x = "y" + } +} + +output list { + value = ["a", "b", "c"] +} \ No newline at end of file diff --git a/test/etcdv3-state/remote_state.tf b/test/etcdv3-state/remote_state.tf new file mode 100644 index 0000000..ad47e88 --- /dev/null +++ b/test/etcdv3-state/remote_state.tf @@ -0,0 +1,7 @@ +terraform { + backend "etcdv3" { + endpoints = ["localhost:2379"] + username = "root" + password = "root123" + } +} \ No newline at end of file diff --git a/test/etcdv3/helmfile.yaml b/test/etcdv3/helmfile.yaml new file mode 100644 index 0000000..013309f --- /dev/null +++ b/test/etcdv3/helmfile.yaml @@ -0,0 +1,18 @@ +repositories: +- name: bitnami + url: https://charts.bitnami.com/bitnami + +releases: +- name: etcdv3 + namespace: etcdv3 + chart: bitnami/etcd + version: 6.13.3 + wait: true + values: + - ./values.yaml + +helmDefaults: + kubeContext: k3d-k3s-default + args: + - --kubeconfig + - {{ requiredEnv "HOME" }}/.k3d/k3s-default-config \ No newline at end of file diff --git a/test/etcdv3/values.yaml b/test/etcdv3/values.yaml new file mode 100644 index 0000000..2108b09 --- /dev/null +++ b/test/etcdv3/values.yaml @@ -0,0 +1,8 @@ +auth: + rbac: + rootPassword: root123 + +service: + type: NodePort + nodePorts: + client: 30732 \ No newline at end of file From 7e5e0daa87f6a35469a378f835dda748a5a2508a Mon Sep 17 00:00:00 2001 From: patoarvizu Date: Sat, 19 Feb 2022 12:30:27 -0500 Subject: [PATCH 2/8] Open port 2379 --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9386fea..a835c11 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -100,7 +100,7 @@ jobs: command: | export KUBECONFIG=~/.k3d/k3s-default-config export KUBE_CONFIG_PATH=~/.k3d/k3s-default-config - k3d cluster create --image rancher/k3s:v1.21.8-k3s1 --port 8500:30058@server[0] --port 5432:32345@server[0] --port 8082:32082@server[0] # --k3s-server-arg "--kube-apiserver-arg=feature-gates=ServerSideApply=false" + k3d cluster create --image rancher/k3s:v1.21.8-k3s1 --port 8500:30058@server[0] --port 5432:32345@server[0] --port 8082:32082@server[0] --port 2379:30732@server[0] # --k3s-server-arg "--kube-apiserver-arg=feature-gates=ServerSideApply=false" k3d image import patoarvizu/amphibian:latest cd test/consul && helmfile sync cd ../consul-service && helmfile sync From 12e364796d5a9b3053cb93a4073a50ee09d849f0 Mon Sep 17 00:00:00 2001 From: patoarvizu Date: Sat, 19 Feb 2022 13:37:41 -0500 Subject: [PATCH 3/8] Set 'endpoints' default --- api/v1/terraformstate_types.go | 1 + config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml | 1 + helm/amphibian/templates/crds/terraformstate.yaml | 1 + 3 files changed, 3 insertions(+) diff --git a/api/v1/terraformstate_types.go b/api/v1/terraformstate_types.go index 12c78c7..8f92bca 100644 --- a/api/v1/terraformstate_types.go +++ b/api/v1/terraformstate_types.go @@ -111,6 +111,7 @@ type ArtifactoryConfig struct { } type EtcdV3Config struct { + // +kubebuilder:default=[] Endpoints []string `json:"endpoints"` Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` diff --git a/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml b/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml index 3bd8a49..496b267 100644 --- a/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml +++ b/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml @@ -82,6 +82,7 @@ spec: certPath: type: string endpoints: + default: '[]' items: type: string type: array diff --git a/helm/amphibian/templates/crds/terraformstate.yaml b/helm/amphibian/templates/crds/terraformstate.yaml index 3bd8a49..496b267 100644 --- a/helm/amphibian/templates/crds/terraformstate.yaml +++ b/helm/amphibian/templates/crds/terraformstate.yaml @@ -82,6 +82,7 @@ spec: certPath: type: string endpoints: + default: '[]' items: type: string type: array From 6e6f5bf996e6db76324f6b1ebfb05e48dde79087 Mon Sep 17 00:00:00 2001 From: patoarvizu Date: Sat, 19 Feb 2022 23:55:22 -0500 Subject: [PATCH 4/8] Revert "Set 'endpoints' default" This reverts commit 12e364796d5a9b3053cb93a4073a50ee09d849f0. --- api/v1/terraformstate_types.go | 1 - config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml | 1 - helm/amphibian/templates/crds/terraformstate.yaml | 1 - 3 files changed, 3 deletions(-) diff --git a/api/v1/terraformstate_types.go b/api/v1/terraformstate_types.go index 8f92bca..12c78c7 100644 --- a/api/v1/terraformstate_types.go +++ b/api/v1/terraformstate_types.go @@ -111,7 +111,6 @@ type ArtifactoryConfig struct { } type EtcdV3Config struct { - // +kubebuilder:default=[] Endpoints []string `json:"endpoints"` Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` diff --git a/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml b/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml index 496b267..3bd8a49 100644 --- a/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml +++ b/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml @@ -82,7 +82,6 @@ spec: certPath: type: string endpoints: - default: '[]' items: type: string type: array diff --git a/helm/amphibian/templates/crds/terraformstate.yaml b/helm/amphibian/templates/crds/terraformstate.yaml index 496b267..3bd8a49 100644 --- a/helm/amphibian/templates/crds/terraformstate.yaml +++ b/helm/amphibian/templates/crds/terraformstate.yaml @@ -82,7 +82,6 @@ spec: certPath: type: string endpoints: - default: '[]' items: type: string type: array From 13cf36c84633601ec1374f010fabc7aca449a843 Mon Sep 17 00:00:00 2001 From: patoarvizu Date: Sun, 20 Feb 2022 00:18:05 -0500 Subject: [PATCH 5/8] Make 'EtcdV3Config' field nullable --- api/v1/terraformstate_types.go | 5 +++-- .../crd/bases/terraform.patoarvizu.dev_terraformstates.yaml | 1 + helm/amphibian/templates/crds/terraformstate.yaml | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/api/v1/terraformstate_types.go b/api/v1/terraformstate_types.go index 12c78c7..65213ab 100644 --- a/api/v1/terraformstate_types.go +++ b/api/v1/terraformstate_types.go @@ -131,8 +131,9 @@ type TerraformStateSpec struct { GCSConfig GCSConfig `json:"gcsConfig,omitempty"` PostgresConfig PostgresConfig `json:"postgresConfig,omitempty"` ArtifactoryConfig ArtifactoryConfig `json:"artifactoryConfig,omitempty"` - EtcdV3Config EtcdV3Config `json:"etcdv3Config,omitempty"` - Target Target `json:"target"` + // +nullable + EtcdV3Config EtcdV3Config `json:"etcdv3Config,omitempty"` + Target Target `json:"target"` } type TerraformStateStatus struct{} diff --git a/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml b/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml index 3bd8a49..0e5b3f4 100644 --- a/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml +++ b/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml @@ -76,6 +76,7 @@ spec: - path type: object etcdv3Config: + nullable: true properties: caCertPath: type: string diff --git a/helm/amphibian/templates/crds/terraformstate.yaml b/helm/amphibian/templates/crds/terraformstate.yaml index 3bd8a49..0e5b3f4 100644 --- a/helm/amphibian/templates/crds/terraformstate.yaml +++ b/helm/amphibian/templates/crds/terraformstate.yaml @@ -76,6 +76,7 @@ spec: - path type: object etcdv3Config: + nullable: true properties: caCertPath: type: string From d187b1483fbdc90a1e2e51da2c73c998c3f6f498 Mon Sep 17 00:00:00 2001 From: patoarvizu Date: Sun, 20 Feb 2022 08:27:24 -0500 Subject: [PATCH 6/8] Missed an important part --- controllers/terraformstate_controller.go | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/controllers/terraformstate_controller.go b/controllers/terraformstate_controller.go index 90ad7f8..49b0546 100644 --- a/controllers/terraformstate_controller.go +++ b/controllers/terraformstate_controller.go @@ -111,6 +111,8 @@ func (r *TerraformStateReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err dataBody.SetAttributeValue("config", createPostgresBackendBody(state.Spec.PostgresConfig)) case "artifactory": dataBody.SetAttributeValue("config", createArtifactoryBackendBody(state.Spec.ArtifactoryConfig)) + case "etcdv3": + dataBody.SetAttributeValue("config", createEtcdV3BackendBody(state.Spec.EtcdV3Config)) } dataFile, err := os.Create(fmt.Sprintf("%s/data.tf", stateDir)) if err != nil { @@ -454,6 +456,34 @@ func createArtifactoryBackendBody(config terraformv1.ArtifactoryConfig) cty.Valu return cty.ObjectVal(c) } +func createEtcdV3BackendBody(config terraformv1.EtcdV3Config) cty.Value { + c := make(map[string]cty.Value) + c["endpoints"] = cty.ListVal(createValueList(config.Endpoints)) + if len(config.Username) > 0 { + c["username"] = cty.StringVal(config.Username) + } + if len(config.Password) > 0 { + c["password"] = cty.StringVal(config.Password) + } + if len(config.Prefix) > 0 { + c["prefix"] = cty.StringVal(config.Prefix) + } + if len(config.Prefix) > 0 { + c["ca_cert_path"] = cty.StringVal(config.CACertPath) + } + if len(config.Prefix) > 0 { + c["cert_path"] = cty.StringVal(config.CertPath) + } + if len(config.Prefix) > 0 { + c["key_path"] = cty.StringVal(config.KeyPath) + } + if len(config.Prefix) > 0 { + c["max_request_bytes"] = cty.StringVal(config.MaxRequestBytes) + } + c["lock"] = cty.BoolVal(config.Lock) + return cty.ObjectVal(c) +} + func createValueList(l []string) []cty.Value { valueList := []cty.Value{} for _, v := range l { From e44741039332ff710f693aa01eac0046be1f08c6 Mon Sep 17 00:00:00 2001 From: patoarvizu Date: Sun, 20 Feb 2022 08:28:15 -0500 Subject: [PATCH 7/8] Make 'endpoints' nullable --- api/v1/terraformstate_types.go | 1 + config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml | 1 + helm/amphibian/templates/crds/terraformstate.yaml | 1 + 3 files changed, 3 insertions(+) diff --git a/api/v1/terraformstate_types.go b/api/v1/terraformstate_types.go index 65213ab..7a65833 100644 --- a/api/v1/terraformstate_types.go +++ b/api/v1/terraformstate_types.go @@ -111,6 +111,7 @@ type ArtifactoryConfig struct { } type EtcdV3Config struct { + // +nullable Endpoints []string `json:"endpoints"` Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` diff --git a/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml b/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml index 0e5b3f4..cfa7e52 100644 --- a/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml +++ b/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml @@ -85,6 +85,7 @@ spec: endpoints: items: type: string + nullable: true type: array keyPath: type: string diff --git a/helm/amphibian/templates/crds/terraformstate.yaml b/helm/amphibian/templates/crds/terraformstate.yaml index 0e5b3f4..cfa7e52 100644 --- a/helm/amphibian/templates/crds/terraformstate.yaml +++ b/helm/amphibian/templates/crds/terraformstate.yaml @@ -85,6 +85,7 @@ spec: endpoints: items: type: string + nullable: true type: array keyPath: type: string From 89e7755c89f5390aa78dacb932d66e4fcb7ff1c7 Mon Sep 17 00:00:00 2001 From: patoarvizu Date: Sun, 20 Feb 2022 09:10:10 -0500 Subject: [PATCH 8/8] I don't think this is needed --- api/v1/terraformstate_types.go | 5 ++--- .../crd/bases/terraform.patoarvizu.dev_terraformstates.yaml | 1 - helm/amphibian/templates/crds/terraformstate.yaml | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/api/v1/terraformstate_types.go b/api/v1/terraformstate_types.go index 7a65833..c1fe004 100644 --- a/api/v1/terraformstate_types.go +++ b/api/v1/terraformstate_types.go @@ -132,9 +132,8 @@ type TerraformStateSpec struct { GCSConfig GCSConfig `json:"gcsConfig,omitempty"` PostgresConfig PostgresConfig `json:"postgresConfig,omitempty"` ArtifactoryConfig ArtifactoryConfig `json:"artifactoryConfig,omitempty"` - // +nullable - EtcdV3Config EtcdV3Config `json:"etcdv3Config,omitempty"` - Target Target `json:"target"` + EtcdV3Config EtcdV3Config `json:"etcdv3Config,omitempty"` + Target Target `json:"target"` } type TerraformStateStatus struct{} diff --git a/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml b/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml index cfa7e52..39683d9 100644 --- a/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml +++ b/config/crd/bases/terraform.patoarvizu.dev_terraformstates.yaml @@ -76,7 +76,6 @@ spec: - path type: object etcdv3Config: - nullable: true properties: caCertPath: type: string diff --git a/helm/amphibian/templates/crds/terraformstate.yaml b/helm/amphibian/templates/crds/terraformstate.yaml index cfa7e52..39683d9 100644 --- a/helm/amphibian/templates/crds/terraformstate.yaml +++ b/helm/amphibian/templates/crds/terraformstate.yaml @@ -76,7 +76,6 @@ spec: - path type: object etcdv3Config: - nullable: true properties: caCertPath: type: string