Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
14bc6e3
feat: add download and install k0s command
siherrmann Nov 4, 2025
5cb3517
update: update logs with oms workdir for k0s start command
siherrmann Nov 5, 2025
b41ce6c
fix: fix lint errors
siherrmann Nov 5, 2025
2e333c6
fix: fix lint errors
siherrmann Nov 5, 2025
66a7ed3
chore(docs): Auto-update docs and licenses
siherrmann Nov 5, 2025
69495fa
Merge branch 'main' into install-k0s
siherrmann Nov 5, 2025
43e5f95
update: update install k0s command to also use package
siherrmann Nov 10, 2025
b670405
feat: add download and install k0s command
siherrmann Nov 4, 2025
2644abf
update: update logs with oms workdir for k0s start command
siherrmann Nov 5, 2025
d31b396
fix: fix lint errors
siherrmann Nov 5, 2025
716a765
fix: fix lint errors
siherrmann Nov 5, 2025
e2197ba
chore(docs): Auto-update docs and licenses
siherrmann Nov 5, 2025
8c6186b
update: update install k0s command to also use package
siherrmann Nov 10, 2025
3c8f6c0
Merge branch 'install-k0s' of https://github.com/codesphere-cloud/oms…
siherrmann Nov 11, 2025
15c182b
chore(docs): Auto-update docs and licenses
siherrmann Nov 11, 2025
c9b6530
fix: fix portal tests
siherrmann Nov 11, 2025
a009027
Merge branch 'install-k0s' of https://github.com/codesphere-cloud/oms…
siherrmann Nov 11, 2025
ad10f12
fix: fix lint error omit type
siherrmann Nov 11, 2025
e08a7c1
fix: remove tests with real network calls
siherrmann Nov 11, 2025
adeac05
Merge branch 'main' into install-k0s
NautiluX Nov 13, 2025
1580667
chore(docs): Auto-update docs and licenses
siherrmann Nov 13, 2025
500ebcf
update: small updates for pr
siherrmann Nov 17, 2025
1894f59
Merge branch 'main' into install-k0s
siherrmann Nov 17, 2025
873ebbc
update: update mocks after merge
siherrmann Nov 17, 2025
b0b1aee
chore(docs): Auto-update docs and licenses
siherrmann Nov 17, 2025
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
1 change: 1 addition & 0 deletions cli/cmd/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ func AddDownloadCmd(rootCmd *cobra.Command, opts *GlobalOptions) {
rootCmd.AddCommand(download.cmd)

AddDownloadPackageCmd(download.cmd, opts)
AddDownloadK0sCmd(download.cmd, opts)
}
92 changes: 92 additions & 0 deletions cli/cmd/download_k0s.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright (c) Codesphere Inc.
// SPDX-License-Identifier: Apache-2.0

package cmd

import (
"fmt"
"log"

packageio "github.com/codesphere-cloud/cs-go/pkg/io"
"github.com/spf13/cobra"

"github.com/codesphere-cloud/oms/internal/env"
"github.com/codesphere-cloud/oms/internal/installer"
"github.com/codesphere-cloud/oms/internal/portal"
"github.com/codesphere-cloud/oms/internal/util"
)

// DownloadK0sCmd represents the k0s download command
type DownloadK0sCmd struct {
cmd *cobra.Command
Opts DownloadK0sOpts
Env env.Env
FileWriter util.FileIO
}

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

func (c *DownloadK0sCmd) RunE(_ *cobra.Command, args []string) error {
hw := portal.NewHttpWrapper()
env := c.Env
k0s := installer.NewK0s(hw, env, c.FileWriter)

err := c.DownloadK0s(k0s)
if err != nil {
return fmt.Errorf("failed to download k0s: %w", err)
}

return nil
}

