Skip to content

Commit

Permalink
feat: add commands to manage/query etcd cluster
Browse files Browse the repository at this point in the history
Used already existing protobufs for that.

Commands:
`talosctl etcd members -n <node>`
`talosctl etcd leave -n <node>`
`talosctl etcd forfeit-leadership -n <node>`

Signed-off-by: Artem Chernyshev <artem.0xD2@gmail.com>
  • Loading branch information
Unix4ever authored and talos-bot committed Dec 22, 2020
1 parent e75bb27 commit a83e875
Show file tree
Hide file tree
Showing 7 changed files with 556 additions and 280 deletions.
4 changes: 3 additions & 1 deletion api/machine/machine.proto
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,9 @@ message EtcdForfeitLeadershipResponse {
repeated EtcdForfeitLeadership messages = 1;
}

message EtcdMemberListRequest {}
message EtcdMemberListRequest {
bool query_local = 1;
}
message EtcdMemberList {
common.Metadata metadata = 1;
repeated string members = 2;
Expand Down
105 changes: 105 additions & 0 deletions cmd/talosctl/cmd/talos/etcd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package talos

import (
"context"
"fmt"
"os"
"strings"
"text/tabwriter"

"github.com/spf13/cobra"

"github.com/talos-systems/talos/pkg/machinery/api/machine"
"github.com/talos-systems/talos/pkg/machinery/client"
)

// etcdCmd represents the etcd command.
var etcdCmd = &cobra.Command{
Use: "etcd",
Short: "Manage etcd",
Long: ``,
}

var etcdLeaveCmd = &cobra.Command{
Use: "leave",
Short: "Tell nodes to leave etcd cluster",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
return WithClient(func(ctx context.Context, c *client.Client) error {
_, err := c.MachineClient.EtcdLeaveCluster(ctx, &machine.EtcdLeaveClusterRequest{})

return err
})
},
}

var etcdForfeitLeadershipCmd = &cobra.Command{
Use: "forfeit-leadership",
Short: "Tell node to forfeit etcd cluster leadership",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
return WithClient(func(ctx context.Context, c *client.Client) error {
_, err := c.MachineClient.EtcdForfeitLeadership(ctx, &machine.EtcdForfeitLeadershipRequest{})

return err
})
},
}

var etcdMemberListCmd = &cobra.Command{
Use: "members",
Short: "Get the list of etcd cluster members",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
return WithClient(func(ctx context.Context, c *client.Client) error {
response, err := c.MachineClient.EtcdMemberList(ctx, &machine.EtcdMemberListRequest{
QueryLocal: true,
})
if err != nil {
return fmt.Errorf("error getting members: %w", err)
}

w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
node := ""
pattern := "%s\t"

for i, message := range response.Messages {
if message.Metadata != nil && message.Metadata.Hostname != "" {
node = message.Metadata.Hostname
}

if len(message.Members) == 0 {
continue
}

if i == 0 {
if node != "" {
fmt.Fprintln(w, "NODE\tMEMBERS")
pattern = "%s\t%s\n"
} else {
fmt.Fprintln(w, "MEMBERS")
}
}

args := []interface{}{strings.Join(message.Members, ",")}
if node != "" {
args = append([]interface{}{node}, args...)
}

fmt.Fprintf(w, pattern, args...)

}

return w.Flush()
})
},
}

func init() {
etcdCmd.AddCommand(etcdLeaveCmd, etcdForfeitLeadershipCmd, etcdMemberListCmd)
addCommand(etcdCmd)
}
Original file line number Diff line number Diff line change
Expand Up @@ -1610,7 +1610,14 @@ func (s *Server) Memory(ctx context.Context, in *empty.Empty) (reply *machine.Me

// EtcdMemberList implements the machine.MachineServer interface.
func (s *Server) EtcdMemberList(ctx context.Context, in *machine.EtcdMemberListRequest) (reply *machine.EtcdMemberListResponse, err error) {
client, err := etcd.NewClientFromControlPlaneIPs(ctx, s.Controller.Runtime().Config().Cluster().CA(), s.Controller.Runtime().Config().Cluster().Endpoint())
var client *etcd.Client

if in.QueryLocal {
client, err = etcd.NewClient([]string{"127.0.0.1:2379"})
} else {
client, err = etcd.NewClientFromControlPlaneIPs(ctx, s.Controller.Runtime().Config().Cluster().CA(), s.Controller.Runtime().Config().Cluster().Endpoint())
}

if err != nil {
return nil, err
}
Expand Down
38 changes: 38 additions & 0 deletions internal/integration/cli/etcd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// +build integration_cli

package cli

import (
"github.com/talos-systems/talos/internal/integration/base"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1/machine"
)

// EtcdSuite verifies etcd command.
type EtcdSuite struct {
base.CLISuite
}

// SuiteName ...
func (suite *EtcdSuite) SuiteName() string {
return "cli.EtcdSuite"
}

// TestMembers etcd members should have some output.
func (suite *EtcdSuite) TestMembers() {
suite.RunCLI([]string{"etcd", "members", "--nodes", suite.RandomDiscoveredNode(machine.TypeControlPlane)}) // default checks for stdout not empty
}

// TestForfeitLeadership etcd forfeit-leadership check.
func (suite *EtcdSuite) TestForfeitLeadership() {
suite.RunCLI([]string{"etcd", "forfeit-leadership", "--nodes", suite.RandomDiscoveredNode(machine.TypeControlPlane)},
base.StdoutEmpty(),
)
}

func init() {
allSuites = append(allSuites, new(EtcdSuite))
}

0 comments on commit a83e875

Please sign in to comment.