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 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 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) }
func TestRunner_execReload(t *testing.T) { t.Parallel() consul := testutil.NewTestServerConfig(t, func(c *testutil.TestServerConfig) { c.Stdout = ioutil.Discard c.Stderr = ioutil.Discard }) defer consul.Stop() out := test.CreateTempfile(nil, t) defer test.DeleteTempfile(out, t) tmpl := test.CreateTempfile([]byte(`{{ key "foo" }}`), t) defer test.DeleteTempfile(tmpl, t) // Create a tiny bash script for us to run as a "program" script := test.CreateTempfile([]byte(strings.TrimSpace(fmt.Sprintf(` #!/usr/bin/env bash trap "echo 'one' >> %s" SIGUSR1 trap "echo 'two' >> %s" SIGUSR2 while true; do : # Hang done `, out.Name(), out.Name()))), t) if err := os.Chmod(script.Name(), 0700); err != nil { t.Fatal(err) } defer test.DeleteTempfile(script, t) config := testConfig(fmt.Sprintf(` consul = "%s" template { source = "%s" } exec { command = "%s" reload_signal = "sigusr1" kill_signal = "sigusr2" # We used SIGUSR2 to check, so there's force-kill shortly to make the # test faster. kill_timeout = "10ms" } `, consul.HTTPAddr, tmpl.Name(), script.Name()), t) runner, err := NewRunner(config, true, false) if err != nil { t.Fatal(err) } go runner.Start() defer runner.Stop() doneCh := make(chan struct{}, 1) go func() { for { if runner.child != nil { close(doneCh) return } time.Sleep(50 * time.Millisecond) } }() select { case err := <-runner.ErrCh: t.Fatal(err) case <-doneCh: // Childprocess is started, we can send it signals now case <-time.After(2 * time.Second): t.Fatal("child process should have started") } // Grab the current child pid - this will help us confirm the child was not // restarted on template change. opid := runner.child.Pid() // Change a dependent value in Consul, which will force the runner to cycle. consul.SetKV("foo", []byte("bar")) // Check that the reload signal was sent. test.WaitForFileContents(out.Name(), []byte("one\n"), t) npid := runner.child.Pid() if opid != npid { t.Errorf("expected %d to be the same as %d", opid, npid) } // Kill the child to check that the kill signal is properly sent. runner.child.Stop() test.WaitForFileContents(out.Name(), []byte("one\ntwo\n"), t) }