From 4ee5e71dcdc9f297517e8b7911ffb51532ca085c Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Tue, 30 Apr 2019 09:59:00 -0500 Subject: [PATCH] cli: actually allow the 'connect envoy' and 'watch' subcommands to work with -token-file (#5733) --- command/connect/envoy/envoy.go | 5 + command/connect/envoy/envoy_test.go | 107 +++++++++++++++++- .../connect/envoy/testdata/token-arg.golden | 60 ++++++++++ .../connect/envoy/testdata/token-env.golden | 60 ++++++++++ .../envoy/testdata/token-file-arg.golden | 60 ++++++++++ .../envoy/testdata/token-file-env.golden | 60 ++++++++++ command/watch/watch.go | 20 +++- command/watch/watch_test.go | 99 ++++++++++++++++ 8 files changed, 461 insertions(+), 10 deletions(-) create mode 100644 command/connect/envoy/testdata/token-arg.golden create mode 100644 command/connect/envoy/testdata/token-env.golden create mode 100644 command/connect/envoy/testdata/token-file-arg.golden create mode 100644 command/connect/envoy/testdata/token-file-env.golden diff --git a/command/connect/envoy/envoy.go b/command/connect/envoy/envoy.go index 3c9bc4dd5..bfb0ae4a7 100644 --- a/command/connect/envoy/envoy.go +++ b/command/connect/envoy/envoy.go @@ -191,6 +191,11 @@ func (c *cmd) templateArgs() (*BootstrapTplArgs, error) { httpCfg := api.DefaultConfig() c.http.MergeOntoConfig(httpCfg) + // Trigger the Client init to do any last-minute updates to the Config. + if _, err := api.NewClient(httpCfg); err != nil { + return nil, err + } + // Decide on TLS if the scheme is provided and indicates it, if the HTTP env // suggests TLS is supported explicitly (CONSUL_HTTP_SSL) or implicitly // (CONSUL_HTTP_ADDR) is https:// diff --git a/command/connect/envoy/envoy_test.go b/command/connect/envoy/envoy_test.go index 61ea4071e..df9b886d4 100644 --- a/command/connect/envoy/envoy_test.go +++ b/command/connect/envoy/envoy_test.go @@ -11,11 +11,11 @@ import ( "strings" "testing" - "github.com/mitchellh/cli" - "github.com/stretchr/testify/require" - "github.com/hashicorp/consul/agent/xds" "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/sdk/testutil" + "github.com/mitchellh/cli" + "github.com/stretchr/testify/require" ) var update = flag.Bool("update", false, "update golden files") @@ -64,6 +64,7 @@ func TestGenerateConfig(t *testing.T) { Name string Flags []string Env []string + Files map[string]string ProxyConfig map[string]interface{} WantArgs BootstrapTplArgs WantErr string @@ -88,6 +89,79 @@ func TestGenerateConfig(t *testing.T) { LocalAgentClusterName: xds.LocalAgentClusterName, }, }, + { + Name: "token-arg", + Flags: []string{"-proxy-id", "test-proxy", + "-token", "c9a52720-bf6c-4aa6-b8bc-66881a5ade95"}, + Env: []string{}, + WantArgs: BootstrapTplArgs{ + ProxyCluster: "test-proxy", + ProxyID: "test-proxy", + AgentAddress: "127.0.0.1", + AgentPort: "8502", // Note this is the gRPC port + AdminBindAddress: "127.0.0.1", + AdminBindPort: "19000", + LocalAgentClusterName: xds.LocalAgentClusterName, + Token: "c9a52720-bf6c-4aa6-b8bc-66881a5ade95", + }, + }, + { + Name: "token-env", + Flags: []string{"-proxy-id", "test-proxy"}, + Env: []string{ + "CONSUL_HTTP_TOKEN=c9a52720-bf6c-4aa6-b8bc-66881a5ade95", + }, + WantArgs: BootstrapTplArgs{ + ProxyCluster: "test-proxy", + ProxyID: "test-proxy", + AgentAddress: "127.0.0.1", + AgentPort: "8502", // Note this is the gRPC port + AdminBindAddress: "127.0.0.1", + AdminBindPort: "19000", + LocalAgentClusterName: xds.LocalAgentClusterName, + Token: "c9a52720-bf6c-4aa6-b8bc-66881a5ade95", + }, + }, + { + Name: "token-file-arg", + Flags: []string{"-proxy-id", "test-proxy", + "-token-file", "@@TEMPDIR@@token.txt", + }, + Env: []string{}, + Files: map[string]string{ + "token.txt": "c9a52720-bf6c-4aa6-b8bc-66881a5ade95", + }, + WantArgs: BootstrapTplArgs{ + ProxyCluster: "test-proxy", + ProxyID: "test-proxy", + AgentAddress: "127.0.0.1", + AgentPort: "8502", // Note this is the gRPC port + AdminBindAddress: "127.0.0.1", + AdminBindPort: "19000", + LocalAgentClusterName: xds.LocalAgentClusterName, + Token: "c9a52720-bf6c-4aa6-b8bc-66881a5ade95", + }, + }, + { + Name: "token-file-env", + Flags: []string{"-proxy-id", "test-proxy"}, + Env: []string{ + "CONSUL_HTTP_TOKEN_FILE=@@TEMPDIR@@token.txt", + }, + Files: map[string]string{ + "token.txt": "c9a52720-bf6c-4aa6-b8bc-66881a5ade95", + }, + WantArgs: BootstrapTplArgs{ + ProxyCluster: "test-proxy", + ProxyID: "test-proxy", + AgentAddress: "127.0.0.1", + AgentPort: "8502", // Note this is the gRPC port + AdminBindAddress: "127.0.0.1", + AdminBindPort: "19000", + LocalAgentClusterName: xds.LocalAgentClusterName, + Token: "c9a52720-bf6c-4aa6-b8bc-66881a5ade95", + }, + }, { Name: "grpc-addr-flag", Flags: []string{"-proxy-id", "test-proxy", @@ -306,10 +380,28 @@ func TestGenerateConfig(t *testing.T) { }, } + copyAndReplaceAll := func(s []string, old, new string) []string { + out := make([]string, len(s)) + for i, v := range s { + out[i] = strings.ReplaceAll(v, old, new) + } + return out + } + for _, tc := range cases { t.Run(tc.Name, func(t *testing.T) { require := require.New(t) + testDir := testutil.TempDir(t, "envoytest") + defer os.RemoveAll(testDir) + + if len(tc.Files) > 0 { + for fn, fv := range tc.Files { + fullname := filepath.Join(testDir, fn) + require.NoError(ioutil.WriteFile(fullname, []byte(fv), 0600)) + } + } + ui := cli.NewMockUi() c := New(ui) @@ -321,10 +413,15 @@ func TestGenerateConfig(t *testing.T) { // Set the agent HTTP address in ENV to be our mock tc.Env = append(tc.Env, "CONSUL_HTTP_ADDR="+srv.URL) - defer testSetAndResetEnv(t, tc.Env)() + testDirPrefix := testDir + string(filepath.Separator) + + myFlags := copyAndReplaceAll(tc.Flags, "@@TEMPDIR@@", testDirPrefix) + myEnv := copyAndReplaceAll(tc.Env, "@@TEMPDIR@@", testDirPrefix) + + defer testSetAndResetEnv(t, myEnv)() // Run the command - args := append([]string{"-bootstrap"}, tc.Flags...) + args := append([]string{"-bootstrap"}, myFlags...) code := c.Run(args) if tc.WantErr == "" { require.Equal(0, code, ui.ErrorWriter.String()) diff --git a/command/connect/envoy/testdata/token-arg.golden b/command/connect/envoy/testdata/token-arg.golden new file mode 100644 index 000000000..07b7f6cac --- /dev/null +++ b/command/connect/envoy/testdata/token-arg.golden @@ -0,0 +1,60 @@ +{ + "admin": { + "access_log_path": "/dev/null", + "address": { + "socket_address": { + "address": "127.0.0.1", + "port_value": 19000 + } + } + }, + "node": { + "cluster": "test-proxy", + "id": "test-proxy" + }, + "static_resources": { + "clusters": [ + { + "name": "local_agent", + "connect_timeout": "1s", + "type": "STATIC", + "http2_protocol_options": {}, + "hosts": [ + { + "socket_address": { + "address": "127.0.0.1", + "port_value": 8502 + } + } + ] + } + ] + }, + "stats_config": { + "stats_tags": [ + { + "tag_name": "local_cluster", + "fixed_value": "test-proxy" + } + ], + "use_all_default_tags": true + }, + "dynamic_resources": { + "lds_config": { "ads": {} }, + "cds_config": { "ads": {} }, + "ads_config": { + "api_type": "GRPC", + "grpc_services": { + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "c9a52720-bf6c-4aa6-b8bc-66881a5ade95" + } + ], + "envoy_grpc": { + "cluster_name": "local_agent" + } + } + } + } +} diff --git a/command/connect/envoy/testdata/token-env.golden b/command/connect/envoy/testdata/token-env.golden new file mode 100644 index 000000000..07b7f6cac --- /dev/null +++ b/command/connect/envoy/testdata/token-env.golden @@ -0,0 +1,60 @@ +{ + "admin": { + "access_log_path": "/dev/null", + "address": { + "socket_address": { + "address": "127.0.0.1", + "port_value": 19000 + } + } + }, + "node": { + "cluster": "test-proxy", + "id": "test-proxy" + }, + "static_resources": { + "clusters": [ + { + "name": "local_agent", + "connect_timeout": "1s", + "type": "STATIC", + "http2_protocol_options": {}, + "hosts": [ + { + "socket_address": { + "address": "127.0.0.1", + "port_value": 8502 + } + } + ] + } + ] + }, + "stats_config": { + "stats_tags": [ + { + "tag_name": "local_cluster", + "fixed_value": "test-proxy" + } + ], + "use_all_default_tags": true + }, + "dynamic_resources": { + "lds_config": { "ads": {} }, + "cds_config": { "ads": {} }, + "ads_config": { + "api_type": "GRPC", + "grpc_services": { + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "c9a52720-bf6c-4aa6-b8bc-66881a5ade95" + } + ], + "envoy_grpc": { + "cluster_name": "local_agent" + } + } + } + } +} diff --git a/command/connect/envoy/testdata/token-file-arg.golden b/command/connect/envoy/testdata/token-file-arg.golden new file mode 100644 index 000000000..07b7f6cac --- /dev/null +++ b/command/connect/envoy/testdata/token-file-arg.golden @@ -0,0 +1,60 @@ +{ + "admin": { + "access_log_path": "/dev/null", + "address": { + "socket_address": { + "address": "127.0.0.1", + "port_value": 19000 + } + } + }, + "node": { + "cluster": "test-proxy", + "id": "test-proxy" + }, + "static_resources": { + "clusters": [ + { + "name": "local_agent", + "connect_timeout": "1s", + "type": "STATIC", + "http2_protocol_options": {}, + "hosts": [ + { + "socket_address": { + "address": "127.0.0.1", + "port_value": 8502 + } + } + ] + } + ] + }, + "stats_config": { + "stats_tags": [ + { + "tag_name": "local_cluster", + "fixed_value": "test-proxy" + } + ], + "use_all_default_tags": true + }, + "dynamic_resources": { + "lds_config": { "ads": {} }, + "cds_config": { "ads": {} }, + "ads_config": { + "api_type": "GRPC", + "grpc_services": { + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "c9a52720-bf6c-4aa6-b8bc-66881a5ade95" + } + ], + "envoy_grpc": { + "cluster_name": "local_agent" + } + } + } + } +} diff --git a/command/connect/envoy/testdata/token-file-env.golden b/command/connect/envoy/testdata/token-file-env.golden new file mode 100644 index 000000000..07b7f6cac --- /dev/null +++ b/command/connect/envoy/testdata/token-file-env.golden @@ -0,0 +1,60 @@ +{ + "admin": { + "access_log_path": "/dev/null", + "address": { + "socket_address": { + "address": "127.0.0.1", + "port_value": 19000 + } + } + }, + "node": { + "cluster": "test-proxy", + "id": "test-proxy" + }, + "static_resources": { + "clusters": [ + { + "name": "local_agent", + "connect_timeout": "1s", + "type": "STATIC", + "http2_protocol_options": {}, + "hosts": [ + { + "socket_address": { + "address": "127.0.0.1", + "port_value": 8502 + } + } + ] + } + ] + }, + "stats_config": { + "stats_tags": [ + { + "tag_name": "local_cluster", + "fixed_value": "test-proxy" + } + ], + "use_all_default_tags": true + }, + "dynamic_resources": { + "lds_config": { "ads": {} }, + "cds_config": { "ads": {} }, + "ads_config": { + "api_type": "GRPC", + "grpc_services": { + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "c9a52720-bf6c-4aa6-b8bc-66881a5ade95" + } + ], + "envoy_grpc": { + "cluster_name": "local_agent" + } + } + } + } +} diff --git a/command/watch/watch.go b/command/watch/watch.go index 6a17b8309..915463330 100644 --- a/command/watch/watch.go +++ b/command/watch/watch.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/consul/agent" "github.com/hashicorp/consul/agent/exec" + "github.com/hashicorp/consul/api" consulwatch "github.com/hashicorp/consul/api/watch" "github.com/hashicorp/consul/command/flags" "github.com/mitchellh/cli" @@ -74,6 +75,17 @@ func (c *cmd) init() { c.help = flags.Usage(help, c.flags) } +func (c *cmd) loadToken() (string, error) { + httpCfg := api.DefaultConfig() + c.http.MergeOntoConfig(httpCfg) + // Trigger the Client init to do any last-minute updates to the Config. + if _, err := api.NewClient(httpCfg); err != nil { + return "", err + } + + return httpCfg.Token, nil +} + func (c *cmd) Run(args []string) int { if err := c.flags.Parse(args); err != nil { return 1 @@ -87,12 +99,10 @@ func (c *cmd) Run(args []string) int { return 1 } - token := c.http.Token() - if tokenFromFile, err := c.http.ReadTokenFile(); err != nil { - c.UI.Error(fmt.Sprintf("Error loading token file: %s", err)) + token, err := c.loadToken() + if err != nil { + c.UI.Error(err.Error()) return 1 - } else if tokenFromFile != "" { - token = tokenFromFile } // Compile the watch parameters diff --git a/command/watch/watch_test.go b/command/watch/watch_test.go index 576742efc..d202cfb4d 100644 --- a/command/watch/watch_test.go +++ b/command/watch/watch_test.go @@ -1,12 +1,17 @@ package watch import ( + "io/ioutil" + "os" + "path/filepath" "strings" "testing" "github.com/hashicorp/consul/agent" + "github.com/hashicorp/consul/sdk/testutil" "github.com/hashicorp/consul/testrpc" "github.com/mitchellh/cli" + "github.com/stretchr/testify/require" ) func TestWatchCommand_noTabs(t *testing.T) { @@ -36,6 +41,100 @@ func TestWatchCommand(t *testing.T) { } } +func TestWatchCommand_loadToken(t *testing.T) { + a := agent.NewTestAgent(t, t.Name(), ` `) + defer a.Shutdown() + testrpc.WaitForTestAgent(t, a.RPC, "dc1") + + const testToken = "4a0db5a1-869f-4602-ae8a-b0306a82f1ef" + testDir := testutil.TempDir(t, "watchtest") + defer os.RemoveAll(testDir) + + fullname := filepath.Join(testDir, "token.txt") + require.NoError(t, ioutil.WriteFile(fullname, []byte(testToken), 0600)) + + resetEnv := func() { + os.Unsetenv("CONSUL_HTTP_TOKEN") + os.Unsetenv("CONSUL_HTTP_TOKEN_FILE") + } + + t.Run("token arg", func(t *testing.T) { + resetEnv() + defer resetEnv() + + ui := cli.NewMockUi() + c := New(ui, nil) + args := []string{ + "-http-addr=" + a.HTTPAddr(), + "-type=nodes", + "-token", testToken, + } + + require.NoError(t, c.flags.Parse(args)) + + tok, err := c.loadToken() + require.NoError(t, err) + require.Equal(t, testToken, tok) + }) + + t.Run("token env", func(t *testing.T) { + resetEnv() + defer resetEnv() + + ui := cli.NewMockUi() + c := New(ui, nil) + args := []string{ + "-http-addr=" + a.HTTPAddr(), + "-type=nodes", + } + os.Setenv("CONSUL_HTTP_TOKEN", testToken) + + require.NoError(t, c.flags.Parse(args)) + + tok, err := c.loadToken() + require.NoError(t, err) + require.Equal(t, testToken, tok) + }) + + t.Run("token file arg", func(t *testing.T) { + resetEnv() + defer resetEnv() + + ui := cli.NewMockUi() + c := New(ui, nil) + args := []string{ + "-http-addr=" + a.HTTPAddr(), + "-type=nodes", + "-token-file", fullname, + } + + require.NoError(t, c.flags.Parse(args)) + + tok, err := c.loadToken() + require.NoError(t, err) + require.Equal(t, testToken, tok) + }) + + t.Run("token file env", func(t *testing.T) { + resetEnv() + defer resetEnv() + + ui := cli.NewMockUi() + c := New(ui, nil) + args := []string{ + "-http-addr=" + a.HTTPAddr(), + "-type=nodes", + } + os.Setenv("CONSUL_HTTP_TOKEN_FILE", fullname) + + require.NoError(t, c.flags.Parse(args)) + + tok, err := c.loadToken() + require.NoError(t, err) + require.Equal(t, testToken, tok) + }) +} + func TestWatchCommandNoConnect(t *testing.T) { t.Parallel() a := agent.NewTestAgent(t, t.Name(), ``)