func AddDownloadK0sCmd(download *cobra.Command, opts *GlobalOptions) {
k0s := DownloadK0sCmd{
cmd: &cobra.Command{
Use: "k0s",
Short: "Download k0s Kubernetes distribution",
Long: packageio.Long(`Download a k0s binary directly to the OMS workdir.
Will download the latest version if no version is specified.`),
Example: formatExamplesWithBinary("download k0s", []packageio.Example{
{Cmd: "", Desc: "Download k0s using the Go-native implementation"},
{Cmd: "--version 1.22.0", Desc: "Download a specific version of k0s"},
{Cmd: "--quiet", Desc: "Download k0s with minimal output"},
{Cmd: "--force", Desc: "Force download even if k0s binary exists"},
}, "oms-cli"),
},
Opts: DownloadK0sOpts{GlobalOptions: opts},
Env: env.NewEnv(),
FileWriter: util.NewFilesystemWriter(),
}
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")

download.AddCommand(k0s.cmd)

k0s.cmd.RunE = k0s.RunE
}

func (c *DownloadK0sCmd) DownloadK0s(k0s installer.K0sManager) error {
version := c.Opts.Version
var err error
if version == "" {
version, err = k0s.GetLatestVersion()
if err != nil {
return fmt.Errorf("failed to get latest k0s version: %w", err)
}
}

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

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

return nil
}
97 changes: 97 additions & 0 deletions cli/cmd/download_k0s_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright (c) Codesphere Inc.
// SPDX-License-Identifier: Apache-2.0

package cmd_test

import (
"errors"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"github.com/codesphere-cloud/oms/cli/cmd"
"github.com/codesphere-cloud/oms/internal/env"
"github.com/codesphere-cloud/oms/internal/installer"
"github.com/codesphere-cloud/oms/internal/util"
)

var _ = Describe("DownloadK0sCmd", func() {
var (
c cmd.DownloadK0sCmd
opts *cmd.DownloadK0sOpts
globalOpts *cmd.GlobalOptions
mockEnv *env.MockEnv
mockFileWriter *util.MockFileIO
)

BeforeEach(func() {
mockEnv = env.NewMockEnv(GinkgoT())
mockFileWriter = util.NewMockFileIO(GinkgoT())
globalOpts = &cmd.GlobalOptions{}
opts = &cmd.DownloadK0sOpts{
GlobalOptions: globalOpts,
Version: "",
Force: false,
Quiet: false,
}
c = cmd.DownloadK0sCmd{
Opts: *opts,
Env: mockEnv,
FileWriter: mockFileWriter,
}
})

AfterEach(func() {
mockEnv.AssertExpectations(GinkgoT())
mockFileWriter.AssertExpectations(GinkgoT())
})

Context("DownloadK0s method", func() {
It("fails when k0s manager fails to get latest version", func() {
mockK0sManager := installer.NewMockK0sManager(GinkgoT())

c.Opts.Version = "" // Test auto-version detection
mockK0sManager.EXPECT().GetLatestVersion().Return("", errors.New("network error"))

err := c.DownloadK0s(mockK0sManager)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("failed to get latest k0s version"))
Expect(err.Error()).To(ContainSubstring("network error"))
})

It("fails when k0s manager fails to download", 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"))

err := c.DownloadK0s(mockK0sManager)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("failed to download k0s"))
Expect(err.Error()).To(ContainSubstring("download failed"))
})

It("succeeds when version is specified and download works", 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)

err := c.DownloadK0s(mockK0sManager)
Expect(err).ToNot(HaveOccurred())
})

It("succeeds when version is auto-detected and download works", func() {
mockK0sManager := installer.NewMockK0sManager(GinkgoT())

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)

err := c.DownloadK0s(mockK0sManager)
Expect(err).ToNot(HaveOccurred())
})
})
})
2 changes: 2 additions & 0 deletions cli/cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,7 @@ func AddInstallCmd(rootCmd *cobra.Command, opts *GlobalOptions) {
},
}
rootCmd.AddCommand(install.cmd)

