Skip to content

Commit 2cd8853

Browse files
feat: octopus deployment-target view support -f parameter (#538)
* feat: support -f parameter for deployment target views command * tidy
1 parent 2fb738a commit 2cd8853

File tree

8 files changed

+257
-28
lines changed

8 files changed

+257
-28
lines changed

pkg/cmd/target/azure-web-app/view/view.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func NewCmdView(f factory.Factory) *cobra.Command {
2828
$ %[1]s deployment-target azure-web-app view Machines-100
2929
`, constants.ExecutableName),
3030
RunE: func(c *cobra.Command, args []string) error {
31-
opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args)
31+
opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args, c)
3232
return ViewRun(opts)
3333
},
3434
}

pkg/cmd/target/cloud-region/view/view.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func NewCmdView(f factory.Factory) *cobra.Command {
2323
$ %[1]s deployment-target cloud-region view Machines-100
2424
`, constants.ExecutableName),
2525
RunE: func(c *cobra.Command, args []string) error {
26-
opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args)
26+
opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args, c)
2727
return ViewRun(opts)
2828
},
2929
}

pkg/cmd/target/kubernetes/view/view.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func NewCmdView(f factory.Factory) *cobra.Command {
2424
$ %[1]s deployment-target kubernetes view 'target-name'
2525
`, constants.ExecutableName),
2626
RunE: func(c *cobra.Command, args []string) error {
27-
opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args)
27+
opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args, c)
2828
return ViewRun(opts)
2929
},
3030
}

pkg/cmd/target/listening-tentacle/view/view.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func NewCmdView(f factory.Factory) *cobra.Command {
2525
$ %[1]s deployment-target listening-tentacle view Machines-100
2626
`, constants.ExecutableName),
2727
RunE: func(c *cobra.Command, args []string) error {
28-
opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args)
28+
opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args, c)
2929
return ViewRun(opts)
3030
},
3131
}

pkg/cmd/target/polling-tentacle/view/view.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func NewCmdView(f factory.Factory) *cobra.Command {
2525
$ %[1]s deployment-target polling-tentacle view Machines-100
2626
`, constants.ExecutableName),
2727
RunE: func(c *cobra.Command, args []string) error {
28-
opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args)
28+
opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args, c)
2929
return ViewRun(opts)
3030
},
3131
}

pkg/cmd/target/shared/view.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/OctopusDeploy/cli/pkg/output"
88
"github.com/OctopusDeploy/cli/pkg/util"
99
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
10+
"github.com/spf13/cobra"
1011
)
1112

1213
type ContributeEndpointCallback func(opts *ViewOptions, endpoint machines.IEndpoint) ([]*output.DataRow, error)
@@ -19,6 +20,7 @@ type ViewOptions struct {
1920
*cmd.Dependencies
2021
IdOrName string
2122
*ViewFlags
23+
Command *cobra.Command
2224
}
2325

2426
func NewViewFlags() *ViewFlags {
@@ -27,11 +29,12 @@ func NewViewFlags() *ViewFlags {
2729
}
2830
}
2931

30-
func NewViewOptions(viewFlags *ViewFlags, dependencies *cmd.Dependencies, args []string) *ViewOptions {
32+
func NewViewOptions(viewFlags *ViewFlags, dependencies *cmd.Dependencies, args []string, command *cobra.Command) *ViewOptions {
3133
return &ViewOptions{
3234
ViewFlags: viewFlags,
3335
Dependencies: dependencies,
3436
IdOrName: args[0],
37+
Command: command,
3538
}
3639
}
3740

@@ -93,8 +96,6 @@ func ViewRun(opts *ViewOptions, contributeEndpoint ContributeEndpointCallback, d
9396
fmt.Fprintf(opts.Out, "\n")
9497
machinescommon.DoWebForTargets(target, opts.Dependencies, opts.WebFlags, description)
9598
return nil
96-
97-
return nil
9899
}
99100

100101
func ContributeProxy(opts *ViewOptions, proxyID string) ([]*output.DataRow, error) {

pkg/cmd/target/ssh/view/view.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func NewCmdView(f factory.Factory) *cobra.Command {
2525
$ %[1]s deployment-target ssh view Machines-100
2626
`, constants.ExecutableName),
2727
RunE: func(c *cobra.Command, args []string) error {
28-
opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args)
28+
opts := shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args, c)
2929
return ViewRun(opts)
3030
},
3131
}

pkg/cmd/target/view/view.go

Lines changed: 247 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,18 @@ package view
22

