Exemple #1
0
func TestReload_sighup(t *testing.T) {
	template := test.CreateTempfile([]byte("initial value"), t)
	defer test.DeleteTempfile(template, t)

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

	outStream := gatedio.NewByteBuffer()
	cli := NewCLI(outStream, outStream)

	command := fmt.Sprintf("consul-template -template %s:%s", template.Name(), out.Name())
	args := strings.Split(command, " ")

	go func(args []string) {
		if exit := cli.Run(args); exit != 0 {
			t.Fatalf("bad exit code: %d", exit)
		}
	}(args)
	defer cli.stop()

	// Ensure we have run at least once
	test.WaitForFileContents(out.Name(), []byte("initial value"), t)

	newValue := []byte("new value")
	ioutil.WriteFile(template.Name(), newValue, 0644)
	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)

	test.WaitForFileContents(out.Name(), []byte("new value"), t)
}
func TestRunner_onceAlreadyRenderedDoesNotHangOrRunCommands(t *testing.T) {
	outFile := test.CreateTempfile(nil, t)
	os.Remove(outFile.Name())
	defer os.Remove(outFile.Name())

	out := test.CreateTempfile([]byte("redis"), t)
	defer os.Remove(out.Name())

	in := test.CreateTempfile([]byte(`{{ key "service_name"}}`), t)
	defer test.DeleteTempfile(in, t)

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

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

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

	d, err := dep.ParseStoreKey("service_name")
	if err != nil {
		t.Fatal(err)
	}
	data := "redis"
	runner.dependencies[d.HashCode()] = d
	runner.watcher.ForceWatching(d, true)
	runner.Receive(d, data)

	go runner.Start()

	select {
	case err := <-runner.ErrCh:
		t.Fatal(err)
	case <-runner.DoneCh:
	case <-time.After(5 * time.Millisecond):
		t.Fatal("runner should have stopped")
		runner.Stop()
	}

	_, err = os.Stat(outFile.Name())
	if err == nil {
		t.Fatal("expected command to not be run")
	}
	if !os.IsNotExist(err) {
		t.Fatal(err)
	}
}
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 "consul@nyc1"}}{{ 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("consul@nyc1")
	if err != nil {
		t.Fatal(err)
	}
	data := []*dep.HealthService{
		&dep.HealthService{
			Node:    "consul",
			Address: "1.2.3.4",
			ID:      "consul@nyc1",
			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)
	}
}
// Warning: this is a super fragile and time-dependent test. If it's failing,
// check the demo Consul cluster and your own sanity before you assume your
// code broke something...
func TestRunner_quiescence(t *testing.T) {
	consul := testutil.NewTestServerConfig(t, func(c *testutil.TestServerConfig) {
		c.Stdout = ioutil.Discard
		c.Stderr = ioutil.Discard
	})
	defer consul.Stop()

	in := test.CreateTempfile([]byte(`
    {{ range service "consul" "any" }}{{.Node}}{{ end }}
  `), t)
	defer test.DeleteTempfile(in, t)

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

	config := testConfig(fmt.Sprintf(`
		consul = "%s"
		wait = "50ms:200ms"

		template {
			source = "%s"
			destination = "%s"
		}
	`, consul.HTTPAddr, in.Name(), out.Name()), t)

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

	go runner.Start()
	defer runner.Stop()

	min := time.After(10 * time.Millisecond)
	max := time.After(500 * time.Millisecond)
	for {
		select {
		case <-min:
			if _, err = os.Stat(out.Name()); !os.IsNotExist(err) {
				t.Errorf("expected quiescence timer to not fire for yet")
			}
			continue
		case <-max:
			if _, err = os.Stat(out.Name()); os.IsNotExist(err) {
				t.Errorf("expected template to be rendered by now")
			}
			return
		}
	}
}
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 "consul@nyc1" }}{{.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("consul@nyc1")
	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")
	}
}
Exemple #6
0
func TestReload_sighup(t *testing.T) {
	template := test.CreateTempfile([]byte("initial value"), t)
	defer test.DeleteTempfile(template, t)

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

	_, outPipeWriter := io.Pipe()
	errPipeReader, errPipeWriter := io.Pipe()
	cli := NewCLI(outPipeWriter, errPipeWriter)

	command := fmt.Sprintf("consul-template -template %s:%s", template.Name(), out.Name())
	args := strings.Split(command, " ")

	go func() {
		buf := bytes.NewBuffer(nil)
		for {
			_, err := buf.ReadFrom(errPipeReader)
			if err != nil {
				if err == io.EOF {
					errPipeReader.CloseWithError(io.EOF)
					return
				}
				t.Fatalf("Error in error reader: %s", err)
			}
			if buf.Len() > 0 {
				out.Write(buf.Bytes())
			}
			buf.Reset()
		}
	}()

	go func(args []string) {
		if exit := cli.Run(args); exit != 0 {
			t.Fatalf("bad exit code: %d", exit)
		}
	}(args)
	defer cli.stop()

	// Ensure we have run at least once
	test.WaitForFileContents(out.Name(), []byte("initial value"), t)

	newValue := []byte("new value")
	ioutil.WriteFile(template.Name(), newValue, 0644)
	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)

	test.WaitForFileContents(out.Name(), []byte("new value"), t)
}
func TestRun_noopIfMissingData(t *testing.T) {
	in := test.CreateTempfile([]byte(`
    {{ range service "consul@nyc1" }}{{ end }}
  `), t)
	defer test.DeleteTempfile(in, t)

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

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

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

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

	if num := len(buff.Bytes()); num != 0 {
		t.Errorf("expected %d to be %d", num, 0)
	}
}
func TestExecute_timeout(t *testing.T) {
	tmpfile := test.CreateTempfile(nil, t)
	defer test.DeleteTempfile(tmpfile, t)

	config := testConfig(`
		consul = "1.2.3.4:5678"
		token = "abcd1234"

		auth {
			username = "******"
			password = "******"
		}

		ssl {
			enabled = true
			verify = false
		}
	`, t)

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

	err = runner.execute("sleep 10", 100*time.Millisecond)
	if err == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expected := "did not return for 100ms"
	if !strings.Contains(err.Error(), expected) {
		t.Errorf("expected %q to include %q", err.Error(), expected)
	}
}
Exemple #9
0
func TestStart_runsCommandOnChange(t *testing.T) {
	t.Parallel()

	consul := testutil.NewTestServerConfig(t, func(c *testutil.TestServerConfig) {
		c.Stdout = ioutil.Discard
		c.Stderr = ioutil.Discard
	})
	consul.SetKV("foo/bar", []byte("one"))
	defer consul.Stop()

	config := testConfig(fmt.Sprintf(`
		consul = "%s"
		upcase = true

		prefix {
			path = "foo"
		}
	`, consul.HTTPAddr), t)

	f := test.CreateTempfile(nil, t)
	defer os.Remove(f.Name())
	os.Remove(f.Name())

	runner, err := NewRunner(config, []string{"sh", "-c", "echo $BAR > " + f.Name()}, true)
	if err != nil {
		t.Fatal(err)
	}

	runner.outStream, runner.errStream = ioutil.Discard, ioutil.Discard

	go runner.Start()
	defer runner.Stop()

	test.WaitForFileContents(f.Name(), []byte("one\n"), t)
}
Exemple #10
0
func TestFileFetch_waits(t *testing.T) {
	data := `{"foo":"bar"}`
	inTemplate := test.CreateTempfile([]byte(data), t)
	defer test.DeleteTempfile(inTemplate, t)

	dep, err := ParseFile(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	_, _, err = dep.Fetch(nil, nil)
	if err != nil {
		t.Fatal(err)
	}

	doneCh := make(chan struct{})
	errCh := make(chan error)
	go func() {
		if _, _, err := dep.Fetch(nil, nil); err != nil {
			errCh <- err
			return
		}
		close(doneCh)
	}()

	select {
	case err := <-errCh:
		t.Fatal(err)
	case <-doneCh:
		t.Fatal("received data, but should not have")
	case <-time.After(1000 * time.Nanosecond):
		return
	}
}
Exemple #11
0
func TestFileFetch_stopped(t *testing.T) {
	data := `{"foo":"bar"}`
	inTemplate := test.CreateTempfile([]byte(data), t)
	defer test.DeleteTempfile(inTemplate, t)

	dep, err := ParseFile(inTemplate.Name())
	if err != nil {
		t.Fatal(err)
	}

	errCh := make(chan error)
	go func() {
		results, _, err := dep.Fetch(nil, &QueryOptions{WaitIndex: 100})
		if results != nil {
			t.Fatalf("should not get results: %#v", results)
		}
		errCh <- err
	}()

	dep.Stop()

	select {
	case err := <-errCh:
		if err != ErrStopped {
			t.Errorf("expected %q to be %q", err, ErrStopped)
		}
	case <-time.After(50 * time.Millisecond):
		t.Errorf("did not return in 50ms")
	}
}
Exemple #12
0
func TestDedup_IsLeader(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")
	}

	// Check that we are the leader
	if !dedup.IsLeader(tmpl) {
		t.Fatalf("should be leader")
	}
}
func TestExecute_missingDependencies(t *testing.T) {
	contents := []byte(`{{key "foo"}}`)

	in := test.CreateTempfile(contents, 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(used); num != 1 {
		t.Fatalf("expected 1 used, got: %d", num)
	}

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

	expectedResult := []byte("")
	if !bytes.Equal(result, expectedResult) {
		t.Errorf("expected %q to be %q", result, expectedResult)
	}
}
func TestExecute_delims(t *testing.T) {
	in := test.CreateTempfile([]byte(`
		redis: <%= key "server/redis/maxconns" %>
	`), t)
	defer test.DeleteTempfile(in, t)

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

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

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

	if num := len(used); num != 1 {
		t.Fatalf("expected 1 used, got: %d", num)
	}
}
func TestFileFetch_firesChanges(t *testing.T) {
	data := `{"foo":"bar"}`
	inTemplate := test.CreateTempfile([]byte(data), t)
	defer test.DeleteTempfile(inTemplate, t)

	dep := &File{
		rawKey: inTemplate.Name(),
	}

	_, _, err := dep.Fetch(nil, nil)
	if err != nil {
		t.Fatal(err)
	}

	dataCh := make(chan interface{})
	go func(c chan<- interface{}) {
		data, _, err := dep.Fetch(nil, nil)
		if err != nil {
			t.Fatal(err)
		}
		c <- data
	}(dataCh)

	newData := `{"bar": "baz"}`
	ioutil.WriteFile(inTemplate.Name(), []byte(newData), 0644)

	select {
	case d := <-dataCh:
		if d != newData {
			t.Fatalf("expected %q to be %q", d, newData)
		}
	case <-time.After(1 * time.Second):
		t.Fatal("did not receive data from file changes")
	}
}
func TestRun_singlePass(t *testing.T) {
	in := test.CreateTempfile([]byte(`
    {{ range service "consul@nyc1"}}{{ end }}
    {{ range service "consul@nyc2"}}{{ end }}
    {{ range service "consul@nyc3"}}{{ end }}
  `), 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)
	}

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

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

	if len(runner.dependencies) != 3 {
		t.Errorf("expected %d to be %d", len(runner.dependencies), 3)
	}
}
func TestFileFetch_waits(t *testing.T) {
	data := `{"foo":"bar"}`
	inTemplate := test.CreateTempfile([]byte(data), t)
	defer test.DeleteTempfile(inTemplate, t)

	dep := &File{
		rawKey: inTemplate.Name(),
	}

	_, _, err := dep.Fetch(nil, nil)
	if err != nil {
		t.Fatal(err)
	}

	dataCh := make(chan interface{})
	go func(c chan<- interface{}) {
		data, _, err := dep.Fetch(nil, nil)
		if err != nil {
			t.Fatal(err)
		}
		c <- data
	}(dataCh)

	select {
	case <-dataCh:
		t.Fatal("received data, but should not have")
	case <-time.After(1000 * time.Nanosecond):
		return
	}
}
func TestExecute_noDependencies(t *testing.T) {
	contents := []byte("This is a template with just text")

	in := test.CreateTempfile(contents, 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(used); num != 0 {
		t.Errorf("expected 0 missing, got: %d", num)
	}

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

	if !bytes.Equal(result, contents) {
		t.Errorf("expected %q to be %q", result, contents)
	}
}
func TestExecute_funcs(t *testing.T) {
	in := test.CreateTempfile([]byte(`
    {{ range service "release.webapp" }}{{.Address}}{{ end }}
    {{ key "service/redis/maxconns" }}
    {{ range ls "service/redis/config" }}{{.Key}}{{ end }}
  `), t)
	defer test.DeleteTempfile(in, t)

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

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

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

	if num := len(used); num != 3 {
		t.Fatalf("expected 3 used, got: %d", num)
	}
}
func TestExecute_duplicateFuncs(t *testing.T) {
	in := test.CreateTempfile([]byte(`
    {{ key "service/redis/maxconns" }}
    {{ key "service/redis/maxconns" }}
    {{ key "service/redis/maxconns" }}
  `), t)
	defer test.DeleteTempfile(in, t)

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

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

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

	if num := len(used); num != 1 {
		t.Fatalf("expected 1 used, got: %d", num)
	}
}
func TestRunner_pidCreate(t *testing.T) {
	pidfile := test.CreateTempfile(nil, t)
	os.Remove(pidfile.Name())
	defer os.Remove(pidfile.Name())

	config := testConfig(fmt.Sprintf(`
		pid_file = "%s"
	`, pidfile.Name()), t)

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

	go runner.Start()
	defer runner.Stop()

	select {
	case err := <-runner.ErrCh:
		t.Fatal(err)
	case <-time.After(100 * time.Millisecond):
	}

	_, err = os.Stat(pidfile.Name())
	if err != nil {
		t.Fatal("expected pidfile to exist")
	}
}
func TestExecte_badFuncs(t *testing.T) {
	in := test.CreateTempfile([]byte(`{{ tickle_me_pink }}`), 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("expected error, but nothing was returned")
	}

	expected := `function "tickle_me_pink" not defined`
	if !strings.Contains(err.Error(), expected) {
		t.Errorf("expected %q to contain %q", err.Error(), expected)
	}

	if used != nil {
		t.Errorf("expected used to be nil")
	}

	if missing != nil {
		t.Errorf("expected missing to be nil")
	}

	if result != nil {
		t.Errorf("expected result to be nil")
	}
}
Exemple #23
0
func TestParseConfig_parseFileError(t *testing.T) {
	configFile := test.CreateTempfile([]byte(`
    invalid
  `), t)
	defer test.DeleteTempfile(configFile, t)

	_, err := ParseConfig(configFile.Name())
	if err == nil {
		t.Fatal("expected error, but nothing was returned")
	}
}
func TestRun_doublePass(t *testing.T) {
	in := test.CreateTempfile([]byte(`
		{{ range ls "services" }}
			{{ range service .Key }}
				{{.Node}} {{.Address}}:{{.Port}}
			{{ end }}
		{{ end }}
  `), 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)
	}

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

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

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

	d, err := dep.ParseStoreKeyPrefix("services")
	if err != nil {
		t.Fatal(err)
	}
	data := []*dep.KeyPair{
		&dep.KeyPair{Key: "service1"},
		&dep.KeyPair{Key: "service2"},
		&dep.KeyPair{Key: "service3"},
	}
	runner.Receive(d, data)

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

	if len(runner.dependencies) != 4 {
		t.Errorf("expected %d to be %d", len(runner.dependencies), 4)
	}
}
Exemple #25
0
func TestRun_onceFlag(t *testing.T) {
	consul := testutil.NewTestServerConfig(t, func(c *testutil.TestServerConfig) {
		c.Stdout = ioutil.Discard
		c.Stderr = ioutil.Discard
	})
	defer consul.Stop()

	consul.SetKV("foo", []byte("bar"))

	template := test.CreateTempfile([]byte(`
	{{key "foo"}}
  `), t)
	defer test.DeleteTempfile(template, t)

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

	outStream := gatedio.NewByteBuffer()
	cli := NewCLI(outStream, outStream)

	command := fmt.Sprintf("consul-template -consul %s -template %s:%s -once -log-level debug",
		consul.HTTPAddr, template.Name(), out.Name())
	args := strings.Split(command, " ")

	ch := make(chan int, 1)
	go func() {
		ch <- cli.Run(args)
	}()

	select {
	case status := <-ch:
		if status != ExitCodeOK {
			t.Errorf("expected %d to eq %d", status, ExitCodeOK)
			t.Errorf("out: %s", outStream.String())
		}
	case <-time.After(500 * time.Millisecond):
		t.Errorf("expected exit, did not exit after 500ms")
		t.Errorf("out: %s", outStream.String())
	}
}
func TestRun_doesNotExecuteCommandMissingDependencies(t *testing.T) {
	outFile := test.CreateTempfile(nil, t)
	os.Remove(outFile.Name())
	defer os.Remove(outFile.Name())

	inTemplate := test.CreateTempfile([]byte(`
    {{ range service "consul@nyc1"}}{{ 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()),
			},
		},
	})

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

	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 "consul@nyc1" }}{{.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("consul@nyc1")
	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)
	}
}
func TestParseConfig_parseWaitError(t *testing.T) {
	configFile := test.CreateTempfile([]byte(`
    wait = "not_valid:duration"
  `), t)
	defer test.DeleteTempfile(configFile, t)

	_, err := ParseConfig(configFile.Name())
	if err == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expectedErr := "wait invalid"
	if !strings.Contains(err.Error(), expectedErr) {
		t.Fatalf("expected error %q to contain %q", err.Error(), expectedErr)
	}
}
func TestParseConfig_parseRetryError(t *testing.T) {
	configFile := test.CreateTempfile([]byte(`
    retry = "bacon pants"
  `), t)
	defer test.DeleteTempfile(configFile, t)

	_, err := ParseConfig(configFile.Name())
	if err == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expectedErr := "retry invalid"
	if !strings.Contains(err.Error(), expectedErr) {
		t.Fatalf("expected error %q to contain %q", err.Error(), expectedErr)
	}
}
// Test that mapstructure errors are propagated up
func TestParseConfig_mapstructureError(t *testing.T) {
	configFile := test.CreateTempfile([]byte(`
    consul = true
  `), t)
	defer test.DeleteTempfile(configFile, t)

	_, err := ParseConfig(configFile.Name())
	if err == nil {
		t.Fatal("expected error, but nothing was returned")
	}

	expectedErr := "nconvertible type 'bool'"
	if !strings.Contains(err.Error(), expectedErr) {
		t.Fatalf("expected error %q to contain %q", err.Error(), expectedErr)
	}
}