Skip to content

Commit

Permalink
Feat: workflow trigger new resources (#627)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomerHeber authored Mar 23, 2023
1 parent f4dc42d commit 8b06996
Show file tree
Hide file tree
Showing 7 changed files with 345 additions and 1 deletion.
1 change: 1 addition & 0 deletions env0/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ func Provider(version string) plugin.ProviderFunc {
"env0_team": resourceTeam(),
"env0_environment": resourceEnvironment(),
"env0_workflow_triggers": resourceWorkflowTriggers(),
"env0_workflow_trigger": resourceWorkflowTrigger(),
"env0_environment_scheduling": resourceEnvironmentScheduling(),
"env0_environment_drift_detection": resourceDriftDetection(),
"env0_notification": resourceNotification(),
Expand Down
90 changes: 90 additions & 0 deletions env0/resource_workflow_trigger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package env0

import (
"context"
"log"

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

func resourceWorkflowTrigger() *schema.Resource {
return &schema.Resource{
CreateContext: resourceWorkflowTriggerCreate,
ReadContext: resourceWorkflowTriggerRead,
DeleteContext: resourceWorkflowTriggerDelete,
Description: "cannot be used with env0_workflow_triggers",

Schema: map[string]*schema.Schema{
"environment_id": {
Type: schema.TypeString,
Description: "id of the source environment",
Required: true,
ForceNew: true,
},
"downstream_environment_id": {
Type: schema.TypeString,
Description: "environment to trigger",
Required: true,
ForceNew: true,
},
},
}
}

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

environmentId := d.Get("environment_id").(string)
downstreamEnvironmentId := d.Get("downstream_environment_id").(string)

triggers, err := apiClient.WorkflowTrigger(environmentId)

if err != nil {
return diag.Errorf("could not get workflow triggers: %v", err)
}

for _, trigger := range triggers {
if trigger.Id == downstreamEnvironmentId {
return nil
}
}

log.Printf("[WARN] Drift Detected: Terraform will remove %s from state", d.Id())
d.SetId("")

return nil
}

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

environmentId := d.Get("environment_id").(string)
downstreamEnvironmentId := d.Get("downstream_environment_id").(string)

payload := client.WorkflowTriggerEnvironments{DownstreamEnvironmentIds: []string{downstreamEnvironmentId}}

if err := apiClient.SubscribeWorkflowTrigger(environmentId, payload); err != nil {
return diag.Errorf("failed to subscribe a workflow trigger: %v", err)
}

d.SetId(environmentId + "_" + downstreamEnvironmentId)

return nil
}

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

environmentId := d.Get("environment_id").(string)
downstreamEnvironmentId := d.Get("downstream_environment_id").(string)

payload := client.WorkflowTriggerEnvironments{DownstreamEnvironmentIds: []string{downstreamEnvironmentId}}

if err := apiClient.UnsubscribeWorkflowTrigger(environmentId, payload); err != nil {
return diag.Errorf("failed to unsubscribe a workflow trigger: %v", err)
}

return nil
}
197 changes: 197 additions & 0 deletions env0/resource_workflow_trigger_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package env0