33
import (
44
"fmt"
5+
"strings"
56

67
"github.com/MakeNowJust/heredoc/v2"
78
"github.com/OctopusDeploy/cli/pkg/cmd"
8-
azureWebApp "github.com/OctopusDeploy/cli/pkg/cmd/target/azure-web-app/view"
9-
cloudRegion "github.com/OctopusDeploy/cli/pkg/cmd/target/cloud-region/view"
10-
k8s "github.com/OctopusDeploy/cli/pkg/cmd/target/kubernetes/view"
11-
listeningTentacle "github.com/OctopusDeploy/cli/pkg/cmd/target/listening-tentacle/view"
12-
pollingTentacle "github.com/OctopusDeploy/cli/pkg/cmd/target/polling-tentacle/view"
139
"github.com/OctopusDeploy/cli/pkg/cmd/target/shared"
14-
ssh "github.com/OctopusDeploy/cli/pkg/cmd/target/ssh/view"
1510
"github.com/OctopusDeploy/cli/pkg/constants"
1611
"github.com/OctopusDeploy/cli/pkg/factory"
1712
"github.com/OctopusDeploy/cli/pkg/machinescommon"
13+
"github.com/OctopusDeploy/cli/pkg/output"
1814
"github.com/OctopusDeploy/cli/pkg/usage"
15+
"github.com/OctopusDeploy/cli/pkg/util"
16+
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
1917
"github.com/spf13/cobra"
2018
)
2119

