fix: fail global deployment if no matchine machines found, improve caddy deploy output in particular

This commit is contained in:
Pavel Sviderski
2025-02-17 20:31:37 +10:00
parent e13408d79f
commit 98e1bdc9b5
3 changed files with 31 additions and 5 deletions
+19 -4
View File
@@ -28,7 +28,7 @@ func NewDeployCommand() *cobra.Command {
Use: "deploy",
Short: "Deploy or upgrade Caddy reverse proxy across all machines in the cluster.",
Long: "Deploy or upgrade Caddy reverse proxy across all machines in the cluster.\n" +
"It performs a rolling update if Caddy is already running.",
"A rolling update is performed when updating existing containers to minimise disruption.",
RunE: func(cmd *cobra.Command, args []string) error {
uncli := cmd.Context().Value("cli").(*cli.CLI)
return deploy(cmd.Context(), uncli, opts)
@@ -120,18 +120,33 @@ func deploy(ctx context.Context, uncli *cli.CLI, opts deployOptions) error {
plan, err := d.Plan(ctx)
if err != nil {
if errors.Is(err, client.ErrNoMatchingMachines) {
return fmt.Errorf("no machines found matching: %s", opts.machine)
}
return fmt.Errorf("plan caddy deployment: %w", err)
}
if len(plan.SequenceOperation.Operations) == 0 {
fmt.Printf("%s service is up to date.\n", client.CaddyServiceName)
if opts.machine != "" {
fmt.Printf("%s service is up to date on selected machines.\n", client.CaddyServiceName)
} else {
fmt.Printf("%s service is up to date.\n", client.CaddyServiceName)
}
return nil
}
if svc.ID == "" {
fmt.Println("This will run a Caddy container on each machine.")
if opts.machine != "" {
fmt.Println("This will run a Caddy container on selected machines.")
} else {
fmt.Println("This will run a Caddy container on each machine.")
}
} else {
fmt.Println("This will perform a rolling update of Caddy containers on each machine.")
if opts.machine != "" {
fmt.Println("This will perform a rolling update of Caddy containers on selected machines.")
} else {
fmt.Println("This will perform a rolling update of Caddy containers on each machine.")
}
}
fmt.Println()
+3 -1
View File
@@ -30,6 +30,8 @@ type Plan struct {
// machines that should be included.
type MachineFilter func(m *pb.MachineInfo) bool
var ErrNoMatchingMachines = errors.New("no machines match the filter")
// NewDeployment creates a new deployment for the given service specification.
// If strategy is nil, a default RollingStrategy will be used.
func (cli *Client) NewDeployment(spec api.ServiceSpec, strategy Strategy) (*Deployment, error) {
@@ -81,7 +83,7 @@ func (d *Deployment) Plan(ctx context.Context) (Plan, error) {
plan, err := d.Strategy.Plan(ctx, d.cli, d.Service, d.Spec)
if err != nil {
return Plan{}, fmt.Errorf("create plan using %T: %w", d.Strategy, err)
return Plan{}, fmt.Errorf("create plan using %s strategy: %w", d.Strategy.Type(), err)
}
d.plan = &plan
+9
View File
@@ -13,6 +13,8 @@ import (
// Strategy defines how a service should be deployed or updated. Different implementations can provide various
// deployment patterns such as rolling updates, blue/green deployments, etc.
type Strategy interface {
// Type returns the type of the deployment strategy, e.g. "rolling", "blue-green".
Type() string
// Plan returns the operation to reconcile the service to the desired state.
// If the service does not exist (new deployment), svc will be nil.
Plan(ctx context.Context, cli *Client, svc *api.Service, spec api.ServiceSpec) (Plan, error)
@@ -25,6 +27,10 @@ type RollingStrategy struct {
MachineFilter MachineFilter
}
func (s *RollingStrategy) Type() string {
return "rolling"
}
func (s *RollingStrategy) Plan(
ctx context.Context, cli *Client, svc *api.Service, spec api.ServiceSpec,
) (Plan, error) {
@@ -81,6 +87,9 @@ func (s *RollingStrategy) planGlobal(
machines = slices.DeleteFunc(machines, func(m *pb.MachineMember) bool {
return !s.MachineFilter(m.Machine)
})
if len(machines) == 0 {
return plan, ErrNoMatchingMachines
}
}
// TODO: figure out how to return a warning if there are machines down. Embed the machinesDown in the plan?