consul/command/maint/maint.go

186 lines
4.9 KiB
Go
Raw Normal View History

// Copyright (c) HashiCorp, Inc.
[COMPLIANCE] License changes (#18443) * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2023-08-11 13:12:13 +00:00
// SPDX-License-Identifier: BUSL-1.1
2017-10-12 21:55:32 +00:00
package maint
2015-01-21 21:00:14 +00:00
import (
2017-10-12 21:55:32 +00:00
"flag"
2015-01-21 21:00:14 +00:00
"fmt"
"strings"
2017-10-12 21:55:32 +00:00
"github.com/hashicorp/consul/command/flags"
"github.com/mitchellh/cli"
2015-01-21 21:00:14 +00:00
)
2017-10-12 21:55:32 +00:00
// cmd is a Command implementation that enables or disables
2015-01-21 21:00:14 +00:00
// node or service maintenance mode.
2017-10-12 21:55:32 +00:00
type cmd struct {
UI cli.Ui
help string
2017-10-12 21:55:32 +00:00
flags *flag.FlagSet
http *flags.HTTPFlags
// flags
enable bool
disable bool
reason string
serviceID string
}
2017-10-12 21:55:32 +00:00
func New(ui cli.Ui) *cmd {
c := &cmd{UI: ui}
c.init()
return c
}
func (c *cmd) init() {
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
c.flags.BoolVar(&c.enable, "enable", false,
"Enable maintenance mode.")
2017-10-12 21:55:32 +00:00
c.flags.BoolVar(&c.disable, "disable", false,
"Disable maintenance mode.")
2017-10-12 21:55:32 +00:00
c.flags.StringVar(&c.reason, "reason", "",
"Text describing the maintenance reason.")
2017-10-12 21:55:32 +00:00
c.flags.StringVar(&c.serviceID, "service", "",
"Control maintenance mode for a specific service ID.")
2015-01-21 21:00:14 +00:00
2017-10-18 00:38:26 +00:00
c.http = &flags.HTTPFlags{}
flags.Merge(c.flags, c.http.ClientFlags())
c.help = flags.Usage(help, c.flags)
2015-01-21 21:00:14 +00:00
}
2017-10-12 21:55:32 +00:00
func (c *cmd) Run(args []string) int {
if err := c.flags.Parse(args); err != nil {
2015-01-21 21:00:14 +00:00
return 1
}
// Ensure we don't have conflicting args
if c.enable && c.disable {
2017-04-21 00:02:42 +00:00
c.UI.Error("Only one of -enable or -disable may be provided")
2015-01-21 21:00:14 +00:00
return 1
}
if !c.enable && c.reason != "" {
2017-04-21 00:02:42 +00:00
c.UI.Error("Reason may only be provided with -enable")
2015-01-21 21:00:14 +00:00
return 1
}
if !c.enable && !c.disable && c.serviceID != "" {
2017-04-21 00:02:42 +00:00
c.UI.Error("Service requires either -enable or -disable")
return 1
}
2015-01-21 21:00:14 +00:00
// Create and test the HTTP client
2017-10-12 21:55:32 +00:00
client, err := c.http.APIClient()
2015-01-21 21:00:14 +00:00
if err != nil {
2017-04-21 00:02:42 +00:00
c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
2015-01-21 21:00:14 +00:00
return 1
}
a := client.Agent()
2015-01-21 21:00:14 +00:00
if !c.enable && !c.disable {
nodeName, err := a.NodeName()
if err != nil {
c.UI.Error(fmt.Sprintf("Error querying Consul agent: %s", err))
return 1
}
// List mode - list nodes/services in maintenance mode
checks, err := a.Checks()
if err != nil {
2017-04-21 00:02:42 +00:00
c.UI.Error(fmt.Sprintf("Error getting checks: %s", err))
return 1
}
for _, check := range checks {
2015-01-22 19:14:28 +00:00
if check.CheckID == "_node_maintenance" {
2017-04-21 00:02:42 +00:00
c.UI.Output("Node:")
c.UI.Output(" Name: " + nodeName)
c.UI.Output(" Reason: " + check.Notes)
c.UI.Output("")
} else if strings.HasPrefix(check.CheckID, "_service_maintenance:") {
2017-04-21 00:02:42 +00:00
c.UI.Output("Service:")
c.UI.Output(" ID: " + check.ServiceID)
c.UI.Output(" Reason: " + check.Notes)
c.UI.Output("")
}
}
return 0
}
if c.enable {
2015-01-21 21:00:14 +00:00
// Enable node maintenance
if c.serviceID == "" {
if err := a.EnableNodeMaintenance(c.reason); err != nil {
2017-04-21 00:02:42 +00:00
c.UI.Error(fmt.Sprintf("Error enabling node maintenance: %s", err))
2015-01-21 21:00:14 +00:00
return 1
}
2017-04-21 00:02:42 +00:00
c.UI.Output("Node maintenance is now enabled")
2015-01-21 21:00:14 +00:00
return 0
}
// Enable service maintenance
if err := a.EnableServiceMaintenance(c.serviceID, c.reason); err != nil {
2017-04-21 00:02:42 +00:00
c.UI.Error(fmt.Sprintf("Error enabling service maintenance: %s", err))
2015-01-21 21:00:14 +00:00
return 1
}
c.UI.Output(fmt.Sprintf("Service maintenance is now enabled for %q", c.serviceID))
2015-01-21 21:11:23 +00:00
return 0
}
if c.disable {
2015-01-21 21:11:23 +00:00
// Disable node maintenance
if c.serviceID == "" {
if err := a.DisableNodeMaintenance(); err != nil {
2017-04-21 00:02:42 +00:00
c.UI.Error(fmt.Sprintf("Error disabling node maintenance: %s", err))
2015-01-21 21:11:23 +00:00
return 1
}
2017-04-21 00:02:42 +00:00
c.UI.Output("Node maintenance is now disabled")
2015-01-21 21:11:23 +00:00
return 0
}
// Disable service maintenance
if err := a.DisableServiceMaintenance(c.serviceID); err != nil {
2017-04-21 00:02:42 +00:00
c.UI.Error(fmt.Sprintf("Error disabling service maintenance: %s", err))
2015-01-21 21:11:23 +00:00
return 1
}
c.UI.Output(fmt.Sprintf("Service maintenance is now disabled for %q", c.serviceID))
2015-01-21 21:11:23 +00:00
return 0
2015-01-21 21:00:14 +00:00
}
return 0
}
2017-10-12 21:55:32 +00:00
func (c *cmd) Synopsis() string {
return synopsis
2015-01-21 21:00:14 +00:00
}
2017-10-12 21:55:32 +00:00
func (c *cmd) Help() string {
return c.help
}
const synopsis = "Controls node or service maintenance mode"
const help = `
Usage: consul maint [options]
2017-10-12 21:55:32 +00:00
Places a node or service into maintenance mode. During maintenance mode,
the node or service will be excluded from all queries through the DNS
or API interfaces, effectively taking it out of the pool of available
nodes. This is done by registering an additional critical health check.
When enabling maintenance mode for a node or service, you may optionally
specify a reason string. This string will appear in the "Notes" field
of the critical health check which is registered against the node or
service. If no reason is provided, a default value will be used.
Maintenance mode is persistent, and will be restored in the event of an
agent restart. It is therefore required to disable maintenance mode on
a given node or service before it will be placed back into the pool.
By default, we operate on the node as a whole. By specifying the
"-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.
`