From b199f50fe2f82442d9596089a992a3cd9bc71b39 Mon Sep 17 00:00:00 2001 From: Ryota Sakamoto Date: Sun, 16 Jun 2024 19:09:36 +0900 Subject: [PATCH] add join cluster --- pkg/cmd/create.go | 16 ++++--- pkg/multipass/multipass.go | 10 +++-- pkg/provisioner/cluster.go | 84 +++++++++++++------------------------ pkg/provisioner/instance.go | 44 +++++++++++++++++++ 4 files changed, 91 insertions(+), 63 deletions(-) create mode 100644 pkg/provisioner/instance.go diff --git a/pkg/cmd/create.go b/pkg/cmd/create.go index 4cdc1d4..8c8f3b8 100644 --- a/pkg/cmd/create.go +++ b/pkg/cmd/create.go @@ -23,13 +23,16 @@ var createWorkerCmd = &cobra.Command{ } func getProvisionerConfig(cmd *cobra.Command) provisioner.Config { + join, _ := cmd.Flags().GetBool("join") + return provisioner.Config{ - Name: cmd.Flag("name").Value.String(), - CPUs: cmd.Flag("cpus").Value.String(), - Memory: cmd.Flag("memory").Value.String(), - Disk: cmd.Flag("disk").Value.String(), - K8sVersion: cmd.Flag("k8s-version").Value.String(), - Image: cmd.Flag("image").Value.String(), + Name: cmd.Flag("name").Value.String(), + CPUs: cmd.Flag("cpus").Value.String(), + Memory: cmd.Flag("memory").Value.String(), + Disk: cmd.Flag("disk").Value.String(), + K8sVersion: cmd.Flag("k8s-version").Value.String(), + Image: cmd.Flag("image").Value.String(), + IsJoinCluster: join, } } @@ -42,6 +45,7 @@ func defineCommonFlags(cmd *cobra.Command) { cmd.Flags().StringP("disk", "d", "10G", "Amount of disk space") cmd.Flags().StringP("k8s-version", "k", "v1.30.0", "Kubernetes version") cmd.Flags().StringP("image", "i", "22.04", "Image to use for the VM") + cmd.Flags().BoolP("join", "j", true, "Join the cluster") } func init() { diff --git a/pkg/multipass/multipass.go b/pkg/multipass/multipass.go index a502d8c..7db9d0f 100644 --- a/pkg/multipass/multipass.go +++ b/pkg/multipass/multipass.go @@ -62,13 +62,17 @@ func LaunchInstance(config InstanceConfig, cloudinit string) error { return err } -func Exec(name string, command string) error { +func Exec(name string, command string) (string, error) { args := []string{"exec", name, "--"} args = append(args, strings.Fields(command)...) cmd := exec.Command("multipass", args...) - _, err := cmd.Output() - return err + output, err := cmd.Output() + if err != nil { + return "", err + } + + return string(output), nil } func Transfer(name string, from string, to string) error { diff --git a/pkg/provisioner/cluster.go b/pkg/provisioner/cluster.go index b6f62dd..e144327 100644 --- a/pkg/provisioner/cluster.go +++ b/pkg/provisioner/cluster.go @@ -9,86 +9,62 @@ import ( "k8s.io/apimachinery/pkg/runtime" clientcmdlatest "k8s.io/client-go/tools/clientcmd/api/latest" - "github.com/ryota-sakamoto/kubernetes-on-multipass/pkg/cloudinit" "github.com/ryota-sakamoto/kubernetes-on-multipass/pkg/kubernetes" "github.com/ryota-sakamoto/kubernetes-on-multipass/pkg/multipass" ) type Config struct { - Name string - CPUs string - Memory string - Disk string - K8sVersion string - Image string + Name string + CPUs string + Memory string + Disk string + K8sVersion string + Image string + IsJoinCluster bool } func CreateMaster(clusterName string, config Config) error { slog.Debug("create master", slog.String("clusterName", clusterName), slog.Any("config", config)) - instanceName := fmt.Sprintf("%s-%s", clusterName, "master") - instance, err := multipass.GetInstance(instanceName) + config.Name = "master" + _, err := LaunchInstance(clusterName, config, GetMasterTemplate()) if err != nil { - return fmt.Errorf("failed to get instance: %w", err) - } - - slog.Debug("get instance", slog.String("instanceName", instanceName), slog.Any("instance", instance)) - if instance != nil { - return nil + return fmt.Errorf("failed to launch instance: %w", err) } - template, err := cloudinit.Generate(GetMasterTemplate(), map[string]string{ - "KubernetesVersion": config.K8sVersion, - "Arch": "amd64", - }) - if err != nil { - return fmt.Errorf("failed to generate cloud-init template: %w", err) - } - - return multipass.LaunchInstance(multipass.InstanceConfig{ - Name: instanceName, - CPUs: config.CPUs, - Memory: config.Memory, - Disk: config.Disk, - Image: config.Image, - }, template) + return nil } func CreateWorker(clusterName string, config Config) error { slog.Debug("create worker", slog.String("clusterName", clusterName), slog.Any("config", config)) - name := config.Name - if name == "" { - name = GetRandomName() + instanceName, err := LaunchInstance(clusterName, config, GetWorkerTemplate()) + if err != nil { + return fmt.Errorf("failed to launch instance: %w", err) } - instanceName := fmt.Sprintf("%s-%s", clusterName, name) - - instance, err := multipass.GetInstance(instanceName) - if err != nil { - return fmt.Errorf("failed to get instance: %w", err) + if config.IsJoinCluster { + return JoinCluster(clusterName, instanceName) } - slog.Debug("get instance", slog.String("instanceName", instanceName), slog.Any("instance", instance)) - if instance != nil { - return nil + return nil +} + +func JoinCluster(clusterName, name string) error { + slog.Debug("join cluster", slog.String("clusterName", clusterName), slog.String("name", name)) + + masterName := clusterName + "-master" + joinCommand, err := multipass.Exec(masterName, "sudo kubeadm token create --print-join-command") + if err != nil { + return fmt.Errorf("failed to get join command: %w", err) } - template, err := cloudinit.Generate(GetWorkerTemplate(), map[string]string{ - "KubernetesVersion": config.K8sVersion, - "Arch": "amd64", - }) + _, err = multipass.Exec(name, fmt.Sprintf("sudo %s", joinCommand)) if err != nil { - return fmt.Errorf("failed to generate cloud-init template: %w", err) + return fmt.Errorf("failed to join cluster: %w", err) } - return multipass.LaunchInstance(multipass.InstanceConfig{ - Name: instanceName, - CPUs: config.CPUs, - Memory: config.Memory, - Disk: config.Disk, - Image: config.Image, - }, template) + return nil } func GenerateKubeconfig(name string) error { @@ -104,7 +80,7 @@ func GenerateKubeconfig(name string) error { return fmt.Errorf("instance not found: %s", name) } - err = multipass.Exec(name, "/opt/csr.sh") + _, err = multipass.Exec(name, "/opt/csr.sh") if err != nil { return fmt.Errorf("failed to execute csr.sh: %w", err) } diff --git a/pkg/provisioner/instance.go b/pkg/provisioner/instance.go new file mode 100644 index 0000000..ecec651 --- /dev/null +++ b/pkg/provisioner/instance.go @@ -0,0 +1,44 @@ +package provisioner + +import ( + "fmt" + "log/slog" + + "github.com/ryota-sakamoto/kubernetes-on-multipass/pkg/cloudinit" + "github.com/ryota-sakamoto/kubernetes-on-multipass/pkg/multipass" +) + +func LaunchInstance(clusterName string, config Config, cloudinitConfig cloudinit.Config) (string, error) { + name := config.Name + if name == "" { + name = GetRandomName() + } + + instanceName := fmt.Sprintf("%s-%s", clusterName, name) + + instance, err := multipass.GetInstance(instanceName) + if err != nil { + return "", fmt.Errorf("failed to get instance: %w", err) + } + + slog.Debug("get instance", slog.String("instanceName", instanceName), slog.Any("instance", instance)) + if instance != nil { + return "", fmt.Errorf("instance already exists: %s", instanceName) + } + + template, err := cloudinit.Generate(GetWorkerTemplate(), map[string]string{ + "KubernetesVersion": config.K8sVersion, + "Arch": "amd64", + }) + if err != nil { + return "", fmt.Errorf("failed to generate cloud-init template: %w", err) + } + + return instanceName, multipass.LaunchInstance(multipass.InstanceConfig{ + Name: instanceName, + CPUs: config.CPUs, + Memory: config.Memory, + Disk: config.Disk, + Image: config.Image, + }, template) +}