vorteil/direktiv

View on GitHub
pkg/flow/states/getter.go

Summary

Maintainability
B
6 hrs
Test Coverage
package states

import (
    "context"
    "encoding/json"
    "errors"
    "fmt"
    "time"

    derrors "github.com/direktiv/direktiv/pkg/flow/errors"
    "github.com/direktiv/direktiv/pkg/model"
    "github.com/direktiv/direktiv/pkg/utils"
    "github.com/google/uuid"
)

//nolint:gochecknoinits
func init() {
    RegisterState(model.StateTypeGetter, Getter)
}

type getterLogic struct {
    *model.GetterState
    Instance
}

func Getter(instance Instance, state model.State) (Logic, error) {
    getter, ok := state.(*model.GetterState)
    if !ok {
        return nil, derrors.NewInternalError(errors.New("bad state object"))
    }

    sl := new(getterLogic)
    sl.Instance = instance
    sl.GetterState = getter

    return sl, nil
}

func (logic *getterLogic) Run(ctx context.Context, wakedata []byte) (*Transition, error) {
    err := scheduleOnce(logic, wakedata)
    if err != nil {
        return nil, err
    }

    var vars []VariableSelector
    var ptrs []string

    m := make(map[string]interface{})

    for idx, v := range logic.Variables {
        key := ""
        var selector VariableSelector

        x, err := jqOne(logic.GetInstanceData(), v.Key) //nolint:contextcheck
        if err != nil {
            return nil, err
        }

        if x != nil {
            if str, ok := x.(string); ok {
                key = str
            }
        }

        if key == "" {
            return nil, derrors.NewCatchableError(ErrCodeJQNotString, "failed to evaluate key as a string for variable at index [%v]", idx)
        }

        if ok := utils.MatchesVarRegex(key); !ok && v.Scope != utils.VarScopeFileSystem {
            return nil, derrors.NewCatchableError(ErrCodeInvalidVariableKey, "variable key must match regex: %s (got: %s)", utils.RegexPattern, key)
        }

        as := key
        if v.As != "" {
            as = v.As
        }

        selector.Key = key
        selector.Scope = v.Scope

        switch v.Scope {
        case "":
            selector.Scope = utils.VarScopeInstance
            fallthrough

        case utils.VarScopeInstance:
            fallthrough

        case utils.VarScopeThread:
            fallthrough

        case utils.VarScopeWorkflow:
            fallthrough

        case utils.VarScopeFileSystem:
            fallthrough

        case utils.VarScopeNamespace:
            vars = append(vars, selector)
            ptrs = append(ptrs, as)

        case utils.VarScopeSystem:

            value, err := valueForSystem(ctx, key, logic.Instance)
            if err != nil {
                return nil, derrors.NewInternalError(err)
            }

            m[as] = value

        default:
            return nil, derrors.NewInternalError(errors.New("invalid scope"))
        }
    }

    results, err := logic.GetVariables(ctx, vars)
    if err != nil {
        return nil, err
    }

    for idx := range results {
        result := results[idx]
        as := ptrs[idx]

        var x interface{}

        x = nil

        if len(result.Data) != 0 {
            err = json.Unmarshal(result.Data, &x)
            if err != nil {
                x = result.Data
            }
        }

        m[as] = x
    }

    err = logic.StoreData("var", m)
    if err != nil {
        return nil, derrors.NewInternalError(err)
    }

    return &Transition{
        Transform: logic.Transform,
        NextState: logic.Transition,
    }, nil
}

func valueForSystem(ctx context.Context, key string, instance Instance) (interface{}, error) {
    var ret interface{}

    switch key {
    case "instance":
        ret = instance.GetInstanceID()
    case "uuid":
        ret = uuid.New().String()
    case "epoch":
        ret = time.Now().UTC().Unix()
    case "trace":
        ret = instance.GetTraceID(ctx)
    default:
        return nil, fmt.Errorf("unknown system key %s", key)
    }

    return ret, nil
}