Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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
33 changes: 22 additions & 11 deletions cmd/minikube/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,13 +356,6 @@ func provisionWithDriver(cmd *cobra.Command, ds registry.DriverState, existing *
os.Exit(0)
}

// If preloadWindowsIso flag is true then cache the windows ISO
if viper.GetBool(preloadWindowsIso) {
if err := download.WindowsISO(viper.GetString(windowsNodeVersion)); err != nil {
return node.Starter{}, errors.Wrap(err, "Failed to cache Windows ISO")
}
}

if driver.IsVM(driverName) && !driver.IsSSH(driverName) {
url, err := download.ISO(viper.GetStringSlice(isoURL), cmd.Flags().Changed(isoURL))
if err != nil {
Expand Down Expand Up @@ -499,15 +492,36 @@ func startWithDriver(cmd *cobra.Command, starter node.Starter, existing *config.
KubernetesVersion: starter.Cfg.KubernetesConfig.KubernetesVersion,
ContainerRuntime: starter.Cfg.KubernetesConfig.ContainerRuntime,
Worker: true,
OS: "linux",
}
if i < numCPNodes { // starter node is also counted as (primary) cp node
n.ControlPlane = true
}
}

out.Ln("") // extra newline for clarity on the command line
// 1st call
if err := node.Add(starter.Cfg, n, viper.GetBool(deleteOnFailure)); err != nil {
return nil, errors.Wrap(err, "adding node")
return nil, errors.Wrap(err, "adding linux node")
}
}

// start windows node. trigger windows node start only if windows node version is set at the time of minikube start
if cmd.Flags().Changed(windowsNodeVersion) {
// TODO: if windows node version is set to windows server 2022 then the windows node name should be minikube-ws2022
nodeName := node.Name(numNodes + 1)
n := config.Node{
Name: nodeName,
Port: starter.Cfg.APIServerPort,
KubernetesVersion: starter.Cfg.KubernetesConfig.KubernetesVersion,
ContainerRuntime: starter.Cfg.KubernetesConfig.ContainerRuntime,
Worker: true,
OS: "windows",
}

out.Ln("") // extra newline for clarity on the command line
if err := node.Add(starter.Cfg, n, viper.GetBool(deleteOnFailure)); err != nil {
return nil, errors.Wrap(err, "adding windows node")
}
}

Expand Down Expand Up @@ -1316,9 +1330,6 @@ func validateFlags(cmd *cobra.Command, drvName string) { //nolint:gocyclo
exit.Message(reason.Usage, "{{.err}}", out.V{"err": err})
}

// set preloadWindowsIso to true since we need to download the windows ISO file
viper.Set(preloadWindowsIso, true)

}

