1 Star 0 Fork 0

powerpaas/machine

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
ls.go 12.30 KB
一键复制 编辑 原始数据 按行查看 历史
Luther Monson 提交于 2019-11-15 16:06 . rancher-machine rename
package commands
import (
"errors"
"fmt"
"os"
"regexp"
"sort"
"strings"
"text/tabwriter"
"text/template"
"time"
"io"
"github.com/rancher/machine/libmachine"
"github.com/rancher/machine/libmachine/drivers"
"github.com/rancher/machine/libmachine/engine"
"github.com/rancher/machine/libmachine/host"
"github.com/rancher/machine/libmachine/log"
"github.com/rancher/machine/libmachine/mcndockerclient"
"github.com/rancher/machine/libmachine/persist"
"github.com/rancher/machine/libmachine/state"
"github.com/rancher/machine/libmachine/swarm"
"github.com/skarademir/naturalsort"
)
const (
lsDefaultTimeout = 10
tableFormatKey = "table"
lsDefaultFormat = "table {{ .Name }}\t{{ .Active }}\t{{ .DriverName}}\t{{ .State }}\t{{ .URL }}\t{{ .Swarm }}\t{{ .DockerVersion }}\t{{ .Error}}"
)
var (
headers = map[string]string{
"Name": "NAME",
"Active": "ACTIVE",
"ActiveHost": "ACTIVE_HOST",
"ActiveSwarm": "ACTIVE_SWARM",
"DriverName": "DRIVER",
"State": "STATE",
"URL": "URL",
"SwarmOptions": "SWARM_OPTIONS",
"Swarm": "SWARM",
"EngineOptions": "ENGINE_OPTIONS",
"Error": "ERRORS",
"DockerVersion": "DOCKER",
"ResponseTime": "RESPONSE",
}
)
type HostListItem struct {
Name string
Active string
ActiveHost bool
ActiveSwarm bool
DriverName string
State state.State
URL string
SwarmOptions *swarm.Options
Swarm string
EngineOptions *engine.Options
Error string
DockerVersion string
ResponseTime time.Duration
}
// FilterOptions -
type FilterOptions struct {
SwarmName []string
DriverName []string
State []string
Name []string
Labels []string
}
func cmdLs(c CommandLine, api libmachine.API) error {
filters, err := parseFilters(c.StringSlice("filter"))
if err != nil {
return err
}
hostList, hostInError, err := persist.LoadAllHosts(api)
if err != nil {
return err
}
hostList = filterHosts(hostList, filters)
// Just print out the names if we're being quiet
if c.Bool("quiet") {
for _, host := range hostList {
fmt.Println(host.Name)
}
return nil
}
template, table, err := parseFormat(c.String("format"))
if err != nil {
return err
}
var w io.Writer
if table {
tabWriter := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
defer tabWriter.Flush()
w = tabWriter
if err := template.Execute(w, headers); err != nil {
return err
}
} else {
w = os.Stdout
}
timeout := time.Duration(c.Int("timeout")) * time.Second
items := getHostListItems(hostList, hostInError, timeout)
swarmMasters := make(map[string]string)
swarmInfo := make(map[string]string)
for _, host := range hostList {
if host.HostOptions != nil {
swarmOptions := host.HostOptions.SwarmOptions
if swarmOptions.Master {
swarmMasters[swarmOptions.Discovery] = host.Name
}
if swarmOptions.Discovery != "" {
swarmInfo[host.Name] = swarmOptions.Discovery
}
}
}
for _, item := range items {
swarmColumn := ""
if item.SwarmOptions != nil && item.SwarmOptions.Discovery != "" {
swarmColumn = swarmMasters[item.SwarmOptions.Discovery]
if item.SwarmOptions.Master {
swarmColumn = fmt.Sprintf("%s (master)", swarmColumn)
}
}
item.Swarm = swarmColumn
if err := template.Execute(w, item); err != nil {
return err
}
}
return nil
}
func parseFormat(format string) (*template.Template, bool, error) {
table := false
finalFormat := format
if finalFormat == "" {
finalFormat = lsDefaultFormat
}
if strings.HasPrefix(finalFormat, tableFormatKey) {
table = true
finalFormat = finalFormat[len(tableFormatKey):]
}
finalFormat = strings.Trim(finalFormat, " ")
r := strings.NewReplacer(`\t`, "\t", `\n`, "\n")
finalFormat = r.Replace(finalFormat)
template, err := template.New("").Parse(finalFormat + "\n")
if err != nil {
return nil, false, err
}
return template, table, nil
}
func parseFilters(filters []string) (FilterOptions, error) {
options := FilterOptions{}
for _, f := range filters {
kv := strings.SplitN(f, "=", 2)
if len(kv) != 2 {
return options, errors.New("Unsupported filter syntax")
}
key, value := strings.ToLower(kv[0]), kv[1]
switch key {
case "swarm":
options.SwarmName = append(options.SwarmName, value)
case "driver":
options.DriverName = append(options.DriverName, value)
case "state":
options.State = append(options.State, value)
case "name":
options.Name = append(options.Name, value)
case "label":
options.Labels = append(options.Labels, value)
default:
return options, fmt.Errorf("Unsupported filter key '%s'", key)
}
}
return options, nil
}
func filterHosts(hosts []*host.Host, filters FilterOptions) []*host.Host {
if len(filters.SwarmName) == 0 &&
len(filters.DriverName) == 0 &&
len(filters.State) == 0 &&
len(filters.Name) == 0 &&
len(filters.Labels) == 0 {
return hosts
}
filteredHosts := []*host.Host{}
swarmMasters := getSwarmMasters(hosts)
for _, h := range hosts {
if filterHost(h, filters, swarmMasters) {
filteredHosts = append(filteredHosts, h)
}
}
return filteredHosts
}
func getSwarmMasters(hosts []*host.Host) map[string]string {
swarmMasters := make(map[string]string)
for _, h := range hosts {
if h.HostOptions != nil {
swarmOptions := h.HostOptions.SwarmOptions
if swarmOptions != nil && swarmOptions.Master {
swarmMasters[swarmOptions.Discovery] = h.Name
}
}
}
return swarmMasters
}
func filterHost(host *host.Host, filters FilterOptions, swarmMasters map[string]string) bool {
swarmMatches := matchesSwarmName(host, filters.SwarmName, swarmMasters)
driverMatches := matchesDriverName(host, filters.DriverName)
stateMatches := matchesState(host, filters.State)
nameMatches := matchesName(host, filters.Name)
labelMatches := matchesLabel(host, filters.Labels)
return swarmMatches && driverMatches && stateMatches && nameMatches && labelMatches
}
func matchesSwarmName(host *host.Host, swarmNames []string, swarmMasters map[string]string) bool {
if len(swarmNames) == 0 {
return true
}
for _, n := range swarmNames {
if host.HostOptions != nil && host.HostOptions.SwarmOptions != nil {
if strings.EqualFold(n, swarmMasters[host.HostOptions.SwarmOptions.Discovery]) {
return true
}
}
}
return false
}
func matchesDriverName(host *host.Host, driverNames []string) bool {
if len(driverNames) == 0 {
return true
}
for _, n := range driverNames {
if strings.EqualFold(host.DriverName, n) {
return true
}
}
return false
}
func matchesState(host *host.Host, states []string) bool {
if len(states) == 0 {
return true
}
for _, n := range states {
s, err := host.Driver.GetState()
if err != nil {
log.Warn(err)
}
if strings.EqualFold(n, s.String()) {
return true
}
}
return false
}
func matchesName(host *host.Host, names []string) bool {
if len(names) == 0 {
return true
}
for _, n := range names {
r, err := regexp.Compile(n)
if err != nil {
log.Error(err)
os.Exit(1) // TODO: Can we get rid of this call, and exit 'properly' ?
}
if r.MatchString(host.Driver.GetMachineName()) {
return true
}
}
return false
}
func matchesLabel(host *host.Host, labels []string) bool {
if len(labels) == 0 {
return true
}
var englabels = make(map[string]string, len(host.HostOptions.EngineOptions.Labels))
if host.HostOptions != nil && host.HostOptions.EngineOptions.Labels != nil {
for _, s := range host.HostOptions.EngineOptions.Labels {
kv := strings.SplitN(s, "=", 2)
englabels[kv[0]] = kv[1]
}
}
for _, l := range labels {
kv := strings.SplitN(l, "=", 2)
if val, exists := englabels[kv[0]]; exists && strings.EqualFold(val, kv[1]) {
return true
}
}
return false
}
// PERFORMANCE: The code of this function is complicated because we try
// to call the underlying drivers as less as possible to get the information
// we need.
func attemptGetHostState(h *host.Host, stateQueryChan chan<- HostListItem) {
requestBeginning := time.Now()
url := ""
currentState := state.None
dockerVersion := "Unknown"
hostError := ""
url, err := h.URL()
// PERFORMANCE: if we have the url, it's ok to assume the host is running
// This reduces the number of calls to the drivers
if err == nil {
if url != "" {
currentState = state.Running
} else {
currentState, err = h.Driver.GetState()
}
} else {
currentState, _ = h.Driver.GetState()
}
if err == nil && url != "" {
// PERFORMANCE: Reuse the url instead of asking the host again.
// This reduces the number of calls to the drivers
dockerHost := &mcndockerclient.RemoteDocker{
HostURL: url,
AuthOption: h.AuthOptions(),
}
dockerVersion, err = mcndockerclient.DockerVersion(dockerHost)
if err != nil {
dockerVersion = "Unknown"
} else {
dockerVersion = fmt.Sprintf("v%s", dockerVersion)
}
}
if err != nil {
hostError = err.Error()
}
if hostError == drivers.ErrHostIsNotRunning.Error() {
hostError = ""
}
var swarmOptions *swarm.Options
var engineOptions *engine.Options
if h.HostOptions != nil {
swarmOptions = h.HostOptions.SwarmOptions
engineOptions = h.HostOptions.EngineOptions
}
isMaster := false
swarmHost := ""
if swarmOptions != nil {
isMaster = swarmOptions.Master
swarmHost = swarmOptions.Host
}
activeHost := isActive(currentState, url)
activeSwarm := isSwarmActive(currentState, url, isMaster, swarmHost)
active := "-"
if activeHost {
active = "*"
}
if activeSwarm {
active = "* (swarm)"
}
stateQueryChan <- HostListItem{
Name: h.Name,
Active: active,
ActiveHost: activeHost,
ActiveSwarm: activeSwarm,
DriverName: h.Driver.DriverName(),
State: currentState,
URL: url,
SwarmOptions: swarmOptions,
EngineOptions: engineOptions,
DockerVersion: dockerVersion,
Error: hostError,
ResponseTime: time.Now().Round(time.Millisecond).Sub(requestBeginning.Round(time.Millisecond)),
}
}
func getHostState(h *host.Host, hostListItemsChan chan<- HostListItem, timeout time.Duration) {
// This channel is used to communicate the properties we are querying
// about the host in the case of a successful read.
stateQueryChan := make(chan HostListItem)
go attemptGetHostState(h, stateQueryChan)
select {
// If we get back useful information, great. Forward it straight to
// the original parent channel.
case hli := <-stateQueryChan:
hostListItemsChan <- hli
// Otherwise, give up after a predetermined duration.
case <-time.After(timeout):
hostListItemsChan <- HostListItem{
Name: h.Name,
DriverName: h.Driver.DriverName(),
State: state.Timeout,
ResponseTime: timeout,
}
}
}
func getHostListItems(hostList []*host.Host, hostsInError map[string]error, timeout time.Duration) []HostListItem {
log.Debugf("timeout set to %s", timeout)
hostListItems := []HostListItem{}
hostListItemsChan := make(chan HostListItem)
for _, h := range hostList {
go getHostState(h, hostListItemsChan, timeout)
}
for range hostList {
hostListItems = append(hostListItems, <-hostListItemsChan)
}
close(hostListItemsChan)
for name, err := range hostsInError {
hostListItems = append(hostListItems, newHostListItemInError(name, err))
}
sortHostListItemsByName(hostListItems)
return hostListItems
}
func newHostListItemInError(name string, err error) HostListItem {
return HostListItem{
Name: name,
DriverName: "not found",
State: state.Error,
Error: strings.Replace(err.Error(), "\n", "", -1),
}
}
func sortHostListItemsByName(items []HostListItem) {
m := make(map[string]HostListItem, len(items))
s := make([]string, len(items))
for i, v := range items {
name := strings.ToLower(v.Name)
m[name] = v
s[i] = name
}
sort.Sort(naturalsort.NaturalSort(s))
for i, v := range s {
items[i] = m[v]
}
}
func isActive(currentState state.State, hostURL string) bool {
return currentState == state.Running && hostURL == os.Getenv("DOCKER_HOST")
}
func isSwarmActive(currentState state.State, hostURL string, isMaster bool, swarmHost string) bool {
return isMaster && currentState == state.Running && toSwarmURL(hostURL, swarmHost) == os.Getenv("DOCKER_HOST")
}
func urlPort(urlWithPort string) string {
parts := strings.Split(urlWithPort, ":")
return parts[len(parts)-1]
}
func toSwarmURL(hostURL string, swarmHost string) string {
hostPort := urlPort(hostURL)
swarmPort := urlPort(swarmHost)
return strings.Replace(hostURL, ":"+hostPort, ":"+swarmPort, 1)
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/powerpaas/machine.git
git@gitee.com:powerpaas/machine.git
powerpaas
machine
machine
v0.15.0-rancher23

搜索帮助