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) exposeAPI, err = cliutil.ParsePortExposureSpec(ppViper.GetString("cli.api-port"), k3d.DefaultAPIPort)
if err != nil { 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) portMapping, err := nat.ParsePortSpec(realPortString)
if err != nil { 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 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) { func GetFreePort() (int, error) {
tcpAddress, err := net.ResolveTCPAddr("tcp", "localhost:0") tcpAddress, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil { if err != nil {
l.Log().Errorln("Failed to resolve address") return 0, fmt.Errorf("failed to resolve address 'localhost:0': %w", err)
return 0, err
} }
tcpListener, err := net.ListenTCP("tcp", tcpAddress) tcpListener, err := net.ListenTCP("tcp", tcpAddress)
if err != nil { if err != nil {
l.Log().Errorln("Failed to create TCP Listener") return 0, fmt.Errorf("failed to create tcp listener: %w", err)
return 0, err
} }
defer tcpListener.Close() defer tcpListener.Close()

@ -98,7 +98,7 @@ func ValidateVolumeMount(runtime runtimes.Runtime, volumeMount string) (string,
func verifyNamedVolume(runtime runtimes.Runtime, volumeName string) error { func verifyNamedVolume(runtime runtimes.Runtime, volumeName string) error {
volumeName, err := runtime.GetVolume(volumeName) volumeName, err := runtime.GetVolume(volumeName)
if err != nil { if err != nil {
return err return fmt.Errorf("Failed to verify named volume: %w", err)
} }
if volumeName == "" { if volumeName == "" {
return fmt.Errorf("Failed to find named volume '%s'", 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) _, err := EnsureToolsNode(ctx, runtime, &clusterConfig.Cluster)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to ensure k3d-tools node: %w", err)
} }
envInfo, err := GatherEnvironmentInfo(ctx, runtime, &clusterConfig.Cluster) envInfo, err := GatherEnvironmentInfo(ctx, runtime, &clusterConfig.Cluster)
if err != nil { 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) regFromNode, err := RegistryFromNode(regNode)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to translate node to registry spec: %w", err)
} }
*reg = *regFromNode *reg = *regFromNode
} }
@ -273,8 +273,7 @@ func ClusterPrepNetwork(ctx context.Context, runtime k3drt.Runtime, cluster *k3d
// create cluster network or use an existing one // create cluster network or use an existing one
network, networkExists, err := runtime.CreateNetworkIfNotPresent(ctx, &cluster.Network) network, networkExists, err := runtime.CreateNetworkIfNotPresent(ctx, &cluster.Network)
if err != nil { if err != nil {
l.Log().Errorln("Failed to create cluster network") return fmt.Errorf("failed to create cluster network: %w", err)
return err
} }
cluster.Network = *network cluster.Network = *network
clusterCreateOpts.GlobalLabels[k3d.LabelNetworkID] = network.ID 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) 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 { 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 fmt.Errorf("failed to create image volume '%s' for cluster '%s': %w", imageVolumeName, cluster.Name, err)
return err
} }
clusterCreateOpts.GlobalLabels[k3d.LabelImageVolume] = imageVolumeName clusterCreateOpts.GlobalLabels[k3d.LabelImageVolume] = imageVolumeName
@ -400,7 +398,7 @@ ClusterCreatOpts:
if cluster.Network.IPAM.Managed { if cluster.Network.IPAM.Managed {
ip, err := GetIP(ctx, runtime, &cluster.Network) ip, err := GetIP(ctx, runtime, &cluster.Network)
if err != nil { 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 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 node.IP.Static = true
@ -427,8 +425,7 @@ ClusterCreatOpts:
// create node // create node
l.Log().Infof("Creating node '%s'", node.Name) l.Log().Infof("Creating node '%s'", node.Name)
if err := NodeCreate(clusterCreateCtx, runtime, node, k3d.NodeCreateOpts{}); err != nil { if err := NodeCreate(clusterCreateCtx, runtime, node, k3d.NodeCreateOpts{}); err != nil {
l.Log().Errorln("Failed to create node") return fmt.Errorf("failed to create node: %w", err)
return err
} }
l.Log().Debugf("Created node '%s'", node.Name) l.Log().Debugf("Created node '%s'", node.Name)
@ -458,7 +455,7 @@ ClusterCreatOpts:
} }
if err := nodeSetup(cluster.InitNode); err != nil { if err := nodeSetup(cluster.InitNode); err != nil {
return err return fmt.Errorf("failed init node setup: %w", err)
} }
serverCount++ serverCount++
@ -486,7 +483,7 @@ ClusterCreatOpts:
} }
if node.Role == k3d.ServerRole || node.Role == k3d.AgentRole { if node.Role == k3d.ServerRole || node.Role == k3d.AgentRole {
if err := nodeSetup(node); err != nil { 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 { if cluster.ServerLoadBalancer == nil {
lbNode, err := LoadbalancerPrepare(ctx, runtime, cluster, &k3d.LoadbalancerCreateOpts{Labels: clusterCreateOpts.GlobalLabels}) lbNode, err := LoadbalancerPrepare(ctx, runtime, cluster, &k3d.LoadbalancerCreateOpts{Labels: clusterCreateOpts.GlobalLabels})
if err != nil { 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 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 // prepare to write config to lb container
configyaml, err := yaml.Marshal(cluster.ServerLoadBalancer.Config) configyaml, err := yaml.Marshal(cluster.ServerLoadBalancer.Config)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to marshal loadbalancer config: %w", err)
} }
writeLbConfigAction := k3d.NodeHook{ writeLbConfigAction := k3d.NodeHook{
@ -542,7 +539,6 @@ ClusterCreatOpts:
return fmt.Errorf("error creating loadbalancer: %v", err) return fmt.Errorf("error creating loadbalancer: %v", err)
} }
l.Log().Debugf("Created loadbalancer '%s'", cluster.ServerLoadBalancer.Node.Name) l.Log().Debugf("Created loadbalancer '%s'", cluster.ServerLoadBalancer.Node.Name)
return err
} }
return nil 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) l.Log().Infof("Deleting cluster '%s'", cluster.Name)
cluster, err := ClusterGet(ctx, runtime, cluster) cluster, err := ClusterGet(ctx, runtime, cluster)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to get cluster %s: %w", cluster.Name, err)
} }
l.Log().Debugf("Cluster Details: %+v", cluster) 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...") l.Log().Traceln("Listing Clusters...")
nodes, err := runtime.GetNodesByLabel(ctx, k3d.DefaultRuntimeLabels) nodes, err := runtime.GetNodesByLabel(ctx, k3d.DefaultRuntimeLabels)
if err != nil { if err != nil {
l.Log().Errorln("Failed to get clusters") return nil, fmt.Errorf("runtime failed to list nodes: %w", err)
return nil, err
} }
l.Log().Debugf("Found %d nodes", len(nodes)) 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 { if err := populateClusterFieldsFromLabels(cluster); err != nil {
l.Log().Warnf("Failed to populate cluster fields from node labels") l.Log().Warnf("Failed to populate cluster fields from node labels: %v", err)
l.Log().Warnln(err)
} }
return cluster, nil 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 // add /etc/hosts and CoreDNS entry for host.k3d.internal, referring to the host system
if err := prepInjectHostIP(ctx, runtime, cluster); err != nil { 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 // create host records in CoreDNS for external registries
if err := prepCoreDNSInjectNetworkMembers(ctx, runtime, cluster); err != nil { if err := prepCoreDNSInjectNetworkMembers(ctx, runtime, cluster); err != nil {
return err return fmt.Errorf("failed to patch CoreDNS with network members: %w", err)
} }
return nil return nil
@ -970,6 +964,8 @@ func ClusterStop(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Cluste
if failed > 0 { if failed > 0 {
return fmt.Errorf("Failed to stop %d nodes: Try to stop them manually", failed) return fmt.Errorf("Failed to stop %d nodes: Try to stop them manually", failed)
} }
l.Log().Infof("Stopped cluster '%s'", cluster.Name)
return nil return nil
} }
@ -1107,7 +1103,7 @@ func ClusterEditChangesetSimple(ctx context.Context, runtime k3drt.Runtime, clus
for _, portWithNodeFilters := range changeset.Ports { for _, portWithNodeFilters := range changeset.Ports {
filteredNodes, err := util.FilterNodesWithSuffix(nodeList, portWithNodeFilters.NodeFilters) filteredNodes, err := util.FilterNodesWithSuffix(nodeList, portWithNodeFilters.NodeFilters)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to filter nodes: %w", err)
} }
for suffix := range filteredNodes { for suffix := range filteredNodes {
@ -1132,7 +1128,7 @@ func ClusterEditChangesetSimple(ctx context.Context, runtime k3drt.Runtime, clus
// prepare to write config to lb container // prepare to write config to lb container
configyaml, err := yaml.Marshal(lbChangeset.Config) configyaml, err := yaml.Marshal(lbChangeset.Config)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to marshal loadbalancer config changeset: %w", err)
} }
writeLbConfigAction := k3d.NodeHook{ writeLbConfigAction := k3d.NodeHook{
Stage: k3d.LifecycleStagePreStart, Stage: k3d.LifecycleStagePreStart,

@ -23,6 +23,7 @@ package client
import ( import (
"context" "context"
"fmt"
"github.com/rancher/k3d/v4/pkg/runtimes" "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) hostIP, err := GetHostIP(ctx, runtime, cluster)
if err != nil { if err != nil {
return envInfo, err return envInfo, fmt.Errorf("failed to get host IP: %w", err)
} }
envInfo.HostGateway = hostIP envInfo.HostGateway = hostIP

@ -50,7 +50,7 @@ func GetHostIP(ctx context.Context, rtime rt.Runtime, cluster *k3d.Cluster) (net
if runtime.GOOS == "linux" { if runtime.GOOS == "linux" {
ip, err := rtime.GetHostIP(ctx, cluster.Network.Name) ip, err := rtime.GetHostIP(ctx, cluster.Network.Name)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("runtime failed to get host IP: %w", err)
} }
return ip, nil 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) toolsNode, err := EnsureToolsNode(ctx, rtime, cluster)
if err != nil { 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") ip, err := resolveHostnameFromInside(ctx, rtime, toolsNode, "host.docker.internal")
if err != nil { 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 return ip, nil
} }

@ -23,6 +23,7 @@ package client
import ( import (
"context" "context"
"fmt"
l "github.com/rancher/k3d/v4/pkg/logger" l "github.com/rancher/k3d/v4/pkg/logger"
k3drt "github.com/rancher/k3d/v4/pkg/runtimes" k3drt "github.com/rancher/k3d/v4/pkg/runtimes"
@ -30,11 +31,12 @@ import (
"inet.af/netaddr" "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) { func GetIP(ctx context.Context, runtime k3drt.Runtime, network *k3d.ClusterNetwork) (netaddr.IP, error) {
network, err := runtime.GetNetwork(ctx, network) network, err := runtime.GetNetwork(ctx, network)
if err != nil { 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 var ipsetbuilder netaddr.IPSetBuilder

@ -53,14 +53,14 @@ func KubeconfigGetWrite(ctx context.Context, runtime runtimes.Runtime, cluster *
// get kubeconfig from cluster node // get kubeconfig from cluster node
kubeconfig, err := KubeconfigGet(ctx, runtime, cluster) kubeconfig, err := KubeconfigGet(ctx, runtime, cluster)
if err != nil { 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 // empty output parameter = write to default
if output == "" { if output == "" {
output, err = KubeconfigGetDefaultPath() output, err = KubeconfigGetDefaultPath()
if err != nil { 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 // create directory path
if err := os.MkdirAll(filepath.Dir(output), 0755); err != nil { if err := os.MkdirAll(filepath.Dir(output), 0755); err != nil {
l.Log().Errorf("Failed to create output directory '%s'", filepath.Dir(output)) return output, fmt.Errorf("failed to create output directory '%s': %w", filepath.Dir(output), err)
return output, err
} }
// try create output file // try create output file
f, err := os.Create(output) f, err := os.Create(output)
if err != nil { if err != nil {
l.Log().Errorf("Failed to create output file '%s'", output) return output, fmt.Errorf("failed to create output file '%s': %w", output, err)
return output, err
} }
f.Close() f.Close()
@ -98,8 +96,7 @@ func KubeconfigGetWrite(ctx context.Context, runtime runtimes.Runtime, cluster *
firstRun = false firstRun = false
continue continue
} }
l.Log().Errorf("Failed to open output file '%s' or it's not a KubeConfig", output) return output, fmt.Errorf("failed to open output file '%s' or it's not a kubeconfig: %w", output, err)
return output, err
} }
break 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 // 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)}) serverNodes, err := runtime.GetNodesByLabel(ctx, map[string]string{k3d.LabelClusterName: cluster.Name, k3d.LabelRole: string(k3d.ServerRole)})
if err != nil { if err != nil {
l.Log().Errorln("Failed to get server nodes") return nil, fmt.Errorf("runtime failed to get server nodes for cluster '%s': %w", cluster.Name, err)
return nil, err
} }
if len(serverNodes) == 0 { 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 // 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 // get the kubeconfig from the first server node
reader, err := runtime.GetKubeconfig(ctx, chosenServer) reader, err := runtime.GetKubeconfig(ctx, chosenServer)
if err != nil { if err != nil {
l.Log().Errorf("Failed to get kubeconfig from node '%s'", chosenServer.Name) return nil, fmt.Errorf("runtime failed to pull kubeconfig from node '%s': %w", chosenServer.Name, err)
return nil, err
} }
defer reader.Close() defer reader.Close()
readBytes, err := ioutil.ReadAll(reader) readBytes, err := ioutil.ReadAll(reader)
if err != nil { if err != nil {
l.Log().Errorln("Couldn't read kubeconfig file") return nil, fmt.Errorf("failed to read kubeconfig file: %w", err)
return nil, err
} }
// drop the first 512 bytes which contain file metadata/control characters // 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) kc, err := clientcmd.Load(trimBytes)
if err != nil { if err != nil {
l.Log().Errorln("Failed to parse the KubeConfig") return nil, fmt.Errorf("failed to parse kubeconfig: %w", err)
return nil, err
} }
// update the server URL // update the server URL
@ -212,22 +205,19 @@ func KubeconfigWriteToPath(ctx context.Context, kubeconfig *clientcmdapi.Config,
} else { } else {
output, err = os.Create(path) output, err = os.Create(path)
if err != nil { if err != nil {
l.Log().Errorf("Failed to create file '%s'", path) return fmt.Errorf("failed to create file '%s': %w", path, err)
return err
} }
defer output.Close() defer output.Close()
} }
kubeconfigBytes, err := clientcmd.Write(*kubeconfig) kubeconfigBytes, err := clientcmd.Write(*kubeconfig)
if err != nil { if err != nil {
l.Log().Errorln("Failed to write KubeConfig") return fmt.Errorf("failed to write kubeconfig: %w", err)
return err
} }
_, err = output.Write(kubeconfigBytes) _, err = output.Write(kubeconfigBytes)
if err != nil { if err != nil {
l.Log().Errorf("Failed to write to file '%s'", output.Name()) return fmt.Errorf("failed to write file '%s': %w", output.Name(), err)
return err
} }
l.Log().Debugf("Wrote kubeconfig to '%s'", output.Name()) 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 { func KubeconfigWrite(ctx context.Context, kubeconfig *clientcmdapi.Config, path string) error {
tempPath := fmt.Sprintf("%s.k3d_%s", path, time.Now().Format("20060102_150405.000000")) tempPath := fmt.Sprintf("%s.k3d_%s", path, time.Now().Format("20060102_150405.000000"))
if err := clientcmd.WriteToFile(*kubeconfig, tempPath); err != nil { if err := clientcmd.WriteToFile(*kubeconfig, tempPath); err != nil {
l.Log().Errorf("Failed to write merged kubeconfig to temporary file '%s'", tempPath) return fmt.Errorf("failed to write merged kubeconfig to temporary file '%s': %w", tempPath, err)
return err
} }
// Move temporary file over existing KubeConfig // Move temporary file over existing KubeConfig
if err := os.Rename(tempPath, path); err != nil { if err := os.Rename(tempPath, path); err != nil {
l.Log().Errorf("Failed to overwrite existing KubeConfig '%s' with new KubeConfig '%s'", path, tempPath) return fmt.Errorf("failed to overwrite existing KubeConfig '%s' with new kubeconfig '%s': %w", path, tempPath, err)
return err
} }
l.Log().Debugf("Wrote kubeconfig to '%s'", path) 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) { func KubeconfigGetDefaultFile() (*clientcmdapi.Config, error) {
path, err := KubeconfigGetDefaultPath() path, err := KubeconfigGetDefaultPath()
if err != nil { 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) l.Log().Debugf("Using default kubeconfig '%s'", path)
return clientcmd.LoadFromFile(path) return clientcmd.LoadFromFile(path)
@ -314,7 +302,7 @@ func KubeconfigGetDefaultFile() (*clientcmdapi.Config, error) {
func KubeconfigGetDefaultPath() (string, error) { func KubeconfigGetDefaultPath() (string, error) {
defaultKubeConfigLoadingRules := clientcmd.NewDefaultClientConfigLoadingRules() defaultKubeConfigLoadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
if len(defaultKubeConfigLoadingRules.GetLoadingPrecedence()) > 1 { 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 return defaultKubeConfigLoadingRules.GetDefaultFilename(), nil
} }
@ -323,11 +311,11 @@ func KubeconfigGetDefaultPath() (string, error) {
func KubeconfigRemoveClusterFromDefaultConfig(ctx context.Context, cluster *k3d.Cluster) error { func KubeconfigRemoveClusterFromDefaultConfig(ctx context.Context, cluster *k3d.Cluster) error {
defaultKubeConfigPath, err := KubeconfigGetDefaultPath() defaultKubeConfigPath, err := KubeconfigGetDefaultPath()
if err != nil { if err != nil {
return err return fmt.Errorf("failed to get default kubeconfig path: %w", err)
} }
kubeconfig, err := KubeconfigGetDefaultFile() kubeconfig, err := KubeconfigGetDefaultFile()
if err != nil { if err != nil {
return err return fmt.Errorf("failed to get default kubeconfig file: %w", err)
} }
kubeconfig = KubeconfigRemoveCluster(ctx, cluster, kubeconfig) kubeconfig = KubeconfigRemoveCluster(ctx, cluster, kubeconfig)
return KubeconfigWrite(ctx, kubeconfig, defaultKubeConfigPath) 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 // update cluster details to ensure that we have the latest node list
cluster, err = ClusterGet(ctx, runtime, cluster) cluster, err = ClusterGet(ctx, runtime, cluster)
if err != nil { if err != nil {
l.Log().Errorf("Failed to update details for cluster '%s'", cluster.Name) return fmt.Errorf("failed to update details for cluster '%s': %w", cluster.Name, err)
return err
} }
currentConfig, err := GetLoadbalancerConfig(ctx, runtime, cluster) currentConfig, err := GetLoadbalancerConfig(ctx, runtime, cluster)
@ -120,7 +119,7 @@ func GetLoadbalancerConfig(ctx context.Context, runtime runtimes.Runtime, cluste
var err error var err error
cluster.ServerLoadBalancer.Node, err = NodeGet(ctx, runtime, node) cluster.ServerLoadBalancer.Node, err = NodeGet(ctx, runtime, node)
if err != nil { 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) reader, err := runtime.ReadFromNode(ctx, types.DefaultLoadbalancerConfigPath, cluster.ServerLoadBalancer.Node)
if err != nil { 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() defer reader.Close()
file, err := ioutil.ReadAll(reader) file, err := ioutil.ReadAll(reader)
if err != nil { 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. file = bytes.Trim(file[512:], "\x00") // trim control characters, etc.
@ -213,7 +212,7 @@ func loadbalancerAddPortConfigs(loadbalancer *k3d.Loadbalancer, portmapping nat.
nodenames := []string{} nodenames := []string{}
for _, node := range targetNodes { for _, node := range targetNodes {
if node.Role == k3d.LoadBalancerRole { 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) nodenames = append(nodenames, node.Name)
} }

@ -56,8 +56,7 @@ func NodeAddToCluster(ctx context.Context, runtime runtimes.Runtime, node *k3d.N
targetClusterName := cluster.Name targetClusterName := cluster.Name
cluster, err := ClusterGet(ctx, runtime, cluster) cluster, err := ClusterGet(ctx, runtime, cluster)
if err != nil { if err != nil {
l.Log().Errorf("Failed to find specified cluster '%s'", targetClusterName) return fmt.Errorf("Failed to find specified cluster '%s': %w", targetClusterName, err)
return err
} }
// network // 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 // merge node config of new node into existing node config
if err := mergo.MergeWithOverwrite(srcNode, *node); err != nil { if err := mergo.MergeWithOverwrite(srcNode, *node); err != nil {
l.Log().Errorln("Failed to merge new node config into existing node config") return fmt.Errorf("failed to merge new node config into existing node config: %w", err)
return err
} }
node = srcNode node = srcNode
@ -203,7 +201,7 @@ func NodeAddToCluster(ctx context.Context, runtime runtimes.Runtime, node *k3d.N
node.State.Status = "" node.State.Status = ""
if err := NodeRun(ctx, runtime, node, createNodeOpts); err != nil { 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 // 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 // NodeRun creates and starts a node
func NodeRun(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, nodeCreateOpts k3d.NodeCreateOpts) error { func NodeRun(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, nodeCreateOpts k3d.NodeCreateOpts) error {
if err := NodeCreate(ctx, runtime, node, nodeCreateOpts); err != nil { 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{ 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, NodeHooks: nodeCreateOpts.NodeHooks,
EnvironmentInfo: nodeCreateOpts.EnvironmentInfo, EnvironmentInfo: nodeCreateOpts.EnvironmentInfo,
}); err != nil { }); err != nil {
return err return fmt.Errorf("failed to start node '%s': %w", node.Name, err)
} }
return nil 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 { if err := enableFixes(ctx, runtime, node, nodeStartOpts); err != nil {
return err return fmt.Errorf("failed to enable k3d fixes: %w", err)
} }
startTime := time.Now() 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) l.Log().Tracef("Starting node '%s'", node.Name)
if err := runtime.StartNode(ctx, node); err != nil { if err := runtime.StartNode(ctx, node); err != nil {
l.Log().Errorf("Failed to start node '%s'", node.Name) return fmt.Errorf("runtime failed to start node '%s': %w", node.Name, err)
return err
} }
if node.State.Started != "" { 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 // specify options depending on node role
if node.Role == k3d.AgentRole { // TODO: check here AND in CLI or only here? if node.Role == k3d.AgentRole { // TODO: check here AND in CLI or only here?
if err := patchAgentSpec(node); err != nil { 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 { } else if node.Role == k3d.ServerRole {
if err := patchServerSpec(node, runtime); err != nil { 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 * CREATION
*/ */
if err := runtime.CreateNode(ctx, node); err != nil { 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 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) { if !opts.SkipLBUpdate && (node.Role == k3d.ServerRole || node.Role == k3d.AgentRole) {
cluster, err := ClusterGet(ctx, runtime, &k3d.Cluster{Name: node.RuntimeLabels[k3d.LabelClusterName]}) cluster, err := ClusterGet(ctx, runtime, &k3d.Cluster{Name: node.RuntimeLabels[k3d.LabelClusterName]})
if err != nil { if err != nil {
l.Log().Errorf("Failed to find cluster for node '%s'", node.Name) return fmt.Errorf("failed fo find cluster for node '%s': %w", node.Name, err)
return err
} }
// if it's a server node, then update the loadbalancer configuration // if it's a server node, then update the loadbalancer configuration
if node.Role == k3d.ServerRole { if node.Role == k3d.ServerRole {
if err := UpdateLoadbalancerConfig(ctx, runtime, cluster); err != nil { if err := UpdateLoadbalancerConfig(ctx, runtime, cluster); err != nil {
if !errors.Is(err, ErrLBConfigHostNotFound) { 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) { func NodeList(ctx context.Context, runtime runtimes.Runtime) ([]*k3d.Node, error) {
nodes, err := runtime.GetNodesByLabel(ctx, k3d.DefaultRuntimeLabels) nodes, err := runtime.GetNodesByLabel(ctx, k3d.DefaultRuntimeLabels)
if err != nil { if err != nil {
l.Log().Errorln("Failed to get nodes") return nil, fmt.Errorf("failed to list nodes: %w", err)
return nil, err
} }
return nodes, nil return nodes, nil
@ -595,8 +590,7 @@ func NodeGet(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node) (*k3
// get node // get node
node, err := runtime.GetNode(ctx, node) node, err := runtime.GetNode(ctx, node)
if err != nil { if err != nil {
l.Log().Errorf("Failed to get node '%s'", node.Name) return nil, fmt.Errorf("failed to get node '%s': %w", node.Name, err)
return nil, err
} }
return node, nil 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}) result, err := CopyNode(ctx, existingNode, CopyNodeOpts{keepState: false})
if err != nil { 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 // prepare to write config to lb container
configyaml, err := yaml.Marshal(lbConfig) configyaml, err := yaml.Marshal(lbConfig)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to marshal loadbalancer config: %w", err)
} }
writeLbConfigAction := k3d.NodeHook{ writeLbConfigAction := k3d.NodeHook{
@ -778,7 +772,7 @@ func NodeReplace(ctx context.Context, runtime runtimes.Runtime, old, new *k3d.No
oldNameOriginal := old.Name oldNameOriginal := old.Name
l.Log().Infof("Renaming existing node %s to %s...", old.Name, oldNameTemp) l.Log().Infof("Renaming existing node %s to %s...", old.Name, oldNameTemp)
if err := runtime.RenameNode(ctx, old, oldNameTemp); err != nil { 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 old.Name = oldNameTemp
@ -794,7 +788,7 @@ func NodeReplace(ctx context.Context, runtime runtimes.Runtime, old, new *k3d.No
// stop existing/old node // stop existing/old node
l.Log().Infof("Stopping existing node %s...", old.Name) l.Log().Infof("Stopping existing node %s...", old.Name)
if err := runtime.StopNode(ctx, old); err != nil { 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 // start new node
@ -816,7 +810,7 @@ func NodeReplace(ctx context.Context, runtime runtimes.Runtime, old, new *k3d.No
// cleanup: delete old node // cleanup: delete old node
l.Log().Infof("Deleting old node %s...", old.Name) l.Log().Infof("Deleting old node %s...", old.Name)
if err := NodeDelete(ctx, runtime, old, k3d.NodeDeleteOpts{SkipLBUpdate: true}); err != nil { 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 // done
@ -831,7 +825,7 @@ func CopyNode(ctx context.Context, src *k3d.Node, opts CopyNodeOpts) (*k3d.Node,
targetCopy, err := copystruct.Copy(src) targetCopy, err := copystruct.Copy(src)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("failed to copy node struct: %w", err)
} }
result := targetCopy.(*k3d.Node) result := targetCopy.(*k3d.Node)
@ -841,5 +835,5 @@ func CopyNode(ctx context.Context, src *k3d.Node, opts CopyNodeOpts) (*k3d.Node,
result.State = k3d.NodeState{} 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 { for _, pm := range portmappings {
if err := loadbalancerAddPortConfigs(cluster.ServerLoadBalancer, pm, nodes); err != nil { 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" { } 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 nil, fmt.Errorf("Failed to start registry: %+v", err)
} }
return regNode, err return regNode, nil
} }
// RegistryCreate creates a registry node // RegistryCreate creates a registry node
@ -99,8 +99,7 @@ func RegistryCreate(ctx context.Context, runtime runtimes.Runtime, reg *k3d.Regi
// create the registry node // create the registry node
l.Log().Infof("Creating node '%s'", registryNode.Name) l.Log().Infof("Creating node '%s'", registryNode.Name)
if err := NodeCreate(ctx, runtime, registryNode, k3d.NodeCreateOpts{}); err != nil { if err := NodeCreate(ctx, runtime, registryNode, k3d.NodeCreateOpts{}); err != nil {
l.Log().Errorln("Failed to create registry node") return nil, fmt.Errorf("failed to create registry node '%s': %w", registryNode.Name, err)
return nil, err
} }
l.Log().Infof("Successfully created registry '%s'", registryNode.Name) 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 // find registry node
registryNode, err := NodeGet(ctx, runtime, registryNode) registryNode, err := NodeGet(ctx, runtime, registryNode)
if err != nil { if err != nil {
l.Log().Errorf("Failed to find registry node '%s'", registryNode.Name) return fmt.Errorf("Failed to find registry node '%s': %w", registryNode.Name, err)
return err
} }
// get cluster details and connect // get cluster details and connect
@ -148,8 +146,7 @@ func RegistryConnectNetworks(ctx context.Context, runtime runtimes.Runtime, regi
// find registry node // find registry node
registryNode, err := NodeGet(ctx, runtime, registryNode) registryNode, err := NodeGet(ctx, runtime, registryNode)
if err != nil { if err != nil {
l.Log().Errorf("Failed to find registry node '%s'", registryNode.Name) return fmt.Errorf("Failed to find registry node '%s': %w", registryNode.Name, err)
return err
} }
// get cluster details and connect // get cluster details and connect
@ -317,7 +314,7 @@ func RegistryGenerateLocalRegistryHostingConfigMapYAML(ctx context.Context, runt
}, },
) )
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("failed to marshal LocalRegistryHosting configmap data: %w", err)
} }
cm := configmap{ cm := configmap{
@ -334,7 +331,7 @@ func RegistryGenerateLocalRegistryHostingConfigMapYAML(ctx context.Context, runt
cmYaml, err := yaml.Marshal(cm) cmYaml, err := yaml.Marshal(cm)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("failed to marshal LocalRegistryHosting configmap: %w", err)
} }
l.Log().Tracef("LocalRegistryHostingConfigMapYaml: %s", string(cmYaml)) 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 // RegistryMergeConfig merges a source registry config into an existing dest registry cofnig
func RegistryMergeConfig(ctx context.Context, dest, src *k3s.Registry) error { func RegistryMergeConfig(ctx context.Context, dest, src *k3s.Registry) error {
if err := mergo.MergeWithOverwrite(dest, src); err != nil { 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 return nil
} }

@ -41,7 +41,7 @@ import (
func ImageImportIntoClusterMulti(ctx context.Context, runtime runtimes.Runtime, images []string, cluster *k3d.Cluster, opts k3d.ImageImportOpts) error { func ImageImportIntoClusterMulti(ctx context.Context, runtime runtimes.Runtime, images []string, cluster *k3d.Cluster, opts k3d.ImageImportOpts) error {
imagesFromRuntime, imagesFromTar, err := findImages(ctx, runtime, images) imagesFromRuntime, imagesFromTar, err := findImages(ctx, runtime, images)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to find images: %w", err)
} }
// no images found to load -> exit early // 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 // create tools node to export images
toolsNode, err := EnsureToolsNode(ctx, runtime, cluster) toolsNode, err := EnsureToolsNode(ctx, runtime, cluster)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to ensure that tools node is running: %w", err)
} }
/* TODO: /* TODO:
@ -70,8 +70,7 @@ func ImageImportIntoClusterMulti(ctx context.Context, runtime runtimes.Runtime,
l.Log().Infof("Saving %d image(s) from runtime...", len(imagesFromRuntime)) 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")) 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 { 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 fmt.Errorf("failed to save image(s) in tools container for cluster '%s': %w", cluster.Name, err)
return err
} }
importTarNames = append(importTarNames, tarName) importTarNames = append(importTarNames, tarName)
} }
@ -82,7 +81,7 @@ func ImageImportIntoClusterMulti(ctx context.Context, runtime runtimes.Runtime,
for _, file := range imagesFromTar { 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)) 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 { 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 continue
} }
importTarNames = append(importTarNames, tarName) 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) { go func(node *k3d.Node, wg *sync.WaitGroup, tarPath string) {
l.Log().Infof("Importing images from tarball '%s' into node '%s'...", tarPath, node.Name) 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 { 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().Errorf("failed to import images in node '%s': %v", node.Name, err)
l.Log().Errorln(err)
} }
wg.Done() wg.Done()
}(node, &importWaitgroup, tarName) }(node, &importWaitgroup, tarName)
@ -114,8 +112,7 @@ func ImageImportIntoClusterMulti(ctx context.Context, runtime runtimes.Runtime,
if !opts.KeepTar && len(importTarNames) > 0 { if !opts.KeepTar && len(importTarNames) > 0 {
l.Log().Infoln("Removing the tarball(s) from image volume...") l.Log().Infoln("Removing the tarball(s) from image volume...")
if err := runtime.ExecInNode(ctx, toolsNode, []string{"rm", "-f", strings.Join(importTarNames, " ")}); err != nil { 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().Errorf("failed to delete one or more tarballs from '%+v': %v", importTarNames, err)
l.Log().Errorln(err)
} }
} }
@ -123,7 +120,7 @@ func ImageImportIntoClusterMulti(ctx context.Context, runtime runtimes.Runtime,
if !opts.KeepToolsNode { if !opts.KeepToolsNode {
l.Log().Infoln("Removing k3d-tools node...") l.Log().Infoln("Removing k3d-tools node...")
if err := runtime.DeleteNode(ctx, toolsNode); err != nil { 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) { func findImages(ctx context.Context, runtime runtimeImageGetter, requestedImages []string) (imagesFromRuntime, imagesFromTar []string, err error) {
runtimeImages, err := runtime.GetImages(ctx) runtimeImages, err := runtime.GetImages(ctx)
if err != nil { if err != nil {
l.Log().Errorln("Failed to fetch list of existing images from runtime") return nil, nil, fmt.Errorf("failed to fetch list of existing images from runtime: %w", err)
return nil, nil, err
} }
for _, requestedImage := range requestedImages { 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) 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) { 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 node.RuntimeLabels[k3d.LabelClusterName] = cluster.Name
if err := NodeRun(ctx, runtime, node, k3d.NodeCreateOpts{}); err != nil { 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, fmt.Errorf("failed to run k3d-tools node for cluster '%s': %w", cluster.Name, err)
return node, err
} }
return node, nil return node, nil
@ -265,12 +260,11 @@ func EnsureToolsNode(ctx context.Context, runtime runtimes.Runtime, cluster *k3d
cluster, err = ClusterGet(ctx, runtime, cluster) cluster, err = ClusterGet(ctx, runtime, cluster)
if err != nil { if err != nil {
l.Log().Errorf("Failed to find the specified cluster") return nil, fmt.Errorf("failed to retrieve cluster '%s': %w", cluster.Name, err)
return nil, err
} }
if cluster.Network.Name == "" { 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 var imageVolume string

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

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

@ -23,6 +23,8 @@ THE SOFTWARE.
package config package config
import ( import (
"fmt"
"github.com/imdario/mergo" "github.com/imdario/mergo"
conf "github.com/rancher/k3d/v4/pkg/config/v1alpha3" conf "github.com/rancher/k3d/v4/pkg/config/v1alpha3"
l "github.com/rancher/k3d/v4/pkg/logger" 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) l.Log().Debugf("Merging %+v into %+v", src, dest)
if err := mergo.Merge(&dest, src); err != nil { 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 return &dest, nil

@ -167,7 +167,7 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
for _, volumeWithNodeFilters := range simpleConfig.Volumes { for _, volumeWithNodeFilters := range simpleConfig.Volumes {
nodes, err := util.FilterNodes(nodeList, volumeWithNodeFilters.NodeFilters) nodes, err := util.FilterNodes(nodeList, volumeWithNodeFilters.NodeFilters)
if err != nil { 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 { for _, node := range nodes {
@ -177,18 +177,18 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
// -> PORTS // -> PORTS
if err := client.TransformPorts(ctx, runtime, &newCluster, simpleConfig.Ports); err != nil { 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 // -> K3S NODE LABELS
for _, k3sNodeLabelWithNodeFilters := range simpleConfig.Options.K3sOptions.NodeLabels { for _, k3sNodeLabelWithNodeFilters := range simpleConfig.Options.K3sOptions.NodeLabels {
if len(k3sNodeLabelWithNodeFilters.NodeFilters) == 0 && nodeCount > 1 { 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) nodes, err := util.FilterNodes(nodeList, k3sNodeLabelWithNodeFilters.NodeFilters)
if err != nil { 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 { for _, node := range nodes {
@ -209,7 +209,7 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
nodes, err := util.FilterNodes(nodeList, runtimeLabelWithNodeFilters.NodeFilters) nodes, err := util.FilterNodes(nodeList, runtimeLabelWithNodeFilters.NodeFilters)
if err != nil { 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 { for _, node := range nodes {
@ -232,7 +232,7 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
nodes, err := util.FilterNodes(nodeList, envVarWithNodeFilters.NodeFilters) nodes, err := util.FilterNodes(nodeList, envVarWithNodeFilters.NodeFilters)
if err != nil { 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 { for _, node := range nodes {
@ -248,7 +248,7 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
nodes, err := util.FilterNodes(nodeList, argWithNodeFilters.NodeFilters) nodes, err := util.FilterNodes(nodeList, argWithNodeFilters.NodeFilters)
if err != nil { 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 { for _, node := range nodes {
@ -283,7 +283,7 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
if simpleConfig.Registries.Create { if simpleConfig.Registries.Create {
regPort, err := cliutil.ParsePortExposureSpec("random", k3d.DefaultRegistryPort) regPort, err := cliutil.ParsePortExposureSpec("random", k3d.DefaultRegistryPort)
if err != nil { 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{ clusterCreateOpts.Registries.Create = &k3d.Registry{
ClusterRef: newCluster.Name, ClusterRef: newCluster.Name,
@ -296,7 +296,7 @@ func TransformSimpleToClusterConfig(ctx context.Context, runtime runtimes.Runtim
for _, usereg := range simpleConfig.Registries.Use { for _, usereg := range simpleConfig.Registries.Use {
reg, err := util.ParseRegistryRef(usereg) reg, err := util.ParseRegistryRef(usereg)
if err != nil { 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) l.Log().Tracef("Parsed registry reference: %+v", reg)
clusterCreateOpts.Registries.Use = append(clusterCreateOpts.Registries.Use, 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) 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) 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 { 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) } else { // CASE 2: registries.yaml file referenced by path (single line)
registryConfigFile, err := os.Open(simpleConfig.Registries.Config) registryConfigFile, err := os.Open(simpleConfig.Registries.Config)
if err != nil { 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) configBytes, err := ioutil.ReadAll(registryConfigFile)
if err != nil { 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 { 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" "fmt"
dockerunits "github.com/docker/go-units" dockerunits "github.com/docker/go-units"
l "github.com/rancher/k3d/v4/pkg/logger"
) )
// ValidateClusterConfig checks a given cluster config for basic errors // ValidateClusterConfig checks a given cluster config for basic errors
func ValidateClusterConfig(ctx context.Context, runtime runtimes.Runtime, config conf.ClusterConfig) error { func ValidateClusterConfig(ctx context.Context, runtime runtimes.Runtime, config conf.ClusterConfig) error {
// cluster name must be a valid host name // cluster name must be a valid host name
if err := k3dc.CheckName(config.Cluster.Name); err != nil { if err := k3dc.CheckName(config.Cluster.Name); err != nil {
l.Log().Errorf("Provided cluster name '%s' does not match requirements", config.Cluster.Name) return fmt.Errorf("provided cluster name '%s' does not match requirements: %w", config.Cluster.Name, err)
return err
} }
// network:: edge case: hostnetwork -> only if we have a single node (to avoid port collisions) // 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 { 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 // timeout can't be negative
if config.ClusterCreateOpts.Timeout < 0*time.Second { 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 // API-Port cannot be changed when using network=host
if config.Cluster.Network.Name == "host" && config.Cluster.KubeAPI.Port.Port() != k3d.DefaultAPIPort { 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. // 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. // 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 // memory limits must have proper format
// if empty we don't care about errors in parsing // if empty we don't care about errors in parsing
if config.ClusterCreateOpts.ServersMemory != "" { if config.ClusterCreateOpts.ServersMemory != "" {
if _, err := dockerunits.RAMInBytes(config.ClusterCreateOpts.ServersMemory); err != nil { 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 config.ClusterCreateOpts.AgentsMemory != "" {
if _, err := dockerunits.RAMInBytes(config.ClusterCreateOpts.AgentsMemory); err != nil { 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 // validate nodes one by one
for _, node := range config.Cluster.Nodes { 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 // volumes have to be either an existing path on the host or a named runtime volume
for _, volume := range node.Volumes { for _, volume := range node.Volumes {
if err := runtimeutil.ValidateVolumeMount(runtime, volume); err != nil { 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 // initialize docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return "", fmt.Errorf("failed to create docker client: %w", err)
return "", err
} }
defer docker.Close() defer docker.Close()
@ -58,13 +57,11 @@ func createContainer(ctx context.Context, dockerNode *NodeInDocker, name string)
if err != nil { if err != nil {
if client.IsErrNotFound(err) { if client.IsErrNotFound(err) {
if err := pullImage(ctx, docker, dockerNode.ContainerConfig.Image); err != nil { if err := pullImage(ctx, docker, dockerNode.ContainerConfig.Image); err != nil {
l.Log().Errorf("Failed to create container '%s'", name) return "", fmt.Errorf("docker failed to pull image '%s': %w", dockerNode.ContainerConfig.Image, err)
return "", err
} }
continue continue
} }
l.Log().Errorf("Failed to create container '%s'", name) return "", fmt.Errorf("docker failed to create container '%s': %w", name, err)
return "", err
} }
l.Log().Debugf("Created container %s (ID: %s)", name, resp.ID) l.Log().Debugf("Created container %s (ID: %s)", name, resp.ID)
break break
@ -77,8 +74,7 @@ func startContainer(ctx context.Context, ID string) error {
// initialize docker client // initialize docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return fmt.Errorf("failed to get docker client: %w", err)
return err
} }
defer docker.Close() defer docker.Close()
@ -91,8 +87,7 @@ func removeContainer(ctx context.Context, ID string) error {
// (0) create docker client // (0) create docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return fmt.Errorf("failed to get docker client: %w", err)
return err
} }
defer docker.Close() defer docker.Close()
@ -104,8 +99,7 @@ func removeContainer(ctx context.Context, ID string) error {
// (2) remove container // (2) remove container
if err := docker.ContainerRemove(ctx, ID, options); err != nil { if err := docker.ContainerRemove(ctx, ID, options); err != nil {
l.Log().Errorf("Failed to delete container '%s'", ID) return fmt.Errorf("docker failed to remove the container '%s': %w", ID, err)
return err
} }
l.Log().Infoln("Deleted", ID) 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{}) resp, err := docker.ImagePull(ctx, image, types.ImagePullOptions{})
if err != nil { if err != nil {
l.Log().Errorf("Failed to pull image '%s'", image) return fmt.Errorf("docker failed to pull the image '%s': %w", image, err)
return err
} }
defer resp.Close() defer resp.Close()
@ -132,8 +125,7 @@ func pullImage(ctx context.Context, docker *client.Client, image string) error {
} }
_, err = io.Copy(writer, resp) _, err = io.Copy(writer, resp)
if err != nil { if err != nil {
l.Log().Warningf("Couldn't get docker output") l.Log().Warnf("Couldn't get docker output: %v", err)
l.Log().Warningln(err)
} }
return nil return nil
@ -145,8 +137,7 @@ func getNodeContainer(ctx context.Context, node *k3d.Node) (*types.Container, er
// (0) create docker client // (0) create docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return nil, fmt.Errorf("failed to get docker client: %w", err)
return nil, err
} }
defer docker.Close() 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) { func executeCheckInContainer(ctx context.Context, image string, cmd []string) (int64, error) {
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return -1, fmt.Errorf("failed to create docker client: %w", err)
return -1, err
} }
defer docker.Close() defer docker.Close()
@ -204,20 +194,17 @@ func executeCheckInContainer(ctx context.Context, image string, cmd []string) (i
if err != nil { if err != nil {
if client.IsErrNotFound(err) { if client.IsErrNotFound(err) {
if err := pullImage(ctx, docker, image); err != nil { 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, fmt.Errorf("docker failed to pull image '%s': %w", image, err)
return -1, err
} }
continue continue
} }
l.Log().Errorf("Failed to create container from image %s with cmd %s", image, cmd) return -1, fmt.Errorf("docker failed to create container from image '%s' with cmd '%s': %w", image, cmd, err)
return -1, err
} }
break break
} }
if err = startContainer(ctx, resp.ID); err != nil { 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, fmt.Errorf("docker failed to start container from image '%s' with cmd '%s': %w", image, cmd, err)
return -1, err
} }
exitCode := -1 exitCode := -1
@ -225,15 +212,14 @@ func executeCheckInContainer(ctx context.Context, image string, cmd []string) (i
select { select {
case err := <-errCh: case err := <-errCh:
if err != nil { if err != nil {
l.Log().Errorf("Error while waiting for container %s to exit", resp.ID) return -1, fmt.Errorf("docker error while waiting for container '%s' to exit: %w", resp.ID, err)
return -1, err
} }
case status := <-statusCh: case status := <-statusCh:
exitCode = int(status.StatusCode) exitCode = int(status.StatusCode)
} }
if err = removeContainer(ctx, resp.ID); err != nil { 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 return int64(exitCode), nil
@ -246,6 +232,5 @@ func CheckIfDirectoryExists(ctx context.Context, image string, dir string) (bool
cmd := []string{"sh", "-c", shellCmd} cmd := []string{"sh", "-c", shellCmd}
exitCode, err := executeCheckInContainer(ctx, image, cmd) exitCode, err := executeCheckInContainer(ctx, image, cmd)
l.Log().Tracef("check dir container returned %d exist code", exitCode) 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" { if runtime.GOOS == "linux" {
ip, err := GetGatewayIP(ctx, network) ip, err := GetGatewayIP(ctx, network)
if err != nil { 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 return ip, nil
} }

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

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

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

@ -43,13 +43,12 @@ func (d Docker) GetNetwork(ctx context.Context, searchNet *k3d.ClusterNetwork) (
// (0) create new docker client // (0) create new docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return nil, fmt.Errorf("failed to create docker client: %w", err)
return nil, err
} }
defer docker.Close() defer docker.Close()
if searchNet.ID == "" && searchNet.Name == "" { 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 // configure list filters
filter := filters.NewArgs() filter := filters.NewArgs()
@ -65,8 +64,7 @@ func (d Docker) GetNetwork(ctx context.Context, searchNet *k3d.ClusterNetwork) (
Filters: filter, Filters: filter,
}) })
if err != nil { if err != nil {
l.Log().Errorln("Failed to list docker networks") return nil, fmt.Errorf("docker failed to list networks: %w", err)
return nil, err
} }
if len(networkList) == 0 { 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{}) targetNetwork, err := docker.NetworkInspect(ctx, networkList[0].ID, types.NetworkInspectOptions{})
if err != nil { 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) 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 { if len(targetNetwork.IPAM.Config) > 0 {
network.IPAM, err = d.parseIPAM(targetNetwork.IPAM.Config[0]) network.IPAM, err = d.parseIPAM(targetNetwork.IPAM.Config[0])
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("failed to parse IPAM config: %w", err)
} }
for _, container := range targetNetwork.Containers { for _, container := range targetNetwork.Containers {
@ -138,8 +136,7 @@ func (d Docker) CreateNetworkIfNotPresent(ctx context.Context, inNet *k3d.Cluste
// (0) create new docker client // (0) create new docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return nil, false, fmt.Errorf("failed to create docker client: %w", err)
return nil, false, err
} }
defer docker.Close() defer docker.Close()
@ -147,8 +144,7 @@ func (d Docker) CreateNetworkIfNotPresent(ctx context.Context, inNet *k3d.Cluste
if err != nil { if err != nil {
if err != runtimeErr.ErrRuntimeNetworkNotExists { if err != runtimeErr.ErrRuntimeNetworkNotExists {
if existingNet == nil { if existingNet == nil {
l.Log().Errorln("Failed to check for duplicate networks") return nil, false, fmt.Errorf("failed to check for duplicate docker networks: %w", err)
return nil, false, err
} else if err == runtimeErr.ErrRuntimeNetworkMultiSameName { } else if err == runtimeErr.ErrRuntimeNetworkMultiSameName {
l.Log().Warnf("%+v, returning the first one: %s (%s)", err, existingNet.Name, existingNet.ID) l.Log().Warnf("%+v, returning the first one: %s (%s)", err, existingNet.Name, existingNet.ID)
return existingNet, true, nil 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...") l.Log().Traceln("No subnet prefix given, but network should be managed: Trying to get a free subnet prefix...")
freeSubnetPrefix, err := d.getFreeSubnetPrefix(ctx) freeSubnetPrefix, err := d.getFreeSubnetPrefix(ctx)
if err != nil { if err != nil {
return nil, false, err return nil, false, fmt.Errorf("failed to get free subnet prefix: %w", err)
} }
inNet.IPAM.IPPrefix = freeSubnetPrefix 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) newNet, err := docker.NetworkCreate(ctx, inNet.Name, netCreateOpts)
if err != nil { if err != nil {
l.Log().Errorln("Failed to create new network") return nil, false, fmt.Errorf("docker failed to create new network '%s': %w", inNet.Name, err)
return nil, false, err
} }
networkDetails, err := docker.NetworkInspect(ctx, newNet.ID, types.NetworkInspectOptions{}) networkDetails, err := docker.NetworkInspect(ctx, newNet.ID, types.NetworkInspectOptions{})
if err != nil { if err != nil {
l.Log().Errorln("Failed to inspect newly created network") return nil, false, fmt.Errorf("docker failed to inspect newly created network '%s': %w", newNet.ID, err)
return nil, false, err
} }
l.Log().Infof("Created network '%s' (%s)", inNet.Name, networkDetails.ID) l.Log().Infof("Created network '%s' (%s)", inNet.Name, networkDetails.ID)
prefix, err := netaddr.ParseIPPrefix(networkDetails.IPAM.Config[0].Subnet) prefix, err := netaddr.ParseIPPrefix(networkDetails.IPAM.Config[0].Subnet)
if err != nil { 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}} 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 // (0) create new docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return fmt.Errorf("failed to get docker client: %w", err)
return err
} }
defer docker.Close() defer docker.Close()
@ -235,7 +228,7 @@ func (d Docker) DeleteNetwork(ctx context.Context, ID string) error {
if strings.HasSuffix(err.Error(), "active endpoints") { if strings.HasSuffix(err.Error(), "active endpoints") {
return runtimeErr.ErrRuntimeNetworkNotEmpty return runtimeErr.ErrRuntimeNetworkNotEmpty
} }
return err return fmt.Errorf("docker failed to remove network '%s': %w", ID, err)
} }
return nil 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) { func GetNetwork(ctx context.Context, ID string) (types.NetworkResource, error) {
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return types.NetworkResource{}, fmt.Errorf("failed to get docker client: %w", err)
return types.NetworkResource{}, err
} }
defer docker.Close() defer docker.Close()
return docker.NetworkInspect(ctx, ID, types.NetworkInspectOptions{}) 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) { func GetGatewayIP(ctx context.Context, network string) (net.IP, error) {
bridgeNetwork, err := GetNetwork(ctx, network) bridgeNetwork, err := GetNetwork(ctx, network)
if err != nil { if err != nil {
l.Log().Errorf("Failed to get bridge network with name '%s'", network) return nil, fmt.Errorf("failed to get bridge network with name '%s': %w", network, err)
return nil, err
} }
if len(bridgeNetwork.IPAM.Config) > 0 { if len(bridgeNetwork.IPAM.Config) > 0 {
@ -279,22 +270,20 @@ func (d Docker) ConnectNodeToNetwork(ctx context.Context, node *k3d.Node, networ
// get container // get container
container, err := getNodeContainer(ctx, node) container, err := getNodeContainer(ctx, node)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
} }
// get docker client // get docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return fmt.Errorf("failed to get docker client: %w", err)
return err
} }
defer docker.Close() defer docker.Close()
// get network // get network
networkResource, err := GetNetwork(ctx, networkName) networkResource, err := GetNetwork(ctx, networkName)
if err != nil { if err != nil {
l.Log().Errorf("Failed to get network '%s'", networkName) return fmt.Errorf("failed to get network '%s': %w", networkName, err)
return err
} }
// connect container to network // connect container to network
@ -307,22 +296,20 @@ func (d Docker) DisconnectNodeFromNetwork(ctx context.Context, node *k3d.Node, n
// get container // get container
container, err := getNodeContainer(ctx, node) container, err := getNodeContainer(ctx, node)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
} }
// get docker client // get docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return fmt.Errorf("failed to get docker client: %w", err)
return err
} }
defer docker.Close() defer docker.Close()
// get network // get network
networkResource, err := GetNetwork(ctx, networkName) networkResource, err := GetNetwork(ctx, networkName)
if err != nil { if err != nil {
l.Log().Errorf("Failed to get network '%s'", networkName) return fmt.Errorf("failed to get network '%s': %w", networkName, err)
return err
} }
return docker.NetworkDisconnect(ctx, networkResource.ID, container.ID, true) 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 // translate node spec to docker container specs
dockerNode, err := TranslateNodeToContainer(node) dockerNode, err := TranslateNodeToContainer(node)
if err != nil { if err != nil {
l.Log().Errorln("Failed to translate k3d node specification to docker container specifications") return fmt.Errorf("failed to translate k3d node spec to docker container spec: %w", err)
return err
} }
// create node // create node
_, err = createContainer(ctx, dockerNode, node.Name) _, err = createContainer(ctx, dockerNode, node.Name)
if err != nil { if err != nil {
l.Log().Errorf("Failed to create node '%s'", node.Name) return fmt.Errorf("failed to create container for node '%s': %w", node.Name, err)
return err
} }
return nil return nil
@ -70,7 +68,7 @@ func (d Docker) GetNodesByLabel(ctx context.Context, labels map[string]string) (
// (0) get containers // (0) get containers
containers, err := getContainersByLabel(ctx, labels) containers, err := getContainersByLabel(ctx, labels)
if err != nil { 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 // (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]) l.Log().Warnf("Failed to get details for container %s", container.Names[0])
node, err = TranslateContainerToNode(&container) node, err = TranslateContainerToNode(&container)
if err != nil { 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 { } else {
node, err = TranslateContainerDetailsToNode(containerDetails) node, err = TranslateContainerDetailsToNode(containerDetails)
if err != nil { 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) nodes = append(nodes, node)
@ -104,15 +102,14 @@ func (d Docker) StartNode(ctx context.Context, node *k3d.Node) error {
// (0) create docker client // (0) create docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { 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() defer docker.Close()
// get container which represents the node // get container which represents the node
nodeContainer, err := getNodeContainer(ctx, node) nodeContainer, err := getNodeContainer(ctx, node)
if err != nil { if err != nil {
l.Log().Errorf("Failed to get container for node '%s'", node.Name) return fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
return err
} }
// check if the container is actually managed by // 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 // actually start the container
l.Log().Infof("Starting Node '%s'", node.Name) l.Log().Infof("Starting Node '%s'", node.Name)
if err := docker.ContainerStart(ctx, nodeContainer.ID, types.ContainerStartOptions{}); err != nil { 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 // 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 // get container which represents the node
nodeContainer, err := getNodeContainer(ctx, node) nodeContainer, err := getNodeContainer(ctx, node)
if err != nil { if err != nil {
l.Log().Errorf("Failed to get container for node '%s'", node.Name) return fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
return err
} }
// check if the container is actually managed by // 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 // actually stop the container
if err := docker.ContainerStop(ctx, nodeContainer.ID, nil); err != nil { 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 return nil
@ -201,14 +197,13 @@ func getContainerDetails(ctx context.Context, containerID string) (types.Contain
// (0) create docker client // (0) create docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { 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() defer docker.Close()
containerDetails, err := docker.ContainerInspect(ctx, containerID) containerDetails, err := docker.ContainerInspect(ctx, containerID)
if err != nil { if err != nil {
l.Log().Errorf("Failed to get details for container '%s'", containerID) return types.ContainerJSON{}, fmt.Errorf("failed to get details for container '%s': %w", containerID, err)
return types.ContainerJSON{}, err
} }
return containerDetails, nil 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) { func (d Docker) GetNode(ctx context.Context, node *k3d.Node) (*k3d.Node, error) {
container, err := getNodeContainer(ctx, node) container, err := getNodeContainer(ctx, node)
if err != nil { 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) containerDetails, err := getContainerDetails(ctx, container.ID)
if err != nil { 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) node, err = TranslateContainerDetailsToNode(containerDetails)
if err != nil { if err != nil {
l.Log().Errorf("Failed to translate container '%s' to node object", containerDetails.Name) return node, fmt.Errorf("failed to translate container '%s' details to node spec: %w", containerDetails.Name, err)
return node, err
} }
return node, nil 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 // get the container for the given node
container, err := getNodeContainer(ctx, node) container, err := getNodeContainer(ctx, node)
if err != nil { 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 // create docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return running, stateString, fmt.Errorf("failed to get docker client: %w", err)
return running, stateString, err
} }
defer docker.Close() defer docker.Close()
containerInspectResponse, err := docker.ContainerInspect(ctx, container.ID) containerInspectResponse, err := docker.ContainerInspect(ctx, container.ID)
if err != nil { 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 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) { func (d Docker) NodeIsRunning(ctx context.Context, node *k3d.Node) (bool, error) {
isRunning, _, err := d.GetNodeStatus(ctx, node) isRunning, _, err := d.GetNodeStatus(ctx, node)
if err != nil { if err != nil {
return false, err return false, fmt.Errorf("failed to get status for node '%s': %w", node.Name, err)
} }
return isRunning, nil 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 // get the container for the given node
container, err := getNodeContainer(ctx, node) container, err := getNodeContainer(ctx, node)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
} }
// create docker client // create docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return nil, fmt.Errorf("failed to get docker client; %w", err)
return nil, err
} }
defer docker.Close() defer docker.Close()
containerInspectResponse, err := docker.ContainerInspect(ctx, container.ID) containerInspectResponse, err := docker.ContainerInspect(ctx, container.ID)
if err != nil { if err != nil {
l.Log().Errorf("Failed to inspect node '%s'(ID %s)", node.Name, container.ID) return nil, fmt.Errorf("failed ton inspect container '%s': %w", container.ID, err)
return nil, err
} }
if !containerInspectResponse.ContainerJSONBase.State.Running { 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 := "" 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}) logreader, err := docker.ContainerLogs(ctx, container.ID, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Since: sinceStr})
if err != nil { if err != nil {
l.Log().Errorf("Failed to get logs from node '%s' (container '%s')", node.Name, container.ID) return nil, fmt.Errorf("docker failed to get logs from node '%s' (container '%s'): %w", node.Name, container.ID, err)
return nil, err
} }
return logreader, nil 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 { if execConnection != nil && execConnection.Reader != nil {
logs, err := ioutil.ReadAll(execConnection.Reader) logs, err := ioutil.ReadAll(execConnection.Reader)
if err != nil { if err != nil {
l.Log().Errorf("Failed to get logs from errored exec process in node '%s'", node.Name) return fmt.Errorf("failed to get logs from errored exec process in node '%s': %w", node.Name, err)
return err
} }
err = fmt.Errorf("%w: Logs from failed access process:\n%s", err, string(logs)) 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 // get the container for the given node
container, err := getNodeContainer(ctx, node) container, err := getNodeContainer(ctx, node)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
} }
// create docker client // create docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return nil, fmt.Errorf("failed to get docker client: %w", err)
return nil, err
} }
defer docker.Close() defer docker.Close()
@ -371,28 +359,25 @@ func executeInNode(ctx context.Context, node *k3d.Node, cmd []string) (*types.Hi
Cmd: cmd, Cmd: cmd,
}) })
if err != nil { 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{ execConnection, err := docker.ContainerExecAttach(ctx, exec.ID, types.ExecStartCheck{
Tty: true, Tty: true,
}) })
if err != nil { if err != nil {
l.Log().Errorf("Failed to connect to exec process in node '%s'", node.Name) return nil, fmt.Errorf("docker failed to attach to exec process in node '%s': %w", node.Name, err)
return nil, err
} }
if err := docker.ContainerExecStart(ctx, exec.ID, types.ExecStartCheck{Tty: true}); err != nil { 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, fmt.Errorf("docker failed to start exec process in node '%s': %w", node.Name, err)
return nil, err
} }
for { for {
// get info about exec process inside container // get info about exec process inside container
execInfo, err := docker.ContainerExecInspect(ctx, exec.ID) execInfo, err := docker.ContainerExecInspect(ctx, exec.ID)
if err != nil { if err != nil {
l.Log().Errorf("Failed to inspect exec process in node '%s'", node.Name) return &execConnection, fmt.Errorf("docker failed to inspect exec process in node '%s': %w", node.Name, err)
return &execConnection, err
} }
// if still running, continue loop // if still running, continue loop
@ -416,14 +401,13 @@ func (d Docker) GetNodesInNetwork(ctx context.Context, network string) ([]*k3d.N
// create docker client // create docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return nil, fmt.Errorf("failed to create docker client: %w", err)
return nil, err
} }
defer docker.Close() defer docker.Close()
net, err := GetNetwork(ctx, network) net, err := GetNetwork(ctx, network)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("failed to get network '%s': %w", network, err)
} }
connectedNodes := []*k3d.Node{} connectedNodes := []*k3d.Node{}
@ -432,7 +416,7 @@ func (d Docker) GetNodesInNetwork(ctx context.Context, network string) ([]*k3d.N
for cID := range net.Containers { for cID := range net.Containers {
containerDetails, err := getContainerDetails(ctx, cID) containerDetails, err := getContainerDetails(ctx, cID)
if err != nil { 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) node, err := TranslateContainerDetailsToNode(containerDetails)
if err != nil { 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) l.Log().Tracef("GetNodesInNetwork: inspected non-k3d-managed container %s", containerDetails.Name)
continue 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) 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 // get the container for the given node
container, err := getNodeContainer(ctx, node) container, err := getNodeContainer(ctx, node)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to get container for node '%s': %w", node.Name, err)
} }
// create docker client // create docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return fmt.Errorf("failed to get docker client: %w", err)
return err
} }
defer docker.Close() defer docker.Close()

@ -57,28 +57,24 @@ func (d Docker) CopyToNode(ctx context.Context, src string, dest string, node *k
// create docker client // create docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return fmt.Errorf("failed to get docker client: %w", err)
return err
} }
defer docker.Close() defer docker.Close()
container, err := getNodeContainer(ctx, node) container, err := getNodeContainer(ctx, node)
if err != nil { if err != nil {
l.Log().Errorf("Failed to find container for target node '%s'", node.Name) return fmt.Errorf("failed to find container for target node '%s': %w", node.Name, err)
return err
} }
// source: docker/cli/cli/command/container/cp // source: docker/cli/cli/command/container/cp
srcInfo, err := archive.CopyInfoSourcePath(src, false) srcInfo, err := archive.CopyInfoSourcePath(src, false)
if err != nil { if err != nil {
l.Log().Errorln("Failed to copy info source path") return fmt.Errorf("failed to copy info source path: %w", err)
return err
} }
srcArchive, err := archive.TarResource(srcInfo) srcArchive, err := archive.TarResource(srcInfo)
if err != nil { if err != nil {
l.Log().Errorln("Failed to create tar resource") return fmt.Errorf("failed to create tar resource: %w", err)
return err
} }
defer srcArchive.Close() 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) destDir, preparedArchive, err := archive.PrepareArchiveCopy(srcArchive, srcInfo, destInfo)
if err != nil { if err != nil {
l.Log().Errorln("Failed to prepare archive") return fmt.Errorf("failed to prepare archive: %w", err)
return err
} }
defer preparedArchive.Close() defer preparedArchive.Close()
@ -109,8 +104,7 @@ func (d Docker) WriteToNode(ctx context.Context, content []byte, dest string, mo
// create docker client // create docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return fmt.Errorf("failed to get docker client: %w", err)
return err
} }
defer docker.Close() 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) l.Log().Tracef("Reading path %s from node %s...", path, node.Name)
nodeContainer, err := getNodeContainer(ctx, node) nodeContainer, err := getNodeContainer(ctx, node)
if err != nil { 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() docker, err := GetDockerClient()
if err != nil { 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) 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) { if client.IsErrNotFound(err) {
return nil, errors.Wrap(runtimeErrors.ErrRuntimeFileNotFound, err.Error()) 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 return reader, err
@ -171,7 +165,7 @@ func (d Docker) ReadFromNode(ctx context.Context, path string, node *k3d.Node) (
func GetDockerClient() (*client.Client, error) { func GetDockerClient() (*client.Client, error) {
dockerCli, err := command.NewDockerCli(command.WithStandardStreams()) dockerCli, err := command.NewDockerCli(command.WithStandardStreams())
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("failed to create new docker CLI with standard streams: %w", err)
} }
newClientOpts := flags.NewClientOptions() newClientOpts := flags.NewClientOptions()
@ -179,7 +173,7 @@ func GetDockerClient() (*client.Client, error) {
err = dockerCli.Initialize(newClientOpts) err = dockerCli.Initialize(newClientOpts)
if err != nil { 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 // check for TLS Files used for protected connections
@ -187,7 +181,7 @@ func GetDockerClient() (*client.Client, error) {
storageInfo := dockerCli.ContextStore().GetStorageInfo(currentContext) storageInfo := dockerCli.ContextStore().GetStorageInfo(currentContext)
tlsFilesMap, err := dockerCli.ContextStore().ListTLSFiles(currentContext) tlsFilesMap, err := dockerCli.ContextStore().ListTLSFiles(currentContext)
if err != nil { 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" endpointDriver := "docker"
tlsFiles := tlsFilesMap[endpointDriver] tlsFiles := tlsFilesMap[endpointDriver]
@ -198,7 +192,7 @@ func GetDockerClient() (*client.Client, error) {
if ep.Host != "" { if ep.Host != "" {
clientopts, err := ep.ClientOpts() clientopts, err := ep.ClientOpts()
if err != nil { 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 := make(map[string]string, 1)
headers["User-Agent"] = command.UserAgent() 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 // (0) create new docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return fmt.Errorf("failed to get docker client: %w", err)
return err
} }
defer docker.Close() 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) vol, err := docker.VolumeCreate(ctx, volumeCreateOptions)
if err != nil { if err != nil {
l.Log().Errorf("Failed to create volume '%s'", name) return fmt.Errorf("failed to create volume '%s': %w", name, err)
return err
} }
l.Log().Infof("Created volume '%s'", vol.Name) l.Log().Infof("Created volume '%s'", vol.Name)
return nil return nil
@ -70,30 +68,26 @@ func (d Docker) DeleteVolume(ctx context.Context, name string) error {
// (0) create new docker client // (0) create new docker client
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return fmt.Errorf("failed to get docker client: %w", err)
return err
} }
defer docker.Close() defer docker.Close()
// get volume and delete it // get volume and delete it
vol, err := docker.VolumeInspect(ctx, name) vol, err := docker.VolumeInspect(ctx, name)
if err != nil { if err != nil {
l.Log().Errorf("Failed to find volume '%s'", name) return fmt.Errorf("failed to find volume '%s': %w", name, err)
return err
} }
// check if volume is still in use // check if volume is still in use
if vol.UsageData != nil { if vol.UsageData != nil {
if vol.UsageData.RefCount > 0 { if vol.UsageData.RefCount > 0 {
l.Log().Errorf("Failed to delete volume '%s'", vol.Name) return fmt.Errorf("failed to delete volume '%s' as it is still referenced by %d containers", name, vol.UsageData.RefCount)
return fmt.Errorf("Volume '%s' is still referenced by %d containers", name, vol.UsageData.RefCount)
} }
} }
// remove volume // remove volume
if err := docker.VolumeRemove(ctx, name, true); err != nil { if err := docker.VolumeRemove(ctx, name, true); err != nil {
l.Log().Errorf("Failed to delete volume '%s'", name) return fmt.Errorf("docker failed to delete volume '%s': %w", name, err)
return err
} }
return nil return nil
@ -105,8 +99,7 @@ func (d Docker) GetVolume(name string) (string, error) {
ctx := context.Background() ctx := context.Background()
docker, err := GetDockerClient() docker, err := GetDockerClient()
if err != nil { if err != nil {
l.Log().Errorln("Failed to create docker client") return "", fmt.Errorf("failed to get docker client: %w", err)
return "", err
} }
defer docker.Close() defer docker.Close()
@ -114,10 +107,10 @@ func (d Docker) GetVolume(name string) (string, error) {
filters.Add("name", fmt.Sprintf("^%s$", name)) filters.Add("name", fmt.Sprintf("^%s$", name))
volumeList, err := docker.VolumeList(ctx, filters) volumeList, err := docker.VolumeList(ctx, filters)
if err != nil { if err != nil {
return "", err return "", fmt.Errorf("docker failed to list volumes: %w", err)
} }
if len(volumeList.Volumes) < 1 { 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 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 // verifyNamedVolume checks whether a named volume exists in the runtime
func verifyNamedVolume(runtime runtimes.Runtime, volumeName string) error { func verifyNamedVolume(runtime runtimes.Runtime, volumeName string) error {
volumeName, err := runtime.GetVolume(volumeName) foundVolName, err := runtime.GetVolume(volumeName)
if err != nil { if err != nil {
return err return fmt.Errorf("runtime failed to get volume '%s': %w", volumeName, err)
} }
if volumeName == "" { if foundVolName == "" {
return fmt.Errorf("Failed to find named volume '%s'", volumeName) return fmt.Errorf("failed to find named volume '%s'", volumeName)
} }
return nil return nil
} }

@ -22,11 +22,11 @@ THE SOFTWARE.
package util package util
import ( import (
"fmt"
"os" "os"
"path" "path"
homedir "github.com/mitchellh/go-homedir" 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 // 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 // build the path
homeDir, err := homedir.Dir() homeDir, err := homedir.Dir()
if err != nil { if err != nil {
l.Log().Errorln("Failed to get user's home directory") return "", fmt.Errorf("failed to get user's home directory: %w", err)
return "", err
} }
configDir := path.Join(homeDir, ".k3d") configDir := path.Join(homeDir, ".k3d")
// create directories if necessary // create directories if necessary
if err := createDirIfNotExists(configDir); err != nil { if err := createDirIfNotExists(configDir); err != nil {
l.Log().Errorf("Failed to create config path '%s'", configDir) return "", fmt.Errorf("failed to create config directory '%s': %w", configDir, err)
return "", err
} }
return configDir, nil return configDir, nil

@ -83,7 +83,7 @@ func FilterNodesWithSuffix(nodes []*k3d.Node, nodefilters []string) (map[string]
filteredNodes, err := FilterNodes(nodes, []string{nf}) filteredNodes, err := FilterNodes(nodes, []string{nf})
if err != nil { 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) l.Log().Tracef("Filtered %d nodes for suffix '%s' (filter: %s)", len(filteredNodes), suffix, nf)

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

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

@ -22,6 +22,7 @@ THE SOFTWARE.
package version package version
import ( import (
"fmt"
"os" "os"
"strings" "strings"
@ -85,12 +86,12 @@ func fetchLatestK3sVersion() (string, error) {
hub, err := registry.New(url, username, password) hub, err := registry.New(url, username, password)
if err != nil { 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) tags, err := hub.Tags(repository)
if err != nil || len(tags) == 0 { 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:") l.Log().Debugln("Fetched the following tags for rancher/k3s from DockerHub:")

Loading…
Cancel
Save