Skip to content

Commit

Permalink
Feat: add support for variable set assignment resource (#870)
Browse files Browse the repository at this point in the history
* Feat: add support for variable set assignment resource

* Add tests

* fix test

* update test

* remove token id to check failures in integration test

* add back token id
  • Loading branch information
TomerHeber authored Jun 4, 2024
1 parent de81881 commit c9b6370
Show file tree
Hide file tree
Showing 9 changed files with 422 additions and 2 deletions.
1 change: 1 addition & 0 deletions client/api_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ type ApiClientInterface interface {
ConfigurationVariablesBySetId(setId string) ([]ConfigurationVariable, error)
AssignConfigurationSets(scope string, scopeId string, sets []string) error
UnassignConfigurationSets(scope string, scopeId string, sets []string) error
ConfigurationSetsAssignments(scope string, scopeId string) ([]ConfigurationSet, error)
}

func NewApiClient(client http.HttpClientInterface, defaultOrganizationId string) ApiClientInterface {
Expand Down
15 changes: 15 additions & 0 deletions client/api_client_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions client/configuration_set_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,15 @@ func (client *ApiClient) UnassignConfigurationSets(scope string, scopeId string,

return client.http.Delete(url, map[string]string{"setIds": setIds})
}

func (client *ApiClient) ConfigurationSetsAssignments(scope string, scopeId string) ([]ConfigurationSet, error) {
var result []ConfigurationSet

url := fmt.Sprintf("/configuration-sets/assignments/%s/%s", scope, scopeId)

if err := client.http.Get(url, nil, &result); err != nil {
return nil, err
}

return result, nil
}
39 changes: 37 additions & 2 deletions client/configuration_set_assignment_test.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
package client_test

import (
. "github.com/env0/terraform-provider-env0/client"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"go.uber.org/mock/gomock"
)

var _ = Describe("Configuration Set", func() {
scope := "environment"
scopeId := "12345"
setIds := []string{"1", "2", "3"}
mockConfigurationSets := []ConfigurationSet{
{
Id: "1",
},
{
Id: "2",
},
{
Id: "3",
},
}

Describe("assign configuration sets", func() {
BeforeEach(func() {
httpCall = mockHttpClient.EXPECT().Post("/configuration-sets/assignments/environment/12345?setIds=1,2,3", nil, nil).Times(1)
httpCall = mockHttpClient.EXPECT().Post("/configuration-sets/assignments/environment/12345?setIds=1,2,3", nil, nil).
Do(func(path string, request interface{}, response *interface{}) {}).
Times(1)
apiClient.AssignConfigurationSets(scope, scopeId, setIds)
})

Expand All @@ -22,10 +38,29 @@ var _ = Describe("Configuration Set", func() {
BeforeEach(func() {
httpCall = mockHttpClient.EXPECT().Delete("/configuration-sets/assignments/environment/12345", map[string]string{
"setIds": "1,2,3",
}).Times(1)
}).
Do(func(path string, request interface{}) {}).
Times(1)
apiClient.UnassignConfigurationSets(scope, scopeId, setIds)
})

It("Should send delete request", func() {})
})

Describe("get configuration sets by scope and scope id", func() {
var configurationSets []ConfigurationSet

BeforeEach(func() {
httpCall = mockHttpClient.EXPECT().
Get("/configuration-sets/assignments/environment/12345", nil, gomock.Any()).
Do(func(path string, request interface{}, response *[]ConfigurationSet) {
*response = mockConfigurationSets
}).Times(1)
configurationSets, _ = apiClient.ConfigurationSetsAssignments(scope, scopeId)
})

It("Should return configuration sets", func() {
Expect(configurationSets).To(Equal(mockConfigurationSets))
})
})
})
1 change: 1 addition & 0 deletions env0/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ func Provider(version string) plugin.ProviderFunc {
"env0_gcp_gke_credentials": resourceGcpGkeCredentials(),
"env0_environment_import": resourceEnvironmentImport(),
"env0_variable_set": resourceVariableSet(),
"env0_variable_set_assignment": resourceVariableSetAssignment(),
},
}

Expand Down
204 changes: 204 additions & 0 deletions env0/resource_variable_set_assignment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
package env0

import (
"context"

"github.com/env0/terraform-provider-env0/client"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

type variableSetAssignmentSchema struct {
Scope string
ScopeId string
SetIds []string
}

func resourceVariableSetAssignment() *schema.Resource {
return &schema.Resource{
CreateContext: resourceVariableSetAssignmentCreate,
UpdateContext: resourceVariableSetAssignmentUpdate,
ReadContext: resourceVariableSetAssignmentRead,
DeleteContext: resourceVariableSetAssignmentDelete,

Schema: map[string]*schema.Schema{
"scope": {
Type: schema.TypeString,
Description: "the resource(scope) type to assign to. Valid values: 'template', 'environment', 'module', 'organization', 'project', 'deployment'",
Required: true,
ValidateDiagFunc: NewStringInValidator([]string{"template", "environment", "module", "organization", "project", "deployment"}),
ForceNew: true,
},
"scope_id": {
Type: schema.TypeString,
Description: "the resource(scope)id (e.g. template id)",
Required: true,
ForceNew: true,
},
"set_ids": {
Type: schema.TypeList,
Description: "list of variable sets",
Required: true,
Elem: &schema.Schema{
Type: schema.TypeString,
Description: "the variable set id",
},
},
},
}
}

func resourceVariableSetAssignmentCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
apiClient := meta.(client.ApiClientInterface)

var assignmentSchema variableSetAssignmentSchema

if err := readResourceData(&assignmentSchema, d); err != nil {
return diag.Errorf("schema resource data deserialization failed: %v", err)
}

if len(assignmentSchema.SetIds) > 0 {
if err := apiClient.AssignConfigurationSets(assignmentSchema.Scope, assignmentSchema.ScopeId, assignmentSchema.SetIds); err != nil {
return diag.Errorf("failed to assign variable sets to the scope: %v", err)
}
}

d.SetId(assignmentSchema.ScopeId)

return nil
}

func resourceVariableSetAssignmentUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
apiClient := meta.(client.ApiClientInterface)

var assignmentSchema variableSetAssignmentSchema

if err := readResourceData(&assignmentSchema, d); err != nil {
return diag.Errorf("schema resource data deserialization failed: %v", err)
}

apiConfigurationSets, err := apiClient.ConfigurationSetsAssignments(assignmentSchema.Scope, assignmentSchema.ScopeId)
if err != nil {
return diag.Errorf("failed to get variable sets assignments: %v", err)
}

// Compare between apiSetIds and schemaSetIds to find what to set ids to delete and what set ids to add.
var toDelete, toAdd []string

// In API but not in Schema - delete.
for _, apiConfigurationSet := range apiConfigurationSets {
found := false

apiSetId := apiConfigurationSet.Id
for _, schemaSetId := range assignmentSchema.SetIds {
if apiSetId == schemaSetId {
found = true
break
}
}

if !found {
toDelete = append(toDelete, apiSetId)
}
}

// In Schema but not in API - add.
for _, schemaSetId := range assignmentSchema.SetIds {
found := false

for _, apiConfigurationSet := range apiConfigurationSets {
apiSetId := apiConfigurationSet.Id
if schemaSetId == apiSetId {
found = true
break
}
}

if !found {
toAdd = append(toAdd, schemaSetId)
}
}

if len(toDelete) > 0 {
if err := apiClient.UnassignConfigurationSets(assignmentSchema.Scope, assignmentSchema.ScopeId, toDelete); err != nil {
return diag.Errorf("failed to unassign variable sets: %v", err)
}
}

if len(toAdd) > 0 {
if err := apiClient.AssignConfigurationSets(assignmentSchema.Scope, assignmentSchema.ScopeId, toAdd); err != nil {
return diag.Errorf("failed to assign variable sets: %v", err)
}
}

return nil
}

func resourceVariableSetAssignmentDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
apiClient := meta.(client.ApiClientInterface)

var assignmentSchema variableSetAssignmentSchema

if err := readResourceData(&assignmentSchema, d); err != nil {
return diag.Errorf("schema resource data deserialization failed: %v", err)
}

if len(assignmentSchema.SetIds) > 0 {
if err := apiClient.UnassignConfigurationSets(assignmentSchema.Scope, assignmentSchema.ScopeId, assignmentSchema.SetIds); err != nil {
return diag.Errorf("failed to unassign variable sets: %v", err)
}
}

return nil
}

func resourceVariableSetAssignmentRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
apiClient := meta.(client.ApiClientInterface)

var assignmentSchema variableSetAssignmentSchema

if err := readResourceData(&assignmentSchema, d); err != nil {
return diag.Errorf("schema resource data deserialization failed: %v", err)
}

apiConfigurationSets, err := apiClient.ConfigurationSetsAssignments(assignmentSchema.Scope, assignmentSchema.ScopeId)
if err != nil {
return diag.Errorf("failed to get variable sets assignments: %v", err)
}

newSchemaSetIds := []string{}

// To avoid drifts keep the schema order as much as possible.
for _, schemaSetId := range assignmentSchema.SetIds {
for _, apiConfigurationSet := range apiConfigurationSets {
apiSetId := apiConfigurationSet.Id

if schemaSetId == apiSetId {
newSchemaSetIds = append(newSchemaSetIds, schemaSetId)
break
}
}
}

for _, apiConfigurationSet := range apiConfigurationSets {
apiSetId := apiConfigurationSet.Id
found := false

for _, schemaSetId := range assignmentSchema.SetIds {
if schemaSetId == apiSetId {
found = true
break
}
}

if !found {
newSchemaSetIds = append(newSchemaSetIds, apiSetId)
}
}

if err := d.Set("set_ids", newSchemaSetIds); err != nil {
return diag.Errorf("failed to set 'set_ids': %v", err)
}

return nil
}
Loading

0 comments on commit c9b6370

Please sign in to comment.