From a3047926534980084d39b4f1c2beadfd330c3e78 Mon Sep 17 00:00:00 2001 From: Isa Aguilar Date: Fri, 22 Sep 2023 21:36:11 -0400 Subject: [PATCH] add logic to ensure sidecar will be applied --- .../tf.galleybytes.com_terraforms_crd.yaml | 14 ++++++-- pkg/apis/tf/v1beta1/terraform_types.go | 34 +++++++++++++++---- pkg/apis/tf/v1beta1/zz_generated.openapi.go | 9 ++++- pkg/controllers/terraform_controller.go | 13 +++++-- 4 files changed, 57 insertions(+), 13 deletions(-) diff --git a/deploy/crds/tf.galleybytes.com_terraforms_crd.yaml b/deploy/crds/tf.galleybytes.com_terraforms_crd.yaml index 48056fa..8ee65d7 100644 --- a/deploy/crds/tf.galleybytes.com_terraforms_crd.yaml +++ b/deploy/crds/tf.galleybytes.com_terraforms_crd.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: terraforms.tf.galleybytes.com spec: group: tf.galleybytes.com @@ -223,6 +222,13 @@ spec: Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' type: string + must: + description: "Must is short for \"must succeed to generate sidecar + spec\". Generation of spec does not guarantee correctness. + Must will only be applied to sidecars. \n If must is false + and the sidecar spec fails to generate, the sidecar addition + will be omitted." + type: boolean task: description: Task is the second part of a two-part selector of when the plugin gets run in the workflow. This should correspond @@ -234,7 +240,9 @@ spec: of \n - At to run at the same time as the defined task \n - After to run after the defined task has completed. \n - Sidecar to run as a sidecar - for the given task." + for the given task. Since sidecars run in the same pod as + a \"main workflow\" task, failed sidecars will cause a failure + in the workflow." type: string required: - image diff --git a/pkg/apis/tf/v1beta1/terraform_types.go b/pkg/apis/tf/v1beta1/terraform_types.go index eeb8993..2426daf 100644 --- a/pkg/apis/tf/v1beta1/terraform_types.go +++ b/pkg/apis/tf/v1beta1/terraform_types.go @@ -358,12 +358,19 @@ type Plugin struct { // // - After to run after the defined task has completed. // - // - Sidecar to run as a sidecar for the given task. + // - Sidecar to run as a sidecar for the given task. Since sidecars run + // in the same pod as a "main workflow" task, failed sidecars will cause a failure in the workflow. When string `json:"when"` // Task is the second part of a two-part selector of when the plugin gets run in the workflow. This // should correspond to one of the tfo task names. Task TaskName `json:"task"` + + // Must is short for "must succeed to generate sidecar spec". Generation of spec does not guarantee + // correctness. Must will only be applied to sidecars. + // + // If must is false and the sidecar spec fails to generate, the sidecar addition will be omitted. + Must bool `json:"must,omitempty"` } // TaskOption are different configuration options to be injected into task pods. Can apply to @@ -423,7 +430,7 @@ type TaskOption struct { // +optional Volumes []corev1.Volume `json:"volumes,omitempty"` - //Extra volumeMounts for task pod + // Extra volumeMounts for task pod // +optional VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"` } @@ -778,11 +785,12 @@ func (s TerraformSpec) MarshalJSON() ([]byte, error) { type Alias TerraformSpec return json.Marshal(&struct { *Alias - KeepLatestPodsOnly bool `json:"keepLatestPodsOnly"` - KeepCompletedPods bool `json:"keepCompletedPods"` - WriteOutputsToStatus bool `json:"writeOutputsToStatus"` - IgnoreDelete bool `json:"ignoreDelete"` - RequireApproval bool `json:"requireApproval"` + KeepLatestPodsOnly bool `json:"keepLatestPodsOnly"` + KeepCompletedPods bool `json:"keepCompletedPods"` + WriteOutputsToStatus bool `json:"writeOutputsToStatus"` + IgnoreDelete bool `json:"ignoreDelete"` + RequireApproval bool `json:"requireApproval"` + TaskOptions []TaskOption `json:"taskOptions"` }{ Alias: (*Alias)(&s), KeepLatestPodsOnly: s.KeepLatestPodsOnly, @@ -790,6 +798,7 @@ func (s TerraformSpec) MarshalJSON() ([]byte, error) { WriteOutputsToStatus: s.WriteOutputsToStatus, IgnoreDelete: s.IgnoreDelete, RequireApproval: s.RequireApproval, + TaskOptions: s.TaskOptions, }) } @@ -837,6 +846,17 @@ func (s ResourceDownload) MarshalJSON() ([]byte, error) { }) } +func (s Plugin) MarshalJSON() ([]byte, error) { + type Alias Plugin + return json.Marshal(&struct { + *Alias + Must bool `json:"must"` + }{ + Alias: (*Alias)(&s), + Must: s.Must, + }) +} + func init() { SchemeBuilder.Register(&Terraform{}, &TerraformList{}) diff --git a/pkg/apis/tf/v1beta1/zz_generated.openapi.go b/pkg/apis/tf/v1beta1/zz_generated.openapi.go index d27e300..08f83cb 100644 --- a/pkg/apis/tf/v1beta1/zz_generated.openapi.go +++ b/pkg/apis/tf/v1beta1/zz_generated.openapi.go @@ -362,7 +362,7 @@ func schema_pkg_apis_tf_v1beta1_Plugin(ref common.ReferenceCallback) common.Open }, "when": { SchemaProps: spec.SchemaProps{ - Description: "When is a keyword of a two-part selector of when the plugin gets run in the workflow. The value must be one of\n\n- At to run at the same time as the defined task\n\n- After to run after the defined task has completed.\n\n- Sidecar to run as a sidecar for the given task.", + Description: "When is a keyword of a two-part selector of when the plugin gets run in the workflow. The value must be one of\n\n- At to run at the same time as the defined task\n\n- After to run after the defined task has completed.\n\n- Sidecar to run as a sidecar for the given task. Since sidecars run in the same pod as a \"main workflow\" task, failed sidecars will cause a failure in the workflow.", Default: "", Type: []string{"string"}, Format: "", @@ -376,6 +376,13 @@ func schema_pkg_apis_tf_v1beta1_Plugin(ref common.ReferenceCallback) common.Open Format: "", }, }, + "must": { + SchemaProps: spec.SchemaProps{ + Description: "Must is short for \"must succeed to generate sidecar spec\". Generation of spec does not guarantee correctness. Must will only be applied to sidecars.\n\nIf must is false and the sidecar spec fails to generate, the sidecar addition will be omitted.", + Type: []string{"boolean"}, + Format: "", + }, + }, }, Required: []string{"image", "when", "task"}, }, diff --git a/pkg/controllers/terraform_controller.go b/pkg/controllers/terraform_controller.go index e33549e..6ddf2fe 100644 --- a/pkg/controllers/terraform_controller.go +++ b/pkg/controllers/terraform_controller.go @@ -798,7 +798,16 @@ func (r *ReconcileTerraform) Reconcile(ctx context.Context, request reconcile.Re } case "Sidecar": if whenTask.ID() == podType.ID() { - runOpts.sidecarPlugins = append(runOpts.sidecarPlugins, *r.getPluginSidecarPod(ctx, reqLogger, tf, pluginTaskName, pluginConfig, globalEnvFrom)) + pluginSidecarPod, err := r.getPluginSidecarPod(ctx, reqLogger, tf, pluginTaskName, pluginConfig, globalEnvFrom) + if err != nil { + if pluginConfig.Must { + reqLogger.V(1).Info(err.Error()) + return reconcile.Result{Requeue: true}, nil + } + reqLogger.V(1).Info("Error adding sidecar plugin: %s", err.Error()) + continue + } + runOpts.sidecarPlugins = append(runOpts.sidecarPlugins, *pluginSidecarPod) } } } @@ -1416,7 +1425,7 @@ func (r ReconcileTerraform) getPluginRunOpts(tf *tfv1beta1.Terraform, pluginTask return pluginRunOpts } -func (r ReconcileTerraform) getPluginSidecarPod(ctx context.Context, logger logr.Logger, tf *tfv1beta1.Terraform, pluginTaskName tfv1beta1.TaskName, pluginConfig tfv1beta1.Plugin, globalEnvFrom []corev1.EnvFromSource) *corev1.Pod { +func (r ReconcileTerraform) getPluginSidecarPod(ctx context.Context, logger logr.Logger, tf *tfv1beta1.Terraform, pluginTaskName tfv1beta1.TaskName, pluginConfig tfv1beta1.Plugin, globalEnvFrom []corev1.EnvFromSource) (*corev1.Pod, error) { return r.getPluginRunOpts(tf, pluginTaskName, pluginConfig, globalEnvFrom).generatePod() }