diff --git a/cache/common.go b/cache/common.go index c660069b..7b2df4ab 100644 --- a/cache/common.go +++ b/cache/common.go @@ -7,6 +7,27 @@ import ( configv1alpha1 "github.com/padok-team/burrito/api/v1alpha1" ) +type CacheError struct { + Err error + Nil bool +} + +func (c *CacheError) Error() string { + return c.Err.Error() +} + +func NotFound(err error) bool { + ce, ok := err.(*CacheError) + if ok { + return ce.Nil + } + return false +} + +func (c *CacheError) NotFound() bool { + return c.Nil +} + type Prefix string const ( diff --git a/cache/memory.go b/cache/memory.go index 0a506dd6..4bddffd3 100644 --- a/cache/memory.go +++ b/cache/memory.go @@ -1,8 +1,6 @@ package cache -import ( - "errors" -) +import "errors" type MemoryCache struct { data map[string][]byte @@ -16,7 +14,10 @@ func NewMemoryCache() *MemoryCache { func (m *MemoryCache) Get(key string) ([]byte, error) { if _, ok := m.data[key]; !ok { - return nil, errors.New("key not found") + return nil, &CacheError{ + Err: errors.New("key not found"), + Nil: true, + } } return m.data[key], nil } diff --git a/cache/redis/redis.go b/cache/redis/redis.go index 731be28f..82fbc587 100644 --- a/cache/redis/redis.go +++ b/cache/redis/redis.go @@ -5,6 +5,7 @@ import ( "time" "github.com/go-redis/redis/v8" + "github.com/padok-team/burrito/cache" ) type Cache struct { @@ -23,8 +24,17 @@ func New(addr string, password string, db int) *Cache { func (c *Cache) Get(key string) ([]byte, error) { val, err := c.Client.Get(context.TODO(), key).Result() + if err == redis.Nil { + return nil, &cache.CacheError{ + Err: err, + Nil: true, + } + } if err != nil { - return nil, err + return nil, &cache.CacheError{ + Err: err, + Nil: false, + } } return []byte(val), nil } diff --git a/controllers/terraformlayer_controller.go b/controllers/terraformlayer_controller.go index 9b69eaf6..657f1db3 100644 --- a/controllers/terraformlayer_controller.go +++ b/controllers/terraformlayer_controller.go @@ -118,10 +118,34 @@ type TerraformLayerConditions struct { } func (t *TerraformLayerConditions) Evaluate() (func(ctx context.Context, c client.Client) ctrl.Result, []metav1.Condition) { - isTerraformRunning := t.IsRunning.Evaluate(*t.Cache, t.Resource) - isPlanArtifactUpToDate := t.IsPlanArtifactUpToDate.Evaluate(*t.Cache, t.Resource) - isApplyUpToDate := t.IsApplyUpToDate.Evaluate(*t.Cache, t.Resource) - hasTerraformFailed := t.HasFailed.Evaluate(*t.Cache, t.Resource) + isTerraformRunning, err := t.IsRunning.Evaluate(*t.Cache, t.Resource) + if err != nil { + log.Log.Info("Something went wrong with conditions evaluation requeuing") + return func(ctx context.Context, c client.Client) ctrl.Result { + return ctrl.Result{RequeueAfter: time.Minute * 2} + }, nil + } + isPlanArtifactUpToDate, err := t.IsPlanArtifactUpToDate.Evaluate(*t.Cache, t.Resource) + if err != nil { + log.Log.Info("Something went wrong with conditions evaluation requeuing") + return func(ctx context.Context, c client.Client) ctrl.Result { + return ctrl.Result{RequeueAfter: time.Minute * 2} + }, nil + } + isApplyUpToDate, err := t.IsApplyUpToDate.Evaluate(*t.Cache, t.Resource) + if err != nil { + log.Log.Info("Something went wrong with conditions evaluation requeuing") + return func(ctx context.Context, c client.Client) ctrl.Result { + return ctrl.Result{RequeueAfter: time.Minute * 2} + }, nil + } + hasTerraformFailed, err := t.HasFailed.Evaluate(*t.Cache, t.Resource) + if err != nil { + log.Log.Info("Something went wrong with conditions evaluation requeuing") + return func(ctx context.Context, c client.Client) ctrl.Result { + return ctrl.Result{RequeueAfter: time.Minute * 2} + }, nil + } conditions := []metav1.Condition{t.IsRunning.Condition, t.IsPlanArtifactUpToDate.Condition, t.IsApplyUpToDate.Condition, t.HasFailed.Condition} cache := *t.Cache switch { @@ -213,7 +237,7 @@ type TerraformRunning struct { Condition metav1.Condition } -func (c *TerraformRunning) Evaluate(cache internal.Cache, t *configv1alpha1.TerraformLayer) bool { +func (c *TerraformRunning) Evaluate(cache internal.Cache, t *configv1alpha1.TerraformLayer) (bool, error) { c.Condition = metav1.Condition{ Type: IsRunning, ObservedGeneration: t.GetObjectMeta().GetGeneration(), @@ -221,23 +245,26 @@ func (c *TerraformRunning) Evaluate(cache internal.Cache, t *configv1alpha1.Terr } key := internal.GenerateKey(internal.Lock, t) _, err := cache.Get(key) - if err != nil { + if internal.NotFound(err) { c.Condition.Reason = "NoLockInCache" c.Condition.Message = "No lock has been found in Cache. Terraform is not running on this layer." c.Condition.Status = metav1.ConditionFalse - return false + return false, nil + } + if err != nil { + return true, err } c.Condition.Reason = "LockInCache" c.Condition.Message = "Lock has been found in Cache. Terraform is already running on this layer." c.Condition.Status = metav1.ConditionTrue - return true + return true, nil } type TerraformPlanArtifactUpToDate struct { Condition metav1.Condition } -func (c *TerraformPlanArtifactUpToDate) Evaluate(cache internal.Cache, t *configv1alpha1.TerraformLayer) bool { +func (c *TerraformPlanArtifactUpToDate) Evaluate(cache internal.Cache, t *configv1alpha1.TerraformLayer) (bool, error) { c.Condition = metav1.Condition{ Type: IsPlanArtifactUpToDate, ObservedGeneration: t.GetObjectMeta().GetGeneration(), @@ -245,11 +272,14 @@ func (c *TerraformPlanArtifactUpToDate) Evaluate(cache internal.Cache, t *config } key := internal.GenerateKey(internal.LastPlanDate, t) value, err := cache.Get(key) - if err != nil { + if internal.NotFound(err) { c.Condition.Reason = "NoTimestampInCache" c.Condition.Message = "The last plan date is not in cache." c.Condition.Status = metav1.ConditionFalse - return false + return false, nil + } + if err != nil { + return true, err } unixTimestamp, _ := strconv.ParseInt(string(value), 10, 64) lastPlanDate := time.Unix(unixTimestamp, 0) @@ -259,19 +289,19 @@ func (c *TerraformPlanArtifactUpToDate) Evaluate(cache internal.Cache, t *config c.Condition.Reason = "PlanIsRecent" c.Condition.Message = "The plan has been made less than 20 minutes ago." c.Condition.Status = metav1.ConditionTrue - return true + return true, nil } c.Condition.Reason = "PlanIsTooOld" c.Condition.Message = "The plan has been made more than 20 minutes ago." c.Condition.Status = metav1.ConditionFalse - return false + return false, nil } type TerraformApplyUpToDate struct { Condition metav1.Condition } -func (c *TerraformApplyUpToDate) Evaluate(cache internal.Cache, t *configv1alpha1.TerraformLayer) bool { +func (c *TerraformApplyUpToDate) Evaluate(cache internal.Cache, t *configv1alpha1.TerraformLayer) (bool, error) { c.Condition = metav1.Condition{ Type: IsApplyUpToDate, ObservedGeneration: t.GetObjectMeta().GetGeneration(), @@ -279,37 +309,43 @@ func (c *TerraformApplyUpToDate) Evaluate(cache internal.Cache, t *configv1alpha } key := internal.GenerateKey(internal.LastPlannedArtifact, t) planHash, err := cache.Get(key) - if err != nil { + if internal.NotFound(err) { c.Condition.Reason = "NoPlanYet" c.Condition.Message = "No plan has run yet, Layer might be new" c.Condition.Status = metav1.ConditionTrue - return true + return true, nil + } + if err != nil { + return true, err } key = internal.GenerateKey(internal.LastAppliedArtifact, t) applyHash, err := cache.Get(key) - if err != nil { + if internal.NotFound(err) { c.Condition.Reason = "NoApplyHasRan" c.Condition.Message = "Apply has not ran yet but a plan is available, launching apply" c.Condition.Status = metav1.ConditionFalse - return false + return false, nil + } + if err != nil { + return true, err } if bytes.Compare(planHash, applyHash) != 0 { c.Condition.Reason = "NewPlanAvailable" c.Condition.Message = "Apply will run." c.Condition.Status = metav1.ConditionFalse - return false + return false, nil } c.Condition.Reason = "ApplyUpToDate" c.Condition.Message = "Last planned artifact is the same as the last applied one" c.Condition.Status = metav1.ConditionTrue - return true + return true, nil } type TerraformFailure struct { Condition metav1.Condition } -func (c *TerraformFailure) Evaluate(cache internal.Cache, t *configv1alpha1.TerraformLayer) bool { +func (c *TerraformFailure) Evaluate(cache internal.Cache, t *configv1alpha1.TerraformLayer) (bool, error) { c.Condition = metav1.Condition{ Type: HasFailed, ObservedGeneration: t.GetObjectMeta().GetGeneration(), @@ -317,17 +353,20 @@ func (c *TerraformFailure) Evaluate(cache internal.Cache, t *configv1alpha1.Terr } key := internal.GenerateKey(internal.RunResult, t) result, err := cache.Get(key) - if err != nil { + if internal.NotFound(err) { c.Condition.Reason = "NoRunYet" c.Condition.Message = "Terraform has not ran yet" c.Condition.Status = metav1.ConditionFalse - return false + return false, nil + } + if err != nil { + return true, err } if string(result) == "0" { c.Condition.Reason = "RunExitedGracefully" c.Condition.Message = "Last run exited gracefully" c.Condition.Status = metav1.ConditionFalse - return false + return false, nil } c.Condition.Status = metav1.ConditionTrue key = internal.GenerateKey(internal.RunMessage, t) @@ -338,5 +377,5 @@ func (c *TerraformFailure) Evaluate(cache internal.Cache, t *configv1alpha1.Terr } c.Condition.Reason = "TerraformRunFailure" c.Condition.Message = string(message) - return true + return true, nil }