mirror of
https://github.com/kemko/nomad.git
synced 2026-01-08 19:35:41 +03:00
multiregion CLI: nomad deployment unblock
This commit is contained in:
@@ -47,6 +47,9 @@ func (s *HTTPServer) DeploymentSpecificRequest(resp http.ResponseWriter, req *ht
|
||||
case strings.HasPrefix(path, "allocation-health/"):
|
||||
deploymentID := strings.TrimPrefix(path, "allocation-health/")
|
||||
return s.deploymentSetAllocHealth(resp, req, deploymentID)
|
||||
case strings.HasPrefix(path, "unblock/"):
|
||||
deploymentID := strings.TrimPrefix(path, "unblock/")
|
||||
return s.deploymentUnblock(resp, req, deploymentID)
|
||||
default:
|
||||
return s.deploymentQuery(resp, req, path)
|
||||
}
|
||||
@@ -120,6 +123,31 @@ func (s *HTTPServer) deploymentPromote(resp http.ResponseWriter, req *http.Reque
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *HTTPServer) deploymentUnblock(resp http.ResponseWriter, req *http.Request, deploymentID string) (interface{}, error) {
|
||||
if req.Method != "PUT" && req.Method != "POST" {
|
||||
return nil, CodedError(405, ErrInvalidMethod)
|
||||
}
|
||||
|
||||
var unblockRequest structs.DeploymentUnblockRequest
|
||||
if err := decodeBody(req, &unblockRequest); err != nil {
|
||||
return nil, CodedError(400, err.Error())
|
||||
}
|
||||
if unblockRequest.DeploymentID == "" {
|
||||
return nil, CodedError(400, "DeploymentID must be specified")
|
||||
}
|
||||
if unblockRequest.DeploymentID != deploymentID {
|
||||
return nil, CodedError(400, "Deployment ID does not match")
|
||||
}
|
||||
s.parseWriteRequest(req, &unblockRequest.WriteRequest)
|
||||
|
||||
var out structs.DeploymentUpdateResponse
|
||||
if err := s.agent.RPC("Deployment.Unblock", &unblockRequest, &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setIndex(resp, out.Index)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *HTTPServer) deploymentSetAllocHealth(resp http.ResponseWriter, req *http.Request, deploymentID string) (interface{}, error) {
|
||||
if req.Method != "PUT" && req.Method != "POST" {
|
||||
return nil, CodedError(405, ErrInvalidMethod)
|
||||
|
||||
@@ -236,6 +236,11 @@ func Commands(metaPtr *Meta, agentUi cli.Ui) map[string]cli.CommandFactory {
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
"deployment unblock": func() (cli.Command, error) {
|
||||
return &DeploymentUnblockCommand{
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
"eval": func() (cli.Command, error) {
|
||||
return &EvalCommand{
|
||||
Meta: meta,
|
||||
|
||||
131
command/deployment_unblock.go
Normal file
131
command/deployment_unblock.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/nomad/api/contexts"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
type DeploymentUnblockCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
func (c *DeploymentUnblockCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: nomad deployment unblock [options] <deployment id>
|
||||
|
||||
Unblock is used to unblock a multiregion deployment that's waiting for
|
||||
peer region deployments to complete.
|
||||
|
||||
General Options:
|
||||
|
||||
` + generalOptionsUsage() + `
|
||||
|
||||
Unblock Options:
|
||||
|
||||
-detach
|
||||
Return immediately instead of entering monitor mode. After deployment
|
||||
unblock, the evaluation ID will be printed to the screen, which can be used
|
||||
to examine the evaluation using the eval-status command.
|
||||
|
||||
-verbose
|
||||
Display full information.
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *DeploymentUnblockCommand) Synopsis() string {
|
||||
return "Unblock a blocked deployment"
|
||||
}
|
||||
|
||||
func (c *DeploymentUnblockCommand) AutocompleteFlags() complete.Flags {
|
||||
return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
|
||||
complete.Flags{
|
||||
"-detach": complete.PredictNothing,
|
||||
"-verbose": complete.PredictNothing,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *DeploymentUnblockCommand) AutocompleteArgs() complete.Predictor {
|
||||
return complete.PredictFunc(func(a complete.Args) []string {
|
||||
client, err := c.Meta.Client()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
resp, _, err := client.Search().PrefixSearch(a.Last, contexts.Deployments, nil)
|
||||
if err != nil {
|
||||
return []string{}
|
||||
}
|
||||
return resp.Matches[contexts.Deployments]
|
||||
})
|
||||
}
|
||||
|
||||
func (c *DeploymentUnblockCommand) Name() string { return "deployment unblock" }
|
||||
func (c *DeploymentUnblockCommand) Run(args []string) int {
|
||||
var detach, verbose bool
|
||||
|
||||
flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
|
||||
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||
flags.BoolVar(&detach, "detach", false, "")
|
||||
flags.BoolVar(&verbose, "verbose", false, "")
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Check that we got exactly one argument
|
||||
args = flags.Args()
|
||||
if l := len(args); l != 1 {
|
||||
c.Ui.Error("This command takes one argument: <deployment id>")
|
||||
c.Ui.Error(commandErrorText(c))
|
||||
return 1
|
||||
}
|
||||
|
||||
dID := args[0]
|
||||
|
||||
// Truncate the id unless full length is requested
|
||||
length := shortId
|
||||
if verbose {
|
||||
length = fullId
|
||||
}
|
||||
|
||||
// Get the HTTP client
|
||||
client, err := c.Meta.Client()
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
// Do a prefix lookup
|
||||
deploy, possible, err := getDeployment(client.Deployments(), dID)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error retrieving deployment: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
if len(possible) != 0 {
|
||||
c.Ui.Error(fmt.Sprintf("Prefix matched multiple deployments\n\n%s", formatDeployments(possible, length)))
|
||||
return 1
|
||||
}
|
||||
|
||||
u, _, err := client.Deployments().Unblock(deploy.ID, nil)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error unblocking deployment: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
c.Ui.Output(fmt.Sprintf("Deployment %q unblocked", deploy.ID))
|
||||
evalCreated := u.EvalID != ""
|
||||
|
||||
// Nothing to do
|
||||
if detach || !evalCreated {
|
||||
return 0
|
||||
}
|
||||
|
||||
c.Ui.Output("")
|
||||
mon := newMonitor(c.Ui, client, length)
|
||||
return mon.monitor(u.EvalID, false)
|
||||
}
|
||||
63
command/deployment_unblock_test.go
Normal file
63
command/deployment_unblock_test.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/nomad/nomad/mock"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/posener/complete"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDeploymentUnblockCommand_Implements(t *testing.T) {
|
||||
t.Parallel()
|
||||
var _ cli.Command = &DeploymentUnblockCommand{}
|
||||
}
|
||||
|
||||
func TestDeploymentUnblockCommand_Fails(t *testing.T) {
|
||||
t.Parallel()
|
||||
ui := new(cli.MockUi)
|
||||
cmd := &DeploymentUnblockCommand{Meta: Meta{Ui: ui}}
|
||||
|
||||
// Unblocks on misuse
|
||||
if code := cmd.Run([]string{"some", "bad", "args"}); code != 1 {
|
||||
t.Fatalf("expected exit code 1, got: %d", code)
|
||||
}
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) {
|
||||
t.Fatalf("expected help output, got: %s", out)
|
||||
}
|
||||
ui.ErrorWriter.Reset()
|
||||
|
||||
if code := cmd.Run([]string{"-address=nope", "12"}); code != 1 {
|
||||
t.Fatalf("expected exit code 1, got: %d", code)
|
||||
}
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, "Error retrieving deployment") {
|
||||
t.Fatalf("expected unblocked query error, got: %s", out)
|
||||
}
|
||||
ui.ErrorWriter.Reset()
|
||||
}
|
||||
|
||||
func TestDeploymentUnblockCommand_AutocompleteArgs(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
t.Parallel()
|
||||
|
||||
srv, _, url := testServer(t, true, nil)
|
||||
defer srv.Shutdown()
|
||||
|
||||
ui := new(cli.MockUi)
|
||||
cmd := &DeploymentUnblockCommand{Meta: Meta{Ui: ui, flagAddress: url}}
|
||||
|
||||
// Create a fake deployment
|
||||
state := srv.Agent.Server().State()
|
||||
d := mock.Deployment()
|
||||
assert.Nil(state.UpsertDeployment(1000, d))
|
||||
|
||||
prefix := d.ID[:5]
|
||||
args := complete.Args{Last: prefix}
|
||||
predictor := cmd.AutocompleteArgs()
|
||||
|
||||
res := predictor.Predict(args)
|
||||
assert.Equal(1, len(res))
|
||||
assert.Equal(d.ID, res[0])
|
||||
}
|
||||
Reference in New Issue
Block a user