func GetRuntimeClient(clicontext *cli.Context, hostname string) runtime.Client {
	return runtime.NewContainerdClient(
		context.Background(),
		clicontext.GlobalDuration("timeout"),
		clicontext.GlobalString("containerd"),
		clicontext.String("containerd-snapshotter"),
		hostname,
	)
}
GetRuntimeClient initialises new runtime client from CLI parameters
package cmd
import (
	"context"
	"encoding/csv"
	"fmt"
	"os"
	"os/signal"
	"os/user"
	"path/filepath"
	"strings"
	"syscall"
	"time"
	"github.com/ernoaapa/eliot/pkg/cmd"
	ui "github.com/ernoaapa/eliot/pkg/cmd/ui"
	"github.com/ernoaapa/eliot/pkg/discovery"
	"github.com/ernoaapa/eliot/pkg/printers"
	"github.com/ernoaapa/eliot/pkg/sync"
	"github.com/ernoaapa/eliot/pkg/utils"
	"github.com/sirupsen/logrus"
	"github.com/ernoaapa/eliot/pkg/api"
	containers "github.com/ernoaapa/eliot/pkg/api/services/containers/v1"
	pods "github.com/ernoaapa/eliot/pkg/api/services/pods/v1"
	"github.com/ernoaapa/eliot/pkg/config"
	"github.com/ernoaapa/eliot/pkg/fs"
	"github.com/ernoaapa/eliot/pkg/runtime"
	"github.com/urfave/cli"
)
const (
	outputHuman = "human"
	outputYaml  = "yaml"
)
var (
	GlobalFlags = []cli.Flag{
		cli.BoolFlag{
			Name:  "debug",
			Usage: "enable debug output in logs",
		},
		cli.BoolFlag{
			Name:  "quiet",
			Usage: "Don't print any progress output",
		},
		cli.StringFlag{
			Name:  "output, o",
			Usage: fmt.Sprintf("Output format. One of: %s", []string{outputHuman, outputYaml}),
			Value: "human",
		},
	}
)
func GlobalBefore(context *cli.Context) error {
	debug := context.GlobalBool("debug")
	if debug {
		logrus.SetLevel(logrus.DebugLevel)
	}
	if cmd.IsPipingOut() || context.GlobalBool("quiet") || context.GlobalString("output") != outputHuman {
		ui.SetOutput(ui.NewHidden())
	} else if debug {
		ui.SetOutput(ui.NewDebug())
	} else {
		ui.SetOutput(ui.NewTerminal())
	}
	return nil
}
func GetClient(config *config.Provider) *api.Client {
	uiline := ui.NewLine()
	endpoints := config.GetEndpoints()
	switch len(endpoints) {
	case 0:
		uiline.Fatal("No node to connect. You must give node endpoint. E.g. --endpoint=192.168.1.2")
		return nil
	case 1:
		uiline.Loadingf("Connecting to %s (%s)", endpoints[0].Name, endpoints[0].URL)
		client := api.NewClient(config.GetNamespace(), endpoints[0])
		info, err := client.GetInfo()
		if err != nil {
			logrus.Debugf("Connection failure: %s", err)
			uiline.Fatalf("Failed connect to %s (%s)", endpoints[0].Name, endpoints[0].URL)
		}
		uiline.Donef("Connected to %s (%s)", info.Hostname, endpoints[0].URL)
		return client
	default:
		uiline.Fatalf("%d node found. You must give target node. E.g. --endpoint=192.168.1.2", len(endpoints))
		return nil
	}
}
func GetConfig(clicontext *cli.Context) *config.Config {
	configPath := clicontext.GlobalString("config")
	conf, err := config.GetConfig(expandTilde(configPath))
	if err != nil {
		ui.NewLine().Fatalf("Error while reading configuration file [%s]: %s", configPath, err)
	}
	return conf
}
func GetConfigProvider(clicontext *cli.Context) *config.Provider {
	provider := config.NewProvider(GetConfig(clicontext))
	if clicontext.GlobalIsSet("namespace") && clicontext.GlobalString("namespace") != "" {
		provider.OverrideNamespace(clicontext.GlobalString("namespace"))
	}
	if clicontext.GlobalIsSet("endpoint") && clicontext.GlobalString("endpoint") != "" {
		provider.OverrideEndpoints([]config.Endpoint{{
			Name: clicontext.GlobalString("endpoint"),
			URL:  clicontext.GlobalString("endpoint"),
		}})
	}
	if len(provider.GetEndpoints()) == 0 {
		uiline := ui.NewLine().Loading("Discover from network automatically...")
		node, err := discovery.Nodes(2 * time.Second)
		if err != nil {
			uiline.Errorf("Failed to auto-discover node in network: %s", err)
		} else {
			if len(node) == 0 {
				uiline.Warn("No node discovered from network")
			} else {
				uiline.Donef("Discovered %d node(s) from network", len(node))
			}
		}
		endpoints := []config.Endpoint{}
		for _, node := range node {
			if len(node.Addresses) > 0 {
				endpoints = append(endpoints, config.Endpoint{
					Name: node.Hostname,
					URL:  fmt.Sprintf("%s:%d", utils.GetFirst(node.Addresses, ""), node.GrpcPort),
				})
			}
		}
		provider.OverrideEndpoints(endpoints)
	}
	if clicontext.GlobalIsSet("node") && clicontext.GlobalString("node") != "" {
		nodeName := clicontext.GlobalString("node")
		endpoint, found := provider.GetEndpointByName(nodeName)
		if !found {
			ui.NewLine().Errorf("Failed to find node with name %s", nodeName)
		}
		provider.OverrideEndpoints([]config.Endpoint{endpoint})
	}
	return provider
}
func UpdateConfig(clicontext *cli.Context, updated *config.Config) error {
	configPath := expandTilde(clicontext.GlobalString("config"))
	return config.WriteConfig(configPath, updated)
}
func GetLabels(clicontext *cli.Context) map[string]string {
	if !clicontext.IsSet("labels") {
		return map[string]string{}
	}
	param := clicontext.String("labels")
	values := strings.Split(param, ",")
	labels := map[string]string{}
	for _, value := range values {
		pair := strings.Split(value, "=")
		if len(pair) == 2 {
			labels[pair[0]] = pair[1]
		} else {
			ui.NewLine().Fatalf("Invalid --labels parameter [%s]. It must be comma separated key=value list. E.g. '--labels foo=bar,one=two'", param)
		}
	}
	return labels
}