if cmd.Flags().Changed(staticIP) {
Expand Down
2 changes: 0 additions & 2 deletions cmd/minikube/cmd/start_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ const (
ha = "ha"
nodes = "nodes"
preload = "preload"
preloadWindowsIso = "preload-windows-iso"
deleteOnFailure = "delete-on-failure"
forceSystemd = "force-systemd"
kicBaseImage = "base-image"
Expand Down Expand Up @@ -196,7 +195,6 @@ func initMinikubeFlags() {
startCmd.Flags().Bool(ha, false, "Create Highly Available Multi-Control Plane Cluster with a minimum of three control-plane nodes that will also be marked for work.")
startCmd.Flags().IntP(nodes, "n", 1, "The total number of nodes to spin up. Defaults to 1.")
startCmd.Flags().Bool(preload, true, "If set, download tarball of preloaded images if available to improve start time. Defaults to true.")
startCmd.Flags().Bool(preloadWindowsIso, false, "If set, download the Windows ISO to improve start time of setting up a windows node. Defaults to false.")
startCmd.Flags().Bool(noKubernetes, false, "If set, minikube VM/container will start without starting or configuring Kubernetes. (only works on new clusters)")
startCmd.Flags().Bool(deleteOnFailure, false, "If set, delete the current cluster if start fails and try again. Defaults to false.")
startCmd.Flags().Bool(forceSystemd, false, "If set, force the container runtime to use systemd as cgroup manager. Defaults to false.")
Expand Down
8 changes: 2 additions & 6 deletions cmd/minikube/cmd/start_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,16 +483,12 @@ func TestValidateWindowsOSVersion(t *testing.T) {
errorMsg string
}{
{
osVersion: "2019",
errorMsg: "",
},
{
osVersion: "2022",
osVersion: "2025",
errorMsg: "",
},
{
osVersion: "2023",
errorMsg: "Invalid Windows Server OS Version: 2023. Valid OS version are: [2019 2022]",
errorMsg: "Invalid Windows Server OS Version: 2023. Valid OS version are: [2025]",
},
}
for _, test := range tests {
Expand Down
4 changes: 4 additions & 0 deletions pkg/minikube/bootstrapper/bootstrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package bootstrapper
import (
"time"

"github.com/docker/machine/libmachine/host"
"k8s.io/minikube/pkg/minikube/bootstrapper/images"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/constants"
Expand All @@ -41,8 +42,11 @@ type Bootstrapper interface {
UpdateCluster(config.ClusterConfig) error
DeleteCluster(config.KubernetesConfig) error
WaitForNode(config.ClusterConfig, config.Node, time.Duration) error
SetupMinikubeCert(*host.Host) (string, error)
JoinClusterWindows(*host.Host, config.ClusterConfig, config.Node, string, time.Duration) (string, error)
JoinCluster(config.ClusterConfig, config.Node, string) error
UpdateNode(config.ClusterConfig, config.Node, cruntime.Manager) error
GenerateTokenWindows(config.ClusterConfig) (string, error)
GenerateToken(config.ClusterConfig) (string, error)
// LogCommands returns a map of log type to a command which will display that log.
LogCommands(config.ClusterConfig, LogOptions) map[string]string
Expand Down
5 changes: 5 additions & 0 deletions pkg/minikube/bootstrapper/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ type sharedCACerts struct {

// SetupCerts gets the generated credentials required to talk to the APIServer.
func SetupCerts(k8s config.ClusterConfig, n config.Node, pcpCmd command.Runner, cmd command.Runner) error {
// no need to setup certs for windows worker nodes as the master node already took care of this
if n.OS == "windows" {
return nil
}

localPath := localpath.Profile(k8s.KubernetesConfig.ClusterName)
klog.Infof("Setting up %s for IP: %s", localPath, n.IP)

Expand Down
103 changes: 103 additions & 0 deletions pkg/minikube/bootstrapper/kubeadm/kubeadm.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

"github.com/blang/semver/v4"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/host"
"github.com/docker/machine/libmachine/state"
"github.com/pkg/errors"
core "k8s.io/api/core/v1"
Expand All @@ -58,7 +59,7 @@
"k8s.io/minikube/pkg/minikube/detect"
"k8s.io/minikube/pkg/minikube/driver"
"k8s.io/minikube/pkg/minikube/kubeconfig"
"k8s.io/minikube/pkg/minikube/machine"

Check failure on line 62 in pkg/minikube/bootstrapper/kubeadm/kubeadm.go

View workflow job for this annotation

GitHub Actions / lint

could not import k8s.io/minikube/pkg/minikube/machine (-: # k8s.io/minikube/pkg/minikube/machine
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/style"
Expand Down Expand Up @@ -751,6 +752,78 @@
return nil
}

//
//

func (k *Bootstrapper) SetupMinikubeCert(host *host.Host) (string, error) {
out.Step(style.Provisioning, "Setting up minikube certificates folder...")

certsDir := `C:\var\lib\minikube\certs`
k8sPkiDir := `C:\etc\kubernetes\pki`
caCert := `ca.crt`

script := fmt.Sprintf(
`mkdir %s; `+
`Copy-Item %s\%s -Destination %s; `+
`Remove-Item %s\%s`,
certsDir, k8sPkiDir, caCert, certsDir, k8sPkiDir, caCert,
)

script = strings.ReplaceAll(script, `"`, `\"`)

command := fmt.Sprintf("powershell -NoProfile -NonInteractive -Command \"%s\"", script)
klog.Infof("[executing] : %v", command)

host.RunSSHCommand(command)

return "", nil
}

func (k *Bootstrapper) JoinClusterWindows(host *host.Host, cc config.ClusterConfig, n config.Node, joinCmd string, timeout time.Duration) (string, error) {
setLocationPath := `Set-Location -Path "C:\k"`

psScript := fmt.Sprintf("%s; %s", setLocationPath, joinCmd)

psScript = strings.ReplaceAll(psScript, `"`, `\"`)

command := fmt.Sprintf("powershell -NoProfile -NonInteractive -Command \"%s\"", psScript)
klog.Infof("[executing] : %v", command)

// TODO: Explore how to make this better; channels for result and errors for now exist
resultChan := make(chan string, 1)
errorChan := make(chan error, 1)

go func() {
output, err := host.RunSSHCommand(command)
if err != nil {
errorChan <- err
return
}

resultChan <- output
}()

if timeout > 0 {
// If timeout is set, enforce it
select {
case result := <-resultChan:
return result, nil
case err := <-errorChan:
return "", err
case <-time.After(timeout):
return "", fmt.Errorf("operation timed out after %s", timeout)
}
} else {
// If no timeout is set, just wait for result or error
select {
case result := <-resultChan:
return result, nil
case err := <-errorChan:
return "", err
}
}
}

// JoinCluster adds new node to an existing cluster.
func (k *Bootstrapper) JoinCluster(cc config.ClusterConfig, n config.Node, joinCmd string) error {
// Join the control plane by specifying its token
Expand Down Expand Up @@ -778,6 +851,30 @@
return nil
}

// GenerateToken creates a token and returns the appropriate kubeadm join command to run, or the already existing token
func (k *Bootstrapper) GenerateTokenWindows(cc config.ClusterConfig) (string, error) {
tokenCmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("%s token create --print-join-command --ttl=0", bsutil.InvokeKubeadm(cc.KubernetesConfig.KubernetesVersion)))
r, err := k.c.RunCmd(tokenCmd)
if err != nil {
return "", errors.Wrap(err, "generating join command")
}

joinCmd := r.Stdout.String()
// log the join command for debugging purposes
klog.Infof("Generated join command ===: %s", joinCmd)
joinCmd = strings.Replace(joinCmd, "kubeadm", ".\\kubeadm.exe", 1)
joinCmd = fmt.Sprintf("%s --ignore-preflight-errors=all", strings.TrimSpace(joinCmd))

// append the cri-socket flag to the join command for windows
joinCmd = fmt.Sprintf("%s --cri-socket \"npipe:////./pipe/containerd-containerd\"", joinCmd)

// append --v=5 to the join command for windows
joinCmd = fmt.Sprintf("%s --v=5", joinCmd)

return joinCmd, nil

}

// GenerateToken creates a token and returns the appropriate kubeadm join command to run, or the already existing token
func (k *Bootstrapper) GenerateToken(cc config.ClusterConfig) (string, error) {
// Take that generated token and use it to get a kubeadm join command
Expand Down Expand Up @@ -931,6 +1028,12 @@

// UpdateNode updates new or existing node.
func (k *Bootstrapper) UpdateNode(cfg config.ClusterConfig, n config.Node, r cruntime.Manager) error {
// skip if the node is a windows node
if n.OS == "windows" {
klog.Infof("skipping node %v update, as it is a windows node", n)
return nil
}

klog.Infof("updating node %v ...", n)

kubeletCfg, err := bsutil.NewKubeletConfig(cfg, n, r)
Expand Down
1 change: 1 addition & 0 deletions pkg/minikube/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ type Node struct {
ContainerRuntime string
ControlPlane bool
Worker bool
OS string
}

// VersionedExtraOption holds information on flags to apply to a specific range
Expand Down
7 changes: 3 additions & 4 deletions pkg/minikube/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import (
var (
// SupportedArchitectures is the list of supported architectures
SupportedArchitectures = [5]string{"amd64", "arm", "arm64", "ppc64le", "s390x"}
// IP Address for the control plane
MasterNodeIP = ""
)

const (
Expand Down Expand Up @@ -165,10 +167,7 @@ const (
AliyunMirror = "registry.cn-hangzhou.aliyuncs.com/google_containers"

// DefaultWindowsNodeVersion is the default version of Windows node
DefaultWindowsNodeVersion = "2022"

// Windows Server ISO URL
DefaultWindowsServerIsoURL = "https://go.microsoft.com/fwlink/p/?LinkID=2195280&clcid=0x409&culture=en-us&country=US"
DefaultWindowsNodeVersion = "2025"
)

var (
Expand Down
42 changes: 0 additions & 42 deletions pkg/minikube/download/iso.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ package download

import (
"fmt"
"mime"
"net/http"
"net/url"
"os"
"path"
Expand All @@ -31,16 +29,13 @@ import (
"github.com/juju/mutex/v2"
"github.com/pkg/errors"
"k8s.io/klog/v2"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/detect"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/util/lock"
"k8s.io/minikube/pkg/version"
)

var isWindowsISO bool

const fileScheme = "file"

// DefaultISOURLs returns a list of ISO URL's to consult by default, in priority order
Expand All @@ -55,22 +50,6 @@ func DefaultISOURLs() []string {
}
}

// WindowsISOURL retrieves the ISO URL for the Windows version specified
func WindowsISOURL(version string) string {
versionToIsoURL := map[string]string{
"2022": constants.DefaultWindowsServerIsoURL,
// Add more versions here when we support them
}

url, exists := versionToIsoURL[version]
if !exists {
klog.Warningf("Windows version %s is not supported. Using default Windows Server ISO URL", version)
return constants.DefaultWindowsServerIsoURL
}

return url
}

// LocalISOResource returns a local file:// URI equivalent for a local or remote ISO path
func LocalISOResource(isoURL string) string {
u, err := url.Parse(isoURL)
Expand Down Expand Up @@ -124,12 +103,6 @@ func ISO(urls []string, skipChecksum bool) (string, error) {
return "", fmt.Errorf(msg.String())
}

func WindowsISO(windowsVersion string) error {
isWindowsISO = true
isoURL := WindowsISOURL(windowsVersion)
return downloadISO(isoURL, false)
}

// downloadISO downloads an ISO URL
func downloadISO(isoURL string, skipChecksum bool) error {
u, err := url.Parse(isoURL)
Expand All @@ -144,21 +117,6 @@ func downloadISO(isoURL string, skipChecksum bool) error {

// Lock before we check for existence to avoid thundering herd issues
dst := localISOPath(u)
if isWindowsISO {
resp, err := http.Head(isoURL)
if err != nil {
return errors.Wrapf(err, "HEAD %s", isoURL)
}

_, params, err := mime.ParseMediaType(resp.Header.Get("Content-Disposition"))
if err != nil {
return errors.Wrapf(err, "ParseMediaType %s", resp.Header.Get("Content-Disposition"))
}

dst = filepath.Join(detect.ISOCacheDir(), params["filename"])

isWindowsISO = false
}

if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil {
return errors.Wrapf(err, "making cache image directory: %s", dst)
Expand Down
Loading
Loading