Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package ephemeralenvironments
import (
"math"

"github.com/OctopusDeploy/go-octopusdeploy/v2/internal"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/constants"
v2 "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/environments/v2"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/environments/v2/environments"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/newclient"
Expand Down Expand Up @@ -96,3 +98,37 @@ func Deprovision(client newclient.Client, spaceID string, environmentId string)

return newclient.Add[DeprovisionEphemeralEnvironmentResponse](client, path, spaceID, body)
}

// GetStatus returns the provisioning status of an ephemeral environment for a project.
func GetStatus(client newclient.Client, spaceID string, environmentId string, projectId string) (*EphemeralEnvironmentStatus, error) {
spaceID, err := internal.GetSpaceID(spaceID, client.GetSpaceID())
if err != nil {
return nil, err
}

if internal.IsEmpty(environmentId) {
return nil, internal.CreateRequiredParameterIsEmptyError(constants.ParameterID)
}

if internal.IsEmpty(projectId) {
return nil, internal.CreateRequiredParameterIsEmptyError(constants.ParameterProjectID)
}

path, err := client.URITemplateCache().Expand(v2.GetEphemeralEnvironmentStatusTemplate, map[string]any{
"id": environmentId,
"projectId": projectId,
"spaceId": spaceID,
})

if err != nil {
return nil, err
}

return newclient.Get[EphemeralEnvironmentStatus](client.HttpSession(), path)
}

