Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[HCP Vault Secrets] Add support for Postgres integration CRUDL #189

Merged
merged 2 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/189.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
vault-secrets: Adds support for creating / updating / reading / deleting Postgres integrations.
```
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-version v1.6.0
github.com/hashicorp/hcl/v2 v2.22.0
github.com/hashicorp/hcp-sdk-go v0.117.0
github.com/hashicorp/hcp-sdk-go v0.121.0
github.com/lithammer/dedent v1.1.0
github.com/manifoldco/promptui v0.9.0
github.com/mitchellh/cli v1.1.5
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/hcl/v2 v2.22.0 h1:hkZ3nCtqeJsDhPRFz5EA9iwcG1hNWGePOTw6oyul12M=
github.com/hashicorp/hcl/v2 v2.22.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
github.com/hashicorp/hcp-sdk-go v0.117.0 h1:7lJpkinpWdsXtejC+X7MdaE/3zhFMweB9Ym3uJ7qFJw=
github.com/hashicorp/hcp-sdk-go v0.117.0/go.mod h1:vQ4fzdL1AmhIAbCw+4zmFe5Hbpajj3NvRWkJoVuxmAk=
github.com/hashicorp/hcp-sdk-go v0.121.0 h1:fDCB0sexSNontS7LLuhF1RJd7eYx1hmFVBFmY4kXU78=
github.com/hashicorp/hcp-sdk-go v0.121.0/go.mod h1:vQ4fzdL1AmhIAbCw+4zmFe5Hbpajj3NvRWkJoVuxmAk=
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
Expand Down
37 changes: 30 additions & 7 deletions internal/commands/vaultsecrets/integrations/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,19 @@ type IntegrationConfig struct {
}

var (
TwilioKeys = []string{"account_sid", "api_key_secret", "api_key_sid"}
MongoKeys = []string{"private_key", "public_key"}
AWSKeys = []string{"access_keys", "federated_workload_identity"}
GCPKeys = []string{"service_account_key", "federated_workload_identity"}
TwilioKeys = []string{"account_sid", "api_key_secret", "api_key_sid"}
MongoKeys = []string{"private_key", "public_key"}
AWSKeys = []string{"access_keys", "federated_workload_identity"}
GCPKeys = []string{"service_account_key", "federated_workload_identity"}
PostgresKeys = []string{"connection_string"}
)

var providerToRequiredFields = map[string][]string{
string(Twilio): TwilioKeys,
string(MongoDBAtlas): MongoKeys,
string(AWS): AWSKeys,
string(GCP): GCPKeys,
string(Postgres): PostgresKeys,
}

var awsAuthMethodsToReqKeys = map[string][]string{
Expand Down Expand Up @@ -201,7 +203,6 @@ func createRun(opts *CreateOpts) error {
req.Body.Name = opts.IntegrationName

_, err = opts.PreviewClient.CreateMongoDBAtlasIntegration(req, nil)

if err != nil {
return fmt.Errorf("failed to create MongoDB Atlas integration: %w", err)
}
Expand All @@ -225,7 +226,6 @@ func createRun(opts *CreateOpts) error {
req.Body.Name = opts.IntegrationName

_, err = opts.PreviewClient.CreateAwsIntegration(req, nil)

if err != nil {
return fmt.Errorf("failed to create AWS integration: %w", err)
}
Expand All @@ -249,11 +249,34 @@ func createRun(opts *CreateOpts) error {
req.Body.Name = opts.IntegrationName

_, err = opts.PreviewClient.CreateGcpIntegration(req, nil)

if err != nil {
return fmt.Errorf("failed to create GCP integration: %w", err)
}

case Postgres:
req := preview_secret_service.NewCreatePostgresIntegrationParamsWithContext(opts.Ctx)
req.OrganizationID = opts.Profile.OrganizationID
req.ProjectID = opts.Profile.ProjectID

detailBytes, err := json.Marshal(internalConfig.Details)
if err != nil {
return fmt.Errorf("error marshaling details config: %w", err)
}

var body preview_models.SecretServiceCreatePostgresIntegrationBody
err = body.UnmarshalBinary(detailBytes)
if err != nil {
return fmt.Errorf("error marshaling details config: %w", err)
}

req.Body = &body
req.Body.Name = opts.IntegrationName

_, err = opts.PreviewClient.CreatePostgresIntegration(req, nil)
if err != nil {
return fmt.Errorf("failed to create MongoDB Atlas integration: %w", err)
}

default:
return fmt.Errorf("unsupported integration provider type")
}
Expand Down
25 changes: 14 additions & 11 deletions internal/commands/vaultsecrets/integrations/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,6 @@ func deleteRun(opts *DeleteOpts) error {
return fmt.Errorf("failed to delete integration: %w", err)
}

fmt.Fprintf(opts.IO.Err(), "%s Successfully deleted integration with name %q\n", opts.IO.ColorScheme().SuccessIcon(), opts.IntegrationName)
return nil

case MongoDBAtlas:
_, err := opts.PreviewClient.DeleteMongoDBAtlasIntegration(&preview_secret_service.DeleteMongoDBAtlasIntegrationParams{
Context: opts.Ctx,
Expand All @@ -113,9 +110,6 @@ func deleteRun(opts *DeleteOpts) error {
return fmt.Errorf("failed to delete integration: %w", err)
}

fmt.Fprintf(opts.IO.Err(), "%s Successfully deleted integration with name %q\n", opts.IO.ColorScheme().SuccessIcon(), opts.IntegrationName)
return nil

case AWS:
_, err := opts.PreviewClient.DeleteAwsIntegration(&preview_secret_service.DeleteAwsIntegrationParams{
Context: opts.Ctx,
Expand All @@ -127,9 +121,6 @@ func deleteRun(opts *DeleteOpts) error {
return fmt.Errorf("failed to delete integration: %w", err)
}

fmt.Fprintf(opts.IO.Err(), "%s Successfully deleted integration with name %q\n", opts.IO.ColorScheme().SuccessIcon(), opts.IntegrationName)
return nil

case GCP:
_, err := opts.PreviewClient.DeleteGcpIntegration(&preview_secret_service.DeleteGcpIntegrationParams{
Context: opts.Ctx,
Expand All @@ -141,10 +132,22 @@ func deleteRun(opts *DeleteOpts) error {
return fmt.Errorf("failed to delete integration: %w", err)
}

fmt.Fprintf(opts.IO.Err(), "%s Successfully deleted integration with name %q\n", opts.IO.ColorScheme().SuccessIcon(), opts.IntegrationName)
return nil
case Postgres:
_, err := opts.PreviewClient.DeletePostgresIntegration(&preview_secret_service.DeletePostgresIntegrationParams{
Context: opts.Ctx,
ProjectID: opts.Profile.ProjectID,
OrganizationID: opts.Profile.OrganizationID,
Name: opts.IntegrationName,
}, nil)
if err != nil {
return fmt.Errorf("failed to delete integration: %w", err)
}

default:
return fmt.Errorf("not a valid integration type")
}

fmt.Fprintf(opts.IO.Err(), "%s Successfully deleted integration with name %q\n", opts.IO.ColorScheme().SuccessIcon(), opts.IntegrationName)

return nil
}
59 changes: 55 additions & 4 deletions internal/commands/vaultsecrets/integrations/displayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ func (m *mongodbDisplayer) DefaultFormat() format.Format {
}

func (m *mongodbDisplayer) Payload() any {

if m.previewMongoDBIntegrations != nil {
return m.previewMongoDBIntegrationsPayload()
}
Expand Down Expand Up @@ -143,7 +142,6 @@ func (a *awsDisplayer) DefaultFormat() format.Format {
}

func (a *awsDisplayer) Payload() any {

if a.previewAwsIntegrations != nil {
return a.previewAwsIntegrationsPayload()
}
Expand Down Expand Up @@ -183,7 +181,6 @@ func (a *awsDisplayer) FieldTemplates() []format.Field {
ValueFormat: "{{ .FederatedWorkloadIdentity.RoleArn }}",
},
}...)

} else {
return append(fields, []format.Field{
{
Expand Down Expand Up @@ -214,7 +211,6 @@ func (g *gcpDisplayer) DefaultFormat() format.Format {
}

func (g *gcpDisplayer) Payload() any {

if g.previewGcpIntegrations != nil {
return g.previewGcpIntegrationsPayload()
}
Expand Down Expand Up @@ -319,3 +315,58 @@ func (g *genericDisplayer) FieldTemplates() []format.Field {
},
}
}

type postgresDisplayer struct {
previewPostgresIntegrations []*preview_models.Secrets20231128PostgresIntegration

single bool
}

func newPostgresDisplayer(single bool, integrations ...*preview_models.Secrets20231128PostgresIntegration) *postgresDisplayer {
return &postgresDisplayer{
previewPostgresIntegrations: integrations,
single: single,
}
}

func (m *postgresDisplayer) DefaultFormat() format.Format {
return format.Table
}

func (m *postgresDisplayer) Payload() any {
if m.previewPostgresIntegrations != nil {
return m.previewPostgresIntegrationsPayload()
}

return nil
}

func (m *postgresDisplayer) previewPostgresIntegrationsPayload() any {
if m.single {
if len(m.previewPostgresIntegrations) != 1 {
return nil
}
return m.previewPostgresIntegrations[0]
}
return m.previewPostgresIntegrations
}

func (m *postgresDisplayer) FieldTemplates() []format.Field {
fields := []format.Field{
{
Name: "Integration Name",
ValueFormat: "{{ .Name }}",
},
}

if m.single {
return append(fields, []format.Field{
{
Name: "Connection String",
ValueFormat: "{{ .StaticCredentialDetails.ConnectionString }}",
},
}...)
} else {
return fields
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const (
MongoDBAtlas IntegrationType = "mongodb-atlas"
AWS IntegrationType = "aws"
GCP IntegrationType = "gcp"
Postgres IntegrationType = "postgres"
)

func NewCmdIntegrations(ctx *cmd.Context) *cmd.Command {
Expand Down
33 changes: 31 additions & 2 deletions internal/commands/vaultsecrets/integrations/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func listRun(opts *ListOpts) error {
for {
resp, err := opts.PreviewClient.ListTwilioIntegrations(params, nil)
if err != nil {
return fmt.Errorf("failed to list twilio integrations: %w", err)
return fmt.Errorf("failed to list Twilio integrations: %w", err)
}

integrations = append(integrations, resp.Payload.Integrations...)
Expand All @@ -146,7 +146,7 @@ func listRun(opts *ListOpts) error {
for {
resp, err := opts.PreviewClient.ListMongoDBAtlasIntegrations(params, nil)
if err != nil {
return fmt.Errorf("failed to list mongo integrations: %w", err)
return fmt.Errorf("failed to list MongoDB Atlas integrations: %w", err)
}

integrations = append(integrations, resp.Payload.Integrations...)
Expand All @@ -157,6 +157,7 @@ func listRun(opts *ListOpts) error {
next := resp.Payload.Pagination.NextPageToken
params.PaginationNextPageToken = &next
}

return opts.Output.Display(newMongoDBDisplayer(false, integrations...))

case AWS:
Expand All @@ -182,6 +183,7 @@ func listRun(opts *ListOpts) error {
next := resp.Payload.Pagination.NextPageToken
params.PaginationNextPageToken = &next
}

return opts.Output.Display(newAwsDisplayer(false, false, integrations...))

case GCP:
Expand All @@ -207,8 +209,35 @@ func listRun(opts *ListOpts) error {
next := resp.Payload.Pagination.NextPageToken
params.PaginationNextPageToken = &next
}

return opts.Output.Display(newGcpDisplayer(false, false, integrations...))

case Postgres:
var integrations []*preview_models.Secrets20231128PostgresIntegration

params := &preview_secret_service.ListPostgresIntegrationsParams{
Context: opts.Ctx,
ProjectID: opts.Profile.ProjectID,
OrganizationID: opts.Profile.OrganizationID,
}

for {
resp, err := opts.PreviewClient.ListPostgresIntegrations(params, nil)
if err != nil {
return fmt.Errorf("failed to list Postgres integrations: %w", err)
}

integrations = append(integrations, resp.Payload.Integrations...)
if resp.Payload.Pagination == nil || resp.Payload.Pagination.NextPageToken == "" {
break
}

next := resp.Payload.Pagination.NextPageToken
params.PaginationNextPageToken = &next
}

return opts.Output.Display(newPostgresDisplayer(false, integrations...))

default:
return fmt.Errorf("not a valid integration type")
}
Expand Down
13 changes: 13 additions & 0 deletions internal/commands/vaultsecrets/integrations/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,19 @@ func readRun(opts *ReadOpts) error {

return opts.Output.Display(newGcpDisplayer(true, resp.Payload.Integration.FederatedWorkloadIdentity != nil, resp.Payload.Integration))

case Postgres:
resp, err := opts.PreviewClient.GetPostgresIntegration(&preview_secret_service.GetPostgresIntegrationParams{
Context: opts.Ctx,
ProjectID: opts.Profile.ProjectID,
OrganizationID: opts.Profile.OrganizationID,
Name: opts.IntegrationName,
}, nil)
if err != nil {
return fmt.Errorf("failed to read integration: %w", err)
}

return opts.Output.Display(newPostgresDisplayer(true, resp.Payload.Integration))

default:
return fmt.Errorf("not a valid integration type")
}
Expand Down
26 changes: 23 additions & 3 deletions internal/commands/vaultsecrets/integrations/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ func updateRun(opts *UpdateOpts) error {
req.Body = &mongoDBBody

_, err = opts.PreviewClient.UpdateMongoDBAtlasIntegration(req, nil)

if err != nil {
return fmt.Errorf("failed to update MongoDB Atlas integration: %w", err)
}
Expand All @@ -176,7 +175,6 @@ func updateRun(opts *UpdateOpts) error {
req.Body = &awsBody

_, err = opts.PreviewClient.UpdateAwsIntegration(req, nil)

if err != nil {
return fmt.Errorf("failed to update AWS integration: %w", err)
}
Expand All @@ -200,10 +198,32 @@ func updateRun(opts *UpdateOpts) error {
req.Body = &gcpBody

_, err = opts.PreviewClient.UpdateGcpIntegration(req, nil)

if err != nil {
return fmt.Errorf("failed to update GCP integration: %w", err)
}

case Postgres:
req := preview_secret_service.NewUpdatePostgresIntegrationParamsWithContext(opts.Ctx)
req.OrganizationID = opts.Profile.OrganizationID
req.ProjectID = opts.Profile.ProjectID
req.Name = opts.IntegrationName

var body preview_models.SecretServiceUpdatePostgresIntegrationBody
detailBytes, err := json.Marshal(internalConfig.Details)
if err != nil {
return fmt.Errorf("error marshaling details config: %w", err)
}

err = body.UnmarshalBinary(detailBytes)
if err != nil {
return fmt.Errorf("error marshaling details config: %w", err)
}
req.Body = &body

_, err = opts.PreviewClient.UpdatePostgresIntegration(req, nil)
if err != nil {
return fmt.Errorf("failed to update MongoDB Atlas integration: %w", err)
}
}

fmt.Fprintln(opts.IO.Err())
Expand Down
Loading