overall: make error handling and error logs a bit more streamlined

pull/732/head
iwilltry42 3 years ago committed by Thorsten Klein
parent b4158a1dc1
commit 9efe980789
  1. 2
      cmd/cluster/clusterCreate.go
  2. 8
      cmd/util/ports.go
  3. 2
      cmd/util/volumes.go
  4. 44
      pkg/client/cluster.go
  5. 3
      pkg/client/environment.go
  6. 6
      pkg/client/host.go
  7. 4
      pkg/client/ipam.go
  8. 50
      pkg/client/kubeconfig.go
  9. 11
      pkg/client/loadbalancer.go
  10. 48
      pkg/client/node.go
  11. 2
      pkg/client/ports.go
  12. 17
      pkg/client/registry.go
  13. 30
      pkg/client/tools.go
  14. 6
      pkg/config/config.go
  15. 2
      pkg/config/jsonschema.go
  16. 5
      pkg/config/merge.go
  17. 26
      pkg/config/transform.go
  18. 22
      pkg/config/validate.go
  19. 47
      pkg/runtimes/docker/container.go
  20. 2
      pkg/runtimes/docker/host.go
  21. 8
      pkg/runtimes/docker/image.go
  22. 7
      pkg/runtimes/docker/info.go
  23. 9
      pkg/runtimes/docker/kubeconfig.go
  24. 55
      pkg/runtimes/docker/network.go
  25. 91
      pkg/runtimes/docker/node.go
  26. 32
      pkg/runtimes/docker/util.go
  27. 25
      pkg/runtimes/docker/volume.go
  28. 8
      pkg/runtimes/util/volumes.go
  29. 8
      pkg/util/files.go
  30. 2
      pkg/util/filter.go
  31. 19
      pkg/util/infofaker.go
  32. 8
      pkg/util/ports.go
  33. 5
      version/version.go

@ -426,7 +426,7 @@ func applyCLIOverrides(cfg conf.SimpleConfig) (conf.SimpleConfig, error) {
}
exposeAPI, err = cliutil.ParsePortExposureSpec(ppViper.GetString("cli.api-port"), k3d.DefaultAPIPort)
if err != nil {
return cfg, err
return cfg, fmt.Errorf("failed to parse API Port spec: %w", err)
}
}