import (
"errors"
"regexp"
"testing"

"github.com/env0/terraform-provider-env0/client"
"github.com/golang/mock/gomock"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestUnitWorkflowTriggerResource(t *testing.T) {
resourceType := "env0_workflow_trigger"
resourceName := "test"
accessor := resourceAccessor(resourceType, resourceName)
environmentId := "environment_id"
triggerId := "trigger_environment_id"
otherTriggerId := "other_trigger_environment_id"

t.Run("Success", func(t *testing.T) {

testCase := resource.TestCase{
Steps: []resource.TestStep{
{
Config: resourceConfigCreate(resourceType, resourceName, map[string]interface{}{
"environment_id": environmentId,
"downstream_environment_id": triggerId,
}),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(accessor, "id", environmentId+"_"+triggerId),
resource.TestCheckResourceAttr(accessor, "environment_id", environmentId),
resource.TestCheckResourceAttr(accessor, "downstream_environment_id", triggerId),
),
},
{
Config: resourceConfigCreate(resourceType, resourceName, map[string]interface{}{
"environment_id": environmentId,
"downstream_environment_id": otherTriggerId,
}),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(accessor, "id", environmentId+"_"+otherTriggerId),
resource.TestCheckResourceAttr(accessor, "environment_id", environmentId),
resource.TestCheckResourceAttr(accessor, "downstream_environment_id", otherTriggerId),
),
},
},
}

runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) {
gomock.InOrder(
mock.EXPECT().SubscribeWorkflowTrigger(environmentId, client.WorkflowTriggerEnvironments{
DownstreamEnvironmentIds: []string{triggerId},
}).Times(1).Return(nil),
mock.EXPECT().WorkflowTrigger(environmentId).Times(2).Return([]client.WorkflowTrigger{
{
Id: triggerId,
},
}, nil),
mock.EXPECT().UnsubscribeWorkflowTrigger(environmentId, client.WorkflowTriggerEnvironments{
DownstreamEnvironmentIds: []string{triggerId},
}).Times(1).Return(nil),
mock.EXPECT().SubscribeWorkflowTrigger(environmentId, client.WorkflowTriggerEnvironments{
DownstreamEnvironmentIds: []string{otherTriggerId},
}).Times(1).Return(nil),
mock.EXPECT().WorkflowTrigger(environmentId).Times(1).Return([]client.WorkflowTrigger{
{
Id: otherTriggerId,
},
}, nil),
mock.EXPECT().UnsubscribeWorkflowTrigger(environmentId, client.WorkflowTriggerEnvironments{
DownstreamEnvironmentIds: []string{otherTriggerId},
}).Times(1).Return(nil),
)
})

})

t.Run("Failure in Get Triggers", func(t *testing.T) {
testCase := resource.TestCase{
Steps: []resource.TestStep{
{
Config: resourceConfigCreate(resourceType, resourceName, map[string]interface{}{
"environment_id": environmentId,
"downstream_environment_id": triggerId,
}),
ExpectError: regexp.MustCompile("could not get workflow triggers: error"),
},
},
}

runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) {
gomock.InOrder(
mock.EXPECT().SubscribeWorkflowTrigger(environmentId, client.WorkflowTriggerEnvironments{
DownstreamEnvironmentIds: []string{triggerId},
}).Times(1).Return(nil),
mock.EXPECT().WorkflowTrigger(environmentId).Times(1).Return(nil, errors.New("error")),
mock.EXPECT().UnsubscribeWorkflowTrigger(environmentId, client.WorkflowTriggerEnvironments{
DownstreamEnvironmentIds: []string{triggerId},
}).Times(1).Return(nil),
)
})
})

t.Run("Failure in Unsubscribe", func(t *testing.T) {
testCase := resource.TestCase{
Steps: []resource.TestStep{
{
Config: resourceConfigCreate(resourceType, resourceName, map[string]interface{}{
"environment_id": environmentId,
"downstream_environment_id": triggerId,
}),
},
{
Config: resourceConfigCreate(resourceType, resourceName, map[string]interface{}{
"environment_id": environmentId,
"downstream_environment_id": otherTriggerId,
}),
ExpectError: regexp.MustCompile("failed to unsubscribe a workflow trigger: error"),
},
},
}

runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) {
gomock.InOrder(
mock.EXPECT().SubscribeWorkflowTrigger(environmentId, client.WorkflowTriggerEnvironments{
DownstreamEnvironmentIds: []string{triggerId},
}).Times(1).Return(nil),
mock.EXPECT().WorkflowTrigger(environmentId).Times(2).Return([]client.WorkflowTrigger{
{
Id: triggerId,
},
}, nil),
mock.EXPECT().UnsubscribeWorkflowTrigger(environmentId, client.WorkflowTriggerEnvironments{
DownstreamEnvironmentIds: []string{triggerId},
}).Times(1).Return(errors.New("error")),
mock.EXPECT().UnsubscribeWorkflowTrigger(environmentId, client.WorkflowTriggerEnvironments{
DownstreamEnvironmentIds: []string{triggerId},
}).Times(1).Return(nil),
)
})
})

