@@ -2,20 +2,18 @@ package view
22
33import (
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 ("\n View 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