Merge pull request #53553 from bsteciuk/kubeadm-windows

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Kubeadm - Added initial support for Windows worker nodes to join cluster using kubeadm

**What this PR does / why we need it**:
This PR adds initial support for adding a Windows worker node to a Kubernetes cluster with kubeadm.  Also adds Windows build of kubeadm to node build targets. 

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes https://github.com/kubernetes/kubeadm/issues/364 

**Special notes for your reviewer**:

Depends on #53730 

**Release note**:

```release-note
kubeadm: Add support for adding a Windows node
```
pull/6/head
Kubernetes Submit Queue 2017-11-01 14:58:58 -07:00 committed by GitHub
commit 574492aed6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 221 additions and 40 deletions

View File

@ -111,6 +111,7 @@ release_filegroup(
name = "node-targets",
srcs = [
"//cmd/kube-proxy",
"//cmd/kubeadm",
"//cmd/kubelet",
],
)
@ -125,7 +126,6 @@ release_filegroup(
"//cmd/hyperkube",
"//cmd/kube-apiserver",
"//cmd/kube-controller-manager",
"//cmd/kubeadm",
"//plugin/cmd/kube-scheduler",
"//vendor/k8s.io/kube-aggregator",
],

View File

@ -9,13 +9,19 @@ go_library(
name = "go_default_library",
srcs = [
"defaults.go",
"defaults_unix.go",
"doc.go",
"register.go",
"types.go",
"zz_generated.conversion.go",
"zz_generated.deepcopy.go",
"zz_generated.defaults.go",
],
] + select({
"@io_bazel_rules_go//go/platform:windows_amd64": [
"defaults_windows.go",
],
"//conditions:default": [],
}),
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1",
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",

View File

