Skip to content

Commit 63727a5

Browse files
authored
Add support for GitLab integration (#1189)
1 parent 92c55ac commit 63727a5

File tree

7 files changed

+209
-3
lines changed

7 files changed

+209
-3
lines changed

.changelog/1189.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:improvement
2+
Add support for GitLab integration in HVS.
3+
```

docs/resources/vault_secrets_integration.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ resource "hcp_vault_secrets_integration" "example_twilio" {
115115
- `confluent_static_credentials` (Attributes) Confluent API key used to authenticate for cloud apis. (see [below for nested schema](#nestedatt--confluent_static_credentials))
116116
- `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))
117117
- `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))
118+
- `gitlab_access` (Attributes) GitLab access token used to authenticate against the target GitLab account. (see [below for nested schema](#nestedatt--gitlab_access))
118119
- `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))
119120
- `project_id` (String) HCP project ID that owns the HCP Vault Secrets integration. Inferred from the provider configuration if omitted.
120121
- `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))
@@ -194,6 +195,14 @@ Read-Only:
194195
- `project_id` (String) GCP project ID corresponding to the service account key.
195196

196197

198+
<a id="nestedatt--gitlab_access"></a>
199+
### Nested Schema for `gitlab_access`
200+
201+
Required:
202+
203+
- `token` (String, Sensitive) Access token used to authenticate against the target GitLab account.
204+
205+
197206
<a id="nestedatt--mongodb_atlas_static_credentials"></a>
198207
### Nested Schema for `mongodb_atlas_static_credentials`
199208

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ require (
1111
github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637
1212
github.com/hashicorp/go-uuid v1.0.3
1313
github.com/hashicorp/go-version v1.7.0
14-
github.com/hashicorp/hcp-sdk-go v0.129.0
14+
github.com/hashicorp/hcp-sdk-go v0.134.0
1515
github.com/hashicorp/terraform-plugin-docs v0.19.4
1616
github.com/hashicorp/terraform-plugin-framework v1.5.0
1717
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ github.com/hashicorp/hc-install v0.7.0 h1:Uu9edVqjKQxxuD28mR5TikkKDd/p55S8vzPC16
124124
github.com/hashicorp/hc-install v0.7.0/go.mod h1:ELmmzZlGnEcqoUMKUuykHaPCIR1sYLYX+KSggWSKZuA=
125125
github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI=
126126
github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE=
127-
github.com/hashicorp/hcp-sdk-go v0.129.0 h1:lxH6eup5kmbGVukg+8p4KjGfb1VoR+vdf3ivVT00ams=
128-
github.com/hashicorp/hcp-sdk-go v0.129.0/go.mod h1:vQ4fzdL1AmhIAbCw+4zmFe5Hbpajj3NvRWkJoVuxmAk=
127+
github.com/hashicorp/hcp-sdk-go v0.134.0 h1:P2c6TEtjGEXjw1cvsOhxitr+1WjSrlLOuK31GtZff/Q=
128+
github.com/hashicorp/hcp-sdk-go v0.134.0/go.mod h1:vQ4fzdL1AmhIAbCw+4zmFe5Hbpajj3NvRWkJoVuxmAk=
129129
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
130130
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
131131
github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ=

internal/provider/vaultsecrets/resource_vault_secrets_integration.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,14 @@ var exactlyOneIntegrationTypeFieldsValidator = objectvalidator.ExactlyOneOf(
3838
path.MatchRoot("gcp_federated_workload_identity"),
3939
path.MatchRoot("mongodb_atlas_static_credentials"),
4040
path.MatchRoot("twilio_static_credentials"),
41+
path.MatchRoot("gitlab_access"),
4142
}...,
4243
)
4344

45+
type gitlabAccessDetails struct {
46+
AccessToken types.String `tfsdk:"token"`
47+
}
48+
4449
type Integration struct {
4550
// Input fields
4651
ProjectID types.String `tfsdk:"project_id"`
@@ -58,6 +63,7 @@ type Integration struct {
5863
GcpFederatedWorkloadIdentity types.Object `tfsdk:"gcp_federated_workload_identity"`
5964
MongoDBAtlasStaticCredentials types.Object `tfsdk:"mongodb_atlas_static_credentials"`
6065
TwilioStaticCredentials types.Object `tfsdk:"twilio_static_credentials"`
66+
GitLabAccess types.Object `tfsdk:"gitlab_access"`
6167

6268
// Computed fields
6369
OrganizationID types.String `tfsdk:"organization_id"`
@@ -75,6 +81,7 @@ type Integration struct {
7581
gcpFederatedWorkloadIdentity *secretmodels.Secrets20231128GcpFederatedWorkloadIdentityRequest `tfsdk:"-"`
7682
mongoDBAtlasStaticCredentials *secretmodels.Secrets20231128MongoDBAtlasStaticCredentialsRequest `tfsdk:"-"`
7783
twilioStaticCredentials *secretmodels.Secrets20231128TwilioStaticCredentialsRequest `tfsdk:"-"`
84+
gitlabAccess *secretmodels.Secrets20231128GitlabAccessTokenRequest `tfsdk:"-"`
7885
}
7986

8087
var _ resource.Resource = &resourceVaultSecretsIntegration{}
@@ -279,6 +286,17 @@ func (r *resourceVaultSecretsIntegration) Schema(_ context.Context, _ resource.S
279286
exactlyOneIntegrationTypeFieldsValidator,
280287
},
281288
},
289+
"gitlab_access": schema.SingleNestedAttribute{
290+
Description: "GitLab access token used to authenticate against the target GitLab account.",
291+
Optional: true,
292+
Attributes: map[string]schema.Attribute{
293+
"token": schema.StringAttribute{
294+
Description: "Access token used to authenticate against the target GitLab account.",
295+
Required: true,
296+
Sensitive: true,
297+
},
298+
},
299+
},
282300
}
283301

284302
maps.Copy(attributes, locationAttributes)
@@ -352,6 +370,7 @@ func (r *resourceVaultSecretsIntegration) Create(ctx context.Context, req resour
352370
GcpFederatedWorkloadIdentity: integration.gcpFederatedWorkloadIdentity,
353371
MongoDbAtlasStaticCredentials: integration.mongoDBAtlasStaticCredentials,
354372
TwilioStaticCredentials: integration.twilioStaticCredentials,
373+
GitlabAccessToken: integration.gitlabAccess,
355374
},
356375
OrganizationID: integration.OrganizationID.ValueString(),
357376
ProjectID: integration.ProjectID.ValueString(),
@@ -386,6 +405,7 @@ func (r *resourceVaultSecretsIntegration) Update(ctx context.Context, req resour
386405
GcpFederatedWorkloadIdentity: integration.gcpFederatedWorkloadIdentity,
387406
MongoDbAtlasStaticCredentials: integration.mongoDBAtlasStaticCredentials,
388407
TwilioStaticCredentials: integration.twilioStaticCredentials,
408+
GitlabAccessToken: integration.gitlabAccess,
389409
},
390410
Name: integration.Name.ValueString(),
391411
OrganizationID: integration.OrganizationID.ValueString(),
@@ -568,6 +588,18 @@ func (i *Integration) initModel(ctx context.Context, orgID, projID string) diag.
568588
}
569589
}
570590

591+
if !i.GitLabAccess.IsNull() {
592+
gad := gitlabAccessDetails{}
593+
diags = i.GitLabAccess.As(ctx, &gad, basetypes.ObjectAsOptions{})
594+
if diags.HasError() {
595+
return diags
596+
}
597+
598+
i.gitlabAccess = &secretmodels.Secrets20231128GitlabAccessTokenRequest{
599+
Token: gad.AccessToken.ValueString(),
600+
}
601+
}
602+
571603
return diag.Diagnostics{}
572604
}
573605

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

754+
if integrationModel.GitlabAccessToken != nil {
755+
accessToken := ""
756+
if i.gitlabAccess != nil {
757+
accessToken = i.gitlabAccess.Token
758+
}
759+
760+
i.GitLabAccess, diags = types.ObjectValue(i.GitLabAccess.AttributeTypes(ctx), map[string]attr.Value{
761+
"token": types.StringValue(accessToken),
762+
})
763+
if diags.HasError() {
764+
return diags
765+
}
766+
}
767+
722768
return diags
723769
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package vaultsecrets_test
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"testing"
7+
8+
"github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/stable/2023-11-28/client/secret_service"
9+
secretmodels "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/stable/2023-11-28/models"
10+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
11+
"github.com/hashicorp/terraform-plugin-testing/terraform"
12+
"github.com/hashicorp/terraform-provider-hcp/internal/clients"
13+
"github.com/hashicorp/terraform-provider-hcp/internal/provider/acctest"
14+
)
15+
16+
func TestAccVaultSecretsResourceIntegrationGitLab(t *testing.T) {
17+
accessToken := checkRequiredEnvVarOrFail(t, "GITLAB_ACCESS_TOKEN")
18+
19+
integrationName1 := generateRandomSlug()
20+
integrationName2 := generateRandomSlug()
21+
22+
resource.Test(t, resource.TestCase{
23+
PreCheck: func() { acctest.PreCheck(t) },
24+
ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories,
25+
Steps: []resource.TestStep{
26+
// Create initial integration with access token
27+
{
28+
Config: gitlabConfig(integrationName1, accessToken),
29+
Check: resource.ComposeTestCheckFunc(
30+
gitlabCheckFuncs(integrationName1, accessToken)...,
31+
),
32+
},
33+
// Changing the name forces a recreation
34+
{
35+
Config: gitlabConfig(integrationName2, accessToken),
36+
Check: resource.ComposeTestCheckFunc(
37+
gitlabCheckFuncs(integrationName2, accessToken)...,
38+
),
39+
},
40+
// Deleting the integration out of band causes a recreation
41+
{
42+
PreConfig: func() {
43+
t.Helper()
44+
client := acctest.HCPClients(t)
45+
_, err := client.VaultSecrets.DeleteIntegration(&secret_service.DeleteIntegrationParams{
46+
Name: integrationName2,
47+
OrganizationID: client.Config.OrganizationID,
48+
ProjectID: client.Config.ProjectID,
49+
}, nil)
50+
if err != nil {
51+
t.Fatal(err)
52+
}
53+
},
54+
Config: gitlabConfig(integrationName2, accessToken),
55+
Check: resource.ComposeTestCheckFunc(
56+
gitlabCheckFuncs(integrationName2, accessToken)...,
57+
),
58+
PlanOnly: true,
59+
ExpectNonEmptyPlan: true,
60+
},
61+
// Pre-existing integration can be imported
62+
{
63+
PreConfig: func() {
64+
t.Helper()
65+
client := acctest.HCPClients(t)
66+
_, err := client.VaultSecrets.CreateIntegration(&secret_service.CreateIntegrationParams{
67+
Body: &secretmodels.SecretServiceCreateIntegrationBody{
68+
Name: integrationName2,
69+
Provider: "gitlab",
70+
Capabilities: []*secretmodels.Secrets20231128Capability{
71+
secretmodels.Secrets20231128CapabilitySYNC.Pointer(),
72+
},
73+
GitlabAccessToken: &secretmodels.Secrets20231128GitlabAccessTokenRequest{
74+
Token: accessToken,
75+
},
76+
},
77+
OrganizationID: client.Config.OrganizationID,
78+
ProjectID: client.Config.ProjectID,
79+
}, nil)
80+
if err != nil {
81+
t.Fatal(err)
82+
}
83+
},
84+
Config: gitlabConfig(integrationName2, accessToken),
85+
Check: resource.ComposeTestCheckFunc(
86+
gitlabCheckFuncs(integrationName2, accessToken)...,
87+
),
88+
ResourceName: "hcp_vault_secrets_integration.acc_test",
89+
ImportStateId: integrationName2,
90+
ImportState: true,
91+
},
92+
},
93+
CheckDestroy: func(_ *terraform.State) error {
94+
if integrationExists(t, integrationName1) {
95+
return fmt.Errorf("test GitLab integration %s was not destroyed", integrationName1)
96+
}
97+
if integrationExists(t, integrationName2) {
98+
return fmt.Errorf("test GitLab integration %s was not destroyed", integrationName2)
99+
}
100+
return nil
101+
},
102+
})
103+
}
104+
105+
func gitlabCheckFuncs(integrationName, accessToken string) []resource.TestCheckFunc {
106+
return []resource.TestCheckFunc{
107+
resource.TestCheckResourceAttrSet("hcp_vault_secrets_integration.acc_test", "organization_id"),
108+
resource.TestCheckResourceAttr("hcp_vault_secrets_integration.acc_test", "project_id", os.Getenv("HCP_PROJECT_ID")),
109+
resource.TestCheckResourceAttrSet("hcp_vault_secrets_integration.acc_test", "resource_id"),
110+
resource.TestCheckResourceAttrSet("hcp_vault_secrets_integration.acc_test", "resource_name"),
111+
resource.TestCheckResourceAttr("hcp_vault_secrets_integration.acc_test", "name", integrationName),
112+
resource.TestCheckResourceAttr("hcp_vault_secrets_integration.acc_test", "capabilities.#", "1"),
113+
resource.TestCheckResourceAttr("hcp_vault_secrets_integration.acc_test", "capabilities.0", "SYNC"),
114+
resource.TestCheckResourceAttr("hcp_vault_secrets_integration.acc_test", "provider_type", "gitlab"),
115+
resource.TestCheckResourceAttr("hcp_vault_secrets_integration.acc_test", "gitlab_access.token", accessToken),
116+
}
117+
}
118+
119+
func gitlabConfig(integrationName, accessToken string) string {
120+
return fmt.Sprintf(`
121+
resource "hcp_vault_secrets_integration" "acc_test" {
122+
name = %q
123+
capabilities = ["SYNC"]
124+
provider_type = "gitlab"
125+
gitlab_access = {
126+
token = %q
127+
}
128+
}`, integrationName, accessToken)
129+
}
130+
131+
func integrationExists(t *testing.T, name string) bool {
132+
t.Helper()
133+
client := acctest.HCPClients(t)
134+
135+
response, err := client.VaultSecrets.GetIntegration(
136+
secret_service.NewGetIntegrationParamsWithContext(ctx).
137+
WithOrganizationID(client.Config.OrganizationID).
138+
WithProjectID(client.Config.ProjectID).
139+
WithName(name), nil)
140+
141+
if err != nil && !clients.IsResponseCodeNotFound(err) {
142+
t.Fatal(err)
143+
}
144+
145+
return !clients.IsResponseCodeNotFound(err) && response != nil && response.Payload != nil && response.Payload.Integration != nil
146+
}

internal/provider/vaultsecrets/vault_secrets_utils.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const (
2929
ProviderGCP Provider = "gcp"
3030
ProviderMongoDBAtlas Provider = "mongodb-atlas"
3131
ProviderTwilio Provider = "twilio"
32+
ProviderGitLab Provider = "gitlab"
3233
)
3334

3435
func (p Provider) String() string {
@@ -43,6 +44,7 @@ func ProviderStrings() []string {
4344
string(ProviderGCP),
4445
string(ProviderMongoDBAtlas),
4546
string(ProviderTwilio),
47+
string(ProviderGitLab),
4648
}
4749
}
4850

0 commit comments

Comments
 (0)