Skip to content

Commit

Permalink
Add organization logic
Browse files Browse the repository at this point in the history
  • Loading branch information
biazmoreira committed Oct 4, 2023
1 parent dcf6297 commit cbb4590
Show file tree
Hide file tree
Showing 8 changed files with 918 additions and 30 deletions.
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ fmtcheck:

.PHONY: fmt
fmt:
gofumpt -l -w .
gofumpt -l -w .

mocks:
go install github.com/vektra/mockery/[email protected]
mockery --srcpkg github.com/hashicorp/hcp-sdk-go/clients/cloud-resource-manager/stable/2019-12-10/client/organization_service --name=ClientService
146 changes: 138 additions & 8 deletions connect.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package hcpvaultengine

import (
"errors"
"flag"
"fmt"
httptransport "github.com/go-openapi/runtime/client"
"strconv"
"strings"

hcprmo "github.com/hashicorp/hcp-sdk-go/clients/cloud-resource-manager/stable/2019-12-10/client/organization_service"
hcprmp "github.com/hashicorp/hcp-sdk-go/clients/cloud-resource-manager/stable/2019-12-10/client/project_service"
hcprmm "github.com/hashicorp/hcp-sdk-go/clients/cloud-resource-manager/stable/2019-12-10/models"
"github.com/mitchellh/cli"
)

Expand All @@ -15,8 +22,6 @@ type HCPConnectCommand struct {
Ui cli.Ui

flagNonInteractiveMethod bool

connectHandler ConnectHandler
}

func (c *HCPConnectCommand) Help() string {
Expand All @@ -32,21 +37,146 @@ func (c *HCPConnectCommand) Run(args []string) int {
return 1
}

if c.flagNonInteractiveMethod {
c.connectHandler = &nonInteractiveConnectHandler{}
} else {
c.connectHandler = &interactiveConnectHandler{}
}
connectHandler := c.connectHandlerFactory()

authStatus, err := c.connectHandler.Connect(args)
hcpHttpClient, err := connectHandler.Connect(args)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to connect to HCP: %s", err))
return 1
}

// List orgs
// If list is greater than 1, ask for user input c.Ui.Ask()
//
// should we add pagination?
organizationID, err := c.getOrganization(hcprmo.New(hcpHttpClient, nil))

Check failure on line 52 in connect.go

View workflow job for this annotation

GitHub Actions / run-tests / Run Tests

organizationID declared and not used
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to get HCP project information: %s", err))
return 1
}

// List projects for chosen org
// If list is greater than 1, ask for user input c.Ui.Ask()

// List clusters for org+project
// If list is greater than 1, ask for user input c.Ui.Ask()

// Get cluster address and configure RoundTripper
// Configure RoundTripper
// closure? pointer?

return 0
}

func (c *HCPConnectCommand) getOrganization(rmOrgClient hcprmo.ClientService) (organizationID string, err error) {
organizationsResp, err := rmOrgClient.OrganizationServiceList(&hcprmo.OrganizationServiceListParams{}, nil)
switch {
case err != nil:
return "", err
case organizationsResp.GetPayload() == nil:
return "", errors.New("payload is nil")
case len(organizationsResp.GetPayload().Organizations) < 1:
return "", errors.New("no organizations available")
case len(organizationsResp.GetPayload().Organizations) > 1:
var orgs []string
for i, org := range organizationsResp.GetPayload().Organizations {
if org.State != hcprmm.HashicorpCloudResourcemanagerOrganizationOrganizationStateACTIVE.Pointer() {
continue
}

orgs = append(orgs, fmt.Sprintf("%d: Organization name: %s Organization ID: %s", i, org.Name, org.ID))
}
userInput, err := c.Ui.Ask(fmt.Sprintf("Choose one of the following organizations: %s", strings.Join(orgs, "\n")))
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to get HCP organization information: %s", err))
return "", err
}
// convert userInput to int
var index int
index, err = strconv.Atoi(userInput)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to get HCP organization information: %s", err))
return "", err
}
// if conversion fails, return an error
// else validate that the index is within boundaries of the organization slice
if index > len(orgs) || index < len(orgs) {
return "", errors.New("invalid organization chosen")
}

// set the org ID
organizationID = organizationsResp.GetPayload().Organizations[index].ID
organizationName := organizationsResp.GetPayload().Organizations[index].Name
c.Ui.Info(fmt.Sprintf("HCP Organization: %s", organizationName))

