config: Count can be a string (for interpolation)

This commit is contained in:
Mitchell Hashimoto 2014-10-02 11:14:50 -07:00
parent fb1c224e12
commit 8e2315599f
6 changed files with 60 additions and 47 deletions

View File

@ -56,7 +56,7 @@ type ProviderConfig struct {
type Resource struct {
Name string
Type string
Count int
Count *RawConfig
RawConfig *RawConfig
Provisioners []*Provisioner
DependsOn []string
@ -244,12 +244,6 @@ func (c *Config) Validate() error {
// Validate resources
for n, r := range resources {
if r.Count < 1 {
errs = append(errs, fmt.Errorf(
"%s: count must be greater than or equal to 1",
n))
}
for _, d := range r.DependsOn {
if _, ok := resources[d]; !ok {
errs = append(errs, fmt.Errorf(
@ -267,8 +261,7 @@ func (c *Config) Validate() error {
}
id := fmt.Sprintf("%s.%s", rv.Type, rv.Name)
r, ok := resources[id]
if !ok {
if _, ok := resources[id]; !ok {
errs = append(errs, fmt.Errorf(
"%s: unknown resource '%s' referenced in variable %s",
source,
@ -276,18 +269,6 @@ func (c *Config) Validate() error {
rv.FullKey()))
continue
}
// If it is a multi reference and resource has a single
// count, it is an error.
if r.Count > 1 && !rv.Multi {
errs = append(errs, fmt.Errorf(
"%s: variable '%s' must specify index for multi-count "+
"resource %s",
source,
rv.FullKey(),
id))
continue
}
}
}
@ -327,6 +308,9 @@ func (c *Config) InterpolatedVariables() map[string][]InterpolatedVariable {
for _, rc := range c.Resources {
source := fmt.Sprintf("resource '%s'", rc.Id())
for _, v := range rc.Count.Variables {
result[source] = append(result[source], v)
}
for _, v := range rc.RawConfig.Variables {
result[source] = append(result[source], v)
}
@ -400,7 +384,7 @@ func (r *Resource) mergerMerge(m merger) merger {
result.Type = r2.Type
result.RawConfig = result.RawConfig.merge(r2.RawConfig)
if r2.Count > 0 {
if r2.Count.Value() != "1" {
result.Count = r2.Count
}

View File

@ -190,10 +190,10 @@ func resourcesStr(rs []*Resource) string {
for _, i := range order {
r := rs[i]
result += fmt.Sprintf(
"%s[%s] (x%d)\n",
"%s[%s] (x%s)\n",
r.Type,
r.Name,
r.Count)
r.Count.Value())
ks := make([]string, 0, len(r.RawConfig.Raw))
for k, _ := range r.RawConfig.Raw {

View File

@ -23,27 +23,6 @@ func TestConfigValidate_badDependsOn(t *testing.T) {
}
}
func TestConfigValidate_badMultiResource(t *testing.T) {
c := testConfig(t, "validate-bad-multi-resource")
if err := c.Validate(); err == nil {
t.Fatal("should not be valid")
}
}
func TestConfigValidate_countBelowZero(t *testing.T) {
c := testConfig(t, "validate-count-below-zero")
if err := c.Validate(); err == nil {
t.Fatal("should not be valid")
}
}
func TestConfigValidate_countZero(t *testing.T) {
c := testConfig(t, "validate-count-zero")
if err := c.Validate(); err == nil {
t.Fatal("should not be valid")
}
}
func TestConfigValidate_dupModule(t *testing.T) {
c := testConfig(t, "validate-dup-module")
if err := c.Validate(); err == nil {

View File

@ -406,7 +406,7 @@ func loadResourcesHcl(os *hclobj.Object) ([]*Resource, error) {
}
// If we have a count, then figure it out
var count int = 1
var count string = "1"
if o := obj.Get("count", false); o != nil {
err = hcl.DecodeObject(&count, o)
if err != nil {
@ -417,6 +417,13 @@ func loadResourcesHcl(os *hclobj.Object) ([]*Resource, error) {
err)
}
}
countConfig, err := NewRawConfig(map[string]interface{}{
"count": count,
})
if err != nil {
return nil, err
}
countConfig.Key = "count"
// If we have depends fields, then add those in
var dependsOn []string
@ -475,7 +482,7 @@ func loadResourcesHcl(os *hclobj.Object) ([]*Resource, error) {
result = append(result, &Resource{
Name: k,
Type: t.Key,
Count: count,
Count: countConfig,
RawConfig: rawConfig,
Provisioners: provisioners,
DependsOn: dependsOn,

View File

@ -24,6 +24,7 @@ const UnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66"
// RawConfig supports a query-like interface to request
// information from deep within the structure.
type RawConfig struct {
Key string
Raw map[string]interface{}
Interpolations []Interpolation
Variables map[string]InterpolatedVariable
@ -43,6 +44,18 @@ func NewRawConfig(raw map[string]interface{}) (*RawConfig, error) {
return result, nil
}
// Value returns the value of the configuration if this configuration
// has a Key set. If this does not have a Key set, nil will be returned.
func (r *RawConfig) Value() interface{} {
if c := r.Config(); c != nil {
if v, ok := c[r.Key]; ok {
return v
}
}
return r.Raw[r.Key]
}
// Config returns the entire configuration with the variables
// interpolated from any call to Interpolate.
//

View File

@ -125,6 +125,36 @@ func TestRawConfig_unknown(t *testing.T) {
}
}
func TestRawConfigValue(t *testing.T) {
raw := map[string]interface{}{
"foo": "${var.bar}",
}
rc, err := NewRawConfig(raw)
if err != nil {
t.Fatalf("err: %s", err)
}
rc.Key = ""
if rc.Value() != nil {
t.Fatalf("bad: %#v", rc.Value())
}
rc.Key = "foo"
if rc.Value() != "${var.bar}" {
t.Fatalf("err: %#v", rc.Value())
}
vars := map[string]string{"var.bar": "baz"}
if err := rc.Interpolate(vars); err != nil {
t.Fatalf("err: %s", err)
}
if rc.Value() != "baz" {
t.Fatalf("bad: %#v", rc.Value())
}
}
func TestRawConfig_implGob(t *testing.T) {
var _ gob.GobDecoder = new(RawConfig)
var _ gob.GobEncoder = new(RawConfig)