diff options
| author | Dave Henderson <dhenderson@gmail.com> | 2018-02-07 07:53:34 -0500 |
|---|---|---|
| committer | Dave Henderson <dhenderson@gmail.com> | 2018-03-02 23:35:26 -0500 |
| commit | 0d465ad2eab7e0f31213efd78be61347fc37f084 (patch) | |
| tree | 46d5461400b6d48e458b11e3b60f4986f03475ec /test | |
| parent | 8aa53b563b57b4debf9dfb9be6ffa2703ed3ca90 (diff) | |
Migrate from bats to pure Go for integration tests
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
Diffstat (limited to 'test')
45 files changed, 1661 insertions, 1260 deletions
diff --git a/test/files/input-dir/config.yml b/test/files/input-dir/config.yml deleted file mode 100644 index 1ec99548..00000000 --- a/test/files/input-dir/config.yml +++ /dev/null @@ -1,2 +0,0 @@ -one: eins -two: zwei diff --git a/test/files/input-dir/in/exclude.txt b/test/files/input-dir/in/exclude.txt deleted file mode 100644 index d9cfae16..00000000 --- a/test/files/input-dir/in/exclude.txt +++ /dev/null @@ -1 +0,0 @@ -Should not be included diff --git a/test/files/input-dir/in/inner/nested.txt b/test/files/input-dir/in/inner/nested.txt deleted file mode 100644 index 55e06b79..00000000 --- a/test/files/input-dir/in/inner/nested.txt +++ /dev/null @@ -1 +0,0 @@ -{{ (datasource "config").two }}
\ No newline at end of file diff --git a/test/files/input-dir/in/top.txt b/test/files/input-dir/in/top.txt deleted file mode 100644 index 9069510b..00000000 --- a/test/files/input-dir/in/top.txt +++ /dev/null @@ -1 +0,0 @@ -{{ (datasource "config").one }}
\ No newline at end of file diff --git a/test/integration/.gitignore b/test/integration/.gitignore deleted file mode 100644 index 62c29879..00000000 --- a/test/integration/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -gomplate -mirror -meta -aws diff --git a/test/integration/Dockerfile b/test/integration/Dockerfile deleted file mode 100644 index a82c7dc5..00000000 --- a/test/integration/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -FROM alpine:edge - -ENV VAULT_VER 0.9.3 -ENV CONSUL_VER 1.0.3 -RUN apk add --no-cache \ - curl \ - bash \ - bats \ - jq \ - && curl -L -o /tmp/vault.zip https://releases.hashicorp.com/vault/${VAULT_VER}/vault_${VAULT_VER}_linux_amd64.zip \ - && unzip /tmp/vault.zip \ - && mv vault /bin/vault \ - && rm /tmp/vault.zip \ - && curl -L -o /tmp/consul.zip https://releases.hashicorp.com/consul/${CONSUL_VER}/consul_${CONSUL_VER}_linux_amd64.zip \ - && unzip /tmp/consul.zip \ - && mv consul /bin/consul \ - && rm /tmp/consul.zip - -RUN mkdir /lib64 \ - && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2 - -COPY gomplate /bin/gomplate -COPY mirror /bin/mirror -COPY meta /bin/meta -COPY aws /bin/aws -COPY *.sh /tests/ -COPY *.bash /tests/ -COPY *.bats /tests/ -COPY *.db /test/integration/ - -CMD ["/tests/test.sh"] diff --git a/test/integration/base64.bats b/test/integration/base64.bats deleted file mode 100644 index 43b623d6..00000000 --- a/test/integration/base64.bats +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bats - -load helper - -tmpdir=$(mktemp -u) - -function setup () { - mkdir -p $tmpdir -} - -function teardown () { - rm -rf $tmpdir -} - -@test "'base64.Encode'" { - gomplate -i '{{ "foo" | base64.Encode }}' - [ "$status" -eq 0 ] - [[ "${output}" == "Zm9v" ]] -} - -@test "'base64.Decode'" { - gomplate -i '{{ "Zm9v" | base64.Decode }}' - [ "$status" -eq 0 ] - [[ "${output}" == "foo" ]] -}
\ No newline at end of file diff --git a/test/integration/base64_test.go b/test/integration/base64_test.go new file mode 100644 index 00000000..28dddd00 --- /dev/null +++ b/test/integration/base64_test.go @@ -0,0 +1,26 @@ +//+build integration +//+build !windows + +package integration + +import ( + . "gopkg.in/check.v1" + + "github.com/gotestyourself/gotestyourself/icmd" +) + +type Base64Suite struct{} + +var _ = Suite(&Base64Suite{}) + +func (s *Base64Suite) TestBase64Encode(c *C) { + result := icmd.RunCommand(GomplateBin, "-i", + `{{ "foo" | base64.Encode }}`) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "Zm9v"}) +} + +func (s *Base64Suite) TestBase64Decode(c *C) { + result := icmd.RunCommand(GomplateBin, "-i", + `{{ "Zm9v" | base64.Decode }}`) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "foo"}) +} diff --git a/test/integration/basic.bats b/test/integration/basic.bats deleted file mode 100644 index 974ef3d8..00000000 --- a/test/integration/basic.bats +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env bats - -load helper - -tmpdir=$(mktemp -u) - -function setup () { - mkdir -p $tmpdir - echo 'hi' > $tmpdir/one - echo 'hello' > $tmpdir/two -} - -function teardown () { - rm -rf $tmpdir -} - -@test "reports version" { - gomplate -v - [ "$status" -eq 0 ] - [[ "${output}" == "gomplate version "* ]] -} - -@test "takes stdin by default" { - gomplate_stdin "hello world" - [ "$status" -eq 0 ] - [[ "${output}" == "hello world" ]] -} - -@test "takes stdin with --file -" { - gomplate_stdin "hello world" --file - - [ "$status" -eq 0 ] - [[ "${output}" == "hello world" ]] -} - -@test "writes to stdout with --out -" { - gomplate_stdin "hello world" --out - - [ "$status" -eq 0 ] - [[ "${output}" == "hello world" ]] -} - -@test "ignores stdin with --in" { - gomplate_stdin "hello world" --in "hi" - [ "$status" -eq 0 ] - [[ "${output}" == "hi" ]] -} - -@test "errors given more inputs than outputs" { - gomplate -f $tmpdir/one -f $tmpdir/two -o $tmpdir/out - [ "$status" -eq 1 ] - [[ "${lines[0]}" == "Error: Must provide same number of --out (1) as --file (2) options" ]] -} - -@test "routes inputs to their proper outputs" { - gomplate -f $tmpdir/one -f $tmpdir/two -o $tmpdir/one.out -o $tmpdir/two.out - [ "$status" -eq 0 ] - [[ "$(cat $tmpdir/one.out)" == "hi" ]] - [[ "$(cat $tmpdir/two.out)" == "hello" ]] -} - -@test "can't mix --in and --file" { - gomplate -i 'HELLO WORLD' -f - - [ "$status" -eq 1 ] - [[ "${lines[0]}" == "Error: --in and --file may not be used together" ]] -} - -@test "delimiters can be changed through opts" { - gomplate --left-delim "((" --right-delim "))" -i '((print "hi"))' - [ "$status" -eq 0 ] - [[ "${output}" == "hi" ]] -} - -@test "delimiters can be changed through envvars" { - GOMPLATE_LEFT_DELIM="<<" GOMPLATE_RIGHT_DELIM=">>" gomplate -i '<<print "hi">>' - [ "$status" -eq 0 ] - [[ "${output}" == "hi" ]] -} - -@test "unknown argument results in error" { - gomplate -in flibbit - [ "$status" -eq 1 ] - [[ "${lines[0]}" == 'Error: unknown command "flibbit" for "gomplate"' ]] -} diff --git a/test/integration/basic_test.go b/test/integration/basic_test.go new file mode 100644 index 00000000..c0a52a0b --- /dev/null +++ b/test/integration/basic_test.go @@ -0,0 +1,154 @@ +//+build integration +//+build !windows + +package integration + +import ( + "bytes" + "io/ioutil" + + . "gopkg.in/check.v1" + + "github.com/gotestyourself/gotestyourself/assert" + "github.com/gotestyourself/gotestyourself/assert/cmp" + "github.com/gotestyourself/gotestyourself/fs" + "github.com/gotestyourself/gotestyourself/icmd" +) + +type BasicSuite struct { + tmpDir *fs.Dir +} + +var _ = Suite(&BasicSuite{}) + +func (s *BasicSuite) SetUpSuite(c *C) { + s.tmpDir = fs.NewDir(c, "gomplate-inttests", + fs.WithFile("one", "hi\n"), + fs.WithFile("two", "hello\n")) +} + +func (s *BasicSuite) TearDownSuite(c *C) { + s.tmpDir.Remove() +} + +func (s *BasicSuite) TestReportsVersion(c *C) { + result := icmd.RunCommand(GomplateBin, "-v") + result.Assert(c, icmd.Success) + assert.Assert(c, cmp.Contains(result.Combined(), "gomplate version ")) +} + +func (s *BasicSuite) TestTakesStdinByDefault(c *C) { + result := icmd.RunCmd(icmd.Command(GomplateBin), func(cmd *icmd.Cmd) { + cmd.Stdin = bytes.NewBufferString("hello world") + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "hello world"}) +} + +func (s *BasicSuite) TestTakesStdinWithFileFlag(c *C) { + result := icmd.RunCmd(icmd.Command(GomplateBin, "--file", "-"), func(cmd *icmd.Cmd) { + cmd.Stdin = bytes.NewBufferString("hello world") + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "hello world"}) +} +func (s *BasicSuite) TestWritesToStdoutWithOutFlag(c *C) { + result := icmd.RunCmd(icmd.Command(GomplateBin, "--out", "-"), func(cmd *icmd.Cmd) { + cmd.Stdin = bytes.NewBufferString("hello world") + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "hello world"}) +} + +func (s *BasicSuite) TestIgnoresStdinWithInFlag(c *C) { + result := icmd.RunCmd(icmd.Command(GomplateBin, "--in", "hi"), func(cmd *icmd.Cmd) { + cmd.Stdin = bytes.NewBufferString("hello world") + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "hi"}) +} + +func (s *BasicSuite) TestErrorsWithInputOutputImbalance(c *C) { + result := icmd.RunCmd(icmd.Command(GomplateBin, + "-f", s.tmpDir.Join("one"), + "-f", s.tmpDir.Join("two"), + "-o", s.tmpDir.Join("out")), func(cmd *icmd.Cmd) { + cmd.Stdin = bytes.NewBufferString("hello world") + }) + result.Assert(c, icmd.Expected{ + ExitCode: 1, + Err: "Error: Must provide same number of --out (1) as --file (2) options", + }) +} + +func (s *BasicSuite) TestRoutesInputsToProperOutputs(c *C) { + oneOut := s.tmpDir.Join("one.out") + twoOut := s.tmpDir.Join("two.out") + result := icmd.RunCmd(icmd.Command(GomplateBin, + "-f", s.tmpDir.Join("one"), + "-f", s.tmpDir.Join("two"), + "-o", oneOut, + "-o", twoOut), func(cmd *icmd.Cmd) { + cmd.Stdin = bytes.NewBufferString("hello world") + }) + result.Assert(c, icmd.Success) + + content, err := ioutil.ReadFile(oneOut) + assert.NilError(c, err) + assert.Equal(c, "hi\n", string(content)) + content, err = ioutil.ReadFile(twoOut) + assert.NilError(c, err) + assert.Equal(c, "hello\n", string(content)) +} + +func (s *BasicSuite) TestFlagRules(c *C) { + result := icmd.RunCommand(GomplateBin, "-f", "-", "-i", "HELLO WORLD") + result.Assert(c, icmd.Expected{ + ExitCode: 1, + Out: "--in and --file may not be used together", + }) + + result = icmd.RunCommand(GomplateBin, "--output-dir", ".") + result.Assert(c, icmd.Expected{ + ExitCode: 1, + Out: "--input-dir must be set when --output-dir is set", + }) + + result = icmd.RunCommand(GomplateBin, "--input-dir", ".", "--in", "param") + result.Assert(c, icmd.Expected{ + ExitCode: 1, + Out: "--input-dir can not be used together with --in or --file", + }) + + result = icmd.RunCommand(GomplateBin, "--input-dir", ".", "--file", "input.txt") + result.Assert(c, icmd.Expected{ + ExitCode: 1, + Out: "--input-dir can not be used together with --in or --file", + }) + + result = icmd.RunCommand(GomplateBin, "--output-dir", ".", "--out", "param") + result.Assert(c, icmd.Expected{ + ExitCode: 1, + Out: "--output-dir can not be used together with --out", + }) +} + +func (s *BasicSuite) TestDelimsChangedThroughOpts(c *C) { + result := icmd.RunCommand(GomplateBin, + "--left-delim", "((", + "--right-delim", "))", + "-i", `((print "hi"))`) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "hi"}) +} + +func (s *BasicSuite) TestDelimsChangedThroughEnvVars(c *C) { + result := icmd.RunCmd(icmd.Command(GomplateBin, "-i", `<<print "hi">>`), + func(cmd *icmd.Cmd) { + cmd.Env = []string{ + "GOMPLATE_LEFT_DELIM=<<", + "GOMPLATE_RIGHT_DELIM=>>", + } + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "hi"}) +} + +func (s *BasicSuite) TestUnknownArgErrors(c *C) { + result := icmd.RunCommand(GomplateBin, "-in", "flibbit") + result.Assert(c, icmd.Expected{ExitCode: 1, Out: `unknown command "flibbit" for "gomplate"`}) +} diff --git a/test/integration/config.db b/test/integration/config.db Binary files differdeleted file mode 100644 index e68a29c6..00000000 --- a/test/integration/config.db +++ /dev/null diff --git a/test/integration/datasources_boltdb.bats b/test/integration/datasources_boltdb.bats deleted file mode 100644 index 8260cbf6..00000000 --- a/test/integration/datasources_boltdb.bats +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bats - -load helper - -tmpdir=$(mktemp -u) - -function setup () { - mkdir -p $tmpdir -} - -function teardown () { - rm -rf $tmpdir || true -} - -@test "supports BoltDB datasource file" { - cp test/integration/config.db $tmpdir/config.db - gomplate -d config=boltdb://$tmpdir/config.db#Bucket1 -i '{{(datasource "config" "foo")}}' - [ "$status" -eq 0 ] - [[ "${output}" == "bar" ]] -} - -@test "supports multi-bucket BoltDB datasource file" { - cp test/integration/config.db $tmpdir/config.db - gomplate -d config=boltdb://$tmpdir/config.db#Bucket1 -d config2=boltdb://$tmpdir/config.db#Bucket2 -i '{{(datasource "config" "foo")}}-{{(datasource "config2" "foobar")}}' - [ "$status" -eq 0 ] - [[ "${output}" == "bar-baz" ]] -} diff --git a/test/integration/datasources_boltdb_test.go b/test/integration/datasources_boltdb_test.go new file mode 100644 index 00000000..a81e2b20 --- /dev/null +++ b/test/integration/datasources_boltdb_test.go @@ -0,0 +1,65 @@ +//+build integration +//+build !windows + +package integration + +import ( + . "gopkg.in/check.v1" + + "github.com/boltdb/bolt" + "github.com/gotestyourself/gotestyourself/fs" + "github.com/gotestyourself/gotestyourself/icmd" +) + +type BoltDBDatasourcesSuite struct { + tmpDir *fs.Dir +} + +var _ = Suite(&BoltDBDatasourcesSuite{}) + +func (s *BoltDBDatasourcesSuite) SetUpSuite(c *C) { + s.tmpDir = fs.NewDir(c, "gomplate-inttests") + db, err := bolt.Open(s.tmpDir.Join("config.db"), 0600, nil) + handle(c, err) + defer db.Close() + + err = db.Update(func(tx *bolt.Tx) error { + var b *bolt.Bucket + b, err = tx.CreateBucket([]byte("Bucket1")) + if err != nil { + return err + } + // the first 8 bytes are ignored when read by libkv, so we prefix with gibberish + err = b.Put([]byte("foo"), []byte("00000000bar")) + if err != nil { + return err + } + + b, err = tx.CreateBucket([]byte("Bucket2")) + if err != nil { + return err + } + err = b.Put([]byte("foobar"), []byte("00000000baz")) + return err + }) + handle(c, err) +} + +func (s *BoltDBDatasourcesSuite) TearDownSuite(c *C) { + s.tmpDir.Remove() +} + +func (s *BoltDBDatasourcesSuite) TestBoltDBDatasource(c *C) { + result := icmd.RunCommand(GomplateBin, + "-d", "config=boltdb://"+s.tmpDir.Join("config.db#Bucket1"), + "-i", `{{(ds "config" "foo")}}`, + ) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) + + result = icmd.RunCommand(GomplateBin, + "-d", "config=boltdb://"+s.tmpDir.Join("config.db#Bucket1"), + "-d", "config2=boltdb://"+s.tmpDir.Join("config.db#Bucket2"), + "-i", `{{(ds "config" "foo")}}-{{(ds "config2" "foobar")}}`, + ) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar-baz"}) +} diff --git a/test/integration/datasources_consul.bats b/test/integration/datasources_consul.bats deleted file mode 100644 index b62848a1..00000000 --- a/test/integration/datasources_consul.bats +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bats - -load helper - -function setup () { - start_consul 8501 - export CONSUL_HTTP_ADDR=http://127.0.0.1:8501 -} - -function teardown () { - export CONSUL_HTTP_ADDR=http://127.0.0.1:8501 - consul kv delete foo - vault secrets disable consul - stop_consul -} - -@test "Testing consul" { - consul kv put foo "$BATS_TEST_DESCRIPTION" - gomplate -d consul=consul:// -i '{{(datasource "consul" "foo")}}' - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} - -@test "Consul datasource works with MIME override" { - consul kv put foo "{\"desc\":$BATS_TEST_DESCRIPTION}" - gomplate -d consul=consul://?type=application/json -i '{{(datasource "consul" "foo").desc}}' - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} - -@test "Consul datasource works with hostname in URL" { - consul kv put foo "$BATS_TEST_DESCRIPTION" - unset CONSUL_HTTP_ADDR - gomplate -d consul=consul://127.0.0.1:8501/ -i '{{(datasource "consul" "foo")}}' - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} - -@test "Consul datasource works with consul+http scheme" { - consul kv put foo "$BATS_TEST_DESCRIPTION" - unset CONSUL_HTTP_ADDR - gomplate -d consul=consul+http://127.0.0.1:8501/ -i '{{(datasource "consul" "foo")}}' - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} - -@test "Consul datasource works with Vault auth" { - vault mount consul - vault write consul/config/access address=127.0.0.1:8501 token=${CONSUL_ROOT_TOKEN} - POLICY='key "" { policy = "read" }' - vault write consul/roles/readonly policy=`echo $POLICY | base64` - consul kv put foo "$BATS_TEST_DESCRIPTION" - CONSUL_VAULT_ROLE=readonly gomplate -d consul=consul:// -i '{{(datasource "consul" "foo")}}' - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} diff --git a/test/integration/datasources_consul_test.go b/test/integration/datasources_consul_test.go new file mode 100644 index 00000000..f0c58f68 --- /dev/null +++ b/test/integration/datasources_consul_test.go @@ -0,0 +1,208 @@ +//+build integration +//+build !windows + +package integration + +import ( + "encoding/base64" + "io/ioutil" + "os" + "os/user" + "path" + "strconv" + + . "gopkg.in/check.v1" + + "github.com/gotestyourself/gotestyourself/fs" + "github.com/gotestyourself/gotestyourself/icmd" + vaultapi "github.com/hashicorp/vault/api" +) + +type ConsulDatasourcesSuite struct { + tmpDir *fs.Dir + pidDir *fs.Dir + consulAddr string + consulResult *icmd.Result + vaultAddr string + vaultResult *icmd.Result +} + +var _ = Suite(&ConsulDatasourcesSuite{}) + +const consulRootToken = "00000000-1111-2222-3333-444455556666" + +func (s *ConsulDatasourcesSuite) SetUpSuite(c *C) { + s.pidDir = fs.NewDir(c, "gomplate-inttests-pid") + s.tmpDir = fs.NewDir(c, "gomplate-inttests", + fs.WithFile( + "consul.json", + `{"acl_datacenter": "dc1", "acl_master_token": "`+consulRootToken+`"}`, + ), + fs.WithFile("vault.json", `{ + "pid_file": "`+s.pidDir.Join("vault.pid")+`" + }`), + ) + var port int + port, s.consulAddr = freeport() + consul := icmd.Command("consul", "agent", + "-dev", + "-config-file="+s.tmpDir.Join("consul.json"), + "-log-level=err", + "-http-port="+strconv.Itoa(port), + "-pid-file="+s.pidDir.Join("consul.pid"), + ) + s.consulResult = icmd.StartCmd(consul) + + c.Logf("Fired up Consul: %v", consul) + + err := waitForURL(c, "http://"+s.consulAddr+"/v1/status/leader") + handle(c, err) + + s.startVault(c) +} + +func (s *ConsulDatasourcesSuite) startVault(c *C) { + // rename any existing token so it doesn't get overridden + u, _ := user.Current() + homeDir := u.HomeDir + tokenFile := path.Join(homeDir, ".vault-token") + info, err := os.Stat(tokenFile) + if err == nil && info.Mode().IsRegular() { + os.Rename(tokenFile, path.Join(homeDir, ".vault-token.bak")) + } + + _, s.vaultAddr = freeport() + vault := icmd.Command("vault", "server", + "-dev", + "-dev-root-token-id="+vaultRootToken, + "-log-level=err", + "-dev-listen-address="+s.vaultAddr, + "-config="+s.tmpDir.Join("vault.json"), + ) + s.vaultResult = icmd.StartCmd(vault) + + c.Logf("Fired up Vault: %v", vault) + + err = waitForURL(c, "http://"+s.vaultAddr+"/v1/sys/health") + handle(c, err) +} + +func killByPidFile(pidFile string) error { + p, err := ioutil.ReadFile(pidFile) + if err != nil { + return err + } + pid, err := strconv.Atoi(string(p)) + if err != nil { + return err + } + process, err := os.FindProcess(pid) + if err != nil { + return err + } + err = process.Kill() + return err +} + +func (s *ConsulDatasourcesSuite) TearDownSuite(c *C) { + defer s.tmpDir.Remove() + defer s.pidDir.Remove() + + err := killByPidFile(s.pidDir.Join("vault.pid")) + handle(c, err) + + err = killByPidFile(s.pidDir.Join("consul.pid")) + handle(c, err) + + // restore old vault token if it was backed up + u, _ := user.Current() + homeDir := u.HomeDir + tokenFile := path.Join(homeDir, ".vault-token.bak") + info, err := os.Stat(tokenFile) + if err == nil && info.Mode().IsRegular() { + os.Rename(tokenFile, path.Join(homeDir, ".vault-token")) + } +} + +func (s *ConsulDatasourcesSuite) consulPut(c *C, k string, v string) { + result := icmd.RunCmd(icmd.Command("consul", "kv", "put", k, v), + func(c *icmd.Cmd) { + c.Env = []string{"CONSUL_HTTP_ADDR=http://" + s.consulAddr} + }) + result.Assert(c, icmd.Success) +} + +func (s *ConsulDatasourcesSuite) consulDelete(c *C, k string) { + result := icmd.RunCmd(icmd.Command("consul", "kv", "delete", k), + func(c *icmd.Cmd) { + c.Env = []string{"CONSUL_HTTP_ADDR=http://" + s.consulAddr} + }) + result.Assert(c, icmd.Success) +} + +func (s *ConsulDatasourcesSuite) TestConsulDatasource(c *C) { + s.consulPut(c, "foo", "bar") + defer s.consulDelete(c, "foo") + result := icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "consul=consul://", + "-i", `{{(ds "consul" "foo")}}`, + ), func(c *icmd.Cmd) { + c.Env = []string{"CONSUL_HTTP_ADDR=http://" + s.consulAddr} + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) + + s.consulPut(c, "foo", `{"bar": "baz"}`) + result = icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "consul=consul://?type=application/json", + "-i", `{{(ds "consul" "foo").bar}}`, + ), func(c *icmd.Cmd) { + c.Env = []string{"CONSUL_HTTP_ADDR=http://" + s.consulAddr} + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "baz"}) + + s.consulPut(c, "foo", `bar`) + result = icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "consul=consul://"+s.consulAddr, + "-i", `{{(ds "consul" "foo")}}`, + )) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) + + s.consulPut(c, "foo", `bar`) + result = icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "consul=consul+http://"+s.consulAddr, + "-i", `{{(ds "consul" "foo")}}`, + )) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) +} + +func (s *ConsulDatasourcesSuite) TestConsulWithVaultAuth(c *C) { + v, err := createVaultClient(s.vaultAddr, vaultRootToken) + handle(c, err) + + err = v.vc.Sys().Mount("consul/", &vaultapi.MountInput{Type: "consul"}) + handle(c, err) + defer v.vc.Sys().Unmount("consul/") + + _, err = v.vc.Logical().Write("consul/config/access", map[string]interface{}{ + "address": s.consulAddr, "token": consulRootToken, + }) + handle(c, err) + policy := base64.StdEncoding.EncodeToString([]byte(`key "" { policy = "read" }`)) + _, err = v.vc.Logical().Write("consul/roles/readonly", map[string]interface{}{"policy": policy}) + handle(c, err) + + s.consulPut(c, "foo", "bar") + defer s.consulDelete(c, "foo") + result := icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "consul=consul://", + "-i", `{{(ds "consul" "foo")}}`, + ), func(c *icmd.Cmd) { + c.Env = []string{ + "VAULT_TOKEN=" + vaultRootToken, + "VAULT_ADDR=http://" + s.vaultAddr, + "CONSUL_VAULT_ROLE=readonly", + "CONSUL_HTTP_ADDR=http://" + s.consulAddr, + } + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) +} diff --git a/test/integration/datasources_file.bats b/test/integration/datasources_file.bats deleted file mode 100644 index cfabcb65..00000000 --- a/test/integration/datasources_file.bats +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bats - -load helper - -tmpdir=$(mktemp -u) - -function setup () { - mkdir -p $tmpdir -} - -function teardown () { - rm -rf $tmpdir || true -} - -@test "supports json datasource file" { - echo '{"foo": {"bar": "baz"}}' > $tmpdir/config.json - gomplate -d config=$tmpdir/config.json -i '{{(datasource "config").foo.bar}}' - [ "$status" -eq 0 ] - [[ "${output}" == "baz" ]] -} - -@test "supports YAML datasource file" { - echo -e 'foo:\n bar: baz' > $tmpdir/config.yml - gomplate -d config=$tmpdir/config.yml -i '{{(datasource "config").foo.bar}}' - [ "$status" -eq 0 ] - [[ "${output}" == "baz" ]] -} - -@test "ds alias" { - echo 'foo: bar' > $tmpdir/config.yml - gomplate -d config=$tmpdir/config.yml -i '{{(ds "config").foo}}' - [ "$status" -eq 0 ] - [[ "${output}" == "bar" ]] -} - -@test "supports CSV datasource file" { - echo -e 'A,B\nA1,B1\nA2,"foo""\nbar"\n' > $tmpdir/foo.csv - gomplate -d csv=$tmpdir/foo.csv -i '{{ index (index (ds "csv") 2) 1 }}' - [ "$status" -eq 0 ] - [[ "${output}" == "foo\" -bar" ]] -} - -@test "'include' doesn't parse file" { - echo 'foo: bar' > $tmpdir/config.yml - gomplate -d config=$tmpdir/config.yml -i '{{include "config"}}' - [ "$status" -eq 0 ] - [[ "${output}" == "foo: bar" ]] -} diff --git a/test/integration/datasources_file_test.go b/test/integration/datasources_file_test.go new file mode 100644 index 00000000..1c8d1530 --- /dev/null +++ b/test/integration/datasources_file_test.go @@ -0,0 +1,67 @@ +//+build integration +//+build !windows + +package integration + +import ( + . "gopkg.in/check.v1" + + "github.com/gotestyourself/gotestyourself/fs" + "github.com/gotestyourself/gotestyourself/icmd" +) + +type FileDatasourcesSuite struct { + tmpDir *fs.Dir +} + +var _ = Suite(&FileDatasourcesSuite{}) + +func (s *FileDatasourcesSuite) SetUpSuite(c *C) { + s.tmpDir = fs.NewDir(c, "gomplate-inttests", + fs.WithFile("config.json", `{"foo": {"bar": "baz"}}`), + fs.WithFile("config.yml", "foo:\n bar: baz\n"), + fs.WithFile("config2.yml", "foo: bar\n"), + fs.WithFile("foo.csv", `A,B +A1,B1 +A2,"foo"" +bar" +`), + ) +} + +func (s *FileDatasourcesSuite) TearDownSuite(c *C) { + s.tmpDir.Remove() +} + +func (s *FileDatasourcesSuite) TestFileDatasources(c *C) { + result := icmd.RunCommand(GomplateBin, + "-d", "config="+s.tmpDir.Join("config.json"), + "-i", `{{(datasource "config").foo.bar}}`, + ) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "baz"}) + + result = icmd.RunCommand(GomplateBin, + "-d", "config="+s.tmpDir.Join("config.yml"), + "-i", `{{(datasource "config").foo.bar}}`, + ) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "baz"}) + + result = icmd.RunCommand(GomplateBin, + "-d", "config="+s.tmpDir.Join("config2.yml"), + "-i", `{{(ds "config").foo}}`, + ) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) + + result = icmd.RunCommand(GomplateBin, + "-d", "csv="+s.tmpDir.Join("foo.csv"), + "-i", `{{ index (index (ds "csv") 2) 1 }}`, + ) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: `foo" +bar`}) + + result = icmd.RunCommand(GomplateBin, + "-d", "config="+s.tmpDir.Join("config2.yml"), + "-i", `{{ include "config" }}`, + ) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: `foo: bar`}) +} diff --git a/test/integration/datasources_http.bats b/test/integration/datasources_http.bats deleted file mode 100644 index 4a510e8c..00000000 --- a/test/integration/datasources_http.bats +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bats - -load helper - -function setup() { - start_mirror_svc -} - -function teardown() { - stop_mirror_svc -} - -@test "HTTP datasource with headers" { - gomplate \ - -d foo=http://127.0.0.1:8080/ \ - -H foo=Foo:bar \ - -i '{{ index (datasource "foo").headers.Foo 0 }}' - [ "$status" -eq 0 ] - [[ "${output}" == "bar" ]] -} diff --git a/test/integration/datasources_http_test.go b/test/integration/datasources_http_test.go new file mode 100644 index 00000000..8e516790 --- /dev/null +++ b/test/integration/datasources_http_test.go @@ -0,0 +1,40 @@ +//+build integration +//+build !windows + +package integration + +import ( + "net" + "net/http" + + . "gopkg.in/check.v1" + + "github.com/gotestyourself/gotestyourself/icmd" +) + +type DatasourcesHTTPSuite struct { + l *net.TCPListener +} + +var _ = Suite(&DatasourcesHTTPSuite{}) + +func (s *DatasourcesHTTPSuite) SetUpSuite(c *C) { + var err error + s.l, err = net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("127.0.0.1")}) + handle(c, err) + + http.HandleFunc("/", mirrorHandler) + go http.Serve(s.l, nil) +} + +func (s *DatasourcesHTTPSuite) TearDownSuite(c *C) { + s.l.Close() +} + +func (s *DatasourcesHTTPSuite) TestReportsVersion(c *C) { + result := icmd.RunCommand(GomplateBin, + "-d", "foo=http://"+s.l.Addr().String()+"/", + "-H", "foo=Foo:bar", + "-i", "{{ index (ds `foo`).headers.Foo 0 }}") + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) +} diff --git a/test/integration/datasources_vault.bats b/test/integration/datasources_vault.bats deleted file mode 100644 index 18d338c1..00000000 --- a/test/integration/datasources_vault.bats +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/bin/env bats - -load helper - -function setup () { - unset VAULT_TOKEN - cat <<EOF | vault policy-write writepol - >& /dev/null -path "*" { - policy = "write" -} -EOF - cat <<EOF | vault policy-write readpol - >& /dev/null -path "*" { - policy = "read" -} -EOF - tmpdir=$(mktemp -d) - cp ~/.vault-token ${tmpdir}/.vault-token.bak - start_meta_svc - start_aws_svc -} - -function teardown () { - mv ${tmpdir}/.vault-token.bak ~/.vault-token - stop_meta_svc - stop_aws_svc - rm -rf $tmpdir - unset VAULT_TOKEN - vault delete secret/foo - vault auth disable userpass - vault auth disable userpass2 - vault auth disable approle - vault auth disable approle2 - vault auth disable app-id - vault auth disable app-id2 - vault auth disable aws - vault policy delete writepol - vault policy delete readpol - vault secrets disable ssh -} - -@test "Testing token vault auth" { - vault write secret/foo value="$BATS_TEST_DESCRIPTION" - VAULT_TOKEN=$(vault token create -format=json -policy=readpol -use-limit=1 -ttl=1m | jq -j .auth.client_token) - VAULT_TOKEN=$VAULT_TOKEN gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} - -@test "Testing token vault auth with addr in URL" { - vault write secret/foo value="$BATS_TEST_DESCRIPTION" - VAULT_TOKEN=$(vault token create -format=json -policy=readpol -use-limit=1 -ttl=1m | jq -j .auth.client_token) - addr=$VAULT_ADDR - unset VAULT_ADDR - VAULT_TOKEN=$VAULT_TOKEN gomplate -d vault=vault+${addr}/secret -i '{{(datasource "vault" "foo").value}}' - export VAULT_ADDR=$addr - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} - -@test "Testing failure with non-existant secret" { - VAULT_TOKEN=$(vault token create -format=json -policy=readpol -use-limit=1 -ttl=1m | jq -j .auth.client_token) - VAULT_TOKEN=$VAULT_TOKEN gomplate -d vault=vault:///secret -i '{{(datasource "vault" "bar").value}}' - [ "$status" -eq 1 ] - [[ "${output}" == *"No value found for [bar] from datasource 'vault'" ]] -} - -@test "Testing token vault auth using file" { - vault write secret/foo value="$BATS_TEST_DESCRIPTION" - vault token create -format=json -policy=readpol -use-limit=1 -ttl=1m | jq -j .auth.client_token > $tmpdir/token - unset VAULT_TOKEN - VAULT_TOKEN_FILE=$tmpdir/token gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} - -@test "Testing userpass vault auth" { - vault write secret/foo value="$BATS_TEST_DESCRIPTION" - vault auth enable userpass - vault write auth/userpass/users/dave password=foo ttl=30s policies=readpol - VAULT_AUTH_USERNAME=dave VAULT_AUTH_PASSWORD=foo gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} - -@test "Testing userpass vault auth using files" { - vault write secret/foo value="$BATS_TEST_DESCRIPTION" - vault auth enable userpass - vault write auth/userpass/users/dave password=foo ttl=30s policies=readpol - echo -n "dave" > $tmpdir/username - echo -n "foo" > $tmpdir/password - VAULT_AUTH_USERNAME_FILE=$tmpdir/username VAULT_AUTH_PASSWORD_FILE=$tmpdir/password gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} - -@test "Testing userpass vault auth with custom mount" { - vault write secret/foo value="$BATS_TEST_DESCRIPTION" - vault auth enable -path=userpass2 userpass - vault write auth/userpass2/users/dave password=foo ttl=30s policies=readpol - VAULT_AUTH_USERPASS_MOUNT=userpass2 VAULT_AUTH_USERNAME=dave VAULT_AUTH_PASSWORD=foo gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} - -@test "Testing approle vault auth" { - vault write secret/foo value="$BATS_TEST_DESCRIPTION" - vault auth enable approle - vault write auth/approle/role/testrole secret_id_ttl=30s token_ttl=35s token_max_ttl=3m secret_id_num_uses=1 policies=readpol - VAULT_ROLE_ID=$(vault read -field role_id auth/approle/role/testrole/role-id) - VAULT_SECRET_ID=$(vault write -f -field=secret_id auth/approle/role/testrole/secret-id) - VAULT_ROLE_ID=$VAULT_ROLE_ID VAULT_SECRET_ID=$VAULT_SECRET_ID gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} - -@test "Testing approle vault auth with custom mount" { - vault write secret/foo value="$BATS_TEST_DESCRIPTION" - vault auth enable -path=approle2 approle - vault write auth/approle2/role/testrole secret_id_ttl=30s token_ttl=35s token_max_ttl=3m secret_id_num_uses=1 policies=readpol - VAULT_ROLE_ID=$(vault read -field role_id auth/approle2/role/testrole/role-id) - VAULT_SECRET_ID=$(vault write -f -field=secret_id auth/approle2/role/testrole/secret-id) - VAULT_AUTH_APPROLE_MOUNT=approle2 VAULT_ROLE_ID=$VAULT_ROLE_ID VAULT_SECRET_ID=$VAULT_SECRET_ID gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} - -@test "Testing app-id vault auth" { - vault write secret/foo value="$BATS_TEST_DESCRIPTION" - vault auth enable app-id - vault write auth/app-id/map/app-id/testappid value=readpol display_name=test_app_id - vault write auth/app-id/map/user-id/testuserid value=testappid - VAULT_APP_ID=testappid VAULT_USER_ID=testuserid gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} - -@test "Testing app-id vault auth with custom mount" { - vault write secret/foo value="$BATS_TEST_DESCRIPTION" - vault auth enable -path=app-id2 app-id - - vault write auth/app-id2/map/app-id/testappid value=readpol display_name=test_app_id - vault write auth/app-id2/map/user-id/testuserid value=testappid - - VAULT_APP_ID=testappid VAULT_USER_ID=testuserid VAULT_AUTH_APP_ID_MOUNT=app-id2 gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} - -@test "Testing ec2 vault auth" { - vault write secret/foo value="$BATS_TEST_DESCRIPTION" - vault auth enable aws - vault write auth/aws/config/client secret_key=secret access_key=access endpoint=http://127.0.0.1:8082/ec2 iam_endpoint=http://127.0.0.1:8082/iam sts_endpoint=http://127.0.0.1:8082/sts - curl -o $tmpdir/certificate -s -f http://127.0.0.1:8081/certificate - vault write auth/aws/config/certificate/testcert type=pkcs7 aws_public_cert=@$tmpdir/certificate - vault write auth/aws/role/ami-00000000 auth_type=ec2 bound_ami_id=ami-00000000 policies=readpol - unset VAULT_TOKEN - rm ~/.vault-token - AWS_META_ENDPOINT=http://127.0.0.1:8081 gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}' - [ "$status" -eq 0 ] - [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]] -} - -@test "Testing vault auth with dynamic secret" { - vault mount ssh - vault write ssh/roles/test key_type=otp default_user=user cidr_list=10.0.0.0/8 - VAULT_TOKEN=$(vault token create -format=json -policy=writepol -use-limit=2 -ttl=1m | jq -j .auth.client_token) - VAULT_TOKEN=$VAULT_TOKEN gomplate -d vault=vault:/// -i '{{(datasource "vault" "ssh/creds/test?ip=10.1.2.3&username=user").ip}}' - [ "$status" -eq 0 ] - [[ "${output}" == "10.1.2.3" ]] -} - -@test "Testing vault auth with dynamic secret using prefix" { - vault mount ssh - vault write ssh/roles/test key_type=otp default_user=user cidr_list=10.0.0.0/8 - VAULT_TOKEN=$(vault token create -format=json -policy=writepol -use-limit=2 -ttl=1m | jq -j .auth.client_token) - VAULT_TOKEN=$VAULT_TOKEN gomplate -d vault=vault:///ssh/creds/test -i '{{(datasource "vault" "?ip=10.1.2.3&username=user").ip}}' - [ "$status" -eq 0 ] - [[ "${output}" == "10.1.2.3" ]] -} - -@test "Testing vault auth with dynamic secret using prefix and options in URL" { - vault mount ssh - vault write ssh/roles/test key_type=otp default_user=user cidr_list=10.0.0.0/8 - VAULT_TOKEN=$(vault token create -format=json -policy=writepol -use-limit=2 -ttl=1m | jq -j .auth.client_token) - VAULT_TOKEN=$VAULT_TOKEN gomplate -d vault=vault:///ssh/creds/test?ip=10.1.2.3\&username=user -i '{{(datasource "vault").ip}}' - [ "$status" -eq 0 ] - [[ "${output}" == "10.1.2.3" ]] -} - -@test "Testing vault auth with dynamic secret using options in URL and path in template" { - vault mount ssh - vault write ssh/roles/test key_type=otp default_user=user cidr_list=10.0.0.0/8 - VAULT_TOKEN=$(vault token create -format=json -policy=writepol -use-limit=2 -ttl=1m | jq -j .auth.client_token) - VAULT_TOKEN=$VAULT_TOKEN gomplate -d vault=vault:///?ip=10.1.2.3\&username=user -i '{{(datasource "vault" "ssh/creds/test").ip}}' - [ "$status" -eq 0 ] - [[ "${output}" == "10.1.2.3" ]] -} - -# TODO: test the github auth backend at some point... this needs a github token though, so... -# vault write auth/github/config organization=DockerOttawaMeetup -# vault write auth/github/map/teams/organizers value=pol diff --git a/test/integration/datasources_vault_ec2_test.go b/test/integration/datasources_vault_ec2_test.go new file mode 100644 index 00000000..eeeeae8b --- /dev/null +++ b/test/integration/datasources_vault_ec2_test.go @@ -0,0 +1,155 @@ +//+build integration +//+build !windows + +package integration + +import ( + "encoding/pem" + "io/ioutil" + "net" + "net/http" + "os" + "os/user" + "path" + "strconv" + + . "gopkg.in/check.v1" + + "github.com/gotestyourself/gotestyourself/fs" + "github.com/gotestyourself/gotestyourself/icmd" +) + +type VaultEc2DatasourcesSuite struct { + tmpDir *fs.Dir + pidDir *fs.Dir + vaultAddr string + vaultResult *icmd.Result + v *vaultClient + l *net.TCPListener + cert []byte +} + +var _ = Suite(&VaultEc2DatasourcesSuite{}) + +func (s *VaultEc2DatasourcesSuite) SetUpSuite(c *C) { + var err error + s.l, err = net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("127.0.0.1")}) + handle(c, err) + priv, der, _ := certificateGenerate() + s.cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}) + http.HandleFunc("/latest/dynamic/instance-identity/pkcs7", pkcsHandler(priv, der)) + http.HandleFunc("/latest/dynamic/instance-identity/document", instanceDocumentHandler) + http.HandleFunc("/sts/", stsHandler) + http.HandleFunc("/ec2/", ec2Handler) + go http.Serve(s.l, nil) + + s.startVault(c) + + s.v, err = createVaultClient(s.vaultAddr, vaultRootToken) + handle(c, err) + + err = s.v.vc.Sys().PutPolicy("writepol", `path "*" { + policy = "write" +}`) + handle(c, err) + err = s.v.vc.Sys().PutPolicy("readpol", `path "*" { + policy = "read" +}`) + handle(c, err) +} + +func (s *VaultEc2DatasourcesSuite) startVault(c *C) { + s.pidDir = fs.NewDir(c, "gomplate-inttests-vaultpid") + s.tmpDir = fs.NewDir(c, "gomplate-inttests", + fs.WithFile("config.json", `{ + "pid_file": "`+s.pidDir.Join("vault.pid")+`" + }`), + ) + + // rename any existing token so it doesn't get overridden + u, _ := user.Current() + homeDir := u.HomeDir + tokenFile := path.Join(homeDir, ".vault-token") + info, err := os.Stat(tokenFile) + if err == nil && info.Mode().IsRegular() { + os.Rename(tokenFile, path.Join(homeDir, ".vault-token.bak")) + } + + _, s.vaultAddr = freeport() + vault := icmd.Command("vault", "server", + "-dev", + "-dev-root-token-id="+vaultRootToken, + "-log-level=trace", + "-dev-listen-address="+s.vaultAddr, + "-config="+s.tmpDir.Join("config.json"), + ) + s.vaultResult = icmd.StartCmd(vault) + + c.Logf("Fired up Vault: %v", vault) + + err = waitForURL(c, "http://"+s.vaultAddr+"/v1/sys/health") + handle(c, err) +} + +func (s *VaultEc2DatasourcesSuite) TearDownSuite(c *C) { + s.l.Close() + + defer s.tmpDir.Remove() + defer s.pidDir.Remove() + + p, err := ioutil.ReadFile(s.pidDir.Join("vault.pid")) + handle(c, err) + pid, err := strconv.Atoi(string(p)) + handle(c, err) + process, err := os.FindProcess(pid) + handle(c, err) + err = process.Kill() + handle(c, err) + + // restore old token if it was backed up + u, _ := user.Current() + homeDir := u.HomeDir + tokenFile := path.Join(homeDir, ".vault-token.bak") + info, err := os.Stat(tokenFile) + if err == nil && info.Mode().IsRegular() { + os.Rename(tokenFile, path.Join(homeDir, ".vault-token")) + } +} + +func (s *VaultEc2DatasourcesSuite) TestEc2Auth(c *C) { + s.v.vc.Logical().Write("secret/foo", map[string]interface{}{"value": "bar"}) + defer s.v.vc.Logical().Delete("secret/foo") + err := s.v.vc.Sys().EnableAuth("aws", "aws", "") + handle(c, err) + defer s.v.vc.Sys().DisableAuth("aws") + _, err = s.v.vc.Logical().Write("auth/aws/config/client", map[string]interface{}{ + "secret_key": "secret", "access_key": "access", + "endpoint": "http://" + s.l.Addr().String() + "/ec2", + "iam_endpoint": "http://" + s.l.Addr().String() + "/iam", + "sts_endpoint": "http://" + s.l.Addr().String() + "/sts", + }) + handle(c, err) + + _, err = s.v.vc.Logical().Write("auth/aws/config/certificate/testcert", map[string]interface{}{ + "type": "pkcs7", "aws_public_cert": string(s.cert), + }) + handle(c, err) + + _, err = s.v.vc.Logical().Write("auth/aws/role/ami-00000000", map[string]interface{}{ + "auth_type": "ec2", "bound_ami_id": "ami-00000000", + "policies": "readpol", + }) + handle(c, err) + + result := icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "vault=vault:///secret", + "-i", `{{(ds "vault" "foo").value}}`, + ), func(c *icmd.Cmd) { + c.Env = []string{ + "HOME=" + s.tmpDir.Join("home"), + "VAULT_ADDR=http://" + s.v.addr, + "AWS_META_ENDPOINT=http://" + s.l.Addr().String(), + } + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) +} diff --git a/test/integration/datasources_vault_test.go b/test/integration/datasources_vault_test.go new file mode 100644 index 00000000..cfa6552a --- /dev/null +++ b/test/integration/datasources_vault_test.go @@ -0,0 +1,358 @@ +//+build integration +//+build !windows + +package integration + +import ( + "io/ioutil" + "os" + "os/user" + "path" + "strconv" + + . "gopkg.in/check.v1" + + "github.com/gotestyourself/gotestyourself/fs" + "github.com/gotestyourself/gotestyourself/icmd" + vaultapi "github.com/hashicorp/vault/api" +) + +type VaultDatasourcesSuite struct { + tmpDir *fs.Dir + pidDir *fs.Dir + vaultAddr string + vaultResult *icmd.Result + v *vaultClient +} + +var _ = Suite(&VaultDatasourcesSuite{}) + +const vaultRootToken = "00000000-1111-2222-3333-444455556666" + +func (s *VaultDatasourcesSuite) SetUpSuite(c *C) { + s.startVault(c) + + var err error + s.v, err = createVaultClient(s.vaultAddr, vaultRootToken) + handle(c, err) + + err = s.v.vc.Sys().PutPolicy("writepol", `path "*" { + policy = "write" +}`) + handle(c, err) + err = s.v.vc.Sys().PutPolicy("readpol", `path "*" { + policy = "read" +}`) + handle(c, err) +} + +func (s *VaultDatasourcesSuite) startVault(c *C) { + s.pidDir = fs.NewDir(c, "gomplate-inttests-vaultpid") + s.tmpDir = fs.NewDir(c, "gomplate-inttests", + fs.WithFile("config.json", `{ + "pid_file": "`+s.pidDir.Join("vault.pid")+`" + }`), + ) + + // rename any existing token so it doesn't get overridden + u, _ := user.Current() + homeDir := u.HomeDir + tokenFile := path.Join(homeDir, ".vault-token") + info, err := os.Stat(tokenFile) + if err == nil && info.Mode().IsRegular() { + os.Rename(tokenFile, path.Join(homeDir, ".vault-token.bak")) + } + + _, s.vaultAddr = freeport() + vault := icmd.Command("vault", "server", + "-dev", + "-dev-root-token-id="+vaultRootToken, + "-log-level=err", + "-dev-listen-address="+s.vaultAddr, + "-config="+s.tmpDir.Join("config.json"), + ) + s.vaultResult = icmd.StartCmd(vault) + + c.Logf("Fired up Vault: %v", vault) + + err = waitForURL(c, "http://"+s.vaultAddr+"/v1/sys/health") + handle(c, err) +} + +func (s *VaultDatasourcesSuite) TearDownSuite(c *C) { + defer s.tmpDir.Remove() + defer s.pidDir.Remove() + + p, err := ioutil.ReadFile(s.pidDir.Join("vault.pid")) + handle(c, err) + pid, err := strconv.Atoi(string(p)) + handle(c, err) + process, err := os.FindProcess(pid) + handle(c, err) + err = process.Kill() + handle(c, err) + + // restore old token if it was backed up + u, _ := user.Current() + homeDir := u.HomeDir + tokenFile := path.Join(homeDir, ".vault-token.bak") + info, err := os.Stat(tokenFile) + if err == nil && info.Mode().IsRegular() { + os.Rename(tokenFile, path.Join(homeDir, ".vault-token")) + } +} + +func (s *VaultDatasourcesSuite) TestTokenAuth(c *C) { + s.v.vc.Logical().Write("secret/foo", map[string]interface{}{"value": "bar"}) + defer s.v.vc.Logical().Delete("secret/foo") + tok, err := s.v.tokenCreate("readpol", 4) + handle(c, err) + + result := icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "vault=vault:///secret", + "-i", `{{(ds "vault" "foo").value}}`, + ), func(c *icmd.Cmd) { + c.Env = []string{ + "VAULT_ADDR=http://" + s.v.addr, + "VAULT_TOKEN=" + tok, + } + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) + + result = icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "vault=vault+http://"+s.v.addr+"/secret", + "-i", `{{(ds "vault" "foo").value}}`, + ), func(c *icmd.Cmd) { + c.Env = []string{ + "VAULT_TOKEN=" + tok, + } + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) + + result = icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "vault=vault:///secret", + "-i", `{{(ds "vault" "bar").value}}`, + ), func(c *icmd.Cmd) { + c.Env = []string{ + "VAULT_ADDR=http://" + s.v.addr, + "VAULT_TOKEN=" + tok, + } + }) + result.Assert(c, icmd.Expected{ExitCode: 1, Err: "No value found for [bar] from datasource 'vault'"}) + + tokFile := fs.NewFile(c, "test-vault-token", fs.WithContent(tok)) + defer tokFile.Remove() + result = icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "vault=vault:///secret", + "-i", `{{(ds "vault" "foo").value}}`, + ), func(c *icmd.Cmd) { + c.Env = []string{ + "VAULT_ADDR=http://" + s.v.addr, + "VAULT_TOKEN_FILE=" + tokFile.Path(), + } + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) +} + +func (s *VaultDatasourcesSuite) TestUserPassAuth(c *C) { + s.v.vc.Logical().Write("secret/foo", map[string]interface{}{"value": "bar"}) + defer s.v.vc.Logical().Delete("secret/foo") + err := s.v.vc.Sys().EnableAuth("userpass", "userpass", "") + handle(c, err) + err = s.v.vc.Sys().EnableAuth("userpass2", "userpass", "") + handle(c, err) + defer s.v.vc.Sys().DisableAuth("userpass") + defer s.v.vc.Sys().DisableAuth("userpass2") + _, err = s.v.vc.Logical().Write("auth/userpass/users/dave", map[string]interface{}{ + "password": "foo", "ttl": "10s", "policies": "readpol"}) + handle(c, err) + _, err = s.v.vc.Logical().Write("auth/userpass2/users/dave", map[string]interface{}{ + "password": "bar", "ttl": "10s", "policies": "readpol"}) + handle(c, err) + + result := icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "vault=vault:///secret", + "-i", `{{(ds "vault" "foo").value}}`, + ), func(c *icmd.Cmd) { + c.Env = []string{ + "VAULT_ADDR=http://" + s.v.addr, + "VAULT_AUTH_USERNAME=dave", "VAULT_AUTH_PASSWORD=foo", + } + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) + + userFile := fs.NewFile(c, "test-vault-user", fs.WithContent("dave")) + passFile := fs.NewFile(c, "test-vault-pass", fs.WithContent("foo")) + defer userFile.Remove() + defer passFile.Remove() + result = icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "vault=vault:///secret", + "-i", `{{(ds "vault" "foo").value}}`, + ), func(c *icmd.Cmd) { + c.Env = []string{ + "VAULT_ADDR=http://" + s.v.addr, + "VAULT_AUTH_USERNAME_FILE=" + userFile.Path(), + "VAULT_AUTH_PASSWORD_FILE=" + passFile.Path(), + } + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) + + result = icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "vault=vault:///secret", + "-i", `{{(ds "vault" "foo").value}}`, + ), func(c *icmd.Cmd) { + c.Env = []string{ + "VAULT_ADDR=http://" + s.v.addr, + "VAULT_AUTH_USERNAME=dave", "VAULT_AUTH_PASSWORD=bar", + "VAULT_AUTH_USERPASS_MOUNT=userpass2", + } + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) +} + +func (s *VaultDatasourcesSuite) TestAppRoleAuth(c *C) { + s.v.vc.Logical().Write("secret/foo", map[string]interface{}{"value": "bar"}) + defer s.v.vc.Logical().Delete("secret/foo") + err := s.v.vc.Sys().EnableAuth("approle", "approle", "") + handle(c, err) + err = s.v.vc.Sys().EnableAuth("approle2", "approle", "") + handle(c, err) + defer s.v.vc.Sys().DisableAuth("approle") + defer s.v.vc.Sys().DisableAuth("approle2") + _, err = s.v.vc.Logical().Write("auth/approle/role/testrole", map[string]interface{}{ + "secret_id_ttl": "10s", "token_ttl": "20s", + "secret_id_num_uses": "1", "policies": "readpol", + }) + handle(c, err) + _, err = s.v.vc.Logical().Write("auth/approle2/role/testrole", map[string]interface{}{ + "secret_id_ttl": "10s", "token_ttl": "20s", + "secret_id_num_uses": "1", "policies": "readpol", + }) + handle(c, err) + + rid, _ := s.v.vc.Logical().Read("auth/approle/role/testrole/role-id") + roleID := rid.Data["role_id"].(string) + sid, _ := s.v.vc.Logical().Write("auth/approle/role/testrole/secret-id", nil) + secretID := sid.Data["secret_id"].(string) + result := icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "vault=vault:///secret", + "-i", `{{(ds "vault" "foo").value}}`, + ), func(c *icmd.Cmd) { + c.Env = []string{ + "VAULT_ADDR=http://" + s.v.addr, + "VAULT_ROLE_ID=" + roleID, + "VAULT_SECRET_ID=" + secretID, + } + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) + + rid, _ = s.v.vc.Logical().Read("auth/approle2/role/testrole/role-id") + roleID = rid.Data["role_id"].(string) + sid, _ = s.v.vc.Logical().Write("auth/approle2/role/testrole/secret-id", nil) + secretID = sid.Data["secret_id"].(string) + result = icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "vault=vault:///secret", + "-i", `{{(ds "vault" "foo").value}}`, + ), func(c *icmd.Cmd) { + c.Env = []string{ + "VAULT_ADDR=http://" + s.v.addr, + "VAULT_ROLE_ID=" + roleID, + "VAULT_SECRET_ID=" + secretID, + "VAULT_AUTH_APPROLE_MOUNT=approle2", + } + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) +} + +func (s *VaultDatasourcesSuite) TestAppIDAuth(c *C) { + s.v.vc.Logical().Write("secret/foo", map[string]interface{}{"value": "bar"}) + defer s.v.vc.Logical().Delete("secret/foo") + err := s.v.vc.Sys().EnableAuth("app-id", "app-id", "") + handle(c, err) + err = s.v.vc.Sys().EnableAuth("app-id2", "app-id", "") + handle(c, err) + defer s.v.vc.Sys().DisableAuth("app-id") + defer s.v.vc.Sys().DisableAuth("app-id2") + _, err = s.v.vc.Logical().Write("auth/app-id/map/app-id/testappid", map[string]interface{}{ + "display_name": "test_app_id", "value": "readpol", + }) + handle(c, err) + _, err = s.v.vc.Logical().Write("auth/app-id/map/user-id/testuserid", map[string]interface{}{ + "value": "testappid", + }) + handle(c, err) + _, err = s.v.vc.Logical().Write("auth/app-id2/map/app-id/testappid", map[string]interface{}{ + "display_name": "test_app_id", "value": "readpol", + }) + handle(c, err) + _, err = s.v.vc.Logical().Write("auth/app-id2/map/user-id/testuserid", map[string]interface{}{ + "value": "testappid", + }) + handle(c, err) + + result := icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "vault=vault:///secret", + "-i", `{{(ds "vault" "foo").value}}`, + ), func(c *icmd.Cmd) { + c.Env = []string{ + "VAULT_ADDR=http://" + s.v.addr, + "VAULT_APP_ID=testappid", + "VAULT_USER_ID=testuserid", + } + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) + + result = icmd.RunCmd(icmd.Command(GomplateBin, + "-d", "vault=vault:///secret", + "-i", `{{(ds "vault" "foo").value}}`, + ), func(c *icmd.Cmd) { + c.Env = []string{ + "VAULT_ADDR=http://" + s.v.addr, + "VAULT_APP_ID=testappid", + "VAULT_USER_ID=testuserid", + "VAULT_AUTH_APP_ID_MOUNT=app-id2", + } + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "bar"}) +} + +func (s *VaultDatasourcesSuite) TestDynamicAuth(c *C) { + err := s.v.vc.Sys().Mount("ssh/", &vaultapi.MountInput{Type: "ssh"}) + handle(c, err) + defer s.v.vc.Sys().Unmount("ssh") + + _, err = s.v.vc.Logical().Write("ssh/roles/test", map[string]interface{}{ + "key_type": "otp", "default_user": "user", "cidr_list": "10.0.0.0/8", + }) + handle(c, err) + testCommands := []icmd.Cmd{ + icmd.Command(GomplateBin, + "-d", "vault=vault:///", + "-i", `{{(ds "vault" "ssh/creds/test?ip=10.1.2.3&username=user").ip}}`, + ), + icmd.Command(GomplateBin, + "-d", "vault=vault:///ssh/creds/test", + "-i", `{{(ds "vault" "?ip=10.1.2.3&username=user").ip}}`, + ), + icmd.Command(GomplateBin, + "-d", "vault=vault:///ssh/creds/test?ip=10.1.2.3&username=user", + "-i", `{{(ds "vault").ip}}`, + ), + icmd.Command(GomplateBin, + "-d", "vault=vault:///?ip=10.1.2.3&username=user", + "-i", `{{(ds "vault" "ssh/creds/test").ip}}`, + ), + } + tok, err := s.v.tokenCreate("writepol", len(testCommands)*2) + handle(c, err) + + for _, v := range testCommands { + result := icmd.RunCmd(v, func(c *icmd.Cmd) { + c.Env = []string{ + "VAULT_ADDR=http://" + s.v.addr, + "VAULT_TOKEN=" + tok, + } + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "10.1.2.3"}) + } +} diff --git a/test/integration/envvars.bats b/test/integration/envvars.bats deleted file mode 100644 index aef1d3a8..00000000 --- a/test/integration/envvars.bats +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bats - -load helper - -# function setup () { -# } - -# function teardown () { -# # rm -rf $tmpdir -# } - -@test "errors with non-existant env var using .Env" { - gomplate -i '{{.Env.FOO}}' - [ "$status" -eq 1 ] - [[ "${lines[0]}" == *"map has no entry for key"* ]] -} - -@test "empty string with non-existant env var using getenv" { - gomplate -i '{{getenv "FOO" }}' - [ "$status" -eq 0 ] - [[ "${output}" == "" ]] -} - -@test "default string with non-existant env var using getenv" { - gomplate -i '{{getenv "FOO" "foo"}}' - [ "$status" -eq 0 ] - [[ "${output}" == "foo" ]] -} - -@test "default string with empty env var using getenv" { - FOO="" gomplate -i '{{getenv "FOO" "foo"}}' - [ "$status" -eq 0 ] - [[ "${output}" == "foo" ]] -} - -@test "existant env var using .Env" { - gomplate -i '{{.Env.HOME}}' - [ "$status" -eq 0 ] - [[ "${output}" == "${HOME}" ]] -} - -@test "existant env var using getenv" { - gomplate -i '{{getenv "HOME"}}' - [ "$status" -eq 0 ] - [[ "${output}" == "${HOME}" ]] -} - -@test "existant env var using env.Getenv" { - gomplate -i '{{env.Getenv "HOME"}}' - [ "$status" -eq 0 ] - [[ "${output}" == "${HOME}" ]] -} diff --git a/test/integration/envvars_test.go b/test/integration/envvars_test.go new file mode 100644 index 00000000..aa8ccc41 --- /dev/null +++ b/test/integration/envvars_test.go @@ -0,0 +1,52 @@ +//+build integration +//+build !windows + +package integration + +import ( + . "gopkg.in/check.v1" + + "github.com/gotestyourself/gotestyourself/icmd" +) + +type EnvvarsSuite struct{} + +var _ = Suite(&EnvvarsSuite{}) + +func (s *EnvvarsSuite) TestNonExistantEnvVar(c *C) { + result := icmd.RunCommand(GomplateBin, "-i", + `{{ .Env.FOO }}`) + result.Assert(c, icmd.Expected{ExitCode: 1, Err: "map has no entry for key"}) + + result = icmd.RunCommand(GomplateBin, "-i", + `{{ getenv "FOO" }}`) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: ""}) + + result = icmd.RunCommand(GomplateBin, "-i", + `{{ getenv "FOO" "foo" }}`) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "foo"}) + + result = icmd.RunCmd(icmd.Command(GomplateBin, "-i", `{{ getenv "FOO" "foo" }}`), + func(c *icmd.Cmd) { + c.Env = []string{"FOO="} + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "foo"}) +} + +func (s *EnvvarsSuite) TestExistantEnvVar(c *C) { + setFoo := func(c *icmd.Cmd) { + c.Env = []string{"FOO=foo"} + } + expected := icmd.Expected{ExitCode: 0, Out: "foo"} + result := icmd.RunCmd(icmd.Command(GomplateBin, "-i", + `{{ .Env.FOO }}`), setFoo) + result.Assert(c, expected) + + result = icmd.RunCmd(icmd.Command(GomplateBin, "-i", + `{{ getenv "FOO" }}`), setFoo) + result.Assert(c, expected) + + result = icmd.RunCmd(icmd.Command(GomplateBin, "-i", + `{{ env.Getenv "FOO" }}`), setFoo) + result.Assert(c, expected) +} diff --git a/test/integration/helper.bash b/test/integration/helper.bash deleted file mode 100644 index 5ae28e16..00000000 --- a/test/integration/helper.bash +++ /dev/null @@ -1,107 +0,0 @@ -#!/bin/bash - -helper_dir=${BASH_SOURCE%/*} - -function gomplate () { - run bin/gomplate "$@" - - # Some debug information to make life easier. bats will only print it if the - # test failed, in which case the output is useful. - echo "gomplate $@ (status=$status):" >&2 - echo "$output" >&2 -} - -function gomplate_stdin () { - run __gomplate_stdin "$@" - - # Some debug information to make life easier. bats will only print it if the - # test failed, in which case the output is useful. - in=$1 - shift - echo "echo \"$in\" | gomplate $@ (status=$status):" >&2 - echo "$output" >&2 -} - -function __gomplate_stdin () { - in=$1 - shift 1 - echo "$in" | bin/gomplate "$@" -} - -function start_mirror_svc () { - ${helper_dir}/mirror & - wait_for_url http://127.0.0.1:8080/ -} - -function stop_mirror_svc () { - wget -q -O - http://127.0.0.1:8080/quit -} - -function start_meta_svc () { - ${helper_dir}/meta &> /tmp/meta.log & - wait_for_url http://127.0.0.1:8081/ -} - -function stop_meta_svc () { - wget -q -O - http://127.0.0.1:8081/quit -} - -function start_aws_svc () { - ${helper_dir}/aws & - wait_for_url http://127.0.0.1:8082/ -} - -function stop_aws_svc () { - wget -q -O - http://127.0.0.1:8082/quit -} - -function wait_for_url () { - url=$1 - for i in {0..10}; do - curl -o /dev/null -s -f $url && break || sleep 1 - done -} - -function start_consul () { - port=$1 - if [ -z $port ]; then - port=8500 - fi - PID_FILE=/tmp/gomplate-test-consul.pid - rm -f $PID_FILE || true - export CONSUL_ROOT_TOKEN=00000000-1111-2222-3333-444455556666 - echo "{\"acl_datacenter\": \"dc1\", \"acl_master_token\": \"${CONSUL_ROOT_TOKEN}\"}" >> /tmp/gomplate-test-consul.json - consul agent -dev -config-file=/tmp/gomplate-test-consul.json -log-level=err -http-port=$port -pid-file=$PID_FILE >/dev/null & - wait_for_url http://127.0.0.1:$port/v1/status/leader -} - -function stop_consul () { - PID_FILE=/tmp/gomplate-test-consul.pid - kill $(cat $PID_FILE) &>/dev/null - rm /tmp/gomplate-test-consul.json -} - -function start_vault () { - port=$1 - PID_FILE=/tmp/gomplate-test-vault.pid - export VAULT_ROOT_TOKEN=00000000-1111-2222-3333-444455556666 - - # back up any existing token so it doesn't get overridden - if [ -f ~/.vault-token ]; then - cp ~/.vault-token ~/.vault-token.bak - fi - - vault server -dev -dev-root-token-id=${VAULT_ROOT_TOKEN} -log-level=err >&/dev/null & - echo $! > $PID_FILE - wait_for_url http://127.0.0.1:$port/sys/health -} - -function stop_vault () { - PID_FILE=/tmp/gomplate-test-vault.pid - kill $(cat $PID_FILE) &>/dev/null - - # restore old token if it was backed up - if [ -f ~/.vault-token.bak ]; then - mv ~/.vault-token.bak ~/.vault-token - fi -} diff --git a/test/integration/input-dir.bats b/test/integration/input-dir.bats deleted file mode 100644 index fb222d26..00000000 --- a/test/integration/input-dir.bats +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env bats - -load helper - -tmpdir=$(mktemp -u) - -function setup () { - mkdir -p $tmpdir - mkdir -p $tmpdir/in/inner - echo -n "{{ (datasource \"config\").one }}" > $tmpdir/in/eins.txt - echo -n "{{ (datasource \"config\").two }}" > $tmpdir/in/inner/deux.txt - - cat <<"EOT" > $tmpdir/config.yml -one: eins -two: deux -EOT -} - -function teardown () { - # rm -rf $tmpdir - echo -} - -@test "takes --input-dir and produces proper output files" { - rm -rf $tmpdir/out || true - gomplate --input-dir $tmpdir/in --output-dir $tmpdir/out -d config=$tmpdir/config.yml - [ "$status" -eq 0 ] - [[ "$(ls $tmpdir/out | wc -l)" == 2 ]] - [[ "$(ls $tmpdir/out/inner | wc -l)" == 1 ]] - [[ "$(cat $tmpdir/out/eins.txt)" == "eins" ]] - [[ "$(cat $tmpdir/out/inner/deux.txt)" == "deux" ]] -} - -@test "test . as default --output-dir param" { - rm -rf $tmpdir/out_dot || true - mkdir -p $tmpdir/out_dot - g=$(pwd)/bin/gomplate - cd $tmpdir/out_dot - run $g --input-dir $tmpdir/in -d config=$tmpdir/config.yml - [ "$?" -eq 0 ] - [[ "$(ls | wc -l)" == 2 ]] - [[ "$(ls inner | wc -l)" == 1 ]] - [[ "$(cat eins.txt)" == "eins" ]] - [[ "$(cat inner/deux.txt)" == "deux" ]] -} - -@test "errors given --output-dir but no --input-dir" { - gomplate --output-dir "." - [ "$status" -eq 1 ] - [[ "${output}" == "Error: --input-dir must be set when --output-dir is set"* ]] -} - -@test "errors given both --input-dir and --in" { - gomplate --input-dir "." --in "param" - [ "$status" -eq 1 ] - [[ "${output}" == "Error: --input-dir can not be used together with --in or --file"* ]] -} - -@test "errors given both --input-dir and --file" { - gomplate --input-dir "." --file input.txt - [ "$status" -eq 1 ] - [[ "${output}" == "Error: --input-dir can not be used together with --in or --file"* ]] -} - -@test "errors given both --output-dir and --out" { - gomplate --input-dir "." --output-dir /tmp --out out - [ "$status" -eq 1 ] - [[ "${output}" == "Error: --output-dir can not be used together with --out"* ]] -} - -@test "errors with filename when using input dir and bad input file" { - rm -rf $tmpdir/out || true - echo -n "{{end}}" > $tmpdir/in/bad.tmpl - gomplate --input-dir $tmpdir/in --output-dir $tmpdir/out -d config=$tmpdir/config.yml - [ "$status" -eq 1 ] - [[ "${output}" == "Error: template: $tmpdir/in/bad.tmpl:1: unexpected {{end}}"* ]] -}
\ No newline at end of file diff --git a/test/integration/inputdir_test.go b/test/integration/inputdir_test.go new file mode 100644 index 00000000..6c0bbf88 --- /dev/null +++ b/test/integration/inputdir_test.go @@ -0,0 +1,104 @@ +//+build integration +//+build !windows + +package integration + +import ( + "io/ioutil" + + . "gopkg.in/check.v1" + + "github.com/gotestyourself/gotestyourself/assert" + "github.com/gotestyourself/gotestyourself/fs" + "github.com/gotestyourself/gotestyourself/icmd" + tassert "github.com/stretchr/testify/assert" +) + +type InputDirSuite struct { + tmpDir *fs.Dir +} + +var _ = Suite(&InputDirSuite{}) + +func (s *InputDirSuite) SetUpTest(c *C) { + s.tmpDir = fs.NewDir(c, "gomplate-inttests", + fs.WithFile("config.yml", "one: eins\ntwo: deux\n"), + fs.WithDir("in", + fs.WithFile("eins.txt", `{{ (ds "config").one }}`), + fs.WithDir("inner", + fs.WithFile("deux.txt", `{{ (ds "config").two }}`), + ), + ), + fs.WithDir("out"), + fs.WithDir("bad_in", + fs.WithFile("bad.tmpl", "{{end}}"), + ), + ) +} + +func (s *InputDirSuite) TearDownTest(c *C) { + s.tmpDir.Remove() +} + +func (s *InputDirSuite) TestInputDir(c *C) { + result := icmd.RunCommand(GomplateBin, + "--input-dir", s.tmpDir.Join("in"), + "--output-dir", s.tmpDir.Join("out"), + "-d", "config="+s.tmpDir.Join("config.yml"), + ) + result.Assert(c, icmd.Success) + + files, err := ioutil.ReadDir(s.tmpDir.Join("out")) + assert.NilError(c, err) + tassert.Len(c, files, 2) + + files, err = ioutil.ReadDir(s.tmpDir.Join("out", "inner")) + assert.NilError(c, err) + tassert.Len(c, files, 1) + + content, err := ioutil.ReadFile(s.tmpDir.Join("out", "eins.txt")) + assert.NilError(c, err) + assert.Equal(c, "eins", string(content)) + + content, err = ioutil.ReadFile(s.tmpDir.Join("out", "inner", "deux.txt")) + assert.NilError(c, err) + assert.Equal(c, "deux", string(content)) +} + +func (s *InputDirSuite) TestDefaultOutputDir(c *C) { + result := icmd.RunCmd(icmd.Command(GomplateBin, + "--input-dir", s.tmpDir.Join("in"), + "-d", "config="+s.tmpDir.Join("config.yml"), + ), func(c *icmd.Cmd) { + c.Dir = s.tmpDir.Join("out") + }) + result.Assert(c, icmd.Success) + + files, err := ioutil.ReadDir(s.tmpDir.Join("out")) + assert.NilError(c, err) + tassert.Len(c, files, 2) + + files, err = ioutil.ReadDir(s.tmpDir.Join("out", "inner")) + assert.NilError(c, err) + tassert.Len(c, files, 1) + + content, err := ioutil.ReadFile(s.tmpDir.Join("out", "eins.txt")) + assert.NilError(c, err) + assert.Equal(c, "eins", string(content)) + + content, err = ioutil.ReadFile(s.tmpDir.Join("out", "inner", "deux.txt")) + assert.NilError(c, err) + assert.Equal(c, "deux", string(content)) +} + +func (s *InputDirSuite) TestReportsFilenameWithBadInputFile(c *C) { + result := icmd.RunCommand(GomplateBin, + "--input-dir", s.tmpDir.Join("bad_in"), + "--output-dir", s.tmpDir.Join("out"), + "-d", "config="+s.tmpDir.Join("config.yml"), + ) + result.Assert(c, icmd.Expected{ + ExitCode: 1, + Out: "template: " + s.tmpDir.Join("bad_in", "bad.tmpl") + ":1: unexpected {{end}}", + }) +} diff --git a/test/integration/integration.go b/test/integration/integration.go new file mode 100644 index 00000000..76ab1b72 --- /dev/null +++ b/test/integration/integration.go @@ -0,0 +1 @@ +package integration diff --git a/test/integration/integration_test.go b/test/integration/integration_test.go new file mode 100644 index 00000000..e22945b0 --- /dev/null +++ b/test/integration/integration_test.go @@ -0,0 +1,122 @@ +package integration + +import ( + "encoding/json" + "go/build" + "io/ioutil" + "log" + "net" + "net/http" + "testing" + "time" + + "github.com/gotestyourself/gotestyourself/icmd" + vaultapi "github.com/hashicorp/vault/api" + . "gopkg.in/check.v1" +) + +var ( + GomplateBin = build.Default.GOPATH + "/src/github.com/hairyhenderson/gomplate/bin/gomplate" +) + +// Hook up gocheck into the "go test" runner. +func Test(t *testing.T) { TestingT(t) } + +// a convenience... +func inOutTest(c *C, i string, o string) { + result := icmd.RunCommand(GomplateBin, "-i", i) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: o}) +} + +func handle(c *C, err error) { + if err != nil { + c.Fatal(err) + } +} + +// mirrorHandler - reflects back the HTTP headers from the request +func mirrorHandler(w http.ResponseWriter, r *http.Request) { + type Req struct { + Headers http.Header `json:"headers"` + } + req := Req{r.Header} + b, err := json.Marshal(req) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusBadRequest) + } + w.Header().Set("Content-Type", "application/json") + w.Write(b) +} + +// freeport - find a free TCP port for immediate use. No guarantees! +func freeport() (port int, addr string) { + l, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("127.0.0.1")}) + defer l.Close() + if err != nil { + panic(err) + } + a := l.Addr().(*net.TCPAddr) + port = a.Port + return port, a.String() +} + +// waitForURL - waits up to 20s for a given URL to respond with a 200 +func waitForURL(c *C, url string) error { + client := http.DefaultClient + retries := 100 + for retries > 0 { + retries-- + time.Sleep(200 * time.Millisecond) + resp, err := client.Get(url) + if err != nil { + c.Logf("Got error, retries left: %d (error: %v)", retries, err) + continue + } + body, err := ioutil.ReadAll(resp.Body) + c.Logf("Body is: %s", body) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode == 200 { + return nil + } + } + return nil +} + +type vaultClient struct { + addr string + rootToken string + vc *vaultapi.Client +} + +func createVaultClient(addr string, rootToken string) (*vaultClient, error) { + config := vaultapi.DefaultConfig() + config.Address = "http://" + addr + client, err := vaultapi.NewClient(config) + if err != nil { + return nil, err + } + v := &vaultClient{ + addr: addr, + rootToken: rootToken, + vc: client, + } + client.SetToken(rootToken) + return v, nil +} + +func (v *vaultClient) tokenCreate(policy string, uses int) (string, error) { + opts := &vaultapi.TokenCreateRequest{ + Policies: []string{policy}, + TTL: "1m", + NumUses: uses, + } + token, err := v.vc.Auth().Token().Create(opts) + if err != nil { + return "", err + } + return token.Auth.ClientToken, nil +} diff --git a/test/integration/math.bats b/test/integration/math.bats deleted file mode 100644 index 09168f3a..00000000 --- a/test/integration/math.bats +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bats - -load helper - -@test "'math.Add'" { - gomplate -i '{{ math.Add 1 2 3 4 }} {{ add -5 5 }}' - [ "$status" -eq 0 ] - [[ "${output}" == "10 0" ]] -} - -@test "'math.Sub'" { - gomplate -i '{{ math.Sub 10 5 }} {{ sub -5 5 }}' - [ "$status" -eq 0 ] - [[ "${output}" == "5 -10" ]] -} - -@test "'math.Mul'" { - gomplate -i '{{ math.Mul 1 2 3 4 }} {{ mul -5 5 }}' - [ "$status" -eq 0 ] - [[ "${output}" == "24 -25" ]] -} - -@test "'math.Div'" { - gomplate -i '{{ math.Div 5 3 }} {{ div -5 5 }}' - [ "$status" -eq 0 ] - [[ "${output}" == "1 -1" ]] -} - -@test "'math.Rem'" { - gomplate -i '{{ math.Rem 5 3 }} {{ rem 2 2 }}' - [ "$status" -eq 0 ] - [[ "${output}" == "2 0" ]] -} - -@test "'math.Pow'" { - gomplate -i '{{ math.Pow 8 4 }} {{ pow 2 2 }}' - [ "$status" -eq 0 ] - [[ "${output}" == "4096 4" ]] -} - -@test "'math.Seq'" { - gomplate -i '{{ math.Seq 0 }}, {{ seq 0 3 }}, {{ seq -5 -10 2 }}' - [ "$status" -eq 0 ] - [[ "${output}" == "[1 0], [0 1 2 3], [-5 -7 -9]" ]] -}
\ No newline at end of file diff --git a/test/integration/math_test.go b/test/integration/math_test.go new file mode 100644 index 00000000..cfdeaab0 --- /dev/null +++ b/test/integration/math_test.go @@ -0,0 +1,22 @@ +//+build integration +//+build !windows + +package integration + +import ( + . "gopkg.in/check.v1" +) + +type MathSuite struct{} + +var _ = Suite(&MathSuite{}) + +func (s *MathSuite) TestMath(c *C) { + inOutTest(c, `{{ math.Add 1 2 3 4 }} {{ add -5 5 }}`, "10 0") + inOutTest(c, `{{ math.Sub 10 5 }} {{ sub -5 5 }}`, "5 -10") + inOutTest(c, `{{ math.Mul 1 2 3 4 }} {{ mul -5 5 }}`, "24 -25") + inOutTest(c, `{{ math.Div 5 3 }} {{ div -5 5 }}`, "1 -1") + inOutTest(c, `{{ math.Rem 5 3 }} {{ rem 2 2 }}`, "2 0") + inOutTest(c, `{{ math.Pow 8 4 }} {{ pow 2 2 }}`, "4096 4") + inOutTest(c, `{{ math.Seq 0 }}, {{ seq 0 3 }}, {{ seq -5 -10 2 }}`, "[1 0], [0 1 2 3], [-5 -7 -9]") +} diff --git a/test/integration/metasvc/main.go b/test/integration/metasvc/main.go deleted file mode 100644 index 2350736d..00000000 --- a/test/integration/metasvc/main.go +++ /dev/null @@ -1,147 +0,0 @@ -package main - -import ( - "bytes" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "flag" - "log" - "math/big" - "net" - "net/http" - "time" - - "github.com/fullsailor/pkcs7" -) - -var port int -var priv *rsa.PrivateKey -var derBytes []byte - -const instanceDocument = `{ - "devpayProductCodes" : null, - "availabilityZone" : "xx-test-1b", - "privateIp" : "10.1.2.3", - "version" : "2010-08-31", - "instanceId" : "i-00000000000000000", - "billingProducts" : null, - "instanceType" : "t2.micro", - "accountId" : "1", - "imageId" : "ami-00000000", - "pendingTime" : "2000-00-01T0:00:00Z", - "architecture" : "x86_64", - "kernelId" : null, - "ramdiskId" : null, - "region" : "xx-test-1" -}` - -func main() { - flag.IntVar(&port, "p", 8081, "Port to listen to") - flag.Parse() - - l, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: port}) - if err != nil { - log.Fatal(err) - } - // defer l.Close() - http.HandleFunc("/", rootHandler) - - http.HandleFunc("/latest/dynamic/instance-identity/pkcs7", pkcsHandler) - http.HandleFunc("/latest/dynamic/instance-identity/document", documentHandler) - http.HandleFunc("/certificate", certificateHandler) - - http.HandleFunc("/quit", quitHandler(l)) - - http.Serve(l, nil) -} - -func certificateGenerate() { - var err error - priv, err = rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - log.Fatalf("failed to generate private key: %s", err) - } - - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - log.Fatalf("failed to generate serial number: %s", err) - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - Organization: []string{"Test"}, - }, - } - - derBytes, err = x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) - if err != nil { - log.Fatalf("Failed to create certificate: %s", err) - } -} - -func rootHandler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/plain") - w.Write([]byte("")) -} - -func pkcsHandler(w http.ResponseWriter, r *http.Request) { - cert, err := x509.ParseCertificate(derBytes) - if err != nil { - log.Fatalf("Cannot decode certificate: %s", err) - } - - // Initialize a SignedData struct with content to be signed - signedData, err := pkcs7.NewSignedData([]byte(instanceDocument)) - if err != nil { - log.Fatalf("Cannot initialize signed data: %s", err) - } - - // Add the signing cert and private key - if err := signedData.AddSigner(cert, priv, pkcs7.SignerInfoConfig{}); err != nil { - log.Fatalf("Cannot add signer: %s", err) - } - - // Finish() to obtain the signature bytes - detachedSignature, err := signedData.Finish() - if err != nil { - log.Fatalf("Cannot finish signing data: %s", err) - } - - encoded := pem.EncodeToMemory(&pem.Block{Type: "PKCS7", Bytes: detachedSignature}) - - encoded = bytes.TrimPrefix(encoded, []byte("-----BEGIN PKCS7-----\n")) - encoded = bytes.TrimSuffix(encoded, []byte("\n-----END PKCS7-----\n")) - - w.Header().Set("Content-Type", "text/plain") - w.Write(encoded) -} - -func documentHandler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte(instanceDocument)) -} - -func certificateHandler(w http.ResponseWriter, r *http.Request) { - if derBytes == nil { - certificateGenerate() - } - encoded := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) - - w.Header().Set("Content-Type", "text/plain") - w.Write(encoded) -} - -func quitHandler(l net.Listener) func(http.ResponseWriter, *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNoContent) - go func() { - time.Sleep(500 * time.Millisecond) - l.Close() - }() - } -} diff --git a/test/integration/mirrorsvc/main.go b/test/integration/mirrorsvc/main.go deleted file mode 100644 index f05113a5..00000000 --- a/test/integration/mirrorsvc/main.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "encoding/json" - "flag" - "log" - "net" - "net/http" - "time" -) - -// Req - -type Req struct { - Headers http.Header `json:"headers"` -} - -var port int - -func main() { - flag.IntVar(&port, "p", 8080, "Port to listen to") - flag.Parse() - - l, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: port}) - if err != nil { - log.Fatal(err) - } - // defer l.Close() - http.HandleFunc("/", rootHandler) - - http.HandleFunc("/quit", quitHandler(l)) - - http.Serve(l, nil) -} - -func rootHandler(w http.ResponseWriter, r *http.Request) { - req := Req{r.Header} - b, err := json.Marshal(req) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) - } - w.Header().Set("Content-Type", "application/json") - w.Write(b) -} - -func quitHandler(l net.Listener) func(http.ResponseWriter, *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNoContent) - go func() { - time.Sleep(500 * time.Millisecond) - l.Close() - }() - } -} diff --git a/test/integration/net.bats b/test/integration/net.bats deleted file mode 100644 index d63eaf1b..00000000 --- a/test/integration/net.bats +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bats - -load helper - -tmpdir=$(mktemp -u) - -function setup () { - mkdir -p $tmpdir -} - -function teardown () { - rm -rf $tmpdir || true -} - -@test "'net.LookupIP'" { - gomplate -i '{{ net.LookupIP "localhost" }}' - [ "$status" -eq 0 ] - [[ "${output}" == "127.0.0.1" ]] -} diff --git a/test/integration/net_test.go b/test/integration/net_test.go new file mode 100644 index 00000000..dc9f1f76 --- /dev/null +++ b/test/integration/net_test.go @@ -0,0 +1,19 @@ +//+build integration +//+build !windows + +package integration + +import ( + . "gopkg.in/check.v1" + + "github.com/gotestyourself/gotestyourself/icmd" +) + +type NetSuite struct{} + +var _ = Suite(&NetSuite{}) + +func (s *NetSuite) TestLookupIP(c *C) { + result := icmd.RunCommand(GomplateBin, "-i", `{{ net.LookupIP "localhost" }}`) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "127.0.0.1"}) +} diff --git a/test/integration/regexp.bats b/test/integration/regexp.bats deleted file mode 100644 index 17b5a743..00000000 --- a/test/integration/regexp.bats +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bats - -load helper - -tmpdir=$(mktemp -u) - -function setup () { - mkdir -p $tmpdir -} - -function teardown () { - rm -rf $tmpdir || true -} - -@test "'regexp.Replace'" { - gomplate -i '{{ "1.2.3-59" | regexp.Replace `-([0-9]*)` `.$1` }}' - [ "$status" -eq 0 ] - [[ "${output}" == "1.2.3.59" ]] -} diff --git a/test/integration/regexp_test.go b/test/integration/regexp_test.go new file mode 100644 index 00000000..c476877f --- /dev/null +++ b/test/integration/regexp_test.go @@ -0,0 +1,20 @@ +//+build integration +//+build !windows + +package integration + +import ( + . "gopkg.in/check.v1" + + "github.com/gotestyourself/gotestyourself/icmd" +) + +type RegexpSuite struct{} + +var _ = Suite(&RegexpSuite{}) + +func (s *RegexpSuite) TestReplace(c *C) { + result := icmd.RunCommand(GomplateBin, "-i", + `{{ "1.2.3-59" | regexp.Replace "-([0-9]*)" ".$1" }}`) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "1.2.3.59"}) +} diff --git a/test/integration/strings.bats b/test/integration/strings.bats deleted file mode 100644 index 9d16eb25..00000000 --- a/test/integration/strings.bats +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bats - -load helper - -tmpdir=$(mktemp -u) - -function setup () { - mkdir -p $tmpdir -} - -function teardown () { - rm -rf $tmpdir || true -} - -@test "'strings.Indent'" { - gomplate -i '{{ strings.Indent " " "hello world" }} -{{ "hello\nmultiline\nworld" | indent 2 "-" }} -{{ "foo\nbar" | strings.Indent 2 }} - {{"hello\nworld" | strings.Indent 5 | strings.TrimSpace }} -' - [ "$status" -eq 0 ] - [[ "${output}" == " hello world ---hello ---multiline ---world - foo - bar - hello - world" ]] -} diff --git a/test/integration/strings_test.go b/test/integration/strings_test.go new file mode 100644 index 00000000..cd570e16 --- /dev/null +++ b/test/integration/strings_test.go @@ -0,0 +1,31 @@ +//+build integration +//+build !windows + +package integration + +import ( + . "gopkg.in/check.v1" + + "github.com/gotestyourself/gotestyourself/icmd" +) + +type StringsSuite struct{} + +var _ = Suite(&StringsSuite{}) + +func (s *StringsSuite) TestIndent(c *C) { + result := icmd.RunCommand(GomplateBin, "-i", + `{{ strings.Indent " " "hello world" }} +{{ "hello\nmultiline\nworld" | indent 2 "-" }} +{{ "foo\nbar" | strings.Indent 2 }} + {{"hello\nworld" | strings.Indent 5 | strings.TrimSpace }} +`) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: ` hello world +--hello +--multiline +--world + foo + bar + hello + world`}) +} diff --git a/test/integration/test.sh b/test/integration/test.sh deleted file mode 100755 index 18d7bc48..00000000 --- a/test/integration/test.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -set -euo pipefail - -source $(dirname $0)/helper.bash - -function finish { - stop_vault -} -trap finish EXIT - -export VAULT_ADDR=http://127.0.0.1:8200 -export VAULT_ROOT_TOKEN=00000000-1111-2222-3333-444455556666 - -start_vault 8200 - -bats $(dirname $0) diff --git a/test/integration/awssvc/main.go b/test/integration/test_ec2_utils.go index a377cd99..2435aeb9 100644 --- a/test/integration/awssvc/main.go +++ b/test/integration/test_ec2_utils.go @@ -1,48 +1,100 @@ -package main +package integration import ( - "encoding/json" - "flag" + "bytes" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" "log" - "net" + "math/big" "net/http" - "time" + + "github.com/fullsailor/pkcs7" ) -// Req - -type Req struct { - Headers http.Header `json:"headers"` -} +const instanceDocument = `{ + "devpayProductCodes" : null, + "availabilityZone" : "xx-test-1b", + "privateIp" : "10.1.2.3", + "version" : "2010-08-31", + "instanceId" : "i-00000000000000000", + "billingProducts" : null, + "instanceType" : "t2.micro", + "accountId" : "1", + "imageId" : "ami-00000000", + "pendingTime" : "2000-00-01T0:00:00Z", + "architecture" : "x86_64", + "kernelId" : null, + "ramdiskId" : null, + "region" : "xx-test-1" +}` -var port int +func instanceDocumentHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(instanceDocument)) +} -func main() { - flag.IntVar(&port, "p", 8082, "Port to listen to") - flag.Parse() +func certificateGenerate() (priv *rsa.PrivateKey, derBytes []byte, err error) { + priv, err = rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + log.Fatalf("failed to generate private key: %s", err) + } - l, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: port}) + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { - log.Fatal(err) + log.Fatalf("failed to generate serial number: %s", err) } - // defer l.Close() - http.HandleFunc("/", rootHandler) - http.HandleFunc("/sts/", stsHandler) - http.HandleFunc("/ec2/", ec2Handler) - http.HandleFunc("/quit", quitHandler(l)) + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Test"}, + }, + } + + derBytes, err = x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + log.Fatalf("Failed to create certificate: %s", err) + } - http.Serve(l, nil) + return priv, derBytes, err } -func rootHandler(w http.ResponseWriter, r *http.Request) { - req := Req{r.Header} - b, err := json.Marshal(req) - if err != nil { - log.Println(err) - w.WriteHeader(http.StatusBadRequest) +func pkcsHandler(priv *rsa.PrivateKey, derBytes []byte) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + cert, err := x509.ParseCertificate(derBytes) + if err != nil { + log.Fatalf("Cannot decode certificate: %s", err) + } + + // Initialize a SignedData struct with content to be signed + signedData, err := pkcs7.NewSignedData([]byte(instanceDocument)) + if err != nil { + log.Fatalf("Cannot initialize signed data: %s", err) + } + + // Add the signing cert and private key + if err = signedData.AddSigner(cert, priv, pkcs7.SignerInfoConfig{}); err != nil { + log.Fatalf("Cannot add signer: %s", err) + } + + // Finish() to obtain the signature bytes + detachedSignature, err := signedData.Finish() + if err != nil { + log.Fatalf("Cannot finish signing data: %s", err) + } + + encoded := pem.EncodeToMemory(&pem.Block{Type: "PKCS7", Bytes: detachedSignature}) + + encoded = bytes.TrimPrefix(encoded, []byte("-----BEGIN PKCS7-----\n")) + encoded = bytes.TrimSuffix(encoded, []byte("\n-----END PKCS7-----\n")) + + w.Header().Set("Content-Type", "text/plain") + w.Write(encoded) } - w.Header().Set("Content-Type", "application/json") - w.Write(b) } func stsHandler(w http.ResponseWriter, r *http.Request) { @@ -182,13 +234,3 @@ func ec2Handler(w http.ResponseWriter, r *http.Request) { </reservationSet> </DescribeInstancesResponse>`)) } - -func quitHandler(l net.Listener) func(http.ResponseWriter, *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNoContent) - go func() { - time.Sleep(500 * time.Millisecond) - l.Close() - }() - } -} diff --git a/test/integration/time.bats b/test/integration/time.bats deleted file mode 100644 index 83441f60..00000000 --- a/test/integration/time.bats +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bats - -load helper - -@test "'time.ZoneName'" { - gomplate -i '{{ time.ZoneName }}' - [ "$status" -eq 0 ] - [[ "${output}" == `date +"%Z"` ]] -} - -@test "'time.ZoneOffset'" { - TZ=UTC gomplate -i '{{ time.ZoneOffset }}' - [ "$status" -eq 0 ] - [[ "${output}" == "0" ]] -} - -@test "'(time.Now).Format'" { - gomplate -i '{{ (time.Now).Format "2006-01-02 15 -0700" }}' - [ "$status" -eq 0 ] - [[ "${output}" == `date +"%Y-%m-%d %H %z"` ]] -} - -@test "'(time.Parse).Format'" { - in=`date -u --date='@1234567890'` - gomplate -i "{{ (time.Parse \"Mon Jan 02 15:04:05 MST 2006\" \"${in}\").Format \"2006-01-02 15 -0700\" }}" - [ "$status" -eq 0 ] - [[ "${output}" == "2009-02-13 23 +0000" ]] -} - -@test "'(time.ParseLocal).Format'" { - TZ=Africa/Luanda gomplate -i "{{ (time.ParseLocal time.Kitchen \"6:00AM\").Format \"15:04 MST\" }}" - [ "$status" -eq 0 ] - [[ "${output}" == "06:00 LMT" ]] -} - -@test "'(time.ParseInLocation).Format'" { - gomplate -i "{{ (time.ParseInLocation time.Kitchen \"Africa/Luanda\" \"6:00AM\").Format \"15:04 MST\" }}" - [ "$status" -eq 0 ] - [[ "${output}" == "06:00 LMT" ]] -} - -@test "'(time.Unix).UTC.Format' int" { - gomplate -i '{{ (time.Unix 1234567890).UTC.Format "2006-01-02 15 -0700" }}' - [ "$status" -eq 0 ] - [[ "${output}" == "2009-02-13 23 +0000" ]] -} - -@test "'(time.Unix).UTC.Format' string" { - gomplate -i '{{ (time.Unix "1234567890").UTC.Format "2006-01-02 15 -0700" }}' - [ "$status" -eq 0 ] - [[ "${output}" == "2009-02-13 23 +0000" ]] -} diff --git a/test/integration/time_test.go b/test/integration/time_test.go new file mode 100644 index 00000000..65ec5f8f --- /dev/null +++ b/test/integration/time_test.go @@ -0,0 +1,49 @@ +//+build integration +//+build !windows + +package integration + +import ( + "time" + + "github.com/gotestyourself/gotestyourself/icmd" + . "gopkg.in/check.v1" +) + +type TimeSuite struct{} + +var _ = Suite(&TimeSuite{}) + +func (s *TimeSuite) TestTime(c *C) { + f := `Mon Jan 02 15:04:05 MST 2006` + i := `Fri Feb 13 23:31:30 UTC 2009` + inOutTest(c, `{{ (time.Parse "`+f+`" "`+i+`").Format "2006-01-02 15 -0700" }}`, + "2009-02-13 23 +0000") + + result := icmd.RunCmd(icmd.Command(GomplateBin, "-i", + `{{ (time.ParseLocal time.Kitchen "6:00AM").Format "15:04 MST" }}`), func(cmd *icmd.Cmd) { + cmd.Env = []string{"TZ=Africa/Luanda"} + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "06:00 LMT"}) + + result = icmd.RunCmd(icmd.Command(GomplateBin, "-i", + `{{ time.ZoneOffset }}`), func(cmd *icmd.Cmd) { + cmd.Env = []string{"TZ=UTC"} + }) + result.Assert(c, icmd.Expected{ExitCode: 0, Out: "0"}) + + zname, _ := time.Now().Zone() + inOutTest(c, `{{ time.ZoneName }}`, zname) + + inOutTest(c, `{{ (time.Now).Format "2006-01-02 15 -0700" }}`, + time.Now().Format("2006-01-02 15 -0700")) + + inOutTest(c, `{{ (time.ParseInLocation time.Kitchen "Africa/Luanda" "6:00AM").Format "15:04 MST" }}`, + "06:00 LMT") + + inOutTest(c, `{{ (time.Unix 1234567890).UTC.Format "2006-01-02 15 -0700" }}`, + "2009-02-13 23 +0000") + + inOutTest(c, `{{ (time.Unix "1234567890").UTC.Format "2006-01-02 15 -0700" }}`, + "2009-02-13 23 +0000") +} diff --git a/test/integration/typeconv_funcs.bats b/test/integration/typeconv_funcs.bats deleted file mode 100644 index bb6f52bd..00000000 --- a/test/integration/typeconv_funcs.bats +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env bats - -load helper - -tmpdir=$(mktemp -u) - -function setup () { - mkdir -p $tmpdir -} - -function teardown () { - rm -rf $tmpdir || true -} - -@test "'has' can handle sub-maps in nested maps" { - gomplate -d config=$tmpdir/config.yml -i '{{ has ("foo:\n bar:\n baz: qux" | yaml).foo.bar "baz"}}' - [ "$status" -eq 0 ] - [[ "${output}" == "true" ]] -} - -@test "'toJSON' can handle nested maps" { - gomplate -i '{{ "foo:\n bar:\n baz: qux" | yaml | toJSON }}' - [ "$status" -eq 0 ] - [[ "${output}" == '{"foo":{"bar":{"baz":"qux"}}}' ]] -} - -@test "'toJSONPretty' can handle nested maps" { - gomplate -i '{{ `{"foo":{"bar":{"baz":"qux"}}}` | json | toJSONPretty " " }} -{{ toJSONPretty "" (`{"foo":{"bar":{"baz":"qux"}}}` | json) }}' - [ "$status" -eq 0 ] - [[ "${output}" == '{ - "foo": { - "bar": { - "baz": "qux" - } - } -} -{ -"foo": { -"bar": { -"baz": "qux" -} -} -}' ]] -} - -@test "join" { - gomplate -i '{{ $a := `[1, 2, 3]` | jsonArray }}{{ join $a "-" }}' - [ "$status" -eq 0 ] - [[ "${output}" == "1-2-3" ]] -} - -@test "'csv'" { - gomplate -i '{{ $c := `lang,keywords -C,32 -Go,25 -COBOL,357` | csv -}} -{{ index (index $c 0) 1 }}' - [ "$status" -eq 0 ] - [[ "${output}" == "keywords" ]] -} - -@test "'csvByRow' with default settings" { - gomplate -i '{{ $c := `lang,keywords -C,32 -Go,25 -COBOL,357` | csvByRow }}{{ range $c }}{{ .lang }} has {{ .keywords }} keywords. -{{end}}' - [ "$status" -eq 0 ] - [[ "${output}" == "C has 32 keywords. -Go has 25 keywords. -COBOL has 357 keywords." ]] -} - -@test "'csvByColumn' (tab-separated)" { - gomplate -i '{{ $c := `lang keywords -C 32 -Go 25 -COBOL 357` | csvByColumn "\t" -}} -Languages are: {{ join $c.lang " and " }}' - [ "$status" -eq 0 ] - [[ "${output}" == "Languages are: C and Go and COBOL" ]] -} - -@test "'toml'" { - gomplate -i '{{ $t := `# comment -foo = "bar" - -[baz] -qux = "quux"` | toml -}} -{{ $t.baz.qux }}' - [ "$status" -eq 0 ] - [[ "${output}" == "quux" ]] -} - -@test "'toTOML'" { - gomplate -i '{{ "foo:\n bar:\n baz: qux" | yaml | toTOML }}' - [ "$status" -eq 0 ] - [[ "${output}" == "[foo] - [foo.bar] - baz = \"qux\"" ]] -}
\ No newline at end of file diff --git a/test/integration/typeconv_test.go b/test/integration/typeconv_test.go new file mode 100644 index 00000000..fece8180 --- /dev/null +++ b/test/integration/typeconv_test.go @@ -0,0 +1,87 @@ +//+build integration +//+build !windows + +package integration + +import ( + . "gopkg.in/check.v1" +) + +type TypeconvSuite struct{} + +var _ = Suite(&TypeconvSuite{}) + +const ( + testYAML = "foo:\\n bar:\\n baz: qux" + testJSON = `{"foo":{"bar":{"baz":"qux"}}}` + testCsv = `lang,keywords +C,32 +Go,25 +COBOL,357` + testTsv = `lang keywords +C 32 +Go 25 +COBOL 357` +) + +func (s *TypeconvSuite) TestTypeconvFuncs(c *C) { + //@test "'has' can handle sub-maps in nested maps" { + inOutTest(c, `{{ has ("`+testYAML+`" | yaml).foo.bar "baz"}}`, + "true") +} +func (s *TypeconvSuite) TestJSON(c *C) { + inOutTest(c, `{{ "`+testYAML+`" | yaml | toJSON }}`, testJSON) + + inOutTest(c, `{{ `+"`"+testJSON+"`"+` | json | toJSONPretty " " }} +{{ toJSONPretty "" (`+"`"+testJSON+"`"+` | json) }}`, + `{ + "foo": { + "bar": { + "baz": "qux" + } + } +} +{ +"foo": { +"bar": { +"baz": "qux" +} +} +}`) +} + +func (s *TypeconvSuite) TestJoin(c *C) { + inOutTest(c, `{{ $a := "[1, 2, 3]" | jsonArray }}{{ join $a "-" }}`, + "1-2-3") +} + +func (s *TypeconvSuite) TestCSV(c *C) { + inOutTest(c, `{{ $c := `+"`"+testCsv+"`"+` | csv -}} +{{ index (index $c 0) 1 }}`, + "keywords") + + inOutTest(c, `{{ $c := `+"`"+testCsv+"`"+` | csvByRow -}} +{{ range $c }}{{ .lang }} has {{ .keywords }} keywords. +{{end}}`, + `C has 32 keywords. +Go has 25 keywords. +COBOL has 357 keywords.`) + + inOutTest(c, `{{ $c := `+"`"+testTsv+"`"+` | csvByColumn "\t" -}} +Languages are: {{ join $c.lang " and " }}`, + "Languages are: C and Go and COBOL") +} + +func (s *TypeconvSuite) TestTOML(c *C) { + inOutTest(c, `{{ $t := `+"`"+`# comment +foo = "bar" + +[baz] +qux = "quux"`+"`"+` | toml -}} +{{ $t.baz.qux }}`, "quux") + + inOutTest(c, `{{ "foo:\n bar:\n baz: qux" | yaml | toTOML }}`, + `[foo] + [foo.bar] + baz = "qux"`) +} |