@@ -31,7 +29,7 @@ func NewCmdView(f factory.Factory) *cobra.Command {
3129
$ %[1]s deployment-target view 'web-server'
3230
`, constants.ExecutableName),
3331
RunE: func(c *cobra.Command, args []string) error {
34-
return ViewRun(shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args))
32+
return ViewRun(shared.NewViewOptions(flags, cmd.NewDependencies(f, c), args, c))
3533
},
3634
}
3735

@@ -46,28 +44,258 @@ func ViewRun(opts *shared.ViewOptions) error {
4644
return err
4745
}
4846

49-
switch target.Endpoint.GetCommunicationStyle() {
47+
return output.PrintResource(target, opts.Command, output.Mappers[*machines.DeploymentTarget]{
48+
Json: func(t *machines.DeploymentTarget) any {
49+
return getDeploymentTargetAsJson(opts, t)
50+
},
51+
Table: output.TableDefinition[*machines.DeploymentTarget]{
52+
Header: []string{"NAME", "TYPE", "HEALTH", "ENVIRONMENTS", "ROLES", "TENANTS", "TENANT TAGS", "ENDPOINT DETAILS"},
53+
Row: func(t *machines.DeploymentTarget) []string {
54+
return getDeploymentTargetAsTableRow(opts, t)
55+
},
56+
},
57+
Basic: func(t *machines.DeploymentTarget) string {
58+
return getDeploymentTargetAsBasic(opts, t)
59+
},
60+
})
61+
}
62+
63+
type DeploymentTargetAsJson struct {
64+
Id string `json:"Id"`
65+
Name string `json:"Name"`
66+
HealthStatus string `json:"HealthStatus"`
67+
StatusSummary string `json:"StatusSummary"`
68+
CommunicationStyle string `json:"CommunicationStyle"`
69+
Environments []string `json:"Environments"`
70+
Roles []string `json:"Roles"`
71+
Tenants []string `json:"Tenants"`
72+
TenantTags []string `json:"TenantTags"`
73+
EndpointDetails map[string]string `json:"EndpointDetails"`
74+
WebUrl string `json:"WebUrl"`
75+
}
76+
77+
func getDeploymentTargetAsJson(opts *shared.ViewOptions, target *machines.DeploymentTarget) DeploymentTargetAsJson {
78+
environmentMap, _ := shared.GetEnvironmentMap(opts)
79+
tenantMap, _ := shared.GetTenantMap(opts)
80+
81+
environments := resolveValues(target.EnvironmentIDs, environmentMap)
82+
tenants := resolveValues(target.TenantIDs, tenantMap)
83+
84+
endpointDetails := getEndpointDetails(target)
85+
86+
return DeploymentTargetAsJson{
87+
Id: target.GetID(),
88+
Name: target.Name,
89+
HealthStatus: target.HealthStatus,
90+
StatusSummary: target.StatusSummary,
91+
CommunicationStyle: target.Endpoint.GetCommunicationStyle(),
92+
Environments: environments,
93+
Roles: target.Roles,
94+
Tenants: tenants,
95+
TenantTags: target.TenantTags,
96+
EndpointDetails: endpointDetails,
97+
WebUrl: util.GenerateWebURL(opts.Host, target.SpaceID, fmt.Sprintf("infrastructure/machines/%s/settings", target.GetID())),
98+
}
99+
}
100+
101+
func getDeploymentTargetAsTableRow(opts *shared.ViewOptions, target *machines.DeploymentTarget) []string {
102+
environmentMap, _ := shared.GetEnvironmentMap(opts)
103+
environments := resolveValues(target.EnvironmentIDs, environmentMap)
104+
105+
healthStatus := getHealthStatusFormatted(target.HealthStatus)
106+
targetType := getTargetTypeDisplayName(target.Endpoint.GetCommunicationStyle())
107+
108+
// Handle tenants
109+
tenants := "None"
110+
if !util.Empty(target.TenantIDs) {
111+
tenantMap, _ := shared.GetTenantMap(opts)
112+
tenantNames := resolveValues(target.TenantIDs, tenantMap)
113+
tenants = strings.Join(tenantNames, ", ")
114+
}
115+
116+
// Handle tenant tags
117+
tenantTags := "None"
118+
if !util.Empty(target.TenantTags) {
119+
tenantTags = strings.Join(target.TenantTags, ", ")
120+
}
121+
122+
// Handle endpoint details
123+
endpointDetails := getEndpointDetails(target)
124+
var endpointDetailsStr strings.Builder
125+
first := true
126+
for key, value := range endpointDetails {
127+
if !first {
128+
endpointDetailsStr.WriteString("; ")
129+
}
130+
endpointDetailsStr.WriteString(fmt.Sprintf("%s: %s", key, value))
131+
first = false
132+
}
133+
endpointDetailsString := endpointDetailsStr.String()
134+
if endpointDetailsString == "" {
135+
endpointDetailsString = "-"
136+
}
137+
138+
return []string{
139+
output.Bold(target.Name),
140+
targetType,
141+
healthStatus,
142+
strings.Join(environments, ", "),
143+
strings.Join(target.Roles, ", "),
144+
tenants,
145+
tenantTags,
146+
endpointDetailsString,
147+
}
148+
}
149+
150+
func getHealthStatusFormatted(status string) string {
151+
switch status {
152+
case "Healthy":
153+
return output.Green(status)
154+
case "Unhealthy":
155+
return output.Red(status)
156+
default:
157+
return output.Yellow(status)
158+
}
159+
}
160+
161+
func getTargetTypeDisplayName(communicationStyle string) string {
162+
switch communicationStyle {
50163
case "None":
51-
return cloudRegion.ViewRun(opts)
164+
return "Cloud Region"
52165
case "TentaclePassive":
53-
return listeningTentacle.ViewRun(opts)
166+
return "Listening Tentacle"
54167
case "TentacleActive":
55-
return pollingTentacle.ViewRun(opts)
168+
return "Polling Tentacle"
56169
case "Ssh":
57-
return ssh.ViewRun(opts)
170+
return "SSH"
58171
case "OfflineDrop":
59-
return shared.ViewRun(opts, nil, "Offline Drop Folder")
172+
return "Offline Drop"
60173
case "AzureWebApp":
61-
return azureWebApp.ViewRun(opts)
174+
return "Azure Web App"
62175
case "AzureCloudService":
63-
return shared.ViewRun(opts, nil, "Azure Cloud Service")
176+
return "Azure Cloud Service"
64177
case "AzureServiceFabricCluster":
65-
return shared.ViewRun(opts, nil, "Azure Service Fabric Cluster")
178+
return "Azure Service Fabric"
66179
case "Kubernetes":
67-
return k8s.ViewRun(opts)
180+
return "Kubernetes"
68181
case "StepPackage":
69-
return shared.ViewRun(opts, nil, "Step Package")
182+
return "Step Package"
183+
default:
184+
return communicationStyle
185+
}
186+
}
187+
188+
func getDeploymentTargetAsBasic(opts *shared.ViewOptions, target *machines.DeploymentTarget) string {
189+
var result strings.Builder
190+
191+
// Header
192+
result.WriteString(fmt.Sprintf("%s %s\n", output.Bold(target.Name), output.Dimf("(%s)", target.GetID())))
193+
194+
// Health status
195+
healthStatus := getHealthStatusFormatted(target.HealthStatus)
196+
result.WriteString(fmt.Sprintf("Health status: %s\n", healthStatus))
197+
198+
// Current status
199+
result.WriteString(fmt.Sprintf("Current status: %s\n", target.StatusSummary))
200+
201+
// Target type and endpoint details
202+
targetType := getTargetTypeDisplayName(target.Endpoint.GetCommunicationStyle())
203+
result.WriteString(fmt.Sprintf("Type: %s\n", output.Cyan(targetType)))
204+
205+
// Add endpoint-specific details
206+
endpointDetails := getEndpointDetails(target)
207+
for key, value := range endpointDetails {
208+
result.WriteString(fmt.Sprintf("%s: %s\n", key, value))
209+
}
210+
211+
// Environments
212+
environmentMap, _ := shared.GetEnvironmentMap(opts)
213+
environments := resolveValues(target.EnvironmentIDs, environmentMap)
214+
result.WriteString(fmt.Sprintf("Environments: %s\n", output.FormatAsList(environments)))
215+
216+
// Roles
217+
result.WriteString(fmt.Sprintf("Roles: %s\n", output.FormatAsList(target.Roles)))
218+
219+
// Tenants
220+
if !util.Empty(target.TenantIDs) {
221+
tenantMap, _ := shared.GetTenantMap(opts)
222+
tenants := resolveValues(target.TenantIDs, tenantMap)
223+
result.WriteString(fmt.Sprintf("Tenants: %s\n", output.FormatAsList(tenants)))
224+
} else {
225+
result.WriteString("Tenants: None\n")
226+
}
227+
228+
// Tenant Tags
229+
if !util.Empty(target.TenantTags) {
230+
result.WriteString(fmt.Sprintf("Tenant Tags: %s\n", output.FormatAsList(target.TenantTags)))
231+
} else {
232+
result.WriteString("Tenant Tags: None\n")
233+
}
234+
235+
// Web URL
236+
url := util.GenerateWebURL(opts.Host, target.SpaceID, fmt.Sprintf("infrastructure/machines/%s/settings", target.GetID()))
237+
result.WriteString(fmt.Sprintf("\nView this deployment target in Octopus Deploy: %s\n", output.Blue(url)))
238+
239+
// Handle web flag
240+
if opts.WebFlags != nil && opts.WebFlags.Web.Value {
241+
machinescommon.DoWebForTargets(target, opts.Dependencies, opts.WebFlags, targetType)
242+
}
243+
244+
return result.String()
245+
}
246+
247+
func resolveValues(keys []string, lookup map[string]string) []string {
248+
var values []string
249+
for _, key := range keys {
250+
if value, exists := lookup[key]; exists {
251+
values = append(values, value)
252+
} else {
253+
values = append(values, key)
254+
}
255+
}
256+
return values
257+
}
258+
259+
func getEndpointDetails(target *machines.DeploymentTarget) map[string]string {
260+
details := make(map[string]string)
261+
262+
switch target.Endpoint.GetCommunicationStyle() {
263+
case "AzureWebApp":
264+
if endpoint, ok := target.Endpoint.(*machines.AzureWebAppEndpoint); ok {
265+
webApp := endpoint.WebAppName
266+
if endpoint.WebAppSlotName != "" {
267+
webApp = fmt.Sprintf("%s/%s", webApp, endpoint.WebAppSlotName)
268+
}
269+
details["Web App"] = webApp
270+
}
271+
case "Kubernetes":
272+
if endpoint, ok := target.Endpoint.(*machines.KubernetesEndpoint); ok {
273+
details["Authentication Type"] = endpoint.Authentication.GetAuthenticationType()
274+
}
275+
case "Ssh":
276+
if endpoint, ok := target.Endpoint.(*machines.SSHEndpoint); ok {
277+
details["URI"] = endpoint.URI.String()
278+
runtime := "Mono"
279+
if endpoint.DotNetCorePlatform != "" {
280+
runtime = endpoint.DotNetCorePlatform
281+
}
282+
details["Runtime architecture"] = runtime
283+
}
284+
case "TentaclePassive":
285+
if endpoint, ok := target.Endpoint.(*machines.ListeningTentacleEndpoint); ok {
286+
details["URI"] = endpoint.URI.String()
287+
details["Tentacle version"] = endpoint.TentacleVersionDetails.Version
288+
}
289+
case "TentacleActive":
290+
if endpoint, ok := target.Endpoint.(*machines.PollingTentacleEndpoint); ok {
291+
details["URI"] = endpoint.URI.String()
292+
details["Tentacle version"] = endpoint.TentacleVersionDetails.Version
293+
}
294+
case "None":
295+
// Cloud regions typically don't have additional endpoint details
296+
case "OfflineDrop", "StepPackage", "AzureCloudService", "AzureServiceFabricCluster":
297+
// These endpoints don't have specific details we can easily extract
70298
}
71299

72-
return fmt.Errorf("unsupported deployment target '%s'", target.Endpoint.GetCommunicationStyle())
300+
return details
73301
}

0 commit comments

Comments
 (0)