diff --git a/plugins/common/io.go b/plugins/common/io.go index d2ebffcf3..4df5e9aa9 100644 --- a/plugins/common/io.go +++ b/plugins/common/io.go @@ -154,8 +154,8 @@ func ReadFirstLine(filename string) (text string) { // SetPermissionsInput is the input struct for SetPermissions type SetPermissionInput struct { Filename string - Mode os.FileMode GroupName string + Mode os.FileMode Username string } @@ -214,9 +214,11 @@ func TouchFile(filename string) error { } type WriteSliceToFileInput struct { - Filename string - Lines []string - Mode os.FileMode + Filename string + GroupName string + Lines []string + Mode os.FileMode + Username string } // WriteSliceToFile writes a slice of strings to a file @@ -239,8 +241,17 @@ func WriteSliceToFile(input WriteSliceToFileInput) error { return err } - return SetPermissions(SetPermissionInput{ + permissionsInput := SetPermissionInput{ Filename: input.Filename, Mode: input.Mode, - }) + } + + if input.GroupName != "" { + permissionsInput.GroupName = input.GroupName + } + if input.Username != "" { + permissionsInput.Username = input.Username + } + + return SetPermissions(permissionsInput) } diff --git a/plugins/scheduler-k3s/functions.go b/plugins/scheduler-k3s/functions.go index d71842a59..2deb4aa4a 100644 --- a/plugins/scheduler-k3s/functions.go +++ b/plugins/scheduler-k3s/functions.go @@ -14,6 +14,8 @@ import ( appjson "github.com/dokku/dokku/plugins/app-json" "github.com/dokku/dokku/plugins/common" + resty "github.com/go-resty/resty/v2" + "golang.org/x/sync/errgroup" "gopkg.in/yaml.v3" corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1" @@ -722,6 +724,61 @@ func installHelmCharts(ctx context.Context, clientset KubernetesClient) error { return nil } +func installHelperCommands(ctx context.Context) error { + urls := map[string]string{ + "kubectx": "https://github.com/ahmetb/kubectx/releases/latest/download/kubectx", + "kubens": "https://github.com/ahmetb/kubectx/releases/latest/download/kubens", + } + + client := resty.New() + for binaryName, url := range urls { + resp, err := client.R(). + SetContext(ctx). + Get(url) + if err != nil { + return fmt.Errorf("Unable to download %s: %w", binaryName, err) + } + if resp == nil { + return fmt.Errorf("Missing response from %s download: %w", binaryName, err) + } + + if resp.StatusCode() != 200 { + return fmt.Errorf("Invalid status code for %s: %d", binaryName, resp.StatusCode()) + } + + f, err := os.Create(filepath.Join("/usr/local/bin", binaryName)) + if err != nil { + return fmt.Errorf("Unable to create %s: %w", binaryName, err) + } + + if err := f.Close(); err != nil { + return fmt.Errorf("Unable to close %s file: %w", binaryName, err) + } + + err = common.WriteSliceToFile(common.WriteSliceToFileInput{ + Filename: f.Name(), + GroupName: "root", + Lines: strings.Split(resp.String(), "\n"), + Mode: os.FileMode(0755), + Username: "root", + }) + if err != nil { + return fmt.Errorf("Unable to write %s to file: %w", binaryName, err) + } + + fi, err := os.Stat(f.Name()) + if err != nil { + return fmt.Errorf("Unable to get %s file size: %w", binaryName, err) + } + + if fi.Size() == 0 { + return fmt.Errorf("Invalid %s filesize", binaryName) + } + } + + return nil +} + func isK3sInstalled() error { if !common.FileExists("/usr/local/bin/k3s") { return fmt.Errorf("k3s binary is not available") @@ -797,6 +854,17 @@ func kubernetesNodeToNode(node v1.Node) Node { } } +func uninstallHelperCommands(ctx context.Context) error { + errs, _ := errgroup.WithContext(ctx) + errs.Go(func() error { + return os.RemoveAll("/usr/local/bin/kubectx") + }) + errs.Go(func() error { + return os.RemoveAll("/usr/local/bin/kubens") + }) + return errs.Wait() +} + func waitForPodBySelectorRunning(ctx context.Context, input WaitForPodBySelectorRunningInput) error { pods, err := waitForPodToExist(ctx, WaitForPodToExistInput{ Clientset: input.Clientset, diff --git a/plugins/scheduler-k3s/subcommands.go b/plugins/scheduler-k3s/subcommands.go index 257338323..a2d692f21 100644 --- a/plugins/scheduler-k3s/subcommands.go +++ b/plugins/scheduler-k3s/subcommands.go @@ -84,6 +84,7 @@ func CommandInitialize(serverIP string, taintScheduling bool) error { common.LogInfo2Quiet("Downloading k3s installer") client := resty.New() resp, err := client.R(). + SetContext(ctx). Get("https://get.k3s.io") if err != nil { return fmt.Errorf("Unable to download k3s installer: %w", err) @@ -247,6 +248,12 @@ func CommandInitialize(serverIP string, taintScheduling bool) error { return fmt.Errorf("Unable to install helm charts: %w", err) } + common.LogInfo2Quiet("Installing helper commands") + err = installHelperCommands(ctx) + if err != nil { + return fmt.Errorf("Unable to install helper commands: %w", err) + } + common.LogVerboseQuiet("Done") return nil @@ -709,5 +716,6 @@ func CommandUninstall() error { return fmt.Errorf("Invalid exit code from k3s uninstaller command: %d", uninstallerCmd.ExitCode) } - return nil + common.LogInfo2Quiet("Removing k3s dependencies") + return uninstallHelperCommands(context.Background()) }