diff --git a/annotations/annotations.go b/annotations/annotations.go new file mode 100644 index 00000000..3b10fbd0 --- /dev/null +++ b/annotations/annotations.go @@ -0,0 +1,14 @@ +package annotations + +const ( + LastApplySum string = "runner.terraform.padok.cloud/apply-sum" + LastApplyDate string = "runner.terraform.padok.cloud/apply-date" + LastApplyCommit string = "runner.terraform.padok.cloud/apply-commit" + LastPlanCommit string = "runner.terraform.padok.cloud/plan-commit" + LastPlanDate string = "runner.terraform.padok.cloud/plan-date" + LastPlanSum string = "runner.terraform.padok.cloud/plan-sum" + Failure string = "runner.terraform.padok.cloud/failure" + + LastBranchCommit string = "notifications.terraform.padok.cloud/branch-commit" + ForceApply string = "notifications.terraform.padok.cloud/force-apply" +) diff --git a/api/v1alpha1/terraformlayer_types.go b/api/v1alpha1/terraformlayer_types.go index 06f9a60d..cdf34986 100644 --- a/api/v1alpha1/terraformlayer_types.go +++ b/api/v1alpha1/terraformlayer_types.go @@ -51,7 +51,7 @@ type TerraformLayerRepository struct { // TerraformLayerStatus defines the observed state of TerraformLayer type TerraformLayerStatus struct { - Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` + Conditions []metav1.Condition `json:"conditions,omitempty"` } //+kubebuilder:object:root=true diff --git a/burrito/config/config.go b/burrito/config/config.go index 075c1b5c..429a086b 100644 --- a/burrito/config/config.go +++ b/burrito/config/config.go @@ -40,15 +40,12 @@ type RunnerConfig struct { Version string `yaml:"version"` Action string `yaml:"action"` Repository RepositoryConfig `yaml:"repository"` - Layer LayerConfig `yaml:"layer"` + Layer Layer `yaml:"layer"` } -type LayerConfig struct { - Lock string `yaml:"lock"` - PlanSum string `yaml:"planSum"` - PlanBin string `yaml:"planBin"` - ApplySum string `yaml:"applySum"` - PlanDate string `yaml:"planDate"` +type Layer struct { + Name string `yaml:"name"` + Namespace string `yaml:"namespace"` } type Redis struct { diff --git a/controllers/pod.go b/controllers/pod.go index b0adf760..37ad8bda 100644 --- a/controllers/pod.go +++ b/controllers/pod.go @@ -4,7 +4,6 @@ import ( "fmt" configv1alpha1 "github.com/padok-team/burrito/api/v1alpha1" - "github.com/padok-team/burrito/cache" corev1 "k8s.io/api/core/v1" ) @@ -99,24 +98,12 @@ func defaultPodSpec(layer *configv1alpha1.TerraformLayer, repository *configv1al Value: layer.Spec.TerraformVersion, }, { - Name: "BURRITO_RUNNER_LAYER_LOCK", - Value: cache.GenerateKey(cache.Lock, layer), + Name: "BURRITO_RUNNER_LAYER_NAME", + Value: layer.GetObjectMeta().GetName(), }, { - Name: "BURRITO_RUNNER_LAYER_PLANSUM", - Value: cache.GenerateKey(cache.LastPlannedArtifact, layer), - }, - { - Name: "BURRITO_RUNNER_LAYER_PLANBIN", - Value: cache.GenerateKey(cache.LastPlannedArtifactBin, layer), - }, - { - Name: "BURRITO_RUNNER_LAYER_APPLYSUM", - Value: cache.GenerateKey(cache.LastAppliedArtifact, layer), - }, - { - Name: "BURRITO_RUNNER_LAYER_PLANDATE", - Value: cache.GenerateKey(cache.LastPlanDate, layer), + Name: "BURRITO_RUNNER_LAYER_NAMESPACE", + Value: layer.GetObjectMeta().GetNamespace(), }, }, }, diff --git a/runner/runner.go b/runner/runner.go index 6cd4d32b..fc943127 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -3,6 +3,7 @@ package runner import ( "context" "crypto/sha256" + "errors" "fmt" "log" "os" @@ -15,8 +16,17 @@ import ( "github.com/hashicorp/hc-install/product" "github.com/hashicorp/hc-install/releases" "github.com/hashicorp/terraform-exec/tfexec" + "github.com/padok-team/burrito/annotations" + configv1alpha1 "github.com/padok-team/burrito/api/v1alpha1" "github.com/padok-team/burrito/burrito/config" "github.com/padok-team/burrito/cache" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" ) const PlanArtifact string = "plan.out" @@ -26,6 +36,8 @@ type Runner struct { config *config.Config terraform *tfexec.Terraform cache cache.Cache + client client.Client + layer *configv1alpha1.TerraformLayer } func New(c *config.Config) *Runner { @@ -35,23 +47,63 @@ func New(c *config.Config) *Runner { } func (r *Runner) Exec() { - r.cache = cache.NewRedisCache(r.config.Redis.URL, r.config.Redis.Password, r.config.Redis.Database) - defer r.cache.Delete(r.config.Runner.Layer.Lock) err := r.init() if err != nil { log.Fatalf("error initializing runner: %s", err) } + defer r.cache.Delete(cache.GenerateKey(cache.Lock, r.layer)) + var ann map[string]string switch r.config.Runner.Action { case "plan": - r.plan() + ann, err = r.plan() case "apply": - r.apply() + ann, err = r.apply() default: - log.Fatalf("Unrecognized runner Action") + err = errors.New("Unrecognized runner action, If this is happening there might be a version mismatch between the controller and runner") + } + if err != nil { + log.Fatalf("Error during runner execution: %s", err) + n, ok := r.layer.Annotations[annotations.Failure] + number := 0 + if ok { + number, err = strconv.Atoi(n) + if err != nil { + number = 0 + } + } + number++ + ann[annotations.Failure] = strconv.Itoa(number) + } + for k, v := range ann { + r.layer.Annotations[k] = v + } + err = r.client.Update(context.TODO(), r.layer) + if err != nil { + log.Fatalf("Could not update annotations on Layer: %s", err) } } func (r *Runner) init() error { + r.cache = cache.NewRedisCache(r.config.Redis.URL, r.config.Redis.Password, r.config.Redis.Database) + scheme := runtime.NewScheme() + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(configv1alpha1.AddToScheme(scheme)) + cl, err := client.New(ctrl.GetConfigOrDie(), client.Options{ + Scheme: scheme, + }) + if err != nil { + return err + } + r.client = cl + layer := &configv1alpha1.TerraformLayer{} + err = r.client.Get(context.TODO(), types.NamespacedName{ + Namespace: r.config.Runner.Layer.Namespace, + Name: r.config.Runner.Layer.Name, + }, layer) + if err != nil { + return err + } + r.layer = layer log.Printf("Using Terraform version: %s", r.config.Runner.Version) terraformVersion, err := version.NewVersion(r.config.Runner.Version) if err != nil { @@ -88,64 +140,78 @@ func (r *Runner) init() error { return nil } -func (r *Runner) plan() { +func (r *Runner) plan() (map[string]string, error) { + ann := map[string]string{} log.Print("Launching terraform plan") diff, err := r.terraform.Plan(context.Background(), tfexec.Out(PlanArtifact)) if err != nil { log.Printf("Terraform plan errored: %s", err) - return + return nil, err } - log.Printf("Setting last plan date cache at key %s", r.config.Runner.Layer.PlanDate) - err = r.cache.Set(r.config.Runner.Layer.PlanDate, []byte(strconv.FormatInt(time.Now().Unix(), 10)), 3600) + planDateKey := cache.GenerateKey(cache.LastPlanDate, r.layer) + log.Printf("Setting last plan date cache at key %s", planDateKey) + err = r.cache.Set(planDateKey, []byte(strconv.FormatInt(time.Now().Unix(), 10)), 3600) + ann[annotations.LastPlanDate] = strconv.FormatInt(time.Now().Unix(), 10) if err != nil { log.Fatalf("Could not put plan date in cache: %s", err) + return nil, err } if !diff { log.Printf("Terraform plan diff empty, no subsequent apply should be launched") - return + return nil, err } plan, err := os.ReadFile(fmt.Sprintf("%s/%s", r.terraform.WorkingDir(), PlanArtifact)) if err != nil { log.Fatalf("Could not read plan output: %s", err) - return + return nil, err } log.Print("Terraform plan ran successfully") sum := sha256.Sum256(plan) - log.Printf("Setting plan binary into cache at key %s", r.config.Runner.Layer.PlanBin) - err = r.cache.Set(r.config.Runner.Layer.PlanBin, plan, 3600) + planBinKey := cache.GenerateKey(cache.LastPlannedArtifactBin, r.layer) + log.Printf("Setting plan binary into cache at key %s", planBinKey) + err = r.cache.Set(planBinKey, plan, 3600) if err != nil { log.Fatalf("Could not put plan binary in cache: %s", err) } - log.Printf("Setting plan binary checksum into cache at key %s", r.config.Runner.Layer.PlanSum) - err = r.cache.Set(r.config.Runner.Layer.PlanSum, sum[:], 3600) + planSumKey := cache.GenerateKey(cache.LastPlannedArtifact, r.layer) + log.Printf("Setting plan binary checksum into cache at key %s", planSumKey) + err = r.cache.Set(planSumKey, sum[:], 3600) + ann[annotations.LastPlanSum] = string(sum[:]) if err != nil { log.Fatalf("Could not put plan checksum in cache: %s", err) } + return ann, nil } -func (r *Runner) apply() { - log.Printf("Getting plan binary in cache at key %s", r.config.Runner.Layer.PlanBin) - plan, err := r.cache.Get(r.config.Runner.Layer.PlanBin) +func (r *Runner) apply() (map[string]string, error) { + ann := map[string]string{} + planBinKey := cache.GenerateKey(cache.LastPlannedArtifactBin, r.layer) + log.Printf("Getting plan binary in cache at key %s", planBinKey) + plan, err := r.cache.Get(planBinKey) if err != nil { log.Printf("Could not get plan artifact: %s", err) - return + return nil, err } sum := sha256.Sum256(plan) err = os.WriteFile(fmt.Sprintf("%s/%s", r.terraform.WorkingDir(), PlanArtifact), plan, 0644) if err != nil { log.Printf("Could not write plan artifact to disk: %s", err) - return + return nil, err } log.Print("Launching terraform apply") err = r.terraform.Apply(context.Background(), tfexec.DirOrPlan(PlanArtifact)) if err != nil { log.Fatalf("Terraform apply errored: %s", err) - return + return nil, err } log.Print("Terraform apply ran successfully") - log.Printf("Setting plan binary checksum into cache at key %s", r.config.Runner.Layer.ApplySum) - err = r.cache.Set(r.config.Runner.Layer.ApplySum, sum[:], 3600) + applySumKey := cache.GenerateKey(cache.LastAppliedArtifact, r.layer) + log.Printf("Setting plan binary checksum into cache at key %s", applySumKey) + err = r.cache.Set(applySumKey, sum[:], 3600) + ann[annotations.LastPlanSum] = string(sum[:]) if err != nil { log.Fatalf("Could not put apply checksum in cache: %s", err) + return nil, err } + return ann, nil }