diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d69aad0f..2156ad8c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,8 +16,12 @@ jobs: go-version: ${{ secrets.GO_VERSION }} - name: Checkout code uses: actions/checkout@v2 + - name: Generate mocks + run: | + go install github.com/golang/mock/mockgen@v1.6.0 + go generate client/api_client.go - name: Go fmt run: | - ! go fmt ./... | read + ! go fmt ./... | read - name: Go Test run: go test -v ./... diff --git a/client/api_client.go b/client/api_client.go index 29990f95..458262d9 100644 --- a/client/api_client.go +++ b/client/api_client.go @@ -63,6 +63,9 @@ type ApiClientInterface interface { EnvironmentSchedulingDelete(environmentId string) error WorkflowTrigger(environmentId string) ([]WorkflowTrigger, error) WorkflowTriggerUpsert(environmentId string, request WorkflowTriggerUpsertPayload) ([]WorkflowTrigger, error) + EnvironmentDriftDetection(environmentId string) (EnvironmentSchedulingExpression, error) + EnvironmentUpdateDriftDetection(environmentId string, payload EnvironmentSchedulingExpression) (EnvironmentSchedulingExpression, error) + EnvironmentStopDriftDetection(environmentId string) error } func NewApiClient(client http.HttpClientInterface) ApiClientInterface { diff --git a/client/api_client_mock.go b/client/api_client_mock.go index 0cdec939..e0605d19 100644 --- a/client/api_client_mock.go +++ b/client/api_client_mock.go @@ -190,12 +190,6 @@ func (m *MockApiClientInterface) ConfigurationVariablesById(arg0 string) (Config return ret0, ret1 } -// ConfigurationVariables indicates an expected call of ConfigurationVariables. -func (mr *MockApiClientInterfaceMockRecorder) ConfigurationVariables(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigurationVariablesByScope", reflect.TypeOf((*MockApiClientInterface)(nil).ConfigurationVariablesByScope), arg0, arg1) -} - // ConfigurationVariablesById indicates an expected call of ConfigurationVariablesById. func (mr *MockApiClientInterfaceMockRecorder) ConfigurationVariablesById(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() @@ -277,6 +271,21 @@ func (mr *MockApiClientInterfaceMockRecorder) EnvironmentDestroy(arg0 interface{ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnvironmentDestroy", reflect.TypeOf((*MockApiClientInterface)(nil).EnvironmentDestroy), arg0) } +// EnvironmentDriftDetection mocks base method. +func (m *MockApiClientInterface) EnvironmentDriftDetection(arg0 string) (EnvironmentSchedulingExpression, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnvironmentDriftDetection", arg0) + ret0, _ := ret[0].(EnvironmentSchedulingExpression) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EnvironmentDriftDetection indicates an expected call of EnvironmentDriftDetection. +func (mr *MockApiClientInterfaceMockRecorder) EnvironmentDriftDetection(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnvironmentDriftDetection", reflect.TypeOf((*MockApiClientInterface)(nil).EnvironmentDriftDetection), arg0) +} + // EnvironmentScheduling mocks base method. func (m *MockApiClientInterface) EnvironmentScheduling(arg0 string) (EnvironmentScheduling, error) { m.ctrl.T.Helper() @@ -321,6 +330,20 @@ func (mr *MockApiClientInterfaceMockRecorder) EnvironmentSchedulingUpdate(arg0, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnvironmentSchedulingUpdate", reflect.TypeOf((*MockApiClientInterface)(nil).EnvironmentSchedulingUpdate), arg0, arg1) } +// EnvironmentStopDriftDetection mocks base method. +func (m *MockApiClientInterface) EnvironmentStopDriftDetection(arg0 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnvironmentStopDriftDetection", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// EnvironmentStopDriftDetection indicates an expected call of EnvironmentStopDriftDetection. +func (mr *MockApiClientInterfaceMockRecorder) EnvironmentStopDriftDetection(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnvironmentStopDriftDetection", reflect.TypeOf((*MockApiClientInterface)(nil).EnvironmentStopDriftDetection), arg0) +} + // EnvironmentUpdate mocks base method. func (m *MockApiClientInterface) EnvironmentUpdate(arg0 string, arg1 EnvironmentUpdate) (Environment, error) { m.ctrl.T.Helper() @@ -336,6 +359,21 @@ func (mr *MockApiClientInterfaceMockRecorder) EnvironmentUpdate(arg0, arg1 inter return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnvironmentUpdate", reflect.TypeOf((*MockApiClientInterface)(nil).EnvironmentUpdate), arg0, arg1) } +// EnvironmentUpdateDriftDetection mocks base method. +func (m *MockApiClientInterface) EnvironmentUpdateDriftDetection(arg0 string, arg1 EnvironmentSchedulingExpression) (EnvironmentSchedulingExpression, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EnvironmentUpdateDriftDetection", arg0, arg1) + ret0, _ := ret[0].(EnvironmentSchedulingExpression) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EnvironmentUpdateDriftDetection indicates an expected call of EnvironmentUpdateDriftDetection. +func (mr *MockApiClientInterfaceMockRecorder) EnvironmentUpdateDriftDetection(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EnvironmentUpdateDriftDetection", reflect.TypeOf((*MockApiClientInterface)(nil).EnvironmentUpdateDriftDetection), arg0, arg1) +} + // EnvironmentUpdateTTL mocks base method. func (m *MockApiClientInterface) EnvironmentUpdateTTL(arg0 string, arg1 TTL) (Environment, error) { m.ctrl.T.Helper() diff --git a/client/environment_drift_detection.go b/client/environment_drift_detection.go new file mode 100644 index 00000000..2e1c42fe --- /dev/null +++ b/client/environment_drift_detection.go @@ -0,0 +1,31 @@ +package client + +func (self *ApiClient) EnvironmentDriftDetection(environmentId string) (EnvironmentSchedulingExpression, error) { + var result EnvironmentSchedulingExpression + + err := self.http.Get("/scheduling/drift-detection/environments/"+environmentId, nil, &result) + if err != nil { + return result, err + } + return result, nil +} + +func (self *ApiClient) EnvironmentUpdateDriftDetection(environmentId string, payload EnvironmentSchedulingExpression) (EnvironmentSchedulingExpression, error) { + var result EnvironmentSchedulingExpression + + err := self.http.Patch("/scheduling/drift-detection/environments/"+environmentId, payload, &result) + if err != nil { + return EnvironmentSchedulingExpression{}, err + } + + return result, nil +} + +func (self *ApiClient) EnvironmentStopDriftDetection(environmentId string) error { + err := self.http.Patch("/scheduling/drift-detection/environments/"+environmentId, EnvironmentSchedulingExpression{Enabled: false}, &EnvironmentScheduling{}) + if err != nil { + return err + } + + return nil +} diff --git a/client/environment_drift_detection_test.go b/client/environment_drift_detection_test.go new file mode 100644 index 00000000..2ccb12cd --- /dev/null +++ b/client/environment_drift_detection_test.go @@ -0,0 +1,114 @@ +package client_test + +import ( + "errors" + . "github.com/env0/terraform-provider-env0/client" + "github.com/golang/mock/gomock" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("EnvironmentDriftDetection", func() { + environmentId := "env" + path := "/scheduling/drift-detection/environments/" + environmentId + mockError := errors.New("I don't like milk") + schedulingExpression := EnvironmentSchedulingExpression{Cron: "0 * * * *", Enabled: true} + var driftResponse EnvironmentSchedulingExpression + + Describe("Get", func() { + Describe("Success", func() { + BeforeEach(func() { + httpCall = mockHttpClient.EXPECT(). + Get(path, nil, gomock.Any()). + Do(func(path string, request interface{}, response *EnvironmentSchedulingExpression) { + *response = schedulingExpression + }) + driftResponse, _ = apiClient.EnvironmentDriftDetection(environmentId) + }) + + It("Should return the drift scheduling response", func() { + Expect(driftResponse).To(Equal(schedulingExpression)) + }) + }) + + Describe("Fail", func() { + var err error + + BeforeEach(func() { + httpCall = mockHttpClient.EXPECT(). + Get(path, gomock.Any(), gomock.Any()). + Return(mockError) + + _, err = apiClient.EnvironmentDriftDetection(environmentId) + + }) + + It("Should fail if API call fails", func() { + Expect(err).To(Equal(mockError)) + }) + }) + }) + Describe("Update", func() { + Describe("Success", func() { + BeforeEach(func() { + httpCall = mockHttpClient.EXPECT(). + Patch(path, schedulingExpression, gomock.Any()). + Do(func(path string, request interface{}, response *EnvironmentSchedulingExpression) { + *response = schedulingExpression + }) + driftResponse, _ = apiClient.EnvironmentUpdateDriftDetection(environmentId, schedulingExpression) + }) + + It("Should return the drift scheduling response", func() { + Expect(driftResponse).To(Equal(schedulingExpression)) + }) + }) + + Describe("Fail", func() { + var err error + + BeforeEach(func() { + httpCall = mockHttpClient.EXPECT(). + Patch(path, schedulingExpression, gomock.Any()). + Return(mockError) + + _, err = apiClient.EnvironmentUpdateDriftDetection(environmentId, schedulingExpression) + + }) + + It("Should fail if API call fails", func() { + Expect(err).To(Equal(mockError)) + }) + }) + }) + Describe("Delete", func() { + Describe("Success", func() { + BeforeEach(func() { + httpCall = mockHttpClient.EXPECT(). + Patch(path, EnvironmentSchedulingExpression{Enabled: false}, gomock.Any()). + Do(func(path string, request interface{}, response *EnvironmentSchedulingExpression) { + *response = schedulingExpression + }) + _ = apiClient.EnvironmentStopDriftDetection(environmentId) + }) + }) + + Describe("Fail", func() { + var err error + + BeforeEach(func() { + httpCall = mockHttpClient.EXPECT(). + Patch(path, EnvironmentSchedulingExpression{Enabled: false}, gomock.Any()). + Return(mockError) + + err = apiClient.EnvironmentStopDriftDetection(environmentId) + + }) + + It("Should fail if API call fails", func() { + Expect(err).To(Equal(mockError)) + }) + }) + }) + +}) diff --git a/client/model.go b/client/model.go index 40c6cc24..297fd27e 100644 --- a/client/model.go +++ b/client/model.go @@ -393,7 +393,7 @@ type PolicyUpdatePayload struct { } type EnvironmentSchedulingExpression struct { - Cron string `json:"cron"` + Cron string `json:"cron,omitempty"` Enabled bool `json:"enabled"` } diff --git a/env0/data_configuration_variable_test.go b/env0/data_configuration_variable_test.go index 3f84f770..45e7b6ae 100644 --- a/env0/data_configuration_variable_test.go +++ b/env0/data_configuration_variable_test.go @@ -65,7 +65,7 @@ func TestUnitConfigurationVariableData(t *testing.T) { func(mock *client.MockApiClientInterface) { mock.EXPECT().ConfigurationVariablesById(configurationVariable.Id).AnyTimes(). Return(configurationVariable, nil) - mock.EXPECT().ConfigurationVariables(client.ScopeGlobal, "").AnyTimes(). + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeGlobal, "").AnyTimes(). Return([]client.ConfigurationVariable{configurationVariable}, nil) }) }) @@ -112,7 +112,7 @@ func TestUnitConfigurationVariableData(t *testing.T) { func(mock *client.MockApiClientInterface) { mock.EXPECT().ConfigurationVariablesById(configurationVariable.Id).AnyTimes(). Return(configurationVariable, nil) - mock.EXPECT().ConfigurationVariables(client.ScopeGlobal, "").AnyTimes(). + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeGlobal, "").AnyTimes(). Return([]client.ConfigurationVariable{configurationVariable}, nil) }) }) @@ -128,7 +128,7 @@ func TestUnitConfigurationVariableData(t *testing.T) { }, }, func(mock *client.MockApiClientInterface) { - mock.EXPECT().ConfigurationVariables(client.ScopeTemplate, "template_id").AnyTimes(). + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeTemplate, "template_id").AnyTimes(). Return([]client.ConfigurationVariable{configurationVariable}, nil) }) }) @@ -144,7 +144,7 @@ func TestUnitConfigurationVariableData(t *testing.T) { }, }, func(mock *client.MockApiClientInterface) { - mock.EXPECT().ConfigurationVariables(client.ScopeEnvironment, configurationVariable.Id).AnyTimes(). + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, configurationVariable.Id).AnyTimes(). Return([]client.ConfigurationVariable{configurationVariable}, nil) }) }) @@ -160,7 +160,7 @@ func TestUnitConfigurationVariableData(t *testing.T) { }, }, func(mock *client.MockApiClientInterface) { - mock.EXPECT().ConfigurationVariables(client.ScopeGlobal, "").AnyTimes(). + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeGlobal, "").AnyTimes(). Return(nil, errors.New("not found")) }) }) @@ -176,7 +176,7 @@ func TestUnitConfigurationVariableData(t *testing.T) { }, }, func(mock *client.MockApiClientInterface) { - mock.EXPECT().ConfigurationVariables(client.ScopeGlobal, "").AnyTimes(). + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeGlobal, "").AnyTimes(). Return([]client.ConfigurationVariable{configurationVariable}, nil) }) }) diff --git a/env0/resource_environment_test.go b/env0/resource_environment_test.go index 9f4a5a6e..62ea9753 100644 --- a/env0/resource_environment_test.go +++ b/env0/resource_environment_test.go @@ -91,7 +91,7 @@ func TestUnitEnvironmentResource(t *testing.T) { AutoDeployOnPathChangesOnly: &autoDeployOnPathChangesOnlyDefault, AutoDeployByCustomGlob: autoDeployByCustomGlobDefault, }).Times(1).Return(updatedEnvironment, nil) - mock.EXPECT().ConfigurationVariables(client.ScopeEnvironment, updatedEnvironment.Id).Times(3).Return(client.ConfigurationChanges{}, nil) + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, updatedEnvironment.Id).Times(3).Return(client.ConfigurationChanges{}, nil) gomock.InOrder( mock.EXPECT().Environment(gomock.Any()).Times(2).Return(environment, nil), // 1 after create, 1 before update mock.EXPECT().Environment(gomock.Any()).Times(1).Return(updatedEnvironment, nil), // 1 after update @@ -257,8 +257,8 @@ func TestUnitEnvironmentResource(t *testing.T) { varTrue := true configurationVariables.ToDelete = &varTrue gomock.InOrder( - mock.EXPECT().ConfigurationVariables(client.ScopeEnvironment, updatedEnvironment.Id).Times(3).Return(client.ConfigurationChanges{configurationVariables}, nil), // read after create -> on update - mock.EXPECT().ConfigurationVariables(client.ScopeEnvironment, updatedEnvironment.Id).Times(1).Return(redeployConfigurationVariables, nil), // read after create -> on update -> read after update + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, updatedEnvironment.Id).Times(3).Return(client.ConfigurationChanges{configurationVariables}, nil), // read after create -> on update + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, updatedEnvironment.Id).Times(1).Return(redeployConfigurationVariables, nil), // read after create -> on update -> read after update ) redeployConfigurationVariables[0].Scope = client.ScopeDeployment redeployConfigurationVariables[0].IsSensitive = &isSensitive @@ -389,7 +389,7 @@ func TestUnitEnvironmentResource(t *testing.T) { mock.EXPECT().EnvironmentDeploy(environment.Id, gomock.Any()).Times(1).Return(client.EnvironmentDeployResponse{ Id: "deployment-id", }, nil) - mock.EXPECT().ConfigurationVariables(client.ScopeEnvironment, updatedEnvironment.Id).Times(4).Return(client.ConfigurationChanges{}, nil) // read after create -> on update -> read after update + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, updatedEnvironment.Id).Times(4).Return(client.ConfigurationChanges{}, nil) // read after create -> on update -> read after update gomock.InOrder( mock.EXPECT().Environment(gomock.Any()).Times(2).Return(environment, nil), // 1 after create, 1 before update mock.EXPECT().Environment(gomock.Any()).Times(1).Return(updatedEnvironment, nil), // 1 after update @@ -461,7 +461,7 @@ func TestUnitEnvironmentResource(t *testing.T) { Type: client.TTLTypeDate, Value: updatedEnvironment.LifespanEndAt, }).Times(1).Return(environment, nil) - mock.EXPECT().ConfigurationVariables(client.ScopeEnvironment, updatedEnvironment.Id).Times(3).Return(client.ConfigurationChanges{}, nil) // read after create -> on update -> read after update + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, updatedEnvironment.Id).Times(3).Return(client.ConfigurationChanges{}, nil) // read after create -> on update -> read after update gomock.InOrder( mock.EXPECT().Environment(gomock.Any()).Times(2).Return(environment, nil), // 1 after create, 1 before update @@ -531,7 +531,7 @@ func TestUnitEnvironmentResource(t *testing.T) { Type: client.TTlTypeInfinite, Value: "", }).Times(1).Return(environment, nil) - mock.EXPECT().ConfigurationVariables(client.ScopeEnvironment, updatedEnvironment.Id).Times(3).Return(client.ConfigurationChanges{}, nil) + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, updatedEnvironment.Id).Times(3).Return(client.ConfigurationChanges{}, nil) gomock.InOrder( mock.EXPECT().Environment(gomock.Any()).Times(2).Return(environment, nil), // 1 after create, 1 before update @@ -634,7 +634,7 @@ func TestUnitEnvironmentResource(t *testing.T) { mock.EXPECT().Environment(gomock.Any()).Times(2).Return(environment, nil), // 1 after create, 1 before update mock.EXPECT().Environment(gomock.Any()).Times(1).Return(environmentAfterUpdate, nil), // 1 after update ) - mock.EXPECT().ConfigurationVariables(client.ScopeEnvironment, environment.Id).Times(3).Return(client.ConfigurationChanges{}, nil) + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, environment.Id).Times(3).Return(client.ConfigurationChanges{}, nil) mock.EXPECT().EnvironmentDestroy(environment.Id).Times(1) }) }) @@ -687,7 +687,7 @@ func TestUnitEnvironmentResource(t *testing.T) { runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { mock.EXPECT().EnvironmentCreate(gomock.Any()).Times(1).Return(environment, nil) - mock.EXPECT().ConfigurationVariables(client.ScopeEnvironment, environment.Id).Times(5).Return(client.ConfigurationChanges{}, nil) + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, environment.Id).Times(5).Return(client.ConfigurationChanges{}, nil) mock.EXPECT().Environment(gomock.Any()).Times(5).Return(environment, nil) mock.EXPECT().EnvironmentDestroy(gomock.Any()).Times(1).Return(environment, nil) @@ -731,7 +731,7 @@ func TestUnitEnvironmentResource(t *testing.T) { runUnitTest(t, autoDeployWithCustomGlobEnabled, func(mock *client.MockApiClientInterface) { mock.EXPECT().EnvironmentCreate(gomock.Any()).Times(1).Return(environment, nil) mock.EXPECT().Environment(gomock.Any()).Times(1).Return(environment, nil) - mock.EXPECT().ConfigurationVariables(gomock.Any(), gomock.Any()).Times(1).Return(client.ConfigurationChanges{}, nil) + mock.EXPECT().ConfigurationVariablesByScope(gomock.Any(), gomock.Any()).Times(1).Return(client.ConfigurationChanges{}, nil) mock.EXPECT().EnvironmentDestroy(environment.Id).Times(1) }) }) @@ -803,7 +803,7 @@ func TestUnitEnvironmentResource(t *testing.T) { BlueprintId: templateId, }, }).Times(1).Return(environment, nil) - mock.EXPECT().ConfigurationVariables(client.ScopeEnvironment, environment.Id).Times(2).Return(client.ConfigurationChanges{}, nil) + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, environment.Id).Times(2).Return(client.ConfigurationChanges{}, nil) mock.EXPECT().EnvironmentUpdate(updatedEnvironment.Id, client.EnvironmentUpdate{ Name: updatedEnvironment.Name, AutoDeployOnPathChangesOnly: &autoDeployOnPathChangesOnlyDefault, @@ -852,7 +852,7 @@ func TestUnitEnvironmentResource(t *testing.T) { }).Times(1).Return(client.EnvironmentDeployResponse{}, errors.New("error")) mock.EXPECT().Environment(gomock.Any()).Times(2).Return(environment, nil) // 1 after create, 1 before update mock.EXPECT().EnvironmentDestroy(environment.Id).Times(1) - mock.EXPECT().ConfigurationVariables(client.ScopeEnvironment, environment.Id).Times(2).Return(client.ConfigurationChanges{}, nil) + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, environment.Id).Times(2).Return(client.ConfigurationChanges{}, nil) }) }) @@ -880,7 +880,7 @@ func TestUnitEnvironmentResource(t *testing.T) { }).Times(1).Return(environment, nil) mock.EXPECT().Environment(gomock.Any()).Return(client.Environment{}, errors.New("error")) mock.EXPECT().EnvironmentDestroy(environment.Id).Times(1) - mock.EXPECT().ConfigurationVariables(client.ScopeEnvironment, environment.Id).Times(0).Return(client.ConfigurationChanges{}, nil) + mock.EXPECT().ConfigurationVariablesByScope(client.ScopeEnvironment, environment.Id).Times(0).Return(client.ConfigurationChanges{}, nil) }) })