break
case len(organizationsResp.GetPayload().Organizations) == 1:
organization := organizationsResp.GetPayload().Organizations[0]
if organization.State != hcprmm.HashicorpCloudResourcemanagerOrganizationOrganizationStateACTIVE.Pointer() {
return "", errors.New("organization is not active")
}
organizationID = organization.ID
c.Ui.Info(fmt.Sprintf("HCP Organization: %s", organization.Name))
}
return organizationID, nil
}

func (c *HCPConnectCommand) getProject(hcpHttpClient *httptransport.Runtime) (projectID string, err error) {
rmProjClient := hcprmp.New(hcpHttpClient, nil)
projectResp, err := rmProjClient.ProjectServiceList(&hcprmp.ProjectServiceListParams{}, nil)
switch {
case err != nil:
return "", err
case projectResp.GetPayload() == nil:
return "", errors.New("payload is nil")
case len(organizationsResp.GetPayload().Organizations) < 1:

Check failure on line 132 in connect.go

View workflow job for this annotation

GitHub Actions / run-tests / Run Tests

undefined: organizationsResp
return "", errors.New("no organizations available")
case len(organizationsResp.GetPayload().Organizations) > 1:

Check failure on line 134 in connect.go

View workflow job for this annotation

GitHub Actions / run-tests / Run Tests

undefined: organizationsResp
var orgs []string
for i, org := range organizationsResp.GetPayload().Organizations {

Check failure on line 136 in connect.go

View workflow job for this annotation

GitHub Actions / run-tests / Run Tests

undefined: organizationsResp
if org.State != hcprmm.HashicorpCloudResourcemanagerOrganizationOrganizationStateACTIVE.Pointer() {
continue
}

orgs = append(orgs, fmt.Sprintf("%d: Organization name: %s Organization ID: %s", i, org.Name, org.ID))
}
userInput, err := c.Ui.Ask(fmt.Sprintf("Choose one of the following organizations: %s", strings.Join(orgs, "\n")))
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to get HCP organization information: %s", err))
return "", err
}
// convert userInput to int
var index int
// if conversion fails, return an error
// else validate that the index is within boundaries of the organization slice
if index > len(orgs) || index < len(orgs) {
return "", errors.New("invalid organization chosen")
}

// set the org ID
organizationID = organizationsResp.GetPayload().Organizations[index].ID

Check failure on line 157 in connect.go

View workflow job for this annotation

GitHub Actions / run-tests / Run Tests

undefined: organizationID

Check failure on line 157 in connect.go

View workflow job for this annotation

GitHub Actions / run-tests / Run Tests

undefined: organizationsResp
organizationName := organizationsResp.GetPayload().Organizations[index].Name

Check failure on line 158 in connect.go

View workflow job for this annotation

GitHub Actions / run-tests / Run Tests

undefined: organizationsResp
c.Ui.Info(fmt.Sprintf("HCP Organization: %s", organizationName))

break
case len(organizationsResp.GetPayload().Organizations) == 1:

Check failure on line 162 in connect.go

View workflow job for this annotation

GitHub Actions / run-tests / Run Tests

undefined: organizationsResp
organization := organizationsResp.GetPayload().Organizations[0]

Check failure on line 163 in connect.go

View workflow job for this annotation

GitHub Actions / run-tests / Run Tests

undefined: organizationsResp
if organization.State != hcprmm.HashicorpCloudResourcemanagerOrganizationOrganizationStateACTIVE.Pointer() {
return "", errors.New("organization is not active")
}
organizationID = organization.ID

Check failure on line 167 in connect.go

View workflow job for this annotation

GitHub Actions / run-tests / Run Tests

undefined: organizationID
c.Ui.Info(fmt.Sprintf("HCP Organization: %s", organization.Name))
}
return organizationID, nil
}

func (c *HCPConnectCommand) connectHandlerFactory() ConnectHandler {
if c.flagNonInteractiveMethod {
return &nonInteractiveConnectHandler{}
}
return &interactiveConnectHandler{}
}

func (c *HCPConnectCommand) Synopsis() string {
return "Authenticate to HCP"
}
Expand Down
62 changes: 54 additions & 8 deletions connect_handler.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
package hcpvaultengine

