Skip to content

Commit 8d430ef

Browse files
feat: Add support to deprovision environment (#562)
1 parent 186340b commit 8d430ef

7 files changed

Lines changed: 790 additions & 1 deletion

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require (
66
github.com/AlecAivazis/survey/v2 v2.3.7
77
github.com/MakeNowJust/heredoc/v2 v2.0.1
88
github.com/OctopusDeploy/go-octodiff v1.0.0
9-
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.86.0
9+
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.88.0
1010
github.com/bmatcuk/doublestar/v4 v4.4.0
1111
github.com/briandowns/spinner v1.19.0
1212
github.com/google/uuid v1.3.0

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ github.com/OctopusDeploy/go-octodiff v1.0.0 h1:U+ORg6azniwwYo+O44giOw6TiD5USk8S4
4848
github.com/OctopusDeploy/go-octodiff v1.0.0/go.mod h1:Mze0+EkOWTgTmi8++fyUc6r0aLZT7qD9gX+31t8MmIU=
4949
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.86.0 h1:yGoohDFkruQ13mxoK21J+U+IUkxoU5jSxGEpknW/E5Y=
5050
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.86.0/go.mod h1:VkTXDoIPbwGFi5+goo1VSwFNdMVo784cVtJdKIEvfus=
51+
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.86.1-0.20251107005245-10b49a98c515 h1:7L4l7zszH5TKP14BXCzgqT86xmHPQ4vQSmrqHc4Bj7E=
52+
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.86.1-0.20251107005245-10b49a98c515/go.mod h1:VkTXDoIPbwGFi5+goo1VSwFNdMVo784cVtJdKIEvfus=
53+
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.88.0 h1:doBP2Xk11l3tNqJfrF0cYqX0df3UHIQiQCts1qAux+w=
54+
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.88.0/go.mod h1:VkTXDoIPbwGFi5+goo1VSwFNdMVo784cVtJdKIEvfus=
5155
github.com/bmatcuk/doublestar/v4 v4.4.0 h1:LmAwNwhjEbYtyVLzjcP/XeVw4nhuScHGkF/XWXnvIic=
5256
github.com/bmatcuk/doublestar/v4 v4.4.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
5357
github.com/briandowns/spinner v1.19.0 h1:s8aq38H+Qju89yhp89b4iIiMzMm8YN3p6vGpwyh/a8E=
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
package deprovision_environment
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/MakeNowJust/heredoc/v2"
8+
"github.com/OctopusDeploy/cli/pkg/cmd"
9+
"github.com/OctopusDeploy/cli/pkg/cmd/ephemeralenvironment/util"
10+
"github.com/OctopusDeploy/cli/pkg/constants"
11+
"github.com/OctopusDeploy/cli/pkg/factory"
12+
"github.com/OctopusDeploy/cli/pkg/output"
13+
"github.com/OctopusDeploy/cli/pkg/question/selectors"
14+
"github.com/OctopusDeploy/cli/pkg/util/flag"
15+
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/environments/v2/ephemeralenvironments"
16+
"github.com/spf13/cobra"
17+
)
18+
19+
const (
20+
FlagName = "name"
21+
)
22+
23+
type DeprovisionEnvironmentFlags struct {
24+
Name *flag.Flag[string]
25+
}
26+
27+
func NewDeprovisionEnvironmentFlags() *DeprovisionEnvironmentFlags {
28+
return &DeprovisionEnvironmentFlags{
29+
Name: flag.New[string](FlagName, false),
30+
}
31+
}
32+
33+
type DeprovisionEnvironmentOptions struct {
34+
*DeprovisionEnvironmentFlags
35+
*cmd.Dependencies
36+
Command *cobra.Command
37+
GetAllEphemeralEnvironments func() ([]*ephemeralenvironments.EphemeralEnvironment, error)
38+
}
39+
40+
func NewDeprovisionEnvironmentOptions(deprovisionEnvironmentFlags *DeprovisionEnvironmentFlags, dependencies *cmd.Dependencies, command *cobra.Command) *DeprovisionEnvironmentOptions {
41+
return &DeprovisionEnvironmentOptions{
42+
DeprovisionEnvironmentFlags: deprovisionEnvironmentFlags,
43+
Dependencies: dependencies,
44+
Command: command,
45+
GetAllEphemeralEnvironments: func() ([]*ephemeralenvironments.EphemeralEnvironment, error) {
46+
return getAllEphemeralEnvironments(dependencies)
47+
},
48+
}
49+
}
50+
51+
func getAllEphemeralEnvironments(dependencies *cmd.Dependencies) ([]*ephemeralenvironments.EphemeralEnvironment, error) {
52+
allEphemeralEnvironments, err := ephemeralenvironments.GetAll(dependencies.Client, dependencies.Client.GetSpaceID())
53+
54+
if err != nil {
55+
return nil, err
56+
}
57+
58+
return allEphemeralEnvironments.Items, nil
59+
}
60+
61+
func NewCmdDeprovisionEnvironment(factory factory.Factory) *cobra.Command {
62+
deprovisionEnvironmentFlags := NewDeprovisionEnvironmentFlags()
63+
64+
command := &cobra.Command{
65+
Use: "deprovision-environment",
66+
Short: "Deprovision an environment",
67+
Long: "Deprovision an environment",
68+
Example: heredoc.Docf("$ %s ephemeral-environment deprovision-environment --name PR-1234", constants.ExecutableName),
69+
RunE: func(c *cobra.Command, _ []string) error {
70+
deprovisionEnvironmentOptions := NewDeprovisionEnvironmentOptions(deprovisionEnvironmentFlags, cmd.NewDependencies(factory, c), c)
71+
72+
return DeprovisionEnvironmentRun(deprovisionEnvironmentOptions, c)
73+
},
74+
}
75+
76+
flags := command.Flags()
77+
flags.StringVarP(&deprovisionEnvironmentFlags.Name.Value, FlagName, "n", "", "Name of the environment")
78+
79+
return command
80+
}
81+
82+
func DeprovisionEnvironmentRun(deprovisionEnvironmentOptions *DeprovisionEnvironmentOptions, command *cobra.Command) error {
83+
var err error
84+
85+
if !deprovisionEnvironmentOptions.NoPrompt {
86+
err = PromptMissing(deprovisionEnvironmentOptions)
87+
if err != nil {
88+
return err
89+
}
90+
}
91+
92+
if deprovisionEnvironmentOptions.Name.Value == "" {
93+
return fmt.Errorf("environment name is required")
94+
}
95+
96+
var environment *ephemeralenvironments.EphemeralEnvironment
97+
98+
environment, err = util.GetByName(deprovisionEnvironmentOptions.Client, deprovisionEnvironmentOptions.Name.Value, deprovisionEnvironmentOptions.Space.ID)
99+
if err != nil {
100+
return err
101+
}
102+
103+
var response *ephemeralenvironments.DeprovisionEphemeralEnvironmentResponse
104+
105+
response, err = deprovisionEnvironment(deprovisionEnvironmentOptions, environment)
106+
if err != nil {
107+
return err
108+
}
109+
110+
outputResult(deprovisionEnvironmentOptions, command, response)
111+
112+
return nil
113+
}
114+
115+
func outputResult(deprovisionEnvironmentOptions *DeprovisionEnvironmentOptions, command *cobra.Command, response *ephemeralenvironments.DeprovisionEphemeralEnvironmentResponse) {
116+
command.Println("Deprovisioning environment: " + deprovisionEnvironmentOptions.Name.Value)
117+
118+
outputFormat, err := command.Flags().GetString(constants.FlagOutputFormat)
119+
if err != nil { // should never happen, but fallback if it does
120+
outputFormat = constants.OutputFormatTable
121+
}
122+
123+
switch outputFormat {
124+
case constants.OutputFormatBasic:
125+
if response.DeprovisioningRuns == nil || len(response.DeprovisioningRuns) == 0 {
126+
command.Println("Environment deprovisioned without running a runbook.")
127+
} else {
128+
for _, run := range response.DeprovisioningRuns {
129+
command.Printf("Runbook Run ID: %s \nServer Task ID %s \n", run.RunbookRunID, run.TaskId)
130+
}
131+
}
132+
case constants.OutputFormatJson:
133+
data, err := json.Marshal(response.DeprovisioningRuns)
134+
if err != nil { // shouldn't happen but fallback in case
135+
command.PrintErrln(err)
136+
} else {
137+
_, _ = command.OutOrStdout().Write(data)
138+
command.Println()
139+
}
140+
default: // table
141+
if response.DeprovisioningRuns == nil || len(response.DeprovisioningRuns) == 0 {
142+
command.Println("Environment deprovisioned without running a runbook.")
143+
} else {
144+
t := output.NewTable(command.OutOrStdout())
145+
t.AddRow(output.Bold("Runbook Run ID"), output.Bold("Server Task ID"))
146+
for _, run := range response.DeprovisioningRuns {
147+
t.AddRow(run.RunbookRunID, run.TaskId)
148+
}
149+
err := t.Print()
150+
if err != nil {
151+
command.PrintErrln(err)
152+
}
153+
}
154+
}
155+
}
156+
157+
func deprovisionEnvironment(deprovisionEnvironmentOptions *DeprovisionEnvironmentOptions, environment *ephemeralenvironments.EphemeralEnvironment) (*ephemeralenvironments.DeprovisionEphemeralEnvironmentResponse, error) {
158+
response, err := ephemeralenvironments.Deprovision(deprovisionEnvironmentOptions.Client, deprovisionEnvironmentOptions.Space.ID, environment.ID)
159+
160+
if err != nil {
161+
return nil, err
162+
}
163+
164+
return response, nil
165+
}
166+
167+
func PromptMissing(options *DeprovisionEnvironmentOptions) error {
168+
if options.Name.Value != "" {
169+
return nil
170+
}
171+
172+
environment, err := selectors.Select(options.Ask, "Please select the name of the environment you wish to deprovision", options.GetAllEphemeralEnvironments, func(environment *ephemeralenvironments.EphemeralEnvironment) string { return environment.Name })
173+
174+
if err != nil {
175+
return err
176+
}
177+
178+
options.Name.Value = environment.Name
179+
180+
return nil
181+
}

0 commit comments

Comments
 (0)