- Add edge-connect apply command with -f/--file and --dry-run flags - Integrate config parser, deployment planner, and resource manager - Provide comprehensive error handling and progress reporting - Support deployment confirmation prompts and result summaries - Move internal packages to public SDK packages for CLI access - Update all tests to pass with new package structure - Complete Phase 4 CLI Command Implementation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
428 lines
No EOL
11 KiB
Go
428 lines
No EOL
11 KiB
Go
// ABOUTME: Deployment planning types for EdgeConnect apply command with state management
|
|
// ABOUTME: Defines structures for deployment plans, actions, and state comparison results
|
|
package apply
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"edp.buildth.ing/DevFW-CICD/edge-connect-client/sdk/config"
|
|
)
|
|
|
|
// ActionType represents the type of action to be performed
|
|
type ActionType string
|
|
|
|
const (
|
|
// ActionCreate indicates a resource needs to be created
|
|
ActionCreate ActionType = "CREATE"
|
|
// ActionUpdate indicates a resource needs to be updated
|
|
ActionUpdate ActionType = "UPDATE"
|
|
// ActionNone indicates no action is needed
|
|
ActionNone ActionType = "NONE"
|
|
// ActionDelete indicates a resource needs to be deleted (for rollback scenarios)
|
|
ActionDelete ActionType = "DELETE"
|
|
)
|
|
|
|
// String returns the string representation of ActionType
|
|
func (a ActionType) String() string {
|
|
return string(a)
|
|
}
|
|
|
|
// DeploymentPlan represents the complete deployment plan for a configuration
|
|
type DeploymentPlan struct {
|
|
// ConfigName is the name from metadata
|
|
ConfigName string
|
|
|
|
// AppAction defines what needs to be done with the application
|
|
AppAction AppAction
|
|
|
|
// InstanceActions defines what needs to be done with each instance
|
|
InstanceActions []InstanceAction
|
|
|
|
// Summary provides a human-readable summary of the plan
|
|
Summary string
|
|
|
|
// TotalActions is the count of all actions that will be performed
|
|
TotalActions int
|
|
|
|
// EstimatedDuration is the estimated time to complete the deployment
|
|
EstimatedDuration time.Duration
|
|
|
|
// CreatedAt timestamp when the plan was created
|
|
CreatedAt time.Time
|
|
|
|
// DryRun indicates if this is a dry-run plan
|
|
DryRun bool
|
|
}
|
|
|
|
// AppAction represents an action to be performed on an application
|
|
type AppAction struct {
|
|
// Type of action to perform
|
|
Type ActionType
|
|
|
|
// Current state of the app (nil if doesn't exist)
|
|
Current *AppState
|
|
|
|
// Desired state of the app
|
|
Desired *AppState
|
|
|
|
// Changes describes what will change
|
|
Changes []string
|
|
|
|
// Reason explains why this action is needed
|
|
Reason string
|
|
|
|
// ManifestHash is the hash of the current manifest file
|
|
ManifestHash string
|
|
|
|
// ManifestChanged indicates if the manifest content has changed
|
|
ManifestChanged bool
|
|
}
|
|
|
|
// InstanceAction represents an action to be performed on an application instance
|
|
type InstanceAction struct {
|
|
// Type of action to perform
|
|
Type ActionType
|
|
|
|
// Target infrastructure where the instance will be deployed
|
|
Target config.InfraTemplate
|
|
|
|
// Current state of the instance (nil if doesn't exist)
|
|
Current *InstanceState
|
|
|
|
// Desired state of the instance
|
|
Desired *InstanceState
|
|
|
|
// Changes describes what will change
|
|
Changes []string
|
|
|
|
// Reason explains why this action is needed
|
|
Reason string
|
|
|
|
// InstanceName is the generated name for this instance
|
|
InstanceName string
|
|
|
|
// Dependencies lists other instances this depends on
|
|
Dependencies []string
|
|
}
|
|
|
|
// AppState represents the current state of an application
|
|
type AppState struct {
|
|
// Name of the application
|
|
Name string
|
|
|
|
// Version of the application
|
|
Version string
|
|
|
|
// Organization that owns the app
|
|
Organization string
|
|
|
|
// Region where the app is deployed
|
|
Region string
|
|
|
|
// ManifestHash is the stored hash of the manifest file
|
|
ManifestHash string
|
|
|
|
// LastUpdated timestamp when the app was last modified
|
|
LastUpdated time.Time
|
|
|
|
// Exists indicates if the app currently exists
|
|
Exists bool
|
|
|
|
// AppType indicates whether this is a k8s or docker app
|
|
AppType AppType
|
|
}
|
|
|
|
// InstanceState represents the current state of an application instance
|
|
type InstanceState struct {
|
|
// Name of the instance
|
|
Name string
|
|
|
|
// AppName that this instance belongs to
|
|
AppName string
|
|
|
|
// AppVersion of the associated app
|
|
AppVersion string
|
|
|
|
// Organization that owns the instance
|
|
Organization string
|
|
|
|
// Region where the instance is deployed
|
|
Region string
|
|
|
|
// CloudletOrg that hosts the cloudlet
|
|
CloudletOrg string
|
|
|
|
// CloudletName where the instance is running
|
|
CloudletName string
|
|
|
|
// FlavorName used for the instance
|
|
FlavorName string
|
|
|
|
// State of the instance (e.g., "Ready", "Pending", "Error")
|
|
State string
|
|
|
|
// PowerState of the instance
|
|
PowerState string
|
|
|
|
// LastUpdated timestamp when the instance was last modified
|
|
LastUpdated time.Time
|
|
|
|
// Exists indicates if the instance currently exists
|
|
Exists bool
|
|
}
|
|
|
|
// AppType represents the type of application
|
|
type AppType string
|
|
|
|
const (
|
|
// AppTypeK8s represents a Kubernetes application
|
|
AppTypeK8s AppType = "k8s"
|
|
// AppTypeDocker represents a Docker application
|
|
AppTypeDocker AppType = "docker"
|
|
)
|
|
|
|
// String returns the string representation of AppType
|
|
func (a AppType) String() string {
|
|
return string(a)
|
|
}
|
|
|
|
// DeploymentSummary provides a high-level overview of the deployment plan
|
|
type DeploymentSummary struct {
|
|
// TotalActions is the total number of actions to be performed
|
|
TotalActions int
|
|
|
|
// ActionCounts breaks down actions by type
|
|
ActionCounts map[ActionType]int
|
|
|
|
// EstimatedDuration for the entire deployment
|
|
EstimatedDuration time.Duration
|
|
|
|
// ResourceSummary describes the resources involved
|
|
ResourceSummary ResourceSummary
|
|
|
|
// Warnings about potential issues
|
|
Warnings []string
|
|
}
|
|
|
|
// ResourceSummary provides details about resources in the deployment
|
|
type ResourceSummary struct {
|
|
// AppsToCreate number of apps that will be created
|
|
AppsToCreate int
|
|
|
|
// AppsToUpdate number of apps that will be updated
|
|
AppsToUpdate int
|
|
|
|
// InstancesToCreate number of instances that will be created
|
|
InstancesToCreate int
|
|
|
|
// InstancesToUpdate number of instances that will be updated
|
|
InstancesToUpdate int
|
|
|
|
// CloudletsAffected number of unique cloudlets involved
|
|
CloudletsAffected int
|
|
|
|
// RegionsAffected number of unique regions involved
|
|
RegionsAffected int
|
|
}
|
|
|
|
// PlanResult represents the result of a deployment planning operation
|
|
type PlanResult struct {
|
|
// Plan is the generated deployment plan
|
|
Plan *DeploymentPlan
|
|
|
|
// Error if planning failed
|
|
Error error
|
|
|
|
// Warnings encountered during planning
|
|
Warnings []string
|
|
}
|
|
|
|
// ExecutionResult represents the result of executing a deployment plan
|
|
type ExecutionResult struct {
|
|
// Plan that was executed
|
|
Plan *DeploymentPlan
|
|
|
|
// Success indicates if the deployment was successful
|
|
Success bool
|
|
|
|
// CompletedActions lists actions that were successfully completed
|
|
CompletedActions []ActionResult
|
|
|
|
// FailedActions lists actions that failed
|
|
FailedActions []ActionResult
|
|
|
|
// Error that caused the deployment to fail (if any)
|
|
Error error
|
|
|
|
// Duration taken to execute the plan
|
|
Duration time.Duration
|
|
|
|
// RollbackPerformed indicates if rollback was executed
|
|
RollbackPerformed bool
|
|
|
|
// RollbackSuccess indicates if rollback was successful
|
|
RollbackSuccess bool
|
|
}
|
|
|
|
// ActionResult represents the result of executing a single action
|
|
type ActionResult struct {
|
|
// Type of action that was attempted
|
|
Type ActionType
|
|
|
|
// Target describes what was being acted upon
|
|
Target string
|
|
|
|
// Success indicates if the action succeeded
|
|
Success bool
|
|
|
|
// Error if the action failed
|
|
Error error
|
|
|
|
// Duration taken to complete the action
|
|
Duration time.Duration
|
|
|
|
// Details provides additional information about the action
|
|
Details string
|
|
}
|
|
|
|
// IsEmpty returns true if the deployment plan has no actions to perform
|
|
func (dp *DeploymentPlan) IsEmpty() bool {
|
|
if dp.AppAction.Type != ActionNone {
|
|
return false
|
|
}
|
|
|
|
for _, action := range dp.InstanceActions {
|
|
if action.Type != ActionNone {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// HasErrors returns true if the plan contains any error conditions
|
|
func (dp *DeploymentPlan) HasErrors() bool {
|
|
// Check for conflicting actions or invalid states
|
|
return false // Implementation would check for various error conditions
|
|
}
|
|
|
|
// GetTargetCloudlets returns a list of unique cloudlets that will be affected
|
|
func (dp *DeploymentPlan) GetTargetCloudlets() []string {
|
|
cloudletSet := make(map[string]bool)
|
|
var cloudlets []string
|
|
|
|
for _, action := range dp.InstanceActions {
|
|
if action.Type != ActionNone {
|
|
key := fmt.Sprintf("%s:%s", action.Target.CloudletOrg, action.Target.CloudletName)
|
|
if !cloudletSet[key] {
|
|
cloudletSet[key] = true
|
|
cloudlets = append(cloudlets, key)
|
|
}
|
|
}
|
|
}
|
|
|
|
return cloudlets
|
|
}
|
|
|
|
// GetTargetRegions returns a list of unique regions that will be affected
|
|
func (dp *DeploymentPlan) GetTargetRegions() []string {
|
|
regionSet := make(map[string]bool)
|
|
var regions []string
|
|
|
|
for _, action := range dp.InstanceActions {
|
|
if action.Type != ActionNone && !regionSet[action.Target.Region] {
|
|
regionSet[action.Target.Region] = true
|
|
regions = append(regions, action.Target.Region)
|
|
}
|
|
}
|
|
|
|
return regions
|
|
}
|
|
|
|
// GenerateSummary creates a human-readable summary of the deployment plan
|
|
func (dp *DeploymentPlan) GenerateSummary() string {
|
|
if dp.IsEmpty() {
|
|
return "No changes required - configuration matches current state"
|
|
}
|
|
|
|
summary := fmt.Sprintf("Deployment plan for '%s':\n", dp.ConfigName)
|
|
|
|
// App actions
|
|
if dp.AppAction.Type != ActionNone {
|
|
summary += fmt.Sprintf("- %s application '%s'\n", dp.AppAction.Type, dp.AppAction.Desired.Name)
|
|
if len(dp.AppAction.Changes) > 0 {
|
|
for _, change := range dp.AppAction.Changes {
|
|
summary += fmt.Sprintf(" - %s\n", change)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Instance actions
|
|
createCount := 0
|
|
updateCount := 0
|
|
for _, action := range dp.InstanceActions {
|
|
switch action.Type {
|
|
case ActionCreate:
|
|
createCount++
|
|
case ActionUpdate:
|
|
updateCount++
|
|
}
|
|
}
|
|
|
|
if createCount > 0 {
|
|
summary += fmt.Sprintf("- CREATE %d instance(s) across %d cloudlet(s)\n", createCount, len(dp.GetTargetCloudlets()))
|
|
}
|
|
if updateCount > 0 {
|
|
summary += fmt.Sprintf("- UPDATE %d instance(s)\n", updateCount)
|
|
}
|
|
|
|
summary += fmt.Sprintf("Estimated duration: %s", dp.EstimatedDuration.String())
|
|
|
|
return summary
|
|
}
|
|
|
|
// Validate checks if the deployment plan is valid and safe to execute
|
|
func (dp *DeploymentPlan) Validate() error {
|
|
if dp.ConfigName == "" {
|
|
return fmt.Errorf("deployment plan must have a config name")
|
|
}
|
|
|
|
// Validate app action
|
|
if dp.AppAction.Type != ActionNone && dp.AppAction.Desired == nil {
|
|
return fmt.Errorf("app action of type %s must have desired state", dp.AppAction.Type)
|
|
}
|
|
|
|
// Validate instance actions
|
|
for i, action := range dp.InstanceActions {
|
|
if action.Type != ActionNone {
|
|
if action.Desired == nil {
|
|
return fmt.Errorf("instance action %d of type %s must have desired state", i, action.Type)
|
|
}
|
|
if action.InstanceName == "" {
|
|
return fmt.Errorf("instance action %d must have an instance name", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Clone creates a deep copy of the deployment plan
|
|
func (dp *DeploymentPlan) Clone() *DeploymentPlan {
|
|
clone := &DeploymentPlan{
|
|
ConfigName: dp.ConfigName,
|
|
Summary: dp.Summary,
|
|
TotalActions: dp.TotalActions,
|
|
EstimatedDuration: dp.EstimatedDuration,
|
|
CreatedAt: dp.CreatedAt,
|
|
DryRun: dp.DryRun,
|
|
AppAction: dp.AppAction, // Struct copy is sufficient for this use case
|
|
}
|
|
|
|
// Deep copy instance actions
|
|
clone.InstanceActions = make([]InstanceAction, len(dp.InstanceActions))
|
|
copy(clone.InstanceActions, dp.InstanceActions)
|
|
|
|
return clone
|
|
} |