From 09fd2a7e944c837ff3fb445266b67393d070025e Mon Sep 17 00:00:00 2001 From: Ryan Uber Date: Thu, 22 Jan 2015 10:26:17 -0800 Subject: [PATCH] command/maint: display active maintenance when no args are passed --- command/agent/agent.go | 14 +++++----- command/maint.go | 52 +++++++++++++++++++++++++----------- command/maint_test.go | 60 +++++++++++++++++++++++++++++++++--------- 3 files changed, 90 insertions(+), 36 deletions(-) diff --git a/command/agent/agent.go b/command/agent/agent.go index 7ec832e709..95ef455725 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -32,8 +32,8 @@ const ( "and try again." // The ID of the faux health checks for maintenance mode - serviceMaintCheckPrefix = "_service_maintenance" - nodeMaintCheckID = "_node_maintenance" + ServiceMaintCheckPrefix = "_service_maintenance" + NodeMaintCheckID = "_node_maintenance" // Default reasons for node/service maintenance mode defaultNodeMaintReason = "Maintenance mode is enabled for this node, " + @@ -1028,7 +1028,7 @@ func (a *Agent) unloadChecks() error { // serviceMaintCheckID returns the ID of a given service's maintenance check func serviceMaintCheckID(serviceID string) string { - return fmt.Sprintf("%s:%s", serviceMaintCheckPrefix, serviceID) + return fmt.Sprintf("%s:%s", ServiceMaintCheckPrefix, serviceID) } // EnableServiceMaintenance will register a false health check against the given @@ -1089,7 +1089,7 @@ func (a *Agent) DisableServiceMaintenance(serviceID string) error { // EnableNodeMaintenance places a node into maintenance mode. func (a *Agent) EnableNodeMaintenance(reason string) { // Ensure node maintenance is not already enabled - if _, ok := a.state.Checks()[nodeMaintCheckID]; ok { + if _, ok := a.state.Checks()[NodeMaintCheckID]; ok { return } @@ -1101,7 +1101,7 @@ func (a *Agent) EnableNodeMaintenance(reason string) { // Create and register the node maintenance check check := &structs.HealthCheck{ Node: a.config.NodeName, - CheckID: nodeMaintCheckID, + CheckID: NodeMaintCheckID, Name: "Node Maintenance Mode", Notes: reason, Status: structs.HealthCritical, @@ -1112,9 +1112,9 @@ func (a *Agent) EnableNodeMaintenance(reason string) { // DisableNodeMaintenance removes a node from maintenance mode func (a *Agent) DisableNodeMaintenance() { - if _, ok := a.state.Checks()[nodeMaintCheckID]; !ok { + if _, ok := a.state.Checks()[NodeMaintCheckID]; !ok { return } - a.RemoveCheck(nodeMaintCheckID, true) + a.RemoveCheck(NodeMaintCheckID, true) a.logger.Printf("[INFO] agent: node left maintenance mode") } diff --git a/command/maint.go b/command/maint.go index 41ac598a2c..eb8d6ef518 100644 --- a/command/maint.go +++ b/command/maint.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/command/agent" "github.com/mitchellh/cli" ) @@ -37,6 +38,9 @@ Usage: consul maint [options] "-service" argument, this behavior can be changed to enable or disable only a specific service. + If no arguments are given, the agent's maintenance status will be shown. + This will return blank if nothing is currently under maintenance. + Options: -enable Enable maintenance mode. @@ -70,17 +74,7 @@ func (c *MaintCommand) Run(args []string) int { return 1 } - // Print help if no args given - if len(args) == 0 { - c.Ui.Error(c.Help()) - return 1 - } - // Ensure we don't have conflicting args - if !enable && !disable { - c.Ui.Error("One of -enable or -disable must be specified") - return 1 - } if enable && disable { c.Ui.Error("Only one of -enable or -disable may be provided") return 1 @@ -99,16 +93,42 @@ func (c *MaintCommand) Run(args []string) int { c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err)) return 1 } - agent := client.Agent() - if _, err := agent.NodeName(); err != nil { + a := client.Agent() + nodeName, err := a.NodeName() + if err != nil { c.Ui.Error(fmt.Sprintf("Error querying Consul agent: %s", err)) return 1 } + if !enable && !disable { + // List mode - list nodes/services in maintenance mode + checks, err := a.Checks() + if err != nil { + c.Ui.Output(fmt.Sprintf("Error getting checks: %s", err)) + return 1 + } + + for _, check := range checks { + if check.CheckID == agent.NodeMaintCheckID { + c.Ui.Output("Node:") + c.Ui.Output(" Name: " + nodeName) + c.Ui.Output(" Reason: " + check.Notes) + c.Ui.Output("") + } else if strings.HasPrefix(check.CheckID, agent.ServiceMaintCheckPrefix) { + c.Ui.Output("Service:") + c.Ui.Output(" ID: " + check.ServiceID) + c.Ui.Output(" Reason: " + check.Notes) + c.Ui.Output("") + } + } + + return 0 + } + if enable { // Enable node maintenance if serviceID == "" { - if err := agent.EnableNodeMaintenance(reason); err != nil { + if err := a.EnableNodeMaintenance(reason); err != nil { c.Ui.Error(fmt.Sprintf("Error enabling node maintenance: %s", err)) return 1 } @@ -117,7 +137,7 @@ func (c *MaintCommand) Run(args []string) int { } // Enable service maintenance - if err := agent.EnableServiceMaintenance(serviceID, reason); err != nil { + if err := a.EnableServiceMaintenance(serviceID, reason); err != nil { c.Ui.Error(fmt.Sprintf("Error enabling service maintenance: %s", err)) return 1 } @@ -128,7 +148,7 @@ func (c *MaintCommand) Run(args []string) int { if disable { // Disable node maintenance if serviceID == "" { - if err := agent.DisableNodeMaintenance(); err != nil { + if err := a.DisableNodeMaintenance(); err != nil { c.Ui.Error(fmt.Sprintf("Error disabling node maintenance: %s", err)) return 1 } @@ -137,7 +157,7 @@ func (c *MaintCommand) Run(args []string) int { } // Disable service maintenance - if err := agent.DisableServiceMaintenance(serviceID); err != nil { + if err := a.DisableServiceMaintenance(serviceID); err != nil { c.Ui.Error(fmt.Sprintf("Error disabling service maintenance: %s", err)) return 1 } diff --git a/command/maint_test.go b/command/maint_test.go index 66d82f90bb..7b60f2b0db 100644 --- a/command/maint_test.go +++ b/command/maint_test.go @@ -12,19 +12,6 @@ func TestMaintCommand_implements(t *testing.T) { var _ cli.Command = &MaintCommand{} } -func TestMaintCommandRun_NoArgs(t *testing.T) { - ui := new(cli.MockUi) - c := &MaintCommand{Ui: ui} - - if code := c.Run([]string{}); code != 1 { - t.Fatalf("expected return code 1, got %d", code) - } - - if strings.TrimSpace(ui.ErrorWriter.String()) != c.Help() { - t.Fatalf("bad:\n%s", ui.ErrorWriter.String()) - } -} - func TestMaintCommandRun_ConflictingArgs(t *testing.T) { ui := new(cli.MockUi) c := &MaintCommand{Ui: ui} @@ -38,6 +25,53 @@ func TestMaintCommandRun_ConflictingArgs(t *testing.T) { } } +func TestMaintCommandRun_NoArgs(t *testing.T) { + a1 := testAgent(t) + defer a1.Shutdown() + + // Register the service and put it into maintenance mode + service := &structs.NodeService{ + ID: "test", + Service: "test", + } + if err := a1.agent.AddService(service, nil, false); err != nil { + t.Fatalf("err: %v", err) + } + if err := a1.agent.EnableServiceMaintenance("test", "broken 1"); err != nil { + t.Fatalf("err: %s", err) + } + + // Enable node maintenance + a1.agent.EnableNodeMaintenance("broken 2") + + // Run consul maint with no args (list mode) + ui := new(cli.MockUi) + c := &MaintCommand{Ui: ui} + + args := []string{"-http-addr=" + a1.httpAddr} + code := c.Run(args) + if code != 0 { + t.Fatalf("bad: %d. %#v", code, ui.ErrorWriter.String()) + } + + // Ensure the service shows up in the list + out := ui.OutputWriter.String() + if !strings.Contains(out, "test") { + t.Fatalf("bad:\n%s", out) + } + if !strings.Contains(out, "broken 1") { + t.Fatalf("bad:\n%s", out) + } + + // Ensure the node shows up in the list + if !strings.Contains(out, a1.config.NodeName) { + t.Fatalf("bad:\n%s", out) + } + if !strings.Contains(out, "broken 2") { + t.Fatalf("bad:\n%s", out) + } +} + func TestMaintCommandRun_EnableNodeMaintenance(t *testing.T) { a1 := testAgent(t) defer a1.Shutdown()