// expandTaskSelector expands strings inside task selectors. func expandTaskSelector(ts taskSelector, exp command.Expansions) (taskSelector, error) { newTS := taskSelector{} newName, err := exp.ExpandString(ts.Name) if err != nil { return newTS, fmt.Errorf("expanding name: %v", err) } newTS.Name = newName if v := ts.Variant; v != nil { if len(v.matrixSelector) > 0 { newMS, err := expandMatrixDefinition(v.matrixSelector, exp) if err != nil { return newTS, fmt.Errorf("expanding variant: %v", err) } newTS.Variant = &variantSelector{ matrixSelector: newMS, } } else { selector, err := exp.ExpandString(v.stringSelector) if err != nil { return newTS, fmt.Errorf("expanding variant: %v", err) } newTS.Variant = &variantSelector{ stringSelector: selector, } } } return newTS, nil }
func expandString(inputVal reflect.Value, expansions *command.Expansions) error { expanded, err := expansions.ExpandString(inputVal.String()) if err != nil { return err } inputVal.SetString(expanded) return nil }
// expandStrings expands a slice of strings. func expandStrings(strings []string, exp command.Expansions) ([]string, error) { var expanded []string for _, s := range strings { newS, err := exp.ExpandString(s) if err != nil { return nil, err } expanded = append(expanded, newS) } return expanded, nil }
func (sc *StatsCollector) expandCommands(exp *command.Expansions) { expandedCmds := []string{} for _, cmd := range sc.Cmds { expanded, err := exp.ExpandString(cmd) if err != nil { sc.logger.Logf(slogger.WARN, "Couldn't expand '%v': %v", cmd, err) continue } expandedCmds = append(expandedCmds, expanded) } sc.Cmds = expandedCmds }
// expandExpansions expands expansion maps. func expandExpansions(in, exp command.Expansions) (command.Expansions, error) { newExp := command.Expansions{} for k, v := range in { newK, err := exp.ExpandString(k) if err != nil { return nil, err } newV, err := exp.ExpandString(v) if err != nil { return nil, err } newExp[newK] = newV } return newExp, nil }
func TestExpansionsPlugin(t *testing.T) { Convey("Should be able to update expansions", t, func() { updateCommand := UpdateCommand{ Updates: []PutCommandParams{ { Key: "base", Value: "eggs", }, { Key: "topping", Concat: ",sausage", }, }, } expansions := command.Expansions{} expansions.Put("base", "not eggs") expansions.Put("topping", "bacon") taskConfig := model.TaskConfig{ Expansions: &expansions, } updateCommand.ExecuteUpdates(&taskConfig) So(expansions.Get("base"), ShouldEqual, "eggs") So(expansions.Get("topping"), ShouldEqual, "bacon,sausage") }) }
// Helper function to expand a map. Returns expanded version with // both keys and values expanded func expandMap(inputMap reflect.Value, expansions *command.Expansions) error { if inputMap.Type().Key().Kind() != reflect.String { return fmt.Errorf("input map to expand must have keys of string type") } // iterate through keys and value, expanding them for _, key := range inputMap.MapKeys() { expandedKeyString, err := expansions.ExpandString(key.String()) if err != nil { return fmt.Errorf("could not expand key %v: %v", key.String(), err) } // expand and set new value val := inputMap.MapIndex(key) expandedVal := reflect.Value{} switch val.Type().Kind() { case reflect.String: expandedValString, err := expansions.ExpandString(val.String()) if err != nil { return fmt.Errorf("could not expand value %v: %v", val.String(), err) } expandedVal = reflect.ValueOf(expandedValString) case reflect.Map: if err := expandMap(val, expansions); err != nil { return fmt.Errorf("could not expand value %v: %v", val.String(), err) } expandedVal = val default: return fmt.Errorf( "could not expand value %v: must be string, map, or struct", val.String()) } // unset unexpanded key then set expanded key inputMap.SetMapIndex(key, reflect.Value{}) inputMap.SetMapIndex(reflect.ValueOf(expandedKeyString), expandedVal) } return nil }
// expandParserBVTask expands strings inside parserBVTs. func expandParserBVTask(pbvt parserBVTask, exp command.Expansions) (parserBVTask, error) { var err error newTask := pbvt newTask.Name, err = exp.ExpandString(pbvt.Name) if err != nil { return parserBVTask{}, fmt.Errorf("expanding name: %v", err) } newTask.RunOn, err = expandStrings(pbvt.RunOn, exp) if err != nil { return parserBVTask{}, fmt.Errorf("expanding run_on: %v", err) } newTask.Distros, err = expandStrings(pbvt.Distros, exp) if err != nil { return parserBVTask{}, fmt.Errorf("expanding distros: %v", err) } var newDeps parserDependencies for i, d := range pbvt.DependsOn { newDep := d newDep.Status, err = exp.ExpandString(d.Status) if err != nil { return parserBVTask{}, fmt.Errorf("expanding depends_on[%v].status: %v", i, err) } newDep.taskSelector, err = expandTaskSelector(d.taskSelector, exp) if err != nil { return parserBVTask{}, fmt.Errorf("expanding depends_on[%v]: %v", i, err) } newDeps = append(newDeps, newDep) } newTask.DependsOn = newDeps var newReqs taskSelectors for i, r := range pbvt.Requires { newReq, err := expandTaskSelector(r, exp) if err != nil { return parserBVTask{}, fmt.Errorf("expanding requires[%v]: %v", i, err) } newReqs = append(newReqs, newReq) } newTask.Requires = newReqs return newTask, nil }
// buildMatrixVariant does the heavy lifting of building a matrix variant based on axis information. // We do this by iterating over all axes and merging the axis value's settings when applicable. Expansions // are evaluated during this process. Rules are parsed and added to the resulting parserBV for later // excecution. func buildMatrixVariant(axes []matrixAxis, mv matrixValue, m *matrix, ase *axisSelectorEvaluator) (*parserBV, error) { v := parserBV{ matrixVal: mv, matrixId: m.Id, Stepback: m.Stepback, BatchTime: m.BatchTime, Modules: m.Modules, RunOn: m.RunOn, Expansions: *command.NewExpansions(mv), } // we declare a separate expansion map for evaluating the display name displayNameExp := command.Expansions{} // build up the variant id while iterating through axis values idBuf := bytes.Buffer{} idBuf.WriteString(m.Id) idBuf.WriteString("__") // track how many axes we cover, so we know the value is only using real axes usedAxes := 0 // we must iterate over axis definitions to have a consistent ordering for our axis priority for _, a := range axes { // skip any axes that aren't used in the variant's definition if _, ok := mv[a.Id]; !ok { continue } usedAxes++ axisVal, err := a.find(mv[a.Id]) if err != nil { return nil, err } if err := v.mergeAxisValue(axisVal); err != nil { return nil, fmt.Errorf("processing axis value %v,%v: %v", a.Id, axisVal.Id, err) } // for display names, fall back to the axis values id so we have *something* if axisVal.DisplayName != "" { displayNameExp.Put(a.Id, axisVal.DisplayName) } else { displayNameExp.Put(a.Id, axisVal.Id) } // append to the variant's name idBuf.WriteString(a.Id) idBuf.WriteRune('~') idBuf.WriteString(axisVal.Id) if usedAxes < len(mv) { idBuf.WriteRune('_') } } if usedAxes != len(mv) { // we could make this error more helpful at the expense of extra complexity return nil, fmt.Errorf("cell %v uses undefined axes", mv) } v.Name = idBuf.String() disp, err := displayNameExp.ExpandString(m.DisplayName) if err != nil { return nil, fmt.Errorf("processing display name: %v", err) } v.DisplayName = disp // add final matrix-level tags and tasks if err := v.mergeAxisValue(axisValue{Tags: m.Tags}); err != nil { return nil, fmt.Errorf("processing matrix tags: %v", err) } for _, t := range m.Tasks { expTask, err := expandParserBVTask(t, v.Expansions) if err != nil { return nil, fmt.Errorf("processing task %v: %v", t.Name, err) } v.Tasks = append(v.Tasks, expTask) } // evaluate rules for matching matrix values for i, rule := range m.Rules { r, err := expandRule(rule, v.Expansions) if err != nil { return nil, fmt.Errorf("processing rule[%v]: %v", i, err) } matchers, errs := r.If.evaluatedCopies(ase) // we could cache this if len(errs) > 0 { return nil, fmt.Errorf("evaluating rules for matrix %v: %v", m.Id, errs) } if matchers.contain(mv) { if r.Then.Set != nil { if err := v.mergeAxisValue(*r.Then.Set); err != nil { return nil, fmt.Errorf("evaluating %v rule %v: %v", m.Id, i, err) } } // we append add/remove task rules internally and execute them // during task evaluation, when other tasks are being evaluated. if len(r.Then.RemoveTasks) > 0 || len(r.Then.AddTasks) > 0 { v.matrixRules = append(v.matrixRules, r.Then) } } } return &v, nil }