// serviceFunc returns or accumulates health service dependencies.
func serviceFunc(brain *Brain,
	used, missing map[string]dep.Dependency) func(...string) ([]*dep.HealthService, error) {
	return func(s ...string) ([]*dep.HealthService, error) {
		result := make([]*dep.HealthService, 0)

		if len(s) == 0 || s[0] == "" {
			return result, nil
		}

		d, err := dep.ParseHealthServices(s...)
		if err != nil {
			return nil, err
		}

		addDependency(used, d)

		if value, ok := brain.Recall(d); ok {
			return value.([]*dep.HealthService), nil
		}

		addDependency(missing, d)

		return result, nil
	}
}
func TestServiceFunc_missingData(t *testing.T) {
	d, err := dep.ParseHealthServices("non-existing")
	if err != nil {
		t.Fatal(err)
	}

	brain := NewBrain()

	used := make(map[string]dep.Dependency)
	missing := make(map[string]dep.Dependency)

	f := serviceFunc(brain, used, missing)
	result, err := f("non-existing")
	if err != nil {
		t.Fatal(err)
	}

	expected := []*dep.HealthService{}
	if !reflect.DeepEqual(result, expected) {
		t.Errorf("expected %q to be %q", result, expected)
	}

	if _, ok := used[d.HashCode()]; !ok {
		t.Errorf("expected dep to be used")
	}

	if _, ok := missing[d.HashCode()]; !ok {
		t.Errorf("expected dep to be missing")
	}
}
func TestRun_executesCommand(t *testing.T) {
	outFile := test.CreateTempfile(nil, t)
	os.Remove(outFile.Name())
	defer os.Remove(outFile.Name())

	inTemplate := test.CreateTempfile([]byte(`
    {{ range service "[email protected]"}}{{ end }}
  `), t)
	defer test.DeleteTempfile(inTemplate, t)

	outTemplate := test.CreateTempfile(nil, t)
	defer test.DeleteTempfile(outTemplate, t)

	config := DefaultConfig()
	config.Merge(&Config{
		ConfigTemplates: []*ConfigTemplate{
			&ConfigTemplate{
				Source:         inTemplate.Name(),
				Destination:    outTemplate.Name(),
				Command:        fmt.Sprintf("echo 'foo' > %s", outFile.Name()),
				CommandTimeout: 1 * time.Second,
			},
		},
	})

	runner, err := NewRunner(config, false, false)
	if err != nil {
		t.Fatal(err)
	}

	d, err := dep.ParseHealthServices("[email protected]")
	if err != nil {
		t.Fatal(err)
	}
	data := []*dep.HealthService{
		&dep.HealthService{
			Node:    "consul",
			Address: "1.2.3.4",
			ID:      "[email protected]",
			Name:    "consul",
		},
	}
	runner.dependencies[d.HashCode()] = d
	runner.watcher.ForceWatching(d, true)
	runner.Receive(d, data)

	if err := runner.Run(); err != nil {
		t.Fatal(err)
	}

	_, err = os.Stat(outFile.Name())
	if err != nil {
		t.Fatal(err)
	}
}
Example #4
0
func TestRender_sameContentsDoesNotExecuteCommand(t *testing.T) {
	outFile := test.CreateTempfile(nil, t)
	os.Remove(outFile.Name())
	defer os.Remove(outFile.Name())

	inTemplate := test.CreateTempfile([]byte(`
    {{ range service "[email protected]" }}{{.Node}}{{ end }}
  `), t)
	defer test.DeleteTempfile(inTemplate, t)

	outTemplate := test.CreateTempfile([]byte(`
    consul1consul2
  `), t)
	defer test.DeleteTempfile(outTemplate, t)

	config := DefaultConfig()
	config.Merge(&Config{
		ConfigTemplates: []*ConfigTemplate{
			&ConfigTemplate{
				Source:      inTemplate.Name(),
				Destination: outTemplate.Name(),
				Command:     fmt.Sprintf("echo 'foo' > %s", outFile.Name()),
			},
		},
	})

	runner, err := NewRunner(config, false, false)
	if err != nil {
		t.Fatal(err)
	}

	d, err := dep.ParseHealthServices("[email protected]")
	if err != nil {
		t.Fatal(err)
	}
	data := []*dep.HealthService{
		&dep.HealthService{Node: "consul1"},
		&dep.HealthService{Node: "consul2"},
	}
	runner.Receive(d, data)

	if err := runner.Run(); err != nil {
		t.Fatal(err)
	}

	_, err = os.Stat(outFile.Name())
	if !os.IsNotExist(err) {
		t.Fatalf("expected command to not be run")
	}
}
func TestRun_dry(t *testing.T) {
	in := test.CreateTempfile([]byte(`
    {{ range service "[email protected]" }}{{.Node}}{{ end }}
  `), t)
	defer test.DeleteTempfile(in, t)

	config := DefaultConfig()
	config.Merge(&Config{
		ConfigTemplates: []*ConfigTemplate{
			&ConfigTemplate{
				Source:      in.Name(),
				Destination: "/out/file.txt",
			},
		},
	})

	runner, err := NewRunner(config, true, false)
	if err != nil {
		t.Fatal(err)
	}

	d, err := dep.ParseHealthServices("[email protected]")
	if err != nil {
		t.Fatal(err)
	}
	data := []*dep.HealthService{
		&dep.HealthService{Node: "consul1"},
		&dep.HealthService{Node: "consul2"},
	}
	runner.dependencies[d.HashCode()] = d
	runner.watcher.ForceWatching(d, true)
	runner.Receive(d, data)

	buff := gatedio.NewByteBuffer()
	runner.outStream, runner.errStream = buff, buff

	if err := runner.Run(); err != nil {
		t.Fatal(err)
	}

	actual := bytes.TrimSpace(buff.Bytes())
	expected := bytes.TrimSpace([]byte(`
    > /out/file.txt

    consul1consul2
  `))
	if !bytes.Equal(actual, expected) {
		t.Errorf("expected \n%q\n to equal \n%q\n", actual, expected)
	}
}
Example #6
0
func TestDedup_UpdateDeps(t *testing.T) {
	t.Parallel()

	// Create a template
	in := test.CreateTempfile([]byte(`
    {{ range service "consul" }}{{.Node}}{{ end }}
  `), t)
	defer test.DeleteTempfile(in, t)
	tmpl, err := NewTemplate(in.Name())
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	consul, dedup := testDedupManager(t, []*Template{tmpl})
	defer consul.Stop()

	// Start dedup
	if err := dedup.Start(); err != nil {
		t.Fatalf("err: %v", err)
	}
	defer dedup.Stop()

	// Wait until we are leader
	select {
	case <-dedup.UpdateCh():
	case <-time.After(2 * time.Second):
		t.Fatalf("timeout")
	}

	// Create the dependency
	dep, err := dependency.ParseHealthServices("consul")
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Inject data into the brain
	dedup.brain.Remember(dep, 123)

	// Update the dependencies
	err = dedup.UpdateDeps(tmpl, []dependency.Dependency{dep})
	if err != nil {
		t.Fatalf("err: %v", err)
	}
}
Example #7
0
func TestRun_removesUnusedDependencies(t *testing.T) {
	in := test.CreateTempfile([]byte(nil), t)
	defer test.DeleteTempfile(in, t)

	config := DefaultConfig()
	config.Merge(&Config{
		ConfigTemplates: []*ConfigTemplate{
			&ConfigTemplate{Source: in.Name()},
		},
	})

	runner, err := NewRunner(config, true, false)
	if err != nil {
		t.Fatal(err)
	}

	d, err := dep.ParseHealthServices("[email protected]")
	if err != nil {
		t.Fatal(err)
	}

	runner.dependencies = map[string]dep.Dependency{"[email protected]": d}

	if err := runner.Run(); err != nil {
		t.Fatal(err)
	}

	if len(runner.dependencies) != 0 {
		t.Errorf("expected %d to be %d", len(runner.dependencies), 0)
	}

	if runner.watcher.Watching(d) {
		t.Errorf("expected watcher to stop watching dependency")
	}

	if _, ok := runner.brain.Recall(d); ok {
		t.Errorf("expected brain to forget dependency")
	}
}
func TestServiceFunc_hasData(t *testing.T) {
	d, err := dep.ParseHealthServices("existing")
	if err != nil {
		t.Fatal(err)
	}

	data := []*dep.HealthService{
		&dep.HealthService{Node: "a"},
		&dep.HealthService{Node: "b"},
	}

	brain := NewBrain()
	brain.Remember(d, data)

	used := make(map[string]dep.Dependency)
	missing := make(map[string]dep.Dependency)

	f := serviceFunc(brain, used, missing)
	result, err := f("existing")
	if err != nil {
		t.Fatal(err)
	}

	expected := data
	if !reflect.DeepEqual(result, expected) {
		t.Errorf("expected %q to be %q", result, expected)
	}

	if len(missing) != 0 {
		t.Errorf("expected missing to have 0 elements, but had %d", len(missing))
	}

	if _, ok := used[d.HashCode()]; !ok {
		t.Errorf("expected dep to be used")
	}
}
func TestExecute_multipass(t *testing.T) {
	in := test.CreateTempfile([]byte(`
		{{ range ls "services" }}{{.Key}}:{{ range service .Key }}
			{{.Node}} {{.Address}}:{{.Port}}{{ end }}
		{{ end }}
	`), t)
	defer test.DeleteTempfile(in, t)

	tmpl, err := NewTemplate(in.Name())
	if err != nil {
		t.Fatal(err)
	}

	brain := NewBrain()

	used, missing, result, err := tmpl.Execute(brain)
	if err != nil {
		t.Fatal(err)
	}

	if num := len(missing); num != 1 {
		t.Errorf("expected 1 missing, got: %d", num)
	}

	if num := len(used); num != 1 {
		t.Errorf("expected 1 used, got: %d", num)
	}

	expected := bytes.TrimSpace([]byte(""))
	result = bytes.TrimSpace(result)
	if !bytes.Equal(result, expected) {
		t.Errorf("expected %q to be %q", result, expected)
	}

	// Receive data for the key prefix dependency
	d1, err := dep.ParseStoreKeyPrefix("services")
	brain.Remember(d1, []*dep.KeyPair{
		&dep.KeyPair{Key: "webapp", Value: "1"},
		&dep.KeyPair{Key: "database", Value: "1"},
	})

	used, missing, result, err = tmpl.Execute(brain)
	if err != nil {
		t.Fatal(err)
	}

	if num := len(missing); num != 2 {
		t.Errorf("expected 2 missing, got: %d", num)
	}

	if num := len(used); num != 3 {
		t.Errorf("expected 3 used, got: %d", num)
	}

	expected = bytes.TrimSpace([]byte(`
		webapp:
		database:
	`))
	result = bytes.TrimSpace(result)
	if !bytes.Equal(result, expected) {
		t.Errorf("expected \n%q\n to be \n%q\n", result, expected)
	}

	// Receive data for the services
	d2, err := dep.ParseHealthServices("webapp")
	brain.Remember(d2, []*dep.HealthService{
		&dep.HealthService{Node: "web01", Address: "1.2.3.4", Port: 1234},
	})

	d3, err := dep.ParseHealthServices("database")
	brain.Remember(d3, []*dep.HealthService{
		&dep.HealthService{Node: "db01", Address: "5.6.7.8", Port: 5678},
	})

	used, missing, result, err = tmpl.Execute(brain)
	if err != nil {
		t.Fatal(err)
	}

	if num := len(missing); num != 0 {
		t.Errorf("expected 0 missing, got: %d", num)
	}

	if num := len(used); num != 3 {
		t.Errorf("expected 3 used, got: %d", num)
	}

	expected = bytes.TrimSpace([]byte(`
		webapp:
			web01 1.2.3.4:1234
		database:
			db01 5.6.7.8:5678
	`))
	result = bytes.TrimSpace(result)
	if !bytes.Equal(result, expected) {
		t.Errorf("expected \n%q\n to be \n%q\n", result, expected)
	}
}
func TestExecute_renders(t *testing.T) {
	// Stub out the time.
	now = func() time.Time { return time.Unix(0, 0).UTC() }

	in := test.CreateTempfile([]byte(`
		API Functions
		-------------
		datacenters:{{ range datacenters }}
			{{.}}{{ end }}
		file: {{ file "/path/to/file" }}
		key: {{ key "config/redis/maxconns" }}
		ls:{{ range ls "config/redis" }}
			{{.Key}}={{.Value}}{{ end }}
		node:{{ with node }}
			{{.Node.Node}}{{ range .Services}}
				{{.Service}}{{ end }}{{ end }}
		nodes:{{ range nodes }}
			{{.Node}}{{ end }}
		service:{{ range service "webapp" }}
			{{.Address}}{{ end }}
		service (any):{{ range service "webapp" "any" }}
			{{.Address}}{{ end }}
		service (tag.Contains):{{ range service "webapp" }}{{ if .Tags.Contains "production" }}
			{{.Node}}{{ end }}{{ end }}
		services:{{ range services }}
			{{.Name}}{{ end }}
		tree:{{ range tree "config/redis" }}
			{{.Key}}={{.Value}}{{ end }}
		vault: {{ with vault "secret/foo/bar" }}{{.Data.zip}}{{ end }}

		Helper Functions
		----------------
		byKey:{{ range $key, $pairs := tree "config/redis" | byKey }}
			{{$key}}:{{ range $pairs }}
				{{.Key}}={{.Value}}{{ end }}{{ end }}
		byTag (health service):{{ range $tag, $services := service "webapp" | byTag }}
			{{$tag}}:{{ range $services }}
				{{.Address}}{{ end }}{{ end }}
		byTag (catalog services):{{ range $tag, $services := services | byTag }}
			{{$tag}}:{{ range $services }}
				{{.Name}}{{ end }}{{ end }}
		contains:{{ range service "webapp" }}{{ if .Tags | contains "production" }}
			{{.Node}}{{ end }}{{ end }}
		env: {{ env "foo" }}
		explode:{{ range $k, $v := tree "config/redis" | explode }}
			{{$k}}{{$v}}{{ end }}
		in:{{ range service "webapp" }}{{ if in .Tags "production" }}
			{{.Node}}{{ end }}{{ end }}
		loop:{{ range loop 3 }}
			test{{ end }}
		loop(i):{{ range $i := loop 5 8 }}
			test{{$i}}{{ end }}
		join: {{ "a,b,c" | split "," | join ";" }}
		parseBool: {{"true" | parseBool}}
		parseFloat: {{"1.2" | parseFloat}}
		parseInt: {{"-1" | parseInt}}
		parseJSON (string):{{ range $key, $value := "{\"foo\": \"bar\"}" | parseJSON }}
			{{$key}}={{$value}}{{ end }}
		parseJSON (file):{{ range $key, $value := file "/path/to/json/file" | parseJSON }}
			{{$key}}={{$value}}{{ end }}
		parseJSON (env):{{ range $key, $value := env "json" | parseJSON }}
			{{$key}}={{$value}}{{ end }}
		parseUint: {{"1" | parseUint}}
		plugin: {{ file "/path/to/json/file" | plugin "echo" }}
		timestamp: {{ timestamp }}
		timestamp (formatted): {{ timestamp "2006-01-02" }}
		regexMatch: {{ file "/path/to/file"  | regexMatch ".*[cont][a-z]+" }}
		regexMatch: {{ file "/path/to/file"  | regexMatch "v[0-9]*" }}
		regexReplaceAll: {{ file "/path/to/file" | regexReplaceAll "\\w" "x" }}
		replaceAll: {{ file "/path/to/file" | replaceAll "some" "this" }}
		split:{{ range "a,b,c" | split "," }}
			{{.}}{{end}}
		toLower: {{ file "/path/to/file" | toLower }}
		toJSON: {{ tree "config/redis" | explode | toJSON }}
		toJSONPretty:
{{ tree "config/redis" | explode | toJSONPretty }}
		toTitle: {{ file "/path/to/file" | toTitle }}
		toUpper: {{ file "/path/to/file" | toUpper }}
		toYAML:
{{ tree "config/redis" | explode | toYAML }}

		Math Functions
		--------------
		add:{{ 2 | add 2 }}
		subtract:{{ 2 | subtract 2 }}
		multiply:{{ 2 | multiply 2 }}
		divide:{{ 2 | divide 2 }}
`), t)
	defer test.DeleteTempfile(in, t)

	tmpl, err := NewTemplate(in.Name())
	if err != nil {
		t.Fatal(err)
	}

	brain := NewBrain()

	var d dep.Dependency

	d, err = dep.ParseDatacenters()
	if err != nil {
		t.Fatal(err)
	}
	brain.Remember(d, []string{"dc1", "dc2"})

	d, err = dep.ParseFile("/path/to/file")
	if err != nil {
		t.Fatal(err)
	}
	brain.Remember(d, "some content")

	d, err = dep.ParseStoreKey("config/redis/maxconns")
	if err != nil {
		t.Fatal(err)
	}
	brain.Remember(d, "5")

	d, err = dep.ParseStoreKeyPrefix("config/redis")
	if err != nil {
		t.Fatal(err)
	}
	brain.Remember(d, []*dep.KeyPair{
		&dep.KeyPair{Key: "", Value: ""},
		&dep.KeyPair{Key: "admin/port", Value: "1134"},
		&dep.KeyPair{Key: "maxconns", Value: "5"},
		&dep.KeyPair{Key: "minconns", Value: "2"},
	})

	d, err = dep.ParseCatalogNode()
	if err != nil {
		t.Fatal(err)
	}
	brain.Remember(d, &dep.NodeDetail{
		Node: &dep.Node{Node: "node1"},
		Services: dep.NodeServiceList([]*dep.NodeService{
			&dep.NodeService{
				Service: "service1",
			},
		}),
	})

	d, err = dep.ParseCatalogNodes("")
	if err != nil {
		t.Fatal(err)
	}
	brain.Remember(d, []*dep.Node{
		&dep.Node{Node: "node1"},
		&dep.Node{Node: "node2"},
	})

	d, err = dep.ParseHealthServices("webapp")
	if err != nil {
		t.Fatal(err)
	}
	brain.Remember(d, []*dep.HealthService{
		&dep.HealthService{
			Node:    "node1",
			Address: "1.2.3.4",
			Tags:    []string{"release"},
		},
		&dep.HealthService{
			Node:    "node2",
			Address: "5.6.7.8",
			Tags:    []string{"release", "production"},
		},
		&dep.HealthService{
			Node:    "node3",
			Address: "9.10.11.12",
			Tags:    []string{"production"},
		},
	})

	d, err = dep.ParseHealthServices("webapp", "any")
	if err != nil {
		t.Fatal(err)
	}
	brain.Remember(d, []*dep.HealthService{
		&dep.HealthService{Node: "node1", Address: "1.2.3.4"},
		&dep.HealthService{Node: "node2", Address: "5.6.7.8"},
	})

	d, err = dep.ParseCatalogServices("")
	if err != nil {
		t.Fatal(err)
	}
	brain.Remember(d, []*dep.CatalogService{
		&dep.CatalogService{
			Name: "service1",
			Tags: []string{"production"},
		},
		&dep.CatalogService{
			Name: "service2",
			Tags: []string{"release", "production"},
		},
	})

	d, err = dep.ParseVaultSecret("secret/foo/bar")
	if err != nil {
		t.Fatal(err)
	}
	brain.Remember(d, &dep.Secret{
		LeaseID:       "abcd1234",
		LeaseDuration: 120,
		Renewable:     true,
		Data:          map[string]interface{}{"zip": "zap"},
	})

	if err := os.Setenv("foo", "bar"); err != nil {
		t.Fatal(err)
	}

	d, err = dep.ParseFile("/path/to/json/file")
	if err != nil {
		t.Fatal(err)
	}
	brain.Remember(d, `{"foo": "bar"}`)

	if err := os.Setenv("json", `{"foo": "bar"}`); err != nil {
		t.Fatal(err)
	}

	_, _, result, err := tmpl.Execute(brain)
	if err != nil {
		t.Fatal(err)
	}

	expected := []byte(`
		API Functions
		-------------
		datacenters:
			dc1
			dc2
		file: some content
		key: 5
		ls:
			maxconns=5
			minconns=2
		node:
			node1
				service1
		nodes:
			node1
			node2
		service:
			1.2.3.4
			5.6.7.8
			9.10.11.12
		service (any):
			1.2.3.4
			5.6.7.8
		service (tag.Contains):
			node2
			node3
		services:
			service1
			service2
		tree:
			admin/port=1134
			maxconns=5
			minconns=2
		vault: zap

		Helper Functions
		----------------
		byKey:
			admin:
				port=1134
		byTag (health service):
			production:
				5.6.7.8
				9.10.11.12
			release:
				1.2.3.4
				5.6.7.8
		byTag (catalog services):
			production:
				service1
				service2
			release:
				service2
		contains:
			node2
			node3
		env: bar
		explode:
			adminmap[port:1134]
			maxconns5
			minconns2
		in:
			node2
			node3
		loop:
			test
			test
			test
		loop(i):
			test5
			test6
			test7
		join: a;b;c
		parseBool: true
		parseFloat: 1.2
		parseInt: -1
		parseJSON (string):
			foo=bar
		parseJSON (file):
			foo=bar
		parseJSON (env):
			foo=bar
		parseUint: 1
		plugin: {"foo": "bar"}
		timestamp: 1970-01-01T00:00:00Z
		timestamp (formatted): 1970-01-01
		regexMatch: true
		regexMatch: false
		regexReplaceAll: xxxx xxxxxxx
		replaceAll: this content
		split:
			a
			b
			c
		toLower: some content
		toJSON: {"admin":{"port":"1134"},"maxconns":"5","minconns":"2"}
		toJSONPretty:
{
  "admin": {
    "port": "1134"
  },
  "maxconns": "5",
  "minconns": "2"
}
		toTitle: Some Content
		toUpper: SOME CONTENT
		toYAML:
admin:
  port: "1134"
maxconns: "5"
minconns: "2"

		Math Functions
		--------------
		add:4
		subtract:0
		multiply:4
		divide:1
`)

	if !bytes.Equal(result, expected) {
		t.Errorf("expected %s to be %s", result, expected)
	}
}
Example #11
0
func TestRun_doesNotExecuteCommandMoreThanOnce(t *testing.T) {
	outFile := test.CreateTempfile(nil, t)
	os.Remove(outFile.Name())
	defer os.Remove(outFile.Name())

	inTemplate := test.CreateTempfile([]byte(`
    {{ range service "[email protected]"}}{{ end }}
  `), t)
	defer test.DeleteTempfile(inTemplate, t)

	outTemplateA := test.CreateTempfile(nil, t)
	defer test.DeleteTempfile(outTemplateA, t)

	outTemplateB := test.CreateTempfile(nil, t)
	defer test.DeleteTempfile(outTemplateB, t)

	config := DefaultConfig()
	config.Merge(&Config{
		ConfigTemplates: []*ConfigTemplate{
			&ConfigTemplate{
				Source:      inTemplate.Name(),
				Destination: outTemplateA.Name(),
				Command:     fmt.Sprintf("echo 'foo' >> %s", outFile.Name()),
			},
			&ConfigTemplate{
				Source:      inTemplate.Name(),
				Destination: outTemplateB.Name(),
				Command:     fmt.Sprintf("echo 'foo' >> %s", outFile.Name()),
			},
		},
	})

	runner, err := NewRunner(config, false, false)
	if err != nil {
		t.Fatal(err)
	}

	d, err := dep.ParseHealthServices("[email protected]")
	if err != nil {
		t.Fatal(err)
	}
	data := []*dep.HealthService{
		&dep.HealthService{
			Node:    "consul",
			Address: "1.2.3.4",
			ID:      "[email protected]",
			Name:    "consul",
		},
	}
	runner.dependencies[d.HashCode()] = d
	runner.Receive(d, data)

	if err := runner.Run(); err != nil {
		t.Fatal(err)
	}

	_, err = os.Stat(outFile.Name())
	if err != nil {
		t.Fatal(err)
	}

	output, err := ioutil.ReadFile(outFile.Name())
	if err != nil {
		t.Fatal(err)
	}

	if strings.Count(string(output), "foo") > 1 {
		t.Fatalf("expected command to be run once.")
	}
}
Example #12
0
func TestRun_multipleTemplatesRunsCommands(t *testing.T) {
	in1 := test.CreateTempfile([]byte(`
    {{ range service "[email protected]" }}{{.Node}}{{ end }}
  `), t)
	defer test.DeleteTempfile(in1, t)

	in2 := test.CreateTempfile([]byte(`
    {{range service "[email protected]"}}{{.Node}}{{ end }}
  `), t)
	defer test.DeleteTempfile(in2, t)

	out1 := test.CreateTempfile(nil, t)
	test.DeleteTempfile(out1, t)

	out2 := test.CreateTempfile(nil, t)
	test.DeleteTempfile(out2, t)

	touch1, err := ioutil.TempFile(os.TempDir(), "touch1-")
	if err != nil {
		t.Fatal(err)
	}
	os.Remove(touch1.Name())
	defer os.Remove(touch1.Name())

	touch2, err := ioutil.TempFile(os.TempDir(), "touch2-")
	if err != nil {
		t.Fatal(err)
	}
	os.Remove(touch2.Name())
	defer os.Remove(touch2.Name())

	config := DefaultConfig()
	config.Merge(&Config{
		ConfigTemplates: []*ConfigTemplate{
			&ConfigTemplate{
				Source:      in1.Name(),
				Destination: out1.Name(),
				Command:     fmt.Sprintf("touch %s", touch1.Name()),
			},
			&ConfigTemplate{
				Source:      in2.Name(),
				Destination: out2.Name(),
				Command:     fmt.Sprintf("touch %s", touch2.Name()),
			},
		},
	})

	runner, err := NewRunner(config, false, false)
	if err != nil {
		t.Fatal(err)
	}

	d, err := dep.ParseHealthServices("[email protected]")
	if err != nil {
		t.Fatal(err)
	}
	data := []*dep.HealthService{
		&dep.HealthService{Node: "consul1"},
		&dep.HealthService{Node: "consul2"},
	}
	runner.dependencies[d.HashCode()] = d
	runner.Receive(d, data)

	if err := runner.Run(); err != nil {
		t.Fatal(err)
	}

	if _, err := os.Stat(touch1.Name()); err != nil {
		t.Errorf("expected first command to run, but did not: %s", err)
	}

	if _, err := os.Stat(touch2.Name()); err == nil {
		t.Errorf("expected second command to not run, but touch exists")
	}
}
Example #13
0
func TestDedup_FollowerUpdate(t *testing.T) {
	t.Parallel()

	// Create a template
	in := test.CreateTempfile([]byte(`
    {{ range service "consul" }}{{.Node}}{{ end }}
  `), t)
	defer test.DeleteTempfile(in, t)
	tmpl, err := NewTemplate(in.Name())
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	consul, dedup1 := testDedupManager(t, []*Template{tmpl})
	defer consul.Stop()

	dedup2 := testDedupFollower(t, dedup1)

	// Start dedups
	if err := dedup1.Start(); err != nil {
		t.Fatalf("err: %v", err)
	}
	defer dedup1.Stop()
	if err := dedup2.Start(); err != nil {
		t.Fatalf("err: %v", err)
	}
	defer dedup2.Stop()

	// Wait until we have a leader
	var leader, follow *DedupManager
	select {
	case <-dedup1.UpdateCh():
		if dedup1.IsLeader(tmpl) {
			leader = dedup1
			follow = dedup2
		}
	case <-dedup2.UpdateCh():
		if dedup2.IsLeader(tmpl) {
			leader = dedup2
			follow = dedup1
		}
	case <-time.After(2 * time.Second):
		t.Fatalf("timeout")
	}

	// Create the dependency
	dep, err := dependency.ParseHealthServices("consul")
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Inject data into the brain
	leader.brain.Remember(dep, 123)

	// Update the dependencies
	err = leader.UpdateDeps(tmpl, []dependency.Dependency{dep})
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Follower should get an update
	select {
	case <-follow.UpdateCh():
	case <-time.After(2 * time.Second):
		t.Fatalf("timeout")
	}

	// Recall from the brain
	data, ok := follow.brain.Recall(dep)
	if !ok {
		t.Fatalf("missing data")
	}
	if data != 123 {
		t.Fatalf("bad: %v", data)
	}
}