Skip to content

Commit

Permalink
Feat: add variable set support for environment schema
Browse files Browse the repository at this point in the history
  • Loading branch information
TomerHeber committed Jun 18, 2024
1 parent 04b3395 commit 82ee076
Show file tree
Hide file tree
Showing 5 changed files with 361 additions and 47 deletions.
59 changes: 33 additions & 26 deletions client/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,15 @@ type SubEnvironment struct {
}

type DeployRequest struct {
BlueprintId string `json:"blueprintId,omitempty"`
BlueprintRevision string `json:"blueprintRevision,omitempty"`
BlueprintRepository string `json:"blueprintRepository,omitempty"`
ConfigurationChanges *ConfigurationChanges `json:"configurationChanges,omitempty"`
TTL *TTL `json:"ttl,omitempty"`
EnvName string `json:"envName,omitempty"`
UserRequiresApproval *bool `json:"userRequiresApproval,omitempty"`
SubEnvironments map[string]SubEnvironment `json:"subEnvironments,omitempty"`
BlueprintId string `json:"blueprintId,omitempty"`
BlueprintRevision string `json:"blueprintRevision,omitempty"`
BlueprintRepository string `json:"blueprintRepository,omitempty"`
ConfigurationChanges *ConfigurationChanges `json:"configurationChanges,omitempty"`
TTL *TTL `json:"ttl,omitempty"`
EnvName string `json:"envName,omitempty"`
UserRequiresApproval *bool `json:"userRequiresApproval,omitempty"`
SubEnvironments map[string]SubEnvironment `json:"subEnvironments,omitempty"`
ConfigurationSetChanges *ConfigurationSetChanges `json:"configurationSetChanges,omitempty" tfschema:"-"`
}

