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

Feat: add support for 'move environment' backend #939

Merged
merged 2 commits into from
Aug 22, 2024
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 client/api_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type ApiClientInterface interface {
EnvironmentUpdate(id string, payload EnvironmentUpdate) (Environment, error)
EnvironmentDeploy(id string, payload DeployRequest) (EnvironmentDeployResponse, error)
EnvironmentUpdateTTL(id string, payload TTL) (Environment, error)
EnvironmentMove(id string, projectId string) error
EnvironmentScheduling(environmentId string) (EnvironmentScheduling, error)
EnvironmentSchedulingUpdate(environmentId string, payload EnvironmentScheduling) (EnvironmentScheduling, error)
EnvironmentSchedulingDelete(environmentId string) error
Expand Down
14 changes: 14 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.

53 changes: 39 additions & 14 deletions client/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,34 @@ package client
import (
"encoding/json"
"fmt"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

type ConfigurationVariableType int

func (c *ConfigurationVariableType) ReadResourceData(fieldName string, d *schema.ResourceData) error {
val := d.Get(fieldName).(string)
intVal, ok := VariableTypes[val]
if !ok {
return fmt.Errorf("unknown configuration variable type %s", val)

intVal, err := GetConfigurationVariableType(val)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

unrelated - working on linting fixes as I go...

if err != nil {
return err
}

*c = intVal

return nil
}

func (c *ConfigurationVariableType) WriteResourceData(fieldName string, d *schema.ResourceData) error {
val := *c
valStr := ""
if val == 0 {
var valStr string
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

unrelated - working on linting fixes as I go...


switch val := *c; val {
case 0:
valStr = "environment"
} else if val == 1 {
case 1:
valStr = "terraform"
} else {
default:
return fmt.Errorf("unknown configuration variable type %d", val)
}

Expand All @@ -40,11 +44,6 @@ const (

type ConfigurationChanges []ConfigurationVariable

var VariableTypes = map[string]ConfigurationVariableType{
"terraform": ConfigurationVariableTypeTerraform,
"environment": ConfigurationVariableTypeEnvironment,
}

type TTL struct {
Type TTLType `json:"type"`
Value string `json:"value,omitempty"`
Expand Down Expand Up @@ -159,6 +158,21 @@ type EnvironmentCreateWithoutTemplate struct {
TemplateCreate TemplateCreatePayload
}

type EnvironmentMoveRequest struct {
ProjectId string `json:"projectId"`
}

func GetConfigurationVariableType(variableType string) (ConfigurationVariableType, error) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

unrelated - working on linting fixes as I go...

switch variableType {
case "terraform":
return ConfigurationVariableTypeTerraform, nil
case "environment":
return ConfigurationVariableTypeEnvironment, nil
default:
return 0, fmt.Errorf("unknown configuration variable type %s", variableType)
}
}

// The custom marshalJSON is used to return a flat JSON.
func (create EnvironmentCreateWithoutTemplate) MarshalJSON() ([]byte, error) {
// 1. Marshal to JSON both structs.
Expand All @@ -173,9 +187,11 @@ func (create EnvironmentCreateWithoutTemplate) MarshalJSON() ([]byte, error) {

// 2. Unmarshal both JSON byte arrays to two maps.
var ecm, tcm map[string]interface{}

if err := json.Unmarshal(ecb, &ecm); err != nil {
return nil, err
}

if err := json.Unmarshal(tcb, &tcm); err != nil {
return nil, err
}
Expand Down Expand Up @@ -254,8 +270,9 @@ func (client *ApiClient) EnvironmentCreateWithoutTemplate(payload EnvironmentCre

organizationId, err := client.OrganizationId()
if err != nil {
return result, nil
return result, err
}

payload.TemplateCreate.OrganizationId = organizationId

if err := client.http.Post("/environments/without-template", payload, &result); err != nil {
Expand Down Expand Up @@ -311,3 +328,11 @@ func (client *ApiClient) EnvironmentDeploy(id string, payload DeployRequest) (En
}
return result, nil
}

func (client *ApiClient) EnvironmentMove(id string, projectId string) error {
payload := &EnvironmentMoveRequest{
ProjectId: projectId,
}

return client.http.Post("/environments/"+id+"/move", payload, nil)
}
30 changes: 26 additions & 4 deletions client/environment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,13 +279,15 @@ var _ = Describe("Environment Client", func() {
})

Describe("EnvironmentDelete", func() {
var err error

BeforeEach(func() {
httpCall = mockHttpClient.EXPECT().Post("/environments/"+mockEnvironment.Id+"/destroy", nil, gomock.Any())
apiClient.EnvironmentDestroy(mockEnvironment.Id)
httpCall = mockHttpClient.EXPECT().Post("/environments/"+mockEnvironment.Id+"/destroy", nil, gomock.Any()).Times(1)
_, err = apiClient.EnvironmentDestroy(mockEnvironment.Id)
})

It("Should send a destroy request", func() {
httpCall.Times(1)
It("Should not return error", func() {
Expect(err).To(BeNil())
})
})

Expand Down Expand Up @@ -416,6 +418,26 @@ var _ = Describe("Environment Client", func() {
})
})
})

Describe("Environment Move", func() {
var err error

environmentId := "envid"
projectId := "projid"

request := EnvironmentMoveRequest{
ProjectId: projectId,
}

BeforeEach(func() {
httpCall = mockHttpClient.EXPECT().Post("/environments/"+environmentId+"/move", &request, nil).Times(1)
err = apiClient.EnvironmentMove(environmentId, projectId)
})

It("Should not return an error", func() {
Expect(err).To(BeNil())
})
})
})

func TestMarshalEnvironmentCreateWithoutTemplate(t *testing.T) {
Expand Down
10 changes: 8 additions & 2 deletions env0/resource_environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ func resourceEnvironment() *schema.Resource {
Type: schema.TypeString,
Description: "project id of the environment",
Required: true,
ForceNew: true,
},
"template_id": {
Type: schema.TypeString,
Expand Down Expand Up @@ -718,6 +717,13 @@ func resourceEnvironmentRead(ctx context.Context, d *schema.ResourceData, meta i
func resourceEnvironmentUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
apiClient := meta.(client.ApiClientInterface)

if d.HasChange("project_id") {
newProjectId := d.Get("project_id").(string)
if err := apiClient.EnvironmentMove(d.Id(), newProjectId); err != nil {
return diag.Errorf("failed to move environment to project id '%s': %s", newProjectId, err)
}
}

if shouldUpdate(d) {
if err := update(d, apiClient); err != nil {
return err
Expand Down Expand Up @@ -1274,7 +1280,7 @@ func typeEqual(variable client.ConfigurationVariable, search client.Configuratio
}

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

configurationVariable := client.ConfigurationVariable{
Name: variable["name"].(string),
Expand Down
76 changes: 76 additions & 0 deletions env0/resource_environment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,82 @@ func TestUnitEnvironmentResource(t *testing.T) {
})
})

t.Run("move environment", func(t *testing.T) {
newProjectId := "new-project-id"

environment := client.Environment{
Id: uuid.New().String(),
Name: "name",
ProjectId: "project-id",
LatestDeploymentLog: client.DeploymentLog{
BlueprintId: templateId,
},
}

environmentCreate := client.EnvironmentCreate{
Name: environment.Name,
ProjectId: environment.ProjectId,
DeployRequest: &client.DeployRequest{
BlueprintId: environment.LatestDeploymentLog.BlueprintId,
},
}

movedEnvironment := environment
movedEnvironment.ProjectId = newProjectId

testCase := resource.TestCase{
Steps: []resource.TestStep{
{
Config: resourceConfigCreate(resourceType, resourceName, map[string]interface{}{
"name": environment.Name,
"project_id": environment.ProjectId,
"template_id": templateId,
"force_destroy": true,
}),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(accessor, "id", environment.Id),
resource.TestCheckResourceAttr(accessor, "project_id", environment.ProjectId),
),
},
{
Config: resourceConfigCreate(resourceType, resourceName, map[string]interface{}{
"name": movedEnvironment.Name,
"project_id": movedEnvironment.ProjectId,
"template_id": templateId,
"force_destroy": true,
}),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(accessor, "id", environment.Id),
resource.TestCheckResourceAttr(accessor, "project_id", movedEnvironment.ProjectId),
),
},
},
}

runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) {
gomock.InOrder(
mock.EXPECT().Template(environment.LatestDeploymentLog.BlueprintId).Times(1).Return(template, nil),
mock.EXPECT().EnvironmentCreate(environmentCreate).Times(1).Return(environment, nil),

mock.EXPECT().Environment(environment.Id).Times(1).Return(environment, nil),
mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, environment.Id).Times(1).Return(client.ConfigurationChanges{}, nil),
mock.EXPECT().ConfigurationSetsAssignments("ENVIRONMENT", environment.Id).Times(1).Return(nil, nil),

mock.EXPECT().Environment(environment.Id).Times(1).Return(environment, nil),
mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, environment.Id).Times(1).Return(client.ConfigurationChanges{}, nil),
mock.EXPECT().ConfigurationSetsAssignments("ENVIRONMENT", environment.Id).Times(1).Return(nil, nil),

mock.EXPECT().EnvironmentMove(environment.Id, newProjectId).Return(nil),

mock.EXPECT().Environment(movedEnvironment.Id).Times(1).Return(movedEnvironment, nil),
mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, movedEnvironment.Id).Times(1).Return(client.ConfigurationChanges{}, nil),
mock.EXPECT().ConfigurationSetsAssignments("ENVIRONMENT", movedEnvironment.Id).Times(1).Return(nil, nil),

mock.EXPECT().EnvironmentDestroy(movedEnvironment.Id).Times(1),
)
})
})

t.Run("vcs pr comments enabled", func(t *testing.T) {
testCase := resource.TestCase{
Steps: []resource.TestStep{
Expand Down
12 changes: 11 additions & 1 deletion tests/integration/012_environment/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ resource "env0_project" "test_project" {
force_destroy = true
}

resource "env0_project" "test_project2" {
name = "Test-Project2-for-environment-${random_string.random.result}"
force_destroy = true
}

data "env0_template" "github_template_for_environment" {
name = "Github Integrated Template"
}
Expand All @@ -29,6 +34,11 @@ resource "env0_template_project_assignment" "assignment" {
project_id = env0_project.test_project.id
}

resource "env0_template_project_assignment" "assignment2" {
template_id = env0_template.template.id
project_id = env0_project.test_project2.id
}

resource "env0_environment" "auto_glob_envrironment" {
depends_on = [env0_template_project_assignment.assignment]
name = "environment-auto-glob-${random_string.random.result}"
Expand All @@ -45,7 +55,7 @@ resource "env0_environment" "example" {
depends_on = [env0_template_project_assignment.assignment]
force_destroy = true
name = "environment-${random_string.random.result}"
project_id = env0_project.test_project.id
project_id = var.second_run ? env0_project.test_project2.id : env0_project.test_project.id
template_id = env0_template.template.id
configuration {
name = "environment configuration variable"
Expand Down
Loading