diff --git a/config.go b/config.go index eafcfc5..70d4ec0 100644 --- a/config.go +++ b/config.go @@ -18,7 +18,7 @@ const ( defaultDirectory = ".config/hcp/hvd" testDirectory = "hcptest" fileName = "hvd_proxy_config.json" - directoryPermissions = 0755 + directoryPermissions = 0o755 envVarCacheTestMode = "HCP_CACHE_TEST_MODE" ) diff --git a/config_test.go b/config_test.go index 2b2eb96..21f6f53 100644 --- a/config_test.go +++ b/config_test.go @@ -37,7 +37,6 @@ func Test_GetHCPConfiguration(t *testing.T) { assert.Nil(t, tk) assert.Nil(t, err) } - }) } } diff --git a/connect.go b/connect.go index edee475..a091f50 100644 --- a/connect.go +++ b/connect.go @@ -101,7 +101,12 @@ func (c *HCPConnectCommand) Run(args []string) int { func (c *HCPConnectCommand) setupClients() error { opts := []config.HCPConfigOption{config.FromEnv()} - if c.flagClientID != "" && c.flagSecretID != "" { + + if c.flagClientID != "" && c.flagSecretID == "" { + return errors.New("secret-id is required when client-id is provided") + } else if c.flagSecretID != "" && c.flagClientID == "" { + return errors.New("client-id is required when secret-id is provided") + } else if c.flagClientID != "" && c.flagSecretID != "" { opts = append(opts, config.WithClientCredentials(c.flagClientID, c.flagSecretID)) opts = append(opts, config.WithoutBrowserLogin()) } @@ -378,9 +383,17 @@ func (c *HCPConnectCommand) listClusters(organizationID string, projectID string default: cluster := clustersResp.GetPayload().Clusters[0] - if *cluster.State != hcpvsm.HashicorpCloudVault20201125ClusterStateRUNNING { + + clusterState := *cluster.State + + if clusterState == hcpvsm.HashicorpCloudVault20201125ClusterStateLOCKED || clusterState == hcpvsm.HashicorpCloudVault20201125ClusterStateLOCKING { + return "", errors.New("cluster is locked") + } else if clusterState == hcpvsm.HashicorpCloudVault20201125ClusterStateCREATING { + return "", errors.New("cluster is still being created") + } else if clusterState != hcpvsm.HashicorpCloudVault20201125ClusterStateRUNNING { return "", errors.New("cluster is not running") } + if *cluster.Config.NetworkConfig.HTTPProxyOption == hcpvsm.HashicorpCloudVault20201125HTTPProxyOptionDISABLED { return "", ErrorProxyDisabled } diff --git a/connect_test.go b/connect_test.go index 3c44088..f15b32a 100644 --- a/connect_test.go +++ b/connect_test.go @@ -33,6 +33,52 @@ func testHCPConnectCommand() (*cli.MockUi, *HCPConnectCommand) { return ui, &HCPConnectCommand{Ui: ui} } +func Test_HCPConnect_FlagValidation(t *testing.T) { + cases := []struct { + name string + flags []string + code int + error string + }{ + { + name: "invalid flags", + flags: []string{"-invalid", "abc123"}, + code: 1, + error: "flag provided but not defined: -invalid", + }, + { + name: "only client-id provided", + flags: []string{"-client-id", "abc123"}, + code: 1, + error: "secret-id is required when client-id is provided", + }, + { + name: "only secret-id provided", + flags: []string{"-secret-id", "abc123"}, + code: 1, + error: "client-id is required when secret-id is provided", + }, + } + + for _, tc := range cases { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + ui, cmd := testHCPConnectCommand() + result := cmd.Run(tc.flags) + output := ui.OutputWriter.String() + ui.ErrorWriter.String() + + assert.Equal(t, tc.code, result) + + if tc.error != "" { + assert.Contains(t, output, tc.error) + } + }) + } +} + func Test_HCPConnectCommand(t *testing.T) { tests := map[string]struct{ getCallerIdentityResp *hcpis.IamServiceGetCallerIdentityOK @@ -408,7 +454,6 @@ func Test_getProject(t *testing.T) { } }) } - } func Test_getCluster(t *testing.T) { @@ -551,6 +596,105 @@ func Test_getCluster(t *testing.T) { expectedError: errors.New("invalid cluster: cluster-4"), }, + // Test error handling for cluster still being created + // UI interaction required + "cluster in creating": { + userInputCluster: "cluster-2", + listClustersServiceListResponse: &hcpvs.ListOK{ + Payload: &hcpvsm.HashicorpCloudVault20201125ListResponse{ + Clusters: []*hcpvsm.HashicorpCloudVault20201125Cluster{ + { + ID: "cluster-1", + DNSNames: &hcpvsm.HashicorpCloudVault20201125ClusterDNSNames{Proxy: "hcp-proxy-cluster-1.addr:8200"}, + State: hcpvsm.NewHashicorpCloudVault20201125ClusterState(hcpvsm.HashicorpCloudVault20201125ClusterStateRUNNING), + Config: &hcpvsm.HashicorpCloudVault20201125ClusterConfig{ + NetworkConfig: &hcpvsm.HashicorpCloudVault20201125NetworkConfig{ + HTTPProxyOption: hcpvsm.NewHashicorpCloudVault20201125HTTPProxyOption(hcpvsm.HashicorpCloudVault20201125HTTPProxyOptionENABLED), + }, + }, + }, + { + ID: "cluster-2", + DNSNames: &hcpvsm.HashicorpCloudVault20201125ClusterDNSNames{Proxy: "hcp-proxy-cluster-2.addr:8200"}, + State: hcpvsm.NewHashicorpCloudVault20201125ClusterState(hcpvsm.HashicorpCloudVault20201125ClusterStateCREATING), + Config: &hcpvsm.HashicorpCloudVault20201125ClusterConfig{ + NetworkConfig: &hcpvsm.HashicorpCloudVault20201125NetworkConfig{ + HTTPProxyOption: hcpvsm.NewHashicorpCloudVault20201125HTTPProxyOption(hcpvsm.HashicorpCloudVault20201125HTTPProxyOptionENABLED), + }, + }, + }, + }, + }, + }, + expectedError: errors.New("cluster is still being created"), + }, + + // Test error handling for cluster is locked + // UI interaction required + "cluster locked": { + userInputCluster: "cluster-2", + listClustersServiceListResponse: &hcpvs.ListOK{ + Payload: &hcpvsm.HashicorpCloudVault20201125ListResponse{ + Clusters: []*hcpvsm.HashicorpCloudVault20201125Cluster{ + { + ID: "cluster-1", + DNSNames: &hcpvsm.HashicorpCloudVault20201125ClusterDNSNames{Proxy: "hcp-proxy-cluster-1.addr:8200"}, + State: hcpvsm.NewHashicorpCloudVault20201125ClusterState(hcpvsm.HashicorpCloudVault20201125ClusterStateLOCKED), + Config: &hcpvsm.HashicorpCloudVault20201125ClusterConfig{ + NetworkConfig: &hcpvsm.HashicorpCloudVault20201125NetworkConfig{ + HTTPProxyOption: hcpvsm.NewHashicorpCloudVault20201125HTTPProxyOption(hcpvsm.HashicorpCloudVault20201125HTTPProxyOptionENABLED), + }, + }, + }, + { + ID: "cluster-2", + DNSNames: &hcpvsm.HashicorpCloudVault20201125ClusterDNSNames{Proxy: "hcp-proxy-cluster-2.addr:8200"}, + State: hcpvsm.NewHashicorpCloudVault20201125ClusterState(hcpvsm.HashicorpCloudVault20201125ClusterStateCREATING), + Config: &hcpvsm.HashicorpCloudVault20201125ClusterConfig{ + NetworkConfig: &hcpvsm.HashicorpCloudVault20201125NetworkConfig{ + HTTPProxyOption: hcpvsm.NewHashicorpCloudVault20201125HTTPProxyOption(hcpvsm.HashicorpCloudVault20201125HTTPProxyOptionENABLED), + }, + }, + }, + }, + }, + }, + expectedError: errors.New("cluster is locked"), + }, + + // Test error handling for cluster is locked + // UI interaction required + "cluster locking": { + userInputCluster: "cluster-2", + listClustersServiceListResponse: &hcpvs.ListOK{ + Payload: &hcpvsm.HashicorpCloudVault20201125ListResponse{ + Clusters: []*hcpvsm.HashicorpCloudVault20201125Cluster{ + { + ID: "cluster-1", + DNSNames: &hcpvsm.HashicorpCloudVault20201125ClusterDNSNames{Proxy: "hcp-proxy-cluster-1.addr:8200"}, + State: hcpvsm.NewHashicorpCloudVault20201125ClusterState(hcpvsm.HashicorpCloudVault20201125ClusterStateLOCKING), + Config: &hcpvsm.HashicorpCloudVault20201125ClusterConfig{ + NetworkConfig: &hcpvsm.HashicorpCloudVault20201125NetworkConfig{ + HTTPProxyOption: hcpvsm.NewHashicorpCloudVault20201125HTTPProxyOption(hcpvsm.HashicorpCloudVault20201125HTTPProxyOptionENABLED), + }, + }, + }, + { + ID: "cluster-2", + DNSNames: &hcpvsm.HashicorpCloudVault20201125ClusterDNSNames{Proxy: "hcp-proxy-cluster-2.addr:8200"}, + State: hcpvsm.NewHashicorpCloudVault20201125ClusterState(hcpvsm.HashicorpCloudVault20201125ClusterStateCREATING), + Config: &hcpvsm.HashicorpCloudVault20201125ClusterConfig{ + NetworkConfig: &hcpvsm.HashicorpCloudVault20201125NetworkConfig{ + HTTPProxyOption: hcpvsm.NewHashicorpCloudVault20201125HTTPProxyOption(hcpvsm.HashicorpCloudVault20201125HTTPProxyOptionENABLED), + }, + }, + }, + }, + }, + }, + expectedError: errors.New("cluster is locked"), + }, + // Test generic expectedError returned "expectedError": { expectedError: errors.New("error getting cluster"), diff --git a/disconnect.go b/disconnect.go index 9820d5a..eeea12d 100644 --- a/disconnect.go +++ b/disconnect.go @@ -12,9 +12,7 @@ import ( "github.com/hashicorp/cli" ) -var ( - _ cli.Command = (*HCPDisconnectCommand)(nil) -) +var _ cli.Command = (*HCPDisconnectCommand)(nil) type HCPDisconnectCommand struct { Ui cli.Ui diff --git a/testhelpers.go b/testhelpers.go index fba8c45..5648548 100644 --- a/testhelpers.go +++ b/testhelpers.go @@ -4,8 +4,9 @@ package vaulthcplib import ( - "golang.org/x/oauth2" "time" + + "golang.org/x/oauth2" ) type TestTokenSource struct{}