type WorkflowSubEnvironment struct {
Expand All @@ -99,6 +100,11 @@ type DriftDetectionRequest struct {
Cron string `json:"cron"`
}

type ConfigurationSetChanges struct {
Assign []string `json:"assign,omitempty"`
Unassign []string `json:"unassign,omitempty"`
}

type Environment struct {
Id string `json:"id"`
Name string `json:"name"`
Expand All @@ -124,24 +130,25 @@ type Environment struct {
}

type EnvironmentCreate struct {
Name string `json:"name"`
ProjectId string `json:"projectId"`
DeployRequest *DeployRequest `json:"deployRequest" tfschema:"-"`
WorkspaceName string `json:"workspaceName,omitempty" tfschema:"workspace"`
RequiresApproval *bool `json:"requiresApproval,omitempty" tfschema:"-"`
ContinuousDeployment *bool `json:"continuousDeployment,omitempty" tfschema:"-"`
PullRequestPlanDeployments *bool `json:"pullRequestPlanDeployments,omitempty" tfschema:"-"`
AutoDeployOnPathChangesOnly *bool `json:"autoDeployOnPathChangesOnly,omitempty" tfchema:"-"`
AutoDeployByCustomGlob string `json:"autoDeployByCustomGlob"`
ConfigurationChanges *ConfigurationChanges `json:"configurationChanges,omitempty" tfschema:"-"`
TTL *TTL `json:"ttl,omitempty" tfschema:"-"`
TerragruntWorkingDirectory string `json:"terragruntWorkingDirectory,omitempty"`
VcsCommandsAlias string `json:"vcsCommandsAlias"`
IsRemoteBackend *bool `json:"isRemoteBackend,omitempty" tfschema:"-"`
Type string `json:"type,omitempty"`
DriftDetectionRequest *DriftDetectionRequest `json:"driftDetectionRequest,omitempty" tfschema:"-"`
PreventAutoDeploy *bool `json:"preventAutoDeploy,omitempty" tfschema:"-"`
K8sNamespace string `json:"k8s_namespace,omitempty"`
Name string `json:"name"`
ProjectId string `json:"projectId"`
DeployRequest *DeployRequest `json:"deployRequest" tfschema:"-"`
WorkspaceName string `json:"workspaceName,omitempty" tfschema:"workspace"`
RequiresApproval *bool `json:"requiresApproval,omitempty" tfschema:"-"`
ContinuousDeployment *bool `json:"continuousDeployment,omitempty" tfschema:"-"`
PullRequestPlanDeployments *bool `json:"pullRequestPlanDeployments,omitempty" tfschema:"-"`
AutoDeployOnPathChangesOnly *bool `json:"autoDeployOnPathChangesOnly,omitempty" tfchema:"-"`
AutoDeployByCustomGlob string `json:"autoDeployByCustomGlob"`
ConfigurationChanges *ConfigurationChanges `json:"configurationChanges,omitempty" tfschema:"-"`
TTL *TTL `json:"ttl,omitempty" tfschema:"-"`
TerragruntWorkingDirectory string `json:"terragruntWorkingDirectory,omitempty"`
VcsCommandsAlias string `json:"vcsCommandsAlias"`
IsRemoteBackend *bool `json:"isRemoteBackend,omitempty" tfschema:"-"`
Type string `json:"type,omitempty"`
DriftDetectionRequest *DriftDetectionRequest `json:"driftDetectionRequest,omitempty" tfschema:"-"`
PreventAutoDeploy *bool `json:"preventAutoDeploy,omitempty" tfschema:"-"`
K8sNamespace string `json:"k8s_namespace,omitempty"`
ConfigurationSetChanges *ConfigurationSetChanges `json:"configurationSetChanges,omitempty" tfschema:"-"`
}

// When converted to JSON needs to be flattened. See custom MarshalJSON below.
Expand Down
2 changes: 1 addition & 1 deletion env0/data_environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func dataEnvironmentRead(ctx context.Context, d *schema.ResourceData, meta inter
}
}

setEnvironmentSchema(ctx, d, environment, client.ConfigurationChanges{})
setEnvironmentSchema(ctx, d, environment, client.ConfigurationChanges{}, nil)

templateId := environment.LatestDeploymentLog.BlueprintId

Expand Down
172 changes: 152 additions & 20 deletions env0/resource_environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,15 @@ func resourceEnvironment() *schema.Resource {
Optional: true,
ForceNew: true,
},
"variable_sets": {
Type: schema.TypeList,
Description: "a list of variable set to assign to this environment",
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
Description: "variable set id",
},
},
},
CustomizeDiff: customdiff.ValidateChange("template_id", func(ctx context.Context, oldValue, newValue, meta interface{}) error {
if oldValue != "" && oldValue != newValue {
Expand All @@ -359,7 +368,7 @@ func resourceEnvironment() *schema.Resource {
}
}

func setEnvironmentSchema(ctx context.Context, d *schema.ResourceData, environment client.Environment, configurationVariables client.ConfigurationChanges) error {
func setEnvironmentSchema(ctx context.Context, d *schema.ResourceData, environment client.Environment, configurationVariables client.ConfigurationChanges, VariableSetsIds []string) error {
if err := writeResourceData(&environment, d); err != nil {
return fmt.Errorf("schema resource data serialization failed: %v", err)
}
Expand Down Expand Up @@ -417,6 +426,10 @@ func setEnvironmentSchema(ctx context.Context, d *schema.ResourceData, environme

setEnvironmentConfigurationSchema(ctx, d, configurationVariables)

if err := d.Set("variable_sets", VariableSetsIds); err != nil {
return fmt.Errorf("failed to set variable_sets value: %w", err)
}

return nil
}

Expand Down Expand Up @@ -590,11 +603,35 @@ func resourceEnvironmentCreate(ctx context.Context, d *schema.ResourceData, meta
d.Set("auto_deploy_on_path_changes_only", *environment.AutoDeployOnPathChangesOnly)
}

setEnvironmentSchema(ctx, d, environment, environmentConfigurationVariables)
var environmentVariableSetIds []string
if environmentPayload.ConfigurationSetChanges != nil {
environmentVariableSetIds = environmentPayload.ConfigurationSetChanges.Assign
}

setEnvironmentSchema(ctx, d, environment, environmentConfigurationVariables, environmentVariableSetIds)

return nil
}

func getEnvironmentVariableSetIdsFromApi(d *schema.ResourceData, apiClient client.ApiClientInterface) ([]string, error) {
scope := "ENVIRONMENT"
if _, ok := d.GetOk("sub_environment_configuration"); ok {
scope = "WORKFLOW"
}

environmentVariableSets, err := apiClient.ConfigurationSetsAssignments(scope, d.Id())
if err != nil {
return nil, err
}

var environmentVariableSetIds []string
for _, variableSet := range environmentVariableSets {
environmentVariableSetIds = append(environmentVariableSetIds, variableSet.Id)
}

return environmentVariableSetIds, nil
}

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

Expand All @@ -613,7 +650,12 @@ func resourceEnvironmentRead(ctx context.Context, d *schema.ResourceData, meta i
return diag.Errorf("could not get environment configuration variables: %v", err)
}

setEnvironmentSchema(ctx, d, environment, environmentConfigurationVariables)
environmentVariableSetIds, err := getEnvironmentVariableSetIdsFromApi(d, apiClient)
if err != nil {
return diag.Errorf("could not get environment variable sets: %v", err)
}

setEnvironmentSchema(ctx, d, environment, environmentConfigurationVariables, environmentVariableSetIds)

if isTemplateless(d) {
// envrionment with no template.
Expand Down Expand Up @@ -672,7 +714,7 @@ func shouldUpdateTemplate(d *schema.ResourceData) bool {
}

func shouldDeploy(d *schema.ResourceData) bool {
return d.HasChanges("revision", "configuration", "sub_environment_configuration")
return d.HasChanges("revision", "configuration", "sub_environment_configuration", "variable_sets")
}

func shouldUpdate(d *schema.ResourceData) bool {
Expand Down Expand Up @@ -721,7 +763,10 @@ func updateDriftDetection(d *schema.ResourceData, apiClient client.ApiClientInte
}

func deploy(d *schema.ResourceData, apiClient client.ApiClientInterface) diag.Diagnostics {
deployPayload := getDeployPayload(d, apiClient, true)
deployPayload, err := getDeployPayload(d, apiClient, true)
if err != nil {
return diag.FromErr(err)
}

subEnvironments, err := getSubEnvironments(d)
if err != nil {
Expand All @@ -734,7 +779,10 @@ func deploy(d *schema.ResourceData, apiClient client.ApiClientInterface) diag.Di
for i, subEnvironment := range subEnvironments {
configuration := d.Get(fmt.Sprintf("sub_environment_configuration.%d.configuration", i)).([]interface{})
configurationChanges := getConfigurationVariablesFromSchema(configuration)
configurationChanges = getUpdateConfigurationVariables(configurationChanges, subEnvironment.Id, client.ScopeEnvironment, apiClient)
configurationChanges, err = getUpdateConfigurationVariables(configurationChanges, subEnvironment.Id, client.ScopeEnvironment, apiClient)
if err != nil {
return diag.FromErr(err)
}

for i := range configurationChanges {
configurationChanges[i].Scope = client.ScopeEnvironment
Expand Down Expand Up @@ -780,6 +828,18 @@ func updateTTL(d *schema.ResourceData, apiClient client.ApiClientInterface) diag
return nil
}

func getEnvironmentVariableSetIdsFromSchema(d *schema.ResourceData) []string {
var variableSets []string

if ivariableSets, ok := d.GetOk("variable_sets"); ok {
for _, ivariableSet := range ivariableSets.([]interface{}) {
variableSets = append(variableSets, ivariableSet.(string))
}
}

return variableSets
}

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

Expand Down Expand Up @@ -868,7 +928,17 @@ func getCreatePayload(d *schema.ResourceData, apiClient client.ApiClientInterfac
}
}

deployPayload := getDeployPayload(d, apiClient, false)
variableSets := getEnvironmentVariableSetIdsFromSchema(d)
if len(variableSets) > 0 {
payload.ConfigurationSetChanges = &client.ConfigurationSetChanges{
Assign: variableSets,
}
}

deployPayload, err := getDeployPayload(d, apiClient, false)
if err != nil {
return client.EnvironmentCreate{}, diag.FromErr(err)
}

subEnvironments, err := getSubEnvironments(d)
if err != nil {
Expand Down Expand Up @@ -957,8 +1027,55 @@ func getUpdatePayload(d *schema.ResourceData) (client.EnvironmentUpdate, diag.Di
return payload, nil
}

func getDeployPayload(d *schema.ResourceData, apiClient client.ApiClientInterface, isRedeploy bool) client.DeployRequest {
func getEnvironmentConfigurationSetChanges(d *schema.ResourceData, apiClient client.ApiClientInterface) (*client.ConfigurationSetChanges, error) {
variableSetsFromSchema := getEnvironmentVariableSetIdsFromSchema(d)
variableSetFromApi, err := getEnvironmentVariableSetIdsFromApi(d, apiClient)
if err != nil {
return nil, err
}

var assignVariableSets []string
var unassignVariableSets []string

for _, sv := range variableSetsFromSchema {
found := false

for _, av := range variableSetFromApi {
if sv == av {
found = true
break
}
}

if !found {
assignVariableSets = append(assignVariableSets, sv)
}
}

for _, av := range variableSetFromApi {
found := false

for _, sv := range variableSetsFromSchema {
if sv == av {
found = true
break
}
}

if !found {
unassignVariableSets = append(unassignVariableSets, av)
}
}

return &client.ConfigurationSetChanges{
Assign: assignVariableSets,
Unassign: unassignVariableSets,
}, nil
}

func getDeployPayload(d *schema.ResourceData, apiClient client.ApiClientInterface, isRedeploy bool) (client.DeployRequest, error) {
payload := client.DeployRequest{}
var err error

if isTemplateless(d) {
templateId, ok := d.GetOk("without_template_settings.0.id")
Expand All @@ -973,22 +1090,32 @@ func getDeployPayload(d *schema.ResourceData, apiClient client.ApiClientInterfac
payload.BlueprintRevision = revision.(string)
}

if configuration, ok := d.GetOk("configuration"); ok && isRedeploy {
configurationChanges := getConfigurationVariablesFromSchema(configuration.([]interface{}))
scope := client.ScopeEnvironment
if _, ok := d.GetOk("sub_environment_configuration"); ok {
scope = client.ScopeWorkflow
if isRedeploy {
if configuration, ok := d.GetOk("configuration"); ok && isRedeploy {
configurationChanges := getConfigurationVariablesFromSchema(configuration.([]interface{}))
scope := client.ScopeEnvironment
if _, ok := d.GetOk("sub_environment_configuration"); ok {
scope = client.ScopeWorkflow
}
configurationChanges, err = getUpdateConfigurationVariables(configurationChanges, d.Get("id").(string), scope, apiClient)
if err != nil {
return client.DeployRequest{}, err
}
payload.ConfigurationChanges = &configurationChanges
}

payload.ConfigurationSetChanges, err = getEnvironmentConfigurationSetChanges(d, apiClient)
if err != nil {
return client.DeployRequest{}, err
}
configurationChanges = getUpdateConfigurationVariables(configurationChanges, d.Get("id").(string), scope, apiClient)
payload.ConfigurationChanges = &configurationChanges
}

if userRequiresApproval, ok := d.GetOk("requires_approval"); ok {
userRequiresApproval := userRequiresApproval.(bool)
payload.UserRequiresApproval = &userRequiresApproval
}

return payload
return payload, nil
}

func getTTl(date string) client.TTL {
Expand All @@ -1004,15 +1131,15 @@ func getTTl(date string) client.TTL {
}
}

func getUpdateConfigurationVariables(configurationChanges client.ConfigurationChanges, environmentId string, scope client.Scope, apiClient client.ApiClientInterface) client.ConfigurationChanges {
func getUpdateConfigurationVariables(configurationChanges client.ConfigurationChanges, environmentId string, scope client.Scope, apiClient client.ApiClientInterface) (client.ConfigurationChanges, error) {
existVariables, err := apiClient.ConfigurationVariablesByScope(scope, environmentId)
if err != nil {
diag.Errorf("could not get environment configuration variables: %v", err)
return client.ConfigurationChanges{}, fmt.Errorf("could not get environment configuration variables: %w", err)
}
configurationChanges = linkToExistConfigurationVariables(configurationChanges, existVariables)
configurationChanges = deleteUnusedConfigurationVariables(configurationChanges, existVariables)

return configurationChanges
return configurationChanges, nil
}

func getConfigurationVariablesFromSchema(configuration []interface{}) client.ConfigurationChanges {
Expand Down Expand Up @@ -1188,6 +1315,11 @@ func resourceEnvironmentImporter(ctx context.Context, d *schema.ResourceData, me
return nil, fmt.Errorf("could not get environment configuration variables: %v", err)
}

environmentVariableSetIds, err := getEnvironmentVariableSetIdsFromApi(d, apiClient)
if err != nil {
return nil, fmt.Errorf("could not get environment variable sets: %v", err)
}

d.Set("deployment_id", environment.LatestDeploymentLogId)

if environment.IsSingleUseBlueprint {
Expand All @@ -1206,7 +1338,7 @@ func resourceEnvironmentImporter(ctx context.Context, d *schema.ResourceData, me
}
}

setEnvironmentSchema(ctx, d, environment, environmentConfigurationVariables)
setEnvironmentSchema(ctx, d, environment, environmentConfigurationVariables, environmentVariableSetIds)

if environment.IsRemoteBackend != nil {
d.Set("is_remote_backend", *environment.IsRemoteBackend)
Expand Down
Loading

0 comments on commit 82ee076

Please sign in to comment.