Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fest: Environment Resource - configuration variables read & delete #182

Merged
merged 7 commits into from
Dec 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ provider "env0" {

## Dev setup

**make sure you run with go version 1.16**
### Build
- Use the `./build.sh` script.
- The output binary is called `terraform-provider-env0`
Expand Down
1 change: 1 addition & 0 deletions client/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ type ConfigurationVariable struct {
Description string `json:"description,omitempty"`
Type *ConfigurationVariableType `json:"type,omitempty"`
Schema *ConfigurationVariableSchema `json:"schema,omitempty"`
ToDelete *bool `json:"toDelete,omitempty"`
}

type Scope string
Expand Down
2 changes: 1 addition & 1 deletion docs/data-sources/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ output "environment_name" {
- **approve_plan_automatically** (Boolean) the default require approval of the environment
- **auto_deploy_on_path_changes_only** (Boolean) does continuous deployment on file changes in path enable
- **deploy_on_push** (Boolean) does continuous deployment is enabled
- **latest_deployment_log_id** (String) the id of the latest deployment
- **deployment_id** (String) the id of the latest deployment
- **project_id** (String) project id of the environment
- **revision** (String) the last deployed revision
- **run_plan_on_pull_requests** (Boolean) does pr plan enable
Expand Down
7 changes: 2 additions & 5 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ provider "env0" {
<!-- schema generated by tfplugindocs -->
## Schema

### Required

- **api_key** (String, Sensitive) env0 api key (https://developer.env0.com/docs/api/YXBpOjY4Njc2-env0-api#creating-an-api-key)
- **api_secret** (String, Sensitive) env0 api key secret

### Optional

- **api_endpoint** (String) override api endpoint (used for testing)
- **api_key** (String, Sensitive) env0 api key (https://developer.env0.com/docs/api/YXBpOjY4Njc2-env0-api#creating-an-api-key)
- **api_secret** (String, Sensitive) env0 api key secret
3 changes: 1 addition & 2 deletions env0/data_environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,8 @@ func dataEnvironmentRead(ctx context.Context, d *schema.ResourceData, meta inter
return err
}
}

d.SetId(environment.Id)
setEnvironmentSchema(d, environment)
setEnvironmentSchema(d, environment, client.ConfigurationChanges{})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursed language
Why no optional parameters?
damn you go

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hate this
also no Ternary Operator ?:

as for the integration tests will be added on a different ticket to unblock the QA PARTY 🎉

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats not a blocker, as those are terraform files we need to update

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update this file
https://github.com/env0/terraform-provider-env0/blob/main/tests/integration/012_environment/main.tf
add the configuration part for the resource to create it and also data to read it
then expose a output and you need to assert the output is as you expect using this file
https://github.com/env0/terraform-provider-env0/blob/main/tests/integration/012_environment/expected_outputs.json

really small part

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not here?
thats really small change

Copy link
Contributor Author

@GiliFaroEnv0 GiliFaroEnv0 Dec 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eranelbaz its not tried alot but its not
since on deploying the scope of the configuration variable changes to DEPLOYMENT and we can’t search by this scope

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets work this out in a next pr

d.Set("status", environment.Status)
d.Set("deployment_id", environment.LatestDeploymentLogId)
return nil
Expand Down
127 changes: 108 additions & 19 deletions env0/resource_environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ func resourceEnvironment() *schema.Resource {
Description: "variable type (allowed values are: terraform, environment)",
Default: "environment",
Optional: true,
ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
value := val.(string)
if value != "environment" && value != "terraform" {
errs = append(errs, fmt.Errorf("%q can be either \"environment\" or \"terraform\", got: %q", key, value))
}
return
},
},
"description": &schema.Schema{
Type: schema.TypeString,
Expand Down Expand Up @@ -161,7 +168,7 @@ func resourceEnvironment() *schema.Resource {
}
}

func setEnvironmentSchema(d *schema.ResourceData, environment client.Environment) {
func setEnvironmentSchema(d *schema.ResourceData, environment client.Environment, configurationVariables client.ConfigurationChanges) {
d.Set("id", environment.Id)
d.Set("name", environment.Name)
d.Set("project_id", environment.ProjectId)
Expand All @@ -184,22 +191,45 @@ func setEnvironmentSchema(d *schema.ResourceData, environment client.Environment
if environment.AutoDeployOnPathChangesOnly != nil {
d.Set("auto_deploy_on_path_changes_only", *environment.AutoDeployOnPathChangesOnly)
}
//TODO: env\terraform variables
setEnvironmentConfigurationSchema(d, configurationVariables)
}

func setEnvironmentConfigurationSchema(d *schema.ResourceData, configurationVariables []client.ConfigurationVariable) {
for index, configurationVariable := range configurationVariables {
variable := make(map[string]interface{})
variable["name"] = configurationVariable.Name
variable["value"] = configurationVariable.Value
variable["type"] = configurationVariable.Type
if configurationVariable.Description != "" {
variable["description"] = configurationVariable.Description
}
if configurationVariable.IsSensitive != nil {
variable["is_sensitive"] = configurationVariable.IsSensitive
}
if configurationVariable.Schema != nil {
variable["schema_type"] = configurationVariable.Schema.Type
variable["schema_enum"] = configurationVariable.Schema.Enum
}
d.Set(fmt.Sprintf(`configuration.%d`, index), variable)
}
}

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

payload := getCreatePayload(d)
payload := getCreatePayload(d, apiClient)

environment, err := apiClient.EnvironmentCreate(payload)
if err != nil {
return diag.Errorf("could not create environment: %v", err)
}

environmentConfigurationVariables := client.ConfigurationChanges{}
if payload.DeployRequest.ConfigurationChanges != nil {
environmentConfigurationVariables = *payload.DeployRequest.ConfigurationChanges
}
d.SetId(environment.Id)
d.Set("deployment_id", environment.LatestDeploymentLogId)
setEnvironmentSchema(d, environment)
setEnvironmentSchema(d, environment, environmentConfigurationVariables)

return nil
}
Expand All @@ -211,22 +241,18 @@ func resourceEnvironmentRead(ctx context.Context, d *schema.ResourceData, meta i
if err != nil {
return diag.Errorf("could not get environment: %v", err)
}

setEnvironmentSchema(d, environment)
environmentConfigurationVariables, err := apiClient.ConfigurationVariables(client.ScopeEnvironment, environment.Id)
if err != nil {
return diag.Errorf("could not get environment configuration variables: %v", err)
}
setEnvironmentSchema(d, environment, environmentConfigurationVariables)

return nil
}

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

if shouldDeploy(d) {
err := deploy(d, apiClient)
if err != nil {
return err
}
}

// TODO: update TTL if needed, also consider not updating ttl if deploy happened (cause we update ttl there too)

if shouldUpdate(d) {
Expand All @@ -243,6 +269,13 @@ func resourceEnvironmentUpdate(ctx context.Context, d *schema.ResourceData, meta
}
}

if shouldDeploy(d) {
err := deploy(d, apiClient)
if err != nil {
return err
}
}

return nil
}

Expand All @@ -259,7 +292,7 @@ func shouldUpdateTTL(d *schema.ResourceData) bool {
}

func deploy(d *schema.ResourceData, apiClient client.ApiClientInterface) diag.Diagnostics {
deployPayload := getDeployPayload(d)
deployPayload := getDeployPayload(d, apiClient, true)
deployResponse, err := apiClient.EnvironmentDeploy(d.Id(), deployPayload)
if err != nil {
return diag.Errorf("failed deploying environment: %v", err)
Expand Down Expand Up @@ -303,7 +336,7 @@ func resourceEnvironmentDelete(ctx context.Context, d *schema.ResourceData, meta
return nil
}

func getCreatePayload(d *schema.ResourceData) client.EnvironmentCreate {
func getCreatePayload(d *schema.ResourceData, apiClient client.ApiClientInterface) client.EnvironmentCreate {
payload := client.EnvironmentCreate{}

if name, ok := d.GetOk("name"); ok {
Expand Down Expand Up @@ -346,7 +379,7 @@ func getCreatePayload(d *schema.ResourceData) client.EnvironmentCreate {
payload.TTL = &ttlPayload
}

deployPayload := getDeployPayload(d)
deployPayload := getDeployPayload(d, apiClient, false)

payload.DeployRequest = &deployPayload

Expand Down Expand Up @@ -382,7 +415,7 @@ func getUpdatePayload(d *schema.ResourceData) client.EnvironmentUpdate {
return payload
}

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

if templateId, ok := d.GetOk("template_id"); ok {
Expand All @@ -395,6 +428,9 @@ func getDeployPayload(d *schema.ResourceData) client.DeployRequest {

if configuration, ok := d.GetOk("configuration"); ok {
configurationChanges := getConfigurationVariables(configuration.([]interface{}))
if isRedeploy {
configurationChanges = getUpdateConfigurationVariables(configurationChanges, d.Get("id").(string), apiClient)
}
payload.ConfigurationChanges = &configurationChanges
}

Expand All @@ -419,15 +455,63 @@ func getTTl(date string) client.TTL {
}
}

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

func getConfigurationVariables(configuration []interface{}) client.ConfigurationChanges {
configurationChanges := client.ConfigurationChanges{}
for _, variable := range configuration {
configurationVariable := getConfigurationVariableForEnvironment(variable.(map[string]interface{}))
configurationChanges = append(configurationChanges, configurationVariable)
}

return configurationChanges
}

func deleteUnusedConfigurationVariables(configurationChanges client.ConfigurationChanges, existVariables client.ConfigurationChanges) client.ConfigurationChanges {
for _, existVariable := range existVariables {
if isExist, _ := isVariableExist(configurationChanges, existVariable); isExist != true {
toDelete := true
existVariable.ToDelete = &toDelete
configurationChanges = append(configurationChanges, existVariable)
}
}
return configurationChanges
}

func linkToExistConfigurationVariables(configurationChanges client.ConfigurationChanges, existVariables client.ConfigurationChanges) client.ConfigurationChanges {
updateConfigurationChanges := client.ConfigurationChanges{}
for _, change := range configurationChanges {
if isExist, existVariable := isVariableExist(existVariables, change); isExist {
change.Id = existVariable.Id
}
updateConfigurationChanges = append(updateConfigurationChanges, change)
}
return updateConfigurationChanges
}

func isVariableExist(variables client.ConfigurationChanges, search client.ConfigurationVariable) (bool, client.ConfigurationVariable) {
for _, variable := range variables {
if variable.Name == search.Name && typeEqual(variable, search) {
return true, variable
}
}
return false, client.ConfigurationVariable{}
}

func typeEqual(variable client.ConfigurationVariable, search client.ConfigurationVariable) bool {
return *variable.Type == *search.Type ||
variable.Type == nil && *search.Type == client.ConfigurationVariableTypeEnvironment ||
search.Type == nil && *variable.Type == client.ConfigurationVariableTypeEnvironment
}

func getConfigurationVariableForEnvironment(variable map[string]interface{}) client.ConfigurationVariable {
varType := client.VariableTypes[variable["type"].(string)]

Expand Down Expand Up @@ -515,8 +599,13 @@ func resourceEnvironmentImport(ctx context.Context, d *schema.ResourceData, meta

environment, getErr = getEnvironmentByName(id, meta)
}
apiClient := meta.(client.ApiClientInterface)
d.SetId(environment.Id)
setEnvironmentSchema(d, environment)
environmentConfigurationVariables, err := apiClient.ConfigurationVariables(client.ScopeEnvironment, environment.Id)
if err != nil {
return nil, errors.New(fmt.Sprintf("could not get environment configuration variables: %v", err))
}
setEnvironmentSchema(d, environment, environmentConfigurationVariables)

if getErr != nil {
return nil, errors.New(getErr[0].Summary)
Expand Down
Loading