-
Notifications
You must be signed in to change notification settings - Fork 82
Expand file tree
/
Copy pathfleet_deployment_deploy.go
More file actions
178 lines (160 loc) Β· 6.5 KB
/
fleet_deployment_deploy.go
File metadata and controls
178 lines (160 loc) Β· 6.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package fleetcontrol
import (
"fmt"
"time"
"github.com/spf13/cobra"
"github.com/newrelic/newrelic-cli/internal/client"
"github.com/newrelic/newrelic-client-go/v2/pkg/fleetcontrol"
)
// spinner displays an animated spinner for the given duration
// to provide visual feedback during wait periods
func spinner(duration time.Duration, message string) {
spinChars := []string{"β ", "β ", "β Ή", "β Έ", "β Ό", "β ΄", "β ¦", "β §", "β ", "β "}
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
done := time.After(duration)
i := 0
for {
select {
case <-done:
// Clear the spinner line
fmt.Print("\r\033[K")
return
case <-ticker.C:
fmt.Printf("\r%s %s", spinChars[i%len(spinChars)], message)
i++
}
}
}
// handleFleetDeploy implements the 'deploy' command to trigger a fleet deployment.
//
// This command triggers the actual deployment process for a fleet deployment,
// initiating the rollout of configurations across specified rings.
// After triggering, it polls the deployment status until completion.
//
// The command:
// 1. Validates flag values (done automatically by framework via YAML rules)
// 2. Constructs the deployment policy with the specified rings
// 3. Calls the New Relic API to trigger the deployment
// 4. Polls the deployment entity to track progress
// 5. Displays status updates while deployment is in progress
// 6. Shows final status when deployment completes (success or failure)
//
// Parameters:
// - cmd: The cobra command being executed
// - args: Command arguments (not used)
// - flags: Validated flag values from YAML configuration
//
// # Returns error if deployment trigger fails, nil on success
//
// suppressing the linter here, since breaking this function down to address cyclomatic complexity concerns
// could affect the code-perception of the hierarchical structure of the fleet commands
//
//nolint:gocyclo
func handleFleetDeploy(_ *cobra.Command, _ []string, flags *FlagValues) error {
// Get typed flag values - no hardcoded strings!
f := flags.Deploy()
// Build the deployment policy input
policy := fleetcontrol.FleetControlFleetDeploymentPolicyInput{
RingDeploymentPolicy: fleetcontrol.FleetControlRingDeploymentPolicyInput{
RingsToDeploy: f.RingsToDeploy,
},
}
// Call New Relic API to trigger the deployment
result, err := client.NRClient.FleetControl.FleetControlDeploy(
f.DeploymentID,
policy,
)
if err != nil {
return PrintError(fmt.Errorf("failed to trigger deployment: %w", err))
}
// Display initial message with rocket emoji
fmt.Printf("π Deployment triggered successfully (ID: %s)\n", result.ID)
fmt.Printf("π Monitoring deployment progress...\n\n")
// Poll the deployment entity to track progress
deploymentID := result.ID
pollInterval := 5 * time.Second
maxPolls := 360 // 30 minutes at 5 second intervals
for i := 0; i < maxPolls; i++ {
// Fetch the deployment entity
entityInterface, err := client.NRClient.FleetControl.GetEntity(deploymentID)
if err != nil {
return PrintError(fmt.Errorf("failed to fetch deployment status: %w", err))
}
if entityInterface == nil {
return PrintError(fmt.Errorf("deployment entity '%s' not found", deploymentID))
}
// Type assert to deployment entity
deploymentEntity, ok := (*entityInterface).(*fleetcontrol.EntityManagementFleetDeploymentEntity)
if !ok {
return PrintError(fmt.Errorf("entity '%s' is not a deployment (type: %T)", deploymentID, *entityInterface))
}
// Check the deployment phase
phase := deploymentEntity.Phase
// Display status
switch phase {
case fleetcontrol.EntityManagementFleetDeploymentPhaseTypes.CREATED:
fmt.Printf("β³ [%s] Status: CREATED - Deployment is being prepared...\n", time.Now().Format("15:04:05"))
case fleetcontrol.EntityManagementFleetDeploymentPhaseTypes.IN_PROGRESS:
// Display ring deployment tracker info if available
if len(deploymentEntity.RingsDeploymentTracker) > 0 {
fmt.Printf("π [%s] Status: IN_PROGRESS - Deploying across rings:\n", time.Now().Format("15:04:05"))
for _, ring := range deploymentEntity.RingsDeploymentTracker {
// Add status-specific emojis for each ring
ringEmoji := "βΊοΈ"
if ring.Status == "COMPLETED" {
ringEmoji = "β
"
} else if ring.Status == "IN_PROGRESS" {
ringEmoji = "π"
} else if ring.Status == "FAILED" {
ringEmoji = "β"
}
fmt.Printf(" %s Ring '%s': %s\n", ringEmoji, ring.Name, ring.Status)
}
} else {
fmt.Printf("π [%s] Status: IN_PROGRESS - Deployment is in progress...\n", time.Now().Format("15:04:05"))
}
case fleetcontrol.EntityManagementFleetDeploymentPhaseTypes.COMPLETED:
fmt.Printf("\nβ
Deployment COMPLETED successfully!\n")
fmt.Printf(" π Deployment ID: %s\n", deploymentEntity.ID)
fmt.Printf(" π Deployment Name: %s\n", deploymentEntity.Name)
if len(deploymentEntity.RingsDeploymentTracker) > 0 {
fmt.Printf(" π― Rings deployed:\n")
for _, ring := range deploymentEntity.RingsDeploymentTracker {
fmt.Printf(" β
%s: %s\n", ring.Name, ring.Status)
}
}
return PrintDeploymentSuccess(deploymentEntity)
case fleetcontrol.EntityManagementFleetDeploymentPhaseTypes.FAILED:
fmt.Printf("\nβ Deployment FAILED\n")
fmt.Printf(" π Deployment ID: %s\n", deploymentEntity.ID)
fmt.Printf(" π Deployment Name: %s\n", deploymentEntity.Name)
if len(deploymentEntity.RingsDeploymentTracker) > 0 {
fmt.Printf(" π Ring status:\n")
for _, ring := range deploymentEntity.RingsDeploymentTracker {
ringEmoji := "β"
if ring.Status == "COMPLETED" {
ringEmoji = "β
"
} else if ring.Status == "IN_PROGRESS" {
ringEmoji = "π"
}
fmt.Printf(" %s %s: %s\n", ringEmoji, ring.Name, ring.Status)
}
}
return PrintError(fmt.Errorf("deployment failed"))
case fleetcontrol.EntityManagementFleetDeploymentPhaseTypes.INTERNAL_FAILURE:
fmt.Printf("\nβ οΈ Deployment encountered an INTERNAL_FAILURE\n")
fmt.Printf(" π Deployment ID: %s\n", deploymentEntity.ID)
fmt.Printf(" π Deployment Name: %s\n", deploymentEntity.Name)
return PrintError(fmt.Errorf("deployment internal failure"))
default:
// Unknown phase - treat as complete and return
fmt.Printf("\nπ Deployment reached phase: %s\n", phase)
return PrintDeploymentSuccess(deploymentEntity)
}
// Show spinner while waiting before next poll
spinner(pollInterval, "Checking deployment status...")
}
// Timeout reached
return PrintError(fmt.Errorf("deployment status check timed out after %d polls", maxPolls))
}