diff --git a/cmd/create/createCluster.go b/cmd/create/createCluster.go index f6c0dba9..444f7910 100644 --- a/cmd/create/createCluster.go +++ b/cmd/create/createCluster.go @@ -64,7 +64,7 @@ func NewCmdCreateCluster() *cobra.Command { cluster := parseCreateClusterCmd(cmd, args, createClusterOpts) // check if a cluster with that name exists already - if _, err := k3dCluster.GetCluster(cluster, runtimes.SelectedRuntime); err == nil { + if _, err := k3dCluster.GetCluster(cmd.Context(), runtimes.SelectedRuntime, cluster); err == nil { log.Fatalf("Failed to create cluster '%s' because a cluster with that name already exists", cluster.Name) } @@ -73,11 +73,11 @@ func NewCmdCreateCluster() *cobra.Command { log.Debugln("'--update-kubeconfig set: enabling wait-for-master") cluster.CreateClusterOpts.WaitForMaster = true } - if err := k3dCluster.CreateCluster(cmd.Context(), cluster, runtimes.SelectedRuntime); err != nil { + if err := k3dCluster.CreateCluster(cmd.Context(), runtimes.SelectedRuntime, cluster); err != nil { // rollback if creation failed log.Errorln(err) log.Errorln("Failed to create cluster >>> Rolling Back") - if err := k3dCluster.DeleteCluster(cluster, runtimes.SelectedRuntime); err != nil { + if err := k3dCluster.DeleteCluster(cmd.Context(), runtimes.SelectedRuntime, cluster); err != nil { log.Errorln(err) log.Fatalln("Cluster creation FAILED, also FAILED to rollback changes!") } @@ -87,7 +87,7 @@ func NewCmdCreateCluster() *cobra.Command { if updateKubeconfig { log.Debugf("Updating default kubeconfig with a new context for cluster %s", cluster.Name) - if _, err := k3dCluster.GetAndWriteKubeConfig(runtimes.SelectedRuntime, cluster, "", &k3dCluster.WriteKubeConfigOptions{UpdateExisting: true, OverwriteExisting: false, UpdateCurrentContext: false}); err != nil { + if _, err := k3dCluster.GetAndWriteKubeConfig(cmd.Context(), runtimes.SelectedRuntime, cluster, "", &k3dCluster.WriteKubeConfigOptions{UpdateExisting: true, OverwriteExisting: false, UpdateCurrentContext: false}); err != nil { log.Fatalln(err) } } diff --git a/cmd/create/createNode.go b/cmd/create/createNode.go index 223eaff0..4c4d7eda 100644 --- a/cmd/create/createNode.go +++ b/cmd/create/createNode.go @@ -45,7 +45,7 @@ func NewCmdCreateNode() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { nodes, cluster := parseCreateNodeCmd(cmd, args) for _, node := range nodes { - if err := k3dc.AddNodeToCluster(runtimes.SelectedRuntime, node, cluster); err != nil { + if err := k3dc.AddNodeToCluster(cmd.Context(), runtimes.SelectedRuntime, node, cluster); err != nil { log.Errorf("Failed to add node '%s' to cluster '%s'", node.Name, cluster.Name) log.Errorln(err) } diff --git a/cmd/delete/deleteCluster.go b/cmd/delete/deleteCluster.go index 6b633d7d..fcd08f86 100644 --- a/cmd/delete/deleteCluster.go +++ b/cmd/delete/deleteCluster.go @@ -46,11 +46,11 @@ func NewCmdDeleteCluster() *cobra.Command { log.Infoln("No clusters found") } else { for _, c := range clusters { - if err := cluster.DeleteCluster(c, runtimes.SelectedRuntime); err != nil { + if err := cluster.DeleteCluster(cmd.Context(), runtimes.SelectedRuntime, c); err != nil { log.Fatalln(err) } log.Infoln("Removing cluster details from default kubeconfig") - if err := cluster.RemoveClusterFromDefaultKubeConfig(c); err != nil { + if err := cluster.RemoveClusterFromDefaultKubeConfig(cmd.Context(), c); err != nil { log.Warnln("Failed to remove cluster details from default kubeconfig") log.Warnln(err) } @@ -80,7 +80,7 @@ func parseDeleteClusterCmd(cmd *cobra.Command, args []string) []*k3d.Cluster { if all, err := cmd.Flags().GetBool("all"); err != nil { log.Fatalln(err) } else if all { - clusters, err = cluster.GetClusters(runtimes.SelectedRuntime) + clusters, err = cluster.GetClusters(cmd.Context(), runtimes.SelectedRuntime) if err != nil { log.Fatalln(err) } @@ -92,7 +92,7 @@ func parseDeleteClusterCmd(cmd *cobra.Command, args []string) []*k3d.Cluster { } for _, name := range args { - cluster, err := cluster.GetCluster(&k3d.Cluster{Name: name}, runtimes.SelectedRuntime) + cluster, err := cluster.GetCluster(cmd.Context(), runtimes.SelectedRuntime, &k3d.Cluster{Name: name}) if err != nil { log.Fatalln(err) } diff --git a/cmd/delete/deleteNode.go b/cmd/delete/deleteNode.go index 230ac505..f4333c36 100644 --- a/cmd/delete/deleteNode.go +++ b/cmd/delete/deleteNode.go @@ -46,7 +46,7 @@ func NewCmdDeleteNode() *cobra.Command { log.Infoln("No nodes found") } else { for _, node := range nodes { - if err := cluster.DeleteNode(runtimes.SelectedRuntime, node); err != nil { + if err := cluster.DeleteNode(cmd.Context(), runtimes.SelectedRuntime, node); err != nil { log.Fatalln(err) } } @@ -72,7 +72,7 @@ func parseDeleteNodeCmd(cmd *cobra.Command, args []string) []*k3d.Node { if all, err := cmd.Flags().GetBool("all"); err != nil { log.Fatalln(err) } else if all { - nodes, err = cluster.GetNodes(runtimes.SelectedRuntime) + nodes, err = cluster.GetNodes(cmd.Context(), runtimes.SelectedRuntime) if err != nil { log.Fatalln(err) } @@ -84,7 +84,7 @@ func parseDeleteNodeCmd(cmd *cobra.Command, args []string) []*k3d.Node { } for _, name := range args { - node, err := cluster.GetNode(&k3d.Node{Name: name}, runtimes.SelectedRuntime) + node, err := cluster.GetNode(cmd.Context(), runtimes.SelectedRuntime, &k3d.Node{Name: name}) if err != nil { log.Fatalln(err) } diff --git a/cmd/get/getCluster.go b/cmd/get/getCluster.go index df9c7d07..db8a9a37 100644 --- a/cmd/get/getCluster.go +++ b/cmd/get/getCluster.go @@ -51,13 +51,13 @@ func NewCmdGetCluster() *cobra.Command { clusters, headersOff := parseGetClusterCmd(cmd, args) var existingClusters []*k3d.Cluster if clusters == nil { // Option a) no cluster name specified -> get all clusters - found, err := cluster.GetClusters(runtimes.SelectedRuntime) + found, err := cluster.GetClusters(cmd.Context(), runtimes.SelectedRuntime) if err != nil { log.Fatalln(err) } existingClusters = append(existingClusters, found...) } else { // Option b) cluster name specified -> get specific cluster - found, err := cluster.GetCluster(clusters, runtimes.SelectedRuntime) + found, err := cluster.GetCluster(cmd.Context(), runtimes.SelectedRuntime, clusters) if err != nil { log.Fatalln(err) } diff --git a/cmd/get/getKubeconfig.go b/cmd/get/getKubeconfig.go index 29b191c3..2be6cc97 100644 --- a/cmd/get/getKubeconfig.go +++ b/cmd/get/getKubeconfig.go @@ -63,7 +63,7 @@ func NewCmdGetKubeconfig() *cobra.Command { // generate list of clusters if getKubeconfigFlags.all { - clusters, err = cluster.GetClusters(runtimes.SelectedRuntime) + clusters, err = cluster.GetClusters(cmd.Context(), runtimes.SelectedRuntime) if err != nil { log.Fatalln(err) } @@ -77,7 +77,7 @@ func NewCmdGetKubeconfig() *cobra.Command { errorGettingKubeconfig := false for _, c := range clusters { log.Debugf("Getting kubeconfig for cluster '%s'", c.Name) - if getKubeconfigFlags.output, err = cluster.GetAndWriteKubeConfig(runtimes.SelectedRuntime, c, getKubeconfigFlags.output, &writeKubeConfigOptions); err != nil { + if getKubeconfigFlags.output, err = cluster.GetAndWriteKubeConfig(cmd.Context(), runtimes.SelectedRuntime, c, getKubeconfigFlags.output, &writeKubeConfigOptions); err != nil { log.Errorln(err) errorGettingKubeconfig = true } diff --git a/cmd/get/getNode.go b/cmd/get/getNode.go index 0097d26c..66628f30 100644 --- a/cmd/get/getNode.go +++ b/cmd/get/getNode.go @@ -49,13 +49,13 @@ func NewCmdGetNode() *cobra.Command { node, headersOff := parseGetNodeCmd(cmd, args) var existingNodes []*k3d.Node if node == nil { // Option a) no name specified -> get all nodes - found, err := cluster.GetNodes(runtimes.SelectedRuntime) + found, err := cluster.GetNodes(cmd.Context(), runtimes.SelectedRuntime) if err != nil { log.Fatalln(err) } existingNodes = append(existingNodes, found...) } else { // Option b) cluster name specified -> get specific cluster - found, err := cluster.GetNode(node, runtimes.SelectedRuntime) + found, err := cluster.GetNode(cmd.Context(), runtimes.SelectedRuntime, node) if err != nil { log.Fatalln(err) } diff --git a/cmd/load/loadImage.go b/cmd/load/loadImage.go index 9aa7430b..f2588438 100644 --- a/cmd/load/loadImage.go +++ b/cmd/load/loadImage.go @@ -42,10 +42,10 @@ func NewCmdLoadImage() *cobra.Command { Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { images, clusters, keepTarball := parseLoadImageCmd(cmd, args) - log.Debugf("Load images [%+v] from runtime [%s] into clusters [%+v]", runtimes.SelectedRuntime, images, clusters) + log.Debugf("Load images [%+v] from runtime [%s] into clusters [%+v]", images, runtimes.SelectedRuntime, clusters) for _, cluster := range clusters { log.Infof("Loading images into '%s'", cluster.Name) - if err := tools.LoadImagesIntoCluster(runtimes.SelectedRuntime, images, &cluster, keepTarball); err != nil { + if err := tools.LoadImagesIntoCluster(cmd.Context(), runtimes.SelectedRuntime, images, &cluster, keepTarball); err != nil { log.Errorf("Failed to load images into cluster '%s'", cluster.Name) log.Errorln(err) } diff --git a/cmd/start/startCluster.go b/cmd/start/startCluster.go index 5a96157c..63722b2e 100644 --- a/cmd/start/startCluster.go +++ b/cmd/start/startCluster.go @@ -50,7 +50,7 @@ func NewCmdStartCluster() *cobra.Command { log.Infoln("No clusters found") } else { for _, c := range clusters { - if err := cluster.StartCluster(cmd.Context(), c, runtimes.SelectedRuntime, startClusterOpts); err != nil { + if err := cluster.StartCluster(cmd.Context(), runtimes.SelectedRuntime, c, startClusterOpts); err != nil { log.Fatalln(err) } } @@ -77,7 +77,7 @@ func parseStartClusterCmd(cmd *cobra.Command, args []string) []*k3d.Cluster { if all, err := cmd.Flags().GetBool("all"); err != nil { log.Fatalln(err) } else if all { - clusters, err = cluster.GetClusters(runtimes.SelectedRuntime) + clusters, err = cluster.GetClusters(cmd.Context(), runtimes.SelectedRuntime) if err != nil { log.Fatalln(err) } @@ -89,7 +89,7 @@ func parseStartClusterCmd(cmd *cobra.Command, args []string) []*k3d.Cluster { } for _, name := range args { - cluster, err := cluster.GetCluster(&k3d.Cluster{Name: name}, runtimes.SelectedRuntime) + cluster, err := cluster.GetCluster(cmd.Context(), runtimes.SelectedRuntime, &k3d.Cluster{Name: name}) if err != nil { log.Fatalln(err) } diff --git a/cmd/start/startNode.go b/cmd/start/startNode.go index 4b8d42f8..0e563b4b 100644 --- a/cmd/start/startNode.go +++ b/cmd/start/startNode.go @@ -39,7 +39,7 @@ func NewCmdStartNode() *cobra.Command { Long: `Start an existing k3d node.`, Run: func(cmd *cobra.Command, args []string) { node := parseStartNodeCmd(cmd, args) - if err := runtimes.SelectedRuntime.StartNode(node); err != nil { + if err := runtimes.SelectedRuntime.StartNode(cmd.Context(), node); err != nil { log.Fatalln(err) } }, diff --git a/cmd/stop/stopCluster.go b/cmd/stop/stopCluster.go index 237ba339..cdc047db 100644 --- a/cmd/stop/stopCluster.go +++ b/cmd/stop/stopCluster.go @@ -45,7 +45,7 @@ func NewCmdStopCluster() *cobra.Command { log.Infoln("No clusters found") } else { for _, c := range clusters { - if err := cluster.StopCluster(c, runtimes.SelectedRuntime); err != nil { + if err := cluster.StopCluster(cmd.Context(), runtimes.SelectedRuntime, c); err != nil { log.Fatalln(err) } } @@ -70,7 +70,7 @@ func parseStopClusterCmd(cmd *cobra.Command, args []string) []*k3d.Cluster { if all, err := cmd.Flags().GetBool("all"); err != nil { log.Fatalln(err) } else if all { - clusters, err = cluster.GetClusters(runtimes.SelectedRuntime) + clusters, err = cluster.GetClusters(cmd.Context(), runtimes.SelectedRuntime) if err != nil { log.Fatalln(err) } @@ -82,7 +82,7 @@ func parseStopClusterCmd(cmd *cobra.Command, args []string) []*k3d.Cluster { } for _, name := range args { - cluster, err := cluster.GetCluster(&k3d.Cluster{Name: name}, runtimes.SelectedRuntime) + cluster, err := cluster.GetCluster(cmd.Context(), runtimes.SelectedRuntime, &k3d.Cluster{Name: name}) if err != nil { log.Fatalln(err) } diff --git a/cmd/stop/stopNode.go b/cmd/stop/stopNode.go index 241c4773..80dbda3d 100644 --- a/cmd/stop/stopNode.go +++ b/cmd/stop/stopNode.go @@ -40,7 +40,7 @@ func NewCmdStopNode() *cobra.Command { Long: `Stop an existing k3d node.`, Run: func(cmd *cobra.Command, args []string) { node := parseStopNodeCmd(cmd, args) - if err := runtimes.SelectedRuntime.StopNode(node); err != nil { + if err := runtimes.SelectedRuntime.StopNode(cmd.Context(), node); err != nil { log.Fatalln(err) } }, diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 1461a525..c51c8515 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -40,7 +40,7 @@ import ( // CreateCluster creates a new cluster consisting of // - some containerized k3s nodes // - a docker network -func CreateCluster(ctx context.Context, cluster *k3d.Cluster, runtime k3drt.Runtime) error { +func CreateCluster(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Cluster) error { if cluster.CreateClusterOpts.Timeout > 0*time.Second { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, cluster.CreateClusterOpts.Timeout) @@ -71,7 +71,7 @@ func CreateCluster(ctx context.Context, cluster *k3d.Cluster, runtime k3drt.Runt } // create cluster network or use an existing one - networkID, networkExists, err := runtime.CreateNetworkIfNotPresent(cluster.Network.Name) + networkID, networkExists, err := runtime.CreateNetworkIfNotPresent(ctx, cluster.Network.Name) if err != nil { log.Errorln("Failed to create cluster network") return err @@ -99,7 +99,7 @@ func CreateCluster(ctx context.Context, cluster *k3d.Cluster, runtime k3drt.Runt */ if !cluster.CreateClusterOpts.DisableImageVolume { imageVolumeName := fmt.Sprintf("%s-%s-images", k3d.DefaultObjectNamePrefix, cluster.Name) - if err := runtime.CreateVolume(imageVolumeName, map[string]string{"k3d.cluster": cluster.Name}); err != nil { + if err := runtime.CreateVolume(ctx, imageVolumeName, map[string]string{"k3d.cluster": cluster.Name}); err != nil { log.Errorln("Failed to create image volume '%s' for cluster '%s'", imageVolumeName, cluster.Name) return err } @@ -154,7 +154,7 @@ func CreateCluster(ctx context.Context, cluster *k3d.Cluster, runtime k3drt.Runt // create node log.Infof("Creating node '%s'", node.Name) - if err := CreateNode(node, runtime); err != nil { + if err := CreateNode(ctx, runtime, node); err != nil { log.Errorln("Failed to create node") return err } @@ -192,7 +192,7 @@ func CreateCluster(ctx context.Context, cluster *k3d.Cluster, runtime k3drt.Runt default: } log.Debugln("Waiting for initializing master node...") - logreader, err := runtime.GetNodeLogs(cluster.InitNode, time.Time{}) + logreader, err := runtime.GetNodeLogs(ctx, cluster.InitNode, time.Time{}) if err != nil { if logreader != nil { logreader.Close() @@ -259,12 +259,6 @@ func CreateCluster(ctx context.Context, cluster *k3d.Cluster, runtime k3drt.Runt } } - if err := waitForMasterWaitgroup.Wait(); err != nil { - log.Errorln("Failed to bring up all master nodes in time. Check the logs:") - log.Errorln(">>> ", err) - return fmt.Errorf("Failed to bring up cluster") - } - /* * Auxiliary Containers */ @@ -306,26 +300,41 @@ func CreateCluster(ctx context.Context, cluster *k3d.Cluster, runtime k3drt.Runt } cluster.Nodes = append(cluster.Nodes, lbNode) // append lbNode to list of cluster nodes, so it will be considered during rollback log.Infof("Creating LoadBalancer '%s'", lbNode.Name) - if err := CreateNode(lbNode, runtime); err != nil { + if err := CreateNode(ctx, runtime, lbNode); err != nil { log.Errorln("Failed to create loadbalancer") return err } + if cluster.CreateClusterOpts.WaitForMaster { + waitForMasterWaitgroup.Go(func() error { + // TODO: avoid `level=fatal msg="starting kubernetes: preparing server: post join: a configuration change is already in progress (5)"` + // ... by scanning for this line in logs and restarting the container in case it appears + log.Debugf("Starting to wait for loadbalancer node '%s'", lbNode.Name) + return WaitForNodeLogMessage(ctx, runtime, lbNode, "start worker processes", time.Time{}) + }) + } } else { log.Infoln("Hostnetwork selected -> Skipping creation of Master LoadBalancer") } } + + if err := waitForMasterWaitgroup.Wait(); err != nil { + log.Errorln("Failed to bring up all master nodes (and loadbalancer) in time. Check the logs:") + log.Errorf(">>> %+v", err) + return fmt.Errorf("Failed to bring up cluster") + } + return nil } // DeleteCluster deletes an existing cluster -func DeleteCluster(cluster *k3d.Cluster, runtime k3drt.Runtime) error { +func DeleteCluster(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Cluster) error { log.Infof("Deleting cluster '%s'", cluster.Name) log.Debugf("Cluster Details: %+v", cluster) failed := 0 for _, node := range cluster.Nodes { - if err := runtime.DeleteNode(node); err != nil { + if err := runtime.DeleteNode(ctx, node); err != nil { log.Warningf("Failed to delete node '%s': Try to delete it manually", node.Name) failed++ continue @@ -336,7 +345,7 @@ func DeleteCluster(cluster *k3d.Cluster, runtime k3drt.Runtime) error { if cluster.Network.Name != "" { if !cluster.Network.External { log.Infof("Deleting cluster network '%s'", cluster.Network.Name) - if err := runtime.DeleteNetwork(cluster.Network.Name); err != nil { + if err := runtime.DeleteNetwork(ctx, cluster.Network.Name); err != nil { if strings.HasSuffix(err.Error(), "active endpoints") { log.Warningf("Failed to delete cluster network '%s' because it's still in use: is there another cluster using it?", cluster.Network.Name) } else { @@ -351,7 +360,7 @@ func DeleteCluster(cluster *k3d.Cluster, runtime k3drt.Runtime) error { // delete image volume if cluster.ImageVolume != "" { log.Infof("Deleting image volume '%s'", cluster.ImageVolume) - if err := runtime.DeleteVolume(cluster.ImageVolume); err != nil { + if err := runtime.DeleteVolume(ctx, cluster.ImageVolume); err != nil { log.Warningf("Failed to delete image volume '%s' of cluster '%s': Try to delete it manually", cluster.ImageVolume, cluster.Name) } } @@ -364,8 +373,8 @@ func DeleteCluster(cluster *k3d.Cluster, runtime k3drt.Runtime) error { } // GetClusters returns a list of all existing clusters -func GetClusters(runtime k3drt.Runtime) ([]*k3d.Cluster, error) { - nodes, err := runtime.GetNodesByLabel(k3d.DefaultObjectLabels) +func GetClusters(ctx context.Context, runtime k3drt.Runtime) ([]*k3d.Cluster, error) { + nodes, err := runtime.GetNodesByLabel(ctx, k3d.DefaultObjectLabels) if err != nil { log.Errorln("Failed to get clusters") return nil, err @@ -438,9 +447,9 @@ func populateClusterFieldsFromLabels(cluster *k3d.Cluster) error { } // GetCluster returns an existing cluster with all fields and node lists populated -func GetCluster(cluster *k3d.Cluster, runtime k3drt.Runtime) (*k3d.Cluster, error) { +func GetCluster(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Cluster) (*k3d.Cluster, error) { // get nodes that belong to the selected cluster - nodes, err := runtime.GetNodesByLabel(map[string]string{"k3d.cluster": cluster.Name}) + nodes, err := runtime.GetNodesByLabel(ctx, map[string]string{"k3d.cluster": cluster.Name}) if err != nil { log.Errorf("Failed to get nodes for cluster '%s'", cluster.Name) } @@ -472,7 +481,7 @@ func generateNodeName(cluster string, role k3d.Role, suffix int) string { } // StartCluster starts a whole cluster (i.e. all nodes of the cluster) -func StartCluster(ctx context.Context, cluster *k3d.Cluster, runtime k3drt.Runtime, startClusterOpts types.StartClusterOpts) error { +func StartCluster(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Cluster, startClusterOpts types.StartClusterOpts) error { log.Infof("Starting cluster '%s'", cluster.Name) start := time.Now() @@ -497,7 +506,7 @@ func StartCluster(ctx context.Context, cluster *k3d.Cluster, runtime k3drt.Runti } // start node - if err := runtime.StartNode(node); err != nil { + if err := runtime.StartNode(ctx, node); err != nil { log.Warningf("Failed to start node '%s': Try to start it manually", node.Name) failed++ continue @@ -518,7 +527,7 @@ func StartCluster(ctx context.Context, cluster *k3d.Cluster, runtime k3drt.Runti // start masterlb if masterlb != nil { log.Debugln("Starting masterlb...") - if err := runtime.StartNode(masterlb); err != nil { // FIXME: we could run into a nullpointer exception here + if err := runtime.StartNode(ctx, masterlb); err != nil { // FIXME: we could run into a nullpointer exception here log.Warningf("Failed to start masterlb '%s': Try to start it manually", masterlb.Name) failed++ } @@ -543,12 +552,12 @@ func StartCluster(ctx context.Context, cluster *k3d.Cluster, runtime k3drt.Runti } // StopCluster stops a whole cluster (i.e. all nodes of the cluster) -func StopCluster(cluster *k3d.Cluster, runtime k3drt.Runtime) error { +func StopCluster(ctx context.Context, runtime k3drt.Runtime, cluster *k3d.Cluster) error { log.Infof("Stopping cluster '%s'", cluster.Name) failed := 0 for _, node := range cluster.Nodes { - if err := runtime.StopNode(node); err != nil { + if err := runtime.StopNode(ctx, node); err != nil { log.Warningf("Failed to stop node '%s': Try to stop it manually", node.Name) failed++ continue diff --git a/pkg/cluster/kubeconfig.go b/pkg/cluster/kubeconfig.go index 5209f22f..5681f2d4 100644 --- a/pkg/cluster/kubeconfig.go +++ b/pkg/cluster/kubeconfig.go @@ -23,6 +23,7 @@ package cluster import ( "bytes" + "context" "fmt" "io/ioutil" "os" @@ -47,10 +48,10 @@ type WriteKubeConfigOptions struct { // 1. fetches the KubeConfig from the first master node retrieved for a given cluster // 2. modifies it by updating some fields with cluster-specific information // 3. writes it to the specified output -func GetAndWriteKubeConfig(runtime runtimes.Runtime, cluster *k3d.Cluster, output string, writeKubeConfigOptions *WriteKubeConfigOptions) (string, error) { +func GetAndWriteKubeConfig(ctx context.Context, runtime runtimes.Runtime, cluster *k3d.Cluster, output string, writeKubeConfigOptions *WriteKubeConfigOptions) (string, error) { // get kubeconfig from cluster node - kubeconfig, err := GetKubeconfig(runtime, cluster) + kubeconfig, err := GetKubeconfig(ctx, runtime, cluster) if err != nil { return output, err } @@ -65,7 +66,7 @@ func GetAndWriteKubeConfig(runtime runtimes.Runtime, cluster *k3d.Cluster, outpu // simply write to the output, ignoring existing contents if writeKubeConfigOptions.OverwriteExisting || output == "-" { - return output, WriteKubeConfigToPath(kubeconfig, output) + return output, WriteKubeConfigToPath(ctx, kubeconfig, output) } // load config from existing file or fail if it has non-kubeconfig contents @@ -102,17 +103,17 @@ func GetAndWriteKubeConfig(runtime runtimes.Runtime, cluster *k3d.Cluster, outpu } // update existing kubeconfig, but error out if there are conflicting fields but we don't want to update them - return output, UpdateKubeConfig(kubeconfig, existingKubeConfig, output, writeKubeConfigOptions.UpdateExisting, writeKubeConfigOptions.UpdateCurrentContext) + return output, UpdateKubeConfig(ctx, kubeconfig, existingKubeConfig, output, writeKubeConfigOptions.UpdateExisting, writeKubeConfigOptions.UpdateCurrentContext) } // GetKubeconfig grabs the kubeconfig file from /output from a master node container, // modifies it by updating some fields with cluster-specific information // and returns a Config object for further processing -func GetKubeconfig(runtime runtimes.Runtime, cluster *k3d.Cluster) (*clientcmdapi.Config, error) { +func GetKubeconfig(ctx context.Context, runtime runtimes.Runtime, cluster *k3d.Cluster) (*clientcmdapi.Config, error) { // get all master nodes for the selected cluster // TODO: getKubeconfig: we should make sure, that the master node we're trying to fetch from is actually running - masterNodes, err := runtime.GetNodesByLabel(map[string]string{"k3d.cluster": cluster.Name, "k3d.role": string(k3d.MasterRole)}) + masterNodes, err := runtime.GetNodesByLabel(ctx, map[string]string{"k3d.cluster": cluster.Name, "k3d.role": string(k3d.MasterRole)}) if err != nil { log.Errorln("Failed to get master nodes") return nil, err @@ -142,7 +143,7 @@ func GetKubeconfig(runtime runtimes.Runtime, cluster *k3d.Cluster) (*clientcmdap chosenMaster = masterNodes[0] } // get the kubeconfig from the first master node - reader, err := runtime.GetKubeconfig(chosenMaster) + reader, err := runtime.GetKubeconfig(ctx, chosenMaster) if err != nil { log.Errorf("Failed to get kubeconfig from node '%s'", chosenMaster.Name) return nil, err @@ -199,7 +200,7 @@ func GetKubeconfig(runtime runtimes.Runtime, cluster *k3d.Cluster) (*clientcmdap } // WriteKubeConfigToPath takes a kubeconfig and writes it to some path, which can be '-' for os.Stdout -func WriteKubeConfigToPath(kubeconfig *clientcmdapi.Config, path string) error { +func WriteKubeConfigToPath(ctx context.Context, kubeconfig *clientcmdapi.Config, path string) error { var output *os.File defer output.Close() var err error @@ -234,7 +235,7 @@ func WriteKubeConfigToPath(kubeconfig *clientcmdapi.Config, path string) error { } // UpdateKubeConfig merges a new kubeconfig into an existing kubeconfig and returns the result -func UpdateKubeConfig(newKubeConfig *clientcmdapi.Config, existingKubeConfig *clientcmdapi.Config, outPath string, overwriteConflicting bool, updateCurrentContext bool) error { +func UpdateKubeConfig(ctx context.Context, newKubeConfig *clientcmdapi.Config, existingKubeConfig *clientcmdapi.Config, outPath string, overwriteConflicting bool, updateCurrentContext bool) error { log.Debugf("Merging new KubeConfig:\n%+v\n>>> into existing KubeConfig:\n%+v", newKubeConfig, existingKubeConfig) @@ -277,11 +278,11 @@ func UpdateKubeConfig(newKubeConfig *clientcmdapi.Config, existingKubeConfig *cl log.Debugf("Merged KubeConfig:\n%+v", existingKubeConfig) - return WriteKubeConfig(existingKubeConfig, outPath) + return WriteKubeConfig(ctx, existingKubeConfig, outPath) } // WriteKubeConfig writes a kubeconfig to a path atomically -func WriteKubeConfig(kubeconfig *clientcmdapi.Config, path string) error { +func WriteKubeConfig(ctx context.Context, kubeconfig *clientcmdapi.Config, path string) error { tempPath := fmt.Sprintf("%s.k3d_%s", path, time.Now().Format("20060102_150405.000000")) if err := clientcmd.WriteToFile(*kubeconfig, tempPath); err != nil { log.Errorf("Failed to write merged kubeconfig to temporary file '%s'", tempPath) @@ -319,7 +320,7 @@ func GetDefaultKubeConfigPath() (string, error) { } // RemoveClusterFromDefaultKubeConfig removes a cluster's details from the default kubeconfig -func RemoveClusterFromDefaultKubeConfig(cluster *k3d.Cluster) error { +func RemoveClusterFromDefaultKubeConfig(ctx context.Context, cluster *k3d.Cluster) error { defaultKubeConfigPath, err := GetDefaultKubeConfigPath() if err != nil { return err @@ -328,12 +329,12 @@ func RemoveClusterFromDefaultKubeConfig(cluster *k3d.Cluster) error { if err != nil { return err } - kubeconfig = RemoveClusterFromKubeConfig(cluster, kubeconfig) - return WriteKubeConfig(kubeconfig, defaultKubeConfigPath) + kubeconfig = RemoveClusterFromKubeConfig(ctx, cluster, kubeconfig) + return WriteKubeConfig(ctx, kubeconfig, defaultKubeConfigPath) } // RemoveClusterFromKubeConfig removes a cluster's details from a given kubeconfig -func RemoveClusterFromKubeConfig(cluster *k3d.Cluster, kubeconfig *clientcmdapi.Config) *clientcmdapi.Config { +func RemoveClusterFromKubeConfig(ctx context.Context, cluster *k3d.Cluster, kubeconfig *clientcmdapi.Config) *clientcmdapi.Config { clusterName := fmt.Sprintf("%s-%s", k3d.DefaultObjectNamePrefix, cluster.Name) contextName := fmt.Sprintf("%s-%s", k3d.DefaultObjectNamePrefix, cluster.Name) authInfoName := fmt.Sprintf("admin@%s-%s", k3d.DefaultObjectNamePrefix, cluster.Name) diff --git a/pkg/cluster/loadbalancer.go b/pkg/cluster/loadbalancer.go index 685499eb..a1310bd5 100644 --- a/pkg/cluster/loadbalancer.go +++ b/pkg/cluster/loadbalancer.go @@ -22,6 +22,7 @@ THE SOFTWARE. package cluster import ( + "context" "fmt" "github.com/rancher/k3d/pkg/runtimes" @@ -30,7 +31,7 @@ import ( ) // AddMasterToLoadBalancer adds a new master node to the loadbalancer configuration -func AddMasterToLoadBalancer(runtime runtimes.Runtime, cluster *k3d.Cluster, newNode *k3d.Node) error { +func AddMasterToLoadBalancer(ctx context.Context, runtime runtimes.Runtime, cluster *k3d.Cluster, newNode *k3d.Node) error { // find the LoadBalancer for the target cluster masterNodes := "" var loadbalancer *k3d.Node @@ -49,7 +50,7 @@ func AddMasterToLoadBalancer(runtime runtimes.Runtime, cluster *k3d.Cluster, new log.Debugf("Servers as passed to masterlb: '%s'", masterNodes) command := fmt.Sprintf("SERVERS=%s %s", masterNodes, "confd -onetime -backend env && nginx -s reload") - if err := runtime.ExecInNode(loadbalancer, []string{"sh", "-c", command}); err != nil { + if err := runtime.ExecInNode(ctx, loadbalancer, []string{"sh", "-c", command}); err != nil { log.Errorln("Failed to update loadbalancer configuration") return err } diff --git a/pkg/cluster/node.go b/pkg/cluster/node.go index 252953b3..97072e3b 100644 --- a/pkg/cluster/node.go +++ b/pkg/cluster/node.go @@ -36,8 +36,8 @@ import ( ) // AddNodeToCluster adds a node to an existing cluster -func AddNodeToCluster(runtime runtimes.Runtime, node *k3d.Node, cluster *k3d.Cluster) error { - cluster, err := GetCluster(cluster, runtime) +func AddNodeToCluster(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node, cluster *k3d.Cluster) error { + cluster, err := GetCluster(ctx, runtime, cluster) if err != nil { log.Errorf("Failed to find specified cluster '%s'", cluster.Name) return err @@ -75,7 +75,7 @@ func AddNodeToCluster(runtime runtimes.Runtime, node *k3d.Node, cluster *k3d.Clu } // get node details - chosenNode, err = GetNode(chosenNode, runtime) + chosenNode, err = GetNode(ctx, runtime, chosenNode) if err != nil { return err } @@ -107,13 +107,13 @@ func AddNodeToCluster(runtime runtimes.Runtime, node *k3d.Node, cluster *k3d.Clu } } - if err := CreateNode(node, runtime); err != nil { + if err := CreateNode(ctx, runtime, node); err != nil { return err } // if it's a master node, then update the loadbalancer configuration to include it if node.Role == k3d.MasterRole { - if err := AddMasterToLoadBalancer(runtime, cluster, node); err != nil { + if err := AddMasterToLoadBalancer(ctx, runtime, cluster, node); err != nil { log.Errorln("Failed to add new master node to cluster loadbalancer") return err } @@ -123,16 +123,16 @@ func AddNodeToCluster(runtime runtimes.Runtime, node *k3d.Node, cluster *k3d.Clu } // CreateNodes creates a list of nodes -func CreateNodes(nodes []*k3d.Node, runtime runtimes.Runtime) { // TODO: pass `--atomic` flag, so we stop and return an error if any node creation fails? +func CreateNodes(ctx context.Context, runtime runtimes.Runtime, nodes []*k3d.Node) { // TODO: pass `--atomic` flag, so we stop and return an error if any node creation fails? for _, node := range nodes { - if err := CreateNode(node, runtime); err != nil { + if err := CreateNode(ctx, runtime, node); err != nil { log.Error(err) } } } // CreateNode creates a new containerized k3s node -func CreateNode(node *k3d.Node, runtime runtimes.Runtime) error { +func CreateNode(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node) error { log.Debugf("Creating node from spec\n%+v", node) /* @@ -170,7 +170,7 @@ func CreateNode(node *k3d.Node, runtime runtimes.Runtime) error { /* * CREATION */ - if err := runtime.CreateNode(node); err != nil { + if err := runtime.CreateNode(ctx, node); err != nil { return err } @@ -178,9 +178,9 @@ func CreateNode(node *k3d.Node, runtime runtimes.Runtime) error { } // DeleteNode deletes an existing node -func DeleteNode(runtime runtimes.Runtime, node *k3d.Node) error { +func DeleteNode(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node) error { - if err := runtime.DeleteNode(node); err != nil { + if err := runtime.DeleteNode(ctx, node); err != nil { log.Error(err) } return nil @@ -214,8 +214,8 @@ func patchMasterSpec(node *k3d.Node) error { } // GetNodes returns a list of all existing clusters -func GetNodes(runtime runtimes.Runtime) ([]*k3d.Node, error) { - nodes, err := runtime.GetNodesByLabel(k3d.DefaultObjectLabels) +func GetNodes(ctx context.Context, runtime runtimes.Runtime) ([]*k3d.Node, error) { + nodes, err := runtime.GetNodesByLabel(ctx, k3d.DefaultObjectLabels) if err != nil { log.Errorln("Failed to get nodes") return nil, err @@ -225,9 +225,9 @@ func GetNodes(runtime runtimes.Runtime) ([]*k3d.Node, error) { } // GetNode returns a node matching the specified node fields -func GetNode(node *k3d.Node, runtime runtimes.Runtime) (*k3d.Node, error) { +func GetNode(ctx context.Context, runtime runtimes.Runtime, node *k3d.Node) (*k3d.Node, error) { // get node - node, err := runtime.GetNode(node) + node, err := runtime.GetNode(ctx, node) if err != nil { log.Errorf("Failed to get node '%s'", node.Name) } @@ -245,7 +245,7 @@ func WaitForNodeLogMessage(ctx context.Context, runtime runtimes.Runtime, node * } // read the logs - out, err := runtime.GetNodeLogs(node, since) + out, err := runtime.GetNodeLogs(ctx, node, since) if err != nil { if out != nil { out.Close() diff --git a/pkg/runtimes/containerd/kubeconfig.go b/pkg/runtimes/containerd/kubeconfig.go index 805ea5ee..2faa3e96 100644 --- a/pkg/runtimes/containerd/kubeconfig.go +++ b/pkg/runtimes/containerd/kubeconfig.go @@ -23,12 +23,13 @@ THE SOFTWARE. package containerd import ( + "context" "io" k3d "github.com/rancher/k3d/pkg/types" ) // GetKubeconfig grabs the kubeconfig from inside a k3d node -func (d Containerd) GetKubeconfig(node *k3d.Node) (io.ReadCloser, error) { +func (d Containerd) GetKubeconfig(ctx context.Context, node *k3d.Node) (io.ReadCloser, error) { return nil, nil } diff --git a/pkg/runtimes/containerd/network.go b/pkg/runtimes/containerd/network.go index 1caa532c..64634e64 100644 --- a/pkg/runtimes/containerd/network.go +++ b/pkg/runtimes/containerd/network.go @@ -21,12 +21,14 @@ THE SOFTWARE. */ package containerd +import "context" + // CreateNetworkIfNotPresent creates a new docker network -func (d Containerd) CreateNetworkIfNotPresent(name string) (string, bool, error) { +func (d Containerd) CreateNetworkIfNotPresent(ctx context.Context, name string) (string, bool, error) { return "", false, nil } // DeleteNetwork deletes a network -func (d Containerd) DeleteNetwork(ID string) error { +func (d Containerd) DeleteNetwork(ctx context.Context, ID string) error { return nil } diff --git a/pkg/runtimes/containerd/node.go b/pkg/runtimes/containerd/node.go index 259f16e4..1147a61c 100644 --- a/pkg/runtimes/containerd/node.go +++ b/pkg/runtimes/containerd/node.go @@ -34,9 +34,8 @@ import ( ) // CreateNode creates a new k3d node -func (d Containerd) CreateNode(node *k3d.Node) error { +func (d Containerd) CreateNode(ctx context.Context, node *k3d.Node) error { // create containerd client - ctx := context.Background() clientOpts := []containerd.ClientOpt{ containerd.WithDefaultNamespace("k3d"), } @@ -76,8 +75,7 @@ func (d Containerd) CreateNode(node *k3d.Node) error { } // DeleteNode deletes an existing k3d node -func (d Containerd) DeleteNode(node *k3d.Node) error { - ctx := context.Background() +func (d Containerd) DeleteNode(ctx context.Context, node *k3d.Node) error { clientOpts := []containerd.ClientOpt{ containerd.WithDefaultNamespace("k3d"), } @@ -101,30 +99,30 @@ func (d Containerd) DeleteNode(node *k3d.Node) error { } // StartNode starts an existing node -func (d Containerd) StartNode(node *k3d.Node) error { +func (d Containerd) StartNode(ctx context.Context, node *k3d.Node) error { return nil // TODO: fill } // StopNode stops an existing node -func (d Containerd) StopNode(node *k3d.Node) error { +func (d Containerd) StopNode(ctx context.Context, node *k3d.Node) error { return nil // TODO: fill } -func (d Containerd) GetNodesByLabel(labels map[string]string) ([]*k3d.Node, error) { +func (d Containerd) GetNodesByLabel(ctx context.Context, labels map[string]string) ([]*k3d.Node, error) { return nil, nil } // GetNode tries to get a node container by its name -func (d Containerd) GetNode(node *k3d.Node) (*k3d.Node, error) { +func (d Containerd) GetNode(ctx context.Context, node *k3d.Node) (*k3d.Node, error) { return nil, nil } // GetNodeLogs returns the logs from a given node -func (d Containerd) GetNodeLogs(node *k3d.Node, since time.Time) (io.ReadCloser, error) { +func (d Containerd) GetNodeLogs(ctx context.Context, node *k3d.Node, since time.Time) (io.ReadCloser, error) { return nil, nil } // ExecInNode execs a command inside a node -func (d Containerd) ExecInNode(node *k3d.Node, cmd []string) error { +func (d Containerd) ExecInNode(ctx context.Context, node *k3d.Node, cmd []string) error { return nil } diff --git a/pkg/runtimes/containerd/volume.go b/pkg/runtimes/containerd/volume.go index 1b06e11e..e5e121c2 100644 --- a/pkg/runtimes/containerd/volume.go +++ b/pkg/runtimes/containerd/volume.go @@ -21,13 +21,15 @@ THE SOFTWARE. */ package containerd +import "context" + // CreateVolume creates a new named volume -func (d Containerd) CreateVolume(name string, labels map[string]string) error { +func (d Containerd) CreateVolume(ctx context.Context, name string, labels map[string]string) error { return nil } // DeleteVolume creates a new named volume -func (d Containerd) DeleteVolume(name string) error { +func (d Containerd) DeleteVolume(ctx context.Context, name string) error { return nil } diff --git a/pkg/runtimes/docker/container.go b/pkg/runtimes/docker/container.go index 9aab8faa..c83fc2e9 100644 --- a/pkg/runtimes/docker/container.go +++ b/pkg/runtimes/docker/container.go @@ -38,12 +38,11 @@ import ( ) // createContainer creates a new docker container from translated specs -func createContainer(dockerNode *NodeInDocker, name string) error { +func createContainer(ctx context.Context, dockerNode *NodeInDocker, name string) error { log.Debugf("Creating docker container with translated config\n%+v\n", dockerNode) // TODO: remove? // initialize docker client - ctx := context.Background() docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { log.Errorln("Failed to create docker client") @@ -57,13 +56,13 @@ func createContainer(dockerNode *NodeInDocker, name string) error { resp, err = docker.ContainerCreate(ctx, &dockerNode.ContainerConfig, &dockerNode.HostConfig, &dockerNode.NetworkingConfig, name) if err != nil { if client.IsErrNotFound(err) { - if err := pullImage(&ctx, docker, dockerNode.ContainerConfig.Image); err != nil { - log.Errorln("Failed to create container") + if err := pullImage(ctx, docker, dockerNode.ContainerConfig.Image); err != nil { + log.Errorf("Failed to create container '%s'", name) return err } continue } - log.Errorln("Failed to create container") + log.Errorf("Failed to create container '%s'", name) return err } log.Debugln("Created container", resp.ID) @@ -80,10 +79,9 @@ func createContainer(dockerNode *NodeInDocker, name string) error { } // removeContainer deletes a running container (like docker rm -f) -func removeContainer(ID string) error { +func removeContainer(ctx context.Context, ID string) error { // (0) create docker client - ctx := context.Background() docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { log.Errorln("Failed to create docker client") @@ -109,9 +107,9 @@ func removeContainer(ID string) error { } // pullImage pulls a container image and outputs progress if --verbose flag is set -func pullImage(ctx *context.Context, docker *client.Client, image string) error { +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 { log.Errorf("Failed to pull image '%s'", image) return err @@ -135,9 +133,8 @@ func pullImage(ctx *context.Context, docker *client.Client, image string) error } -func getNodeContainer(node *k3d.Node) (*types.Container, error) { +func getNodeContainer(ctx context.Context, node *k3d.Node) (*types.Container, error) { // (0) create docker client - ctx := context.Background() docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { log.Errorln("Failed to create docker client") diff --git a/pkg/runtimes/docker/kubeconfig.go b/pkg/runtimes/docker/kubeconfig.go index 266f0b14..8d6ff606 100644 --- a/pkg/runtimes/docker/kubeconfig.go +++ b/pkg/runtimes/docker/kubeconfig.go @@ -32,8 +32,7 @@ import ( ) // GetKubeconfig grabs the kubeconfig from inside a k3d node -func (d Docker) GetKubeconfig(node *k3d.Node) (io.ReadCloser, error) { - ctx := context.Background() +func (d Docker) GetKubeconfig(ctx context.Context, node *k3d.Node) (io.ReadCloser, error) { docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { log.Errorln("Failed to create docker client") @@ -41,7 +40,7 @@ func (d Docker) GetKubeconfig(node *k3d.Node) (io.ReadCloser, error) { } defer docker.Close() - container, err := getNodeContainer(node) + container, err := getNodeContainer(ctx, node) if err != nil { return nil, err } diff --git a/pkg/runtimes/docker/network.go b/pkg/runtimes/docker/network.go index aacaeb05..63ccd6e7 100644 --- a/pkg/runtimes/docker/network.go +++ b/pkg/runtimes/docker/network.go @@ -34,10 +34,9 @@ import ( // CreateNetworkIfNotPresent creates a new docker network // @return: network name, exists, error -func (d Docker) CreateNetworkIfNotPresent(name string) (string, bool, error) { +func (d Docker) CreateNetworkIfNotPresent(ctx context.Context, name string) (string, bool, error) { // (0) create new docker client - ctx := context.Background() docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { log.Errorln("Failed to create docker client") @@ -82,9 +81,8 @@ func (d Docker) CreateNetworkIfNotPresent(name string) (string, bool, error) { } // DeleteNetwork deletes a network -func (d Docker) DeleteNetwork(ID string) error { +func (d Docker) DeleteNetwork(ctx context.Context, ID string) error { // (0) create new docker client - ctx := context.Background() docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { log.Errorln("Failed to create docker client") @@ -97,8 +95,7 @@ func (d Docker) DeleteNetwork(ID string) error { } // GetNetwork gets information about a network by its ID -func GetNetwork(ID string) (types.NetworkResource, error) { - ctx := context.Background() +func GetNetwork(ctx context.Context, ID string) (types.NetworkResource, error) { docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { log.Errorln("Failed to create docker client") diff --git a/pkg/runtimes/docker/node.go b/pkg/runtimes/docker/node.go index 6c5a6419..c89c7a4e 100644 --- a/pkg/runtimes/docker/node.go +++ b/pkg/runtimes/docker/node.go @@ -37,7 +37,7 @@ import ( ) // CreateNode creates a new container -func (d Docker) CreateNode(node *k3d.Node) error { +func (d Docker) CreateNode(ctx context.Context, node *k3d.Node) error { // translate node spec to docker container specs dockerNode, err := TranslateNodeToContainer(node) @@ -47,8 +47,8 @@ func (d Docker) CreateNode(node *k3d.Node) error { } // create node - if err := createContainer(dockerNode, node.Name); err != nil { - log.Errorln("Failed to create k3d node") + if err := createContainer(ctx, dockerNode, node.Name); err != nil { + log.Errorf("Failed to create node '%s'", node.Name) return err } @@ -56,15 +56,15 @@ func (d Docker) CreateNode(node *k3d.Node) error { } // DeleteNode deletes a node -func (d Docker) DeleteNode(nodeSpec *k3d.Node) error { - return removeContainer(nodeSpec.Name) +func (d Docker) DeleteNode(ctx context.Context, nodeSpec *k3d.Node) error { + return removeContainer(ctx, nodeSpec.Name) } // GetNodesByLabel returns a list of existing nodes -func (d Docker) GetNodesByLabel(labels map[string]string) ([]*k3d.Node, error) { +func (d Docker) GetNodesByLabel(ctx context.Context, labels map[string]string) ([]*k3d.Node, error) { // (0) get containers - containers, err := getContainersByLabel(labels) + containers, err := getContainersByLabel(ctx, labels) if err != nil { return nil, err } @@ -84,9 +84,8 @@ func (d Docker) GetNodesByLabel(labels map[string]string) ([]*k3d.Node, error) { } // StartNode starts an existing node -func (d Docker) StartNode(node *k3d.Node) error { +func (d Docker) StartNode(ctx context.Context, node *k3d.Node) error { // (0) create docker client - ctx := context.Background() docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { return fmt.Errorf("Failed to create docker client. %+v", err) @@ -94,7 +93,7 @@ func (d Docker) StartNode(node *k3d.Node) error { defer docker.Close() // get container which represents the node - nodeContainer, err := getNodeContainer(node) + nodeContainer, err := getNodeContainer(ctx, node) if err != nil { log.Errorf("Failed to get container for node '%s'", node.Name) return err @@ -115,9 +114,8 @@ func (d Docker) StartNode(node *k3d.Node) error { } // StopNode stops an existing node -func (d Docker) StopNode(node *k3d.Node) error { +func (d Docker) StopNode(ctx context.Context, node *k3d.Node) error { // (0) create docker client - ctx := context.Background() docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { return fmt.Errorf("Failed to create docker client. %+v", err) @@ -125,7 +123,7 @@ func (d Docker) StopNode(node *k3d.Node) error { defer docker.Close() // get container which represents the node - nodeContainer, err := getNodeContainer(node) + nodeContainer, err := getNodeContainer(ctx, node) if err != nil { log.Errorf("Failed to get container for node '%s'", node.Name) return err @@ -144,9 +142,8 @@ func (d Docker) StopNode(node *k3d.Node) error { return nil } -func getContainersByLabel(labels map[string]string) ([]types.Container, error) { +func getContainersByLabel(ctx context.Context, labels map[string]string) ([]types.Container, error) { // (0) create docker client - ctx := context.Background() docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { return nil, fmt.Errorf("Failed to create docker client. %+v", err) @@ -175,9 +172,8 @@ func getContainersByLabel(labels map[string]string) ([]types.Container, error) { } // getContainer details returns the containerjson with more details -func getContainerDetails(containerID string) (types.ContainerJSON, error) { +func getContainerDetails(ctx context.Context, containerID string) (types.ContainerJSON, error) { // (0) create docker client - ctx := context.Background() docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { return types.ContainerJSON{}, fmt.Errorf("Failed to create docker client. %+v", err) @@ -195,14 +191,14 @@ func getContainerDetails(containerID string) (types.ContainerJSON, error) { } // GetNode tries to get a node container by its name -func (d Docker) GetNode(node *k3d.Node) (*k3d.Node, error) { - container, err := getNodeContainer(node) +func (d Docker) GetNode(ctx context.Context, node *k3d.Node) (*k3d.Node, error) { + container, err := getNodeContainer(ctx, node) if err != nil { log.Errorf("Failed to get container for node '%s'", node.Name) return node, err } - containerDetails, err := getContainerDetails(container.ID) + containerDetails, err := getContainerDetails(ctx, container.ID) if err != nil { return node, err } @@ -218,15 +214,14 @@ func (d Docker) GetNode(node *k3d.Node) (*k3d.Node, error) { } // GetNodeLogs returns the logs from a given node -func (d Docker) GetNodeLogs(node *k3d.Node, since time.Time) (io.ReadCloser, error) { +func (d Docker) GetNodeLogs(ctx context.Context, node *k3d.Node, since time.Time) (io.ReadCloser, error) { // get the container for the given node - container, err := getNodeContainer(node) + container, err := getNodeContainer(ctx, node) if err != nil { return nil, err } // create docker client - ctx := context.Background() docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { log.Errorln("Failed to create docker client") @@ -258,18 +253,17 @@ func (d Docker) GetNodeLogs(node *k3d.Node, since time.Time) (io.ReadCloser, err } // ExecInNode execs a command inside a node -func (d Docker) ExecInNode(node *k3d.Node, cmd []string) error { +func (d Docker) ExecInNode(ctx context.Context, node *k3d.Node, cmd []string) error { log.Debugf("Executing command '%+v' in node '%s'", cmd, node.Name) // get the container for the given node - container, err := getNodeContainer(node) + container, err := getNodeContainer(ctx, node) if err != nil { return err } // create docker client - ctx := context.Background() docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { log.Errorln("Failed to create docker client") diff --git a/pkg/runtimes/docker/translate.go b/pkg/runtimes/docker/translate.go index af169337..e2ef8c21 100644 --- a/pkg/runtimes/docker/translate.go +++ b/pkg/runtimes/docker/translate.go @@ -23,6 +23,7 @@ THE SOFTWARE. package docker import ( + "context" "fmt" "strings" @@ -94,7 +95,7 @@ func TranslateNodeToContainer(node *k3d.Node) (*NodeInDocker, error) { networkingConfig.EndpointsConfig = map[string]*network.EndpointSettings{ node.Network: {}, } - netInfo, err := GetNetwork(node.Network) + netInfo, err := GetNetwork(context.Background(), node.Network) if err != nil { log.Warnln("Failed to get network information") log.Warnln(err) diff --git a/pkg/runtimes/docker/volume.go b/pkg/runtimes/docker/volume.go index 2400e27b..751cce9a 100644 --- a/pkg/runtimes/docker/volume.go +++ b/pkg/runtimes/docker/volume.go @@ -33,9 +33,8 @@ import ( ) // CreateVolume creates a new named volume -func (d Docker) CreateVolume(name string, labels map[string]string) error { +func (d Docker) CreateVolume(ctx context.Context, name string, labels map[string]string) error { // (0) create new docker client - ctx := context.Background() docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { log.Errorln("Failed to create docker client") @@ -64,9 +63,8 @@ func (d Docker) CreateVolume(name string, labels map[string]string) error { } // DeleteVolume creates a new named volume -func (d Docker) DeleteVolume(name string) error { +func (d Docker) DeleteVolume(ctx context.Context, name string) error { // (0) create new docker client - ctx := context.Background() docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { log.Errorln("Failed to create docker client") diff --git a/pkg/runtimes/runtime.go b/pkg/runtimes/runtime.go index fc05c465..1e1463b6 100644 --- a/pkg/runtimes/runtime.go +++ b/pkg/runtimes/runtime.go @@ -22,6 +22,7 @@ THE SOFTWARE. package runtimes import ( + "context" "fmt" "io" "time" @@ -42,22 +43,21 @@ var Runtimes = map[string]Runtime{ // Runtime defines an interface that can be implemented for various container runtime environments (docker, containerd, etc.) type Runtime interface { - CreateNode(*k3d.Node) error - DeleteNode(*k3d.Node) error - GetNodesByLabel(map[string]string) ([]*k3d.Node, error) - GetNode(*k3d.Node) (*k3d.Node, error) - CreateNetworkIfNotPresent(name string) (string, bool, error) // @return NETWORK_NAME, EXISTS, ERROR - GetKubeconfig(*k3d.Node) (io.ReadCloser, error) - DeleteNetwork(ID string) error - StartNode(*k3d.Node) error - StopNode(*k3d.Node) error - CreateVolume(string, map[string]string) error - DeleteVolume(string) error + CreateNode(context.Context, *k3d.Node) error + DeleteNode(context.Context, *k3d.Node) error + GetNodesByLabel(context.Context, map[string]string) ([]*k3d.Node, error) + GetNode(context.Context, *k3d.Node) (*k3d.Node, error) + CreateNetworkIfNotPresent(context.Context, string) (string, bool, error) // @return NETWORK_NAME, EXISTS, ERROR + GetKubeconfig(context.Context, *k3d.Node) (io.ReadCloser, error) + DeleteNetwork(context.Context, string) error + StartNode(context.Context, *k3d.Node) error + StopNode(context.Context, *k3d.Node) error + CreateVolume(context.Context, string, map[string]string) error + DeleteVolume(context.Context, string) error GetVolume(string) (string, error) GetRuntimePath() string // returns e.g. '/var/run/docker.sock' for a default docker setup - ExecInNode(*k3d.Node, []string) error - // DeleteContainer() error - GetNodeLogs(*k3d.Node, time.Time) (io.ReadCloser, error) + ExecInNode(context.Context, *k3d.Node, []string) error + GetNodeLogs(context.Context, *k3d.Node, time.Time) (io.ReadCloser, error) } // GetRuntime checks, if a given name is represented by an implemented k3d runtime and returns it diff --git a/pkg/tools/tools.go b/pkg/tools/tools.go index 2ad393f0..ddb8fdc4 100644 --- a/pkg/tools/tools.go +++ b/pkg/tools/tools.go @@ -22,6 +22,7 @@ THE SOFTWARE. package tools import ( + "context" "fmt" "sync" "time" @@ -34,8 +35,8 @@ import ( // LoadImagesIntoCluster starts up a k3d tools container for the selected cluster and uses it to export // images from the runtime to import them into the nodes of the selected cluster -func LoadImagesIntoCluster(runtime runtimes.Runtime, images []string, cluster *k3d.Cluster, keepTarball bool) error { - cluster, err := k3dc.GetCluster(cluster, runtime) +func LoadImagesIntoCluster(ctx context.Context, runtime runtimes.Runtime, images []string, cluster *k3d.Cluster, keepTarball bool) error { + cluster, err := k3dc.GetCluster(ctx, runtime, cluster) if err != nil { log.Errorf("Failed to find the specified cluster") return err @@ -63,6 +64,7 @@ func LoadImagesIntoCluster(runtime runtimes.Runtime, images []string, cluster *k // create tools node to export images log.Infoln("Starting k3d-tools node...") toolsNode, err := startToolsNode( // TODO: re-use existing container + ctx, runtime, cluster, cluster.Network.Name, @@ -77,7 +79,7 @@ func LoadImagesIntoCluster(runtime runtimes.Runtime, images []string, cluster *k // save image to tarfile in shared volume log.Infoln("Saving images...") tarName := fmt.Sprintf("%s/k3d-%s-images-%s.tar", k3d.DefaultImageVolumeMountPath, cluster.Name, time.Now().Format("20060102150405")) // FIXME: change - if err := runtime.ExecInNode(toolsNode, append([]string{"./k3d-tools", "save-image", "-d", tarName}, images...)); err != nil { + if err := runtime.ExecInNode(ctx, toolsNode, append([]string{"./k3d-tools", "save-image", "-d", tarName}, images...)); err != nil { log.Errorf("Failed to save images in tools container for cluster '%s'", cluster.Name) return err } @@ -91,7 +93,7 @@ func LoadImagesIntoCluster(runtime runtimes.Runtime, images []string, cluster *k importWaitgroup.Add(1) go func(node *k3d.Node, wg *sync.WaitGroup) { log.Infof("Importing images into node '%s'...", node.Name) - if err := runtime.ExecInNode(node, []string{"ctr", "image", "import", tarName}); err != nil { + if err := runtime.ExecInNode(ctx, node, []string{"ctr", "image", "import", tarName}); err != nil { log.Errorf("Failed to import images in node '%s'", node.Name) log.Errorln(err) } @@ -104,7 +106,7 @@ func LoadImagesIntoCluster(runtime runtimes.Runtime, images []string, cluster *k // remove tarball if !keepTarball { log.Infoln("Removing the tarball...") - if err := runtime.ExecInNode(cluster.Nodes[0], []string{"rm", "-f", tarName}); err != nil { // TODO: do this in tools node (requires rm) + if err := runtime.ExecInNode(ctx, cluster.Nodes[0], []string{"rm", "-f", tarName}); err != nil { // TODO: do this in tools node (requires rm) log.Errorf("Failed to delete tarball '%s'", tarName) log.Errorln(err) } @@ -112,7 +114,7 @@ func LoadImagesIntoCluster(runtime runtimes.Runtime, images []string, cluster *k // delete tools container log.Infoln("Removing k3d-tools node...") - if err := runtime.DeleteNode(toolsNode); err != nil { + if err := runtime.DeleteNode(ctx, toolsNode); err != nil { log.Errorln("Failed to delete tools node '%s': Try to delete it manually", toolsNode.Name) } @@ -123,7 +125,7 @@ func LoadImagesIntoCluster(runtime runtimes.Runtime, images []string, cluster *k } // startToolsNode will start a new k3d tools container and connect it to the network of the chosen cluster -func startToolsNode(runtime runtimes.Runtime, cluster *k3d.Cluster, network string, volumes []string) (*k3d.Node, error) { +func startToolsNode(ctx context.Context, runtime runtimes.Runtime, cluster *k3d.Cluster, network string, volumes []string) (*k3d.Node, error) { node := &k3d.Node{ Name: fmt.Sprintf("%s-%s-tools", k3d.DefaultObjectNamePrefix, cluster.Name), Image: k3d.DefaultToolsContainerImage, @@ -133,7 +135,7 @@ func startToolsNode(runtime runtimes.Runtime, cluster *k3d.Cluster, network stri Cmd: []string{}, Args: []string{"noop"}, } - if err := runtime.CreateNode(node); err != nil { + if err := runtime.CreateNode(ctx, node); err != nil { log.Errorf("Failed to create tools container for cluster '%s'", cluster.Name) return node, err }