Skip to content

Commit

Permalink
Add support for GitLab integration (#1189)
Browse files Browse the repository at this point in the history
  • Loading branch information
rodrigo-hcp authored Jan 29, 2025
1 parent 92c55ac commit 63727a5
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 3 deletions.
3 changes: 3 additions & 0 deletions .changelog/1189.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
Add support for GitLab integration in HVS.
```
9 changes: 9 additions & 0 deletions docs/resources/vault_secrets_integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ resource "hcp_vault_secrets_integration" "example_twilio" {
- `confluent_static_credentials` (Attributes) Confluent API key used to authenticate for cloud apis. (see [below for nested schema](#nestedatt--confluent_static_credentials))
- `gcp_federated_workload_identity` (Attributes) (Recommended) Federated identity configuration to authenticate against the target GCP project. Cannot be used with `service_account_key`. (see [below for nested schema](#nestedatt--gcp_federated_workload_identity))
- `gcp_service_account_key` (Attributes) GCP service account key used to authenticate against the target GCP project. Cannot be used with `federated_workload_identity`. (see [below for nested schema](#nestedatt--gcp_service_account_key))
- `gitlab_access` (Attributes) GitLab access token used to authenticate against the target GitLab account. (see [below for nested schema](#nestedatt--gitlab_access))
- `mongodb_atlas_static_credentials` (Attributes) MongoDB Atlas API key used to authenticate against the target project. (see [below for nested schema](#nestedatt--mongodb_atlas_static_credentials))
- `project_id` (String) HCP project ID that owns the HCP Vault Secrets integration. Inferred from the provider configuration if omitted.
- `twilio_static_credentials` (Attributes) Twilio API key parts used to authenticate against the target Twilio account. (see [below for nested schema](#nestedatt--twilio_static_credentials))
Expand Down Expand Up @@ -194,6 +195,14 @@ Read-Only:
- `project_id` (String) GCP project ID corresponding to the service account key.


<a id="nestedatt--gitlab_access"></a>
### Nested Schema for `gitlab_access`

Required:

- `token` (String, Sensitive) Access token used to authenticate against the target GitLab account.


<a id="nestedatt--mongodb_atlas_static_credentials"></a>
### Nested Schema for `mongodb_atlas_static_credentials`

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require (
github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637
github.com/hashicorp/go-uuid v1.0.3
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/hcp-sdk-go v0.129.0
github.com/hashicorp/hcp-sdk-go v0.134.0
github.com/hashicorp/terraform-plugin-docs v0.19.4
github.com/hashicorp/terraform-plugin-framework v1.5.0
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ github.com/hashicorp/hc-install v0.7.0 h1:Uu9edVqjKQxxuD28mR5TikkKDd/p55S8vzPC16
github.com/hashicorp/hc-install v0.7.0/go.mod h1:ELmmzZlGnEcqoUMKUuykHaPCIR1sYLYX+KSggWSKZuA=
github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI=
github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE=
github.com/hashicorp/hcp-sdk-go v0.129.0 h1:lxH6eup5kmbGVukg+8p4KjGfb1VoR+vdf3ivVT00ams=
github.com/hashicorp/hcp-sdk-go v0.129.0/go.mod h1:vQ4fzdL1AmhIAbCw+4zmFe5Hbpajj3NvRWkJoVuxmAk=
github.com/hashicorp/hcp-sdk-go v0.134.0 h1:P2c6TEtjGEXjw1cvsOhxitr+1WjSrlLOuK31GtZff/Q=
github.com/hashicorp/hcp-sdk-go v0.134.0/go.mod h1:vQ4fzdL1AmhIAbCw+4zmFe5Hbpajj3NvRWkJoVuxmAk=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,14 @@ var exactlyOneIntegrationTypeFieldsValidator = objectvalidator.ExactlyOneOf(
path.MatchRoot("gcp_federated_workload_identity"),
path.MatchRoot("mongodb_atlas_static_credentials"),
path.MatchRoot("twilio_static_credentials"),
path.MatchRoot("gitlab_access"),
}...,
)

type gitlabAccessDetails struct {
AccessToken types.String `tfsdk:"token"`
}

type Integration struct {
// Input fields
ProjectID types.String `tfsdk:"project_id"`
Expand All @@ -58,6 +63,7 @@ type Integration struct {
GcpFederatedWorkloadIdentity types.Object `tfsdk:"gcp_federated_workload_identity"`
MongoDBAtlasStaticCredentials types.Object `tfsdk:"mongodb_atlas_static_credentials"`
TwilioStaticCredentials types.Object `tfsdk:"twilio_static_credentials"`
GitLabAccess types.Object `tfsdk:"gitlab_access"`

// Computed fields
OrganizationID types.String `tfsdk:"organization_id"`
Expand All @@ -75,6 +81,7 @@ type Integration struct {
gcpFederatedWorkloadIdentity *secretmodels.Secrets20231128GcpFederatedWorkloadIdentityRequest `tfsdk:"-"`
mongoDBAtlasStaticCredentials *secretmodels.Secrets20231128MongoDBAtlasStaticCredentialsRequest `tfsdk:"-"`
twilioStaticCredentials *secretmodels.Secrets20231128TwilioStaticCredentialsRequest `tfsdk:"-"`
gitlabAccess *secretmodels.Secrets20231128GitlabAccessTokenRequest `tfsdk:"-"`
}

var _ resource.Resource = &resourceVaultSecretsIntegration{}
Expand Down Expand Up @@ -279,6 +286,17 @@ func (r *resourceVaultSecretsIntegration) Schema(_ context.Context, _ resource.S
exactlyOneIntegrationTypeFieldsValidator,
},
},
"gitlab_access": schema.SingleNestedAttribute{
Description: "GitLab access token used to authenticate against the target GitLab account.",
Optional: true,
Attributes: map[string]schema.Attribute{
"token": schema.StringAttribute{
Description: "Access token used to authenticate against the target GitLab account.",
Required: true,
Sensitive: true,
},
},
},
}

maps.Copy(attributes, locationAttributes)
Expand Down Expand Up @@ -352,6 +370,7 @@ func (r *resourceVaultSecretsIntegration) Create(ctx context.Context, req resour
GcpFederatedWorkloadIdentity: integration.gcpFederatedWorkloadIdentity,
MongoDbAtlasStaticCredentials: integration.mongoDBAtlasStaticCredentials,
TwilioStaticCredentials: integration.twilioStaticCredentials,
GitlabAccessToken: integration.gitlabAccess,
},
OrganizationID: integration.OrganizationID.ValueString(),
ProjectID: integration.ProjectID.ValueString(),
Expand Down Expand Up @@ -386,6 +405,7 @@ func (r *resourceVaultSecretsIntegration) Update(ctx context.Context, req resour
GcpFederatedWorkloadIdentity: integration.gcpFederatedWorkloadIdentity,
MongoDbAtlasStaticCredentials: integration.mongoDBAtlasStaticCredentials,
TwilioStaticCredentials: integration.twilioStaticCredentials,
GitlabAccessToken: integration.gitlabAccess,
},
Name: integration.Name.ValueString(),
OrganizationID: integration.OrganizationID.ValueString(),
Expand Down Expand Up @@ -568,6 +588,18 @@ func (i *Integration) initModel(ctx context.Context, orgID, projID string) diag.
}
}

if !i.GitLabAccess.IsNull() {
gad := gitlabAccessDetails{}
diags = i.GitLabAccess.As(ctx, &gad, basetypes.ObjectAsOptions{})
if diags.HasError() {
return diags
}

i.gitlabAccess = &secretmodels.Secrets20231128GitlabAccessTokenRequest{
Token: gad.AccessToken.ValueString(),
}
}

return diag.Diagnostics{}
}

Expand Down Expand Up @@ -719,5 +751,19 @@ func (i *Integration) fromModel(ctx context.Context, orgID, projID string, model
}
}

if integrationModel.GitlabAccessToken != nil {
accessToken := ""
if i.gitlabAccess != nil {
accessToken = i.gitlabAccess.Token
}

i.GitLabAccess, diags = types.ObjectValue(i.GitLabAccess.AttributeTypes(ctx), map[string]attr.Value{
"token": types.StringValue(accessToken),
})
if diags.HasError() {
return diags
}
}

return diags
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package vaultsecrets_test

import (
"fmt"
"os"
"testing"

"github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/stable/2023-11-28/client/secret_service"
secretmodels "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/stable/2023-11-28/models"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/hashicorp/terraform-provider-hcp/internal/clients"
"github.com/hashicorp/terraform-provider-hcp/internal/provider/acctest"
)

func TestAccVaultSecretsResourceIntegrationGitLab(t *testing.T) {
accessToken := checkRequiredEnvVarOrFail(t, "GITLAB_ACCESS_TOKEN")

integrationName1 := generateRandomSlug()
integrationName2 := generateRandomSlug()

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create initial integration with access token
{
Config: gitlabConfig(integrationName1, accessToken),
Check: resource.ComposeTestCheckFunc(
gitlabCheckFuncs(integrationName1, accessToken)...,
),
},
// Changing the name forces a recreation
{
Config: gitlabConfig(integrationName2, accessToken),
Check: resource.ComposeTestCheckFunc(
gitlabCheckFuncs(integrationName2, accessToken)...,
),
},
// Deleting the integration out of band causes a recreation
{
PreConfig: func() {
t.Helper()
client := acctest.HCPClients(t)
_, err := client.VaultSecrets.DeleteIntegration(&secret_service.DeleteIntegrationParams{
Name: integrationName2,
OrganizationID: client.Config.OrganizationID,
ProjectID: client.Config.ProjectID,
}, nil)
if err != nil {
t.Fatal(err)
}
},
Config: gitlabConfig(integrationName2, accessToken),
Check: resource.ComposeTestCheckFunc(
gitlabCheckFuncs(integrationName2, accessToken)...,
),
PlanOnly: true,
ExpectNonEmptyPlan: true,
},
// Pre-existing integration can be imported
{
PreConfig: func() {
t.Helper()
client := acctest.HCPClients(t)
_, err := client.VaultSecrets.CreateIntegration(&secret_service.CreateIntegrationParams{
Body: &secretmodels.SecretServiceCreateIntegrationBody{
Name: integrationName2,
Provider: "gitlab",
Capabilities: []*secretmodels.Secrets20231128Capability{
secretmodels.Secrets20231128CapabilitySYNC.Pointer(),
},
GitlabAccessToken: &secretmodels.Secrets20231128GitlabAccessTokenRequest{
Token: accessToken,
},
},
OrganizationID: client.Config.OrganizationID,
ProjectID: client.Config.ProjectID,
}, nil)
if err != nil {
t.Fatal(err)
}
},
Config: gitlabConfig(integrationName2, accessToken),
Check: resource.ComposeTestCheckFunc(
gitlabCheckFuncs(integrationName2, accessToken)...,
),
ResourceName: "hcp_vault_secrets_integration.acc_test",
ImportStateId: integrationName2,
ImportState: true,
},
},
CheckDestroy: func(_ *terraform.State) error {
if integrationExists(t, integrationName1) {
return fmt.Errorf("test GitLab integration %s was not destroyed", integrationName1)
}
if integrationExists(t, integrationName2) {
return fmt.Errorf("test GitLab integration %s was not destroyed", integrationName2)
}
return nil
},
})
}

func gitlabCheckFuncs(integrationName, accessToken string) []resource.TestCheckFunc {
return []resource.TestCheckFunc{
resource.TestCheckResourceAttrSet("hcp_vault_secrets_integration.acc_test", "organization_id"),
resource.TestCheckResourceAttr("hcp_vault_secrets_integration.acc_test", "project_id", os.Getenv("HCP_PROJECT_ID")),
resource.TestCheckResourceAttrSet("hcp_vault_secrets_integration.acc_test", "resource_id"),
resource.TestCheckResourceAttrSet("hcp_vault_secrets_integration.acc_test", "resource_name"),
resource.TestCheckResourceAttr("hcp_vault_secrets_integration.acc_test", "name", integrationName),
resource.TestCheckResourceAttr("hcp_vault_secrets_integration.acc_test", "capabilities.#", "1"),
resource.TestCheckResourceAttr("hcp_vault_secrets_integration.acc_test", "capabilities.0", "SYNC"),
resource.TestCheckResourceAttr("hcp_vault_secrets_integration.acc_test", "provider_type", "gitlab"),
resource.TestCheckResourceAttr("hcp_vault_secrets_integration.acc_test", "gitlab_access.token", accessToken),
}
}

func gitlabConfig(integrationName, accessToken string) string {
return fmt.Sprintf(`
resource "hcp_vault_secrets_integration" "acc_test" {
name = %q
capabilities = ["SYNC"]
provider_type = "gitlab"
gitlab_access = {
token = %q
}
}`, integrationName, accessToken)
}

func integrationExists(t *testing.T, name string) bool {
t.Helper()
client := acctest.HCPClients(t)

response, err := client.VaultSecrets.GetIntegration(
secret_service.NewGetIntegrationParamsWithContext(ctx).
WithOrganizationID(client.Config.OrganizationID).
WithProjectID(client.Config.ProjectID).
WithName(name), nil)

if err != nil && !clients.IsResponseCodeNotFound(err) {
t.Fatal(err)
}

return !clients.IsResponseCodeNotFound(err) && response != nil && response.Payload != nil && response.Payload.Integration != nil
}
2 changes: 2 additions & 0 deletions internal/provider/vaultsecrets/vault_secrets_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const (
ProviderGCP Provider = "gcp"
ProviderMongoDBAtlas Provider = "mongodb-atlas"
ProviderTwilio Provider = "twilio"
ProviderGitLab Provider = "gitlab"
)

func (p Provider) String() string {
Expand All @@ -43,6 +44,7 @@ func ProviderStrings() []string {
string(ProviderGCP),
string(ProviderMongoDBAtlas),
string(ProviderTwilio),
string(ProviderGitLab),
}
}

Expand Down

0 comments on commit 63727a5

Please sign in to comment.