cli: actually allow the 'connect envoy' and 'watch' subcommands to work with -token-file (#5733)

This commit is contained in:
R.B. Boyer 2019-04-30 09:59:00 -05:00 committed by GitHub
parent 374c0f66b1
commit 4ee5e71dcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 461 additions and 10 deletions

View File

@ -191,6 +191,11 @@ func (c *cmd) templateArgs() (*BootstrapTplArgs, error) {
httpCfg := api.DefaultConfig() httpCfg := api.DefaultConfig()
c.http.MergeOntoConfig(httpCfg) 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 // 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 // suggests TLS is supported explicitly (CONSUL_HTTP_SSL) or implicitly
// (CONSUL_HTTP_ADDR) is https:// // (CONSUL_HTTP_ADDR) is https://

View File

@ -11,11 +11,11 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/mitchellh/cli"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/agent/xds" "github.com/hashicorp/consul/agent/xds"
"github.com/hashicorp/consul/api" "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") var update = flag.Bool("update", false, "update golden files")
@ -64,6 +64,7 @@ func TestGenerateConfig(t *testing.T) {
Name string Name string
Flags []string Flags []string
Env []string Env []string
Files map[string]string
ProxyConfig map[string]interface{} ProxyConfig map[string]interface{}
WantArgs BootstrapTplArgs WantArgs BootstrapTplArgs
WantErr string WantErr string
@ -88,6 +89,79 @@ func TestGenerateConfig(t *testing.T) {
LocalAgentClusterName: xds.LocalAgentClusterName, 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", Name: "grpc-addr-flag",
Flags: []string{"-proxy-id", "test-proxy", 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 { for _, tc := range cases {
t.Run(tc.Name, func(t *testing.T) { t.Run(tc.Name, func(t *testing.T) {
require := require.New(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() ui := cli.NewMockUi()
c := New(ui) c := New(ui)
@ -321,10 +413,15 @@ func TestGenerateConfig(t *testing.T) {
// Set the agent HTTP address in ENV to be our mock // Set the agent HTTP address in ENV to be our mock
tc.Env = append(tc.Env, "CONSUL_HTTP_ADDR="+srv.URL) 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 // Run the command
args := append([]string{"-bootstrap"}, tc.Flags...) args := append([]string{"-bootstrap"}, myFlags...)
code := c.Run(args) code := c.Run(args)
if tc.WantErr == "" { if tc.WantErr == "" {
require.Equal(0, code, ui.ErrorWriter.String()) require.Equal(0, code, ui.ErrorWriter.String())

View File

@ -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"
}
}
}
}
}

View File

@ -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"
}
}
}
}
}

View File

@ -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"
}
}
}
}
}

View File

@ -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"
}
}
}
}
}

View File

@ -12,6 +12,7 @@ import (
"github.com/hashicorp/consul/agent" "github.com/hashicorp/consul/agent"
"github.com/hashicorp/consul/agent/exec" "github.com/hashicorp/consul/agent/exec"
"github.com/hashicorp/consul/api"
consulwatch "github.com/hashicorp/consul/api/watch" consulwatch "github.com/hashicorp/consul/api/watch"
"github.com/hashicorp/consul/command/flags" "github.com/hashicorp/consul/command/flags"
"github.com/mitchellh/cli" "github.com/mitchellh/cli"
@ -74,6 +75,17 @@ func (c *cmd) init() {
c.help = flags.Usage(help, c.flags) 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 { func (c *cmd) Run(args []string) int {
if err := c.flags.Parse(args); err != nil { if err := c.flags.Parse(args); err != nil {
return 1 return 1
@ -87,12 +99,10 @@ func (c *cmd) Run(args []string) int {
return 1 return 1
} }
token := c.http.Token() token, err := c.loadToken()
if tokenFromFile, err := c.http.ReadTokenFile(); err != nil { if err != nil {
c.UI.Error(fmt.Sprintf("Error loading token file: %s", err)) c.UI.Error(err.Error())
return 1 return 1
} else if tokenFromFile != "" {
token = tokenFromFile
} }
// Compile the watch parameters // Compile the watch parameters

View File

@ -1,12 +1,17 @@
package watch package watch
import ( import (
"io/ioutil"
"os"
"path/filepath"
"strings" "strings"
"testing" "testing"
"github.com/hashicorp/consul/agent" "github.com/hashicorp/consul/agent"
"github.com/hashicorp/consul/sdk/testutil"
"github.com/hashicorp/consul/testrpc" "github.com/hashicorp/consul/testrpc"
"github.com/mitchellh/cli" "github.com/mitchellh/cli"
"github.com/stretchr/testify/require"
) )
func TestWatchCommand_noTabs(t *testing.T) { 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) { func TestWatchCommandNoConnect(t *testing.T) {
t.Parallel() t.Parallel()
a := agent.NewTestAgent(t, t.Name(), ``) a := agent.NewTestAgent(t, t.Name(), ``)