Skip to content

Commit

Permalink
Merge pull request #292 from OctopusDeploy/huy/deployment-freeze-supp…
Browse files Browse the repository at this point in the history
…ort-tenant-and-recurring

Update deployment freeze to support tenant and recurring
  • Loading branch information
HuyPhanNguyen authored Dec 5, 2024
2 parents b346b5f + 63b0b7f commit 7bd692c
Show file tree
Hide file tree
Showing 3 changed files with 335 additions and 4 deletions.
53 changes: 49 additions & 4 deletions pkg/deploymentfreezes/deploymentfreeze.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,61 @@ import (
"time"
)

type TenantProjectEnvironment struct {
TenantId string `json:"TenantId"`
ProjectId string `json:"ProjectId"`
EnvironmentId string `json:"EnvironmentId"`
}

type RecurringScheduleType string

const (
OnceDaily RecurringScheduleType = "OnceDaily"
DaysPerWeek RecurringScheduleType = "DaysPerWeek"
DaysPerMonth RecurringScheduleType = "DaysPerMonth"
Annually RecurringScheduleType = "Annually"
)

type RecurringScheduleEndType string

const (
Never RecurringScheduleEndType = "Never"
OnDate RecurringScheduleEndType = "OnDate"
AfterOccurrences RecurringScheduleEndType = "AfterOccurrences"
)

type MonthlyScheduleType string

const (
DayOfMonth MonthlyScheduleType = "DayOfMonth"
DateOfMonth MonthlyScheduleType = "DateOfMonth"
)

type RecurringSchedule struct {
Type RecurringScheduleType `json:"Type"`
Unit int `json:"Unit"`
EndType RecurringScheduleEndType `json:"EndType"`
EndOnDate *time.Time `json:"EndOnDate,omitempty"`
EndAfterOccurrences *int `json:"EndAfterOccurrences,omitempty"`
MonthlyScheduleType string `json:"MonthlyScheduleType,omitempty"`
DateOfMonth *string `json:"DateOfMonth,omitempty"`
DayNumberOfMonth *string `json:"DayNumberOfMonth,omitempty"`
DaysOfWeek []string `json:"DaysOfWeek,omitempty"`
DayOfWeek *string `json:"DayOfWeek,omitempty"`
}

type DeploymentFreezes struct {
DeploymentFreezes []DeploymentFreeze `json:"DeploymentFreezes"`
Count int `json:"Count"`
}