t.Run("Failure in Subscribe", func(t *testing.T) {
testCase := resource.TestCase{
Steps: []resource.TestStep{
{
Config: resourceConfigCreate(resourceType, resourceName, map[string]interface{}{
"environment_id": environmentId,
"downstream_environment_id": triggerId,
}),
ExpectError: regexp.MustCompile("failed to subscribe a workflow trigger: error"),
},
},
}

runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) {
gomock.InOrder(
mock.EXPECT().SubscribeWorkflowTrigger(environmentId, client.WorkflowTriggerEnvironments{
DownstreamEnvironmentIds: []string{triggerId},
}).Times(1).Return(errors.New("error")),
)
})
})

t.Run("Drift", func(t *testing.T) {
testCase := resource.TestCase{
Steps: []resource.TestStep{
{
Config: resourceConfigCreate(resourceType, resourceName, map[string]interface{}{
"environment_id": environmentId,
"downstream_environment_id": triggerId,
}),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(accessor, "id", environmentId+"_"+triggerId),
resource.TestCheckResourceAttr(accessor, "environment_id", environmentId),
resource.TestCheckResourceAttr(accessor, "downstream_environment_id", triggerId),
),
ExpectNonEmptyPlan: true,
},
},
}

runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) {
gomock.InOrder(
mock.EXPECT().SubscribeWorkflowTrigger(environmentId, client.WorkflowTriggerEnvironments{
DownstreamEnvironmentIds: []string{triggerId},
}).Times(1).Return(nil),
mock.EXPECT().WorkflowTrigger(environmentId).Times(2).Return([]client.WorkflowTrigger{
{
Id: otherTriggerId,
},
}, nil),
)
})
})
}
1 change: 1 addition & 0 deletions env0/resource_workflow_triggers.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ func resourceWorkflowTriggers() *schema.Resource {
ReadContext: resourceWorkflowTriggersRead,
UpdateContext: resourceWorkflowTriggersCreateOrUpdate,
DeleteContext: resourceWorkflowTriggersDelete,
Description: "cannot be used with env0_workflow_trigger",

Schema: map[string]*schema.Schema{
"environment_id": {
Expand Down
2 changes: 1 addition & 1 deletion env0/resource_workflow_triggers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestUnitWorkflowTriggerResource(t *testing.T) {
func TestUnitWorkflowTriggersResource(t *testing.T) {
resourceType := "env0_workflow_triggers"
resourceName := "test"
accessor := resourceAccessor(resourceType, resourceName)
Expand Down
32 changes: 32 additions & 0 deletions examples/resources/env0_workflow_trigger/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
data "env0_project" "default" {
name = "Default Organization Project"
}

resource "env0_template" "template" {
name = "Template for environment resource"
type = "terraform"
repository = "https://github.com/env0/templates"
path = "misc/null-resource"
terraform_version = "0.15.1"
}

resource "env0_environment" "the_trigger" {
force_destroy = true
name = "the_trigger"
project_id = data.env0_project.default.id
template_id = env0_template.template.id
approve_plan_automatically = true
}

resource "env0_environment" "downstream_environment" {
force_destroy = true
name = "downstream_environment"
project_id = data.env0_project.default.id
template_id = env0_template.template.id
approve_plan_automatically = true
}

resource "env0_workflow_trigger" "trigger_link" {
environment_id = env0_environment.the_trigger.id
downstream_environment_id = env0_environment.downstream_environment.id
}
23 changes: 23 additions & 0 deletions tests/integration/013_downstream_environments/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,26 @@ resource "env0_workflow_triggers" "trigger_link" {
env0_environment.downstream_environment.id
]
}

resource "env0_environment" "the_trigger_2" {
depends_on = [env0_template_project_assignment.assignment]
force_destroy = true
name = "the_trigger-${random_string.random.result}-2"
project_id = env0_project.test_project.id
template_id = env0_template.template.id
approve_plan_automatically = true
}

resource "env0_environment" "downstream_environment_2" {
depends_on = [env0_template_project_assignment.assignment]
force_destroy = true
name = "downstream_environment-${random_string.random.result}-2"
project_id = env0_project.test_project.id
template_id = env0_template.template.id
approve_plan_automatically = true
}

resource "env0_workflow_trigger" "trigger_link_2" {
environment_id = env0_environment.the_trigger_2.id
downstream_environment_id = env0_environment.downstream_environment_2.id
}

0 comments on commit 8b06996

Please sign in to comment.