github.com/blixtra/nomad@v0.7.2-0.20171221000451-da9a1d7bb050/command/server_members.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "net" 6 "sort" 7 "strings" 8 9 "github.com/hashicorp/nomad/api" 10 "github.com/posener/complete" 11 "github.com/ryanuber/columnize" 12 ) 13 14 type ServerMembersCommand struct { 15 Meta 16 } 17 18 func (c *ServerMembersCommand) Help() string { 19 helpText := ` 20 Usage: nomad server-members [options] 21 22 Display a list of the known servers and their status. Only Nomad servers are 23 able to service this command. 24 25 General Options: 26 27 ` + generalOptionsUsage() + ` 28 29 Server Members Options: 30 31 -detailed 32 Show detailed information about each member. This dumps 33 a raw set of tags which shows more information than the 34 default output format. 35 ` 36 return strings.TrimSpace(helpText) 37 } 38 39 func (c *ServerMembersCommand) AutocompleteFlags() complete.Flags { 40 return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient), 41 complete.Flags{ 42 "-detailed": complete.PredictNothing, 43 }) 44 } 45 46 func (c *ServerMembersCommand) AutocompleteArgs() complete.Predictor { 47 return complete.PredictNothing 48 } 49 50 func (c *ServerMembersCommand) Synopsis() string { 51 return "Display a list of known servers and their status" 52 } 53 54 func (c *ServerMembersCommand) Run(args []string) int { 55 var detailed bool 56 57 flags := c.Meta.FlagSet("server-members", FlagSetClient) 58 flags.Usage = func() { c.Ui.Output(c.Help()) } 59 flags.BoolVar(&detailed, "detailed", false, "Show detailed output") 60 61 if err := flags.Parse(args); err != nil { 62 return 1 63 } 64 65 // Check for extra arguments 66 args = flags.Args() 67 if len(args) != 0 { 68 c.Ui.Error(c.Help()) 69 return 1 70 } 71 72 // Get the HTTP client 73 client, err := c.Meta.Client() 74 if err != nil { 75 c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) 76 return 1 77 } 78 79 // Query the members 80 srvMembers, err := client.Agent().Members() 81 if err != nil { 82 c.Ui.Error(fmt.Sprintf("Error querying servers: %s", err)) 83 return 1 84 } 85 86 if srvMembers == nil { 87 c.Ui.Error("Agent doesn't know about server members") 88 return 0 89 } 90 91 // Sort the members 92 sort.Sort(api.AgentMembersNameSort(srvMembers.Members)) 93 94 // Determine the leaders per region. 95 leaders, err := regionLeaders(client, srvMembers.Members) 96 if err != nil { 97 c.Ui.Error(fmt.Sprintf("Error determining leaders: %s", err)) 98 return 1 99 } 100 101 // Format the list 102 var out []string 103 if detailed { 104 out = detailedOutput(srvMembers.Members) 105 } else { 106 out = standardOutput(srvMembers.Members, leaders) 107 } 108 109 // Dump the list 110 c.Ui.Output(columnize.SimpleFormat(out)) 111 return 0 112 } 113 114 func standardOutput(mem []*api.AgentMember, leaders map[string]string) []string { 115 // Format the members list 116 members := make([]string, len(mem)+1) 117 members[0] = "Name|Address|Port|Status|Leader|Protocol|Build|Datacenter|Region" 118 for i, member := range mem { 119 reg := member.Tags["region"] 120 regLeader, ok := leaders[reg] 121 isLeader := false 122 if ok { 123 if regLeader == net.JoinHostPort(member.Addr, member.Tags["port"]) { 124 125 isLeader = true 126 } 127 } 128 129 members[i+1] = fmt.Sprintf("%s|%s|%d|%s|%t|%d|%s|%s|%s", 130 member.Name, 131 member.Addr, 132 member.Port, 133 member.Status, 134 isLeader, 135 member.ProtocolCur, 136 member.Tags["build"], 137 member.Tags["dc"], 138 member.Tags["region"]) 139 } 140 return members 141 } 142 143 func detailedOutput(mem []*api.AgentMember) []string { 144 // Format the members list 145 members := make([]string, len(mem)+1) 146 members[0] = "Name|Address|Port|Tags" 147 for i, member := range mem { 148 // Format the tags 149 tagPairs := make([]string, 0, len(member.Tags)) 150 for k, v := range member.Tags { 151 tagPairs = append(tagPairs, fmt.Sprintf("%s=%s", k, v)) 152 } 153 tags := strings.Join(tagPairs, ",") 154 155 members[i+1] = fmt.Sprintf("%s|%s|%d|%s", 156 member.Name, 157 member.Addr, 158 member.Port, 159 tags) 160 } 161 return members 162 } 163 164 // regionLeaders returns a map of regions to the IP of the member that is the 165 // leader. 166 func regionLeaders(client *api.Client, mem []*api.AgentMember) (map[string]string, error) { 167 // Determine the unique regions. 168 leaders := make(map[string]string) 169 regions := make(map[string]struct{}) 170 for _, m := range mem { 171 regions[m.Tags["region"]] = struct{}{} 172 } 173 174 if len(regions) == 0 { 175 return leaders, nil 176 } 177 178 status := client.Status() 179 for reg := range regions { 180 l, err := status.RegionLeader(reg) 181 if err != nil { 182 // This error means that region has no leader. 183 if strings.Contains(err.Error(), "No cluster leader") { 184 continue 185 } 186 return nil, err 187 } 188 189 leaders[reg] = l 190 } 191 192 return leaders, nil 193 }