Beispiel #1
0
func mapList(source []yaml.Node, names []string, e Expression, binding Binding) ([]yaml.Node, EvaluationInfo, bool) {
	inp := map[string]yaml.Node{}
	result := []yaml.Node{}
	info := DefaultInfo()

	for i, n := range source {
		debug.Debug("map:  mapping for %d: %+v\n", i, n)
		inp[names[0]] = n
		if len(names) > 1 {
			inp[names[1]] = node(i)
		}
		ctx := MapContext{binding, inp}
		mapped, info, ok := e.Evaluate(ctx)
		if !ok {
			debug.Debug("map:  %d %+v: failed\n", i, n)
			return nil, info, false
		}

		_, ok = mapped.Value().(Expression)
		if ok {
			debug.Debug("map:  %d unresolved  -> KEEP\n")
			return nil, info, true
		}
		debug.Debug("map:  %d --> %+v\n", i, mapped)
		result = append(result, mapped)
	}
	return result, info, true
}
Beispiel #2
0
func func_exec(arguments []interface{}, binding Binding) (yaml.Node, EvaluationInfo, bool) {
	info := DefaultInfo()

	if len(arguments) < 1 {
		return nil, info, false
	}
	args := []string{}
	debug.Debug("exec: found %d arguments for call\n", len(arguments))
	for i, arg := range arguments {
		list, ok := arg.([]yaml.Node)
		if i == 0 && ok {
			debug.Debug("exec: found array as first argument\n")
			if len(arguments) == 1 && len(list) > 0 {
				// handle single list argument to gain command and argument
				for j, arg := range list {
					v, ok := getArg(j, arg.Value())
					if !ok {
						info.Issue = "command argument must be string"
						return nil, info, false
					}
					args = append(args, v)
				}
			} else {
				info.Issue = "list not allowed for command argument"
				return nil, info, false
			}
		} else {
			v, ok := getArg(i, arg)
			if !ok {
				info.Issue = "command argument must be string"
				return nil, info, false
			}
			args = append(args, v)
		}
	}
	result, err := cachedExecute(args)
	if err != nil {
		info.Issue = "execution '" + args[0] + "' failed"
		// expression set to undefined
		return nil, info, false
	}

	str := string(result)
	execYML, err := yaml.Parse("exec", result)
	if strings.HasPrefix(str, "---\n") && err == nil {
		debug.Debug("exec: found yaml result %+v\n", execYML)
		return execYML, info, true
	} else {
		if strings.HasSuffix(str, "\n") {
			str = str[:len(str)-1]
		}
		int64YML, err := strconv.ParseInt(str, 10, 64)
		if err == nil {
			debug.Debug("exec: found integer result: %s\n", int64YML)
			return node(int64YML), info, true
		}
		debug.Debug("exec: found string result: %s\n", string(result))
		return node(str), info, true
	}
}
Beispiel #3
0
func flowList(root yaml.Node, env Environment) yaml.Node {
	rootList := root.Value().([]yaml.Node)

	debug.Debug("HANDLE LIST %v\n", env.Path)
	merged, process, replaced, redirectPath, keyName := processMerges(root, rootList, env)

	if process {

		newList := []yaml.Node{}
		if len(redirectPath) > 0 {
			env = env.RedirectOverwrite(redirectPath)
		}
		for idx, val := range merged {
			step := stepName(idx, val, keyName)
			debug.Debug("  step %s\n", step)
			newList = append(newList, flow(val, env.WithPath(step), false))
		}

		merged = newList
	}

	if keyName != "" {
		root = yaml.KeyNameNode(root, keyName)
	}
	debug.Debug("LIST DONE (%s)%v\n", root.KeyName(), env.Path)
	if replaced {
		return yaml.ReplaceNode(merged, root, redirectPath)
	}
	if len(redirectPath) > 0 {
		return yaml.RedirectNode(merged, root, redirectPath)
	}
	return yaml.SubstituteNode(merged, root)
}
Beispiel #4
0
func (e MapExpr) Evaluate(binding Binding) (yaml.Node, EvaluationInfo, bool) {
	resolved := true

	debug.Debug("evaluate mapping\n")
	value, info, ok := ResolveExpressionOrPushEvaluation(&e.A, &resolved, nil, binding)
	if !ok {
		return nil, info, false
	}
	if !resolved {
		return node(e), info, ok
	}

	debug.Debug("map: using expression %+v\n", e.B)
	var result []yaml.Node
	switch value.(type) {
	case []yaml.Node:
		result, info, ok = mapList(value.([]yaml.Node), e.Names, e.B, binding)

	case map[string]yaml.Node:
		result, info, ok = mapMap(value.(map[string]yaml.Node), e.Names, e.B, binding)

	default:
		info.Issue = "map or list required for mapping"
		return nil, info, false
	}
	if !ok {
		return nil, info, false
	}
	if result == nil {
		return node(e), info, true
	}
	debug.Debug("map: --> %+v\n", result)
	return node(result), info, true
}
Beispiel #5
0
func (e ReferenceExpr) Evaluate(binding Binding) (yaml.Node, EvaluationInfo, bool) {
	var step yaml.Node
	var ok bool

	info := DefaultInfo()
	fromRoot := e.Path[0] == ""

	debug.Debug("reference: %v\n", e.Path)
	for i := 0; i < len(e.Path); i++ {
		if fromRoot {
			step, ok = binding.FindFromRoot(e.Path[1 : i+1])
		} else {
			step, ok = binding.FindReference(e.Path[:i+1])
		}

		debug.Debug("  %d: %v %+v\n", i, ok, step)
		if !ok {
			info.Issue = fmt.Sprintf("'%s' not found", strings.Join(e.Path, "."))
			return nil, info, false
		}

		if !isLocallyResolved(step) {
			debug.Debug("  unresolved\n")
			return node(e), info, true
		}
	}

	if !isResolved(step) {
		debug.Debug("  unresolved\n")
		return node(e), info, true
	}

	debug.Debug("reference %v -> %+v\n", e.Path, step)
	return yaml.ReferencedNode(step), info, true
}
Beispiel #6
0
func mapMap(source map[string]yaml.Node, names []string, e Expression, binding Binding) ([]yaml.Node, EvaluationInfo, bool) {
	inp := map[string]yaml.Node{}
	result := []yaml.Node{}
	info := DefaultInfo()

	keys := getSortedKeys(source)
	for _, k := range keys {
		n := source[k]
		debug.Debug("map:  mapping for %s: %+v\n", k, n)
		inp[names[0]] = n
		if len(names) > 1 {
			inp[names[1]] = node(k)
		}
		ctx := MapContext{binding, inp}
		mapped, info, ok := e.Evaluate(ctx)
		if !ok {
			debug.Debug("map:  %s %+v: failed\n", k, n)
			return nil, info, false
		}

		_, ok = mapped.Value().(Expression)
		if ok {
			debug.Debug("map:  %d unresolved  -> KEEP\n")
			return nil, info, true
		}
		debug.Debug("map:  %s --> %+v\n", k, mapped)
		result = append(result, mapped)
	}
	return result, info, true
}
Beispiel #7
0
func processMerges(orig yaml.Node, root []yaml.Node, env Environment) ([]yaml.Node, bool, bool, []string, string) {
	spliced := []yaml.Node{}
	process := true
	keyName := orig.KeyName()
	replaced := orig.ReplaceFlag()
	redirectPath := orig.RedirectPath()

	for _, val := range root {
		if val == nil {
			continue
		}

		inlineNode, ok := yaml.UnresolvedListEntryMerge(val)
		if ok {
			debug.Debug("*** %+v\n", inlineNode.Value())
			_, initial := inlineNode.Value().(string)
			result := flow(inlineNode, env, false)
			if result.KeyName() != "" {
				keyName = result.KeyName()
			}
			debug.Debug("=== (%s)%+v\n", keyName, result)
			_, ok := result.Value().(dynaml.Expression)
			if ok {
				if simpleMergeCompatibilityCheck(initial, inlineNode) {
					continue
				}
				newMap := make(map[string]yaml.Node)
				newMap["<<"] = result
				val = yaml.SubstituteNode(newMap, orig)
				process = false
			} else {
				inline, ok := result.Value().([]yaml.Node)

				if ok {
					inlineNew := newEntries(inline, root, keyName)
					replaced = result.ReplaceFlag()
					redirectPath = result.RedirectPath()
					if replaced {
						spliced = inlineNew
						process = false
						break
					} else {
						spliced = append(spliced, inlineNew...)
					}
				}
				continue
			}
		}

		val, newKey := ProcessKeyTag(val)
		if newKey != "" {
			keyName = newKey
		}
		spliced = append(spliced, val)
	}

	debug.Debug("--> %+v  proc=%v replaced=%v redirect=%v key=%s\n", spliced, process, replaced, redirectPath, keyName)
	return spliced, process, replaced, redirectPath, keyName
}
Beispiel #8
0
func (c MapContext) FindReference(path []string) (yaml.Node, bool) {
	for name, node := range c.names {
		if len(path) >= 1 && path[0] == name {
			debug.Debug("map: catch find ref: %v\n", path)
			if len(path) == 1 {
				return node, true
			}
			return yaml.Find(node, path[1:]...)
		}
	}
	debug.Debug("map: forward find ref: %v\n", path)
	return c.Binding.FindReference(path)
}
Beispiel #9
0
func cachedExecute(args []string) ([]byte, error) {
	h := md5.New()
	for _, arg := range args {
		h.Write([]byte(arg))
	}
	hash := fmt.Sprintf("%x", h.Sum(nil))
	result := cache[hash]
	if result != nil {
		debug.Debug("exec: reusing cache %s for %v\n", hash, args)
		return result, nil
	}
	debug.Debug("exec: calling %v\n", args)
	cmd := exec.Command(args[0], args[1:]...)
	result, err := cmd.Output()
	cache[hash] = result
	return result, err
}
Beispiel #10
0
func Flow(source yaml.Node, stubs ...yaml.Node) (yaml.Node, error) {
	result := source

	for {
		debug.Debug("@@@ loop:  %+v\n", result)
		next := flow(result, Environment{Stubs: stubs}, true)
		debug.Debug("@@@ --->   %+v\n", next)

		if reflect.DeepEqual(result, next) {
			break
		}

		result = next
	}
	debug.Debug("@@@ Done\n")
	unresolved := dynaml.FindUnresolvedNodes(result)
	if len(unresolved) > 0 {
		return nil, dynaml.UnresolvedNodes{unresolved}
	}

	return result, nil
}
Beispiel #11
0
func flowString(root yaml.Node, env Environment) yaml.Node {

	sub := yaml.EmbeddedDynaml(root)
	if sub == nil {
		return root
	}
	debug.Debug("dynaml: %v: %s\n", env.Path, *sub)
	expr, err := dynaml.Parse(*sub, env.Path, env.StubPath)
	if err != nil {
		return root
	}

	return yaml.SubstituteNode(expr, root)
}
Beispiel #12
0
func (e MergeExpr) Evaluate(binding Binding) (yaml.Node, EvaluationInfo, bool) {
	var info EvaluationInfo
	if e.Redirect {
		info.RedirectPath = e.Path
	}
	info.KeyName = e.KeyName
	debug.Debug("/// lookup %v\n", e.Path)
	node, ok := binding.FindInStubs(e.Path)
	if ok {
		info.Replace = e.Replace
		info.Merged = true
	} else {
		info.Issue = fmt.Sprintf("'%s' not found in any stub", strings.Join(e.Path, "."))
	}
	return node, info, ok
}
Beispiel #13
0
func getArg(i int, value interface{}) (string, bool) {
	debug.Debug("arg %d: %+v\n", i, value)
	switch value.(type) {
	case string:
		return value.(string), true
	case int64:
		return strconv.FormatInt(value.(int64), 10), true
	default:
		if i == 0 || value == nil {
			return "", false
		}
		yaml, err := candiedyaml.Marshal(node(value))
		if err != nil {
			log.Fatalln("error marshalling manifest:", err)
		}
		return "---\n" + string(yaml), true
	}
}
Beispiel #14
0
func (e ConcatenationExpr) Evaluate(binding Binding) (yaml.Node, EvaluationInfo, bool) {
	resolved := true

	debug.Debug("CONCAT %+v,%+v\n", e.A, e.B)

	a, infoa, ok := ResolveExpressionOrPushEvaluation(&e.A, &resolved, nil, binding)
	if !ok {
		debug.Debug("  eval a failed\n")
		return nil, infoa, false
	}

	b, info, ok := ResolveExpressionOrPushEvaluation(&e.B, &resolved, &infoa, binding)
	if !ok {
		debug.Debug("  eval b failed\n")
		return nil, info, false
	}

	if !resolved {
		debug.Debug("  still unresolved operands\n")
		return node(e), info, true
	}

	debug.Debug("CONCAT resolved %+v,%+v\n", a, b)

	val, ok := concatenateStringAndInt(a, b)
	if ok {
		debug.Debug("CONCAT --> string %+v\n", val)
		return node(val), info, true
	}

	alist, aok := a.([]yaml.Node)
	if !aok {
		switch a.(type) {
		case map[string]yaml.Node:
			info.Issue = "first argument must be list or simple value"
		default:
			info.Issue = "simple value can only be concatenated with simple values"
		}
		return nil, info, false
	}

	switch b.(type) {
	case []yaml.Node:
		return node(append(alist, b.([]yaml.Node)...)), info, true
	case nil:
		return node(a), info, true
	default:
		return node(append(alist, node(b))), info, true
	}
}
Beispiel #15
0
func flowMap(root yaml.Node, env Environment) yaml.Node {
	processed := true
	rootMap := root.Value().(map[string]yaml.Node)

	env = env.WithScope(rootMap)

	redirect := root.RedirectPath()
	replace := root.ReplaceFlag()
	newMap := make(map[string]yaml.Node)

	sortedKeys := getSortedKeys(rootMap)

	debug.Debug("HANDLE MAP %v\n", env.Path)

	// iteration order matters for the "<<" operator, it must be the first key in the map that is handled
	for i := range sortedKeys {
		key := sortedKeys[i]
		val := rootMap[key]

		if key == "<<" {
			_, initial := val.Value().(string)
			base := flow(val, env, false)
			_, ok := base.Value().(dynaml.Expression)
			if ok {
				if simpleMergeCompatibilityCheck(initial, base) {
					continue
				}
				val = base
				processed = false
			} else {
				baseMap, ok := base.Value().(map[string]yaml.Node)
				if base != nil && base.RedirectPath() != nil {
					redirect = base.RedirectPath()
					env = env.RedirectOverwrite(redirect)
				}
				if ok {
					for k, v := range baseMap {
						newMap[k] = v
					}
				}
				replace = base.ReplaceFlag()
				if replace {
					break
				}
				continue
			}
		} else {
			if processed {
				val = flow(val, env.WithPath(key), true)
			}
		}

		debug.Debug("MAP (%s)%s\n", val.KeyName(), key)
		newMap[key] = val
	}

	debug.Debug("MAP DONE %v\n", env.Path)
	if replace {
		return yaml.ReplaceNode(newMap, root, redirect)
	}
	return yaml.RedirectNode(newMap, root, redirect)
}
Beispiel #16
0
func buildExpression(grammar *DynamlGrammar, path []string, stubPath []string) Expression {
	tokens := &tokenStack{}

	// flags for parsing merge options in merge expression
	// this expression is NOT recursive, therefore single flag variables are sufficient
	replace := false
	required := false
	keyName := ""

	for token := range grammar.Tokens() {
		contents := grammar.Buffer[token.begin:token.end]

		switch token.pegRule {
		case ruleDynaml:
			return tokens.Pop()
		case rulePrefer:
			tokens.Push(PreferExpr{tokens.Pop()})
		case ruleAuto:
			tokens.Push(AutoExpr{path})
		case ruleMerge:
			replace = false
			required = false
			keyName = ""
		case ruleSimpleMerge:
			debug.Debug("*** rule simple merge\n")
			redirect := !equals(path, stubPath)
			tokens.Push(MergeExpr{stubPath, redirect, replace, replace || required || redirect, keyName})
		case ruleRefMerge:
			debug.Debug("*** rule ref merge\n")
			rhs := tokens.Pop()
			tokens.Push(MergeExpr{rhs.(ReferenceExpr).Path, true, replace, true, keyName})
		case ruleReplace:
			replace = true
		case ruleRequired:
			required = true
		case ruleOn:
			keyName = tokens.Pop().(nameHelper).name

		case ruleReference:
			tokens.Push(ReferenceExpr{strings.Split(contents, ".")})

		case ruleInteger:
			val, err := strconv.ParseInt(contents, 10, 64)
			if err != nil {
				panic(err)
			}

			tokens.Push(IntegerExpr{val})
		case ruleNil:
			tokens.Push(NilExpr{})
		case ruleBoolean:
			tokens.Push(BooleanExpr{contents == "true"})
		case ruleString:
			val := strings.Replace(contents[1:len(contents)-1], `\"`, `"`, -1)
			tokens.Push(StringExpr{val})
		case ruleOr:
			rhs := tokens.Pop()
			lhs := tokens.Pop()

			tokens.Push(OrExpr{A: lhs, B: rhs})
		case ruleConcatenation:
			rhs := tokens.Pop()
			lhs := tokens.Pop()

			tokens.Push(ConcatenationExpr{A: lhs, B: rhs})
		case ruleAddition:
			rhs := tokens.Pop()
			lhs := tokens.Pop()

			tokens.Push(AdditionExpr{A: lhs, B: rhs})
		case ruleSubtraction:
			rhs := tokens.Pop()
			lhs := tokens.Pop()

			tokens.Push(SubtractionExpr{A: lhs, B: rhs})
		case ruleMultiplication:
			rhs := tokens.Pop()
			lhs := tokens.Pop()

			tokens.Push(MultiplicationExpr{A: lhs, B: rhs})
		case ruleDivision:
			rhs := tokens.Pop()
			lhs := tokens.Pop()

			tokens.Push(DivisionExpr{A: lhs, B: rhs})
		case ruleModulo:
			rhs := tokens.Pop()
			lhs := tokens.Pop()

			tokens.Push(ModuloExpr{A: lhs, B: rhs})
		case ruleCall:
			tokens.Push(CallExpr{
				Name:      tokens.Pop().(nameHelper).name,
				Arguments: tokens.GetExpressionList(),
			})
		case ruleName:
			tokens.Push(nameHelper{name: contents})

		case ruleMapping:
			rhs := tokens.Pop()
			names := []string{tokens.Pop().(nameHelper).name}
			lhs := tokens.Pop()
			name, ok := lhs.(nameHelper)
			if ok {
				names = append(names, name.name)
				lhs = tokens.Pop()
			}
			tokens.Push(MapExpr{A: lhs, Names: names, B: rhs})

		case ruleList:
			seq := tokens.GetExpressionList()
			tokens.Push(ListExpr{seq})

		case ruleNextExpression:
			rhs := tokens.Pop()

			list := tokens.PopExpressionList()
			list.list = append(list.list, rhs)
			tokens.Push(list)

		case ruleContents, ruleArguments:
			tokens.SetExpressionList(tokens.PopExpressionList())

		case ruleKey:
		case ruleGrouped:
		case ruleLevel0, ruleLevel1, ruleLevel2, ruleLevel3, ruleLevel4:
		case ruleExpression:
		case rulews:
		case rulereq_ws:
		default:
			panic("unhandled:" + rul3s[token.pegRule])
		}
	}

	panic("unreachable")
}
Beispiel #17
0
func flow(root yaml.Node, env Environment, shouldOverride bool) yaml.Node {
	if root == nil {
		return root
	}

	replace := root.ReplaceFlag()
	redirect := root.RedirectPath()
	preferred := root.Preferred()
	merged := root.Merged()
	keyName := root.KeyName()

	if redirect != nil {
		env = env.RedirectOverwrite(redirect)
	}

	if !replace {
		switch val := root.Value().(type) {
		case map[string]yaml.Node:
			return flowMap(root, env)

		case []yaml.Node:
			return flowList(root, env)

		case dynaml.Expression:
			debug.Debug("??? eval %+v\n", val)
			result, info, ok := val.Evaluate(env)
			if !ok {
				root = yaml.IssueNode(root, info.Issue)
				debug.Debug("??? failed ---> KEEP\n")
				if !shouldOverride {
					return root
				}
				replace = replace || info.Replace
			} else {
				_, ok = result.Value().(string)
				if ok {
					// map result to potential expression
					result = flowString(result, env)
				}
				_, expr := result.Value().(dynaml.Expression)

				// preserve accumulated node attributes
				if preferred || info.Preferred {
					debug.Debug("   PREFERRED")
					result = yaml.PreferredNode(result)
				}

				if info.KeyName != "" {
					keyName = info.KeyName
					result = yaml.KeyNameNode(result, keyName)
				}
				if len(info.RedirectPath) > 0 {
					redirect = info.RedirectPath
				}
				if len(redirect) > 0 {
					debug.Debug("   REDIRECT -> %v\n", redirect)
					result = yaml.RedirectNode(result.Value(), result, redirect)
				}

				if replace || info.Replace {
					debug.Debug("   REPLACE\n")
					result = yaml.ReplaceNode(result.Value(), result, redirect)
				} else {
					if merged || info.Merged {
						debug.Debug("   MERGED\n")
						result = yaml.MergedNode(result)
					}
				}
				if expr || result.Merged() || !shouldOverride || result.Preferred() {
					debug.Debug("   prefer expression over override")
					debug.Debug("??? ---> %+v\n", result)
					return result
				}
				debug.Debug("???   try override")
				replace = result.ReplaceFlag()
				root = result
			}

		case string:
			result := flowString(root, env)
			if result != nil {
				_, ok := result.Value().(dynaml.Expression)
				if ok {
					// analyse expression before overriding
					return result
				}
			}
		}
	}

	if !merged && shouldOverride {
		debug.Debug("/// lookup stub %v -> %v\n", env.Path, env.StubPath)
		overridden, found := env.FindInStubs(env.StubPath)
		if found {
			root = overridden
			if keyName != "" {
				root = yaml.KeyNameNode(root, keyName)
			}
			if replace {
				return yaml.ReplaceNode(root.Value(), root, redirect)
			}
			if redirect != nil {
				return yaml.RedirectNode(root.Value(), root, redirect)
			}
			if merged {
				return yaml.MergedNode(root)
			}
		}
	}

	return root
}