diff --git a/api/v1beta1/configuration_types.go b/api/v1beta1/configuration_types.go index 278db841..9955b8cb 100644 --- a/api/v1beta1/configuration_types.go +++ b/api/v1beta1/configuration_types.go @@ -71,6 +71,11 @@ type BaseConfigurationSpec struct { // ConfigurationStatus defines the observed state of Configuration type ConfigurationStatus struct { + // observedGeneration is the most recent generation observed for this Configuration. It corresponds to the + // Configuration's generation, which is updated on mutation by the API Server. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + Apply ConfigurationApplyStatus `json:"apply,omitempty"` Destroy ConfigurationDestroyStatus `json:"destroy,omitempty"` } diff --git a/chart/Chart.yaml b/chart/Chart.yaml index 2d5a0926..40f476ab 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -3,4 +3,4 @@ name: terraform-controller version: 0.2.8 description: A Kubernetes Terraform controller home: https://github.com/oam-dev/terraform-controller -appVersion: "0.3.0" +appVersion: "0.3.1" \ No newline at end of file diff --git a/chart/crds/terraform.core.oam.dev_configurations.yaml b/chart/crds/terraform.core.oam.dev_configurations.yaml index 3ed6f36d..10e07dc5 100644 --- a/chart/crds/terraform.core.oam.dev_configurations.yaml +++ b/chart/crds/terraform.core.oam.dev_configurations.yaml @@ -148,6 +148,12 @@ spec: description: A ConfigurationState represents the status of a resource type: string type: object + observedGeneration: + description: observedGeneration is the most recent generation observed + for this Configuration. It corresponds to the Configuration's generation, + which is updated on mutation by the API Server. + format: int64 + type: integer type: object type: object served: true diff --git a/controllers/configuration_controller.go b/controllers/configuration_controller.go index f6ed7736..82fd1e31 100644 --- a/controllers/configuration_controller.go +++ b/controllers/configuration_controller.go @@ -133,7 +133,7 @@ func (r *ConfigurationReconciler) Reconcile(ctx context.Context, req ctrl.Reques var tfExecutionJob = &batchv1.Job{} if err := r.Client.Get(ctx, client.ObjectKey{Name: meta.ApplyJobName, Namespace: meta.Namespace}, tfExecutionJob); err == nil { - if tfExecutionJob.Status.Succeeded == int32(1) { + if !meta.EnvChanged && tfExecutionJob.Status.Succeeded == int32(1) { if err := meta.updateApplyStatus(ctx, r.Client, types.Available, types.MessageCloudResourceDeployed); err != nil { return ctrl.Result{}, err } @@ -200,6 +200,7 @@ type TFConfigurationMeta struct { RemoteGit string RemoteGitPath string ConfigurationChanged bool + EnvChanged bool ConfigurationCMName string BackendSecretName string ApplyJobName string @@ -277,7 +278,7 @@ func (r *ConfigurationReconciler) terraformApply(ctx context.Context, namespace return errors.Wrap(err, types.ErrUpdateTerraformApplyJob) } - if tfExecutionJob.Status.Succeeded == int32(1) { + if !meta.EnvChanged && tfExecutionJob.Status.Succeeded == int32(1) { if err := meta.updateApplyStatus(ctx, k8sClient, types.Available, types.MessageCloudResourceDeployed); err != nil { return err } @@ -461,6 +462,28 @@ func (r *ConfigurationReconciler) preCheck(ctx context.Context, configuration *v return err } + // Check whether env changes + if err := meta.prepareTFVariables(configuration); err != nil { + return err + } + + var variableInSecret v1.Secret + if err := k8sClient.Get(ctx, client.ObjectKey{Name: meta.VariableSecretName, Namespace: meta.Namespace}, &variableInSecret); err == nil { + for k, v := range variableInSecret.Data { + if val, ok := meta.VariableSecretData[k]; !ok || !bytes.Equal(v, val) { + meta.EnvChanged = true + klog.Info("Job's env changed") + if err := meta.updateApplyStatus(ctx, k8sClient, types.ConfigurationReloading, types.ConfigurationReloadingAsVariableChanged); err != nil { + return err + } + + break + } + } + } else if !kerrors.IsNotFound(err) { + return err + } + // Apply ClusterRole return createTerraformExecutorClusterRole(ctx, k8sClient, fmt.Sprintf("%s-%s", meta.Namespace, ClusterRoleName)) } @@ -472,6 +495,7 @@ func (meta *TFConfigurationMeta) updateApplyStatus(ctx context.Context, k8sClien State: state, Message: message, } + configuration.Status.ObservedGeneration = configuration.Generation if state == types.Available { outputs, err := meta.getTFOutputs(ctx, k8sClient, configuration) if err != nil { @@ -539,31 +563,11 @@ func (meta *TFConfigurationMeta) assembleAndTriggerJob(ctx context.Context, k8sC // updateTerraformJob will set deletion finalizer to the Terraform job if its envs are changed, which will result in // deleting the job. Finally, a new Terraform job will be generated -func (meta *TFConfigurationMeta) updateTerraformJobIfNeeded(ctx context.Context, k8sClient client.Client, configuration v1beta1.Configuration, +func (meta *TFConfigurationMeta) updateTerraformJobIfNeeded(ctx context.Context, k8sClient client.Client, _ v1beta1.Configuration, job batchv1.Job) error { - if err := meta.prepareTFVariables(&configuration); err != nil { - return err - } - - // check whether env changes - var variableInSecret v1.Secret - if err := k8sClient.Get(ctx, client.ObjectKey{Name: meta.VariableSecretName, Namespace: meta.Namespace}, &variableInSecret); err != nil { - return err - } - - var envChanged bool - for k, v := range variableInSecret.Data { - if val, ok := meta.VariableSecretData[k]; !ok || !bytes.Equal(v, val) { - envChanged = true - klog.Info("Job's env changed") - if err := meta.updateApplyStatus(ctx, k8sClient, types.ConfigurationReloading, types.ConfigurationReloadingAsVariableChanged); err != nil { - return err - } - } - } // if either one changes, delete the job - if envChanged || meta.ConfigurationChanged { + if meta.EnvChanged || meta.ConfigurationChanged { klog.InfoS("about to delete job", "Name", job.Name, "Namespace", job.Namespace) var j batchv1.Job if err := k8sClient.Get(ctx, client.ObjectKey{Name: job.Name, Namespace: job.Namespace}, &j); err == nil {