From 2945c4de7ecdb4b6d5480c547402ae10a8877d64 Mon Sep 17 00:00:00 2001 From: Douglas Parsons Date: Sat, 13 May 2023 17:13:41 +0100 Subject: [PATCH 1/5] Add support for shared environment variables --- Taskfile.yml | 4 +- client/shared_environment_variable_create.go | 72 +++++ client/shared_environment_variable_delete.go | 39 +++ client/shared_environment_variable_get.go | 31 ++ client/shared_environment_variable_update.go | 61 ++++ docs/resources/shared_environment_variable.md | 70 +++++ .../import.sh | 4 + .../resource.tf | 19 ++ vercel/provider.go | 1 + vercel/resource_project.go | 1 + .../resource_project_environment_variable.go | 5 + ...urce_project_environment_variable_model.go | 4 +- vercel/resource_project_model.go | 4 +- .../resource_shared_environment_variable.go | 280 ++++++++++++++++++ ...ource_shared_environment_variable_model.go | 85 ++++++ ...source_shared_environment_variable_test.go | 154 ++++++++++ vercel/validator_string_set_min_count.go | 40 +++ 17 files changed, 868 insertions(+), 6 deletions(-) create mode 100644 client/shared_environment_variable_create.go create mode 100644 client/shared_environment_variable_delete.go create mode 100644 client/shared_environment_variable_get.go create mode 100644 client/shared_environment_variable_update.go create mode 100644 docs/resources/shared_environment_variable.md create mode 100644 examples/resources/vercel_shared_environment_variable/import.sh create mode 100644 examples/resources/vercel_shared_environment_variable/resource.tf create mode 100644 vercel/resource_shared_environment_variable.go create mode 100644 vercel/resource_shared_environment_variable_model.go create mode 100644 vercel/resource_shared_environment_variable_test.go create mode 100644 vercel/validator_string_set_min_count.go diff --git a/Taskfile.yml b/Taskfile.yml index a40e2e44..076a96b9 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -25,7 +25,7 @@ tasks: cmds: - go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs@v0.7.0 status: - - which staticcheck + - which tfplugindocs docs: desc: "Update the docs generated from description fields" @@ -46,7 +46,7 @@ tasks: install-staticcheck: desc: "Install the staticheck tool" cmds: - - go install honnef.co/go/tools/cmd/staticcheck@2022.1.3 + - go install honnef.co/go/tools/cmd/staticcheck@2023.1 status: - which staticcheck diff --git a/client/shared_environment_variable_create.go b/client/shared_environment_variable_create.go new file mode 100644 index 00000000..6919a47f --- /dev/null +++ b/client/shared_environment_variable_create.go @@ -0,0 +1,72 @@ +package client + +import ( + "context" + "fmt" + "net/http" + "strings" + + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +type SharedEnvironmentVariableResponse struct { + Key string `json:"key"` + TeamID string `json:"ownerId"` + ID string `json:"id,omitempty"` + Value string `json:"value"` + Type string `json:"type"` + Target []string `json:"target"` + ProjectIDs []string `json:"projectId"` +} + +type SharedEnvVarRequest struct { + Key string `json:"key"` + Value string `json:"value"` +} + +type SharedEnvironmentVariableRequest struct { + Type string `json:"type"` + ProjectIDs []string `json:"projectId"` + Target []string `json:"target"` + EnvironmentVariables []SharedEnvVarRequest `json:"evs"` +} + +type CreateSharedEnvironmentVariableRequest struct { + EnvironmentVariable SharedEnvironmentVariableRequest + TeamID string +} + +// CreateSharedEnvironmentVariable will create a brand new shared environment variable if one does not exist. +func (c *Client) CreateSharedEnvironmentVariable(ctx context.Context, request CreateSharedEnvironmentVariableRequest) (e SharedEnvironmentVariableResponse, err error) { + url := fmt.Sprintf("%s/v1/env", c.baseURL) + if c.teamID(request.TeamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID)) + } + payload := string(mustMarshal(request.EnvironmentVariable)) + req, err := http.NewRequestWithContext( + ctx, + "POST", + url, + strings.NewReader(payload), + ) + if err != nil { + return e, err + } + tflog.Trace(ctx, "creating shared environment variable", map[string]interface{}{ + "url": url, + "payload": payload, + }) + var response struct { + Created []SharedEnvironmentVariableResponse `json:"created"` + } + err = c.doRequest(req, &response) + if err != nil { + return e, err + } + if len(response.Created) != 1 { + return e, fmt.Errorf("expected 1 environment variable to be created, got %d", len(response.Created)) + } + // Override the value, as it returns the encrypted value. + response.Created[0].Value = request.EnvironmentVariable.EnvironmentVariables[0].Value + return response.Created[0], err +} diff --git a/client/shared_environment_variable_delete.go b/client/shared_environment_variable_delete.go new file mode 100644 index 00000000..ed873c3b --- /dev/null +++ b/client/shared_environment_variable_delete.go @@ -0,0 +1,39 @@ +package client + +import ( + "context" + "fmt" + "net/http" + "strings" + + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +// DeleteEnvironmentVariable will remove an environment variable from Vercel. +func (c *Client) DeleteSharedEnvironmentVariable(ctx context.Context, teamID, variableID string) error { + url := fmt.Sprintf("%s/v1/env", c.baseURL) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) + } + payload := string(mustMarshal(struct { + IDs []string `json:"ids"` + }{ + IDs: []string{ + variableID, + }, + })) + req, err := http.NewRequestWithContext( + ctx, + "DELETE", + url, + strings.NewReader(payload), + ) + if err != nil { + return err + } + + tflog.Trace(ctx, "deleting shared environment variable", map[string]interface{}{ + "url": url, + }) + return c.doRequest(req, nil) +} diff --git a/client/shared_environment_variable_get.go b/client/shared_environment_variable_get.go new file mode 100644 index 00000000..4029dce6 --- /dev/null +++ b/client/shared_environment_variable_get.go @@ -0,0 +1,31 @@ +package client + +import ( + "context" + "fmt" + "net/http" + + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +func (c *Client) GetSharedEnvironmentVariable(ctx context.Context, teamID, envID string) (e SharedEnvironmentVariableResponse, err error) { + url := fmt.Sprintf("%s/v1/env/%s", c.baseURL, envID) + if c.teamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(teamID)) + } + req, err := http.NewRequestWithContext( + ctx, + "GET", + url, + nil, + ) + if err != nil { + return e, err + } + tflog.Trace(ctx, "getting shared environment variable", map[string]interface{}{ + "url": url, + }) + err = c.doRequest(req, &e) + e.TeamID = c.teamID(teamID) + return e, err +} diff --git a/client/shared_environment_variable_update.go b/client/shared_environment_variable_update.go new file mode 100644 index 00000000..a26f4180 --- /dev/null +++ b/client/shared_environment_variable_update.go @@ -0,0 +1,61 @@ +package client + +import ( + "context" + "fmt" + "net/http" + "strings" + + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +type UpdateSharedEnvironmentVariableRequest struct { + Key string `json:"key"` + Value string `json:"value"` + Type string `json:"type"` + ProjectIDs []string `json:"projectId"` + Target []string `json:"target"` + TeamID string `json:"-"` + EnvID string `json:"-"` +} + +func (c *Client) UpdateSharedEnvironmentVariable(ctx context.Context, request UpdateSharedEnvironmentVariableRequest) (e SharedEnvironmentVariableResponse, err error) { + url := fmt.Sprintf("%s/v1/env", c.baseURL) + if c.teamID(request.TeamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.teamID(request.TeamID)) + } + payload := string(mustMarshal(struct { + Updates map[string]UpdateSharedEnvironmentVariableRequest `json:"updates"` + }{ + Updates: map[string]UpdateSharedEnvironmentVariableRequest{ + request.EnvID: request, + }, + })) + req, err := http.NewRequestWithContext( + ctx, + "PATCH", + url, + strings.NewReader(payload), + ) + if err != nil { + return e, err + } + + tflog.Trace(ctx, "updating shared environment variable", map[string]interface{}{ + "url": url, + "payload": payload, + }) + var response struct { + Updated []SharedEnvironmentVariableResponse `json:"updated"` + } + err = c.doRequest(req, &response) + if err != nil { + return e, err + } + if len(response.Updated) != 1 { + return e, fmt.Errorf("expected 1 environment variable to be created, got %d", len(response.Updated)) + } + // Override the value, as it returns the encrypted value. + response.Updated[0].Value = request.Value + return response.Updated[0], err +} diff --git a/docs/resources/shared_environment_variable.md b/docs/resources/shared_environment_variable.md new file mode 100644 index 00000000..ca0f5192 --- /dev/null +++ b/docs/resources/shared_environment_variable.md @@ -0,0 +1,70 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "vercel_shared_environment_variable Resource - terraform-provider-vercel" +subcategory: "" +description: |- + Provides a Shared Environment Variable resource. + A Shared Environment Variable resource defines an Environment Variable that can be shared between multiple Vercel Projects. + For more detailed information, please see the Vercel documentation https://vercel.com/docs/concepts/projects/environment-variables/shared-environment-variables. +--- + +# vercel_shared_environment_variable (Resource) + +Provides a Shared Environment Variable resource. + +A Shared Environment Variable resource defines an Environment Variable that can be shared between multiple Vercel Projects. + +For more detailed information, please see the [Vercel documentation](https://vercel.com/docs/concepts/projects/environment-variables/shared-environment-variables). + +## Example Usage + +```terraform +resource "vercel_project" "example" { + name = "example" + + git_repository = { + type = "github" + repo = "vercel/some-repo" + } +} + +# An environment variable that will be created +# and associated with the "example" project. +resource "vercel_shared_environment_variable" "example" { + key = "EXAMPLE" + value = "some_value" + target = ["production"] + project_ids = [ + vercel_project.example.id + ] +} +``` + + +## Schema + +### Required + +- `key` (String) The name of the Environment Variable. +- `project_ids` (Set of String) The ID of the Vercel project. +- `target` (Set of String) The environments that the Environment Variable should be present on. Valid targets are either `production`, `preview`, or `development`. +- `value` (String, Sensitive) The value of the Environment Variable. + +### Optional + +- `team_id` (String) The ID of the Vercel team. Shared environment variables require a team. + +### Read-Only + +- `id` (String) The ID of the Environment Variable. + +## Import + +Import is supported using the following syntax: + +```shell +# You can import via the team_id and environment variable id. +# - team_id can be found in the team `settings` tab in the Vercel UI. +# - environment variable id can be taken from the network tab on the shared environment variable page. +terraform import vercel_shared_environment_variable.example team_xxxxxxxxxxxxxxxxxxxxxxxx/env_yyyyyyyyyyyyy +``` diff --git a/examples/resources/vercel_shared_environment_variable/import.sh b/examples/resources/vercel_shared_environment_variable/import.sh new file mode 100644 index 00000000..f731bef1 --- /dev/null +++ b/examples/resources/vercel_shared_environment_variable/import.sh @@ -0,0 +1,4 @@ +# You can import via the team_id and environment variable id. +# - team_id can be found in the team `settings` tab in the Vercel UI. +# - environment variable id can be taken from the network tab on the shared environment variable page. +terraform import vercel_shared_environment_variable.example team_xxxxxxxxxxxxxxxxxxxxxxxx/env_yyyyyyyyyyyyy diff --git a/examples/resources/vercel_shared_environment_variable/resource.tf b/examples/resources/vercel_shared_environment_variable/resource.tf new file mode 100644 index 00000000..59b66f24 --- /dev/null +++ b/examples/resources/vercel_shared_environment_variable/resource.tf @@ -0,0 +1,19 @@ +resource "vercel_project" "example" { + name = "example" + + git_repository = { + type = "github" + repo = "vercel/some-repo" + } +} + +# An environment variable that will be created +# and associated with the "example" project. +resource "vercel_shared_environment_variable" "example" { + key = "EXAMPLE" + value = "some_value" + target = ["production"] + project_ids = [ + vercel_project.example.id + ] +} diff --git a/vercel/provider.go b/vercel/provider.go index 54138633..ebf5c3fe 100644 --- a/vercel/provider.go +++ b/vercel/provider.go @@ -56,6 +56,7 @@ func (p *vercelProvider) Resources(_ context.Context) []func() resource.Resource newProjectResource, newProjectDomainResource, newProjectEnvironmentVariableResource, + newSharedEnvironmentVariableResource, } } diff --git a/vercel/resource_project.go b/vercel/resource_project.go index 28ce7ec3..f82ded56 100644 --- a/vercel/resource_project.go +++ b/vercel/resource_project.go @@ -107,6 +107,7 @@ At this time you cannot use a Vercel Project resource with in-line ` + "`environ ElementType: types.StringType, Validators: []validator.Set{ stringSetItemsIn("production", "preview", "development"), + stringSetMinCount(1), }, Required: true, }, diff --git a/vercel/resource_project_environment_variable.go b/vercel/resource_project_environment_variable.go index 971d83c9..adeb9046 100644 --- a/vercel/resource_project_environment_variable.go +++ b/vercel/resource_project_environment_variable.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/vercel/terraform-provider-vercel/client" @@ -62,6 +63,10 @@ At this time you cannot use a Vercel Project resource with in-line ` + "`environ Required: true, Description: "The environments that the Environment Variable should be present on. Valid targets are either `production`, `preview`, or `development`.", ElementType: types.StringType, + Validators: []validator.Set{ + stringSetItemsIn("production", "preview", "development"), + stringSetMinCount(1), + }, }, "key": schema.StringAttribute{ Required: true, diff --git a/vercel/resource_project_environment_variable_model.go b/vercel/resource_project_environment_variable_model.go index 5751b354..a7bd6588 100644 --- a/vercel/resource_project_environment_variable_model.go +++ b/vercel/resource_project_environment_variable_model.go @@ -17,7 +17,7 @@ type ProjectEnvironmentVariable struct { } func (e *ProjectEnvironmentVariable) toCreateEnvironmentVariableRequest() client.CreateEnvironmentVariableRequest { - var target []string + target := []string{} for _, t := range e.Target { target = append(target, t.ValueString()) } @@ -35,7 +35,7 @@ func (e *ProjectEnvironmentVariable) toCreateEnvironmentVariableRequest() client } func (e *ProjectEnvironmentVariable) toUpdateEnvironmentVariableRequest() client.UpdateEnvironmentVariableRequest { - var target []string + target := []string{} for _, t := range e.Target { target = append(target, t.ValueString()) } diff --git a/vercel/resource_project_model.go b/vercel/resource_project_model.go index 5b995a96..13738717 100644 --- a/vercel/resource_project_model.go +++ b/vercel/resource_project_model.go @@ -43,7 +43,7 @@ func (p *Project) environment(ctx context.Context) ([]EnvironmentItem, error) { func parseEnvironment(vars []EnvironmentItem) []client.EnvironmentVariable { out := []client.EnvironmentVariable{} for _, e := range vars { - var target []string + target := []string{} for _, t := range e.Target { target = append(target, t.ValueString()) } @@ -107,7 +107,7 @@ type EnvironmentItem struct { } func (e *EnvironmentItem) toEnvironmentVariableRequest() client.EnvironmentVariableRequest { - var target []string + target := []string{} for _, t := range e.Target { target = append(target, t.ValueString()) } diff --git a/vercel/resource_shared_environment_variable.go b/vercel/resource_shared_environment_variable.go new file mode 100644 index 00000000..9e5573f3 --- /dev/null +++ b/vercel/resource_shared_environment_variable.go @@ -0,0 +1,280 @@ +package vercel + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/vercel/terraform-provider-vercel/client" +) + +func newSharedEnvironmentVariableResource() resource.Resource { + return &sharedEnvironmentVariableResource{} +} + +type sharedEnvironmentVariableResource struct { + client *client.Client +} + +func (r *sharedEnvironmentVariableResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_shared_environment_variable" +} + +func (r *sharedEnvironmentVariableResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + + r.client = client +} + +// Schema returns the schema information for a project environment variable resource. +func (r *sharedEnvironmentVariableResource) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: ` +Provides a Shared Environment Variable resource. + +A Shared Environment Variable resource defines an Environment Variable that can be shared between multiple Vercel Projects. + +For more detailed information, please see the [Vercel documentation](https://vercel.com/docs/concepts/projects/environment-variables/shared-environment-variables). +`, + Attributes: map[string]schema.Attribute{ + "target": schema.SetAttribute{ + Required: true, + Description: "The environments that the Environment Variable should be present on. Valid targets are either `production`, `preview`, or `development`.", + ElementType: types.StringType, + Validators: []validator.Set{ + stringSetItemsIn("production", "preview", "development"), + stringSetMinCount(1), + }, + }, + "key": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, + Description: "The name of the Environment Variable.", + }, + "value": schema.StringAttribute{ + Required: true, + Description: "The value of the Environment Variable.", + Sensitive: true, + }, + "project_ids": schema.SetAttribute{ + Required: true, + Description: "The ID of the Vercel project.", + ElementType: types.StringType, + }, + "team_id": schema.StringAttribute{ + Optional: true, + Computed: true, + Description: "The ID of the Vercel team. Shared environment variables require a team.", + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace(), stringplanmodifier.UseStateForUnknown()}, + }, + "id": schema.StringAttribute{ + Description: "The ID of the Environment Variable.", + PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace(), stringplanmodifier.UseStateForUnknown()}, + Computed: true, + }, + }, + } +} + +// Create will create a new shared environment variable. +// This is called automatically by the provider when a new resource should be created. +func (r *sharedEnvironmentVariableResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan SharedEnvironmentVariable + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + response, err := r.client.CreateSharedEnvironmentVariable(ctx, plan.toCreateSharedEnvironmentVariableRequest()) + if err != nil { + resp.Diagnostics.AddError( + "Error creating project environment variable", + "Could not create project environment variable, unexpected error: "+err.Error(), + ) + return + } + + result := convertResponseToSharedEnvironmentVariable(response) + + tflog.Trace(ctx, "created shared environment variable", map[string]interface{}{ + "id": result.ID.ValueString(), + "team_id": result.TeamID.ValueString(), + }) + + diags = resp.State.Set(ctx, result) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Read will read an shared environment variable by requesting it from the Vercel API, and will update terraform +// with this information. +func (r *sharedEnvironmentVariableResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state SharedEnvironmentVariable + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + out, err := r.client.GetSharedEnvironmentVariable(ctx, state.TeamID.ValueString(), state.ID.ValueString()) + if client.NotFound(err) { + resp.State.RemoveResource(ctx) + return + } + if err != nil { + resp.Diagnostics.AddError( + "Error reading shared environment variable", + fmt.Sprintf("Could not get shared environment variable %s %s, unexpected error: %s", + state.TeamID.ValueString(), + state.ID.ValueString(), + err, + ), + ) + return + } + + result := convertResponseToSharedEnvironmentVariable(out) + tflog.Trace(ctx, "read shared environment variable", map[string]interface{}{ + "id": result.ID.ValueString(), + "team_id": result.TeamID.ValueString(), + }) + + diags = resp.State.Set(ctx, result) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Update updates the project environment variable of a Vercel project state. +func (r *sharedEnvironmentVariableResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan SharedEnvironmentVariable + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + response, err := r.client.UpdateSharedEnvironmentVariable(ctx, plan.toUpdateSharedEnvironmentVariableRequest()) + if err != nil { + resp.Diagnostics.AddError( + "Error updating shared environment variable", + "Could not update shared environment variable, unexpected error: "+err.Error(), + ) + return + } + + result := convertResponseToSharedEnvironmentVariable(response) + + tflog.Trace(ctx, "updated project environment variable", map[string]interface{}{ + "id": result.ID.ValueString(), + "team_id": result.TeamID.ValueString(), + }) + + diags = resp.State.Set(ctx, result) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +// Delete deletes a Vercel project environment variable. +func (r *sharedEnvironmentVariableResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state SharedEnvironmentVariable + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + err := r.client.DeleteSharedEnvironmentVariable(ctx, state.TeamID.ValueString(), state.ID.ValueString()) + if client.NotFound(err) { + return + } + if err != nil { + resp.Diagnostics.AddError( + "Error deleting shared environment variable", + fmt.Sprintf( + "Could not delete shared environment variable %s, unexpected error: %s", + state.ID.ValueString(), + err, + ), + ) + return + } + + tflog.Trace(ctx, "deleted project environment variable", map[string]interface{}{ + "id": state.ID.ValueString(), + "team_id": state.TeamID.ValueString(), + }) +} + +// splitID is a helper function for splitting an import ID into the corresponding parts. +// It also validates whether the ID is in a correct format. +func splitSharedEnvironmentVariableID(id string) (teamID, envID string, ok bool) { + attributes := strings.Split(id, "/") + if len(attributes) == 2 { + return attributes[0], attributes[1], true + } + + return "", "", false +} + +// ImportState takes an identifier and reads all the project environment variable information from the Vercel API. +// The results are then stored in terraform state. +func (r *sharedEnvironmentVariableResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + teamID, envID, ok := splitSharedEnvironmentVariableID(req.ID) + if !ok { + resp.Diagnostics.AddError( + "Error importing shared environment variable", + fmt.Sprintf("Invalid id '%s' specified. should be in format \"team_id/env_id\"", req.ID), + ) + } + + out, err := r.client.GetSharedEnvironmentVariable(ctx, teamID, envID) + if err != nil { + resp.Diagnostics.AddError( + "Error reading shared environment variable", + fmt.Sprintf("Could not get shared environment variable %s %s, unexpected error: %s", + teamID, + envID, + err, + ), + ) + return + } + + result := convertResponseToSharedEnvironmentVariable(out) + tflog.Trace(ctx, "imported shared environment variable", map[string]interface{}{ + "team_id": result.TeamID.ValueString(), + "env_id": result.ID.ValueString(), + }) + + diags := resp.State.Set(ctx, result) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} diff --git a/vercel/resource_shared_environment_variable_model.go b/vercel/resource_shared_environment_variable_model.go new file mode 100644 index 00000000..8462e768 --- /dev/null +++ b/vercel/resource_shared_environment_variable_model.go @@ -0,0 +1,85 @@ +package vercel + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/vercel/terraform-provider-vercel/client" +) + +// SharedEnvironmentVariable reflects the state terraform stores internally for a project environment variable. +type SharedEnvironmentVariable struct { + Target []types.String `tfsdk:"target"` + Key types.String `tfsdk:"key"` + Value types.String `tfsdk:"value"` + TeamID types.String `tfsdk:"team_id"` + ProjectIDs []types.String `tfsdk:"project_ids"` + ID types.String `tfsdk:"id"` +} + +func (e *SharedEnvironmentVariable) toCreateSharedEnvironmentVariableRequest() client.CreateSharedEnvironmentVariableRequest { + target := []string{} + for _, t := range e.Target { + target = append(target, t.ValueString()) + } + projectIDs := []string{} + for _, t := range e.ProjectIDs { + projectIDs = append(projectIDs, t.ValueString()) + } + return client.CreateSharedEnvironmentVariableRequest{ + EnvironmentVariable: client.SharedEnvironmentVariableRequest{ + Target: target, + Type: "encrypted", + ProjectIDs: projectIDs, + EnvironmentVariables: []client.SharedEnvVarRequest{ + { + Key: e.Key.ValueString(), + Value: e.Value.ValueString(), + }, + }, + }, + TeamID: e.TeamID.ValueString(), + } +} + +func (e *SharedEnvironmentVariable) toUpdateSharedEnvironmentVariableRequest() client.UpdateSharedEnvironmentVariableRequest { + target := []string{} + for _, t := range e.Target { + target = append(target, t.ValueString()) + } + projectIDs := []string{} + for _, t := range e.ProjectIDs { + projectIDs = append(projectIDs, t.ValueString()) + } + return client.UpdateSharedEnvironmentVariableRequest{ + Key: e.Key.ValueString(), + Value: e.Value.ValueString(), + Target: target, + Type: "encrypted", + TeamID: e.TeamID.ValueString(), + EnvID: e.ID.ValueString(), + ProjectIDs: projectIDs, + } +} + +// convertResponseToSharedEnvironmentVariable is used to populate terraform state based on an API response. +// Where possible, values from the API response are used to populate state. If not possible, +// values from plan are used. +func convertResponseToSharedEnvironmentVariable(response client.SharedEnvironmentVariableResponse) SharedEnvironmentVariable { + target := []types.String{} + for _, t := range response.Target { + target = append(target, types.StringValue(t)) + } + + project_ids := []types.String{} + for _, t := range response.ProjectIDs { + project_ids = append(project_ids, types.StringValue(t)) + } + + return SharedEnvironmentVariable{ + Target: target, + Key: types.StringValue(response.Key), + Value: types.StringValue(response.Value), + ProjectIDs: project_ids, + TeamID: toTeamID(response.TeamID), + ID: types.StringValue(response.ID), + } +} diff --git a/vercel/resource_shared_environment_variable_test.go b/vercel/resource_shared_environment_variable_test.go new file mode 100644 index 00000000..9b064368 --- /dev/null +++ b/vercel/resource_shared_environment_variable_test.go @@ -0,0 +1,154 @@ +package vercel_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func testAccSharedEnvironmentVariableExists(n, teamID string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no ID is set") + } + + _, err := testClient().GetSharedEnvironmentVariable(context.TODO(), teamID, rs.Primary.ID) + return err + } +} + +func testAccSharedEnvironmentVariableDoesNotExist(n, teamID string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no ID is set") + } + + _, err := testClient().GetSharedEnvironmentVariable(context.TODO(), teamID, rs.Primary.ID) + + if err != nil { + return nil + } + return fmt.Errorf("expected an error, but got none") + } +} + +func TestAcc_SharedEnvironmentVariables(t *testing.T) { + nameSuffix := acctest.RandString(16) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + CheckDestroy: resource.ComposeAggregateTestCheckFunc( + testAccProjectDestroy("vercel_project.example", testTeam()), + ), + Steps: []resource.TestStep{ + { + Config: testAccSharedEnvironmentVariablesConfig(nameSuffix), + Check: resource.ComposeAggregateTestCheckFunc( + testAccSharedEnvironmentVariableExists("vercel_shared_environment_variable.example", testTeam()), + resource.TestCheckResourceAttr("vercel_shared_environment_variable.example", "key", "foo"), + resource.TestCheckResourceAttr("vercel_shared_environment_variable.example", "value", "bar"), + resource.TestCheckTypeSetElemAttr("vercel_shared_environment_variable.example", "target.*", "production"), + ), + }, + { + Config: testAccSharedEnvironmentVariablesConfigUpdated(nameSuffix), + Check: resource.ComposeAggregateTestCheckFunc( + testAccSharedEnvironmentVariableExists("vercel_shared_environment_variable.example", testTeam()), + resource.TestCheckResourceAttr("vercel_shared_environment_variable.example", "key", "foo"), + resource.TestCheckResourceAttr("vercel_shared_environment_variable.example", "value", "updated-bar"), + resource.TestCheckTypeSetElemAttr("vercel_shared_environment_variable.example", "target.*", "development"), + resource.TestCheckTypeSetElemAttr("vercel_shared_environment_variable.example", "target.*", "preview"), + ), + }, + { + ResourceName: "vercel_shared_environment_variable.example", + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: getSharedEnvironmentVariableImportID("vercel_shared_environment_variable.example"), + }, + { + Config: testAccSharedEnvironmentVariablesConfigDeleted(nameSuffix), + Check: resource.ComposeAggregateTestCheckFunc( + testAccSharedEnvironmentVariableDoesNotExist("vercel_project.example", testTeam()), + ), + }, + }, + }) +} + +func getSharedEnvironmentVariableImportID(n string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[n] + if !ok { + return "", fmt.Errorf("not found: %s", n) + } + + if rs.Primary.ID == "" { + return "", fmt.Errorf("no ID is set") + } + + return fmt.Sprintf("%s/%s", rs.Primary.Attributes["team_id"], rs.Primary.ID), nil + } +} + +func testAccSharedEnvironmentVariablesConfig(projectName string) string { + return fmt.Sprintf(` +resource "vercel_project" "example" { + name = "test-acc-example-project-%[1]s" + %[2]s +} + +resource "vercel_shared_environment_variable" "example" { + %[2]s + key = "foo" + value = "bar" + target = ["production"] + project_ids = [ + vercel_project.example.id + ] +} +`, projectName, teamIDConfig()) +} + +func testAccSharedEnvironmentVariablesConfigUpdated(projectName string) string { + return fmt.Sprintf(` +resource "vercel_project" "example" { + name = "test-acc-example-project-%[1]s" + %[2]s +} + +resource "vercel_project" "example2" { + name = "test-acc-example-project-2-%[1]s" + %[2]s +} + +resource "vercel_shared_environment_variable" "example" { + %[2]s + key = "foo" + value = "updated-bar" + target = ["preview", "development"] + project_ids = [ + vercel_project.example.id, + vercel_project.example2.id + ] +} +`, projectName, teamIDConfig()) +} + +func testAccSharedEnvironmentVariablesConfigDeleted(projectName string) string { + return "" +} diff --git a/vercel/validator_string_set_min_count.go b/vercel/validator_string_set_min_count.go new file mode 100644 index 00000000..2fd874a1 --- /dev/null +++ b/vercel/validator_string_set_min_count.go @@ -0,0 +1,40 @@ +package vercel + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +func stringSetMinCount(min int) validatorStringSetMinCount { + return validatorStringSetMinCount{ + Min: min, + } +} + +type validatorStringSetMinCount struct { + Min int +} + +func (v validatorStringSetMinCount) Description(ctx context.Context) string { + return fmt.Sprintf("Set must contain at least %d items", v.Min) +} +func (v validatorStringSetMinCount) MarkdownDescription(ctx context.Context) string { + return fmt.Sprintf("Set must contain at least %d items", v.Min) +} + +func (v validatorStringSetMinCount) ValidateSet(ctx context.Context, req validator.SetRequest, resp *validator.SetResponse) { + if req.ConfigValue.IsUnknown() || req.ConfigValue.IsNull() { + return + } + + if len(req.ConfigValue.Elements()) < v.Min { + resp.Diagnostics.AddAttributeError( + req.Path, + "Invalid value provided", + v.Description(ctx), + ) + return + } +} From 6a26ffdda05c59a0289c954fbf979cd199e52e35 Mon Sep 17 00:00:00 2001 From: Douglas Parsons Date: Sun, 14 May 2023 18:22:14 +0100 Subject: [PATCH 2/5] Skip DNS Record test until a test domain exists --- vercel/resource_dns_record_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/vercel/resource_dns_record_test.go b/vercel/resource_dns_record_test.go index bab1e3db..0b71ca06 100644 --- a/vercel/resource_dns_record_test.go +++ b/vercel/resource_dns_record_test.go @@ -52,6 +52,7 @@ func testAccDNSRecordExists(n, teamID string) resource.TestCheckFunc { } func TestAcc_DNSRecord(t *testing.T) { + t.Skip("Skipping until i have a domain in a suitable location to test with") nameSuffix := acctest.RandString(16) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, From 04dd28f3fdb6e19a6272c280b7522f231f315fc2 Mon Sep 17 00:00:00 2001 From: Douglas Parsons Date: Sun, 14 May 2023 18:35:20 +0100 Subject: [PATCH 3/5] Update deps + Go version --- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 4 +-- go.mod | 20 +++++++-------- go.sum | 46 ++++++++++++++++++----------------- 4 files changed, 37 insertions(+), 35 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3639d32f..6a69da94 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: "1.19" + go-version: "1.20" - name: Import GPG key id: import_gpg uses: crazy-max/ghaction-import-gpg@v5 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 61475bab..7d323cac 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,7 +25,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: "1.19" + go-version: "1.20" id: go - name: Install Task uses: arduino/setup-task@v1 @@ -58,7 +58,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: "1.19" + go-version: "1.20" id: go - uses: hashicorp/setup-terraform@v1 with: diff --git a/go.mod b/go.mod index f75f36d2..bd991dd1 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/hashicorp/terraform-plugin-framework v1.2.0 - github.com/hashicorp/terraform-plugin-go v0.14.3 + github.com/hashicorp/terraform-plugin-go v0.15.0 github.com/hashicorp/terraform-plugin-log v0.8.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.10.1 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 @@ -32,11 +32,11 @@ require ( github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.15.0 // indirect github.com/hashicorp/terraform-json v0.13.0 // indirect - github.com/hashicorp/terraform-registry-address v0.1.0 // indirect + github.com/hashicorp/terraform-registry-address v0.2.0 // indirect github.com/hashicorp/terraform-svchost v0.1.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect @@ -44,15 +44,15 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/oklog/run v1.1.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect - github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect - github.com/vmihailenco/tagparser v0.1.2 // indirect + github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/zclconf/go-cty v1.12.1 // indirect golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230320184635-7606e756e683 // indirect - google.golang.org/grpc v1.54.0 // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + google.golang.org/grpc v1.55.0 // indirect google.golang.org/protobuf v1.30.0 // indirect ) diff --git a/go.sum b/go.sum index 4f6c37cc..f118a47e 100644 --- a/go.sum +++ b/go.sum @@ -19,11 +19,11 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY= +cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/iam v0.12.0 h1:DRtTY29b75ciH6Ov1PHb4/iat2CLCvrOm40Q0a6DFpE= +cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -212,16 +212,16 @@ github.com/hashicorp/terraform-json v0.13.0/go.mod h1:y5OdLBCT+rxbwnpxZs9kGL7R9E github.com/hashicorp/terraform-plugin-framework v1.2.0 h1:MZjFFfULnFq8fh04FqrKPcJ/nGpHOvX4buIygT3MSNY= github.com/hashicorp/terraform-plugin-framework v1.2.0/go.mod h1:nToI62JylqXDq84weLJ/U3umUsBhZAaTmU0HXIVUOcw= github.com/hashicorp/terraform-plugin-go v0.5.0/go.mod h1:PAVN26PNGpkkmsvva1qfriae5Arky3xl3NfzKa8XFVM= -github.com/hashicorp/terraform-plugin-go v0.14.3 h1:nlnJ1GXKdMwsC8g1Nh05tK2wsC3+3BL/DBBxFEki+j0= -github.com/hashicorp/terraform-plugin-go v0.14.3/go.mod h1:7ees7DMZ263q8wQ6E4RdIdR6nHHJtrdt4ogX5lPkX1A= +github.com/hashicorp/terraform-plugin-go v0.15.0 h1:1BJNSUFs09DS8h/XNyJNJaeusQuWc/T9V99ylU9Zwp0= +github.com/hashicorp/terraform-plugin-go v0.15.0/go.mod h1:tk9E3/Zx4RlF/9FdGAhwxHExqIHHldqiQGt20G6g+nQ= github.com/hashicorp/terraform-plugin-log v0.2.0/go.mod h1:E1kJmapEHzqu1x6M++gjvhzM2yMQNXPVWZRCB8sgYjg= github.com/hashicorp/terraform-plugin-log v0.8.0 h1:pX2VQ/TGKu+UU1rCay0OlzosNKe4Nz1pepLXj95oyy0= github.com/hashicorp/terraform-plugin-log v0.8.0/go.mod h1:1myFrhVsBLeylQzYYEV17VVjtG8oYPRFdaZs7xdW2xs= github.com/hashicorp/terraform-plugin-sdk/v2 v2.10.1 h1:B9AocC+dxrCqcf4vVhztIkSkt3gpRjUkEka8AmZWGlQ= github.com/hashicorp/terraform-plugin-sdk/v2 v2.10.1/go.mod h1:FjM9DXWfP0w/AeOtJoSKHBZ01LqmaO6uP4bXhv3fekw= github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896/go.mod h1:bzBPnUIkI0RxauU8Dqo+2KrZZ28Cf48s8V6IHt3p4co= -github.com/hashicorp/terraform-registry-address v0.1.0 h1:W6JkV9wbum+m516rCl5/NjKxCyTVaaUBbzYcMzBDO3U= -github.com/hashicorp/terraform-registry-address v0.1.0/go.mod h1:EnyO2jYO6j29DTHbJcm00E5nQTFeTtyZH3H5ycydQ5A= +github.com/hashicorp/terraform-registry-address v0.2.0 h1:92LUg03NhfgZv44zpNTLBGIbiyTokQCDcdH5BhVHT3s= +github.com/hashicorp/terraform-registry-address v0.2.0/go.mod h1:478wuzJPzdmqT6OGbB/iH82EDcI8VFM4yujknh/1nIs= github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= github.com/hashicorp/terraform-svchost v0.1.0 h1:0+RcgZdZYNd81Vw7tu62g9JiLLvbOigp7QtyNh6CjXk= github.com/hashicorp/terraform-svchost v0.1.0/go.mod h1:ut8JaH0vumgdCfJaihdcZULqkAwHdQNwNH7taIDdsZM= @@ -274,8 +274,8 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= @@ -333,11 +333,12 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= +github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= -github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= -github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -429,14 +430,14 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= +golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -484,18 +485,19 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -593,8 +595,8 @@ google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1m google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200711021454-869866162049/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20230320184635-7606e756e683 h1:khxVcsk/FhnzxMKOyD+TDGwjbEOpcPuIpmafPGFmhMA= -google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -608,8 +610,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= From 720e76f8e057e254c76ced9ec5206b7bcb444c96 Mon Sep 17 00:00:00 2001 From: Douglas Parsons Date: Mon, 15 May 2023 08:58:10 +0100 Subject: [PATCH 4/5] Fix test --- vercel/resource_shared_environment_variable_test.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/vercel/resource_shared_environment_variable_test.go b/vercel/resource_shared_environment_variable_test.go index 9b064368..08dbd647 100644 --- a/vercel/resource_shared_environment_variable_test.go +++ b/vercel/resource_shared_environment_variable_test.go @@ -150,5 +150,15 @@ resource "vercel_shared_environment_variable" "example" { } func testAccSharedEnvironmentVariablesConfigDeleted(projectName string) string { - return "" + return fmt.Sprintf(` +resource "vercel_project" "example" { + name = "test-acc-example-project-%[1]s" + %[2]s +} + +resource "vercel_project" "example2" { + name = "test-acc-example-project-2-%[1]s" + %[2]s +} + `, projectName, teamIDConfig()) } From 17392303b2ea71585a9c2522245e72e4a5a020cd Mon Sep 17 00:00:00 2001 From: Douglas Parsons Date: Mon, 15 May 2023 10:36:35 +0100 Subject: [PATCH 5/5] PR feedback --- client/shared_environment_variable_delete.go | 2 +- .../resources/vercel_shared_environment_variable/resource.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/shared_environment_variable_delete.go b/client/shared_environment_variable_delete.go index ed873c3b..57c9dba1 100644 --- a/client/shared_environment_variable_delete.go +++ b/client/shared_environment_variable_delete.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-log/tflog" ) -// DeleteEnvironmentVariable will remove an environment variable from Vercel. +// DeleteSharedEnvironmentVariable will remove a shared environment variable from Vercel. func (c *Client) DeleteSharedEnvironmentVariable(ctx context.Context, teamID, variableID string) error { url := fmt.Sprintf("%s/v1/env", c.baseURL) if c.teamID(teamID) != "" { diff --git a/examples/resources/vercel_shared_environment_variable/resource.tf b/examples/resources/vercel_shared_environment_variable/resource.tf index 59b66f24..29c7fece 100644 --- a/examples/resources/vercel_shared_environment_variable/resource.tf +++ b/examples/resources/vercel_shared_environment_variable/resource.tf @@ -7,7 +7,7 @@ resource "vercel_project" "example" { } } -# An environment variable that will be created +# A shared environment variable that will be created # and associated with the "example" project. resource "vercel_shared_environment_variable" "example" { key = "EXAMPLE"