AddInstallCodesphereCmd(install.cmd, opts)
AddInstallK0sCmd(install.cmd, opts)
}
100 changes: 100 additions & 0 deletions cli/cmd/install_k0s.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright (c) Codesphere Inc.
// SPDX-License-Identifier: Apache-2.0

package cmd

import (
"fmt"

packageio "github.com/codesphere-cloud/cs-go/pkg/io"
"github.com/spf13/cobra"

"github.com/codesphere-cloud/oms/internal/env"
"github.com/codesphere-cloud/oms/internal/installer"
"github.com/codesphere-cloud/oms/internal/portal"
"github.com/codesphere-cloud/oms/internal/util"
)

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

type InstallK0sOpts struct {
*GlobalOptions
Version string
Package string
Config string
Force bool
}

func (c *InstallK0sCmd) RunE(_ *cobra.Command, args []string) error {
hw := portal.NewHttpWrapper()
env := c.Env
pm := installer.NewPackage(env.GetOmsWorkdir(), c.Opts.Package)
k0s := installer.NewK0s(hw, env, c.FileWriter)

err := c.InstallK0s(pm, k0s)
if err != nil {
return fmt.Errorf("failed to install k0s: %w", err)
}

return nil
}

func AddInstallK0sCmd(install *cobra.Command, opts *GlobalOptions) {
k0s := InstallK0sCmd{
cmd: &cobra.Command{
Use: "k0s",
Short: "Install k0s Kubernetes distribution",
Long: packageio.Long(`Install k0s either from the package or by downloading it.
This will either download the k0s binary directly to the OMS workdir, if not already present, and install it
or load the k0s binary from the provided package file and install it.
If no version is specified, the latest version will be downloaded.
If no install config is provided, k0s will be installed with the '--single' flag.`),
Example: formatExamplesWithBinary("install k0s", []packageio.Example{
{Cmd: "", Desc: "Install k0s using the Go-native implementation"},
{Cmd: "--version <version>", Desc: "Version of k0s to install"},
{Cmd: "--package <file>", Desc: "Package file (e.g. codesphere-v1.2.3-installer.tar.gz) to load k0s from"},
{Cmd: "--k0s-config <path>", Desc: "Path to k0s configuration file, if not set k0s will be installed with the '--single' flag"},
{Cmd: "--force", Desc: "Force new download and installation even if k0s binary exists or is already installed"},
}, "oms-cli"),
},
Opts: InstallK0sOpts{GlobalOptions: opts},
Env: env.NewEnv(),
FileWriter: util.NewFilesystemWriter(),
}
k0s.cmd.Flags().StringVarP(&k0s.Opts.Version, "version", "v", "", "Version of k0s to install")
k0s.cmd.Flags().StringVarP(&k0s.Opts.Package, "package", "p", "", "Package file (e.g. codesphere-v1.2.3-installer.tar.gz) to load k0s from")
k0s.cmd.Flags().StringVar(&k0s.Opts.Config, "k0s-config", "", "Path to k0s configuration file")
k0s.cmd.Flags().BoolVarP(&k0s.Opts.Force, "force", "f", false, "Force new download and installation")

install.AddCommand(k0s.cmd)

k0s.cmd.RunE = k0s.RunE
}

const defaultK0sPath = "kubernetes/files/k0s"

func (c *InstallK0sCmd) InstallK0s(pm installer.PackageManager, k0s installer.K0sManager) error {
// Default dependency path for k0s binary within package
k0sPath := pm.GetDependencyPath(defaultK0sPath)

var err error
if c.Opts.Package == "" {
k0sPath, err = k0s.Download(c.Opts.Version, c.Opts.Force, false)
if err != nil {
return fmt.Errorf("failed to download k0s: %w", err)
}
}

err = k0s.Install(c.Opts.Config, k0sPath, c.Opts.Force)
if err != nil {
return fmt.Errorf("failed to install k0s: %w", err)
}

return nil
}
Loading