@ -93,7 +93,7 @@ func ParsePortExposureSpec(exposedPortSpec, internalPort string) (*k3d.ExposureO
portMapping, err := nat.ParsePortSpec(realPortString)
if err != nil {
return nil, fmt.Errorf("Failed to parse port spec for Port Exposure '%s': %+v", realPortString, err)
return nil, fmt.Errorf("failed to parse port spec for Port Exposure '%s': %+v", realPortString, err)
}
api.Port = portMapping[0].Port // there can be only one due to our regexp
@ -112,14 +112,12 @@ func ValidatePortMap(portmap string) (string, error) {
func GetFreePort() (int, error) {
tcpAddress, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil {
l.Log().Errorln("Failed to resolve address")
return 0, err
return 0, fmt.Errorf("failed to resolve address 'localhost:0': %w", err)
}
tcpListener, err := net.ListenTCP("tcp", tcpAddress)
if err != nil {
l.Log().Errorln("Failed to create TCP Listener")
return 0, err
return 0, fmt.Errorf("failed to create tcp listener: %w", err)
}
defer tcpListener.Close()

@ -98,7 +98,7 @@ func ValidateVolumeMount(runtime runtimes.Runtime, volumeMount string) (string,
func verifyNamedVolume(runtime runtimes.Runtime, volumeName string) error {
volumeName, err := runtime.GetVolume(volumeName)
if err != nil {
return err
return fmt.Errorf("Failed to verify named volume: %w", err)
}
if volumeName == "" {
return fmt.Errorf("Failed to find named volume '%s'", volumeName)

@ -72,11 +72,11 @@ func ClusterRun(ctx context.Context, runtime k3drt.Runtime, clusterConfig *confi
*/
_, err := EnsureToolsNode(ctx, runtime, &clusterConfig.Cluster)
if err != nil {
return err
return fmt.Errorf("failed to ensure k3d-tools node: %w", err)
}
envInfo, err := GatherEnvironmentInfo(ctx, runtime, &clusterConfig.Cluster)
if err != nil {
return err
return fmt.Errorf("failed to gather environment information used for cluster creation: %w", err)
}
/*
@ -155,7 +155,7 @@ func ClusterPrep(ctx context.Context, runtime k3drt.Runtime, clusterConfig *conf
}
regFromNode, err := RegistryFromNode(regNode)
if err != nil {
return err
return fmt.Errorf("failed to translate node to registry spec: %w", err)
}
*reg = *regFromNode
}
@ -273,8 +273,7 @@ func ClusterPrepNetwork(ctx context.Context, runtime k3drt.Runtime, cluster *k3d
// create cluster network or use an existing one
network, networkExists, err := runtime.CreateNetworkIfNotPresent(ctx, &cluster.Network)
if err != nil {
l.Log().Errorln("Failed to create cluster network")
return err
return fmt.Errorf("failed to create cluster network: %w", err)
}
cluster.Network = *network
clusterCreateOpts.GlobalLabels[k3d.LabelNetworkID] = network.ID
@ -296,8 +295,7 @@ func ClusterPrepImageVolume(ctx context.Context, runtime k3drt.Runtime, cluster
*/
imageVolumeName := fmt.Sprintf("%s-%s-images", k3d.DefaultObjectNamePrefix, cluster.Name)
if err := runtime.CreateVolume(ctx, imageVolumeName, map[string]string{k3d.LabelClusterName: cluster.Name}); err != nil {
l.Log().Errorf("Failed to create image volume '%s' for cluster '%s'", imageVolumeName, cluster.Name)
return err
return fmt.Errorf("failed to create image volume '%s' for cluster '%s': %w", imageVolumeName, cluster.Name, err)
}
clusterCreateOpts.GlobalLabels[k3d.LabelImageVolume] = imageVolumeName
@ -400,7 +398,7 @@ ClusterCreatOpts:
if cluster.Network.IPAM.Managed {
ip, err := GetIP(ctx, runtime, &cluster.Network)
if err != nil {
return err
return fmt.Errorf("failed to find free IP in network %s: %w", cluster.Network.Name, err)
}
cluster.Network.IPAM.IPsUsed = append(cluster.Network.IPAM.IPsUsed, ip) // make sure that we're not reusing the same IP next time
node.IP.Static = true
@ -427,8 +425,7 @@ ClusterCreatOpts:
// create node
l.Log().Infof("Creating node '%s'", node.Name)
if err := NodeCreate(clusterCreateCtx, runtime, node, k3d.NodeCreateOpts{}); err != nil {
l.Log().Errorln("Failed to create node")
return err
return fmt.Errorf("failed to create node: %w", err)
}
l.Log().Debugf("Created node '%s'", node.Name)
@ -458,7 +455,7 @@ ClusterCreatOpts:
}
if err := nodeSetup(cluster.InitNode); err != nil {
return err
return fmt.Errorf("failed init node setup: %w", err)
}
serverCount++
@ -486,7 +483,7 @@ ClusterCreatOpts:
}
if node.Role == k3d.ServerRole || node.Role == k3d.AgentRole {
if err := nodeSetup(node); err != nil {
return err
return fmt.Errorf("failed setup of server/agent node %s: %w", node.Name, err)
}
}
}
@ -504,7 +501,7 @@ ClusterCreatOpts:
if cluster.ServerLoadBalancer == nil {
lbNode, err := LoadbalancerPrepare(ctx, runtime, cluster, &k3d.LoadbalancerCreateOpts{Labels: clusterCreateOpts.GlobalLabels})
if err != nil {
return err
return fmt.Errorf("failed to prepare loadbalancer: %w", err)
}
cluster.Nodes = append(cluster.Nodes, lbNode) // append lbNode to list of cluster nodes, so it will be considered during rollback
}
@ -522,7 +519,7 @@ ClusterCreatOpts:
// prepare to write config to lb container
configyaml, err := yaml.Marshal(cluster.ServerLoadBalancer.Config)
if err != nil {
return err
return fmt.Errorf("failed to marshal loadbalancer config: %w", err)
}
writeLbConfigAction := k3d.NodeHook{
@ -542,7 +539,6 @@ ClusterCreatOpts:
return fmt.Errorf("error creating loadbalancer: %v", err)
}
l.Log().Debugf("Created loadbalancer '%s'", cluster.ServerLoadBalancer.Node.Name)
return err
}
return nil
@ -554,7 +550,7 @@ func ClusterDelete(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Clus
l.Log().Infof("Deleting cluster '%s'", cluster.Name)
cluster, err := ClusterGet(ctx, runtime, cluster)
if err != nil {
return err
return fmt.Errorf("failed to get cluster %s: %w", cluster.Name, err)
}
l.Log().Debugf("Cluster Details: %+v", cluster)
@ -656,8 +652,7 @@ func ClusterList(ctx context.Context, runtime k3drt.Runtime) ([]*k3d.Cluster, er
l.Log().Traceln("Listing Clusters...")
nodes, err := runtime.GetNodesByLabel(ctx, k3d.DefaultRuntimeLabels)
if err != nil {
l.Log().Errorln("Failed to get clusters")
return nil, err
return nil, fmt.Errorf("runtime failed to list nodes: %w", err)
}
l.Log().Debugf("Found %d nodes", len(nodes))
@ -804,8 +799,7 @@ func ClusterGet(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Cluster
}
if err := populateClusterFieldsFromLabels(cluster); err != nil {
l.Log().Warnf("Failed to populate cluster fields from node labels")
l.Log().Warnln(err)
l.Log().Warnf("Failed to populate cluster fields from node labels: %v", err)
}
return cluster, nil
@ -943,12 +937,12 @@ func ClusterStart(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Clust
// add /etc/hosts and CoreDNS entry for host.k3d.internal, referring to the host system
if err := prepInjectHostIP(ctx, runtime, cluster); err != nil {
return err
return fmt.Errorf("failed to inject host IP: %w", err)
}
// create host records in CoreDNS for external registries
if err := prepCoreDNSInjectNetworkMembers(ctx, runtime, cluster); err != nil {
return err
return fmt.Errorf("failed to patch CoreDNS with network members: %w", err)
}
return nil
@ -970,6 +964,8 @@ func ClusterStop(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Cluste
if failed > 0 {
return fmt.Errorf("Failed to stop %d nodes: Try to stop them manually", failed)
}
l.Log().Infof("Stopped cluster '%s'", cluster.Name)
return nil
}
@ -1107,7 +1103,7 @@ func ClusterEditChangesetSimple(ctx context.Context, runtime k3drt.Runtime, clus
for _, portWithNodeFilters := range changeset.Ports {
filteredNodes, err := util.FilterNodesWithSuffix(nodeList, portWithNodeFilters.NodeFilters)
if err != nil {
return err
return fmt.Errorf("failed to filter nodes: %w", err)
}
for suffix := range filteredNodes {
@ -1132,7 +1128,7 @@ func ClusterEditChangesetSimple(ctx context.Context, runtime k3drt.Runtime, clus
// prepare to write config to lb container
configyaml, err := yaml.Marshal(lbChangeset.Config)
if err != nil {
return err
return fmt.Errorf("failed to marshal loadbalancer config changeset: %w", err)
}
writeLbConfigAction := k3d.NodeHook{
Stage: k3d.LifecycleStagePreStart,

@ -23,6 +23,7 @@ package client
import (
"context"
"fmt"
"github.com/rancher/k3d/v4/pkg/runtimes"
@ -34,7 +35,7 @@ func GatherEnvironmentInfo(ctx context.Context, runtime runtimes.Runtime, cluste
hostIP, err := GetHostIP(ctx, runtime, cluster)
if err != nil {
return envInfo, err
return envInfo, fmt.Errorf("failed to get host IP: %w", err)
}
envInfo.HostGateway = hostIP

@ -50,7 +50,7 @@ func GetHostIP(ctx context.Context, rtime rt.Runtime, cluster *k3d.Cluster) (net
if runtime.GOOS == "linux" {
ip, err := rtime.GetHostIP(ctx, cluster.Network.Name)
if err != nil {
return nil, err
return nil, fmt.Errorf("runtime failed to get host IP: %w", err)
}
return ip, nil
}
@ -60,12 +60,12 @@ func GetHostIP(ctx context.Context, rtime rt.Runtime, cluster *k3d.Cluster) (net
toolsNode, err := EnsureToolsNode(ctx, rtime, cluster)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to ensure that k3d-tools node is running to get host IP :%w", err)
}
ip, err := resolveHostnameFromInside(ctx, rtime, toolsNode, "host.docker.internal")
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to resolve 'host.docker.internal' from inside the k3d-tools node: %w", err)
}
return ip, nil
}

@ -23,6 +23,7 @@ package client
import (
"context"
"fmt"
l "github.com/rancher/k3d/v4/pkg/logger"
k3drt "github.com/rancher/k3d/v4/pkg/runtimes"
@ -30,11 +31,12 @@ import (
"inet.af/netaddr"
)
// GetIP checks a given network for a free IP and returns it, if possible
func GetIP(ctx context.Context, runtime k3drt.Runtime, network *k3d.ClusterNetwork) (netaddr.IP, error) {
network, err := runtime.GetNetwork(ctx, network)
if err != nil {
return netaddr.IP{}, err
return netaddr.IP{}, fmt.Errorf("runtime failed to get network '%s': %w", network.Name, err)
}
var ipsetbuilder netaddr.IPSetBuilder

@ -53,14 +53,14 @@ func KubeconfigGetWrite(ctx context.Context, runtime runtimes.Runtime, cluster *
// get kubeconfig from cluster node
kubeconfig, err := KubeconfigGet(ctx, runtime, cluster)
if err != nil {
return output, err
return output, fmt.Errorf("failed to get kubeconfig for cluster '%s': %w", cluster.Name, err)
}
// empty output parameter = write to default
if output == "" {
output, err = KubeconfigGetDefaultPath()
if err != nil {
return output, err
return output, fmt.Errorf("failed to get default kubeconfig path: %w", err)
}
}
@ -82,15 +82,13 @@ func KubeconfigGetWrite(ctx context.Context, runtime runtimes.Runtime, cluster *
// create directory path
if err := os.MkdirAll(filepath.Dir(output), 0755); err != nil {
l.Log().Errorf("Failed to create output directory '%s'", filepath.Dir(output))
return output, err
return output, fmt.Errorf("failed to create output directory '%s': %w", filepath.Dir(output), err)
}
// try create output file
f, err := os.Create(output)
if err != nil {
l.Log().Errorf("Failed to create output file '%s'", output)
return output, err
return output, fmt.Errorf("failed to create output file '%s': %w", output, err)
}
f.Close()
@ -98,8 +96,7 @@ func KubeconfigGetWrite(ctx context.Context, runtime runtimes.Runtime, cluster *
firstRun = false
continue
}
l.Log().Errorf("Failed to open output file '%s' or it's not a KubeConfig", output)
return output, err
return output, fmt.Errorf("failed to open output file '%s' or it's not a kubeconfig: %w", output, err)
}
break
}
@ -117,11 +114,10 @@ func KubeconfigGet(ctx context.Context, runtime runtimes.Runtime, cluster *k3d.C
// TODO: getKubeconfig: we should make sure, that the server node we're trying to fetch from is actually running
serverNodes, err := runtime.GetNodesByLabel(ctx, map[string]string{k3d.LabelClusterName: cluster.Name, k3d.LabelRole: string(k3d.ServerRole)})
if err != nil {
l.Log().Errorln("Failed to get server nodes")
return nil, err
return nil, fmt.Errorf("runtime failed to get server nodes for cluster '%s': %w", cluster.Name, err)
}
if len(serverNodes) == 0 {
return nil, fmt.Errorf("Didn't find any server node")
return nil, fmt.Errorf("didn't find any server node for cluster '%s'", cluster.Name)
}
// prefer a server node, which actually has the port exposed
@ -147,15 +143,13 @@ func KubeconfigGet(ctx context.Context, runtime runtimes.Runtime, cluster *k3d.C
// get the kubeconfig from the first server node
reader, err := runtime.GetKubeconfig(ctx, chosenServer)
if err != nil {
l.Log().Errorf("Failed to get kubeconfig from node '%s'", chosenServer.Name)
return nil, err
return nil, fmt.Errorf("runtime failed to pull kubeconfig from node '%s': %w", chosenServer.Name, err)
}
defer reader.Close()
readBytes, err := ioutil.ReadAll(reader)
if err != nil {
l.Log().Errorln("Couldn't read kubeconfig file")
return nil, err
return nil, fmt.Errorf("failed to read kubeconfig file: %w", err)
}
// drop the first 512 bytes which contain file metadata/control characters
@ -167,8 +161,7 @@ func KubeconfigGet(ctx context.Context, runtime runtimes.Runtime, cluster *k3d.C
*/
kc, err := clientcmd.Load(trimBytes)
if err != nil {
l.Log().Errorln("Failed to parse the KubeConfig")
return nil, err
return nil, fmt.Errorf("failed to parse kubeconfig: %w", err)
}
// update the server URL
@ -212,22 +205,19 @@ func KubeconfigWriteToPath(ctx context.Context, kubeconfig *clientcmdapi.Config,
} else {
output, err = os.Create(path)
if err != nil {
l.Log().Errorf("Failed to create file '%s'", path)
return err
return fmt.Errorf("failed to create file '%s': %w", path, err)
}
defer output.Close()
}
kubeconfigBytes, err := clientcmd.Write(*kubeconfig)
if err != nil {
l.Log().Errorln("Failed to write KubeConfig")
return err
return fmt.Errorf("failed to write kubeconfig: %w", err)
}
_, err = output.Write(kubeconfigBytes)
if err != nil {
l.Log().Errorf("Failed to write to file '%s'", output.Name())
return err
return fmt.Errorf("failed to write file '%s': %w", output.Name(), err)
}
l.Log().Debugf("Wrote kubeconfig to '%s'", output.Name())
@ -285,14 +275,12 @@ func KubeconfigMerge(ctx context.Context, newKubeConfig *clientcmdapi.Config, ex
func KubeconfigWrite(ctx context.Context, kubeconfig *clientcmdapi.Config, path string) error {
tempPath := fmt.Sprintf("%s.k3d_%s", path, time.Now().Format("20060102_150405.000000"))
if err := clientcmd.WriteToFile(*kubeconfig, tempPath); err != nil {
l.Log().Errorf("Failed to write merged kubeconfig to temporary file '%s'", tempPath)
return err
return fmt.Errorf("failed to write merged kubeconfig to temporary file '%s': %w", tempPath, err)
}
// Move temporary file over existing KubeConfig
if err := os.Rename(tempPath, path); err != nil {
l.Log().Errorf("Failed to overwrite existing KubeConfig '%s' with new KubeConfig '%s'", path, tempPath)
return err
return fmt.Errorf("failed to overwrite existing KubeConfig '%s' with new kubeconfig '%s': %w", path, tempPath, err)
}
l.Log().Debugf("Wrote kubeconfig to '%s'", path)
@ -304,7 +292,7 @@ func KubeconfigWrite(ctx context.Context, kubeconfig *clientcmdapi.Config, path
func KubeconfigGetDefaultFile() (*clientcmdapi.Config, error) {
path, err := KubeconfigGetDefaultPath()
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get default kubeconfig path: %w", err)
}
l.Log().Debugf("Using default kubeconfig '%s'", path)
return clientcmd.LoadFromFile(path)
@ -314,7 +302,7 @@ func KubeconfigGetDefaultFile() (*clientcmdapi.Config, error) {
func KubeconfigGetDefaultPath() (string, error) {
defaultKubeConfigLoadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
if len(defaultKubeConfigLoadingRules.GetLoadingPrecedence()) > 1 {
return "", fmt.Errorf("Multiple kubeconfigs specified via KUBECONFIG env var: Please reduce to one entry, unset KUBECONFIG or explicitly choose an output")
return "", fmt.Errorf("multiple kubeconfigs specified via KUBECONFIG env var: Please reduce to one entry, unset KUBECONFIG or explicitly choose an output")
}
return defaultKubeConfigLoadingRules.GetDefaultFilename(), nil
}
@ -323,11 +311,11 @@ func KubeconfigGetDefaultPath() (string, error) {
func KubeconfigRemoveClusterFromDefaultConfig(ctx context.Context, cluster *k3d.Cluster) error {
defaultKubeConfigPath, err := KubeconfigGetDefaultPath()
if err != nil {
return err
return fmt.Errorf("failed to get default kubeconfig path: %w", err)
}
kubeconfig, err := KubeconfigGetDefaultFile()
if err != nil {
return err
return fmt.Errorf("failed to get default kubeconfig file: %w", err)
}
kubeconfig = KubeconfigRemoveCluster(ctx, cluster, kubeconfig)
return KubeconfigWrite(ctx, kubeconfig, defaultKubeConfigPath)

@ -51,8 +51,7 @@ func UpdateLoadbalancerConfig(ctx context.Context, runtime runtimes.Runtime, clu
// update cluster details to ensure that we have the latest node list
cluster, err = ClusterGet(ctx, runtime, cluster)
if err != nil {
l.Log().Errorf("Failed to update details for cluster '%s'", cluster.Name)
return err
return fmt.Errorf("failed to update details for cluster '%s': %w", cluster.Name, err)
}
currentConfig, err := GetLoadbalancerConfig(ctx, runtime, cluster)
@ -120,7 +119,7 @@ func GetLoadbalancerConfig(ctx context.Context, runtime runtimes.Runtime, cluste
var err error
cluster.ServerLoadBalancer.Node, err = NodeGet(ctx, runtime, node)
if err != nil {
return cfg, err
return cfg, fmt.Errorf("failed to get loadbalancer node '%s': %w", node.Name, err)
}
}
}
@ -128,13 +127,13 @@ func GetLoadbalancerConfig(ctx context.Context, runtime runtimes.Runtime, cluste
reader, err := runtime.ReadFromNode(ctx, types.DefaultLoadbalancerConfigPath, cluster.ServerLoadBalancer.Node)
if err != nil {
return cfg, err
return cfg, fmt.Errorf("runtime failed to read loadbalancer config '%s' from node '%s': %w", types.DefaultLoadbalancerConfigPath, cluster.ServerLoadBalancer.Node.Name, err)
}
defer reader.Close()
file, err := ioutil.ReadAll(reader)
if err != nil {
return cfg, err
return cfg, fmt.Errorf("failed to read loadbalancer config file: %w", err)
}
file = bytes.Trim(file[512:], "\x00") // trim control characters, etc.
@ -213,7 +212,7 @@ func loadbalancerAddPortConfigs(loadbalancer *k3d.Loadbalancer, portmapping nat.
nodenames := []string{}
for _, node := range targetNodes {
if node.Role == k3d.LoadBalancerRole {
return fmt.Errorf("error adding port config to loadbalancer: cannot add port config referencing the loadbalancer itself (loop)")
return fmt.Errorf("cannot add port config referencing the loadbalancer itself (loop)")
}
nodenames = append(nodenames, node.Name)
}

@ -56,8 +56,7 @@ func NodeAddToCluster(ctx context.Context, runtime runtimes.Runtime, node *k3d.N
targetClusterName := cluster.Name
cluster, err := ClusterGet(ctx, runtime, cluster)
if err != nil {
l.Log().Errorf("Failed to find specified cluster '%s'", targetClusterName)
return err
return fmt.Errorf("Failed to find specified cluster '%s': %w", targetClusterName, err)
}
// network
@ -159,8 +158,7 @@ func NodeAddToCluster(ctx context.Context, runtime runtimes.Runtime, node *k3d.N
// merge node config of new node into existing node config
if err := mergo.MergeWithOverwrite(srcNode, *node); err != nil {
l.Log().Errorln("Failed to merge new node config into existing node config")
return err
return fmt.Errorf("failed to merge new node config into existing node config: %w", err)
}
node = srcNode
@ -203,7 +201,7 @@ func NodeAddToCluster(ctx context.Context, runtime runtimes.Runtime, node *k3d.N
node.State.Status = ""
if err := NodeRun(ctx, runtime, node, createNodeOpts); err != nil {
return err
return fmt.Errorf("failed to run node '%s': %w", node.Name, err)
}
// if it's a server node, then update the loadbalancer configuration
@ -280,7 +278,7 @@ func NodeCreateMulti(ctx context.Context, runtime runtimes.Runtime, nodes []*k3d
// NodeRun creates and starts a node
func NodeRun(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, nodeCreateOpts k3d.NodeCreateOpts) error {
if err := NodeCreate(ctx, runtime, node, nodeCreateOpts); err != nil {
return err
return fmt.Errorf("failed to create node '%s': %w", node.Name, err)
}
if err := NodeStart(ctx, runtime, node, &k3d.NodeStartOpts{
@ -289,7 +287,7 @@ func NodeRun(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, node
NodeHooks: nodeCreateOpts.NodeHooks,
EnvironmentInfo: nodeCreateOpts.EnvironmentInfo,
}); err != nil {
return err
return fmt.Errorf("failed to start node '%s': %w", node.Name, err)
}
return nil
@ -305,7 +303,7 @@ func NodeStart(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, no
}
if err := enableFixes(ctx, runtime, node, nodeStartOpts); err != nil {
return err
return fmt.Errorf("failed to enable k3d fixes: %w", err)
}
startTime := time.Now()
@ -325,8 +323,7 @@ func NodeStart(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, no
l.Log().Tracef("Starting node '%s'", node.Name)
if err := runtime.StartNode(ctx, node); err != nil {
l.Log().Errorf("Failed to start node '%s'", node.Name)
return err
return fmt.Errorf("runtime failed to start node '%s': %w", node.Name, err)
}
if node.State.Started != "" {
@ -453,11 +450,11 @@ func NodeCreate(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, c
// specify options depending on node role
if node.Role == k3d.AgentRole { // TODO: check here AND in CLI or only here?
if err := patchAgentSpec(node); err != nil {
return err
return fmt.Errorf("failed to patch agent spec on node %s: %w", node.Name, err)
}
} else if node.Role == k3d.ServerRole {
if err := patchServerSpec(node, runtime); err != nil {
return err
return fmt.Errorf("failed to patch server spec on node %s: %w", node.Name, err)
}
}
@ -496,7 +493,7 @@ func NodeCreate(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, c
* CREATION
*/
if err := runtime.CreateNode(ctx, node); err != nil {
return err
return fmt.Errorf("runtime failed to create node '%s': %w", node.Name, err)
}
return nil
@ -524,15 +521,14 @@ func NodeDelete(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, o
if !opts.SkipLBUpdate && (node.Role == k3d.ServerRole || node.Role == k3d.AgentRole) {
cluster, err := ClusterGet(ctx, runtime, &k3d.Cluster{Name: node.RuntimeLabels[k3d.LabelClusterName]})
if err != nil {
l.Log().Errorf("Failed to find cluster for node '%s'", node.Name)
return err
return fmt.Errorf("failed fo find cluster for node '%s': %w", node.Name, err)
}
// if it's a server node, then update the loadbalancer configuration
if node.Role == k3d.ServerRole {
if err := UpdateLoadbalancerConfig(ctx, runtime, cluster); err != nil {
if !errors.Is(err, ErrLBConfigHostNotFound) {
return fmt.Errorf("Failed to update cluster loadbalancer: %w", err)
return fmt.Errorf("failed to update cluster loadbalancer: %w", err)
}
}
}
@ -583,8 +579,7 @@ func patchServerSpec(node *k3d.Node, runtime runtimes.Runtime) error {
func NodeList(ctx context.Context, runtime runtimes.Runtime) ([]*k3d.Node, error) {
nodes, err := runtime.GetNodesByLabel(ctx, k3d.DefaultRuntimeLabels)
if err != nil {
l.Log().Errorln("Failed to get nodes")
return nil, err
return nil, fmt.Errorf("failed to list nodes: %w", err)
}
return nodes, nil
@ -595,8 +590,7 @@ func NodeGet(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node) (*k3
// get node
node, err := runtime.GetNode(ctx, node)
if err != nil {
l.Log().Errorf("Failed to get node '%s'", node.Name)
return nil, err
return nil, fmt.Errorf("failed to get node '%s': %w", node.Name, err)
}
return node, nil
@ -706,7 +700,7 @@ func NodeEdit(ctx context.Context, runtime runtimes.Runtime, existingNode, chang
result, err := CopyNode(ctx, existingNode, CopyNodeOpts{keepState: false})
if err != nil {
return err
return fmt.Errorf("failed to copy node %s: %w", existingNode.Name, err)
}
/*
@ -751,7 +745,7 @@ func NodeEdit(ctx context.Context, runtime runtimes.Runtime, existingNode, chang
// prepare to write config to lb container
configyaml, err := yaml.Marshal(lbConfig)
if err != nil {
return err
return fmt.Errorf("failed to marshal loadbalancer config: %w", err)
}
writeLbConfigAction := k3d.NodeHook{
@ -778,7 +772,7 @@ func NodeReplace(ctx context.Context, runtime runtimes.Runtime, old, new *k3d.No
oldNameOriginal := old.Name
l.Log().Infof("Renaming existing node %s to %s...", old.Name, oldNameTemp)
if err := runtime.RenameNode(ctx, old, oldNameTemp); err != nil {
return err
return fmt.Errorf("runtime failed to rename node '%s': %w", old.Name, err)
}
old.Name = oldNameTemp
@ -794,7 +788,7 @@ func NodeReplace(ctx context.Context, runtime runtimes.Runtime, old, new *k3d.No
// stop existing/old node
l.Log().Infof("Stopping existing node %s...", old.Name)
if err := runtime.StopNode(ctx, old); err != nil {
return err
return fmt.Errorf("runtime failed to stop node '%s': %w", old.Name, err)
}
// start new node
@ -816,7 +810,7 @@ func NodeReplace(ctx context.Context, runtime runtimes.Runtime, old, new *k3d.No
// cleanup: delete old node
l.Log().Infof("Deleting old node %s...", old.Name)
if err := NodeDelete(ctx, runtime, old, k3d.NodeDeleteOpts{SkipLBUpdate: true}); err != nil {
return err
return fmt.Errorf("failed to delete old node '%s': %w", old.Name, err)
}
// done
@ -831,7 +825,7 @@ func CopyNode(ctx context.Context, src *k3d.Node, opts CopyNodeOpts) (*k3d.Node,
targetCopy, err := copystruct.Copy(src)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to copy node struct: %w", err)
}
result := targetCopy.(*k3d.Node)
@ -841,5 +835,5 @@ func CopyNode(ctx context.Context, src *k3d.Node, opts CopyNodeOpts) (*k3d.Node,
result.State = k3d.NodeState{}
}
return result, err
return result, nil
}

@ -81,7 +81,7 @@ func TransformPorts(ctx context.Context, runtime runtimes.Runtime, cluster *k3d.
}
for _, pm := range portmappings {
if err := loadbalancerAddPortConfigs(cluster.ServerLoadBalancer, pm, nodes); err != nil {
return err
return fmt.Errorf("error adding port config to loadbalancer: %w", err)
}
}
} else if suffix == "direct" {

@ -47,7 +47,7 @@ func RegistryRun(ctx context.Context, runtime runtimes.Runtime, reg *k3d.Registr
return nil, fmt.Errorf("Failed to start registry: %+v", err)
}
return regNode, err
return regNode, nil
}
// RegistryCreate creates a registry node
@ -99,8 +99,7 @@ func RegistryCreate(ctx context.Context, runtime runtimes.Runtime, reg *k3d.Regi
// create the registry node
l.Log().Infof("Creating node '%s'", registryNode.Name)
if err := NodeCreate(ctx, runtime, registryNode, k3d.NodeCreateOpts{}); err != nil {
l.Log().Errorln("Failed to create registry node")
return nil, err
return nil, fmt.Errorf("failed to create registry node '%s': %w", registryNode.Name, err)
}
l.Log().Infof("Successfully created registry '%s'", registryNode.Name)
@ -115,8 +114,7 @@ func RegistryConnectClusters(ctx context.Context, runtime runtimes.Runtime, regi
// find registry node
registryNode, err := NodeGet(ctx, runtime, registryNode)
if err != nil {
l.Log().Errorf("Failed to find registry node '%s'", registryNode.Name)
return err
return fmt.Errorf("Failed to find registry node '%s': %w", registryNode.Name, err)
}
// get cluster details and connect
@ -148,8 +146,7 @@ func RegistryConnectNetworks(ctx context.Context, runtime runtimes.Runtime, regi
// find registry node
registryNode, err := NodeGet(ctx, runtime, registryNode)
if err != nil {
l.Log().Errorf("Failed to find registry node '%s'", registryNode.Name)
return err
return fmt.Errorf("Failed to find registry node '%s': %w", registryNode.Name, err)
}
// get cluster details and connect
@ -317,7 +314,7 @@ func RegistryGenerateLocalRegistryHostingConfigMapYAML(ctx context.Context, runt
},
)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to marshal LocalRegistryHosting configmap data: %w", err)
}
cm := configmap{
@ -334,7 +331,7 @@ func RegistryGenerateLocalRegistryHostingConfigMapYAML(ctx context.Context, runt
cmYaml, err := yaml.Marshal(cm)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to marshal LocalRegistryHosting configmap: %w", err)
}
l.Log().Tracef("LocalRegistryHostingConfigMapYaml: %s", string(cmYaml))
@ -345,7 +342,7 @@ func RegistryGenerateLocalRegistryHostingConfigMapYAML(ctx context.Context, runt
// RegistryMergeConfig merges a source registry config into an existing dest registry cofnig
func RegistryMergeConfig(ctx context.Context, dest, src *k3s.Registry) error {
if err := mergo.MergeWithOverwrite(dest, src); err != nil {
return fmt.Errorf("Failed to merge registry configs: %+v", err)
return fmt.Errorf("failed to merge registry configs: %w", err)
}
return nil
}

@ -41,7 +41,7 @@ import (
func ImageImportIntoClusterMulti(ctx context.Context, runtime runtimes.Runtime, images []string, cluster *k3d.Cluster, opts k3d.ImageImportOpts) error {
imagesFromRuntime, imagesFromTar, err := findImages(ctx, runtime, images)
if err != nil {
return err
return fmt.Errorf("failed to find images: %w", err)
}
// no images found to load -> exit early
@ -52,7 +52,7 @@ func ImageImportIntoClusterMulti(ctx context.Context, runtime runtimes.Runtime,
// create tools node to export images
toolsNode, err := EnsureToolsNode(ctx, runtime, cluster)
if err != nil {
return err
return fmt.Errorf("failed to ensure that tools node is running: %w", err)
}
/* TODO:
@ -70,8 +70,7 @@ func ImageImportIntoClusterMulti(ctx context.Context, runtime runtimes.Runtime,
l.Log().Infof("Saving %d image(s) from runtime...", len(imagesFromRuntime))
tarName := fmt.Sprintf("%s/k3d-%s-images-%s.tar", k3d.DefaultImageVolumeMountPath, cluster.Name, time.Now().Format("20060102150405"))
if err := runtime.ExecInNode(ctx, toolsNode, append([]string{"./k3d-tools", "save-image", "-d", tarName}, imagesFromRuntime...)); err != nil {
l.Log().Errorf("Failed to save image(s) in tools container for cluster '%s'", cluster.Name)
return err
return fmt.Errorf("failed to save image(s) in tools container for cluster '%s': %w", cluster.Name, err)
}
importTarNames = append(importTarNames, tarName)
}
@ -82,7 +81,7 @@ func ImageImportIntoClusterMulti(ctx context.Context, runtime runtimes.Runtime,
for _, file := range imagesFromTar {
tarName := fmt.Sprintf("%s/k3d-%s-images-%s-file-%s", k3d.DefaultImageVolumeMountPath, cluster.Name, time.Now().Format("20060102150405"), path.Base(file))
if err := runtime.CopyToNode(ctx, file, tarName, toolsNode); err != nil {
l.Log().Errorf("Failed to copy image tar '%s' to tools node! Error below:\n%+v", file, err)
l.Log().Errorf("failed to copy image tar '%s' to tools node! Error below:\n%+v", file, err)
continue
}
importTarNames = append(importTarNames, tarName)
@ -100,8 +99,7 @@ func ImageImportIntoClusterMulti(ctx context.Context, runtime runtimes.Runtime,
go func(node *k3d.Node, wg *sync.WaitGroup, tarPath string) {
l.Log().Infof("Importing images from tarball '%s' into node '%s'...", tarPath, node.Name)
if err := runtime.ExecInNode(ctx, node, []string{"ctr", "image", "import", tarPath}); err != nil {
l.Log().Errorf("Failed to import images in node '%s'", node.Name)
l.Log().Errorln(err)
l.Log().Errorf("failed to import images in node '%s': %v", node.Name, err)
}
wg.Done()
}(node, &importWaitgroup, tarName)
@ -114,8 +112,7 @@ func ImageImportIntoClusterMulti(ctx context.Context, runtime runtimes.Runtime,
if !opts.KeepTar && len(importTarNames) > 0 {
l.Log().Infoln("Removing the tarball(s) from image volume...")
if err := runtime.ExecInNode(ctx, toolsNode, []string{"rm", "-f", strings.Join(importTarNames, " ")}); err != nil {
l.Log().Errorf("Failed to delete one or more tarballs from '%+v'", importTarNames)
l.Log().Errorln(err)
l.Log().Errorf("failed to delete one or more tarballs from '%+v': %v", importTarNames, err)
}
}
@ -123,7 +120,7 @@ func ImageImportIntoClusterMulti(ctx context.Context, runtime runtimes.Runtime,
if !opts.KeepToolsNode {
l.Log().Infoln("Removing k3d-tools node...")
if err := runtime.DeleteNode(ctx, toolsNode); err != nil {
l.Log().Errorf("Failed to delete tools node '%s': Try to delete it manually", toolsNode.Name)
l.Log().Errorf("failed to delete tools node '%s' (try to delete it manually): %v", toolsNode.Name, err)
}
}
@ -140,8 +137,7 @@ type runtimeImageGetter interface {
func findImages(ctx context.Context, runtime runtimeImageGetter, requestedImages []string) (imagesFromRuntime, imagesFromTar []string, err error) {
runtimeImages, err := runtime.GetImages(ctx)
if err != nil {
l.Log().Errorln("Failed to fetch list of existing images from runtime")
return nil, nil, err
return nil, nil, fmt.Errorf("failed to fetch list of existing images from runtime: %w", err)
}
for _, requestedImage := range requestedImages {
@ -160,7 +156,7 @@ func findImages(ctx context.Context, runtime runtimeImageGetter, requestedImages
l.Log().Warnf("Image '%s' is not a file and couldn't be found in the container runtime", requestedImage)
}
return imagesFromRuntime, imagesFromTar, err
return imagesFromRuntime, imagesFromTar, nil
}
func findRuntimeImage(requestedImage string, runtimeImages []string) (string, bool) {
@ -253,8 +249,7 @@ func runToolsNode(ctx context.Context, runtime runtimes.Runtime, cluster *k3d.Cl
}
node.RuntimeLabels[k3d.LabelClusterName] = cluster.Name
if err := NodeRun(ctx, runtime, node, k3d.NodeCreateOpts{}); err != nil {
l.Log().Errorf("Failed to create tools container for cluster '%s'", cluster.Name)
return node, err
return node, fmt.Errorf("failed to run k3d-tools node for cluster '%s': %w", cluster.Name, err)
}
return node, nil
@ -265,12 +260,11 @@ func EnsureToolsNode(ctx context.Context, runtime runtimes.Runtime, cluster *k3d
cluster, err = ClusterGet(ctx, runtime, cluster)
if err != nil {
l.Log().Errorf("Failed to find the specified cluster")
return nil, err
return nil, fmt.Errorf("failed to retrieve cluster '%s': %w", cluster.Name, err)
}
if cluster.Network.Name == "" {
return nil, fmt.Errorf("Failed to get network for cluster '%s'", cluster.Name)
return nil, fmt.Errorf("failed to get network for cluster '%s'", cluster.Name)
}
var imageVolume string

@ -73,13 +73,11 @@ func FromViper(config *viper.Viper) (types.Config, error) {
}
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to parse config '%s': %w'", config.ConfigFileUsed(), err)
}
if err := config.Unmarshal(&cfg); err != nil {
l.Log().Errorln("Failed to unmarshal File config")
return nil, err
return nil, fmt.Errorf("failed to unmarshal config file '%s': %w", config.ConfigFileUsed(), err)
}
return cfg, nil

@ -73,7 +73,7 @@ func ValidateSchema(content map[string]interface{}, schemaJSON []byte) error {
result, err := gojsonschema.Validate(schemaLoader, configLoader)
if err != nil {
return err
return fmt.Errorf("failed to validate config: %w", err)
}
l.Log().Debugf("JSON Schema Validation Result: %+v", result)

@ -23,6 +23,8 @@ THE SOFTWARE.
package config
import (
"fmt"
"github.com/imdario/mergo"
conf "github.com/rancher/k3d/v4/pkg/config/v1alpha3"
l "github.com/rancher/k3d/v4/pkg/logger"
@ -33,9 +35,8 @@ func MergeSimple(dest, src conf.SimpleConfig) (*conf.SimpleConfig, error) {
l.Log().Debugf("Merging %+v into %+v", src, dest)
if err := mergo.Merge(&dest, src); err != nil {
l.Log().Errorln("Failed to merge config")
return nil, err
return nil, fmt.Errorf("failed to merge configs: %w", err)
}
return &dest, nil

@ -167,7 +167,7 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
for _, volumeWithNodeFilters := range simpleConfig.Volumes {
nodes, err := util.FilterNodes(nodeList, volumeWithNodeFilters.NodeFilters)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to filter nodes for volume mapping '%s': %w", volumeWithNodeFilters.Volume, err)
}
for _, node := range nodes {
@ -177,18 +177,18 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
// -> PORTS
if err := client.TransformPorts(ctx, runtime, &newCluster, simpleConfig.Ports); err != nil {
return nil, err
return nil, fmt.Errorf("failed to transform ports: %w", err)
}
// -> K3S NODE LABELS
for _, k3sNodeLabelWithNodeFilters := range simpleConfig.Options.K3sOptions.NodeLabels {
if len(k3sNodeLabelWithNodeFilters.NodeFilters) == 0 && nodeCount > 1 {
return nil, fmt.Errorf("K3sNodeLabelmapping '%s' lacks a node filter, but there's more than one node", k3sNodeLabelWithNodeFilters.Label)
return nil, fmt.Errorf("k3s node label mapping '%s' lacks a node filter, but there's more than one node", k3sNodeLabelWithNodeFilters.Label)
}
nodes, err := util.FilterNodes(nodeList, k3sNodeLabelWithNodeFilters.NodeFilters)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to filter nodes for k3s node label mapping '%s': %w", k3sNodeLabelWithNodeFilters.Label, err)
}
for _, node := range nodes {
@ -209,7 +209,7 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
nodes, err := util.FilterNodes(nodeList, runtimeLabelWithNodeFilters.NodeFilters)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to filter nodes for runtime label mapping '%s': %w", runtimeLabelWithNodeFilters.Label, err)
}
for _, node := range nodes {
@ -232,7 +232,7 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
nodes, err := util.FilterNodes(nodeList, envVarWithNodeFilters.NodeFilters)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to filter nodes for environment variable config '%s': %w", envVarWithNodeFilters.EnvVar, err)
}
for _, node := range nodes {
@ -248,7 +248,7 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
nodes, err := util.FilterNodes(nodeList, argWithNodeFilters.NodeFilters)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to filter nodes for k3s extra args config '%s': %w", argWithNodeFilters.Arg, err)
}
for _, node := range nodes {
@ -283,7 +283,7 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
if simpleConfig.Registries.Create {
regPort, err := cliutil.ParsePortExposureSpec("random", k3d.DefaultRegistryPort)
if err != nil {
return nil, fmt.Errorf("Failed to get port for registry: %+v", err)
return nil, fmt.Errorf("failed to get port for registry: %w", err)
}
clusterCreateOpts.Registries.Create = &k3d.Registry{
ClusterRef: newCluster.Name,
@ -296,7 +296,7 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
for _, usereg := range simpleConfig.Registries.Use {
reg, err := util.ParseRegistryRef(usereg)
if err != nil {
return nil, fmt.Errorf("Failed to parse use-registry string '%s': %+v", usereg, err)
return nil, fmt.Errorf("failed to parse use-registry string '%s': %w", usereg, err)
}
l.Log().Tracef("Parsed registry reference: %+v", reg)
clusterCreateOpts.Registries.Use = append(clusterCreateOpts.Registries.Use, reg)
@ -308,20 +308,20 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
if strings.Contains(simpleConfig.Registries.Config, "\n") { // CASE 1: embedded registries.yaml (multiline string)
l.Log().Debugf("Found multiline registries config embedded in SimpleConfig:\n%s", simpleConfig.Registries.Config)
if err := yaml.Unmarshal([]byte(simpleConfig.Registries.Config), &k3sRegistry); err != nil {
return nil, fmt.Errorf("Failed to read embedded registries config: %+v", err)
return nil, fmt.Errorf("failed to read embedded registries config: %w", err)
}
} else { // CASE 2: registries.yaml file referenced by path (single line)
registryConfigFile, err := os.Open(simpleConfig.Registries.Config)
if err != nil {
return nil, fmt.Errorf("Failed to open registry config file at %s: %+v", simpleConfig.Registries.Config, err)
return nil, fmt.Errorf("failed to open registry config file at %s: %w", simpleConfig.Registries.Config, err)
}
configBytes, err := ioutil.ReadAll(registryConfigFile)
if err != nil {
return nil, fmt.Errorf("Failed to read registry config file at %s: %+v", registryConfigFile.Name(), err)
return nil, fmt.Errorf("failed to read registry config file at %s: %w", registryConfigFile.Name(), err)
}
if err := yaml.Unmarshal(configBytes, &k3sRegistry); err != nil {
return nil, fmt.Errorf("Failed to read registry configuration: %+v", err)
return nil, fmt.Errorf("failed to read registry configuration: %w", err)
}
}

@ -35,63 +35,55 @@ import (
"fmt"
dockerunits "github.com/docker/go-units"
l "github.com/rancher/k3d/v4/pkg/logger"
)
// ValidateClusterConfig checks a given cluster config for basic errors
func ValidateClusterConfig(ctx context.Context, runtime runtimes.Runtime, config conf.ClusterConfig) error {
// cluster name must be a valid host name
if err := k3dc.CheckName(config.Cluster.Name); err != nil {
l.Log().Errorf("Provided cluster name '%s' does not match requirements", config.Cluster.Name)
return err
return fmt.Errorf("provided cluster name '%s' does not match requirements: %w", config.Cluster.Name, err)
}
// network:: edge case: hostnetwork -> only if we have a single node (to avoid port collisions)
if config.Cluster.Network.Name == "host" && len(config.Cluster.Nodes) > 1 {
return fmt.Errorf("Can only use hostnetwork mode with a single node (port collisions, etc.)")
return fmt.Errorf("can only use hostnetwork mode with a single node (port collisions, etc.)")
}
// timeout can't be negative
if config.ClusterCreateOpts.Timeout < 0*time.Second {
return fmt.Errorf("Timeout may not be negative (is '%s')", config.ClusterCreateOpts.Timeout)
return fmt.Errorf("timeout may not be negative (is '%s')", config.ClusterCreateOpts.Timeout)
}
// API-Port cannot be changed when using network=host
if config.Cluster.Network.Name == "host" && config.Cluster.KubeAPI.Port.Port() != k3d.DefaultAPIPort {
// in hostNetwork mode, we're not going to map a hostport. Here it should always use 6443.
// Note that hostNetwork mode is super inflexible and since we don't change the backend port (on the container), it will only be one hostmode cluster allowed.
return fmt.Errorf("The API Port can not be changed when using 'host' network")
return fmt.Errorf("the API Port can not be changed when using 'host' network")
}
// memory limits must have proper format
// if empty we don't care about errors in parsing
if config.ClusterCreateOpts.ServersMemory != "" {
if _, err := dockerunits.RAMInBytes(config.ClusterCreateOpts.ServersMemory); err != nil {
return fmt.Errorf("Provided servers memory limit value is invalid")
return fmt.Errorf("provided servers memory limit value is invalid: %w", err)
}
}
if config.ClusterCreateOpts.AgentsMemory != "" {
if _, err := dockerunits.RAMInBytes(config.ClusterCreateOpts.AgentsMemory); err != nil {
return fmt.Errorf("Provided agents memory limit value is invalid")
return fmt.Errorf("provided agents memory limit value is invalid: %w", err)
}
}
// validate nodes one by one
for _, node := range config.Cluster.Nodes {
// node names have to be valid hostnames // TODO: validate hostnames once we generate them before this step
/*if err := k3dc.CheckName(node.Name); err != nil {
return err
}*/
// volumes have to be either an existing path on the host or a named runtime volume
for _, volume := range node.Volumes {
if err := runtimeutil.ValidateVolumeMount(runtime, volume); err != nil {
return err
return fmt.Errorf("failed to validate volume mount '%s': %w", volume, err)
}
}
}

@ -46,8 +46,7 @@ func createContainer(ctx context.Context, dockerNode *NodeInDocker, name string)
// initialize docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return "", err
return "", fmt.Errorf("failed to create docker client: %w", err)
}
defer docker.Close()
@ -58,13 +57,11 @@ func createContainer(ctx context.Context, dockerNode *NodeInDocker, name string)
if err != nil {
if client.IsErrNotFound(err) {
if err := pullImage(ctx, docker, dockerNode.ContainerConfig.Image); err != nil {
l.Log().Errorf("Failed to create container '%s'", name)
return "", err
return "", fmt.Errorf("docker failed to pull image '%s': %w", dockerNode.ContainerConfig.Image, err)
}
continue
}
l.Log().Errorf("Failed to create container '%s'", name)
return "", err
return "", fmt.Errorf("docker failed to create container '%s': %w", name, err)
}
l.Log().Debugf("Created container %s (ID: %s)", name, resp.ID)
break
@ -77,8 +74,7 @@ func startContainer(ctx context.Context, ID string) error {
// initialize docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return err
return fmt.Errorf("failed to get docker client: %w", err)
}
defer docker.Close()
@ -91,8 +87,7 @@ func removeContainer(ctx context.Context, ID string) error {
// (0) create docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return err
return fmt.Errorf("failed to get docker client: %w", err)
}
defer docker.Close()
@ -104,8 +99,7 @@ func removeContainer(ctx context.Context, ID string) error {
// (2) remove container
if err := docker.ContainerRemove(ctx, ID, options); err != nil {
l.Log().Errorf("Failed to delete container '%s'", ID)
return err
return fmt.Errorf("docker failed to remove the container '%s': %w", ID, err)
}
l.Log().Infoln("Deleted", ID)
@ -118,8 +112,7 @@ func pullImage(ctx context.Context, docker *client.Client, image string) error {
resp, err := docker.ImagePull(ctx, image, types.ImagePullOptions{})
if err != nil {
l.Log().Errorf("Failed to pull image '%s'", image)
return err
return fmt.Errorf("docker failed to pull the image '%s': %w", image, err)
}
defer resp.Close()
@ -132,8 +125,7 @@ func pullImage(ctx context.Context, docker *client.Client, image string) error {
}
_, err = io.Copy(writer, resp)
if err != nil {
l.Log().Warningf("Couldn't get docker output")
l.Log().Warningln(err)
l.Log().Warnf("Couldn't get docker output: %v", err)
}
return nil
@ -145,8 +137,7 @@ func getNodeContainer(ctx context.Context, node *k3d.Node) (*types.Container, er
// (0) create docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return nil, err
return nil, fmt.Errorf("failed to get docker client: %w", err)
}
defer docker.Close()
@ -187,8 +178,7 @@ func getNodeContainer(ctx context.Context, node *k3d.Node) (*types.Container, er
func executeCheckInContainer(ctx context.Context, image string, cmd []string) (int64, error) {
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return -1, err
return -1, fmt.Errorf("failed to create docker client: %w", err)
}
defer docker.Close()
@ -204,20 +194,17 @@ func executeCheckInContainer(ctx context.Context, image string, cmd []string) (i
if err != nil {
if client.IsErrNotFound(err) {
if err := pullImage(ctx, docker, image); err != nil {
l.Log().Errorf("Failed to create container from image %s with cmd %s", image, cmd)
return -1, err
return -1, fmt.Errorf("docker failed to pull image '%s': %w", image, err)
}
continue
}
l.Log().Errorf("Failed to create container from image %s with cmd %s", image, cmd)
return -1, err
return -1, fmt.Errorf("docker failed to create container from image '%s' with cmd '%s': %w", image, cmd, err)
}
break
}
if err = startContainer(ctx, resp.ID); err != nil {
l.Log().Errorf("Failed to start container from image %s with cmd %s", image, cmd)
return -1, err
return -1, fmt.Errorf("docker failed to start container from image '%s' with cmd '%s': %w", image, cmd, err)
}
exitCode := -1
@ -225,15 +212,14 @@ func executeCheckInContainer(ctx context.Context, image string, cmd []string) (i
select {
case err := <-errCh:
if err != nil {
l.Log().Errorf("Error while waiting for container %s to exit", resp.ID)
return -1, err
return -1, fmt.Errorf("docker error while waiting for container '%s' to exit: %w", resp.ID, err)
}
case status := <-statusCh:
exitCode = int(status.StatusCode)
}
if err = removeContainer(ctx, resp.ID); err != nil {
return -1, err
return -1, fmt.Errorf("docker failed to remove container '%s': %w", resp.ID, err)
}
return int64(exitCode), nil
@ -246,6 +232,5 @@ func CheckIfDirectoryExists(ctx context.Context, image string, dir string) (bool
cmd := []string{"sh", "-c", shellCmd}
exitCode, err := executeCheckInContainer(ctx, image, cmd)
l.Log().Tracef("check dir container returned %d exist code", exitCode)
return exitCode == 0, err
return exitCode == 0, fmt.Errorf("error executing check command '%s' in container with image '%s': %w", cmd, image, err)
}

@ -33,7 +33,7 @@ func (d Docker) GetHostIP(ctx context.Context, network string) (net.IP, error) {
if runtime.GOOS == "linux" {
ip, err := GetGatewayIP(ctx, network)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get gateway IP of docker network '%s': %w", network, err)
}
return ip, nil
}

@ -23,9 +23,9 @@ package docker
import (
"context"
"fmt"
"github.com/docker/docker/api/types"
l "github.com/rancher/k3d/v4/pkg/logger"
)
// GetImages returns a list of images present in the runtime
@ -33,15 +33,13 @@ func (d Docker) GetImages(ctx context.Context) ([]string, error) {
// create docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return nil, err
return nil, fmt.Errorf("failed to create docker client: %w", err)
}
defer docker.Close()
imageSummary, err := docker.ImageList(ctx, types.ImageListOptions{All: true})
if err != nil {
l.Log().Errorln("Failed to list available docker images")
return nil, err
return nil, fmt.Errorf("docker failed to list images: %w", err)
}
var images []string

@ -24,9 +24,9 @@ package docker
import (
"context"
"fmt"
"strings"
l "github.com/rancher/k3d/v4/pkg/logger"
runtimeTypes "github.com/rancher/k3d/v4/pkg/runtimes/types"
)
@ -34,14 +34,13 @@ func (d Docker) Info() (*runtimeTypes.RuntimeInfo, error) {
// create docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return nil, err
return nil, fmt.Errorf("failed to create docker client: %w", err)
}
defer docker.Close()
info, err := docker.Info(context.Background())
if err != nil {
return nil, err
return nil, fmt.Errorf("docker failed to provide info output: %w", err)
}
runtimeInfo := runtimeTypes.RuntimeInfo{

@ -24,6 +24,7 @@ package docker
import (
"context"
"fmt"
"io"
l "github.com/rancher/k3d/v4/pkg/logger"
@ -34,22 +35,20 @@ import (
func (d Docker) GetKubeconfig(ctx context.Context, node *k3d.Node) (io.ReadCloser, error) {
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return nil, err
return nil, fmt.Errorf("failed to create docker client: %w", err)
}
defer docker.Close()
container, err := getNodeContainer(ctx, node)
if err != nil {
return nil, err
return nil, fmt.Errorf("docker failed to get container for node '%s': %w", node.Name, err)
}
l.Log().Tracef("Container Details: %+v", container)
reader, _, err := docker.CopyFromContainer(ctx, container.ID, "/output/kubeconfig.yaml")
if err != nil {
l.Log().Errorf("Failed to copy from container '%s'", container.ID)
return nil, err
return nil, fmt.Errorf("docker failed to copy path '/output/kubeconfig.yaml' from container '%s': %w", container.ID, err)
}
return reader, nil

@ -43,13 +43,12 @@ func (d Docker) GetNetwork(ctx context.Context, searchNet *k3d.ClusterNetwork) (
// (0) create new docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return nil, err
return nil, fmt.Errorf("failed to create docker client: %w", err)
}
defer docker.Close()
if searchNet.ID == "" && searchNet.Name == "" {
return nil, fmt.Errorf("need one of name, id to get network")
return nil, fmt.Errorf("failed to get network, because neither name nor ID was provided")
}
// configure list filters
filter := filters.NewArgs()
@ -65,8 +64,7 @@ func (d Docker) GetNetwork(ctx context.Context, searchNet *k3d.ClusterNetwork) (
Filters: filter,
})
if err != nil {
l.Log().Errorln("Failed to list docker networks")
return nil, err
return nil, fmt.Errorf("docker failed to list networks: %w", err)
}
if len(networkList) == 0 {
@ -75,7 +73,7 @@ func (d Docker) GetNetwork(ctx context.Context, searchNet *k3d.ClusterNetwork) (
targetNetwork, err := docker.NetworkInspect(ctx, networkList[0].ID, types.NetworkInspectOptions{})
if err != nil {
return nil, fmt.Errorf("failed to inspect network %s: %w", networkList[0].Name, err)
return nil, fmt.Errorf("docker failed to inspect network %s: %w", networkList[0].Name, err)
}
l.Log().Debugf("Found network %+v", targetNetwork)
@ -88,7 +86,7 @@ func (d Docker) GetNetwork(ctx context.Context, searchNet *k3d.ClusterNetwork) (
if len(targetNetwork.IPAM.Config) > 0 {
network.IPAM, err = d.parseIPAM(targetNetwork.IPAM.Config[0])
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to parse IPAM config: %w", err)
}
for _, container := range targetNetwork.Containers {
@ -138,8 +136,7 @@ func (d Docker) CreateNetworkIfNotPresent(ctx context.Context, inNet *k3d.Cluste
// (0) create new docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return nil, false, err
return nil, false, fmt.Errorf("failed to create docker client: %w", err)
}
defer docker.Close()
@ -147,8 +144,7 @@ func (d Docker) CreateNetworkIfNotPresent(ctx context.Context, inNet *k3d.Cluste
if err != nil {
if err != runtimeErr.ErrRuntimeNetworkNotExists {
if existingNet == nil {
l.Log().Errorln("Failed to check for duplicate networks")
return nil, false, err
return nil, false, fmt.Errorf("failed to check for duplicate docker networks: %w", err)
} else if err == runtimeErr.ErrRuntimeNetworkMultiSameName {
l.Log().Warnf("%+v, returning the first one: %s (%s)", err, existingNet.Name, existingNet.ID)
return existingNet, true, nil
@ -176,7 +172,7 @@ func (d Docker) CreateNetworkIfNotPresent(ctx context.Context, inNet *k3d.Cluste
l.Log().Traceln("No subnet prefix given, but network should be managed: Trying to get a free subnet prefix...")
freeSubnetPrefix, err := d.getFreeSubnetPrefix(ctx)
if err != nil {
return nil, false, err
return nil, false, fmt.Errorf("failed to get free subnet prefix: %w", err)
}
inNet.IPAM.IPPrefix = freeSubnetPrefix
}
@ -195,20 +191,18 @@ func (d Docker) CreateNetworkIfNotPresent(ctx context.Context, inNet *k3d.Cluste
newNet, err := docker.NetworkCreate(ctx, inNet.Name, netCreateOpts)
if err != nil {
l.Log().Errorln("Failed to create new network")
return nil, false, err
return nil, false, fmt.Errorf("docker failed to create new network '%s': %w", inNet.Name, err)
}
networkDetails, err := docker.NetworkInspect(ctx, newNet.ID, types.NetworkInspectOptions{})
if err != nil {
l.Log().Errorln("Failed to inspect newly created network")
return nil, false, err
return nil, false, fmt.Errorf("docker failed to inspect newly created network '%s': %w", newNet.ID, err)
}
l.Log().Infof("Created network '%s' (%s)", inNet.Name, networkDetails.ID)
prefix, err := netaddr.ParseIPPrefix(networkDetails.IPAM.Config[0].Subnet)
if err != nil {
return nil, false, err
return nil, false, fmt.Errorf("failed to parse IP Prefix of newly created network '%s': %w", newNet.ID, err)
}
newClusterNet := &k3d.ClusterNetwork{Name: inNet.Name, ID: networkDetails.ID, IPAM: k3d.IPAM{IPPrefix: prefix}}
@ -225,8 +219,7 @@ func (d Docker) DeleteNetwork(ctx context.Context, ID string) error {
// (0) create new docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return err
return fmt.Errorf("failed to get docker client: %w", err)
}
defer docker.Close()
@ -235,7 +228,7 @@ func (d Docker) DeleteNetwork(ctx context.Context, ID string) error {
if strings.HasSuffix(err.Error(), "active endpoints") {
return runtimeErr.ErrRuntimeNetworkNotEmpty
}
return err
return fmt.Errorf("docker failed to remove network '%s': %w", ID, err)
}
return nil
}
@ -244,8 +237,7 @@ func (d Docker) DeleteNetwork(ctx context.Context, ID string) error {
func GetNetwork(ctx context.Context, ID string) (types.NetworkResource, error) {
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return types.NetworkResource{}, err
return types.NetworkResource{}, fmt.Errorf("failed to get docker client: %w", err)
}
defer docker.Close()
return docker.NetworkInspect(ctx, ID, types.NetworkInspectOptions{})
@ -255,8 +247,7 @@ func GetNetwork(ctx context.Context, ID string) (types.NetworkResource, error) {
func GetGatewayIP(ctx context.Context, network string) (net.IP, error) {
bridgeNetwork, err := GetNetwork(ctx, network)
if err != nil {
l.Log().Errorf("Failed to get bridge network with name '%s'", network)
return nil, err
return nil, fmt.Errorf("failed to get bridge network with name '%s': %w", network, err)
}
if len(bridgeNetwork.IPAM.Config) > 0 {
@ -279,22 +270,20 @@ func (d Docker) ConnectNodeToNetwork(ctx context.Context, node *k3d.Node, networ
// get container
container, err := getNodeContainer(ctx, node)
if err != nil {
return err
return fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
}
// get docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return err
return fmt.Errorf("failed to get docker client: %w", err)
}
defer docker.Close()
// get network
networkResource, err := GetNetwork(ctx, networkName)
if err != nil {
l.Log().Errorf("Failed to get network '%s'", networkName)
return err
return fmt.Errorf("failed to get network '%s': %w", networkName, err)
}
// connect container to network
@ -307,22 +296,20 @@ func (d Docker) DisconnectNodeFromNetwork(ctx context.Context, node *k3d.Node, n
// get container
container, err := getNodeContainer(ctx, node)
if err != nil {
return err
return fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
}
// get docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return err
return fmt.Errorf("failed to get docker client: %w", err)
}
defer docker.Close()
// get network
networkResource, err := GetNetwork(ctx, networkName)
if err != nil {
l.Log().Errorf("Failed to get network '%s'", networkName)
return err
return fmt.Errorf("failed to get network '%s': %w", networkName, err)
}
return docker.NetworkDisconnect(ctx, networkResource.ID, container.ID, true)

@ -44,15 +44,13 @@ func (d Docker) CreateNode(ctx context.Context, node *k3d.Node) error {
// translate node spec to docker container specs
dockerNode, err := TranslateNodeToContainer(node)
if err != nil {
l.Log().Errorln("Failed to translate k3d node specification to docker container specifications")
return err
return fmt.Errorf("failed to translate k3d node spec to docker container spec: %w", err)
}
// create node
_, err = createContainer(ctx, dockerNode, node.Name)
if err != nil {
l.Log().Errorf("Failed to create node '%s'", node.Name)
return err
return fmt.Errorf("failed to create container for node '%s': %w", node.Name, err)
}
return nil
@ -70,7 +68,7 @@ func (d Docker) GetNodesByLabel(ctx context.Context, labels map[string]string) (
// (0) get containers
containers, err := getContainersByLabel(ctx, labels)
if err != nil {
return nil, err
return nil, fmt.Errorf("docker failed to get containers with labels '%v': %w", labels, err)
}
// (1) convert them to node structs
@ -84,12 +82,12 @@ func (d Docker) GetNodesByLabel(ctx context.Context, labels map[string]string) (
l.Log().Warnf("Failed to get details for container %s", container.Names[0])
node, err = TranslateContainerToNode(&container)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to translate container '%s' to k3d node spec: %w", container.Names[0], err)
}
} else {
node, err = TranslateContainerDetailsToNode(containerDetails)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to translate container'%s' details to k3d node spec: %w", containerDetails.Name, err)
}
}
nodes = append(nodes, node)
@ -104,15 +102,14 @@ func (d Docker) StartNode(ctx context.Context, node *k3d.Node) error {
// (0) create docker client
docker, err := GetDockerClient()
if err != nil {
return fmt.Errorf("Failed to create docker client. %+v", err)
return fmt.Errorf("failed to create docker client. %w", err)
}
defer docker.Close()
// get container which represents the node
nodeContainer, err := getNodeContainer(ctx, node)
if err != nil {
l.Log().Errorf("Failed to get container for node '%s'", node.Name)
return err
return fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
}
// check if the container is actually managed by
@ -123,7 +120,7 @@ func (d Docker) StartNode(ctx context.Context, node *k3d.Node) error {
// actually start the container
l.Log().Infof("Starting Node '%s'", node.Name)
if err := docker.ContainerStart(ctx, nodeContainer.ID, types.ContainerStartOptions{}); err != nil {
return err
return fmt.Errorf("docker failed to start container for node '%s': %w", node.Name, err)
}
// get container which represents the node
@ -151,8 +148,7 @@ func (d Docker) StopNode(ctx context.Context, node *k3d.Node) error {
// get container which represents the node
nodeContainer, err := getNodeContainer(ctx, node)
if err != nil {
l.Log().Errorf("Failed to get container for node '%s'", node.Name)
return err
return fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
}
// check if the container is actually managed by
@ -162,7 +158,7 @@ func (d Docker) StopNode(ctx context.Context, node *k3d.Node) error {
// actually stop the container
if err := docker.ContainerStop(ctx, nodeContainer.ID, nil); err != nil {
return err
return fmt.Errorf("docker failed to stop the container '%s': %w", nodeContainer.ID, err)
}
return nil
@ -201,14 +197,13 @@ func getContainerDetails(ctx context.Context, containerID string) (types.Contain
// (0) create docker client
docker, err := GetDockerClient()
if err != nil {
return types.ContainerJSON{}, fmt.Errorf("Failed to create docker client. %+v", err)
return types.ContainerJSON{}, fmt.Errorf("failed to create docker client. %w", err)
}
defer docker.Close()
containerDetails, err := docker.ContainerInspect(ctx, containerID)
if err != nil {
l.Log().Errorf("Failed to get details for container '%s'", containerID)
return types.ContainerJSON{}, err
return types.ContainerJSON{}, fmt.Errorf("failed to get details for container '%s': %w", containerID, err)
}
return containerDetails, nil
@ -219,18 +214,17 @@ func getContainerDetails(ctx context.Context, containerID string) (types.Contain
func (d Docker) GetNode(ctx context.Context, node *k3d.Node) (*k3d.Node, error) {
container, err := getNodeContainer(ctx, node)
if err != nil {
return node, err
return node, fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
}
containerDetails, err := getContainerDetails(ctx, container.ID)
if err != nil {
return node, err
return node, fmt.Errorf("failed to get details for container '%s': %w", container.ID, err)
}
node, err = TranslateContainerDetailsToNode(containerDetails)
if err != nil {
l.Log().Errorf("Failed to translate container '%s' to node object", containerDetails.Name)
return node, err
return node, fmt.Errorf("failed to translate container '%s' details to node spec: %w", containerDetails.Name, err)
}
return node, nil
@ -246,20 +240,19 @@ func (d Docker) GetNodeStatus(ctx context.Context, node *k3d.Node) (bool, string
// get the container for the given node
container, err := getNodeContainer(ctx, node)
if err != nil {
return running, stateString, err
return running, stateString, fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
}
// create docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return running, stateString, err
return running, stateString, fmt.Errorf("failed to get docker client: %w", err)
}
defer docker.Close()
containerInspectResponse, err := docker.ContainerInspect(ctx, container.ID)
if err != nil {
return running, stateString, err
return running, stateString, fmt.Errorf("docker failed to inspect container '%s': %w", container.ID, err)
}
running = containerInspectResponse.ContainerJSONBase.State.Running
@ -272,7 +265,7 @@ func (d Docker) GetNodeStatus(ctx context.Context, node *k3d.Node) (bool, string
func (d Docker) NodeIsRunning(ctx context.Context, node *k3d.Node) (bool, error) {
isRunning, _, err := d.GetNodeStatus(ctx, node)
if err != nil {
return false, err
return false, fmt.Errorf("failed to get status for node '%s': %w", node.Name, err)
}
return isRunning, nil
}
@ -282,25 +275,23 @@ func (d Docker) GetNodeLogs(ctx context.Context, node *k3d.Node, since time.Time
// get the container for the given node
container, err := getNodeContainer(ctx, node)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
}
// create docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return nil, err
return nil, fmt.Errorf("failed to get docker client; %w", err)
}
defer docker.Close()
containerInspectResponse, err := docker.ContainerInspect(ctx, container.ID)
if err != nil {
l.Log().Errorf("Failed to inspect node '%s'(ID %s)", node.Name, container.ID)
return nil, err
return nil, fmt.Errorf("failed ton inspect container '%s': %w", container.ID, err)
}
if !containerInspectResponse.ContainerJSONBase.State.Running {
return nil, fmt.Errorf("Node '%s' (container '%s') not running", node.Name, containerInspectResponse.ID)
return nil, fmt.Errorf("node '%s' (container '%s') not running", node.Name, containerInspectResponse.ID)
}
sinceStr := ""
@ -309,8 +300,7 @@ func (d Docker) GetNodeLogs(ctx context.Context, node *k3d.Node, since time.Time
}
logreader, err := docker.ContainerLogs(ctx, container.ID, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Since: sinceStr})
if err != nil {
l.Log().Errorf("Failed to get logs from node '%s' (container '%s')", node.Name, container.ID)
return nil, err
return nil, fmt.Errorf("docker failed to get logs from node '%s' (container '%s'): %w", node.Name, container.ID, err)
}
return logreader, nil
@ -335,8 +325,7 @@ func (d Docker) ExecInNode(ctx context.Context, node *k3d.Node, cmd []string) er
if execConnection != nil && execConnection.Reader != nil {
logs, err := ioutil.ReadAll(execConnection.Reader)
if err != nil {
l.Log().Errorf("Failed to get logs from errored exec process in node '%s'", node.Name)
return err
return fmt.Errorf("failed to get logs from errored exec process in node '%s': %w", node.Name, err)
}
err = fmt.Errorf("%w: Logs from failed access process:\n%s", err, string(logs))
}
@ -351,14 +340,13 @@ func executeInNode(ctx context.Context, node *k3d.Node, cmd []string) (*types.Hi
// get the container for the given node
container, err := getNodeContainer(ctx, node)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
}
// create docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return nil, err
return nil, fmt.Errorf("failed to get docker client: %w", err)
}
defer docker.Close()
@ -371,28 +359,25 @@ func executeInNode(ctx context.Context, node *k3d.Node, cmd []string) (*types.Hi
Cmd: cmd,
})
if err != nil {
return nil, fmt.Errorf("Failed to create exec config for node '%s': %+v", node.Name, err)
return nil, fmt.Errorf("docker failed to create exec config for node '%s': %+v", node.Name, err)
}
execConnection, err := docker.ContainerExecAttach(ctx, exec.ID, types.ExecStartCheck{
Tty: true,
})
if err != nil {
l.Log().Errorf("Failed to connect to exec process in node '%s'", node.Name)
return nil, err
return nil, fmt.Errorf("docker failed to attach to exec process in node '%s': %w", node.Name, err)
}
if err := docker.ContainerExecStart(ctx, exec.ID, types.ExecStartCheck{Tty: true}); err != nil {
l.Log().Errorf("Failed to start exec process in node '%s'", node.Name)
return nil, err
return nil, fmt.Errorf("docker failed to start exec process in node '%s': %w", node.Name, err)
}
for {
// get info about exec process inside container
execInfo, err := docker.ContainerExecInspect(ctx, exec.ID)
if err != nil {
l.Log().Errorf("Failed to inspect exec process in node '%s'", node.Name)
return &execConnection, err
return &execConnection, fmt.Errorf("docker failed to inspect exec process in node '%s': %w", node.Name, err)
}
// if still running, continue loop
@ -416,14 +401,13 @@ func (d Docker) GetNodesInNetwork(ctx context.Context, network string) ([]*k3d.N
// create docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return nil, err
return nil, fmt.Errorf("failed to create docker client: %w", err)
}
defer docker.Close()
net, err := GetNetwork(ctx, network)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get network '%s': %w", network, err)
}
connectedNodes := []*k3d.Node{}
@ -432,7 +416,7 @@ func (d Docker) GetNodesInNetwork(ctx context.Context, network string) ([]*k3d.N
for cID := range net.Containers {
containerDetails, err := getContainerDetails(ctx, cID)
if err != nil {
return nil, err
return nil, fmt.Errorf("docker failed to get details of container '%s': %w", cID, err)
}
node, err := TranslateContainerDetailsToNode(containerDetails)
if err != nil {
@ -440,7 +424,7 @@ func (d Docker) GetNodesInNetwork(ctx context.Context, network string) ([]*k3d.N
l.Log().Tracef("GetNodesInNetwork: inspected non-k3d-managed container %s", containerDetails.Name)
continue
}
return nil, err
return nil, fmt.Errorf("failed to translate container '%s' details to node spec: %w", containerDetails.Name, err)
}
connectedNodes = append(connectedNodes, node)
}
@ -452,14 +436,13 @@ func (d Docker) RenameNode(ctx context.Context, node *k3d.Node, newName string)
// get the container for the given node
container, err := getNodeContainer(ctx, node)
if err != nil {
return err
return fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
}
// create docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return err
return fmt.Errorf("failed to get docker client: %w", err)
}
defer docker.Close()

@ -57,28 +57,24 @@ func (d Docker) CopyToNode(ctx context.Context, src string, dest string, node *k
// create docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return err
return fmt.Errorf("failed to get docker client: %w", err)
}
defer docker.Close()
container, err := getNodeContainer(ctx, node)
if err != nil {
l.Log().Errorf("Failed to find container for target node '%s'", node.Name)
return err
return fmt.Errorf("failed to find container for target node '%s': %w", node.Name, err)
}
// source: docker/cli/cli/command/container/cp
srcInfo, err := archive.CopyInfoSourcePath(src, false)
if err != nil {
l.Log().Errorln("Failed to copy info source path")
return err
return fmt.Errorf("failed to copy info source path: %w", err)
}
srcArchive, err := archive.TarResource(srcInfo)
if err != nil {
l.Log().Errorln("Failed to create tar resource")
return err
return fmt.Errorf("failed to create tar resource: %w", err)
}
defer srcArchive.Close()
@ -90,8 +86,7 @@ func (d Docker) CopyToNode(ctx context.Context, src string, dest string, node *k
destDir, preparedArchive, err := archive.PrepareArchiveCopy(srcArchive, srcInfo, destInfo)
if err != nil {
l.Log().Errorln("Failed to prepare archive")
return err
return fmt.Errorf("failed to prepare archive: %w", err)
}
defer preparedArchive.Close()
@ -109,8 +104,7 @@ func (d Docker) WriteToNode(ctx context.Context, content []byte, dest string, mo
// create docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return err
return fmt.Errorf("failed to get docker client: %w", err)
}
defer docker.Close()
@ -148,12 +142,12 @@ func (d Docker) ReadFromNode(ctx context.Context, path string, node *k3d.Node) (
l.Log().Tracef("Reading path %s from node %s...", path, node.Name)
nodeContainer, err := getNodeContainer(ctx, node)
if err != nil {
return nil, fmt.Errorf("Failed to find container for node '%s': %+v", node.Name, err)
return nil, fmt.Errorf("failed to find container for node '%s': %w", node.Name, err)
}
docker, err := GetDockerClient()
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get docker client: %w", err)
}
reader, _, err := docker.CopyFromContainer(ctx, nodeContainer.ID, path)
@ -161,7 +155,7 @@ func (d Docker) ReadFromNode(ctx context.Context, path string, node *k3d.Node) (
if client.IsErrNotFound(err) {
return nil, errors.Wrap(runtimeErrors.ErrRuntimeFileNotFound, err.Error())
}
return nil, err
return nil, fmt.Errorf("failed to copy path '%s' from container '%s': %w", path, nodeContainer.ID, err)
}
return reader, err
@ -171,7 +165,7 @@ func (d Docker) ReadFromNode(ctx context.Context, path string, node *k3d.Node) (
func GetDockerClient() (*client.Client, error) {
dockerCli, err := command.NewDockerCli(command.WithStandardStreams())
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to create new docker CLI with standard streams: %w", err)
}
newClientOpts := flags.NewClientOptions()
@ -179,7 +173,7 @@ func GetDockerClient() (*client.Client, error) {
err = dockerCli.Initialize(newClientOpts)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to initialize docker CLI: %w", err)
}
// check for TLS Files used for protected connections
@ -187,7 +181,7 @@ func GetDockerClient() (*client.Client, error) {
storageInfo := dockerCli.ContextStore().GetStorageInfo(currentContext)
tlsFilesMap, err := dockerCli.ContextStore().ListTLSFiles(currentContext)
if err != nil {
return nil, err
return nil, fmt.Errorf("docker CLI failed to list TLS files for context '%s': %w", currentContext, err)
}
endpointDriver := "docker"
tlsFiles := tlsFilesMap[endpointDriver]
@ -198,7 +192,7 @@ func GetDockerClient() (*client.Client, error) {
if ep.Host != "" {
clientopts, err := ep.ClientOpts()
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to get client opts for docker endpoint: %w", err)
}
headers := make(map[string]string, 1)
headers["User-Agent"] = command.UserAgent()

@ -36,8 +36,7 @@ func (d Docker) CreateVolume(ctx context.Context, name string, labels map[string
// (0) create new docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return err
return fmt.Errorf("failed to get docker client: %w", err)
}
defer docker.Close()
@ -58,8 +57,7 @@ func (d Docker) CreateVolume(ctx context.Context, name string, labels map[string
vol, err := docker.VolumeCreate(ctx, volumeCreateOptions)
if err != nil {
l.Log().Errorf("Failed to create volume '%s'", name)
return err
return fmt.Errorf("failed to create volume '%s': %w", name, err)
}
l.Log().Infof("Created volume '%s'", vol.Name)
return nil
@ -70,30 +68,26 @@ func (d Docker) DeleteVolume(ctx context.Context, name string) error {
// (0) create new docker client
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return err
return fmt.Errorf("failed to get docker client: %w", err)
}
defer docker.Close()
// get volume and delete it
vol, err := docker.VolumeInspect(ctx, name)
if err != nil {
l.Log().Errorf("Failed to find volume '%s'", name)
return err
return fmt.Errorf("failed to find volume '%s': %w", name, err)
}
// check if volume is still in use
if vol.UsageData != nil {
if vol.UsageData.RefCount > 0 {
l.Log().Errorf("Failed to delete volume '%s'", vol.Name)
return fmt.Errorf("Volume '%s' is still referenced by %d containers", name, vol.UsageData.RefCount)
return fmt.Errorf("failed to delete volume '%s' as it is still referenced by %d containers", name, vol.UsageData.RefCount)
}
}
// remove volume
if err := docker.VolumeRemove(ctx, name, true); err != nil {
l.Log().Errorf("Failed to delete volume '%s'", name)
return err
return fmt.Errorf("docker failed to delete volume '%s': %w", name, err)
}
return nil
@ -105,8 +99,7 @@ func (d Docker) GetVolume(name string) (string, error) {
ctx := context.Background()
docker, err := GetDockerClient()
if err != nil {
l.Log().Errorln("Failed to create docker client")
return "", err
return "", fmt.Errorf("failed to get docker client: %w", err)
}
defer docker.Close()
@ -114,10 +107,10 @@ func (d Docker) GetVolume(name string) (string, error) {
filters.Add("name", fmt.Sprintf("^%s$", name))
volumeList, err := docker.VolumeList(ctx, filters)
if err != nil {
return "", err
return "", fmt.Errorf("docker failed to list volumes: %w", err)
}
if len(volumeList.Volumes) < 1 {
return "", fmt.Errorf("Failed to find named volume '%s'", name)
return "", fmt.Errorf("failed to find named volume '%s'", name)
}
return volumeList.Volumes[0].Name, nil

@ -93,12 +93,12 @@ func ValidateVolumeMount(runtime runtimes.Runtime, volumeMount string) error {
// verifyNamedVolume checks whether a named volume exists in the runtime
func verifyNamedVolume(runtime runtimes.Runtime, volumeName string) error {
volumeName, err := runtime.GetVolume(volumeName)
foundVolName, err := runtime.GetVolume(volumeName)
if err != nil {
return err
return fmt.Errorf("runtime failed to get volume '%s': %w", volumeName, err)
}
if volumeName == "" {
return fmt.Errorf("Failed to find named volume '%s'", volumeName)
if foundVolName == "" {
return fmt.Errorf("failed to find named volume '%s'", volumeName)
}
return nil
}

@ -22,11 +22,11 @@ THE SOFTWARE.
package util
import (
"fmt"
"os"
"path"
homedir "github.com/mitchellh/go-homedir"
l "github.com/rancher/k3d/v4/pkg/logger"
)
// GetConfigDirOrCreate will return the base path of the k3d config directory or create it if it doesn't exist yet
@ -36,15 +36,13 @@ func GetConfigDirOrCreate() (string, error) {
// build the path
homeDir, err := homedir.Dir()
if err != nil {
l.Log().Errorln("Failed to get user's home directory")
return "", err
return "", fmt.Errorf("failed to get user's home directory: %w", err)
}
configDir := path.Join(homeDir, ".k3d")
// create directories if necessary
if err := createDirIfNotExists(configDir); err != nil {
l.Log().Errorf("Failed to create config path '%s'", configDir)
return "", err
return "", fmt.Errorf("failed to create config directory '%s': %w", configDir, err)
}
return configDir, nil

@ -83,7 +83,7 @@ func FilterNodesWithSuffix(nodes []*k3d.Node, nodefilters []string) (map[string]
filteredNodes, err := FilterNodes(nodes, []string{nf})
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to filder nodes by filter '%s': %w", nf, err)
}
l.Log().Tracef("Filtered %d nodes for suffix '%s' (filter: %s)", len(filteredNodes), suffix, nf)

@ -28,7 +28,6 @@ import (
"strings"
dockerunits "github.com/docker/go-units"
l "github.com/rancher/k3d/v4/pkg/logger"
)
const (
@ -58,14 +57,13 @@ func GetNodeFakerDirOrCreate(name string) (string, error) {
// this folder needs to be kept across reboots, keep it in ~/.k3d
configdir, err := GetConfigDirOrCreate()
if err != nil {
return "", err
return "", fmt.Errorf("failed to get config directory: %w", err)
}
fakeDir := path.Join(configdir, fmt.Sprintf(".%s", name))
// create directories if necessary
if err := createDirIfNotExists(fakeDir); err != nil {
l.Log().Errorf("Failed to create fake files path '%s'", fakeDir)
return "", err
return "", fmt.Errorf("failed to create fake files path '%s': %w", fakeDir, err)
}
return fakeDir, nil
@ -83,12 +81,12 @@ func GetFakeMeminfoPathForName(nodeName string) (string, error) {
func MakeFakeMeminfo(memoryBytes int64, nodeName string) (string, error) {
fakeMeminfoPath, err := GetFakeMeminfoPathForName(nodeName)
if err != nil {
return "", err
return "", fmt.Errorf("failed to get fake meminfo path for node '%s': %w", nodeName, err)
}
fakememinfo, err := os.Create(fakeMeminfoPath)
defer fakememinfo.Close()
if err != nil {
return "", err
return "", fmt.Errorf("failed to create fake meminfo path '%s': %w", fakeMeminfoPath, err)
}
// write content, must be kB
@ -96,7 +94,7 @@ func MakeFakeMeminfo(memoryBytes int64, nodeName string) (string, error) {
content := meminfoContent(memoryKb)
_, err = fakememinfo.WriteString(content)
if err != nil {
return "", err
return "", fmt.Errorf("failed to write fake meminfo file: %w", err)
}
return fakememinfo.Name(), nil
@ -107,13 +105,12 @@ func MakeFakeMeminfo(memoryBytes int64, nodeName string) (string, error) {
func MakeFakeEdac(nodeName string) (string, error) {
dir, err := GetNodeFakerDirOrCreate(nodeName)
if err != nil {
return "", err
return "", fmt.Errorf("failed to get or create fake files dir for node '%s': %w", nodeName, err)
}
edacPath := path.Join(dir, "edac")
// create directories if necessary
if err := createDirIfNotExists(edacPath); err != nil {
l.Log().Errorf("Failed to create fake edac path '%s'", edacPath)
return "", err
return "", fmt.Errorf("failed to create fake edac path '%s': %w", edacPath, err)
}
return edacPath, nil
@ -124,7 +121,7 @@ func fakeInfoPathForName(infoType string, nodeName string) (string, error) {
// this file needs to be kept across reboots, keep it in ~/.k3d
dir, err := GetNodeFakerDirOrCreate(nodeName)
if err != nil {
return "", err
return "", fmt.Errorf("failed to get or create fake files dir for node '%s': %w", nodeName, err)
}
return path.Join(dir, infoType), nil
}

@ -23,24 +23,22 @@ THE SOFTWARE.
package util
import (
"fmt"
"net"
"github.com/docker/go-connections/nat"
l "github.com/rancher/k3d/v4/pkg/logger"
)
// GetFreePort tries to fetch an open port from the OS-Kernel
func GetFreePort() (int, error) {
tcpAddress, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil {
l.Log().Errorln("Failed to resolve address")
return 0, err
return 0, fmt.Errorf("failed to resolve address 'localhost:0': %w", err)
}
tcpListener, err := net.ListenTCP("tcp", tcpAddress)
if err != nil {
l.Log().Errorln("Failed to create TCP Listener")
return 0, err
return 0, fmt.Errorf("failed to create tcp listener: %w", err)
}
defer tcpListener.Close()

@ -22,6 +22,7 @@ THE SOFTWARE.
package version
import (
"fmt"
"os"
"strings"
@ -85,12 +86,12 @@ func fetchLatestK3sVersion() (string, error) {
hub, err := registry.New(url, username, password)
if err != nil {
return "", err
return "", fmt.Errorf("failed to create new registry instance from URL '%s': %w", url, err)
}
tags, err := hub.Tags(repository)
if err != nil || len(tags) == 0 {
return "", err
return "", fmt.Errorf("failed to list tags from repository with URL '%s': %w", url, err)
}
l.Log().Debugln("Fetched the following tags for rancher/k3s from DockerHub:")

Loading…
Cancel
Save