@ -36,8 +36,6 @@ const (
DefaultAPIBindPort = 6443
// DefaultAuthorizationModes defines default authorization modes
DefaultAuthorizationModes = "Node,RBAC"
// DefaultCACertPath defines default location of CA certificate
DefaultCACertPath = "/etc/kubernetes/pki/ca.crt"
// DefaultCertificatesDir defines default certificate directory
DefaultCertificatesDir = "/etc/kubernetes/pki"
// DefaultImageRepository defines default image registry

View File

@ -0,0 +1,22 @@
// +build !windows
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
// DefaultCACertPath defines default location of CA certificate on Linux
const DefaultCACertPath = "/etc/kubernetes/pki/ca.crt"

View File

@ -0,0 +1,22 @@
// +build windows
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
// DefaultCACertPath defines default location of CA certificate on Windows
const DefaultCACertPath = "C:/etc/kubernetes/pki/ca.crt"

View File

@ -10,8 +10,14 @@ go_library(
name = "go_default_library",
srcs = [
"checks.go",
"checks_unix.go",
"utils.go",
],
] + select({
"@io_bazel_rules_go//go/platform:windows_amd64": [
"checks_windows.go",
],
"//conditions:default": [],
}),
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/preflight",
deps = [
"//cmd/kube-apiserver/app/options:go_default_library",

View File

@ -28,6 +28,7 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
@ -166,18 +167,8 @@ func (poc PortOpenCheck) Check() (warnings, errors []error) {
return nil, errors
}
// IsRootCheck verifies user is root
type IsRootCheck struct{}
// Check validates if an user has root privileges.
func (irc IsRootCheck) Check() (warnings, errors []error) {
errors = []error{}
if os.Getuid() != 0 {
errors = append(errors, fmt.Errorf("user is not running as root"))
}
return nil, errors
}
// IsPrivilegedUserCheck verifies user is privileged (linux - root, windows - Administrator)
type IsPrivilegedUserCheck struct{}
// DirAvailableCheck checks if the given directory either does not exist, or is empty.
type DirAvailableCheck struct {
@ -438,12 +429,16 @@ func (sysver SystemVerificationCheck) Check() (warnings, errors []error) {
var errs []error
var warns []error
// All the validators we'd like to run:
// All the common validators we'd like to run:
var validators = []system.Validator{
&system.OSValidator{Reporter: reporter},
&system.KernelValidator{Reporter: reporter},
&system.CgroupsValidator{Reporter: reporter},
&system.DockerValidator{Reporter: reporter},
&system.DockerValidator{Reporter: reporter}}
if runtime.GOOS == "linux" {
//add linux validators
validators = append(validators,
&system.OSValidator{Reporter: reporter},
&system.CgroupsValidator{Reporter: reporter})
}
// Run all validators
@ -691,7 +686,7 @@ func RunInitMasterChecks(cfg *kubeadmapi.MasterConfiguration) error {
checks := []Checker{
KubernetesVersionCheck{KubernetesVersion: cfg.KubernetesVersion, KubeadmVersion: kubeadmversion.Get().GitVersion},
SystemVerificationCheck{},
IsRootCheck{},
IsPrivilegedUserCheck{},
HostnameCheck{nodeName: cfg.NodeName},
KubeletVersionCheck{},
ServiceCheck{Service: "kubelet", CheckIfActive: false},
@ -774,7 +769,7 @@ func RunJoinNodeChecks(cfg *kubeadmapi.NodeConfiguration) error {
checks := []Checker{
SystemVerificationCheck{},
IsRootCheck{},
IsPrivilegedUserCheck{},
HostnameCheck{cfg.NodeName},
KubeletVersionCheck{},
ServiceCheck{Service: "kubelet", CheckIfActive: false},
@ -783,17 +778,21 @@ func RunJoinNodeChecks(cfg *kubeadmapi.NodeConfiguration) error {
DirAvailableCheck{Path: filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ManifestsSubDirName)},
FileAvailableCheck{Path: cfg.CACertPath},
FileAvailableCheck{Path: filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.KubeletKubeConfigFileName)},
FileContentCheck{Path: bridgenf, Content: []byte{'1'}},
SwapCheck{},
InPathCheck{executable: "ip", mandatory: true},
InPathCheck{executable: "iptables", mandatory: true},
InPathCheck{executable: "mount", mandatory: true},
InPathCheck{executable: "nsenter", mandatory: true},
InPathCheck{executable: "ebtables", mandatory: false},
InPathCheck{executable: "ethtool", mandatory: false},
InPathCheck{executable: "socat", mandatory: false},
InPathCheck{executable: "tc", mandatory: false},
InPathCheck{executable: "touch", mandatory: false},
}
//non-windows checks
if runtime.GOOS == "linux" {
checks = append(checks,
FileContentCheck{Path: bridgenf, Content: []byte{'1'}},
SwapCheck{},
InPathCheck{executable: "ip", mandatory: true},
InPathCheck{executable: "iptables", mandatory: true},
InPathCheck{executable: "mount", mandatory: true},
InPathCheck{executable: "nsenter", mandatory: true},
InPathCheck{executable: "ebtables", mandatory: false},
InPathCheck{executable: "ethtool", mandatory: false},
InPathCheck{executable: "socat", mandatory: false},
InPathCheck{executable: "tc", mandatory: false},
InPathCheck{executable: "touch", mandatory: false})
}
if len(cfg.DiscoveryTokenAPIServers) > 0 {
@ -808,10 +807,10 @@ func RunJoinNodeChecks(cfg *kubeadmapi.NodeConfiguration) error {
return RunChecks(checks, os.Stderr)
}
// RunRootCheckOnly initializes cheks slice of structs and call RunChecks
// RunRootCheckOnly initializes checks slice of structs and call RunChecks
func RunRootCheckOnly() error {
checks := []Checker{
IsRootCheck{},
IsPrivilegedUserCheck{},
}
return RunChecks(checks, os.Stderr)

View File

@ -0,0 +1,34 @@
// +build !windows
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package preflight
import (
"fmt"
"os"
)
// Check validates if an user has elevated (root) privileges.
func (ipuc IsPrivilegedUserCheck) Check() (warnings, errors []error) {
errors = []error{}
if os.Getuid() != 0 {
errors = append(errors, fmt.Errorf("user is not running as root"))
}
return nil, errors
}

View File

@ -0,0 +1,44 @@
// +build windows
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package preflight
import (
"fmt"
"os/exec"
"strings"
)
// Check validates if an user has elevated (administrator) privileges.
func (ipuc IsPrivilegedUserCheck) Check() (warnings, errors []error) {
errors = []error{}
// The "Well-known SID" of Administrator group is S-1-5-32-544
// The following powershell will return "True" if run as an administrator, "False" otherwise
// See https://msdn.microsoft.com/en-us/library/cc980032.aspx
args := []string{"[bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match \"S-1-5-32-544\")"}
isAdmin, err := exec.Command("powershell", args...).Output()
if err != nil {
errors = append(errors, fmt.Errorf("unable to determine if user is running as administrator: %s", err))
} else if strings.EqualFold(strings.TrimSpace(string(isAdmin)), "false") {
errors = append(errors, fmt.Errorf("user is not running as administrator"))
}
return nil, errors
}

View File

@ -44,6 +44,7 @@ readonly KUBE_SERVER_BINARIES=("${KUBE_SERVER_TARGETS[@]##*/}")
kube::golang::node_targets() {
local targets=(
cmd/kube-proxy
cmd/kubeadm
cmd/kubelet
)
echo "${targets[@]}"

View File

@ -43,13 +43,13 @@ type SystemdInitSystem struct{}
func (sysd SystemdInitSystem) ServiceStart(service string) error {
args := []string{"start", service}
_, err := exec.Command("systemctl", args...).Output()
err := exec.Command("systemctl", args...).Run()
return err
}
func (sysd SystemdInitSystem) ServiceStop(service string) error {
args := []string{"stop", service}
_, err := exec.Command("systemctl", args...).Output()
err := exec.Command("systemctl", args...).Run()
return err
}
@ -65,7 +65,7 @@ func (sysd SystemdInitSystem) ServiceExists(service string) bool {
func (sysd SystemdInitSystem) ServiceIsEnabled(service string) bool {
args := []string{"is-enabled", service}
_, err := exec.Command("systemctl", args...).Output()
err := exec.Command("systemctl", args...).Run()
if err != nil {
return false
}
@ -86,7 +86,52 @@ func (sysd SystemdInitSystem) ServiceIsActive(service string) bool {
return false
}
// getInitSystem returns an InitSystem for the current system, or nil
// WindowsInitSystem is the windows implementation of InitSystem
type WindowsInitSystem struct{}
func (sysd WindowsInitSystem) ServiceStart(service string) error {
args := []string{"Start-Service", service}
err := exec.Command("powershell", args...).Run()
return err
}
func (sysd WindowsInitSystem) ServiceStop(service string) error {
args := []string{"Stop-Service", service}
err := exec.Command("powershell", args...).Run()
return err
}
func (sysd WindowsInitSystem) ServiceExists(service string) bool {
args := []string{"Get-Service", service}
err := exec.Command("powershell", args...).Run()
if err != nil {
return false
}
return true
}
func (sysd WindowsInitSystem) ServiceIsEnabled(service string) bool {
args := []string{"Get-Service", service + "| select -property starttype"}
outBytes, _ := exec.Command("powershell", args...).Output()
output := strings.TrimSpace(string(outBytes))
if strings.Contains(output, "Automatic") {
return true
}
return false
}
func (sysd WindowsInitSystem) ServiceIsActive(service string) bool {
args := []string{"Get-Service", service + "| select -property status"}
outBytes, _ := exec.Command("powershell", args...).Output()
output := strings.TrimSpace(string(outBytes))
if strings.Contains(output, "Running") {
return true
}
return false
}
// GetInitSystem returns an InitSystem for the current system, or nil
// if we cannot detect a supported init system for pre-flight checks.
// This indicates we will skip init system checks, not an error.
func GetInitSystem() (InitSystem, error) {
@ -95,5 +140,9 @@ func GetInitSystem() (InitSystem, error) {
if err == nil {
return &SystemdInitSystem{}, nil
}
_, err = exec.LookPath("wininit.exe")
if err == nil {
return &WindowsInitSystem{}, nil
}
return nil, fmt.Errorf("no supported init system detected, skipping checking for services")
}