// Delete deletes an ephemeral environment without running its deprovisioning runbook.
// Use it for environments that cannot be deprovisioned, for example when provisioning failed.
func Delete(client newclient.Client, spaceID string, environmentId string) error {
return newclient.DeleteByID(client, v2.DeleteEphemeralEnvironmentTemplate, spaceID, environmentId)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package ephemeralenvironments

import (
"net/http"
"net/http/httptest"
"net/url"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/newclient"
)

func newTestClient(t *testing.T, handler http.HandlerFunc) newclient.Client {
server := httptest.NewServer(handler)
t.Cleanup(server.Close)

baseURL, err := url.Parse(server.URL)
require.NoError(t, err)

return newclient.NewClientS(&newclient.HttpSession{
HttpClient: server.Client(),
BaseURL: baseURL,
DefaultHeaders: map[string]string{},
}, "Spaces-1")
}

func TestGetStatus(t *testing.T) {
var method, requested string
client := newTestClient(t, func(w http.ResponseWriter, r *http.Request) {
method = r.Method
requested = r.URL.RequestURI()
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"Status":"ProvisioningFailed"}`))
})

status, err := GetStatus(client, "Spaces-1", "Environments-1001", "Projects-1")
require.NoError(t, err)

assert.Equal(t, http.MethodGet, method)
assert.Equal(t, "/api/Spaces-1/projects/Projects-1/environments/ephemeral/Environments-1001/status", requested)
assert.Equal(t, "ProvisioningFailed", status.Status)
}

func TestGetStatusReturnsServerError(t *testing.T) {
client := newTestClient(t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(`{"ErrorMessage":"not found"}`))
})

status, err := GetStatus(client, "Spaces-1", "Environments-1001", "Projects-1")
assert.Error(t, err)
assert.Nil(t, status)
}

func TestGetStatusRequiresIDs(t *testing.T) {
client := newTestClient(t, func(w http.ResponseWriter, r *http.Request) {
t.Errorf("no request expected, got %s %s", r.Method, r.URL.RequestURI())
})

_, err := GetStatus(client, "Spaces-1", "", "Projects-1")
assert.Error(t, err)

_, err = GetStatus(client, "Spaces-1", "Environments-1001", "")
assert.Error(t, err)
}

func TestDelete(t *testing.T) {
var method, requested string
client := newTestClient(t, func(w http.ResponseWriter, r *http.Request) {
method = r.Method
requested = r.URL.RequestURI()
w.WriteHeader(http.StatusOK)
})

err := Delete(client, "Spaces-1", "Environments-1001")
require.NoError(t, err)

assert.Equal(t, http.MethodDelete, method)
assert.Equal(t, "/api/Spaces-1/environments/Environments-1001", requested)
}

func TestDeleteRequiresID(t *testing.T) {
client := newTestClient(t, func(w http.ResponseWriter, r *http.Request) {
t.Errorf("no request expected, got %s %s", r.Method, r.URL.RequestURI())
})

assert.Error(t, Delete(client, "Spaces-1", ""))
}
5 changes: 5 additions & 0 deletions pkg/environments/v2/ephemeralenvironments/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ type DeprovisioningRunbookRun struct {
RunbookRunID string `json:"RunbookRunId"`
TaskId string `json:"TaskId"`
}

// EphemeralEnvironmentStatus is the provisioning status of an ephemeral environment for a project.
type EphemeralEnvironmentStatus struct {
Status string `json:"Status"`
}
2 changes: 2 additions & 0 deletions pkg/environments/v2/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ const (
CreateEphemeralEnvironmentTemplate = "/api/{spaceId}/projects/{projectId}/environments/ephemeral"
DeprovisionEphemeralEnvironmentForProjectTemplate = "/api/{spaceId}/projects/{projectId}/environments/ephemeral/{id}/deprovision"
DeprovisionEphemeralEnvironmentTemplate = "/api/{spaceId}/environments/ephemeral/{id}/deprovision"
GetEphemeralEnvironmentStatusTemplate = "/api/{spaceId}/projects/{projectId}/environments/ephemeral/{id}/status"
DeleteEphemeralEnvironmentTemplate = "/api/{spaceId}/environments/{id}"
)
73 changes: 73 additions & 0 deletions test/e2e/ephemeral_environment_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,3 +246,76 @@ func DeprovisionEphemeralEnvironment(t *testing.T, client *client.Client, enviro

return deprovisionResponse.DeprovisioningRuns
}

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

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

lifecycle := CreateTestLifecycle(t, client)
require.NotNil(t, lifecycle)
defer DeleteTestLifecycle(t, client, lifecycle)

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

project := CreateTestProject(t, client, space, lifecycle, projectGroup)
require.NotNil(t, project)
defer DeleteTestProject(t, client, project)

parentEnvironment := CreateParentEnvironment(t, client)
require.NotNil(t, parentEnvironment)
defer DeleteParentEnvironment(t, client, parentEnvironment)

ephemeralChannel := CreateEphemeralTestChannel(t, client, project, parentEnvironment)
require.NotNil(t, ephemeralChannel)
defer DeleteTestChannel(t, client, ephemeralChannel)

createdEnvironmentId := CreateEphemeralEnvironment(t, client, project)
defer DeprovisionEphemeralEnvironment(t, client, &createdEnvironmentId)

status, err := ephemeralenvironments.GetStatus(client, client.GetSpaceID(), createdEnvironmentId, project.GetID())
require.NoError(t, err)
require.NotNil(t, status)
require.NotEmpty(t, status.Status)
}

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

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

lifecycle := CreateTestLifecycle(t, client)
require.NotNil(t, lifecycle)
defer DeleteTestLifecycle(t, client, lifecycle)

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

project := CreateTestProject(t, client, space, lifecycle, projectGroup)
require.NotNil(t, project)
defer DeleteTestProject(t, client, project)

parentEnvironment := CreateParentEnvironment(t, client)
require.NotNil(t, parentEnvironment)
defer DeleteParentEnvironment(t, client, parentEnvironment)

ephemeralChannel := CreateEphemeralTestChannel(t, client, project, parentEnvironment)
require.NotNil(t, ephemeralChannel)
defer DeleteTestChannel(t, client, ephemeralChannel)

createdEnvironmentId := CreateEphemeralEnvironment(t, client, project)

err := ephemeralenvironments.Delete(client, client.GetSpaceID(), createdEnvironmentId)
require.NoError(t, err)

deletedEnvironment, err := ephemeralenvironments.GetByID(client, client.GetSpaceID(), createdEnvironmentId)
require.NoError(t, err)
require.Nil(t, deletedEnvironment, "ephemeral environment should be deleted")
}