Skip to content

Commit

Permalink
Feat: Add support for Environment Discovery API (#802)
Browse files Browse the repository at this point in the history
* Feat: Add support for Environment Discovery API

* added tests

* add create and delete

* added optional

* added optional

* adding tests - WIP

* added tests

* added tests

* fix test

* update go version

* update go version

* added import

* Update env0/resource_environment_discovery_configuration.go

Co-authored-by: Yaron Yarimi <[email protected]>

* small rename refactoring

* fix race condition

* add sleep to avoid race condition

---------

Co-authored-by: Yaron Yarimi <[email protected]>
  • Loading branch information
TomerHeber and yaronya authored Mar 17, 2024
1 parent 1b16141 commit 71032c4
Show file tree
Hide file tree
Showing 24 changed files with 1,518 additions and 31 deletions.
9 changes: 2 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ env:
ENV0_API_ENDPOINT: ${{ secrets.ENV0_API_ENDPOINT }}
ENV0_API_KEY: ${{ secrets.TF_PROVIDER_INTEGRATION_TEST_API_KEY }} # API Key for organization 'TF-provider-integration-tests' @ dev
ENV0_API_SECRET: ${{ secrets.TF_PROVIDER_INTEGRATION_TEST_API_SECRET }}
GO_VERSION: "1.20"
GO_VERSION: "1.21"
TERRAFORM_VERSION: 1.1.7

jobs:
Expand All @@ -33,19 +33,14 @@ jobs:
- name: Go vet
run: |
! go vet ./... | read
- name: Go staticcheck
uses: dominikh/[email protected]
with:
version: "2023.1.3"
install-go: false
- name: Go Test
run: go test -v ./...

# See terraform-provider-env0 README for integration tests prerequisites
integration-tests:
name: Integration Tests
runs-on: ubuntu-20.04
container: golang:1.20-alpine3.18
container: golang:1.21-alpine3.18
timeout-minutes: 20
steps:
- name: Install Terraform
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- main

env:
GO_VERSION: "1.20"
GO_VERSION: "1.21"

jobs:
generate-docs:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ on:
- "v*.*.*"

env:
GO_VERSION: "1.20"
GO_VERSION: "1.21"

jobs:
goreleaser:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ resource "env0_template" "example" {

## Development Setup

> **Supported Go Version: 1.20**
> **Supported Go Version: 1.21**
### Build

Expand Down
3 changes: 3 additions & 0 deletions client/api_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ type ApiClientInterface interface {
ProjectBudget(projectId string) (*ProjectBudget, error)
ProjectBudgetUpdate(projectId string, payload *ProjectBudgetUpdatePayload) (*ProjectBudget, error)
ProjectBudgetDelete(projectId string) error
PutEnvironmentDiscovery(projectId string, payload *EnvironmentDiscoveryPutPayload) (*EnvironmentDiscoveryPayload, error)
GetEnvironmentDiscovery(projectId string) (*EnvironmentDiscoveryPayload, error)
DeleteEnvironmentDiscovery(projectId string) error
}

func NewApiClient(client http.HttpClientInterface, defaultOrganizationId string) ApiClientInterface {
Expand Down
44 changes: 44 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.

68 changes: 68 additions & 0 deletions client/environment_discovery.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package client

type EnvironmentDiscoveryPutPayload struct {
GlobPattern string `json:"globPattern"`
EnvironmentPlacement string `json:"environmentPlacement"`
WorkspaceNaming string `json:"workspaceNaming"`
AutoDeployByCustomGlob string `json:"autoDeployByCustomGlob,omitempty"`
Repository string `json:"repository"`
TerraformVersion string `json:"terraformVersion,omitempty"`
OpentofuVersion string `json:"opentofuVersion,omitempty"`
TerragruntVersion string `json:"terragruntVersion,omitempty"`
TerragruntTfBinary string `json:"terragruntTfBinary,omitempty"`
IsTerragruntRunAll bool `json:"is_terragrunt_run_all"`
Type string `json:"type"`
GitlabProjectId int `json:"gitlabProjectId,omitempty"`
TokenId string `json:"tokenId,omitempty"`
SshKeys []TemplateSshKey `json:"sshKeys,omitempty"`
GithubInstallationId int `json:"githubInstallationId,omitempty"`
BitbucketClientKey string `json:"bitbucketClientKey,omitempty"`
IsAzureDevops bool `json:"isAzureDevOps"`
Retry TemplateRetry `json:"retry"`
}

type EnvironmentDiscoveryPayload struct {
Id string `json:"id"`
GlobPattern string `json:"globPattern"`
EnvironmentPlacement string `json:"environmentPlacement"`
WorkspaceNaming string `json:"workspaceNaming"`
AutoDeployByCustomGlob string `json:"autoDeployByCustomGlob"`
Repository string `json:"repository"`
TerraformVersion string `json:"terraformVersion"`
OpentofuVersion string `json:"opentofuVersion"`
TerragruntVersion string `json:"terragruntVersion"`
TerragruntTfBinary string `json:"terragruntTfBinary" tfschema:",omitempty"`
IsTerragruntRunAll bool `json:"is_terragrunt_run_all"`
Type string `json:"type"`
GitlabProjectId int `json:"gitlabProjectId"`
TokenId string `json:"tokenId"`
SshKeys []TemplateSshKey `json:"sshKeys" tfschema:"-"`
GithubInstallationId int `json:"githubInstallationId"`
BitbucketClientKey string `json:"bitbucketClientKey"`
IsAzureDevops bool `json:"isAzureDevOps"`
Retry TemplateRetry `json:"retry" tfschema:"-"`
}

func (client *ApiClient) PutEnvironmentDiscovery(projectId string, payload *EnvironmentDiscoveryPutPayload) (*EnvironmentDiscoveryPayload, error) {
var result EnvironmentDiscoveryPayload

if err := client.http.Put("/environment-discovery/projects/"+projectId, payload, &result); err != nil {
return nil, err
}

return &result, nil
}

func (client *ApiClient) GetEnvironmentDiscovery(projectId string) (*EnvironmentDiscoveryPayload, error) {
var result EnvironmentDiscoveryPayload

if err := client.http.Get("/environment-discovery/projects/"+projectId, nil, &result); err != nil {
return nil, err
}

return &result, nil
}

func (client *ApiClient) DeleteEnvironmentDiscovery(projectId string) error {
return client.http.Delete("/environment-discovery/projects/"+projectId, nil)
}
130 changes: 130 additions & 0 deletions client/environment_discovery_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package client_test

import (
"errors"

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

var _ = Describe("Environment Discovery", func() {
mockError := errors.New("error")

projectId := "pid"

mockEnvironmentDiscovery := EnvironmentDiscoveryPayload{
Id: "id",
GlobPattern: "**",
EnvironmentPlacement: "topProject",
WorkspaceNaming: "default",
AutoDeployByCustomGlob: "**",
Repository: "https://re.po",
TerraformVersion: "1.5.6",
Type: "terraform",
GithubInstallationId: 1000,
}

Describe("PUT", func() {
putPayload := EnvironmentDiscoveryPutPayload{
GlobPattern: mockEnvironmentDiscovery.GlobPattern,
EnvironmentPlacement: mockEnvironmentDiscovery.EnvironmentPlacement,
WorkspaceNaming: mockEnvironmentDiscovery.WorkspaceNaming,
AutoDeployByCustomGlob: mockEnvironmentDiscovery.AutoDeployByCustomGlob,
Repository: mockEnvironmentDiscovery.Repository,
TerraformVersion: mockEnvironmentDiscovery.TerraformVersion,
Type: mockEnvironmentDiscovery.Type,
GithubInstallationId: mockEnvironmentDiscovery.GithubInstallationId,
}

Describe("success", func() {
var ret *EnvironmentDiscoveryPayload

BeforeEach(func() {
httpCall = mockHttpClient.EXPECT().
Put("/environment-discovery/projects/"+projectId, &putPayload, gomock.Any()).
Do(func(path string, request interface{}, response *EnvironmentDiscoveryPayload) {
*response = mockEnvironmentDiscovery
}).Times(1)
ret, _ = apiClient.PutEnvironmentDiscovery(projectId, &putPayload)
})

It("Should return environment discovery", func() {
Expect(*ret).To(Equal(mockEnvironmentDiscovery))
})
})

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

BeforeEach(func() {
httpCall = mockHttpClient.EXPECT().
Put("/environment-discovery/projects/"+projectId, &putPayload, gomock.Any()).Return(mockError).Times(1)
_, err = apiClient.PutEnvironmentDiscovery(projectId, &putPayload)
})

It("Should return error", func() {
Expect(err).To(Equal(mockError))
})
})
})

Describe("GET", func() {
Describe("success", func() {
var ret *EnvironmentDiscoveryPayload

BeforeEach(func() {
httpCall = mockHttpClient.EXPECT().
Get("/environment-discovery/projects/"+projectId, nil, gomock.Any()).
Do(func(path string, request interface{}, response *EnvironmentDiscoveryPayload) {
*response = mockEnvironmentDiscovery
}).Times(1)
ret, _ = apiClient.GetEnvironmentDiscovery(projectId)
})

It("Should return environment discovery", func() {
Expect(*ret).To(Equal(mockEnvironmentDiscovery))
})
})

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

BeforeEach(func() {
httpCall = mockHttpClient.EXPECT().
Get("/environment-discovery/projects/"+projectId, nil, gomock.Any()).Return(mockError).Times(1)
_, err = apiClient.GetEnvironmentDiscovery(projectId)
})

It("Should return error", func() {
Expect(err).To(Equal(mockError))
})
})
})

Describe("DELETE", func() {
Describe("success", func() {
BeforeEach(func() {
httpCall = mockHttpClient.EXPECT().Delete("/environment-discovery/projects/"+projectId, nil).Times(1)
apiClient.DeleteEnvironmentDiscovery(projectId)
})

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

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

BeforeEach(func() {
httpCall = mockHttpClient.EXPECT().
Delete("/environment-discovery/projects/"+projectId, nil).Return(mockError).Times(1)
err = apiClient.DeleteEnvironmentDiscovery(projectId)
})

It("Should return error", func() {
Expect(err).To(Equal(mockError))
})
})
})
})
8 changes: 4 additions & 4 deletions env0/data_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func dataProjectRead(ctx context.Context, d *schema.ResourceData, meta interface
return nil
}

