Skip to content

Commit

Permalink
feat(cache): implement not found generic error and use it to fail fas…
Browse files Browse the repository at this point in the history
…t in case cache has an issue (#22)
  • Loading branch information
Alan-pad authored Dec 23, 2022
1 parent 7a3b1ef commit 0dcac6b
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 30 deletions.
21 changes: 21 additions & 0 deletions cache/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
9 changes: 5 additions & 4 deletions cache/memory.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package cache

import (
"errors"
)
import "errors"

type MemoryCache struct {
data map[string][]byte
Expand All @@ -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
}
Expand Down
12 changes: 11 additions & 1 deletion cache/redis/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

"github.com/go-redis/redis/v8"
"github.com/padok-team/burrito/cache"
)

type Cache struct {
Expand All @@ -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
}
Expand Down
89 changes: 64 additions & 25 deletions controllers/terraformlayer_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -213,43 +237,49 @@ 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(),
Status: metav1.ConditionUnknown,
}
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(),
Status: metav1.ConditionUnknown,
}
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)
Expand All @@ -259,75 +289,84 @@ 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(),
Status: metav1.ConditionUnknown,
}
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(),
Status: metav1.ConditionUnknown,
}
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)
Expand All @@ -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
}

0 comments on commit 0dcac6b

Please sign in to comment.