Example #1
0
// computeVars takes the State and given RawConfig and processes all
// the variables. This dynamically discovers the attributes instead of
// using a static map[string]string that the genericWalkFn uses.
func (c *Context) computeVars(raw *config.RawConfig) error {
	// If there isn't a raw configuration, don't do anything
	if raw == nil {
		return nil
	}

	// If there are on variables, then we're done
	if len(raw.Variables) == 0 {
		return nil
	}

	// Start building up the variables. First, defaults
	vs := make(map[string]string)
	for k, v := range c.defaultVars {
		vs[k] = v
	}

	// Next, the actual computed variables
	for n, rawV := range raw.Variables {
		switch v := rawV.(type) {
		case *config.ResourceVariable:
			var attr string
			var err error
			if v.Multi && v.Index == -1 {
				attr, err = c.computeResourceMultiVariable(v)
			} else {
				attr, err = c.computeResourceVariable(v)
			}
			if err != nil {
				return err
			}

			vs[n] = attr
		case *config.UserVariable:
			val, ok := c.variables[v.Name]
			if ok {
				vs[n] = val
				continue
			}

			// Look up if we have any variables with this prefix because
			// those are map overrides. Include those.
			for k, val := range c.variables {
				if strings.HasPrefix(k, v.Name+".") {
					vs["var."+k] = val
				}
			}
		}
	}

	// Interpolate the variables
	return raw.Interpolate(vs)
}
func (ctx *BuiltinEvalContext) Interpolate(
	cfg *config.RawConfig, r *Resource) (*ResourceConfig, error) {
	if cfg != nil {
		scope := &InterpolationScope{
			Path:     ctx.Path(),
			Resource: r,
		}
		vs, err := ctx.Interpolater.Values(scope, cfg.Variables)
		if err != nil {
			return nil, err
		}

		// Do the interpolation
		if err := cfg.Interpolate(vs); err != nil {
			return nil, err
		}
	}

	result := NewResourceConfig(cfg)
	result.interpolateForce()
	return result, nil
}
Example #3
0
func TestResourceConfigGet(t *testing.T) {
	cases := []struct {
		Config map[string]interface{}
		Vars   map[string]string
		Key    string
		Value  interface{}
	}{
		{
			Config: nil,
			Key:    "foo",
			Value:  nil,
		},

		{
			Config: map[string]interface{}{
				"foo": "${var.foo}",
			},
			Key:   "foo",
			Value: "${var.foo}",
		},

		{
			Config: map[string]interface{}{
				"foo": "${var.foo}",
			},
			Vars:  map[string]string{"foo": "bar"},
			Key:   "foo",
			Value: "bar",
		},

		{
			Config: map[string]interface{}{
				"foo": []interface{}{1, 2, 5},
			},
			Key:   "foo.0",
			Value: 1,
		},

		{
			Config: map[string]interface{}{
				"foo": []interface{}{1, 2, 5},
			},
			Key:   "foo.5",
			Value: nil,
		},

		// get from map
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{"key": 1},
				},
			},
			Key:   "mapname.0.key",
			Value: 1,
		},

		// get from map with dot in key
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{"key.name": 1},
				},
			},
			Key:   "mapname.0.key.name",
			Value: 1,
		},

		// get from map with overlapping key names
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{
						"key.name":   1,
						"key.name.2": 2,
					},
				},
			},
			Key:   "mapname.0.key.name.2",
			Value: 2,
		},
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{
						"key.name":     1,
						"key.name.foo": 2,
					},
				},
			},
			Key:   "mapname.0.key.name",
			Value: 1,
		},
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{
						"listkey": []map[string]interface{}{
							{"key": 3},
						},
					},
				},
			},
			Key:   "mapname.0.listkey.0.key",
			Value: 3,
		},
		// FIXME: this is ambiguous, and matches the nested map
		//        leaving here to catch this behaviour if it changes.
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{
						"key.name":   1,
						"key.name.0": 2,
						"key":        map[string]interface{}{"name": 3},
					},
				},
			},
			Key:   "mapname.0.key.name",
			Value: 3,
		},
		/*
			// TODO: can't access this nested list at all.
			// FIXME: key with name matching substring of nested list can panic
			{
				Config: map[string]interface{}{
					"mapname": []map[string]interface{}{
						map[string]interface{}{
							"key.name": []map[string]interface{}{
								{"subkey": 1},
							},
							"key": 3,
						},
					},
				},
				Key:   "mapname.0.key.name.0.subkey",
				Value: 3,
			},
		*/
	}

	for i, tc := range cases {
		var rawC *config.RawConfig
		if tc.Config != nil {
			var err error
			rawC, err = config.NewRawConfig(tc.Config)
			if err != nil {
				t.Fatalf("err: %s", err)
			}
		}

		if tc.Vars != nil {
			vs := make(map[string]ast.Variable)
			for k, v := range tc.Vars {
				vs["var."+k] = ast.Variable{Value: v, Type: ast.TypeString}
			}

			if err := rawC.Interpolate(vs); err != nil {
				t.Fatalf("err: %s", err)
			}
		}

		rc := NewResourceConfig(rawC)
		rc.interpolateForce()

		// Test getting a key
		t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) {
			v, _ := rc.Get(tc.Key)
			if !reflect.DeepEqual(v, tc.Value) {
				t.Fatalf("%d bad: %#v", i, v)
			}
		})

		// If we have vars, we don't test copying
		if len(tc.Vars) > 0 {
			continue
		}

		// Test copying and equality
		t.Run(fmt.Sprintf("copy-and-equal-%d", i), func(t *testing.T) {
			copy := rc.DeepCopy()
			if !reflect.DeepEqual(copy, rc) {
				t.Fatalf("bad:\n\n%#v\n\n%#v", copy, rc)
			}

			if !copy.Equal(rc) {
				t.Fatalf("copy != rc:\n\n%#v\n\n%#v", copy, rc)
			}
			if !rc.Equal(copy) {
				t.Fatalf("rc != copy:\n\n%#v\n\n%#v", copy, rc)
			}
		})
	}
}
Example #4
0
func TestResourceConfigGet(t *testing.T) {
	cases := []struct {
		Config map[string]interface{}
		Vars   map[string]string
		Key    string
		Value  interface{}
	}{
		{
			Config: nil,
			Key:    "foo",
			Value:  nil,
		},

		{
			Config: map[string]interface{}{
				"foo": "${var.foo}",
			},
			Key:   "foo",
			Value: "${var.foo}",
		},

		{
			Config: map[string]interface{}{
				"foo": "${var.foo}",
			},
			Vars:  map[string]string{"foo": "bar"},
			Key:   "foo",
			Value: "bar",
		},

		{
			Config: map[string]interface{}{
				"foo": []interface{}{1, 2, 5},
			},
			Key:   "foo.0",
			Value: 1,
		},

		{
			Config: map[string]interface{}{
				"foo": []interface{}{1, 2, 5},
			},
			Key:   "foo.5",
			Value: nil,
		},
	}

	for i, tc := range cases {
		var rawC *config.RawConfig
		if tc.Config != nil {
			var err error
			rawC, err = config.NewRawConfig(tc.Config)
			if err != nil {
				t.Fatalf("err: %s", err)
			}
		}

		if tc.Vars != nil {
			vs := make(map[string]ast.Variable)
			for k, v := range tc.Vars {
				vs["var."+k] = ast.Variable{Value: v, Type: ast.TypeString}
			}

			if err := rawC.Interpolate(vs); err != nil {
				t.Fatalf("err: %s", err)
			}
		}

		rc := NewResourceConfig(rawC)
		rc.interpolateForce()

		v, _ := rc.Get(tc.Key)
		if !reflect.DeepEqual(v, tc.Value) {
			t.Fatalf("%d bad: %#v", i, v)
		}
	}
}
Example #5
0
func TestResourceConfigCheckSet(t *testing.T) {
	cases := []struct {
		Name   string
		Config map[string]interface{}
		Vars   map[string]interface{}
		Input  []string
		Errs   bool
	}{
		{
			Name: "computed basic",
			Config: map[string]interface{}{
				"foo": "${var.foo}",
			},
			Vars: map[string]interface{}{
				"foo": unknownValue(),
			},
			Input: []string{"foo"},
			Errs:  false,
		},

		{
			Name: "basic",
			Config: map[string]interface{}{
				"foo": "bar",
			},
			Vars:  nil,
			Input: []string{"foo"},
			Errs:  false,
		},

		{
			Name: "basic with not set",
			Config: map[string]interface{}{
				"foo": "bar",
			},
			Vars:  nil,
			Input: []string{"foo", "bar"},
			Errs:  true,
		},

		{
			Name: "basic with one computed",
			Config: map[string]interface{}{
				"foo": "bar",
				"bar": "${var.foo}",
			},
			Vars: map[string]interface{}{
				"foo": unknownValue(),
			},
			Input: []string{"foo", "bar"},
			Errs:  false,
		},
	}

	for i, tc := range cases {
		t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
			var rawC *config.RawConfig
			if tc.Config != nil {
				var err error
				rawC, err = config.NewRawConfig(tc.Config)
				if err != nil {
					t.Fatalf("err: %s", err)
				}
			}

			if tc.Vars != nil {
				vs := make(map[string]ast.Variable)
				for k, v := range tc.Vars {
					hilVar, err := hil.InterfaceToVariable(v)
					if err != nil {
						t.Fatalf("%#v to var: %s", v, err)
					}

					vs["var."+k] = hilVar
				}

				if err := rawC.Interpolate(vs); err != nil {
					t.Fatalf("err: %s", err)
				}
			}

			rc := NewResourceConfig(rawC)
			rc.interpolateForce()

			t.Logf("Config: %#v", rc)

			errs := rc.CheckSet(tc.Input)
			if tc.Errs != (len(errs) > 0) {
				t.Fatalf("bad: %#v", errs)
			}
		})
	}
}
Example #6
0
func TestResourceConfigGet(t *testing.T) {
	cases := []struct {
		Config map[string]interface{}
		Vars   map[string]interface{}
		Key    string
		Value  interface{}
	}{
		{
			Config: nil,
			Key:    "foo",
			Value:  nil,
		},

		{
			Config: map[string]interface{}{
				"foo": "bar",
			},
			Key:   "foo",
			Value: "bar",
		},

		{
			Config: map[string]interface{}{
				"foo": "${var.foo}",
			},
			Key:   "foo",
			Value: "${var.foo}",
		},

		{
			Config: map[string]interface{}{
				"foo": "${var.foo}",
			},
			Vars:  map[string]interface{}{"foo": unknownValue()},
			Key:   "foo",
			Value: "${var.foo}",
		},

		{
			Config: map[string]interface{}{
				"foo": []interface{}{1, 2, 5},
			},
			Key:   "foo.0",
			Value: 1,
		},

		{
			Config: map[string]interface{}{
				"foo": []interface{}{1, 2, 5},
			},
			Key:   "foo.5",
			Value: nil,
		},

		// get from map
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{"key": 1},
				},
			},
			Key:   "mapname.0.key",
			Value: 1,
		},

		// get from map with dot in key
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{"key.name": 1},
				},
			},
			Key:   "mapname.0.key.name",
			Value: 1,
		},

		// get from map with overlapping key names
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{
						"key.name":   1,
						"key.name.2": 2,
					},
				},
			},
			Key:   "mapname.0.key.name.2",
			Value: 2,
		},
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{
						"key.name":     1,
						"key.name.foo": 2,
					},
				},
			},
			Key:   "mapname.0.key.name",
			Value: 1,
		},
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{
						"listkey": []map[string]interface{}{
							{"key": 3},
						},
					},
				},
			},
			Key:   "mapname.0.listkey.0.key",
			Value: 3,
		},

		// A map assigned to a list via interpolation should Get a non-existent
		// value. The test code now also checks that Get doesn't return (nil,
		// true), which it previously did for this configuration.
		{
			Config: map[string]interface{}{
				"maplist": "${var.maplist}",
			},
			Key:   "maplist.0",
			Value: nil,
		},

		// Reference list of maps variable.
		// This does not work from GetRaw.
		{
			Vars: map[string]interface{}{
				"maplist": []interface{}{
					map[string]interface{}{
						"key": "a",
					},
					map[string]interface{}{
						"key": "b",
					},
				},
			},
			Config: map[string]interface{}{
				"maplist": "${var.maplist}",
			},
			Key:   "maplist.0",
			Value: map[string]interface{}{"key": "a"},
		},

		// Reference a map-of-lists variable.
		// This does not work from GetRaw.
		{
			Vars: map[string]interface{}{
				"listmap": map[string]interface{}{
					"key1": []interface{}{"a", "b"},
					"key2": []interface{}{"c", "d"},
				},
			},
			Config: map[string]interface{}{
				"listmap": "${var.listmap}",
			},
			Key:   "listmap.key1",
			Value: []interface{}{"a", "b"},
		},

		// FIXME: this is ambiguous, and matches the nested map
		//        leaving here to catch this behaviour if it changes.
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{
						"key.name":   1,
						"key.name.0": 2,
						"key":        map[string]interface{}{"name": 3},
					},
				},
			},
			Key:   "mapname.0.key.name",
			Value: 3,
		},
		/*
			// TODO: can't access this nested list at all.
			// FIXME: key with name matching substring of nested list can panic
			{
				Config: map[string]interface{}{
					"mapname": []map[string]interface{}{
						map[string]interface{}{
							"key.name": []map[string]interface{}{
								{"subkey": 1},
							},
							"key": 3,
						},
					},
				},
				Key:   "mapname.0.key.name.0.subkey",
				Value: 3,
			},
		*/
	}

	for i, tc := range cases {
		var rawC *config.RawConfig
		if tc.Config != nil {
			var err error
			rawC, err = config.NewRawConfig(tc.Config)
			if err != nil {
				t.Fatalf("err: %s", err)
			}
		}

		if tc.Vars != nil {
			vs := make(map[string]ast.Variable)
			for k, v := range tc.Vars {
				hilVar, err := hil.InterfaceToVariable(v)
				if err != nil {
					t.Fatalf("%#v to var: %s", v, err)
				}

				vs["var."+k] = hilVar
			}

			if err := rawC.Interpolate(vs); err != nil {
				t.Fatalf("err: %s", err)
			}
		}

		rc := NewResourceConfig(rawC)
		rc.interpolateForce()

		// Test getting a key
		t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) {
			v, ok := rc.Get(tc.Key)
			if ok && v == nil {
				t.Fatal("(nil, true) returned from Get")
			}

			if !reflect.DeepEqual(v, tc.Value) {
				t.Fatalf("%d bad: %#v", i, v)
			}
		})

		// If we have vars, we don't test copying
		if len(tc.Vars) > 0 {
			continue
		}

		// Test copying and equality
		t.Run(fmt.Sprintf("copy-and-equal-%d", i), func(t *testing.T) {
			copy := rc.DeepCopy()
			if !reflect.DeepEqual(copy, rc) {
				t.Fatalf("bad:\n\n%#v\n\n%#v", copy, rc)
			}

			if !copy.Equal(rc) {
				t.Fatalf("copy != rc:\n\n%#v\n\n%#v", copy, rc)
			}
			if !rc.Equal(copy) {
				t.Fatalf("rc != copy:\n\n%#v\n\n%#v", copy, rc)
			}
		})
	}
}
Example #7
0
func TestResourceConfigIsComputed(t *testing.T) {
	cases := []struct {
		Name   string
		Config map[string]interface{}
		Vars   map[string]interface{}
		Key    string
		Result bool
	}{
		{
			Name: "basic value",
			Config: map[string]interface{}{
				"foo": "${var.foo}",
			},
			Vars: map[string]interface{}{
				"foo": unknownValue(),
			},
			Key:    "foo",
			Result: true,
		},

		{
			Name: "set with a computed element",
			Config: map[string]interface{}{
				"foo": "${var.foo}",
			},
			Vars: map[string]interface{}{
				"foo": []string{
					"a",
					unknownValue(),
				},
			},
			Key:    "foo",
			Result: true,
		},

		{
			Name: "set with no computed elements",
			Config: map[string]interface{}{
				"foo": "${var.foo}",
			},
			Vars: map[string]interface{}{
				"foo": []string{
					"a",
					"b",
				},
			},
			Key:    "foo",
			Result: false,
		},

		/*
			{
				Name: "set count with computed elements",
				Config: map[string]interface{}{
					"foo": "${var.foo}",
				},
				Vars: map[string]interface{}{
					"foo": []string{
						"a",
						unknownValue(),
					},
				},
				Key:    "foo.#",
				Result: true,
			},
		*/

		{
			Name: "set count with computed elements",
			Config: map[string]interface{}{
				"foo": []interface{}{"${var.foo}"},
			},
			Vars: map[string]interface{}{
				"foo": []string{
					"a",
					unknownValue(),
				},
			},
			Key:    "foo.#",
			Result: true,
		},

		{
			Name: "nested set with computed elements",
			Config: map[string]interface{}{
				"route": []map[string]interface{}{
					map[string]interface{}{
						"index":   "1",
						"gateway": []interface{}{"${var.foo}"},
					},
				},
			},
			Vars: map[string]interface{}{
				"foo": unknownValue(),
			},
			Key:    "route.0.gateway",
			Result: true,
		},
	}

	for i, tc := range cases {
		t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
			var rawC *config.RawConfig
			if tc.Config != nil {
				var err error
				rawC, err = config.NewRawConfig(tc.Config)
				if err != nil {
					t.Fatalf("err: %s", err)
				}
			}

			if tc.Vars != nil {
				vs := make(map[string]ast.Variable)
				for k, v := range tc.Vars {
					hilVar, err := hil.InterfaceToVariable(v)
					if err != nil {
						t.Fatalf("%#v to var: %s", v, err)
					}

					vs["var."+k] = hilVar
				}

				if err := rawC.Interpolate(vs); err != nil {
					t.Fatalf("err: %s", err)
				}
			}

			rc := NewResourceConfig(rawC)
			rc.interpolateForce()

			t.Logf("Config: %#v", rc)

			actual := rc.IsComputed(tc.Key)
			if actual != tc.Result {
				t.Fatalf("bad: %#v", actual)
			}
		})
	}
}
Example #8
0
func TestResourceConfigGetRaw(t *testing.T) {
	cases := []struct {
		Config map[string]interface{}
		Vars   map[string]interface{}
		Key    string
		Value  interface{}
	}{
		// Referencing a list-of-maps variable doesn't work from GetRaw.
		// The ConfigFieldReader currently catches this case and looks up the
		// variable in the config.
		{
			Vars: map[string]interface{}{
				"maplist": []interface{}{
					map[string]interface{}{
						"key": "a",
					},
					map[string]interface{}{
						"key": "b",
					},
				},
			},
			Config: map[string]interface{}{
				"maplist": "${var.maplist}",
			},
			Key:   "maplist.0",
			Value: nil,
		},
		// Reference a map-of-lists variable.
		// The ConfigFieldReader currently catches this case and looks up the
		// variable in the config.
		{
			Vars: map[string]interface{}{
				"listmap": map[string]interface{}{
					"key1": []interface{}{"a", "b"},
					"key2": []interface{}{"c", "d"},
				},
			},
			Config: map[string]interface{}{
				"listmap": "${var.listmap}",
			},
			Key:   "listmap.key1",
			Value: nil,
		},
	}

	for i, tc := range cases {
		var rawC *config.RawConfig
		if tc.Config != nil {
			var err error
			rawC, err = config.NewRawConfig(tc.Config)
			if err != nil {
				t.Fatalf("err: %s", err)
			}
		}

		if tc.Vars != nil {
			vs := make(map[string]ast.Variable)
			for k, v := range tc.Vars {
				hilVar, err := hil.InterfaceToVariable(v)
				if err != nil {
					t.Fatalf("%#v to var: %s", v, err)
				}
				vs["var."+k] = hilVar
			}
			if err := rawC.Interpolate(vs); err != nil {
				t.Fatalf("err: %s", err)
			}
		}

		rc := NewResourceConfig(rawC)
		rc.interpolateForce()

		// Test getting a key
		t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) {
			v, ok := rc.GetRaw(tc.Key)
			if ok && v == nil {
				t.Fatal("(nil, true) returned from GetRaw")
			}

			if !reflect.DeepEqual(v, tc.Value) {
				t.Fatalf("%d bad: %#v", i, v)
			}
		})
	}
}
Example #9
0
// computeVars takes the State and given RawConfig and processes all
// the variables. This dynamically discovers the attributes instead of
// using a static map[string]string that the genericWalkFn uses.
func (c *walkContext) computeVars(
	raw *config.RawConfig, r *Resource) error {
	// If there isn't a raw configuration, don't do anything
	if raw == nil {
		return nil
	}

	// Copy the default variables
	vs := make(map[string]string)
	for k, v := range c.defaultVariables {
		vs[k] = v
	}

	// Next, the actual computed variables
	for n, rawV := range raw.Variables {
		switch v := rawV.(type) {
		case *config.CountVariable:
			switch v.Type {
			case config.CountValueIndex:
				if r != nil {
					vs[n] = strconv.FormatInt(int64(r.CountIndex), 10)
				}
			}
		case *config.ModuleVariable:
			if c.Operation == walkValidate {
				vs[n] = config.UnknownVariableValue
				continue
			}

			value, err := c.computeModuleVariable(v)
			if err != nil {
				return err
			}

			vs[n] = value
		case *config.PathVariable:
			switch v.Type {
			case config.PathValueCwd:
				wd, err := os.Getwd()
				if err != nil {
					return fmt.Errorf(
						"Couldn't get cwd for var %s: %s",
						v.FullKey(), err)
				}

				vs[n] = wd
			case config.PathValueModule:
				if t := c.Context.module.Child(c.Path[1:]); t != nil {
					vs[n] = t.Config().Dir
				}
			case config.PathValueRoot:
				vs[n] = c.Context.module.Config().Dir
			}
		case *config.ResourceVariable:
			if c.Operation == walkValidate {
				vs[n] = config.UnknownVariableValue
				continue
			}

			var attr string
			var err error
			if v.Multi && v.Index == -1 {
				attr, err = c.computeResourceMultiVariable(v)
			} else {
				attr, err = c.computeResourceVariable(v)
			}
			if err != nil {
				return err
			}

			vs[n] = attr
		case *config.UserVariable:
			val, ok := c.Variables[v.Name]
			if ok {
				vs[n] = val
				continue
			}

			if _, ok := vs[n]; !ok && c.Operation == walkValidate {
				vs[n] = config.UnknownVariableValue
				continue
			}

			// Look up if we have any variables with this prefix because
			// those are map overrides. Include those.
			for k, val := range c.Variables {
				if strings.HasPrefix(k, v.Name+".") {
					vs["var."+k] = val
				}
			}
		}
	}

	// Interpolate the variables
	return raw.Interpolate(vs)
}