func filterByParentProjectId(name string, parentId string, projects []client.Project) ([]client.Project, error) {
func filterByParentProjectId(parentId string, projects []client.Project) ([]client.Project, error) {
filteredProjects := make([]client.Project, 0)
for _, project := range projects {
if len(project.ParentProjectId) == 0 {
Expand All @@ -107,7 +107,7 @@ func filterByParentProjectId(name string, parentId string, projects []client.Pro
return filteredProjects, nil
}

func filterByParentProjectName(name string, parentName string, projects []client.Project, meta interface{}) ([]client.Project, error) {
func filterByParentProjectName(parentName string, projects []client.Project, meta interface{}) ([]client.Project, error) {
filteredProjects := make([]client.Project, 0)
for _, project := range projects {
if len(project.ParentProjectId) == 0 {
Expand Down Expand Up @@ -142,13 +142,13 @@ func getProjectByName(name string, parentId string, parentName string, meta inte
}
if len(parentId) > 0 {
// Use parentId filter to reduce the results.
projectsByName, err = filterByParentProjectId(name, parentId, projectsByName)
projectsByName, err = filterByParentProjectId(parentId, projectsByName)
if err != nil {
return client.Project{}, err
}
} else if len(parentName) > 0 {
// Use parentName filter to reduce the results.
projectsByName, err = filterByParentProjectName(name, parentName, projectsByName, meta)
projectsByName, err = filterByParentProjectName(parentName, projectsByName, meta)
if err != nil {
return client.Project{}, err
}
Expand Down
4 changes: 2 additions & 2 deletions env0/data_project_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestPolicyDataSource(t *testing.T) {
resourceName := "test_policy"
accessor := dataSourceAccessor(resourceType, resourceName)

getValidTestCase := func(input map[string]interface{}) resource.TestCase {
getValidTestCase := func() resource.TestCase {
return resource.TestCase{
Steps: []resource.TestStep{
{
Expand Down Expand Up @@ -57,7 +57,7 @@ func TestPolicyDataSource(t *testing.T) {
}

t.Run("valid", func(t *testing.T) {
runUnitTest(t, getValidTestCase(map[string]interface{}{}), func(mock *client.MockApiClientInterface) {
runUnitTest(t, getValidTestCase(), func(mock *client.MockApiClientInterface) {
mock.EXPECT().Policy(policy.ProjectId).AnyTimes().Return(policy, nil)
})
})
Expand Down
1 change: 1 addition & 0 deletions env0/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ func Provider(version string) plugin.ProviderFunc {
"env0_approval_policy": resourceApprovalPolicy(),
"env0_approval_policy_assignment": resourceApprovalPolicyAssignment(),
"env0_project_budget": resourceProjectBudget(),
"env0_environment_discovery_configuration": resourceEnvironmentDiscoveryConfiguration(),
},
}

Expand Down
Loading

0 comments on commit 71032c4

Please sign in to comment.