From 966471af19a07ffe94159e231899a0983a71c188 Mon Sep 17 00:00:00 2001 From: qiaozp <47812250+chivalryq@users.noreply.github.com> Date: Mon, 25 Sep 2023 01:25:36 -0500 Subject: [PATCH] Refactor constructing containers and support checkout certain git refs (#382) --- {controllers/process => api/types}/const.go | 44 ++-- api/types/state.go | 4 +- api/types/terraform.go | 19 ++ api/v1beta1/zz_generated.deepcopy.go | 2 +- api/v1beta2/configuration_types.go | 3 + api/v1beta2/zz_generated.deepcopy.go | 2 +- ...terraform.core.oam.dev_configurations.yaml | 11 + controllers/configuration_controller.go | 10 +- controllers/configuration_controller_test.go | 14 +- controllers/process/container/apply.go | 60 +++++ controllers/process/container/assembler.go | 80 +++++++ controllers/process/container/git.go | 85 +++++++ controllers/process/container/git_test.go | 47 ++++ controllers/process/container/init.go | 53 +++++ controllers/process/container/input.go | 36 +++ controllers/process/git.go | 26 --- controllers/process/meta.go | 22 +- controllers/process/process.go | 220 ++++-------------- controllers/process/process_test.go | 97 ++++---- controllers/terraform/logging.go | 4 +- controllers/terraform/status.go | 4 +- controllers/terraform/status_test.go | 2 +- controllers/util/rbac_test.go | 1 + controllers/util/suite_test.go | 3 +- 24 files changed, 542 insertions(+), 307 deletions(-) rename {controllers/process => api/types}/const.go (95%) create mode 100644 controllers/process/container/apply.go create mode 100644 controllers/process/container/assembler.go create mode 100644 controllers/process/container/git.go create mode 100644 controllers/process/container/git_test.go create mode 100644 controllers/process/container/init.go create mode 100644 controllers/process/container/input.go delete mode 100644 controllers/process/git.go diff --git a/controllers/process/const.go b/api/types/const.go similarity index 95% rename from controllers/process/const.go rename to api/types/const.go index bf2ab13f..0c6fa11c 100644 --- a/controllers/process/const.go +++ b/api/types/const.go @@ -1,24 +1,21 @@ -package process +package types const ( - // WorkingVolumeMountPath is the mount path for working volume - WorkingVolumeMountPath = "/data" - // InputTFConfigurationVolumeName is the volume name for input Terraform Configuration - InputTFConfigurationVolumeName = "tf-input-configuration" - // BackendVolumeName is the volume name for Terraform backend - BackendVolumeName = "tf-backend" - // InputTFConfigurationVolumeMountPath is the volume mount path for input Terraform Configuration - InputTFConfigurationVolumeMountPath = "/opt/tf-configuration" - // BackendVolumeMountPath is the volume mount path for Terraform backend - BackendVolumeMountPath = "/opt/tf-backend" + DefaultNamespace = "default" GitCredsKnownHosts = "known_hosts" - // Terraform credentials + // TerraformCredentials - TerraformCredentials = "credentials.tfrc.json" - // Terraform Registry Configuration + // TerraformRegistryConfig - TerraformRegistryConfig = ".terraformrc" ) +const ( + // TerraformContainerName is the name of the container that executes terraform in the pod + TerraformContainerName = "terraform-executor" + TerraformInitContainerName = "terraform-init" +) + const ( // TFInputConfigMapName is the CM name for Terraform Input Configuration TFInputConfigMapName = "tf-%s" @@ -43,23 +40,36 @@ const ( ServiceAccountName = "tf-executor-service-account" ) +// Volume names and mount paths const ( - defaultNamespace = "default" - // TerraformContainerName is the name of the container that executes terraform in the pod - TerraformContainerName = "terraform-executor" - TerraformInitContainerName = "terraform-init" + // WorkingVolumeMountPath is the mount path for working volume + WorkingVolumeMountPath = "/data" + + // InputTFConfigurationVolumeName is the volume name for input Terraform Configuration + InputTFConfigurationVolumeName = "tf-input-configuration" + // InputTFConfigurationVolumeMountPath is the volume mount path for input Terraform Configuration + InputTFConfigurationVolumeMountPath = "/opt/tf-configuration" + + // BackendVolumeName is the volume name for Terraform backend + BackendVolumeName = "tf-backend" + // BackendVolumeMountPath is the volume mount path for Terraform backend + BackendVolumeMountPath = "/opt/tf-backend" + // GitAuthConfigVolumeName is the volume name for git auth configurtaion GitAuthConfigVolumeName = "git-auth-configuration" // GitAuthConfigVolumeMountPath is the volume mount path for git auth configurtaion GitAuthConfigVolumeMountPath = "/root/.ssh" + // TerraformCredentialsConfigVolumeName is the volume name for terraform auth configurtaion TerraformCredentialsConfigVolumeName = "terraform-credentials-configuration" // TerraformCredentialsConfigVolumeMountPath is the volume mount path for terraform auth configurtaion TerraformCredentialsConfigVolumeMountPath = "/root/.terraform.d" + // TerraformRCConfigVolumeName is the volume name of the terraform registry configuration TerraformRCConfigVolumeName = "terraform-rc-configuration" // TerraformRCConfigVolumeMountPath is the volume mount path for registry configuration TerraformRCConfigVolumeMountPath = "/root" + // TerraformCredentialsHelperConfigVolumeName is the volume name for terraform auth configurtaion TerraformCredentialsHelperConfigVolumeName = "terraform-credentials-helper-configuration" // TerraformCredentialsHelperConfigVolumeMountPath is the volume mount path for terraform auth configurtaion diff --git a/api/types/state.go b/api/types/state.go index 925e237c..227b1fce 100644 --- a/api/types/state.go +++ b/api/types/state.go @@ -44,8 +44,8 @@ const ( type Stage string const ( - TerraformInit Stage = "TerraformInit" - TerraformApply Stage = "TerraformApply" + InitStage Stage = "InitStage" + ApplyStage Stage = "Apply" ) const ( diff --git a/api/types/terraform.go b/api/types/terraform.go index 22653ac0..3151754c 100644 --- a/api/types/terraform.go +++ b/api/types/terraform.go @@ -1,5 +1,7 @@ package types +import "k8s.io/apimachinery/pkg/api/resource" + const ( // TerraformHCLConfigurationName is the file name for Terraform hcl Configuration TerraformHCLConfigurationName = "main.tf" @@ -15,9 +17,26 @@ const ( ConfigurationRemote ConfigurationType = "Remote" ) +type Git struct { + URL string + Path string + Ref GitRef +} + // GitRef specifies the git reference type GitRef struct { Branch string `json:"branch,omitempty"` Tag string `json:"tag,omitempty"` Commit string `json:"commit,omitempty"` } + +type ResourceQuota struct { + ResourcesLimitsCPU string + ResourcesLimitsCPUQuantity resource.Quantity + ResourcesLimitsMemory string + ResourcesLimitsMemoryQuantity resource.Quantity + ResourcesRequestsCPU string + ResourcesRequestsCPUQuantity resource.Quantity + ResourcesRequestsMemory string + ResourcesRequestsMemoryQuantity resource.Quantity +} diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 454b670f..5cbfb525 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -23,7 +23,7 @@ package v1beta1 import ( crossplane_runtime "github.com/oam-dev/terraform-controller/api/types/crossplane-runtime" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ) diff --git a/api/v1beta2/configuration_types.go b/api/v1beta2/configuration_types.go index 54c9af4f..4f530ea3 100644 --- a/api/v1beta2/configuration_types.go +++ b/api/v1beta2/configuration_types.go @@ -33,6 +33,9 @@ type ConfigurationSpec struct { // Remote is a git repo which contains hcl files. Currently, only public git repos are supported. Remote string `json:"remote,omitempty"` + // GitRef is the git branch or tag or commit hash to checkout. Only used when Remote is specified. + GitRef apitypes.GitRef `json:"gitRef,omitempty"` + // +kubebuilder:pruning:PreserveUnknownFields Variable *runtime.RawExtension `json:"variable,omitempty"` diff --git a/api/v1beta2/zz_generated.deepcopy.go b/api/v1beta2/zz_generated.deepcopy.go index 56fadfce..bec9361c 100644 --- a/api/v1beta2/zz_generated.deepcopy.go +++ b/api/v1beta2/zz_generated.deepcopy.go @@ -23,7 +23,7 @@ package v1beta2 import ( crossplane_runtime "github.com/oam-dev/terraform-controller/api/types/crossplane-runtime" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ) diff --git a/chart/crds/terraform.core.oam.dev_configurations.yaml b/chart/crds/terraform.core.oam.dev_configurations.yaml index 8c8b7a1c..0cc2890d 100644 --- a/chart/crds/terraform.core.oam.dev_configurations.yaml +++ b/chart/crds/terraform.core.oam.dev_configurations.yaml @@ -331,6 +331,17 @@ spec: name must be unique. type: string type: object + gitRef: + description: GitRef is the git branch or tag or commit hash to checkout. + Only used when Remote is specified. + properties: + branch: + type: string + commit: + type: string + tag: + type: string + type: object hcl: description: HCL is the Terraform HCL type configuration type: string diff --git a/controllers/configuration_controller.go b/controllers/configuration_controller.go index f38f4175..d104b3a1 100644 --- a/controllers/configuration_controller.go +++ b/controllers/configuration_controller.go @@ -99,7 +99,7 @@ func (r *ConfigurationReconciler) Reconcile(ctx context.Context, req ctrl.Reques klog.InfoS("performing Configuration Destroy", "Namespace", req.Namespace, "Name", req.Name, "JobName", meta.DestroyJobName) // if allow to delete halfway, we will not check the status of the apply job. - _, err := terraform.GetTerraformStatus(ctx, meta.ControllerNamespace, meta.DestroyJobName, process.TerraformContainerName, process.TerraformInitContainerName) + _, err := terraform.GetTerraformStatus(ctx, meta.ControllerNamespace, meta.DestroyJobName, types.TerraformContainerName, types.TerraformInitContainerName) if err != nil { klog.ErrorS(err, "Terraform destroy failed") if updateErr := meta.UpdateDestroyStatus(ctx, r.Client, types.ConfigurationDestroyFailed, err.Error()); updateErr != nil { @@ -151,7 +151,7 @@ func (r *ConfigurationReconciler) Reconcile(ctx context.Context, req ctrl.Reques } return ctrl.Result{RequeueAfter: 3 * time.Second}, errors.Wrap(err, "failed to create/update cloud resource") } - state, err := terraform.GetTerraformStatus(ctx, meta.ControllerNamespace, meta.ApplyJobName, process.TerraformContainerName, process.TerraformInitContainerName) + state, err := terraform.GetTerraformStatus(ctx, meta.ControllerNamespace, meta.ApplyJobName, types.TerraformContainerName, types.TerraformInitContainerName) if err != nil { klog.ErrorS(err, "Terraform apply failed") if updateErr := meta.UpdateApplyStatus(ctx, r.Client, state, err.Error()); updateErr != nil { @@ -173,7 +173,7 @@ func (r *ConfigurationReconciler) terraformApply(ctx context.Context, configurat if err := meta.GetApplyJob(ctx, k8sClient, &tfExecutionJob); err != nil { if kerrors.IsNotFound(err) { - return meta.AssembleAndTriggerJob(ctx, k8sClient, process.TerraformApply) + return meta.AssembleAndTriggerJob(ctx, k8sClient, types.TerraformApply) } } klog.InfoS("terraform apply job", "Namespace", tfExecutionJob.Namespace, "Name", tfExecutionJob.Name) @@ -229,7 +229,7 @@ func (r *ConfigurationReconciler) terraformDestroy(ctx context.Context, configur if err := k8sClient.Get(ctx, client.ObjectKey{Name: meta.DestroyJobName, Namespace: meta.ControllerNamespace}, &destroyJob); err != nil { if kerrors.IsNotFound(err) { if err := r.Client.Get(ctx, client.ObjectKey{Name: configuration.Name, Namespace: configuration.Namespace}, &v1beta2.Configuration{}); err == nil { - if err = meta.AssembleAndTriggerJob(ctx, k8sClient, process.TerraformDestroy); err != nil { + if err = meta.AssembleAndTriggerJob(ctx, k8sClient, types.TerraformDestroy); err != nil { return err } } @@ -480,7 +480,7 @@ func (r *ConfigurationReconciler) preCheck(ctx context.Context, configuration *v return err } - return util.CreateTerraformExecutorClusterRole(ctx, k8sClient, fmt.Sprintf("%s-%s", meta.ControllerNamespace, process.ClusterRoleName)) + return util.CreateTerraformExecutorClusterRole(ctx, k8sClient, fmt.Sprintf("%s-%s", meta.ControllerNamespace, types.ClusterRoleName)) } // SetupWithManager setups with a manager diff --git a/controllers/configuration_controller_test.go b/controllers/configuration_controller_test.go index 4e4fcf28..fd23f8f3 100644 --- a/controllers/configuration_controller_test.go +++ b/controllers/configuration_controller_test.go @@ -130,7 +130,7 @@ func TestConfigurationReconcile(t *testing.T) { applyingJob2 := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ - Name: req.Name + "-" + string(process.TerraformApply), + Name: req.Name + "-" + string(types.TerraformApply), Namespace: req.Namespace, }, Status: batchv1.JobStatus{ @@ -153,7 +153,7 @@ func TestConfigurationReconcile(t *testing.T) { variableSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf(process.TFVariableSecret, req.Name), + Name: fmt.Sprintf(types.TFVariableSecret, req.Name), Namespace: req.Namespace, }, Data: map[string][]byte{ @@ -306,7 +306,7 @@ terraform { varMap := map[string]string{"name": "abc"} appliedEnvVariable := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf(process.TFVariableSecret, req.Name), + Name: fmt.Sprintf(types.TFVariableSecret, req.Name), Namespace: req.Namespace, }, Data: map[string][]byte{ @@ -318,7 +318,7 @@ terraform { }, Type: corev1.SecretTypeOpaque, } - appliedJobName := req.Name + "-" + string(process.TerraformApply) + appliedJobName := req.Name + "-" + string(types.TerraformApply) appliedJob := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ Name: appliedJobName, @@ -1165,7 +1165,7 @@ func TestTerraformDestroy(t *testing.T) { } baseVariableSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf(process.TFVariableSecret, secretSuffix), + Name: fmt.Sprintf(types.TFVariableSecret, secretSuffix), Namespace: controllerNamespace, }, Type: corev1.SecretTypeOpaque, @@ -1197,7 +1197,7 @@ func TestTerraformDestroy(t *testing.T) { Name: baseProvider.Name, Namespace: baseProvider.Namespace, }, - VariableSecretName: fmt.Sprintf(process.TFVariableSecret, secretSuffix), + VariableSecretName: fmt.Sprintf(types.TFVariableSecret, secretSuffix), ConfigurationCMName: configurationCMName, // True is default value if user ignores configuration.Spec.DeleteResource DeleteResource: true, @@ -1208,7 +1208,7 @@ func TestTerraformDestroy(t *testing.T) { ApplyJobName: applyJobName, DestroyJobName: destroyJobName, ConfigurationCMName: configurationCMName, - VariableSecretName: fmt.Sprintf(process.TFVariableSecret, secretSuffix), + VariableSecretName: fmt.Sprintf(types.TFVariableSecret, secretSuffix), } metaWithDeleteResourceIsFalse := baseMeta diff --git a/controllers/process/container/apply.go b/controllers/process/container/apply.go new file mode 100644 index 00000000..d4e8caa0 --- /dev/null +++ b/controllers/process/container/apply.go @@ -0,0 +1,60 @@ +package container + +import ( + "fmt" + + "github.com/oam-dev/terraform-controller/api/types" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" +) + +func (a *Assembler) ApplyContainer(executionType types.TerraformExecutionType, resourceQuota types.ResourceQuota) v1.Container { + + c := v1.Container{ + Name: types.TerraformContainerName, + Image: a.TerraformImage, + ImagePullPolicy: v1.PullIfNotPresent, + Command: []string{ + "bash", + "-c", + fmt.Sprintf("terraform %s -lock=false -auto-approve", executionType), + }, + VolumeMounts: []v1.VolumeMount{ + { + Name: a.Name, + MountPath: types.WorkingVolumeMountPath, + }, + { + Name: types.InputTFConfigurationVolumeName, + MountPath: types.InputTFConfigurationVolumeMountPath, + }, + }, + Env: a.Envs, + } + + if resourceQuota.ResourcesLimitsCPU != "" || resourceQuota.ResourcesLimitsMemory != "" || + resourceQuota.ResourcesRequestsCPU != "" || resourceQuota.ResourcesRequestsMemory != "" { + resourceRequirements := v1.ResourceRequirements{} + if resourceQuota.ResourcesLimitsCPU != "" || resourceQuota.ResourcesLimitsMemory != "" { + resourceRequirements.Limits = map[v1.ResourceName]resource.Quantity{} + if resourceQuota.ResourcesLimitsCPU != "" { + resourceRequirements.Limits["cpu"] = resourceQuota.ResourcesLimitsCPUQuantity + } + if resourceQuota.ResourcesLimitsMemory != "" { + resourceRequirements.Limits["memory"] = resourceQuota.ResourcesLimitsMemoryQuantity + } + } + if resourceQuota.ResourcesRequestsCPU != "" || resourceQuota.ResourcesLimitsMemory != "" { + resourceRequirements.Requests = map[v1.ResourceName]resource.Quantity{} + if resourceQuota.ResourcesRequestsCPU != "" { + resourceRequirements.Requests["cpu"] = resourceQuota.ResourcesRequestsCPUQuantity + } + if resourceQuota.ResourcesRequestsMemory != "" { + resourceRequirements.Requests["memory"] = resourceQuota.ResourcesRequestsMemoryQuantity + } + } + c.Resources = resourceRequirements + } + + return c +} diff --git a/controllers/process/container/assembler.go b/controllers/process/container/assembler.go new file mode 100644 index 00000000..e6464115 --- /dev/null +++ b/controllers/process/container/assembler.go @@ -0,0 +1,80 @@ +package container + +import ( + "github.com/oam-dev/terraform-controller/api/types" + v1 "k8s.io/api/core/v1" +) + +// Assembler helps to assemble the init containers +type Assembler struct { + Name string + + GitCredential bool + TerraformCredential bool + TerraformRC bool + TerraformCredentialsHelper bool + + TerraformImage string + BusyboxImage string + GitImage string + + Git types.Git + Envs []v1.EnvVar +} + +func NewAssembler(name string) *Assembler { + return &Assembler{Name: name} +} + +func (a *Assembler) GitCredReference(ptr *v1.SecretReference) *Assembler { + if ptr != nil { + a.GitCredential = true + } + return a +} + +func (a *Assembler) TerraformCredReference(ptr *v1.SecretReference) *Assembler { + if ptr != nil { + a.TerraformCredential = true + } + return a +} + +func (a *Assembler) TerraformRCReference(ptr *v1.SecretReference) *Assembler { + if ptr != nil { + a.TerraformRC = true + } + return a +} + +func (a *Assembler) TerraformCredentialsHelperReference(ptr *v1.SecretReference) *Assembler { + if ptr != nil { + a.TerraformCredentialsHelper = true + } + return a +} + +func (a *Assembler) SetTerraformImage(image string) *Assembler { + a.TerraformImage = image + return a +} + +func (a *Assembler) SetBusyboxImage(image string) *Assembler { + a.BusyboxImage = image + return a +} + +func (a *Assembler) SetGitImage(image string) *Assembler { + a.GitImage = image + return a +} + +func (a *Assembler) SetGit(git types.Git) *Assembler { + a.Git = git + return a +} + +func (a *Assembler) SetEnvs(envs []v1.EnvVar) *Assembler { + a.Envs = envs + return a +} diff --git a/controllers/process/container/git.go b/controllers/process/container/git.go new file mode 100644 index 00000000..e6418ec9 --- /dev/null +++ b/controllers/process/container/git.go @@ -0,0 +1,85 @@ +package container + +import ( + "fmt" + "path/filepath" + + "github.com/oam-dev/terraform-controller/api/types" + + v1 "k8s.io/api/core/v1" +) + +const GitContainerName = "git-configuration" + +// GitContainer will clone the git repository, and copy the files to the working directory +func (a *Assembler) GitContainer() v1.Container { + mounts := []v1.VolumeMount{ + { + Name: a.Name, + MountPath: types.WorkingVolumeMountPath, + }, + { + Name: types.BackendVolumeName, + MountPath: types.BackendVolumeMountPath, + }, + } + + if a.GitCredential { + mounts = append(mounts, + v1.VolumeMount{ + Name: types.GitAuthConfigVolumeName, + MountPath: types.GitAuthConfigVolumeMountPath, + }) + } + + command := a.getCloneCommand() + return v1.Container{ + Name: GitContainerName, + Image: a.GitImage, + ImagePullPolicy: v1.PullIfNotPresent, + Command: command, + VolumeMounts: mounts, + } +} + +func (a *Assembler) getCloneCommand() []string { + var cmd string + hclPath := filepath.Join(types.BackendVolumeMountPath, a.Git.Path) + copyCommand := fmt.Sprintf("cp -r %s/* %s", hclPath, types.WorkingVolumeMountPath) + + checkoutCommand := "" + checkoutObject := getCheckoutObj(a.Git.Ref) + if checkoutObject != "" { + checkoutCommand = fmt.Sprintf("git checkout %s", checkoutObject) + } + cloneCommand := fmt.Sprintf("git clone %s %s", a.Git.URL, types.BackendVolumeMountPath) + + // Check for git credentials, mount the SSH known hosts and private key, add private key into the SSH authentication agent + if a.GitCredential { + sshCommand := fmt.Sprintf("eval `ssh-agent` && ssh-add %s/%s", types.GitAuthConfigVolumeMountPath, v1.SSHAuthPrivateKey) + cloneCommand = fmt.Sprintf("%s && %s", sshCommand, cloneCommand) + } + + cmd = cloneCommand + + if checkoutCommand != "" { + cmd = fmt.Sprintf("%s && %s", cmd, checkoutCommand) + } + cmd = fmt.Sprintf("%s && %s", cmd, copyCommand) + + command := []string{ + "sh", + "-c", + cmd, + } + return command +} + +func getCheckoutObj(ref types.GitRef) string { + if ref.Commit != "" { + return ref.Commit + } else if ref.Tag != "" { + return ref.Tag + } + return ref.Branch +} diff --git a/controllers/process/container/git_test.go b/controllers/process/container/git_test.go new file mode 100644 index 00000000..2f79901e --- /dev/null +++ b/controllers/process/container/git_test.go @@ -0,0 +1,47 @@ +package container + +import ( + "testing" + + "github.com/oam-dev/terraform-controller/api/types" +) + +func Test_getCheckoutObj(t *testing.T) { + tests := []struct { + name string + ref types.GitRef + want string + }{ + { + name: "only branch", + ref: types.GitRef{ + Branch: "feature", + }, + want: "feature", + }, + { + name: "tag take precedence over branch", + ref: types.GitRef{ + Branch: "feature", + Tag: "v1.0.0", + }, + want: "v1.0.0", + }, + { + name: "commit take precedence over tag", + ref: types.GitRef{ + Branch: "feature", + Tag: "v1.0.0", + Commit: "123456", + }, + want: "123456", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getCheckoutObj(tt.ref); got != tt.want { + t.Errorf("getCheckoutObj() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/controllers/process/container/init.go b/controllers/process/container/init.go new file mode 100644 index 00000000..37a5cc4c --- /dev/null +++ b/controllers/process/container/init.go @@ -0,0 +1,53 @@ +package container + +import ( + "github.com/oam-dev/terraform-controller/api/types" + v1 "k8s.io/api/core/v1" +) + +// InitContainer will run terraform init +func (a *Assembler) InitContainer() v1.Container { + mounts := []v1.VolumeMount{ + { + Name: a.Name, + MountPath: types.WorkingVolumeMountPath, + }, + } + if a.TerraformCredential { + mounts = append(mounts, + v1.VolumeMount{ + Name: types.TerraformCredentialsConfigVolumeName, + MountPath: types.TerraformCredentialsConfigVolumeMountPath, + }) + } + + if a.TerraformRC { + mounts = append(mounts, + v1.VolumeMount{ + Name: types.TerraformRCConfigVolumeName, + MountPath: types.TerraformRCConfigVolumeMountPath, + }) + } + + if a.TerraformCredentialsHelper { + mounts = append(mounts, + v1.VolumeMount{ + Name: types.TerraformCredentialsHelperConfigVolumeName, + MountPath: types.TerraformCredentialsHelperConfigVolumeMountPath, + }) + } + + c := v1.Container{ + Name: types.TerraformInitContainerName, + Image: a.TerraformImage, + ImagePullPolicy: v1.PullIfNotPresent, + Command: []string{ + "sh", + "-c", + "terraform init", + }, + VolumeMounts: mounts, + Env: a.Envs, + } + return c +} diff --git a/controllers/process/container/input.go b/controllers/process/container/input.go new file mode 100644 index 00000000..d262705f --- /dev/null +++ b/controllers/process/container/input.go @@ -0,0 +1,36 @@ +package container + +import ( + "fmt" + + "github.com/oam-dev/terraform-controller/api/types" + v1 "k8s.io/api/core/v1" +) + +const InputContainerName = "prepare-input-terraform-configurations" + +// InputContainer prepare input .tf files, copy them to the working directory +func (a *Assembler) InputContainer() v1.Container { + mounts := []v1.VolumeMount{ + + { + Name: a.Name, + MountPath: types.WorkingVolumeMountPath, + }, + { + Name: types.InputTFConfigurationVolumeName, + MountPath: types.InputTFConfigurationVolumeMountPath, + }, + } + return v1.Container{ + Name: InputContainerName, + Image: a.BusyboxImage, + ImagePullPolicy: v1.PullIfNotPresent, + Command: []string{ + "sh", + "-c", + fmt.Sprintf("cp %s/* %s", types.InputTFConfigurationVolumeMountPath, types.WorkingVolumeMountPath), + }, + VolumeMounts: mounts, + } +} diff --git a/controllers/process/git.go b/controllers/process/git.go deleted file mode 100644 index ed84a729..00000000 --- a/controllers/process/git.go +++ /dev/null @@ -1,26 +0,0 @@ -package process - -import ( - "fmt" - "path/filepath" - - v1 "k8s.io/api/core/v1" -) - -func (meta *TFConfigurationMeta) GetCloneCommand() []string { - hclPath := filepath.Join(BackendVolumeMountPath, meta.Git.Path) - cloneCommand := fmt.Sprintf("git clone %s %s && cp -r %s/* %s", meta.Git.URL, BackendVolumeMountPath, hclPath, WorkingVolumeMountPath) - - // Check for git credentials, mount the SSH known hosts and private key, add private key into the SSH authentication agent - if meta.GitCredentialsSecretReference != nil { - sshCommand := fmt.Sprintf("eval `ssh-agent` && ssh-add %s/%s", GitAuthConfigVolumeMountPath, v1.SSHAuthPrivateKey) - cloneCommand = fmt.Sprintf("%s && %s", sshCommand, cloneCommand) - } - - command := []string{ - "sh", - "-c", - cloneCommand, - } - return command -} diff --git a/controllers/process/meta.go b/controllers/process/meta.go index 347c56e4..ba4ab27c 100644 --- a/controllers/process/meta.go +++ b/controllers/process/meta.go @@ -8,7 +8,6 @@ import ( "github.com/oam-dev/terraform-controller/controllers/configuration/backend" "github.com/pkg/errors" v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -23,17 +22,6 @@ type LegacySubResources struct { VariableSecretName string } -type ResourceQuota struct { - ResourcesLimitsCPU string - ResourcesLimitsCPUQuantity resource.Quantity - ResourcesLimitsMemory string - ResourcesLimitsMemoryQuantity resource.Quantity - ResourcesRequestsCPU string - ResourcesRequestsCPUQuantity resource.Quantity - ResourcesRequestsMemory string - ResourcesRequestsMemoryQuantity resource.Quantity -} - // TFConfigurationMeta is all the metadata of a Configuration type TFConfigurationMeta struct { Name string @@ -41,7 +29,7 @@ type TFConfigurationMeta struct { ControllerNamespace string ConfigurationType types.ConfigurationType CompleteConfiguration string - Git Git + Git types.Git ConfigurationChanged bool EnvChanged bool ConfigurationCMName string @@ -73,7 +61,7 @@ type TFConfigurationMeta struct { BackoffLimit int32 // ResourceQuota series Variables are for Setting Compute Resources required by this container - ResourceQuota ResourceQuota + ResourceQuota types.ResourceQuota LegacySubResources LegacySubResources ControllerNSSpecified bool @@ -81,12 +69,6 @@ type TFConfigurationMeta struct { K8sClient client.Client } -type Git struct { - URL string - Path string - // Ref types.GitRef -} - // TFState is Terraform State type TFState struct { Outputs map[string]TfStateProperty `json:"outputs"` diff --git a/controllers/process/process.go b/controllers/process/process.go index 01b81097..3eeaf3d8 100644 --- a/controllers/process/process.go +++ b/controllers/process/process.go @@ -5,14 +5,14 @@ import ( "encoding/json" "fmt" "os" - "path/filepath" "reflect" + "github.com/oam-dev/terraform-controller/controllers/process/container" + "github.com/pkg/errors" batchv1 "k8s.io/api/batch/v1" v1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/klog/v2" @@ -40,10 +40,10 @@ func ControllerNamespaceOption(controllerNamespace string) Option { // @step: since we are using a single namespace to run these, we must ensure the names // are unique across the namespace meta.KeepLegacySubResourceMetas() - meta.ApplyJobName = uid + "-" + string(TerraformApply) - meta.DestroyJobName = uid + "-" + string(TerraformDestroy) - meta.ConfigurationCMName = fmt.Sprintf(TFInputConfigMapName, uid) - meta.VariableSecretName = fmt.Sprintf(TFVariableSecret, uid) + meta.ApplyJobName = uid + "-" + string(types.TerraformApply) + meta.DestroyJobName = uid + "-" + string(types.TerraformDestroy) + meta.ConfigurationCMName = fmt.Sprintf(types.TFInputConfigMapName, uid) + meta.VariableSecretName = fmt.Sprintf(types.TFVariableSecret, uid) meta.ControllerNamespace = controllerNamespace meta.ControllerNSSpecified = true } @@ -55,10 +55,10 @@ func New(req ctrl.Request, configuration v1beta2.Configuration, k8sClient client ControllerNamespace: req.Namespace, Namespace: req.Namespace, Name: req.Name, - ConfigurationCMName: fmt.Sprintf(TFInputConfigMapName, req.Name), - VariableSecretName: fmt.Sprintf(TFVariableSecret, req.Name), - ApplyJobName: req.Name + "-" + string(TerraformApply), - DestroyJobName: req.Name + "-" + string(TerraformDestroy), + ConfigurationCMName: fmt.Sprintf(types.TFInputConfigMapName, req.Name), + VariableSecretName: fmt.Sprintf(types.TFVariableSecret, req.Name), + ApplyJobName: req.Name + "-" + string(types.TerraformApply), + DestroyJobName: req.Name + "-" + string(types.TerraformDestroy), K8sClient: k8sClient, } @@ -127,21 +127,21 @@ func (meta *TFConfigurationMeta) ValidateSecretAndConfigMap(ctx context.Context, ref: meta.GitCredentialsSecretReference, notFoundState: types.InvalidGitCredentialsSecretReference, isSecret: true, - neededKeys: []string{GitCredsKnownHosts, v1.SSHAuthPrivateKey}, + neededKeys: []string{types.GitCredsKnownHosts, v1.SSHAuthPrivateKey}, errKey: "git credentials", }, { ref: meta.TerraformCredentialsSecretReference, notFoundState: types.InvalidTerraformCredentialsSecretReference, isSecret: true, - neededKeys: []string{TerraformCredentials}, + neededKeys: []string{types.TerraformCredentials}, errKey: "terraform credentials", }, { ref: meta.TerraformRCConfigMapReference, notFoundState: types.InvalidTerraformRCConfigMapReference, isSecret: false, - neededKeys: []string{TerraformRegistryConfig}, + neededKeys: []string{types.TerraformRegistryConfig}, errKey: "terraformrc configuration", }, { @@ -226,12 +226,12 @@ func (meta *TFConfigurationMeta) UpdateDestroyStatus(ctx context.Context, k8sCli return nil } -func (meta *TFConfigurationMeta) AssembleAndTriggerJob(ctx context.Context, k8sClient client.Client, executionType TerraformExecutionType) error { +func (meta *TFConfigurationMeta) AssembleAndTriggerJob(ctx context.Context, k8sClient client.Client, executionType types.TerraformExecutionType) error { // apply rbac - if err := createTerraformExecutorServiceAccount(ctx, k8sClient, meta.ControllerNamespace, ServiceAccountName); err != nil { + if err := createTerraformExecutorServiceAccount(ctx, k8sClient, meta.ControllerNamespace, types.ServiceAccountName); err != nil { return err } - if err := util.CreateTerraformExecutorClusterRoleBinding(ctx, k8sClient, meta.ControllerNamespace, fmt.Sprintf("%s-%s", meta.ControllerNamespace, ClusterRoleName), ServiceAccountName); err != nil { + if err := util.CreateTerraformExecutorClusterRoleBinding(ctx, k8sClient, meta.ControllerNamespace, fmt.Sprintf("%s-%s", meta.ControllerNamespace, types.ClusterRoleName), types.ServiceAccountName); err != nil { return err } @@ -262,166 +262,36 @@ func (meta *TFConfigurationMeta) UpdateTerraformJobIfNeeded(ctx context.Context, return nil } -func (meta *TFConfigurationMeta) assembleTerraformJob(executionType TerraformExecutionType) *batchv1.Job { +func (meta *TFConfigurationMeta) assembleTerraformJob(executionType types.TerraformExecutionType) *batchv1.Job { var ( - initContainer v1.Container - tfPreApplyInitContainer v1.Container - initContainers []v1.Container - parallelism int32 = 1 - completions int32 = 1 + initContainers []v1.Container + parallelism int32 = 1 + completions int32 = 1 ) executorVolumes := meta.assembleExecutorVolumes() - initContainerVolumeMounts := []v1.VolumeMount{ - { - Name: meta.Name, - MountPath: WorkingVolumeMountPath, - }, - { - Name: InputTFConfigurationVolumeName, - MountPath: InputTFConfigurationVolumeMountPath, - }, - { - Name: BackendVolumeName, - MountPath: BackendVolumeMountPath, - }, - } - - if meta.TerraformCredentialsSecretReference != nil { - initContainerVolumeMounts = append(initContainerVolumeMounts, - v1.VolumeMount{ - Name: TerraformCredentialsConfigVolumeName, - MountPath: TerraformCredentialsConfigVolumeMountPath, - }) - } - - if meta.TerraformRCConfigMapReference != nil { - initContainerVolumeMounts = append(initContainerVolumeMounts, - v1.VolumeMount{ - Name: TerraformRCConfigVolumeName, - MountPath: TerraformRCConfigVolumeMountPath, - }) - } - - if meta.TerraformCredentialsHelperConfigMapReference != nil { - initContainerVolumeMounts = append(initContainerVolumeMounts, - v1.VolumeMount{ - Name: TerraformCredentialsHelperConfigVolumeName, - MountPath: TerraformCredentialsHelperConfigVolumeMountPath, - }) - } - - // prepare local Terraform .tf files - initContainer = v1.Container{ - Name: "prepare-input-terraform-configurations", - Image: meta.BusyboxImage, - ImagePullPolicy: v1.PullIfNotPresent, - Command: []string{ - "sh", - "-c", - fmt.Sprintf("cp %s/* %s", InputTFConfigurationVolumeMountPath, WorkingVolumeMountPath), - }, - VolumeMounts: initContainerVolumeMounts, - } - - initContainers = append(initContainers, initContainer) - - hclPath := filepath.Join(BackendVolumeMountPath, meta.Git.Path) + assembler := container.NewAssembler(meta.Name). + TerraformCredReference(meta.TerraformCredentialsSecretReference). + TerraformRCReference(meta.TerraformRCConfigMapReference). + TerraformCredentialsHelperReference(meta.TerraformCredentialsHelperConfigMapReference). + GitCredReference(meta.GitCredentialsSecretReference). + SetGit(meta.Git). + SetBusyboxImage(meta.BusyboxImage). + SetTerraformImage(meta.TerraformImage). + SetGitImage(meta.GitImage). + SetEnvs(meta.Envs) + + initContainers = append(initContainers, assembler.InputContainer()) if meta.Git.URL != "" { - cloneCommand := fmt.Sprintf("git clone %s %s && cp -r %s/* %s", meta.Git.URL, BackendVolumeMountPath, hclPath, WorkingVolumeMountPath) - - // Check for git credentials, mount the SSH known hosts and private key, add private key into the SSH authentication agent - if meta.GitCredentialsSecretReference != nil { - initContainerVolumeMounts = append(initContainerVolumeMounts, - v1.VolumeMount{ - Name: GitAuthConfigVolumeName, - MountPath: GitAuthConfigVolumeMountPath, - }) - - sshCommand := fmt.Sprintf("eval `ssh-agent` && ssh-add %s/%s", GitAuthConfigVolumeMountPath, v1.SSHAuthPrivateKey) - cloneCommand = fmt.Sprintf("%s && %s", sshCommand, cloneCommand) - } - - command := []string{ - "sh", - "-c", - cloneCommand, - } - - initContainers = append(initContainers, - v1.Container{ - Name: "git-configuration", - Image: meta.GitImage, - ImagePullPolicy: v1.PullIfNotPresent, - Command: command, - VolumeMounts: initContainerVolumeMounts, - }) - } - - // run `terraform init` - tfPreApplyInitContainer = v1.Container{ - Name: TerraformInitContainerName, - Image: meta.TerraformImage, - ImagePullPolicy: v1.PullIfNotPresent, - Command: []string{ - "sh", - "-c", - "terraform init", - }, - VolumeMounts: initContainerVolumeMounts, - Env: meta.Envs, - } - initContainers = append(initContainers, tfPreApplyInitContainer) - - container := v1.Container{ - Name: TerraformContainerName, - Image: meta.TerraformImage, - ImagePullPolicy: v1.PullIfNotPresent, - Command: []string{ - "bash", - "-c", - fmt.Sprintf("terraform %s -lock=false -auto-approve", executionType), - }, - VolumeMounts: []v1.VolumeMount{ - { - Name: meta.Name, - MountPath: WorkingVolumeMountPath, - }, - { - Name: InputTFConfigurationVolumeName, - MountPath: InputTFConfigurationVolumeMountPath, - }, - }, - Env: meta.Envs, + initContainers = append(initContainers, assembler.GitContainer()) } + initContainers = append(initContainers, assembler.InitContainer()) - if meta.ResourceQuota.ResourcesLimitsCPU != "" || meta.ResourceQuota.ResourcesLimitsMemory != "" || - meta.ResourceQuota.ResourcesRequestsCPU != "" || meta.ResourceQuota.ResourcesRequestsMemory != "" { - resourceRequirements := v1.ResourceRequirements{} - if meta.ResourceQuota.ResourcesLimitsCPU != "" || meta.ResourceQuota.ResourcesLimitsMemory != "" { - resourceRequirements.Limits = map[v1.ResourceName]resource.Quantity{} - if meta.ResourceQuota.ResourcesLimitsCPU != "" { - resourceRequirements.Limits["cpu"] = meta.ResourceQuota.ResourcesLimitsCPUQuantity - } - if meta.ResourceQuota.ResourcesLimitsMemory != "" { - resourceRequirements.Limits["memory"] = meta.ResourceQuota.ResourcesLimitsMemoryQuantity - } - } - if meta.ResourceQuota.ResourcesRequestsCPU != "" || meta.ResourceQuota.ResourcesLimitsMemory != "" { - resourceRequirements.Requests = map[v1.ResourceName]resource.Quantity{} - if meta.ResourceQuota.ResourcesRequestsCPU != "" { - resourceRequirements.Requests["cpu"] = meta.ResourceQuota.ResourcesRequestsCPUQuantity - } - if meta.ResourceQuota.ResourcesRequestsMemory != "" { - resourceRequirements.Requests["memory"] = meta.ResourceQuota.ResourcesRequestsMemoryQuantity - } - } - container.Resources = resourceRequirements - } + applyContainer := assembler.ApplyContainer(executionType, meta.ResourceQuota) name := meta.ApplyJobName - if executionType == TerraformDestroy { + if executionType == types.TerraformDestroy { name = meta.DestroyJobName } @@ -453,8 +323,8 @@ func (meta *TFConfigurationMeta) assembleTerraformJob(executionType TerraformExe InitContainers: initContainers, // Container terraform-executor will first copy predefined terraform.d to working directory, and // then run terraform init/apply. - Containers: []v1.Container{container}, - ServiceAccountName: ServiceAccountName, + Containers: []v1.Container{applyContainer}, + ServiceAccountName: types.ServiceAccountName, Volumes: executorVolumes, RestartPolicy: v1.RestartPolicyOnFailure, NodeSelector: meta.JobNodeSelector, @@ -477,22 +347,22 @@ func (meta *TFConfigurationMeta) assembleExecutorVolumes() []v1.Volume { }{ { ref: meta.GitCredentialsSecretReference, - volumeName: GitAuthConfigVolumeName, + volumeName: types.GitAuthConfigVolumeName, isSecret: true, }, { ref: meta.TerraformCredentialsSecretReference, - volumeName: TerraformCredentialsConfigVolumeName, + volumeName: types.TerraformCredentialsConfigVolumeName, isSecret: true, }, { ref: meta.TerraformRCConfigMapReference, - volumeName: TerraformRCConfigVolumeName, + volumeName: types.TerraformRCConfigVolumeName, isSecret: false, }, { ref: meta.TerraformCredentialsHelperConfigMapReference, - volumeName: TerraformCredentialsHelperConfigVolumeName, + volumeName: types.TerraformCredentialsHelperConfigVolumeName, isSecret: false, }, } @@ -507,14 +377,14 @@ func (meta *TFConfigurationMeta) assembleExecutorVolumes() []v1.Volume { func (meta *TFConfigurationMeta) createConfigurationVolume() v1.Volume { inputCMVolumeSource := v1.ConfigMapVolumeSource{} inputCMVolumeSource.Name = meta.ConfigurationCMName - inputTFConfigurationVolume := v1.Volume{Name: InputTFConfigurationVolumeName} + inputTFConfigurationVolume := v1.Volume{Name: types.InputTFConfigurationVolumeName} inputTFConfigurationVolume.ConfigMap = &inputCMVolumeSource return inputTFConfigurationVolume } func (meta *TFConfigurationMeta) createTFBackendVolume() v1.Volume { - gitVolume := v1.Volume{Name: BackendVolumeName} + gitVolume := v1.Volume{Name: types.BackendVolumeName} gitVolume.EmptyDir = &v1.EmptyDirVolumeSource{} return gitVolume } @@ -614,7 +484,7 @@ func (meta *TFConfigurationMeta) getTFOutputs(ctx context.Context, k8sClient cli name := writeConnectionSecretToReference.Name ns := writeConnectionSecretToReference.Namespace if ns == "" { - ns = defaultNamespace + ns = types.DefaultNamespace } data := make(map[string][]byte) for k, v := range outputs { diff --git a/controllers/process/process_test.go b/controllers/process/process_test.go index 16cce4ad..f6f3d31e 100644 --- a/controllers/process/process_test.go +++ b/controllers/process/process_test.go @@ -78,7 +78,7 @@ func TestInitTFConfigurationMeta(t *testing.T) { ApplyJobName: "abc-apply", DestroyJobName: "abc-destroy", - Git: Git{ + Git: types.Git{ Path: ".", }, ProviderReference: &crossplane.Reference{ @@ -99,7 +99,7 @@ func TestInitTFConfigurationMeta(t *testing.T) { ApplyJobName: "abc-apply", DestroyJobName: "abc-destroy", - Git: Git{ + Git: types.Git{ Path: "alibaba/rds", }, ProviderReference: &crossplane.Reference{ @@ -436,11 +436,11 @@ func TestAssembleTerraformJob(t *testing.T) { GitImage: "d", Namespace: "e", TerraformImage: "f", - Git: Git{ + Git: types.Git{ URL: "g", }, } - job := meta.assembleTerraformJob(TerraformApply) + job := meta.assembleTerraformJob(types.TerraformApply) containers := job.Spec.Template.Spec.InitContainers assert.Equal(t, containers[0].Image, "c") assert.Equal(t, containers[1].Image, "d") @@ -454,13 +454,13 @@ func TestAssembleTerraformJobWithNodeSelectorSetting(t *testing.T) { GitImage: "d", Namespace: "e", TerraformImage: "f", - Git: Git{ + Git: types.Git{ URL: "g", }, JobNodeSelector: map[string]string{"ssd": "true"}, } - job := meta.assembleTerraformJob(TerraformApply) + job := meta.assembleTerraformJob(types.TerraformApply) spec := job.Spec.Template.Spec assert.Equal(t, spec.NodeSelector, map[string]string{"ssd": "true"}) } @@ -600,11 +600,11 @@ func TestAssembleTerraformJobWithResourcesSetting(t *testing.T) { GitImage: "d", Namespace: "e", TerraformImage: "f", - Git: Git{ + Git: types.Git{ URL: "g", }, - ResourceQuota: ResourceQuota{ + ResourceQuota: types.ResourceQuota{ ResourcesLimitsCPU: "10m", ResourcesLimitsCPUQuantity: quantityLimitsCPU, ResourcesLimitsMemory: "10Mi", @@ -616,7 +616,7 @@ func TestAssembleTerraformJobWithResourcesSetting(t *testing.T) { }, } - job := meta.assembleTerraformJob(TerraformApply) + job := meta.assembleTerraformJob(types.TerraformApply) initContainers := job.Spec.Template.Spec.InitContainers assert.Equal(t, initContainers[0].Image, "c") assert.Equal(t, initContainers[1].Image, "d") @@ -640,7 +640,7 @@ func TestAssembleTerraformJobWithGitCredentialsSecretRef(t *testing.T) { GitImage: "d", Namespace: "e", TerraformImage: "f", - Git: Git{ + Git: types.Git{ URL: "g", }, GitCredentialsSecretReference: &corev1.SecretReference{ @@ -649,19 +649,19 @@ func TestAssembleTerraformJobWithGitCredentialsSecretRef(t *testing.T) { }, } - job := meta.assembleTerraformJob(TerraformApply) + job := meta.assembleTerraformJob(types.TerraformApply) spec := job.Spec.Template.Spec var gitSecretDefaultMode int32 = 0400 - gitAuthSecretVolume := corev1.Volume{Name: GitAuthConfigVolumeName} + gitAuthSecretVolume := corev1.Volume{Name: types.GitAuthConfigVolumeName} gitAuthSecretVolume.Secret = &corev1.SecretVolumeSource{ SecretName: "git-ssh", DefaultMode: &gitSecretDefaultMode, } gitSecretVolumeMount := corev1.VolumeMount{ - Name: GitAuthConfigVolumeName, - MountPath: GitAuthConfigVolumeMountPath, + Name: types.GitAuthConfigVolumeName, + MountPath: types.GitAuthConfigVolumeMountPath, } assert.Contains(t, spec.InitContainers[1].VolumeMounts, gitSecretVolumeMount) assert.Contains(t, spec.Volumes, gitAuthSecretVolume) @@ -675,7 +675,7 @@ func TestAssembleTerraformJobWithTerraformRCAndCredentials(t *testing.T) { GitImage: "d", Namespace: "e", TerraformImage: "f", - Git: Git{ + Git: types.Git{ URL: "g", }, TerraformRCConfigMapReference: &corev1.SecretReference{ @@ -692,12 +692,12 @@ func TestAssembleTerraformJobWithTerraformRCAndCredentials(t *testing.T) { }, } - job := meta.assembleTerraformJob(TerraformApply) + job := meta.assembleTerraformJob(types.TerraformApply) spec := job.Spec.Template.Spec var terraformSecretDefaultMode int32 = 0400 - terraformRegistryConfigMapVolume := corev1.Volume{Name: TerraformRCConfigVolumeName} + terraformRegistryConfigMapVolume := corev1.Volume{Name: types.TerraformRCConfigVolumeName} terraformRegistryConfigMapVolume.ConfigMap = &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{ Name: "terraform-registry-config", @@ -706,21 +706,21 @@ func TestAssembleTerraformJobWithTerraformRCAndCredentials(t *testing.T) { } terraformRegistryConfigVolumeMount := corev1.VolumeMount{ - Name: TerraformRCConfigVolumeName, - MountPath: TerraformRCConfigVolumeMountPath, + Name: types.TerraformRCConfigVolumeName, + MountPath: types.TerraformRCConfigVolumeMountPath, } - terraformCredentialsSecretVolume := corev1.Volume{Name: TerraformCredentialsConfigVolumeName} + terraformCredentialsSecretVolume := corev1.Volume{Name: types.TerraformCredentialsConfigVolumeName} terraformCredentialsSecretVolume.Secret = &corev1.SecretVolumeSource{ SecretName: "terraform-credentials", DefaultMode: &terraformSecretDefaultMode, } terraformCredentialsSecretVolumeMount := corev1.VolumeMount{ - Name: TerraformCredentialsConfigVolumeName, - MountPath: TerraformCredentialsConfigVolumeMountPath, + Name: types.TerraformCredentialsConfigVolumeName, + MountPath: types.TerraformCredentialsConfigVolumeMountPath, } - terraformCredentialsHelperConfigVolume := corev1.Volume{Name: TerraformCredentialsHelperConfigVolumeName} + terraformCredentialsHelperConfigVolume := corev1.Volume{Name: types.TerraformCredentialsHelperConfigVolumeName} terraformCredentialsHelperConfigVolume.ConfigMap = &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{ Name: "terraform-credentials-helper", @@ -729,20 +729,22 @@ func TestAssembleTerraformJobWithTerraformRCAndCredentials(t *testing.T) { } terraformCredentialsHelperConfigVolumeMount := corev1.VolumeMount{ - Name: TerraformCredentialsHelperConfigVolumeName, - MountPath: TerraformCredentialsHelperConfigVolumeMountPath, + Name: types.TerraformCredentialsHelperConfigVolumeName, + MountPath: types.TerraformCredentialsHelperConfigVolumeMountPath, } - assert.Contains(t, spec.InitContainers[0].VolumeMounts, terraformCredentialsHelperConfigVolumeMount) + terraformInitContainerMounts := spec.InitContainers[2].VolumeMounts + + assert.Contains(t, terraformInitContainerMounts, terraformCredentialsHelperConfigVolumeMount) assert.Contains(t, spec.Volumes, terraformCredentialsHelperConfigVolume) - assert.Contains(t, spec.InitContainers[0].VolumeMounts, terraformRegistryConfigVolumeMount) + assert.Contains(t, terraformInitContainerMounts, terraformRegistryConfigVolumeMount) assert.Contains(t, spec.Volumes, terraformRegistryConfigMapVolume) - assert.Contains(t, spec.InitContainers[0].VolumeMounts, terraformCredentialsSecretVolumeMount) + assert.Contains(t, terraformInitContainerMounts, terraformCredentialsSecretVolumeMount) assert.Contains(t, spec.Volumes, terraformCredentialsSecretVolume) - assert.Contains(t, spec.InitContainers[0].VolumeMounts, terraformRegistryConfigVolumeMount) + assert.Contains(t, terraformInitContainerMounts, terraformRegistryConfigVolumeMount) assert.Contains(t, spec.Volumes, terraformRegistryConfigMapVolume) } @@ -754,7 +756,7 @@ func TestAssembleTerraformJobWithTerraformRCAndCredentialsHelper(t *testing.T) { GitImage: "d", Namespace: "e", TerraformImage: "f", - Git: Git{ + Git: types.Git{ URL: "g", }, TerraformRCConfigMapReference: &corev1.SecretReference{ @@ -767,12 +769,12 @@ func TestAssembleTerraformJobWithTerraformRCAndCredentialsHelper(t *testing.T) { }, } - job := meta.assembleTerraformJob(TerraformApply) + job := meta.assembleTerraformJob(types.TerraformApply) spec := job.Spec.Template.Spec var terraformSecretDefaultMode int32 = 0400 - terraformRegistryConfigMapVolume := corev1.Volume{Name: TerraformRCConfigVolumeName} + terraformRegistryConfigMapVolume := corev1.Volume{Name: types.TerraformRCConfigVolumeName} terraformRegistryConfigMapVolume.ConfigMap = &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{ Name: "terraform-registry-config", @@ -781,10 +783,10 @@ func TestAssembleTerraformJobWithTerraformRCAndCredentialsHelper(t *testing.T) { } terraformRegistryConfigVolumeMount := corev1.VolumeMount{ - Name: TerraformRCConfigVolumeName, - MountPath: TerraformRCConfigVolumeMountPath, + Name: types.TerraformRCConfigVolumeName, + MountPath: types.TerraformRCConfigVolumeMountPath, } - terraformCredentialsHelperConfigVolume := corev1.Volume{Name: TerraformCredentialsHelperConfigVolumeName} + terraformCredentialsHelperConfigVolume := corev1.Volume{Name: types.TerraformCredentialsHelperConfigVolumeName} terraformCredentialsHelperConfigVolume.ConfigMap = &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{ Name: "terraform-credentials-helper", @@ -793,14 +795,15 @@ func TestAssembleTerraformJobWithTerraformRCAndCredentialsHelper(t *testing.T) { } terraformCredentialsHelperConfigVolumeMount := corev1.VolumeMount{ - Name: TerraformCredentialsHelperConfigVolumeName, - MountPath: TerraformCredentialsHelperConfigVolumeMountPath, + Name: types.TerraformCredentialsHelperConfigVolumeName, + MountPath: types.TerraformCredentialsHelperConfigVolumeMountPath, } - assert.Contains(t, spec.InitContainers[0].VolumeMounts, terraformRegistryConfigVolumeMount) + terraformInitContainerMounts := spec.InitContainers[2].VolumeMounts + assert.Contains(t, terraformInitContainerMounts, terraformRegistryConfigVolumeMount) assert.Contains(t, spec.Volumes, terraformRegistryConfigMapVolume) - assert.Contains(t, spec.InitContainers[0].VolumeMounts, terraformCredentialsHelperConfigVolumeMount) + assert.Contains(t, terraformInitContainerMounts, terraformCredentialsHelperConfigVolumeMount) assert.Contains(t, spec.Volumes, terraformCredentialsHelperConfigVolume) } @@ -1448,7 +1451,7 @@ func TestAssembleAndTriggerJob(t *testing.T) { type prepare func(t *testing.T) type args struct { k8sClient client.Client - executionType TerraformExecutionType + executionType types.TerraformExecutionType prepare } type want struct { @@ -1471,7 +1474,7 @@ func TestAssembleAndTriggerJob(t *testing.T) { }{ "failed to create ServiceAccount": { args: args{ - executionType: TerraformApply, + executionType: types.TerraformApply, }, want: want{ errMsg: "failed to create ServiceAccount for Terraform executor", @@ -1943,7 +1946,7 @@ func TestCheckGitCredentialsSecretReference(t *testing.T) { }, }, want: want{ - errMsg: fmt.Sprintf("'%s' not in git credentials secret", GitCredsKnownHosts), + errMsg: fmt.Sprintf("'%s' not in git credentials secret", types.GitCredsKnownHosts), }, }, { @@ -1973,7 +1976,7 @@ func TestCheckGitCredentialsSecretReference(t *testing.T) { }, }, } - neededKeys := []string{GitCredsKnownHosts, corev1.SSHAuthPrivateKey} + neededKeys := []string{types.GitCredsKnownHosts, corev1.SSHAuthPrivateKey} errKey := "git credentials" for _, tc := range testcases { @@ -2063,7 +2066,7 @@ func TestCheckTerraformCredentialsSecretReference(t *testing.T) { }, }, want: want{ - errMsg: fmt.Sprintf("'%s' not in terraform credentials secret", TerraformCredentials), + errMsg: fmt.Sprintf("'%s' not in terraform credentials secret", types.TerraformCredentials), }, }, { @@ -2081,7 +2084,7 @@ func TestCheckTerraformCredentialsSecretReference(t *testing.T) { }, } - neededKeys := []string{TerraformCredentials} + neededKeys := []string{types.TerraformCredentials} errKey := "terraform credentials" for _, tc := range testcases { @@ -2169,7 +2172,7 @@ func TestCheckTerraformRCConfigMapReference(t *testing.T) { }, }, want: want{ - errMsg: fmt.Sprintf("'%s' not in terraformrc configuration configmap", TerraformRegistryConfig), + errMsg: fmt.Sprintf("'%s' not in terraformrc configuration configmap", types.TerraformRegistryConfig), }, }, { @@ -2187,7 +2190,7 @@ func TestCheckTerraformRCConfigMapReference(t *testing.T) { }, } - neededKeys := []string{TerraformRegistryConfig} + neededKeys := []string{types.TerraformRegistryConfig} errKey := "terraformrc configuration" for _, tc := range testcases { diff --git a/controllers/terraform/logging.go b/controllers/terraform/logging.go index 33c9b573..3ffac40c 100644 --- a/controllers/terraform/logging.go +++ b/controllers/terraform/logging.go @@ -30,7 +30,7 @@ func getPods(ctx context.Context, client kubernetes.Interface, namespace, jobNam func getPodLog(ctx context.Context, client kubernetes.Interface, namespace, jobName, containerName, initContainerName string) (types.Stage, string, error) { var ( targetContainer = containerName - stage = types.TerraformApply + stage = types.ApplyStage ) pods, err := getPods(ctx, client, namespace, jobName) if err != nil || pods == nil || len(pods.Items) == 0 { @@ -44,7 +44,7 @@ func getPodLog(ctx context.Context, client kubernetes.Interface, namespace, jobN for _, c := range pod.Status.InitContainerStatuses { if c.Name == initContainerName && !c.Ready { targetContainer = initContainerName - stage = types.TerraformInit + stage = types.InitStage break } } diff --git a/controllers/terraform/status.go b/controllers/terraform/status.go index edd02dea..7b051af5 100644 --- a/controllers/terraform/status.go +++ b/controllers/terraform/status.go @@ -45,9 +45,9 @@ func analyzeTerraformLog(logs string, stage types.Stage) (bool, types.Configurat return false, types.InvalidRegion, errMsg } switch stage { - case types.TerraformInit: + case types.InitStage: return false, types.TerraformInitError, errMsg - case types.TerraformApply: + case types.ApplyStage: return false, types.ConfigurationApplyFailed, errMsg } } diff --git a/controllers/terraform/status_test.go b/controllers/terraform/status_test.go index 0f02708c..f65a4b14 100644 --- a/controllers/terraform/status_test.go +++ b/controllers/terraform/status_test.go @@ -143,7 +143,7 @@ func TestAnalyzeTerraformLog(t *testing.T) { } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { - success, state, errMsg := analyzeTerraformLog(tc.args.logs, types.TerraformApply) + success, state, errMsg := analyzeTerraformLog(tc.args.logs, types.ApplyStage) if tc.want.errMsg != "" { assert.Contains(t, errMsg, tc.want.errMsg) } else { diff --git a/controllers/util/rbac_test.go b/controllers/util/rbac_test.go index ba09ba36..69d57e1e 100644 --- a/controllers/util/rbac_test.go +++ b/controllers/util/rbac_test.go @@ -2,6 +2,7 @@ package util import ( "context" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" rbacv1 "k8s.io/api/rbac/v1" diff --git a/controllers/util/suite_test.go b/controllers/util/suite_test.go index e27d0e24..1c4d94bf 100644 --- a/controllers/util/suite_test.go +++ b/controllers/util/suite_test.go @@ -1,9 +1,10 @@ package util import ( + "testing" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "testing" ) func TestUtils(t *testing.T) {