import (
"errors"
"flag"
httptransport "github.com/go-openapi/runtime/client"
"github.com/hashicorp/hcp-sdk-go/config"
"github.com/hashicorp/hcp-sdk-go/httpclient"
"golang.org/x/oauth2"
)

type AuthStatus struct {
Token string //should this be encrypted? how?
Token oauth2.TokenSource
}

var (
Expand All @@ -10,18 +19,55 @@ var (
)

type ConnectHandler interface {
// should we parse this before [during the Run function common flag parsin] into a struct that's specific to the different handlers?
Connect(args []string) (AuthStatus, error)
Connect(args []string) (*httptransport.Runtime, error)
}

type interactiveConnectHandler struct{}

func (h *interactiveConnectHandler) Connect(args []string) (AuthStatus, error) {
return AuthStatus{}, nil
func (h *interactiveConnectHandler) Connect(_ []string) (*httptransport.Runtime, error) {
// Start a callback listener
// Define timeout: 2 minutes?
return nil, nil
}

type nonInteractiveConnectHandler struct {
flagClientID string
flagSecretID string
}

type nonInteractiveConnectHandler struct{}
func (h *nonInteractiveConnectHandler) Connect(args []string) (*httptransport.Runtime, error) {
f := h.Flags()

if err := f.Parse(args); err != nil {
return nil, err
}

if h.flagClientID == "" || h.flagSecretID == "" {
return nil, errors.New("client ID and Secret ID need to be set in non-interactive mode")
}

opts := []config.HCPConfigOption{config.FromEnv()}
opts = append(opts, config.WithClientCredentials(h.flagClientID, h.flagSecretID))
opts = append(opts, config.WithoutBrowserLogin())

cfg, err := config.NewHCPConfig(opts...)
if err != nil {
return nil, err
}

hcpClient, err := httpclient.New(httpclient.Config{HCPConfig: cfg})
if err != nil {
return nil, err
}

return hcpClient, nil
}

func (h *nonInteractiveConnectHandler) Flags() *flag.FlagSet {
mainSet := flag.NewFlagSet("", flag.ContinueOnError)

mainSet.Var(&stringValue{target: &h.flagClientID}, "client-id", "")
mainSet.Var(&stringValue{target: &h.flagSecretID}, "secret-id", "")

func (h *nonInteractiveConnectHandler) Connect(args []string) (AuthStatus, error) {
return AuthStatus{}, nil
return mainSet
}
14 changes: 14 additions & 0 deletions connect_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package hcpvaultengine

import (
"github.com/mitchellh/cli"
"testing"
)

func Test_getOrganization(t *testing.T) {
mockUi := cli.NewMockUi()
cmd := HCPConnectCommand{Ui: mockUi}

mockRmOrgClient :=
cmd.getOrganization()
}
13 changes: 13 additions & 0 deletions flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,16 @@ func (v *boolValue) Set(s string) error {
*v.target = value
return nil
}

type stringValue struct {
target *string
}

func (v *stringValue) String() string {
return *v.target
}

func (v *stringValue) Set(s string) error {
*v.target = s
return nil
}
53 changes: 50 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,63 @@ module github.com/hashicorp/vault-plugin-scaffolding

go 1.21

require github.com/mitchellh/cli v1.0.0
require (
github.com/go-openapi/runtime v0.25.0
github.com/hashicorp/cloud-resource-manager v1.0.35
github.com/hashicorp/hcp-sdk-go v0.63.0
github.com/mitchellh/cli v1.0.0
golang.org/x/oauth2 v0.12.0
)

require (
github.com/armon/go-radix v1.0.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.14.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/errors v0.20.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/loads v0.21.2 // indirect
github.com/go-openapi/spec v0.20.8 // indirect
github.com/go-openapi/strfmt v0.21.3 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-openapi/validate v0.22.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/cloud-api-grpc-go v0.0.0-20221031155033-ac8d84457361 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/posener/complete v1.1.1 // indirect
golang.org/x/sys v0.6.0 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
go.mongodb.org/mongo-driver v1.11.0 // indirect
go.opentelemetry.io/otel v1.11.1 // indirect
go.opentelemetry.io/otel/trace v1.11.1 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 // indirect
google.golang.org/grpc v1.57.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit cbb4590

Please sign in to comment.