diff --git a/command/operator_autopilot_set.go b/command/operator_autopilot_set.go index 6808add954..a0a47c2d3a 100644 --- a/command/operator_autopilot_set.go +++ b/command/operator_autopilot_set.go @@ -69,10 +69,10 @@ func (c *OperatorAutopilotSetCommand) Run(args []string) int { return 1 } if result { - c.Ui.Output("Configuration updated") + c.Ui.Output("Configuration updated!") + return 0 } else { c.Ui.Output("Configuration could not be atomically updated") + return 1 } - - return 0 } diff --git a/consul/operator_endpoint_test.go b/consul/operator_endpoint_test.go index 27cc54ea5e..e7cfdb772c 100644 --- a/consul/operator_endpoint_test.go +++ b/consul/operator_endpoint_test.go @@ -255,7 +255,6 @@ func TestOperator_Autopilot_GetConfiguration(t *testing.T) { testutil.WaitForLeader(t, s1.RPC, "dc1") - // Change the autopilot config from the default arg := structs.DCSpecificRequest{ Datacenter: "dc1", } @@ -283,7 +282,7 @@ func TestOperator_Autopilot_GetConfiguration_ACLDeny(t *testing.T) { testutil.WaitForLeader(t, s1.RPC, "dc1") - // Change the autopilot config from the default + // Try to get config without permissions arg := structs.DCSpecificRequest{ Datacenter: "dc1", } @@ -315,8 +314,7 @@ func TestOperator_Autopilot_GetConfiguration_ACLDeny(t *testing.T) { } } - // Now it should kick back for being an invalid config, which means it - // tried to do the operation. + // Now we can read and verify the config arg.Token = token err = msgpackrpc.CallWithCodec(codec, "Operator.AutopilotGetConfiguration", &arg, &reply) if err != nil { @@ -345,7 +343,7 @@ func TestOperator_Autopilot_SetConfiguration(t *testing.T) { DeadServerCleanup: true, }, } - var reply struct{} + var reply *bool err := msgpackrpc.CallWithCodec(codec, "Operator.AutopilotSetConfiguration", &arg, &reply) if err != nil { t.Fatalf("err: %v", err) @@ -353,7 +351,7 @@ func TestOperator_Autopilot_SetConfiguration(t *testing.T) { // Make sure it's changed state := s1.fsm.State() - config, err := state.AutopilotConfig() + _, config, err := state.AutopilotConfig() if err != nil { t.Fatal(err) } @@ -376,14 +374,14 @@ func TestOperator_Autopilot_SetConfiguration_ACLDeny(t *testing.T) { testutil.WaitForLeader(t, s1.RPC, "dc1") - // Change the autopilot config from the default + // Try to set config without permissions arg := structs.AutopilotSetConfigRequest{ Datacenter: "dc1", Config: structs.AutopilotConfig{ DeadServerCleanup: true, }, } - var reply struct{} + var reply *bool err := msgpackrpc.CallWithCodec(codec, "Operator.AutopilotSetConfiguration", &arg, &reply) if err == nil || !strings.Contains(err.Error(), permissionDenied) { t.Fatalf("err: %v", err) @@ -411,8 +409,7 @@ func TestOperator_Autopilot_SetConfiguration_ACLDeny(t *testing.T) { } } - // Now it should kick back for being an invalid config, which means it - // tried to do the operation. + // Now we can update the config arg.Token = token err = msgpackrpc.CallWithCodec(codec, "Operator.AutopilotSetConfiguration", &arg, &reply) if err != nil { @@ -421,7 +418,7 @@ func TestOperator_Autopilot_SetConfiguration_ACLDeny(t *testing.T) { // Make sure it's changed state := s1.fsm.State() - config, err := state.AutopilotConfig() + _, config, err := state.AutopilotConfig() if err != nil { t.Fatal(err) } diff --git a/website/source/docs/agent/http/operator.html.markdown b/website/source/docs/agent/http/operator.html.markdown index a75b1d08d4..12323971bc 100644 --- a/website/source/docs/agent/http/operator.html.markdown +++ b/website/source/docs/agent/http/operator.html.markdown @@ -287,7 +287,9 @@ A JSON body is returned that looks like this: ```javascript { - "DeadServerCleanup": true + "DeadServerCleanup": true, + "CreateIndex": 4, + "ModifyIndex": 4 } ``` @@ -299,6 +301,10 @@ a new server is added to the cluster. Using the `PUT` method, this endpoint will update the Autopilot configuration of the cluster. +The `?cas=` can optionally be specified to update the configuration as a +Check-And-Set operation. The update will only happen if the given index matches +the `ModifyIndex` of the configuration at the time of writing. + If ACLs are enabled, the client will need to supply an ACL Token with [`operator`](/docs/internals/acl.html#operator) write privileges. diff --git a/website/source/docs/commands/operator.html.markdown.erb b/website/source/docs/commands/operator.html.markdown.erb index 6961f57627..69bd2a826f 100644 --- a/website/source/docs/commands/operator.html.markdown.erb +++ b/website/source/docs/commands/operator.html.markdown.erb @@ -35,10 +35,12 @@ Usage: consul operator [options] Subcommands: - raft Provides cluster-level tools for Consul operators + autopilot Provides tools for modifying Autopilot configuration + raft Provides cluster-level tools for Consul operators ``` For more information, examples, and usage about a subcommand, click on the name of the subcommand in the sidebar or one of the links below: +- [autopilot] (/docs/commands/operator/autopilot.html) - [raft] (/docs/commands/operator/raft.html) diff --git a/website/source/docs/commands/operator/autopilot.html.markdown.erb b/website/source/docs/commands/operator/autopilot.html.markdown.erb new file mode 100644 index 0000000000..a028914add --- /dev/null +++ b/website/source/docs/commands/operator/autopilot.html.markdown.erb @@ -0,0 +1,67 @@ +--- +layout: "docs" +page_title: "Commands: Operator Autopilot" +sidebar_current: "docs-commands-operator-autopilot" +description: > + The operator autopilot subcommand is used to view and modify Consul's Autopilot configuration. +--- + +# Consul Operator Autopilot + +Command: `consul operator autopilot` + +The Autopilot operator command is used to interact with Consul's Autopilot subsystem. The +command can be used to view or modify the current Autopilot configuration. + +```text +Usage: consul operator autopilot [options] + +The Autopilot operator command is used to interact with Consul's Autopilot +subsystem. The command can be used to view or modify the current configuration. + +Subcommands: + + get-config Display the current Autopilot configuration + set-config Modify the current Autopilot configuration +``` + +## get-config + +This command displays the current Raft peer configuration. + +Usage: `consul operator autopilot get-config [options]` + +#### API Options + +<%= partial "docs/commands/http_api_options_client" %> +<%= partial "docs/commands/http_api_options_server" %> + +The output looks like this: + +``` +DeadServerCleanup = true +``` + +## set-config + +Modifies the current Autopilot configuration. + +Usage: `consul operator autopilot set-config [options]` + +#### API Options + +<%= partial "docs/commands/http_api_options_client" %> +<%= partial "docs/commands/http_api_options_server" %> + +#### Command Options + +* `-dead-server-cleanup` - Specifies whether to enable automatic removal of dead servers +upon the successful joining of new servers to the cluster. Must be one of `[true|false]`. + +The output looks like this: + +``` +Configuration updated! +``` + +The return code will indicate success or failure. diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index f972c06607..c1e2f28876 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -142,6 +142,9 @@ > operator