Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions cli/cmd/k0s/download_k0s.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ type DownloadK0sCmd struct {

type DownloadK0sOpts struct {
*util.GlobalOptions
Version string
Force bool
Quiet bool
Version string
Force bool
Quiet bool
AirGapped bool
}

func (c *DownloadK0sCmd) RunE(_ *cobra.Command, args []string) error {
Expand Down Expand Up @@ -66,6 +67,7 @@ func AddDownloadCmd(download *cobra.Command, opts *util.GlobalOptions) {
k0s.cmd.Flags().StringVarP(&k0s.Opts.Version, "version", "v", "", "Version of k0s to download")
k0s.cmd.Flags().BoolVarP(&k0s.Opts.Force, "force", "f", false, "Force download even if k0s binary exists")
k0s.cmd.Flags().BoolVarP(&k0s.Opts.Quiet, "quiet", "q", false, "Suppress progress output during download")
k0s.cmd.Flags().BoolVarP(&k0s.Opts.AirGapped, "airgapped", "a", false, "Downloads the airgapped bundle for that version")

util.AddCmd(download, k0s.cmd)

Expand All @@ -74,6 +76,7 @@ func AddDownloadCmd(download *cobra.Command, opts *util.GlobalOptions) {

func (c *DownloadK0sCmd) DownloadK0s(k0s installer.K0sManager) error {
version := c.Opts.Version

var err error
if version == "" {
version, err = k0s.GetLatestVersion()
Expand All @@ -82,12 +85,12 @@ func (c *DownloadK0sCmd) DownloadK0s(k0s installer.K0sManager) error {
}
}

k0sPath, err := k0s.Download(version, c.Opts.Force, c.Opts.Quiet)
k0sPath, err := k0s.Download(version, c.Opts.Force, c.Opts.Quiet, c.Opts.AirGapped)
if err != nil {
return fmt.Errorf("failed to download k0s: %w", err)
}

log.Printf("k0s binary downloaded successfully at '%s'", k0sPath)
log.Printf("k0s binary downloaded successfully to '%s'", k0sPath)

return nil
}
10 changes: 7 additions & 3 deletions cli/cmd/k0s/download_k0s_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ var _ = Describe("DownloadK0sk0s", func() {
mockK0sManager := installer.NewMockK0sManager(GinkgoT())

c.Opts.Version = "" // Test auto-version detection

mockK0sManager.EXPECT().GetLatestVersion().Return("", errors.New("network error"))

err := c.DownloadK0s(mockK0sManager)
Expand All @@ -64,7 +65,8 @@ var _ = Describe("DownloadK0sk0s", func() {
mockK0sManager := installer.NewMockK0sManager(GinkgoT())

c.Opts.Version = "v1.29.1+k0s.0"
mockK0sManager.EXPECT().Download("v1.29.1+k0s.0", false, false).Return("", errors.New("download failed"))

mockK0sManager.EXPECT().Download("v1.29.1+k0s.0", false, false, false).Return("", errors.New("download failed"))

err := c.DownloadK0s(mockK0sManager)
Expect(err).To(HaveOccurred())
Expand All @@ -76,7 +78,8 @@ var _ = Describe("DownloadK0sk0s", func() {
mockK0sManager := installer.NewMockK0sManager(GinkgoT())

c.Opts.Version = "v1.29.1+k0s.0"
mockK0sManager.EXPECT().Download("v1.29.1+k0s.0", false, false).Return("/test/workdir/k0s", nil)

mockK0sManager.EXPECT().Download("v1.29.1+k0s.0", false, false, false).Return("/test/workdir/k0s", nil)

err := c.DownloadK0s(mockK0sManager)
Expect(err).ToNot(HaveOccurred())
Expand All @@ -88,8 +91,9 @@ var _ = Describe("DownloadK0sk0s", func() {
c.Opts.Version = "" // Test auto-version detection
c.Opts.Force = true
c.Opts.Quiet = true

mockK0sManager.EXPECT().GetLatestVersion().Return("v1.29.1+k0s.0", nil)
mockK0sManager.EXPECT().Download("v1.29.1+k0s.0", true, true).Return("/test/workdir/k0s", nil)
mockK0sManager.EXPECT().Download("v1.29.1+k0s.0", true, true, false).Return("/test/workdir/k0s", nil)

err := c.DownloadK0s(mockK0sManager)
Expect(err).ToNot(HaveOccurred())
Expand Down
24 changes: 22 additions & 2 deletions cli/cmd/k0s/install_k0s.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ import (
intutil "github.com/codesphere-cloud/oms/internal/util"
)

// InstallK0sCmd represents the k0s download command
// InstallK0sCmd represents the k0s command
type InstallK0sCmd struct {
cmd *cobra.Command
Opts InstallK0sOpts
Env env.Env
FileWriter intutil.FileIO
}

// InstallK0sOpts holds the flags of the k0s install command
type InstallK0sOpts struct {
*util.GlobalOptions
Version string
Expand All @@ -43,6 +44,7 @@ type InstallK0sOpts struct {
VaultType string
}

// RunE is the starting point for the k0s install command that executes the bootstrap logic.
func (c *InstallK0sCmd) RunE(_ *cobra.Command, args []string) error {
hw := portal.NewHttpWrapper()
env := c.Env
Expand All @@ -53,6 +55,7 @@ func (c *InstallK0sCmd) RunE(_ *cobra.Command, args []string) error {
return c.InstallK0s(pm, k0s, k0sctl)
}

// AddInstallCmd registers the k0s install command in the parent command
func AddInstallCmd(install *cobra.Command, opts *util.GlobalOptions) {
k0s := InstallK0sCmd{
cmd: &cobra.Command{
Expand All @@ -79,6 +82,7 @@ func AddInstallCmd(install *cobra.Command, opts *util.GlobalOptions) {
Env: env.NewEnv(),
FileWriter: intutil.NewFilesystemWriter(),
}

k0s.cmd.Flags().StringVarP(&k0s.Opts.Version, "version", "v", installer.DefaultK0sVersion, "Version of k0s to install")
k0s.cmd.Flags().StringVar(&k0s.Opts.K0sctlVersion, "k0sctl-version", "", "Version of k0sctl to use")
k0s.cmd.Flags().StringVarP(&k0s.Opts.Package, "package", "p", "", "Package file (e.g. codesphere-v1.2.3-installer-lite.tar.gz) to load k0s from")
Expand All @@ -103,6 +107,8 @@ const (
vaultSecretNameKubeconfig = "kubeConfig"
)

// InstallK0s generates a k0sctl config based on the input parameters and installs k0s
// Returns an error if installation fails
func (c *InstallK0sCmd) InstallK0s(pm installer.PackageManager, k0s installer.K0sManager, k0sctl installer.K0sctlManager) error {
if err := c.FileWriter.MkdirAll(c.Env.GetOmsWorkdir(), 0755); err != nil {
return fmt.Errorf("failed to create oms workdir: %w", err)
Expand Down Expand Up @@ -163,12 +169,15 @@ func (c *InstallK0sCmd) determineK0sVersion(k0s installer.K0sManager) (string, e
k0sVersion := c.Opts.Version
if k0sVersion == "" {
var err error

k0sVersion, err = k0s.GetLatestVersion()
if err != nil {
return "", fmt.Errorf("failed to get latest k0s version: %w", err)
}

log.Printf("Using latest k0s version: %s", k0sVersion)
}

return k0sVersion, nil
}

Expand All @@ -181,27 +190,32 @@ func (c *InstallK0sCmd) getK0sBinaryPath(pm installer.PackageManager, k0s instal
if err := pm.ExtractDependency(defaultK0sPath, c.Opts.Force); err != nil {
return "", fmt.Errorf("failed to extract k0s from package: %w", err)
}

return pm.GetDependencyPath(defaultK0sPath), nil
}

k0sBinaryPath, err := k0s.Download(k0sVersion, c.Opts.Force, false)
k0sBinaryPath, err := k0s.Download(k0sVersion, c.Opts.Force, false, false)
if err != nil {
return "", fmt.Errorf("failed to download k0s: %w", err)
}

return k0sBinaryPath, nil
}

func (c *InstallK0sCmd) downloadK0sctl(k0sctl installer.K0sctlManager) (string, error) {
log.Println("Downloading k0sctl...")

k0sctlPath, err := k0sctl.Download(c.Opts.K0sctlVersion, c.Opts.Force, false)
if err != nil {
return "", fmt.Errorf("failed to download k0sctl: %w", err)
}

return k0sctlPath, nil
}

func (c *InstallK0sCmd) generateK0sctlConfig(config *files.RootConfig, k0sVersion string, k0sBinaryPath string) (string, error) {
log.Println("Generating k0sctl configuration from install-config...")

k0sctlConfig, err := installer.GenerateK0sctlConfig(config, k0sVersion, c.Opts.SSHKeyPath, k0sBinaryPath)
if err != nil {
return "", fmt.Errorf("failed to generate k0sctl config: %w", err)
Expand All @@ -218,11 +232,13 @@ func (c *InstallK0sCmd) generateK0sctlConfig(config *files.RootConfig, k0sVersio
}

log.Printf("Generated k0sctl configuration at %s", k0sctlConfigPath)

return k0sctlConfigPath, nil
}

func (c *InstallK0sCmd) deployK0sCluster(k0sctl installer.K0sctlManager, k0sctlPath string, k0sctlConfigPath string) error {
log.Println("Applying k0sctl configuration to deploy k0s cluster...")

if err := k0sctl.Apply(k0sctlConfigPath, k0sctlPath, c.Opts.Force); err != nil {
return fmt.Errorf("failed to apply k0sctl config: %w", err)
}
Expand All @@ -235,10 +251,12 @@ func (c *InstallK0sCmd) deployK0sCluster(k0sctl installer.K0sctlManager, k0sctlP

func (c *InstallK0sCmd) saveKubeconfigToVault(k0sctl installer.K0sctlManager, k0sctlConfigPath, k0sctlPath string) error {
log.Println("Retrieving kubeconfig from k0sctl for vault...")

kubeconfigContent, err := k0sctl.GetKubeconfig(k0sctlConfigPath, k0sctlPath)
if err != nil {
return fmt.Errorf("failed to retrieve kubeconfig from k0sctl: %w", err)
}

kubeconfigContent = strings.TrimRight(kubeconfigContent, "\n\r")

vault, err := c.loadOrCreateVault()
Expand Down Expand Up @@ -271,6 +289,7 @@ func (c *InstallK0sCmd) saveKubeconfigToVault(k0sctl installer.K0sctlManager, k0
}

log.Printf("Saved kubeconfig to %s", c.Opts.Vault)

return nil
}

Expand All @@ -293,5 +312,6 @@ func (c *InstallK0sCmd) vaultStore() (vault.Vault, error) {
if err != nil {
return nil, fmt.Errorf("failed to load vault: %w", err)
}

return vault, nil
}
18 changes: 16 additions & 2 deletions cli/cmd/k0s/install_k0s_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ var _ = Describe("InstallK0sCmd", func() {
Context("RunE method", func() {
It("fails when install-config is not provided", func() {
c.Opts.InstallConfig = ""

mockEnv.EXPECT().GetOmsWorkdir().Return("/test/workdir").Times(2)
mockFileWriter.EXPECT().MkdirAll("/test/workdir", os.FileMode(0755)).Return(nil)

Expand All @@ -85,7 +86,9 @@ var _ = Describe("InstallK0sCmd", func() {
mockPM = installer.NewMockPackageManager(GinkgoT())
mockK0s = installer.NewMockK0sManager(GinkgoT())
mockK0sctl = installer.NewMockK0sctlManager(GinkgoT())

var err error

tempDir, err = os.MkdirTemp("", "install-k0s-test-*")
Expect(err).NotTo(HaveOccurred())
})
Expand All @@ -94,6 +97,7 @@ var _ = Describe("InstallK0sCmd", func() {
mockPM.AssertExpectations(GinkgoT())
mockK0s.AssertExpectations(GinkgoT())
mockK0sctl.AssertExpectations(GinkgoT())

if tempDir != "" {
_ = os.RemoveAll(tempDir)
}
Expand Down Expand Up @@ -134,6 +138,7 @@ var _ = Describe("InstallK0sCmd", func() {
Expect(err).NotTo(HaveOccurred())
err = os.WriteFile(configPath, configData, 0644)
Expect(err).NotTo(HaveOccurred())

return configPath
}

Expand Down Expand Up @@ -184,7 +189,7 @@ var _ = Describe("InstallK0sCmd", func() {

mockEnv.EXPECT().GetOmsWorkdir().Return(tempDir)
mockFileWriter.EXPECT().MkdirAll(tempDir, os.FileMode(0755)).Return(nil)
mockK0s.EXPECT().Download("v1.29.0+k0s.0", false, false).Return("/downloaded/k0s", nil)
mockK0s.EXPECT().Download("v1.29.0+k0s.0", false, false, false).Return("/downloaded/k0s", nil)
mockK0sctl.EXPECT().Download("", false, false).Return("/tmp/k0sctl", nil)
mockFileWriter.EXPECT().WriteFile(mock.Anything, mock.Anything, mock.Anything).Return(nil)
mockK0sctl.EXPECT().Apply(mock.Anything, "/tmp/k0sctl", false).Return(nil)
Expand All @@ -200,7 +205,7 @@ var _ = Describe("InstallK0sCmd", func() {
mockEnv.EXPECT().GetOmsWorkdir().Return(tempDir)
mockFileWriter.EXPECT().MkdirAll(tempDir, os.FileMode(0755)).Return(nil)
mockK0s.EXPECT().GetLatestVersion().Return("v1.30.0+k0s.0", nil)
mockK0s.EXPECT().Download("v1.30.0+k0s.0", false, false).Return("", os.ErrNotExist)
mockK0s.EXPECT().Download("v1.30.0+k0s.0", false, false, false).Return("", os.ErrNotExist)

err := c.InstallK0s(mockPM, mockK0s, mockK0sctl)
Expect(err).To(HaveOccurred())
Expand Down Expand Up @@ -254,6 +259,7 @@ var _ = Describe("InstallK0sCmd", func() {
if !testutil.SopsAndAgeAvailable() {
Skip("sops and age-keygen not available")
}

c.FileWriter = intutil.NewFilesystemWriter()
})

Expand All @@ -278,6 +284,7 @@ var _ = Describe("InstallK0sCmd", func() {
Expect(err).NotTo(HaveOccurred())
loaded, err := backend.Load()
Expect(err).NotTo(HaveOccurred())

secret := loaded.GetSecret(files.SecretKubeConfig)
Expect(secret).NotTo(BeNil())
Expect(secret.File.Content).To(Equal("apiVersion: v1\nkind: Config"))
Expand Down Expand Up @@ -313,6 +320,7 @@ var _ = Describe("InstallK0sCmd", func() {

vaultYAML, err := existingVault.Marshal()
Expect(err).NotTo(HaveOccurred())

plainPath := c.Opts.Vault + ".plain"
err = os.WriteFile(plainPath, vaultYAML, 0600)
Expect(err).NotTo(HaveOccurred())
Expand Down Expand Up @@ -365,6 +373,7 @@ var _ = Describe("InstallK0sCmd", func() {
}
vaultYAML, err := existingVault.Marshal()
Expect(err).NotTo(HaveOccurred())

plainPath := c.Opts.Vault + ".plain"
err = os.WriteFile(plainPath, vaultYAML, 0600)
Expect(err).NotTo(HaveOccurred())
Expand All @@ -382,6 +391,7 @@ var _ = Describe("InstallK0sCmd", func() {
Expect(err).NotTo(HaveOccurred())
loaded, err := backend.Load()
Expect(err).NotTo(HaveOccurred())

secret := loaded.GetSecret(files.SecretKubeConfig)
Expect(secret).NotTo(BeNil())
Expect(secret.File.Content).To(Equal("apiVersion: v1\nkind: Config\nnew: true"))
Expand Down Expand Up @@ -411,6 +421,7 @@ var _ = Describe("InstallK0sCmd", func() {
Expect(err).NotTo(HaveOccurred())
loaded, err := backend.Load()
Expect(err).NotTo(HaveOccurred())

secret := loaded.GetSecret(files.SecretKubeConfig)
Expect(secret).NotTo(BeNil())
Expect(secret.File.Content).To(Equal("apiVersion: v1\nkind: Config"))
Expand Down Expand Up @@ -459,6 +470,7 @@ var _ = Describe("InstallK0sCmd", func() {
}
vaultYAML, err := existingVault.Marshal()
Expect(err).NotTo(HaveOccurred())

plainPath := vaultPath + ".plain"
err = os.WriteFile(plainPath, vaultYAML, 0600)
Expect(err).NotTo(HaveOccurred())
Expand Down Expand Up @@ -513,6 +525,7 @@ var _ = Describe("InstallK0sCmd", func() {
}
vaultYAML, err := existingVault.Marshal()
Expect(err).NotTo(HaveOccurred())

plainPath := vaultPath + ".plain"
err = os.WriteFile(plainPath, vaultYAML, 0600)
Expect(err).NotTo(HaveOccurred())
Expand Down Expand Up @@ -577,6 +590,7 @@ var _ = Describe("InstallK0sCmd", func() {
}
vaultYAML, err := existingVault.Marshal()
Expect(err).NotTo(HaveOccurred())

plainPath := vaultPath + ".plain"
err = os.WriteFile(plainPath, vaultYAML, 0600)
Expect(err).NotTo(HaveOccurred())
Expand Down
1 change: 1 addition & 0 deletions docs/oms_download_k0s.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ $ oms download k0s --force
### Options

```
-a, --airgapped Downloads the airgapped bundle for that version
-f, --force Force download even if k0s binary exists
-h, --help help for k0s
-q, --quiet Suppress progress output during download
Expand Down
17 changes: 13 additions & 4 deletions internal/installer/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,24 @@ import (
"github.com/codesphere-cloud/oms/internal/util"
)

func downloadBinaryToPath(fw util.FileIO, http portal.Http, binaryPath, binaryName, downloadURL string, quiet bool) (string, error) {
dstFile, err := fw.Create(binaryPath)
func downloadToPath(fw util.FileIO, http portal.Http, path, downloadURL string, quiet bool) error {
dstFile, err := fw.Create(path)
if err != nil {
return "", fmt.Errorf("failed to create %s binary file: %w", binaryName, err)
return fmt.Errorf("failed to create file: %s: %w", path, err)
}
defer util.CloseFileIgnoreError(dstFile)

if err := http.Download(downloadURL, dstFile, quiet); err != nil {
return "", fmt.Errorf("failed to download %s binary: %w", binaryName, err)
return fmt.Errorf("failed to download %s: %w", path, err)
}

return nil
}

func downloadBinaryToPath(fw util.FileIO, http portal.Http, binaryPath, binaryName, downloadURL string, quiet bool) (string, error) {
err := downloadToPath(fw, http, binaryPath, downloadURL, quiet)
if err != nil {
return "", fmt.Errorf("failed to download: %w", err)
}

if err := fw.Chmod(binaryPath, 0755); err != nil {
Expand Down
Loading