type DeploymentFreeze struct {
Name string `json:"Name" validate:"required"`
Start *time.Time `json:"Start,required"`
End *time.Time `json:"End,required"`
ProjectEnvironmentScope map[string][]string `json:"ProjectEnvironmentScope,omitempty"`
Name string `json:"Name" validate:"required"`
Start *time.Time `json:"Start,required"`
End *time.Time `json:"End,required"`
ProjectEnvironmentScope map[string][]string `json:"ProjectEnvironmentScope,omitempty"`
TenantProjectEnvironmentScope []TenantProjectEnvironment `json:"TenantProjectEnvironmentScope,omitempty"`
RecurringSchedule *RecurringSchedule `json:"RecurringSchedule,omitempty"`

resources.Resource
}
Expand Down
1 change: 1 addition & 0 deletions pkg/deploymentfreezes/deploymentfreeze_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type DeploymentFreezeQuery struct {
PartialName string `uri:"partialName,omitempty" url:"partialName,omitempty"`
Status string `uri:"status,omitempty" url:"status,omitempty"`
ProjectIds []string `uri:"projectIds,omitempty" url:"projectIds,omitempty"`
TenantIds []string `uri:"tenantIds,omitempty" url:"tenantIds,omitempty"`
EnvironmentIds []string `uri:"environmentIds,omitempty" url:"environmentIds,omitempty"`
IDs []string `uri:"ids,omitempty" url:"ids,omitempty"`
Skip int `uri:"skip" url:"skip"`
Expand Down
285 changes: 285 additions & 0 deletions test/e2e/deployment_freeze_service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
package e2e

import (
"github.com/OctopusDeploy/go-octopusdeploy/v2/internal"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/client"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/deploymentfreezes"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/environments"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/tenants"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
"time"
)

type testResources struct {
environment *environments.Environment
project *projects.Project
tenant *tenants.Tenant
cleanup func()
}

func createTestResources(t *testing.T, client *client.Client) *testResources {
env := CreateTestEnvironment_NewClient(t, client)
require.NotNil(t, env)

lifecycle := CreateTestLifecycle_NewClient(t, client)
require.NotNil(t, lifecycle)

projectGroup := CreateTestProjectGroup(t, client)
require.NotNil(t, projectGroup)

space := GetDefaultSpace(t, client)
require.NotNil(t, space)

project := CreateTestProject_NewClient(t, client, space, lifecycle, projectGroup)
require.NotNil(t, project)

tenant := CreateTestTenant_NewClient(t, client, project, env)
require.NotNil(t, tenant)

cleanup := func() {
DeleteTestEnvironment_NewClient(t, client, env)
DeleteTestProject_NewClient(t, client, project)
DeleteTestLifecycle_NewClient(t, client, lifecycle)
DeleteTestProjectGroup(t, client, projectGroup)
DeleteTestTenant_NewClient(t, client, tenant)
}

return &testResources{
environment: env,
project: project,
tenant: tenant,
cleanup: cleanup,
}
}

func getTestDeploymentFreeze() *deploymentfreezes.DeploymentFreeze {
name := internal.GetRandomName()
startTime := time.Now().Add(time.Hour).UTC().Truncate(time.Second)
endTime := startTime.Add(time.Hour * 24).UTC().Truncate(time.Second)

return &deploymentfreezes.DeploymentFreeze{
Name: name,
Start: &startTime,
End: &endTime,
ProjectEnvironmentScope: make(map[string][]string),
TenantProjectEnvironmentScope: []deploymentfreezes.TenantProjectEnvironment{},
}
}

func createTestDeploymentFreeze(t *testing.T, client *client.Client) *deploymentfreezes.DeploymentFreeze {
freeze := getTestDeploymentFreeze()
require.NotNil(t, freeze)

createdFreeze, err := deploymentfreezes.Add(client, freeze)
require.NoError(t, err)
require.NotNil(t, createdFreeze)
require.NotEmpty(t, createdFreeze.GetID())

return createdFreeze
}

func cleanDeploymentFreeze(t *testing.T, client *client.Client, freezeID string) {
freeze, err := deploymentfreezes.GetById(client, freezeID)
if err != nil || freeze.GetID() == "" {
return
}
err = deploymentfreezes.Delete(client, freeze)
assert.NoError(t, err)
}

func TestDeploymentFreezeCRUD(t *testing.T) {
client := getOctopusClient()
require.NotNil(t, client)

resources := createTestResources(t, client)
defer resources.cleanup()

t.Run("Add and Delete", func(t *testing.T) {
freeze := createTestDeploymentFreeze(t, client)
require.NotNil(t, freeze)
defer cleanDeploymentFreeze(t, client, freeze.GetID())

err := deploymentfreezes.Delete(client, freeze)
assert.NoError(t, err)

_, err = deploymentfreezes.GetById(client, freeze.GetID())
assert.Error(t, err)
})

t.Run("Update", func(t *testing.T) {
freeze := createTestDeploymentFreeze(t, client)
require.NotNil(t, freeze)
defer cleanDeploymentFreeze(t, client, freeze.GetID())

newName := internal.GetRandomName()
freeze.Name = newName
freeze.ProjectEnvironmentScope[resources.project.GetID()] = []string{resources.environment.GetID()}

updatedFreeze, err := deploymentfreezes.Update(client, freeze)
require.NoError(t, err)
require.Equal(t, newName, updatedFreeze.Name)
require.Contains(t, updatedFreeze.ProjectEnvironmentScope, resources.project.GetID())
})

t.Run("GetAll", func(t *testing.T) {
freeze1 := createTestDeploymentFreeze(t, client)
require.NotNil(t, freeze1)
defer cleanDeploymentFreeze(t, client, freeze1.GetID())

freeze2 := createTestDeploymentFreeze(t, client)
require.NotNil(t, freeze2)
defer cleanDeploymentFreeze(t, client, freeze2.GetID())

allFreezes, err := deploymentfreezes.GetAll(client)
assert.NoError(t, err)
assert.GreaterOrEqual(t, allFreezes.Count, 2)

freezeIds := map[string]bool{
freeze1.GetID(): false,
freeze2.GetID(): false,
}
for _, freeze := range allFreezes.DeploymentFreezes {
if _, exists := freezeIds[freeze.GetID()]; exists {
freezeIds[freeze.GetID()] = true
}
}
for id, found := range freezeIds {
assert.True(t, found, "Freeze %s not found in GetAll results", id)
}
})

t.Run("Get Non-existent", func(t *testing.T) {
_, err := deploymentfreezes.GetById(client, internal.GetRandomName())
assert.Error(t, err)
})
}

func TestDeploymentFreezeRecurringSchedules(t *testing.T) {
client := getOctopusClient()
require.NotNil(t, client)

resources := createTestResources(t, client)
defer resources.cleanup()

testCases := []struct {
name string
schedule *deploymentfreezes.RecurringSchedule
validate func(*testing.T, *deploymentfreezes.DeploymentFreeze)
}{
{
name: "Weekly Schedule",
schedule: &deploymentfreezes.RecurringSchedule{
Type: deploymentfreezes.DaysPerWeek,
Unit: 24,
EndType: deploymentfreezes.AfterOccurrences,
EndAfterOccurrences: ptr(5),
DaysOfWeek: []string{"Monday", "Wednesday", "Friday"},
},
validate: func(t *testing.T, freeze *deploymentfreezes.DeploymentFreeze) {
require.Equal(t, deploymentfreezes.DaysPerWeek, freeze.RecurringSchedule.Type)
require.Equal(t, []string{"Monday", "Wednesday", "Friday"}, freeze.RecurringSchedule.DaysOfWeek)
},
},
{
name: "Monthly Schedule",
schedule: &deploymentfreezes.RecurringSchedule{
Type: deploymentfreezes.DaysPerMonth,
Unit: 24,
EndType: deploymentfreezes.Never,
MonthlyScheduleType: "DayOfMonth",
DayOfWeek: ptr("Thursday"),
DayNumberOfMonth: ptr("1"),
},
validate: func(t *testing.T, freeze *deploymentfreezes.DeploymentFreeze) {
require.Equal(t, deploymentfreezes.DaysPerMonth, freeze.RecurringSchedule.Type)
require.Equal(t, "DayOfMonth", freeze.RecurringSchedule.MonthlyScheduleType)
require.Equal(t, "Thursday", *freeze.RecurringSchedule.DayOfWeek)
},
},
{
name: "Yearly Schedule",
schedule: &deploymentfreezes.RecurringSchedule{
Type: deploymentfreezes.Annually,
Unit: 1,
EndType: deploymentfreezes.OnDate,
EndOnDate: ptr(time.Now().AddDate(1, 0, 0).UTC().Truncate(time.Second)),
},
validate: func(t *testing.T, freeze *deploymentfreezes.DeploymentFreeze) {
require.Equal(t, deploymentfreezes.Annually, freeze.RecurringSchedule.Type)
require.Equal(t, 1, freeze.RecurringSchedule.Unit)
require.Equal(t, deploymentfreezes.OnDate, freeze.RecurringSchedule.EndType)
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
freeze := getTestDeploymentFreeze()
freeze.ProjectEnvironmentScope[resources.project.GetID()] = []string{resources.environment.GetID()}
freeze.RecurringSchedule = tc.schedule

createdFreeze, err := deploymentfreezes.Add(client, freeze)
require.NoError(t, err)
defer cleanDeploymentFreeze(t, client, createdFreeze.GetID())

tc.validate(t, createdFreeze)
})
}
}

func TestDeploymentFreezeScopeQueries(t *testing.T) {
client := getOctopusClient()
require.NotNil(t, client)

resources := createTestResources(t, client)
defer resources.cleanup()

t.Run("Tenant and Project Query", func(t *testing.T) {
freeze := getTestDeploymentFreeze()
freeze.ProjectEnvironmentScope[resources.project.GetID()] = []string{resources.environment.GetID()}
freeze.TenantProjectEnvironmentScope = []deploymentfreezes.TenantProjectEnvironment{
{
TenantId: resources.tenant.GetID(),
ProjectId: resources.project.GetID(),
EnvironmentId: resources.environment.GetID(),
},
}

createdFreeze, err := deploymentfreezes.Add(client, freeze)
require.NoError(t, err)
defer cleanDeploymentFreeze(t, client, createdFreeze.GetID())

query := deploymentfreezes.DeploymentFreezeQuery{
TenantIds: []string{resources.tenant.GetID()},
ProjectIds: []string{resources.project.GetID()},
Skip: 0,
Take: 30,
}

result, err := deploymentfreezes.Get(client, query)
require.NoError(t, err)
require.Greater(t, result.Count, 0)

var found bool
for _, f := range result.DeploymentFreezes {
if f.GetID() == createdFreeze.GetID() {
found = true
require.Contains(t, f.ProjectEnvironmentScope, resources.project.GetID())
require.Contains(t, f.TenantProjectEnvironmentScope, deploymentfreezes.TenantProjectEnvironment{
TenantId: resources.tenant.GetID(),
ProjectId: resources.project.GetID(),
EnvironmentId: resources.environment.GetID(),
})
break
}
}
assert.True(t, found)
})
}

func ptr[T any](v T) *T {
return &v
}

0 comments on commit 7bd692c

Please sign in to comment.