Merge branch 'master' into int-ughs-local

pull/6119/head
Thomas Stromberg 2020-01-09 15:09:46 -08:00
commit 9611d7e7e2
173 changed files with 4971 additions and 1929 deletions

View File

@ -1,5 +1,29 @@
# Release Notes
## Version 1.6.2 - 2019-12-19
* Offline: always transfer image if lookup fails, always download drivers [#6111](https://github.com/kubernetes/minikube/pull/6111)
* Update ingress-dns addon [#6091](https://github.com/kubernetes/minikube/pull/6091)
* Fix update-context to use KUBECONFIG when the env is set [#6090](https://github.com/kubernetes/minikube/pull/6090)
* Fixed IPv6 format for SSH [#6110](https://github.com/kubernetes/minikube/pull/6110)
* Add hyperkit version check whether user's hyperkit is newer [#5833](https://github.com/kubernetes/minikube/pull/5833)
* start: Remove create/delete retry loop [#6129](https://github.com/kubernetes/minikube/pull/6129)
* Change error text to encourage better issue reports [#6121](https://github.com/kubernetes/minikube/pull/6121)
Huge thank you for this release towards our contributors:
- Anukul Sangwan
- Aresforchina
- Curtis Carter
- Kenta Iso
- Medya Ghazizadeh
- Sharif Elgamal
- Thomas Strömberg
- Zhou Hao
- priyawadhwa
- tstromberg
## Version 1.6.1 - 2019-12-11
A special bugfix release to fix a Windows regression:

View File

@ -15,7 +15,7 @@
# Bump these on release - and please check ISO_VERSION for correctness.
VERSION_MAJOR ?= 1
VERSION_MINOR ?= 6
VERSION_BUILD ?= 1
VERSION_BUILD ?= 2
RAW_VERSION=$(VERSION_MAJOR).$(VERSION_MINOR).${VERSION_BUILD}
VERSION ?= v$(RAW_VERSION)
@ -29,7 +29,7 @@ RPM_VERSION ?= $(DEB_VERSION)
GO_VERSION ?= 1.13.4
INSTALL_SIZE ?= $(shell du out/minikube-windows-amd64.exe | cut -f1)
BUILDROOT_BRANCH ?= 2019.02.7
BUILDROOT_BRANCH ?= 2019.02.8
REGISTRY?=gcr.io/k8s-minikube
# Get git commit id
@ -49,7 +49,7 @@ MINIKUBE_BUCKET ?= minikube/releases
MINIKUBE_UPLOAD_LOCATION := gs://${MINIKUBE_BUCKET}
MINIKUBE_RELEASES_URL=https://github.com/kubernetes/minikube/releases/download
KERNEL_VERSION ?= 4.19.81
KERNEL_VERSION ?= 4.19.88
# latest from https://github.com/golangci/golangci-lint/releases
GOLINT_VERSION ?= v1.21.0
# Limit number of default jobs, to avoid the CI builds running out of memory
@ -473,7 +473,7 @@ $(ISO_BUILD_IMAGE): deploy/iso/minikube-iso/Dockerfile
@echo "$(@) successfully built"
out/storage-provisioner:
GOOS=linux go build -o $@ -ldflags=$(PROVISIONER_LDFLAGS) cmd/storage-provisioner/main.go
CGO_ENABLED=0 GOOS=linux go build -o $@ -ldflags=$(PROVISIONER_LDFLAGS) cmd/storage-provisioner/main.go
.PHONY: storage-provisioner-image
storage-provisioner-image: out/storage-provisioner ## Build storage-provisioner docker image

View File

@ -23,7 +23,7 @@ minikube runs the latest stable release of Kubernetes, with support for standard
* Multi-cluster - using `minikube start -p <name>`
* NodePorts - using `minikube service`
* [Persistent Volumes](https://minikube.sigs.k8s.io/docs/reference/persistent_volumes/)
* Ingress
* [Ingress](https://kubernetes.io/docs/tasks/access-application-cluster/ingress-minikube/)
* [Dashboard](https://minikube.sigs.k8s.io/docs/tasks/dashboard/) - `minikube dashboard`
* [Container runtimes](https://minikube.sigs.k8s.io/docs/reference/runtimes/) - `start --container-runtime`
* [Configure apiserver and kubelet options](https://minikube.sigs.k8s.io/docs/reference/configuration/kubernetes/) via command-line flags

View File

@ -22,6 +22,7 @@ import (
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/image"
"k8s.io/minikube/pkg/minikube/localpath"
"k8s.io/minikube/pkg/minikube/machine"
)
@ -64,12 +65,25 @@ var deleteCacheCmd = &cobra.Command{
exit.WithError("Failed to delete images from config", err)
}
// Delete images from cache/images directory
if err := machine.DeleteFromImageCacheDir(args); err != nil {
if err := image.DeleteFromCacheDir(args); err != nil {
exit.WithError("Failed to delete images", err)
}
},
}
// reloadCacheCmd represents the cache reload command
var reloadCacheCmd = &cobra.Command{
Use: "reload",
Short: "reload cached images.",
Long: "reloads images previously added using the 'cache add' subcommand",
Run: func(cmd *cobra.Command, args []string) {
err := cacheAndLoadImagesInConfig()
if err != nil {
exit.WithError("Failed to reload cached images", err)
}
},
}
func imagesInConfigFile() ([]string, error) {
configFile, err := config.ReadConfig(localpath.ConfigFile)
if err != nil {
@ -85,8 +99,9 @@ func imagesInConfigFile() ([]string, error) {
return []string{}, nil
}
// CacheImagesInConfigFile caches the images currently in the config file (minikube start)
func CacheImagesInConfigFile() error {
// saveImagesToTarFromConfig saves images to tar in cache which specified in config file.
// currently only used by download-only option
func saveImagesToTarFromConfig() error {
images, err := imagesInConfigFile()
if err != nil {
return err
@ -94,11 +109,12 @@ func CacheImagesInConfigFile() error {
if len(images) == 0 {
return nil
}
return machine.CacheImages(images, constants.ImageCacheDir)
return image.SaveToDir(images, constants.ImageCacheDir)
}
// loadCachedImagesInConfigFile loads the images currently in the config file (minikube start)
func loadCachedImagesInConfigFile() error {
// cacheAndLoadImagesInConfig loads the images currently in the config file
// called by 'start' and 'cache reload' commands.
func cacheAndLoadImagesInConfig() error {
images, err := imagesInConfigFile()
if err != nil {
return err
@ -112,4 +128,5 @@ func loadCachedImagesInConfigFile() error {
func init() {
cacheCmd.AddCommand(addCacheCmd)
cacheCmd.AddCommand(deleteCacheCmd)
cacheCmd.AddCommand(reloadCacheCmd)
}

View File

@ -142,111 +142,8 @@ var settings = []Setting{
set: SetBool,
},
{
name: "dashboard",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableAddon},
},
{
name: "addon-manager",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableAddon},
},
{
name: "default-storageclass",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableStorageClasses},
},
{
name: "efk",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableAddon},
},
{
name: "ingress",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableAddon},
},
{
name: "insecure-registry",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableAddon},
},
{
name: "registry",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableAddon},
},
{
name: "registry-creds",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableAddon},
},
{
name: "freshpod",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableAddon},
},
{
name: "storage-provisioner",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableAddon},
},
{
name: "storage-provisioner-gluster",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableStorageClasses},
},
{
name: "metrics-server",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableAddon},
},
{
name: "nvidia-driver-installer",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableAddon},
},
{
name: "nvidia-gpu-device-plugin",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableAddon},
},
{
name: "logviewer",
set: SetBool,
validations: []setFn{IsValidAddon},
},
{
name: "gvisor",
set: SetBool,
validations: []setFn{IsValidAddon, IsContainerdRuntime},
callbacks: []setFn{EnableOrDisableAddon},
},
{
name: "helm-tiller",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableAddon},
},
{
name: "ingress-dns",
set: SetBool,
validations: []setFn{IsValidAddon},
callbacks: []setFn{EnableOrDisableAddon},
name: "insecure-registry",
set: SetString,
},
{
name: "hyperv-virtual-switch",

View File

@ -18,6 +18,9 @@ package config
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/minikube/pkg/addons"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/out"
)
@ -32,7 +35,7 @@ var addonsDisableCmd = &cobra.Command{
}
addon := args[0]
err := Set(addon, "false")
err := addons.Set(addon, "false", viper.GetString(config.MachineProfile))
if err != nil {
exit.WithError("disable failed", err)
}

View File

@ -1,39 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 config
import (
"testing"
"gotest.tools/assert"
pkgConfig "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/localpath"
)
func TestDisableUnknownAddon(t *testing.T) {
if err := Set("InvalidAddon", "false"); err == nil {
t.Fatalf("Disable did not return error for unknown addon")
}
}
func TestDisableAddon(t *testing.T) {
if err := Set("dashboard", "false"); err != nil {
t.Fatalf("Disable returned unexpected error: " + err.Error())
}
config, _ := pkgConfig.ReadConfig(localpath.ConfigFile)
assert.Equal(t, config["dashboard"], false)
}

View File

@ -18,6 +18,9 @@ package config
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/minikube/pkg/addons"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/out"
)
@ -32,7 +35,7 @@ var addonsEnableCmd = &cobra.Command{
}
addon := args[0]
err := Set(addon, "true")
err := addons.Set(addon, "true", viper.GetString(config.MachineProfile))
if err != nil {
exit.WithError("enable failed", err)
}

View File

@ -1,39 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 config
import (
"testing"
"gotest.tools/assert"
pkgConfig "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/localpath"
)
func TestEnableUnknownAddon(t *testing.T) {
if err := Set("InvalidAddon", "false"); err == nil {
t.Fatalf("Enable did not return error for unknown addon")
}
}
func TestEnableAddon(t *testing.T) {
if err := Set("ingress", "true"); err != nil {
t.Fatalf("Enable returned unexpected error: " + err.Error())
}
config, _ := pkgConfig.ReadConfig(localpath.ConfigFile)
assert.Equal(t, config["ingress"], true)
}

View File

@ -0,0 +1,41 @@
/*
Copyright 2020 The Kubernetes Authors All rights reserved.
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 config
import "testing"
func TestGetNotFound(t *testing.T) {
_, err := Get("nonexistent")
if err == nil {
t.Fatalf("Get did not return error for unknown property")
}
}
func TestGetOK(t *testing.T) {
name := "vm-driver"
err := Set(name, "virtualbox")
if err != nil {
t.Fatalf("Set returned error for property %s", name)
}
val, err := Get(name)
if err != nil {
t.Fatalf("Get returned error for property %s", name)
}
if val != "virtualbox" {
t.Fatalf("Get returned %s, expected virtualbox", val)
}
}

View File

@ -24,3 +24,24 @@ func TestNotFound(t *testing.T) {
t.Fatalf("Set did not return error for unknown property")
}
}
func TestSetNotAllowed(t *testing.T) {
err := Set("vm-driver", "123456")
if err == nil || err.Error() != "[driver \"123456\" is not supported]" {
t.Fatalf("Set did not return error for unallowed value")
}
}
func TestSetOK(t *testing.T) {
err := Set("vm-driver", "virtualbox")
if err != nil {
t.Fatalf("Set returned error for valid property value")
}
val, err := Get("vm-driver")
if err != nil {
t.Fatalf("Get returned error for valid property")
}
if val != "virtualbox" {
t.Fatalf("Get returned %s, expected \"virtualbox\"", val)
}
}

View File

@ -18,25 +18,13 @@ package config
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/pkg/errors"
"github.com/spf13/viper"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/command"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/storageclass"
)
// defaultStorageClassProvisioner is the name of the default storage class provisioner
const defaultStorageClassProvisioner = "standard"
// Runs all the validation or callback functions and collects errors
func run(name string, value string, fns []setFn) error {
var errors []error
@ -106,145 +94,6 @@ func SetBool(m config.MinikubeConfig, name string, val string) error {
return nil
}
// EnableOrDisableAddon updates addon status executing any commands necessary
func EnableOrDisableAddon(name string, val string) error {
enable, err := strconv.ParseBool(val)
if err != nil {
return errors.Wrapf(err, "parsing bool: %s", name)
}
addon := assets.Addons[name]
// check addon status before enabling/disabling it
alreadySet, err := isAddonAlreadySet(addon, enable)
if err != nil {
out.ErrT(out.Conflict, "{{.error}}", out.V{"error": err})
return err
}
//if addon is already enabled or disabled, do nothing
if alreadySet {
return nil
}
// TODO(r2d4): config package should not reference API, pull this out
api, err := machine.NewAPIClient()
if err != nil {
return errors.Wrap(err, "machine client")
}
defer api.Close()
//if minikube is not running, we return and simply update the value in the addon
//config and rewrite the file
if !cluster.IsMinikubeRunning(api) {
return nil
}
cfg, err := config.Load(viper.GetString(config.MachineProfile))
if err != nil && !os.IsNotExist(err) {
exit.WithCodeT(exit.Data, "Unable to load config: {{.error}}", out.V{"error": err})
}
host, err := cluster.CheckIfHostExistsAndLoad(api, cfg.Name)
if err != nil {
return errors.Wrap(err, "getting host")
}
cmd, err := machine.CommandRunner(host)
if err != nil {
return errors.Wrap(err, "command runner")
}
data := assets.GenerateTemplateData(cfg.KubernetesConfig)
return enableOrDisableAddonInternal(addon, cmd, data, enable)
}
func isAddonAlreadySet(addon *assets.Addon, enable bool) (bool, error) {
addonStatus, err := addon.IsEnabled()
if err != nil {
return false, errors.Wrap(err, "get the addon status")
}
if addonStatus && enable {
return true, nil
} else if !addonStatus && !enable {
return true, nil
}
return false, nil
}
func enableOrDisableAddonInternal(addon *assets.Addon, cmd command.Runner, data interface{}, enable bool) error {
var err error
if enable {
for _, addon := range addon.Assets {
var addonFile assets.CopyableFile
if addon.IsTemplate() {
addonFile, err = addon.Evaluate(data)
if err != nil {
return errors.Wrapf(err, "evaluate bundled addon %s asset", addon.GetAssetName())
}
} else {
addonFile = addon
}
if err := cmd.Copy(addonFile); err != nil {
return errors.Wrapf(err, "enabling addon %s", addon.AssetName)
}
}
} else {
for _, addon := range addon.Assets {
var addonFile assets.CopyableFile
if addon.IsTemplate() {
addonFile, err = addon.Evaluate(data)
if err != nil {
return errors.Wrapf(err, "evaluate bundled addon %s asset", addon.GetAssetName())
}
} else {
addonFile = addon
}
if err := cmd.Remove(addonFile); err != nil {
return errors.Wrapf(err, "disabling addon %s", addon.AssetName)
}
}
}
return nil
}
// EnableOrDisableStorageClasses enables or disables storage classes
func EnableOrDisableStorageClasses(name, val string) error {
enable, err := strconv.ParseBool(val)
if err != nil {
return errors.Wrap(err, "Error parsing boolean")
}
class := defaultStorageClassProvisioner
if name == "storage-provisioner-gluster" {
class = "glusterfile"
}
storagev1, err := storageclass.GetStoragev1()
if err != nil {
return errors.Wrapf(err, "Error getting storagev1 interface %v ", err)
}
if enable {
// Only StorageClass for 'name' should be marked as default
err = storageclass.SetDefaultStorageClass(storagev1, class)
if err != nil {
return errors.Wrapf(err, "Error making %s the default storage class", class)
}
} else {
// Unset the StorageClass as default
err := storageclass.DisableDefaultStorageClass(storagev1, class)
if err != nil {
return errors.Wrapf(err, "Error disabling %s as the default storage class", class)
}
}
return EnableOrDisableAddon(name, val)
}
// ErrValidateProfile Error to validate profile
type ErrValidateProfile struct {
Name string

View File

@ -20,7 +20,6 @@ import (
"fmt"
"testing"
"k8s.io/minikube/pkg/minikube/assets"
pkgConfig "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/driver"
)
@ -83,37 +82,6 @@ func TestSetBool(t *testing.T) {
}
}
func TestIsAddonAlreadySet(t *testing.T) {
testCases := []struct {
addonName string
}{
{
addonName: "ingress",
},
{
addonName: "registry",
},
}
for _, test := range testCases {
addon := assets.Addons[test.addonName]
addonStatus, _ := addon.IsEnabled()
alreadySet, err := isAddonAlreadySet(addon, addonStatus)
if !alreadySet {
if addonStatus {
t.Errorf("Did not get expected status, \n\n expected %+v already enabled", test.addonName)
} else {
t.Errorf("Did not get expected status, \n\n expected %+v already disabled", test.addonName)
}
}
if err != nil {
t.Errorf("Got unexpected error: %+v", err)
}
}
}
func TestValidateProfile(t *testing.T) {
testCases := []struct {
profileName string

View File

@ -25,25 +25,11 @@ import (
"strings"
units "github.com/docker/go-units"
"github.com/pkg/errors"
"github.com/spf13/viper"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/cruntime"
"k8s.io/minikube/pkg/minikube/driver"
"k8s.io/minikube/pkg/minikube/out"
)
// containerdOnlyMsg is the message shown when a containerd-only addon is enabled
const containerdOnlyAddonMsg = `
This addon can only be enabled with the containerd runtime backend. To enable this backend, please first stop minikube with:
minikube stop
and then start minikube again with the following flags:
minikube start --container-runtime=containerd --docker-opt containerd=/var/run/containerd/containerd.sock`
// IsValidDriver checks if a driver is supported
func IsValidDriver(string, name string) error {
if driver.Supported(name) {
@ -140,14 +126,6 @@ func IsValidPath(name string, path string) error {
return nil
}
// IsValidAddon checks if a string is a valid addon
func IsValidAddon(name string, val string) error {
if _, ok := assets.Addons[name]; ok {
return nil
}
return errors.Errorf("Cannot enable/disable invalid addon %s", name)
}
// IsValidRuntime checks if a string is a valid runtime
func IsValidRuntime(name string, runtime string) error {
_, err := cruntime.New(cruntime.Config{Type: runtime})
@ -156,20 +134,3 @@ func IsValidRuntime(name string, runtime string) error {
}
return nil
}
// IsContainerdRuntime is a validator which returns an error if the current runtime is not containerd
func IsContainerdRuntime(_, _ string) error {
config, err := config.Load(viper.GetString(config.MachineProfile))
if err != nil {
return fmt.Errorf("config.Load: %v", err)
}
r, err := cruntime.New(cruntime.Config{Type: config.KubernetesConfig.ContainerRuntime})
if err != nil {
return err
}
_, ok := r.(*cruntime.Containerd)
if !ok {
return fmt.Errorf(containerdOnlyAddonMsg)
}
return nil
}

View File

@ -33,7 +33,7 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
configcmd "k8s.io/minikube/cmd/minikube/cmd/config"
pkgaddons "k8s.io/minikube/pkg/addons"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/config"
@ -106,7 +106,7 @@ var dashboardCmd = &cobra.Command{
// Send status messages to stderr for folks re-using this output.
out.ErrT(out.Enabling, "Enabling dashboard ...")
// Enable the dashboard add-on
err = configcmd.Set("dashboard", "true")
err = pkgaddons.Set("dashboard", "true", viper.GetString(config.MachineProfile))
if err != nil {
exit.WithError("Unable to enable dashboard", err)
}

View File

@ -0,0 +1,68 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
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 cmd
import (
"bytes"
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"k8s.io/minikube/pkg/minikube/out"
)
// optionsCmd represents the options command
var optionsCmd = &cobra.Command{
Use: "options",
Short: "Show a list of global command-line options (applies to all commands).",
Long: "Show a list of global command-line options (applies to all commands).",
Run: runOptions,
}
// runOptions handles the executes the flow of "minikube options"
func runOptions(cmd *cobra.Command, args []string) {
out.String("The following options can be passed to any command:\n\n")
for _, flagName := range viperWhiteList {
f := pflag.Lookup(flagName)
out.String(flagUsage(f))
}
}
func flagUsage(flag *pflag.Flag) string {
x := new(bytes.Buffer)
if flag.Hidden {
return ""
}
format := "--%s=%s: %s\n"
if flag.Value.Type() == "string" {
format = "--%s='%s': %s\n"
}
if len(flag.Shorthand) > 0 {
format = " -%s, " + format
} else {
format = " %s " + format
}
fmt.Fprintf(x, format, flag.Shorthand, flag.Name, flag.DefValue, flag.Usage)
return x.String()
}

View File

@ -32,6 +32,7 @@ import (
"k8s.io/kubectl/pkg/util/templates"
configCmd "k8s.io/minikube/cmd/minikube/cmd/config"
"k8s.io/minikube/pkg/minikube/bootstrapper"
"k8s.io/minikube/pkg/minikube/bootstrapper/kicbs"
"k8s.io/minikube/pkg/minikube/bootstrapper/kubeadm"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/constants"
@ -53,9 +54,9 @@ var dirs = [...]string{
}
var viperWhiteList = []string{
"v",
"alsologtostderr",
"log_dir",
"v",
}
// RootCmd represents the base command when called without any subcommands
@ -213,6 +214,7 @@ func init() {
logsCmd,
updateCheckCmd,
versionCmd,
optionsCmd,
},
},
}
@ -267,11 +269,17 @@ func getClusterBootstrapper(api libmachine.API, bootstrapperName string) (bootst
var b bootstrapper.Bootstrapper
var err error
switch bootstrapperName {
case bootstrapper.BootstrapperTypeKubeadm:
b, err = kubeadm.NewKubeadmBootstrapper(api)
case bootstrapper.Kubeadm:
b, err = kubeadm.NewBootstrapper(api)
if err != nil {
return nil, errors.Wrap(err, "getting kubeadm bootstrapper")
return nil, errors.Wrap(err, "getting a new kubeadm bootstrapper")
}
case bootstrapper.KIC:
b, err = kicbs.NewBootstrapper(api)
if err != nil {
return nil, errors.Wrap(err, "getting a new kic bootstrapper")
}
default:
return nil, fmt.Errorf("unknown bootstrapper: %s", bootstrapperName)
}

View File

@ -46,9 +46,10 @@ import (
"github.com/spf13/viper"
"golang.org/x/sync/errgroup"
cmdcfg "k8s.io/minikube/cmd/minikube/cmd/config"
pkgaddons "k8s.io/minikube/pkg/addons"
"k8s.io/minikube/pkg/minikube/bootstrapper"
"k8s.io/minikube/pkg/minikube/bootstrapper/bsutil"
"k8s.io/minikube/pkg/minikube/bootstrapper/images"
"k8s.io/minikube/pkg/minikube/bootstrapper/kubeadm"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/command"
"k8s.io/minikube/pkg/minikube/config"
@ -186,7 +187,7 @@ func initKubernetesFlags() {
`A set of key=value pairs that describe configuration that may be passed to different components.
The key should be '.' separated, and the first part before the dot is the component to apply the configuration to.
Valid components are: kubelet, kubeadm, apiserver, controller-manager, etcd, proxy, scheduler
Valid kubeadm parameters: `+fmt.Sprintf("%s, %s", strings.Join(kubeadm.KubeadmExtraArgsWhitelist[kubeadm.KubeadmCmdParam], ", "), strings.Join(kubeadm.KubeadmExtraArgsWhitelist[kubeadm.KubeadmConfigParam], ",")))
Valid kubeadm parameters: `+fmt.Sprintf("%s, %s", strings.Join(bsutil.KubeadmExtraArgsWhitelist[bsutil.KubeadmCmdParam], ", "), strings.Join(bsutil.KubeadmExtraArgsWhitelist[bsutil.KubeadmConfigParam], ",")))
startCmd.Flags().String(featureGates, "", "A set of key=value pairs that describe feature gates for alpha/experimental features.")
startCmd.Flags().String(dnsDomain, constants.ClusterDNSDomain, "The cluster dns domain name used in the kubernetes cluster")
startCmd.Flags().Int(apiServerPort, constants.APIServerPort, "The apiserver listening port")
@ -313,11 +314,7 @@ func runStart(cmd *cobra.Command, args []string) {
exit.WithError("Failed to generate config", err)
}
if !driver.BareMetal(driverName) {
if err := cluster.CacheISO(config); err != nil {
exit.WithError("Failed to cache ISO", err)
}
}
cacheISO(&config, driverName)
if viper.GetBool(nativeSSH) {
ssh.SetDefaultClient(ssh.Native)
@ -327,7 +324,7 @@ func runStart(cmd *cobra.Command, args []string) {
// Now that the ISO is downloaded, pull images in the background while the VM boots.
var cacheGroup errgroup.Group
beginCacheImages(&cacheGroup, config.KubernetesConfig.ImageRepository, k8sVersion)
beginCacheRequiredImages(&cacheGroup, config.KubernetesConfig.ImageRepository, k8sVersion)
// Abstraction leakage alert: startHost requires the config to be saved, to satistfy pkg/provision/buildroot.
// Hence, saveConfig must be called before startHost, and again afterwards when we know the IP.
@ -342,7 +339,7 @@ func runStart(cmd *cobra.Command, args []string) {
// configure the runtime (docker, containerd, crio)
cr := configureRuntimes(mRunner, driverName, config.KubernetesConfig)
showVersionInfo(k8sVersion, cr)
waitCacheImages(&cacheGroup)
waitCacheRequiredImages(&cacheGroup)
// Must be written before bootstrap, otherwise health checks may flake due to stale IP
kubeconfig, err := setupKubeconfig(host, &config, config.Name)
@ -360,7 +357,7 @@ func runStart(cmd *cobra.Command, args []string) {
// enable addons with start command
enableAddons()
if err = loadCachedImagesInConfigFile(); err != nil {
if err = cacheAndLoadImagesInConfig(); err != nil {
out.T(out.FailureType, "Unable to load cached images from config file.")
}
@ -389,9 +386,17 @@ func updateDriver(driverName string) {
}
}
func cacheISO(config *cfg.MachineConfig, driverName string) {
if !driver.BareMetal(driverName) && !driver.IsKIC(driverName) {
if err := cluster.CacheISO(*config); err != nil {
exit.WithError("Failed to cache ISO", err)
}
}
}
func enableAddons() {
for _, a := range addonList {
err := cmdcfg.Set(a, "true")
err := pkgaddons.Set(a, "true", viper.GetString(config.MachineProfile))
if err != nil {
exit.WithError("addon enable failed", err)
}
@ -460,9 +465,9 @@ func handleDownloadOnly(cacheGroup *errgroup.Group, k8sVersion string) {
if err := doCacheBinaries(k8sVersion); err != nil {
exit.WithError("Failed to cache binaries", err)
}
waitCacheImages(cacheGroup)
if err := CacheImagesInConfigFile(); err != nil {
exit.WithError("Failed to cache images", err)
waitCacheRequiredImages(cacheGroup)
if err := saveImagesToTarFromConfig(); err != nil {
exit.WithError("Failed to cache images to tar", err)
}
out.T(out.Check, "Download complete!")
os.Exit(0)
@ -481,6 +486,7 @@ func startMachine(config *cfg.MachineConfig) (runner command.Runner, preExists b
}
ip := validateNetwork(host, runner)
// Bypass proxy for minikube's vm host ip
err = proxy.ExcludeIP(ip)
if err != nil {
@ -703,7 +709,7 @@ func minikubeCmd() string {
return "minikube"
}
// validerUser validates minikube is run by the recommended user (privileged or regular)
// validateUser validates minikube is run by the recommended user (privileged or regular)
func validateUser(drvName string) {
u, err := user.Current()
if err != nil {
@ -737,13 +743,16 @@ func validateUser(drvName string) {
}
}
// validateFlags validates the supplied flags against known bad combinations
func validateFlags(cmd *cobra.Command, drvName string) {
// validateDiskSize validates the disk size matches the minimum recommended
func validateDiskSize() {
diskSizeMB := pkgutil.CalculateSizeInMB(viper.GetString(humanReadableDiskSize))
if diskSizeMB < pkgutil.CalculateSizeInMB(minimumDiskSize) && !viper.GetBool(force) {
exit.WithCodeT(exit.Config, "Requested disk size {{.requested_size}} is less than minimum of {{.minimum_size}}", out.V{"requested_size": diskSizeMB, "minimum_size": pkgutil.CalculateSizeInMB(minimumDiskSize)})
}
}
// validateMemorySize validates the memory size matches the minimum recommended
func validateMemorySize() {
memorySizeMB := pkgutil.CalculateSizeInMB(viper.GetString(memory))
if memorySizeMB < pkgutil.CalculateSizeInMB(minimumMemorySize) && !viper.GetBool(force) {
exit.UsageT("Requested memory allocation {{.requested_size}} is less than the minimum allowed of {{.minimum_size}}", out.V{"requested_size": memorySizeMB, "minimum_size": pkgutil.CalculateSizeInMB(minimumMemorySize)})
@ -752,8 +761,32 @@ func validateFlags(cmd *cobra.Command, drvName string) {
out.T(out.Notice, "Requested memory allocation ({{.memory}}MB) is less than the default memory allocation of {{.default_memorysize}}MB. Beware that minikube might not work correctly or crash unexpectedly.",
out.V{"memory": memorySizeMB, "default_memorysize": pkgutil.CalculateSizeInMB(defaultMemorySize)})
}
}
// validateCPUCount validates the cpu count matches the minimum recommended
func validateCPUCount(local bool) {
var cpuCount int
if local {
// Uses the gopsutil cpu package to count the number of physical cpu cores
ci, err := cpu.Counts(false)
if err != nil {
glog.Warningf("Unable to get CPU info: %v", err)
} else {
cpuCount = ci
}
} else {
cpuCount = viper.GetInt(cpus)
}
if cpuCount < minimumCPUS && !viper.GetBool(force) {
exit.UsageT("Requested cpu count {{.requested_cpus}} is less than the minimum allowed of {{.minimum_cpus}}", out.V{"requested_cpus": cpuCount, "minimum_cpus": minimumCPUS})
}
}
// validateFlags validates the supplied flags against known bad combinations
func validateFlags(cmd *cobra.Command, drvName string) {
validateDiskSize()
validateMemorySize()
if driver.BareMetal(drvName) {
if viper.GetString(cfg.MachineProfile) != constants.DefaultMachineName {
exit.WithCodeT(exit.Config, "The 'none' driver does not support multiple profiles: https://minikube.sigs.k8s.io/docs/reference/drivers/none/")
@ -770,25 +803,14 @@ func validateFlags(cmd *cobra.Command, drvName string) {
if runtime != "docker" {
out.WarningT("Using the '{{.runtime}}' runtime with the 'none' driver is an untested configuration!", out.V{"runtime": runtime})
}
}
// Uses the gopsutil cpu package to count the number of physical cpu cores
ci, err := cpu.Counts(false)
if err != nil {
glog.Warningf("Unable to get CPU info: %v", err)
} else {
cpuCount = ci
}
} else {
cpuCount = viper.GetInt(cpus)
}
if cpuCount < minimumCPUS && !viper.GetBool(force) {
exit.UsageT("Requested cpu count {{.requested_cpus}} is less than the minimum allowed of {{.minimum_cpus}}", out.V{"requested_cpus": cpuCount, "minimum_cpus": minimumCPUS})
}
validateCPUCount(driver.BareMetal(drvName))
// check that kubeadm extra args contain only whitelisted parameters
for param := range extraOptions.AsMap().Get(kubeadm.Kubeadm) {
if !cfg.ContainsParam(kubeadm.KubeadmExtraArgsWhitelist[kubeadm.KubeadmCmdParam], param) &&
!cfg.ContainsParam(kubeadm.KubeadmExtraArgsWhitelist[kubeadm.KubeadmConfigParam], param) {
for param := range extraOptions.AsMap().Get(bsutil.Kubeadm) {
if !cfg.ContainsParam(bsutil.KubeadmExtraArgsWhitelist[bsutil.KubeadmCmdParam], param) &&
!cfg.ContainsParam(bsutil.KubeadmExtraArgsWhitelist[bsutil.KubeadmConfigParam], param) {
exit.UsageT("Sorry, the kubeadm.{{.parameter_name}} parameter is currently not supported by --extra-config", out.V{"parameter_name": param})
}
}
@ -819,8 +841,8 @@ func doCacheBinaries(k8sVersion string) error {
return machine.CacheBinariesForBootstrapper(k8sVersion, viper.GetString(cmdcfg.Bootstrapper))
}
// beginCacheImages caches Docker images in the background
func beginCacheImages(g *errgroup.Group, imageRepository string, k8sVersion string) {
// beginCacheRequiredImages caches images required for kubernetes version in the background
func beginCacheRequiredImages(g *errgroup.Group, imageRepository string, k8sVersion string) {
if !viper.GetBool(cacheImages) {
return
}
@ -830,8 +852,8 @@ func beginCacheImages(g *errgroup.Group, imageRepository string, k8sVersion stri
})
}
// waitCacheImages blocks until the image cache jobs complete
func waitCacheImages(g *errgroup.Group) {
// waitCacheRequiredImages blocks until the required images are all cached.
func waitCacheRequiredImages(g *errgroup.Group) {
if !viper.GetBool(cacheImages) {
return
}
@ -967,6 +989,19 @@ func autoSetDriverOptions(cmd *cobra.Command, drvName string) error {
if !cmd.Flags().Changed(cacheImages) {
viper.Set(cacheImages, hints.CacheImages)
}
// currently only used for kic
if !cmd.Flags().Changed(containerRuntime) && hints.ContainerRuntime != "" {
viper.Set(containerRuntime, hints.ContainerRuntime)
glog.Infof("auto set container runtime to %s for kic driver.", hints.ContainerRuntime)
}
if !cmd.Flags().Changed("bootstrapper") && hints.Bootstrapper != "" {
viper.Set(cmdcfg.Bootstrapper, hints.Bootstrapper)
glog.Infof("auto set bootstrapper to %s for kic driver.", hints.Bootstrapper)
}
return nil
}
@ -1006,20 +1041,9 @@ func startHost(api libmachine.API, mc cfg.MachineConfig) (*host.Host, bool) {
exit.WithError("Failed to check if machine exists", err)
}
var host *host.Host
start := func() (err error) {
host, err = cluster.StartHost(api, mc)
if err != nil {
out.T(out.Resetting, "Retriable failure: {{.error}}", out.V{"error": err})
if derr := cluster.DeleteHost(api, mc.Name); derr != nil {
glog.Warningf("DeleteHost: %v", derr)
}
}
return err
}
if err = retry.Expo(start, 5*time.Second, 3*time.Minute, 3); err != nil {
exit.WithError("Unable to start VM", err)
host, err := cluster.StartHost(api, mc)
if err != nil {
exit.WithError("Unable to start VM. Please investigate and run 'minikube delete' if possible", err)
}
return host, exists
}
@ -1049,7 +1073,7 @@ func validateNetwork(h *host.Host, r command.Runner) string {
}
}
if !driver.BareMetal(h.Driver.DriverName()) {
if !driver.BareMetal(h.Driver.DriverName()) && !driver.IsKIC(h.Driver.DriverName()) {
trySSH(h, ip)
}

View File

@ -47,7 +47,7 @@ var updateContextCmd = &cobra.Command{
if err != nil {
exit.WithError("Error host driver ip status", err)
}
updated := false
var updated bool
kubeConfigPath := os.Getenv("KUBECONFIG")
if kubeConfigPath == "" {
updated, err = kubeconfig.UpdateIP(ip, machineName, constants.KubeconfigPath)

View File

@ -33,6 +33,8 @@ spec:
value: "5"
- name: ADDON_MANAGER_LEADER_ELECTION
value: "false"
- name: KUBECTL_EXTRA_PRUNE_WHITELIST
value: install.istio.io/v1alpha2/IstioControlPlane
imagePullPolicy: IfNotPresent
resources:
requests:

View File

@ -0,0 +1,271 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: istio-operator
labels:
kubernetes.io/minikube-addons: istio
addonmanager.kubernetes.io/mode: EnsureExists
...
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: istiocontrolplanes.install.istio.io
labels:
kubernetes.io/minikube-addons: istio
addonmanager.kubernetes.io/mode: EnsureExists
spec:
group: install.istio.io
names:
kind: IstioControlPlane
listKind: IstioControlPlaneList
plural: istiocontrolplanes
singular: istiocontrolplane
shortNames:
- icp
scope: Namespaced
subresources:
status: {}
validation:
openAPIV3Schema:
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values.
More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase.
More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
spec:
description: 'Specification of the desired state of the istio control plane resource.
More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status'
type: object
status:
description: 'Status describes each of istio control plane component status at the current time.
0 means NONE, 1 means UPDATING, 2 means HEALTHY, 3 means ERROR, 4 means RECONCILING.
More info: https://github.com/istio/operator/blob/master/pkg/apis/istio/v1alpha2/v1alpha2.pb.html &
https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status'
type: object
versions:
- name: v1alpha2
served: true
storage: true
...
---
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: istio-operator
name: istio-operator
labels:
kubernetes.io/minikube-addons: istio
addonmanager.kubernetes.io/mode: EnsureExists
...
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: istio-operator
labels:
kubernetes.io/minikube-addons: istio
addonmanager.kubernetes.io/mode: EnsureExists
rules:
# istio groups
- apiGroups:
- authentication.istio.io
resources:
- '*'
verbs:
- '*'
- apiGroups:
- config.istio.io
resources:
- '*'
verbs:
- '*'
- apiGroups:
- install.istio.io
resources:
- '*'
verbs:
- '*'
- apiGroups:
- networking.istio.io
resources:
- '*'
verbs:
- '*'
- apiGroups:
- rbac.istio.io
resources:
- '*'
verbs:
- '*'
- apiGroups:
- security.istio.io
resources:
- '*'
verbs:
- '*'
# k8s groups
- apiGroups:
- admissionregistration.k8s.io
resources:
- mutatingwebhookconfigurations
- validatingwebhookconfigurations
verbs:
- '*'
- apiGroups:
- apiextensions.k8s.io
resources:
- customresourcedefinitions.apiextensions.k8s.io
- customresourcedefinitions
verbs:
- '*'
- apiGroups:
- apps
- extensions
resources:
- daemonsets
- deployments
- deployments/finalizers
- ingresses
- replicasets
- statefulsets
verbs:
- '*'
- apiGroups:
- autoscaling
resources:
- horizontalpodautoscalers
verbs:
- '*'
- apiGroups:
- monitoring.coreos.com
resources:
- servicemonitors
verbs:
- get
- create
- apiGroups:
- policy
resources:
- poddisruptionbudgets
verbs:
- '*'
- apiGroups:
- rbac.authorization.k8s.io
resources:
- clusterrolebindings
- clusterroles
- roles
- rolebindings
verbs:
- '*'
- apiGroups:
- ""
resources:
- configmaps
- endpoints
- events
- namespaces
- pods
- persistentvolumeclaims
- secrets
- services
- serviceaccounts
verbs:
- '*'
...
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: istio-operator
labels:
kubernetes.io/minikube-addons: istio
addonmanager.kubernetes.io/mode: EnsureExists
subjects:
- kind: ServiceAccount
name: istio-operator
namespace: istio-operator
roleRef:
kind: ClusterRole
name: istio-operator
apiGroup: rbac.authorization.k8s.io
...
---
apiVersion: v1
kind: Service
metadata:
namespace: istio-operator
labels:
name: istio-operator
kubernetes.io/minikube-addons: istio
addonmanager.kubernetes.io/mode: EnsureExists
name: istio-operator-metrics
spec:
ports:
- name: http-metrics
port: 8383
targetPort: 8383
selector:
name: istio-operator
...
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: istio-operator
name: istio-operator
labels:
kubernetes.io/minikube-addons: istio
addonmanager.kubernetes.io/mode: Reconcile
spec:
replicas: 1
selector:
matchLabels:
name: istio-operator
template:
metadata:
labels:
name: istio-operator
kubernetes.io/minikube-addons: istio
addonmanager.kubernetes.io/mode: EnsureExists
spec:
serviceAccountName: istio-operator
containers:
- name: istio-operator
image: docker.io/istio/operator:1.4.0
command:
- istio-operator
- server
imagePullPolicy: Always
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 50m
memory: 128Mi
env:
- name: WATCH_NAMESPACE
value: ""
- name: LEADER_ELECTION_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: OPERATOR_NAME
value: "istio-operator"
...

View File

@ -0,0 +1,26 @@
## istio Addon
[istio](https://istio.io/docs/setup/getting-started/) - Cloud platforms provide a wealth of benefits for the organizations that use them.
### Enabling istio
Propose to startup minikube with at least 8192 MB of memory and 4 CPUs to enable istio.
To enable this addon, simply run:
```shell script
minikube addons enable istio
```
In a minute or so istio default components will be installed into your cluster. You could run `kubectl get po -n istio-system` to see the progress for istio installation.
### Testing installation
```shell script
kubectl get po -n istio-system
```
If everything went well you shouldn't get any errors about istio being installed in your cluster. If you haven't deployed any releases `kubectl get po -n istio-system` won't return anything.
### Deprecation of istio
To disable this addon, simply run:
```shell script
minikube addons disable istio
```

View File

@ -0,0 +1,10 @@
apiVersion: install.istio.io/v1alpha2
kind: IstioControlPlane
metadata:
namespace: istio-operator
name: example-istiocontrolplane
labels:
kubernetes.io/minikube-addons: istio
addonmanager.kubernetes.io/mode: Reconcile
spec:
profile: default

View File

@ -0,0 +1,5 @@
{{ define "main" }}
<div style="padding-top:20px">
{{ .Render "content" }}
</div>
{{ end }}

View File

@ -0,0 +1,36 @@
Index: systemd-240/src/basic/random-util.c
===================================================================
--- systemd-240.orig/src/basic/random-util.c
+++ systemd-240/src/basic/random-util.c
@@ -37,6 +37,7 @@ int rdrand(unsigned long *ret) {
#if defined(__i386__) || defined(__x86_64__)
static int have_rdrand = -1;
+ unsigned long v;
unsigned char err;
if (have_rdrand < 0) {
@@ -56,9 +57,22 @@ int rdrand(unsigned long *ret) {
asm volatile("rdrand %0;"
"setc %1"
- : "=r" (*ret),
+ : "=r" (v),
"=qm" (err));
+ /* Apparently on some AMD CPUs RDRAND will sometimes (after a suspend/resume cycle?) report success
+ * via the carry flag but nonetheless return the same fixed value -1 in all cases. This appears to be
+ * a bad bug in the CPU or firmware. Let's deal with that and work-around this by explicitly checking
+ * for this special value (and also 0, just to be sure) and filtering it out. This is a work-around
+ * only however and something AMD really should fix properly. The Linux kernel should probably work
+ * around this issue by turning off RDRAND altogether on those CPUs. See:
+ * https://github.com/systemd/systemd/issues/11810 */
+ if (v == 0 || v == ULONG_MAX)
+ return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN),
+ "RDRAND returned suspicious value %lx, assuming bad hardware RNG, not using value.", v);
+
+ *ret = v;
+
#if HAS_FEATURE_MEMORY_SANITIZER
__msan_unpoison(&err, sizeof(err));
#endif

View File

@ -17,6 +17,7 @@ BR2_SYSTEM_BIN_SH_BASH=y
# BR2_TARGET_GENERIC_GETTY is not set
BR2_ROOTFS_USERS_TABLES="$(BR2_EXTERNAL_MINIKUBE_PATH)/board/coreos/minikube/users"
BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_MINIKUBE_PATH)/board/coreos/minikube/rootfs-overlay"
BR2_GLOBAL_PATCH_DIR="$(BR2_EXTERNAL_MINIKUBE_PATH)/board/coreos/minikube/patches"
BR2_LINUX_KERNEL=y
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL_MINIKUBE_PATH)/board/coreos/minikube/linux_defconfig"

View File

@ -9,3 +9,4 @@ sha256 f7041a92e2d3a4c341be8df58f1076ba57ecb5daa02b6c65e652530c5f242739 v1.15.0.
sha256 6218a99877da9b9895e0088944731f5384803c15628d4b3c6b40ba1ddd39e052 v1.15.1.tar.gz
sha256 70d4c746fe207422c78420dc4239768f485eea639a38c993c02872ec6305dd1d v1.15.2.tar.gz
sha256 05f9614c4d5970b4662499b84c270b0ab953596ee863dcd09c9dc7a2d2f09789 v1.16.0.tar.gz
sha256 57e1ee990ef2d5af8b32c33a21b4998682608e3556dcf1d3349666f55e7d95b9 v1.16.1.tar.gz

View File

@ -4,8 +4,8 @@
#
################################################################################
CRIO_BIN_VERSION = v1.16.0
CRIO_BIN_COMMIT = fa99ff4ae2aa45115bf3c6bb33db07191db2518e
CRIO_BIN_VERSION = v1.16.1
CRIO_BIN_COMMIT = bf8fcf34c942ba973a4f694094b46f914c779c0a
CRIO_BIN_SITE = https://github.com/cri-o/cri-o/archive
CRIO_BIN_SOURCE = $(CRIO_BIN_VERSION).tar.gz
CRIO_BIN_DEPENDENCIES = host-go libgpgme

View File

@ -4,7 +4,7 @@
#
################################################################################
HYPERV_DAEMONS_VERSION = 4.19.81
HYPERV_DAEMONS_VERSION = 4.19.88
HYPERV_DAEMONS_SITE = https://www.kernel.org/pub/linux/kernel/v4.x
HYPERV_DAEMONS_SOURCE = linux-$(HYPERV_DAEMONS_VERSION).tar.xz

View File

@ -10,3 +10,4 @@ sha256 17fdf68e85106d0848e89825e191198a4079bb6d9ca6dd3e415e5c010192db9e v1.4.0.t
sha256 45eb7bccd81a1431b0c7a0697829c0bcc397048595d143fd91179b31d22a3c63 v1.4.1.tar.gz
sha256 2e027c1b935f3a03f27ef7f17823ccf334607a17d033d4ce53a90b98294e7f68 v1.4.4.tar.gz
sha256 61b44b739c485125f179044f7aa7dc58c820f771bce4ce495fa555a38dc68b57 v1.6.3.tar.gz
sha256 6e59821320b435543bc7554e73faa66d5956e4ad3f7e7f4ea03bebd6726758e9 v1.6.4.tar.gz

View File

@ -1,5 +1,5 @@
PODMAN_VERSION = v1.6.3
PODMAN_COMMIT = 9d087f6a766259ba53b224944f1b7b778035c370
PODMAN_VERSION = v1.6.4
PODMAN_COMMIT = 5cc92849f7fc9dd734ca2fd8f3ae8830b9a7eb26
PODMAN_SITE = https://github.com/containers/libpod/archive
PODMAN_SOURCE = $(PODMAN_VERSION).tar.gz
PODMAN_LICENSE = Apache-2.0
@ -24,7 +24,7 @@ endef
define PODMAN_BUILD_CMDS
mkdir -p $(@D)/bin
$(PODMAN_BIN_ENV) $(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D) GIT_COMMIT=$(PODMAN_COMMIT) PREFIX=/usr podman
$(PODMAN_BIN_ENV) CIRRUS_TAG=$(PODMAN_VERSION) $(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D) GIT_COMMIT=$(PODMAN_COMMIT) PREFIX=/usr podman
endef
define PODMAN_INSTALL_TARGET_CMDS

View File

@ -13,15 +13,15 @@ RUNC_MASTER_LICENSE_FILES = LICENSE
RUNC_MASTER_DEPENDENCIES = host-go
RUNC_MASTER_GOPATH = "$(@D)/Godeps/_workspace"
RUNC_MASTER_GOPATH = $(@D)/_output
RUNC_MASTER_MAKE_ENV = $(HOST_GO_TARGET_ENV) \
CGO_ENABLED=1 \
GOBIN="$(@D)/bin" \
GO111MODULE=off \
GOPATH="$(RUNC_MASTER_GOPATH)" \
PATH=$(BR_PATH)
GOBIN="$(RUNC_MASTER_GOPATH)/bin" \
PATH=$(RUNC_MASTER_GOPATH)/bin:$(BR_PATH)
RUNC_MASTER_GLDFLAGS = \
-buildmode=pie -X main.gitCommit=$(RUNC_MASTER_VERSION)
RUNC_MASTER_COMPILE_SRC = $(RUNC_MASTER_GOPATH)/src/github.com/opencontainers/runc
ifeq ($(BR2_PACKAGE_LIBSECCOMP),y)
RUNC_MASTER_GOTAGS += seccomp
@ -34,13 +34,11 @@ define RUNC_MASTER_CONFIGURE_CMDS
endef
define RUNC_MASTER_BUILD_CMDS
cd $(@D) && $(RUNC_MASTER_MAKE_ENV) $(HOST_DIR)/usr/bin/go \
build -v -o $(@D)/bin/runc \
-tags "$(RUNC_MASTER_GOTAGS)" -ldflags "$(RUNC_MASTER_GLDFLAGS)" github.com/opencontainers/runc
PWD=$(RUNC_MASTER_COMPILE_SRC) $(RUNC_MASTER_MAKE_ENV) $(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D) BUILDTAGS="$(RUNC_MASTER_GOTAGS)" COMMIT_NO=$(RUNC_MASTER_VERSION) PREFIX=/usr
endef
define RUNC_MASTER_INSTALL_TARGET_CMDS
$(INSTALL) -D -m 0755 $(@D)/bin/runc $(TARGET_DIR)/usr/bin/runc
$(INSTALL) -D -m 0755 $(@D)/runc $(TARGET_DIR)/usr/bin/runc
endef
$(eval $(generic-package))

View File

@ -1,4 +1,12 @@
[
{
"name": "v1.6.2",
"checksums": {
"darwin": "5ea5168a80597ee6221bf50a524429a24a37f0c0f36725e6b297dc5a7a6a2105",
"linux": "eabd027438953d29a4b0f7b810c801919cc13bef3ebe7aff08c9534ac2b091ab",
"windows": "79d66c874cfe3497656e9ba191680cc95abd92d2f722b10de38f00b76ef82393"
}
},
{
"name": "v1.6.1",
"checksums": {

10
go.mod
View File

@ -16,15 +16,17 @@ require (
github.com/cheggaaa/pb/v3 v3.0.1
github.com/cloudfoundry-attic/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 // indirect
github.com/docker/docker v1.13.1 // indirect
github.com/docker/docker v1.13.1
github.com/docker/go-units v0.3.3
github.com/docker/machine v0.7.1-0.20190718054102-a555e4f7a8f5 // version is 0.7.1 to pin to a555e4f7a8f5
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect
github.com/evanphx/json-patch v4.5.0+incompatible // indirect
github.com/go-ole/go-ole v1.2.4 // indirect
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/google/go-cmp v0.3.0
github.com/googleapis/gnostic v0.3.0 // indirect
github.com/gorilla/mux v1.7.1 // indirect
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce // indirect
github.com/hashicorp/go-getter v1.4.0
@ -69,17 +71,19 @@ require (
github.com/xeipuuv/gojsonschema v0.0.0-20160623135812-c539bca196be
github.com/zchee/go-vmnet v0.0.0-20161021174912-97ebf9174097
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
golang.org/x/sync v0.0.0-20190423024810-112230192c58
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb
golang.org/x/text v0.3.2
gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect
gotest.tools v2.2.0+incompatible
k8s.io/api v0.0.0
k8s.io/apimachinery v0.0.0
k8s.io/client-go v0.0.0
k8s.io/client-go v11.0.0+incompatible
k8s.io/klog v0.3.3 // indirect
k8s.io/kubectl v0.0.0-00010101000000-000000000000
sigs.k8s.io/sig-storage-lib-external-provisioner v4.0.0+incompatible
)

14
go.sum
View File

@ -135,6 +135,8 @@ github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdR
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
github.com/fatih/camelcase v0.0.0-20160318181535-f6a740d52f96/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
@ -227,6 +229,8 @@ github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.3.0 h1:CcQijm0XKekKjP/YCz28LXVSpgguuB+nCxaSjCe09y0=
github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
@ -316,10 +320,12 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGi
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.0.0-20131111012553-2788f0dbd169/go.mod h1:glhvuHOU9Hy7/8PwwdtnarXqLagOX0b/TbZx2zLMqEg=
github.com/kr/pretty v0.0.0-20140812000539-f31442d60e51/go.mod h1:Bvhd+E3laJ0AVkG0c9rmtZcnhV0HQ3+c3YxxqTvc/gA=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.0.0-20130911015532-6807e777504f/go.mod h1:sjUstKUATFIcff4qlB53Kml0wQPtJVc/3fWrmuUmcfA=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/libopenstorage/openstorage v0.0.0-20170906232338-093a0c388875/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wnOzEhNx2YQedreMcUyc=
github.com/libvirt/libvirt-go v3.4.0+incompatible h1:Cpyalgj1x8JIeTlL6SDYZBo7j8nY3+5XHqmi8DaunCk=
@ -550,8 +556,8 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -680,6 +686,8 @@ gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNat
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.27 h1:kJdccidYzt3CaHD1crCFTS1hxyhSi059NhOFUf03YFo=
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
@ -714,6 +722,8 @@ k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
k8s.io/heapster v1.2.0-beta.1/go.mod h1:h1uhptVXMwC8xtZBYsPXKVi8fpdlYkTs6k949KozGrM=
k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68=
k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.3 h1:niceAagH1tzskmaie/icWd7ci1wbG7Bf2c6YGcQv+3c=
k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30 h1:TRb4wNWoBVrH9plmkp2q86FIDppkbrEXdXlxU3a3BMI=
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
k8s.io/kubernetes v1.15.2 h1:RO9EuRw5vlN3oa/lnmPxmywOoJRtg9o40KcklHXNIAQ=

View File

@ -26,6 +26,9 @@
readonly TEST_ROOT="${HOME}/minikube-integration"
readonly TEST_HOME="${TEST_ROOT}/${OS_ARCH}-${VM_DRIVER}-${MINIKUBE_LOCATION}-$$-${COMMIT}"
export GOPATH="$HOME/go"
export PATH=$PATH:"/usr/local/bin/:/usr/local/go/bin/:$GOPATH/bin"
echo ">> Starting at $(date)"
echo ""
echo "arch: ${OS_ARCH}"
@ -39,6 +42,8 @@ echo "uptime: $(uptime)"
# Setting KUBECONFIG prevents the version ceck from erroring out due to permission issues
echo "kubectl: $(env KUBECONFIG=${TEST_HOME} kubectl version --client --short=true)"
echo "docker: $(docker version --format '{{ .Client.Version }}')"
echo "go: $(go version || true)"
case "${VM_DRIVER}" in
kvm2)
@ -258,16 +263,27 @@ if [[ "${LOAD}" -gt 2 ]]; then
uptime
fi
readonly TEST_OUT="${TEST_HOME}/testout.txt"
readonly JSON_OUT="${TEST_HOME}/test.json"
readonly HTML_OUT="${TEST_HOME}/test.html"
e2e_start_time="$(date -u +%s)"
echo ""
echo ">> Starting ${E2E_BIN} at $(date)"
set -x
if test -f "${TEST_OUT}"; then
rm "${TEST_OUT}" || true # clean up previous runs of same build
fi
touch "${TEST_OUT}"
${SUDO_PREFIX}${E2E_BIN} \
-minikube-start-args="--vm-driver=${VM_DRIVER} ${EXTRA_START_ARGS}" \
-expected-default-driver="${EXPECTED_DEFAULT_DRIVER}" \
-test.timeout=70m \
-test.timeout=70m -test.v \
${EXTRA_TEST_ARGS} \
-binary="${MINIKUBE_BIN}" && result=$? || result=$?
-binary="${MINIKUBE_BIN}" 2>&1 | tee "${TEST_OUT}"
result=${PIPESTATUS[0]} # capture the exit code of the first cmd in pipe.
set +x
echo ">> ${E2E_BIN} exited with ${result} at $(date)"
echo ""
@ -286,9 +302,46 @@ elapsed=$(($e2e_end_time-$e2e_start_time))
min=$(($elapsed/60))
sec=$(tail -c 3 <<< $((${elapsed}00/60)))
elapsed=$min.$sec
JOB_GCS_BUCKET="minikube-builds/logs/${MINIKUBE_LOCATION}/${JOB_NAME}"
echo ">> Copying ${TEST_OUT} to gs://${JOB_GCS_BUCKET}out.txt"
gsutil -qm cp "${TEST_OUT}" "gs://${JOB_GCS_BUCKET}out.txt"
echo ">> Attmpting to convert test logs to json"
if test -f "${JSON_OUT}"; then
rm "${JSON_OUT}" || true # clean up previous runs of same build
fi
touch "${JSON_OUT}"
# Generate JSON output
echo ">> Running go test2json"
go tool test2json -t < "${TEST_OUT}" > "${JSON_OUT}" || true
echo ">> Installing gopogh"
GO111MODULE="on" go get -u github.com/medyagh/gopogh@v0.0.17 || true
echo ">> Running gopogh"
if test -f "${HTML_OUT}"; then
rm "${HTML_OUT}" || true # clean up previous runs of same build
fi
touch "${HTML_OUT}"
pessimistic_status=$(gopogh -in "${JSON_OUT}" -out "${HTML_OUT}" -name "${JOB_NAME}" -pr "${MINIKUBE_LOCATION}" -repo github.com/kubernetes/minikube/ -details "${COMMIT}") || true
description="completed with ${status} in ${elapsed} minute(s)."
if [ "$status" = "failure" ]; then
description="completed with ${pessimistic_status} in ${elapsed} minute(s)."
fi
echo $description
echo ">> uploading ${JSON_OUT}"
gsutil -qm cp "${JSON_OUT}" "gs://${JOB_GCS_BUCKET}.json" || true
echo ">> uploading ${HTML_OUT}"
gsutil -qm cp "${HTML_OUT}" "gs://${JOB_GCS_BUCKET}.html" || true
public_log_url="https://storage.googleapis.com/${JOB_GCS_BUCKET}.txt"
if grep -q html "$HTML_OUT"; then
public_log_url="https://storage.googleapis.com/${JOB_GCS_BUCKET}.html"
fi
echo ">> Cleaning up after ourselves ..."
${SUDO_PREFIX}${MINIKUBE_BIN} tunnel --cleanup || true
@ -297,7 +350,10 @@ cleanup_stale_routes || true
${SUDO_PREFIX} rm -Rf "${MINIKUBE_HOME}" || true
${SUDO_PREFIX} rm -f "${KUBECONFIG}" || true
rmdir "${TEST_HOME}"
${SUDO_PREFIX} rm -f "${TEST_OUT}" || true
${SUDO_PREFIX} rm -f "${JSON_OUT}" || true
${SUDO_PREFIX} rm -f "${HTML_OUT}" || true
rmdir "${TEST_HOME}" || true
echo ">> ${TEST_HOME} completed at $(date)"
if [[ "${MINIKUBE_LOCATION}" == "master" ]]; then
@ -341,5 +397,5 @@ function retry_github_status() {
retry_github_status "${COMMIT}" "${JOB_NAME}" "${status}" "${access_token}" "https://storage.googleapis.com/minikube-builds/logs/${MINIKUBE_LOCATION}/${JOB_NAME}.txt" "${description}"
retry_github_status "${COMMIT}" "${JOB_NAME}" "${status}" "${access_token}" "${public_log_url}" "${description}"
exit $result

View File

@ -36,5 +36,4 @@ EXTRA_TEST_ARGS="-gvisor"
mkdir -p cron && gsutil -qm rsync "gs://minikube-builds/${MINIKUBE_LOCATION}/cron" cron || echo "FAILED TO GET CRON FILES"
sudo install cron/cleanup_and_reboot_Linux.sh /etc/cron.hourly/cleanup_and_reboot || echo "FAILED TO INSTALL CLEANUP"
# Download files and set permissions
source ./common.sh

View File

@ -54,5 +54,4 @@ systemctl is-active --quiet kubelet \
mkdir -p cron && gsutil -m rsync "gs://minikube-builds/${MINIKUBE_LOCATION}/cron" cron || echo "FAILED TO GET CRON FILES"
sudo install cron/cleanup_and_reboot_Linux.sh /etc/cron.hourly/cleanup_and_reboot || echo "FAILED TO INSTALL CLEANUP"
# Download files and set permissions
source ./common.sh

View File

@ -33,6 +33,4 @@ EXPECTED_DEFAULT_DRIVER="kvm2"
mkdir -p cron && gsutil -qm rsync "gs://minikube-builds/${MINIKUBE_LOCATION}/cron" cron || echo "FAILED TO GET CRON FILES"
sudo install cron/cleanup_and_reboot_Linux.sh /etc/cron.hourly/cleanup_and_reboot || echo "FAILED TO INSTALL CLEANUP"
# Download files and set permissions
source ./common.sh

View File

@ -78,6 +78,6 @@ function retry_github_status() {
for j in ${jobs[@]}; do
retry_github_status "${ghprbActualCommit}" "${j}" "pending" "${access_token}" \
"https://storage.googleapis.com/minikube-builds/logs/${ghprbPullId}/${j}.txt"
"https://storage.googleapis.com/minikube-builds/logs/${ghprbPullId}/${j}.pending"
done

View File

@ -39,5 +39,4 @@ install cron/cleanup_and_reboot_Darwin.sh $HOME/cleanup_and_reboot.sh || echo "F
echo "*/30 * * * * $HOME/cleanup_and_reboot.sh" | crontab
crontab -l
# Download files and set permissions
source common.sh

View File

@ -39,5 +39,4 @@ install cron/cleanup_and_reboot_Darwin.sh $HOME/cleanup_and_reboot.sh || echo "
echo "*/30 * * * * $HOME/cleanup_and_reboot.sh" | crontab
crontab -l
# Download files and set permissions
source common.sh

View File

@ -55,7 +55,11 @@ gsutil -m cp out/* "gs://$BUCKET/releases/$TAGNAME/"
# Update "latest" release for non-beta/non-alpha builds
if ! [[ ${VERSION_BUILD} =~ ^[0-9]+$ ]]; then
echo "NOTE: ${VERSION} appears to be a non-standard release, not updating /releases/latest"
else
echo "Updating latest bucket for ${VERSION} release"
gsutil cp -r "gs://${BUCKET}/releases/${TAGNAME}/*" "gs://${BUCKET}/releases/latest/"
fi
exit 0
fi
echo "Updating Docker images ..."
make push-gvisor-addon-image push-storage-provisioner-image
echo "Updating latest bucket for ${VERSION} release ..."
gsutil cp -r "gs://${BUCKET}/releases/${TAGNAME}/*" "gs://${BUCKET}/releases/latest/"

View File

@ -29,4 +29,4 @@ $env:target_url="https://storage.googleapis.com/minikube-builds/logs/$env:MINIKU
$json = "{`"state`": `"$env:status`", `"description`": `"Jenkins`", `"target_url`": `"$env:target_url`", `"context`": `"Hyper-V_Windows`"}"
Invoke-WebRequest -Uri "https://api.github.com/repos/kubernetes/minikube/statuses/$env:COMMIT`?access_token=$env:access_token" -Body $json -ContentType "application/json" -Method Post -usebasicparsing
Exit $env:result
Exit $env:result

View File

@ -29,4 +29,4 @@ $env:target_url="https://storage.googleapis.com/minikube-builds/logs/$env:MINIKU
$json = "{`"state`": `"$env:status`", `"description`": `"Jenkins`", `"target_url`": `"$env:target_url`", `"context`": `"VirtualBox_Windows`"}"
Invoke-WebRequest -Uri "https://api.github.com/repos/kubernetes/minikube/statuses/$env:COMMIT`?access_token=$env:access_token" -Body $json -ContentType "application/json" -Method Post -usebasicparsing
Exit $env:result
Exit $env:result

232
pkg/addons/addons.go Normal file
View File

@ -0,0 +1,232 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
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 addons
import (
"fmt"
"os"
"strconv"
"github.com/pkg/errors"
"github.com/spf13/viper"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/cluster"
"k8s.io/minikube/pkg/minikube/command"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/storageclass"
pkgutil "k8s.io/minikube/pkg/util"
)
// defaultStorageClassProvisioner is the name of the default storage class provisioner
const defaultStorageClassProvisioner = "standard"
func Set(name, value, profile string) error {
a, valid := isAddonValid(name)
if !valid {
return errors.Errorf("%s is not a valid addon", name)
}
// Run any additional validations for this property
if err := run(name, value, profile, a.validations); err != nil {
return errors.Wrap(err, "running validations")
}
// Set the value
c, err := config.Load(profile)
if err != nil {
return errors.Wrap(err, "loading profile")
}
if err := a.set(c, name, value); err != nil {
return errors.Wrap(err, "setting new value of addon")
}
// Run any callbacks for this property
if err := run(name, value, profile, a.callbacks); err != nil {
return errors.Wrap(err, "running callbacks")
}
// Write the value
return config.Write(profile, c)
}
// Runs all the validation or callback functions and collects errors
func run(name, value, profile string, fns []setFn) error {
var errors []error
for _, fn := range fns {
err := fn(name, value, profile)
if err != nil {
errors = append(errors, err)
}
}
if len(errors) > 0 {
return fmt.Errorf("%v", errors)
}
return nil
}
// SetBool sets a bool value
func SetBool(m *config.MachineConfig, name string, val string) error {
b, err := strconv.ParseBool(val)
if err != nil {
return err
}
if m.Addons == nil {
m.Addons = map[string]bool{}
}
m.Addons[name] = b
return nil
}
// enableOrDisableAddon updates addon status executing any commands necessary
func enableOrDisableAddon(name, val, profile string) error {
enable, err := strconv.ParseBool(val)
if err != nil {
return errors.Wrapf(err, "parsing bool: %s", name)
}
addon := assets.Addons[name]
// check addon status before enabling/disabling it
alreadySet, err := isAddonAlreadySet(addon, enable)
if err != nil {
out.ErrT(out.Conflict, "{{.error}}", out.V{"error": err})
return err
}
//if addon is already enabled or disabled, do nothing
if alreadySet {
return nil
}
if name == "istio" && enable {
minMem := 8192
minCpus := 4
memorySizeMB := pkgutil.CalculateSizeInMB(viper.GetString("memory"))
cpuCount := viper.GetInt("cpus")
if memorySizeMB < minMem || cpuCount < minCpus {
out.WarningT("Enable istio needs {{.minMem}} MB of memory and {{.minCpus}} CPUs.", out.V{"minMem": minMem, "minCpus": minCpus})
}
}
// TODO(r2d4): config package should not reference API, pull this out
api, err := machine.NewAPIClient()
if err != nil {
return errors.Wrap(err, "machine client")
}
defer api.Close()
//if minikube is not running, we return and simply update the value in the addon
//config and rewrite the file
if !cluster.IsMinikubeRunning(api) {
return nil
}
cfg, err := config.Load(profile)
if err != nil && !os.IsNotExist(err) {
exit.WithCodeT(exit.Data, "Unable to load config: {{.error}}", out.V{"error": err})
}
host, err := cluster.CheckIfHostExistsAndLoad(api, cfg.Name)
if err != nil {
return errors.Wrap(err, "getting host")
}
cmd, err := machine.CommandRunner(host)
if err != nil {
return errors.Wrap(err, "command runner")
}
data := assets.GenerateTemplateData(cfg.KubernetesConfig)
return enableOrDisableAddonInternal(addon, cmd, data, enable)
}
func isAddonAlreadySet(addon *assets.Addon, enable bool) (bool, error) {
addonStatus, err := addon.IsEnabled()
if err != nil {
return false, errors.Wrap(err, "get the addon status")
}
if addonStatus && enable {
return true, nil
} else if !addonStatus && !enable {
return true, nil
}
return false, nil
}
func enableOrDisableAddonInternal(addon *assets.Addon, cmd command.Runner, data interface{}, enable bool) error {
var err error
updateFile := cmd.Copy
if !enable {
updateFile = cmd.Remove
}
for _, addon := range addon.Assets {
var addonFile assets.CopyableFile
if addon.IsTemplate() {
addonFile, err = addon.Evaluate(data)
if err != nil {
return errors.Wrapf(err, "evaluate bundled addon %s asset", addon.GetAssetName())
}
} else {
addonFile = addon
}
if err := updateFile(addonFile); err != nil {
return errors.Wrapf(err, "updating addon %s", addon.AssetName)
}
}
return nil
}
// enableOrDisableStorageClasses enables or disables storage classes
func enableOrDisableStorageClasses(name, val, profile string) error {
enable, err := strconv.ParseBool(val)
if err != nil {
return errors.Wrap(err, "Error parsing boolean")
}
class := defaultStorageClassProvisioner
if name == "storage-provisioner-gluster" {
class = "glusterfile"
}
storagev1, err := storageclass.GetStoragev1()
if err != nil {
return errors.Wrapf(err, "Error getting storagev1 interface %v ", err)
}
if enable {
// Only StorageClass for 'name' should be marked as default
err = storageclass.SetDefaultStorageClass(storagev1, class)
if err != nil {
return errors.Wrapf(err, "Error making %s the default storage class", class)
}
} else {
// Unset the StorageClass as default
err := storageclass.DisableDefaultStorageClass(storagev1, class)
if err != nil {
return errors.Wrapf(err, "Error disabling %s as the default storage class", class)
}
}
return enableOrDisableAddon(name, val, profile)
}

109
pkg/addons/addons_test.go Normal file
View File

@ -0,0 +1,109 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
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 addons
import (
"fmt"
"os"
"testing"
"gotest.tools/assert"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/config"
)
func TestIsAddonAlreadySet(t *testing.T) {
testCases := []struct {
addonName string
}{
{
addonName: "ingress",
},
{
addonName: "registry",
},
}
for _, test := range testCases {
addon := assets.Addons[test.addonName]
addonStatus, _ := addon.IsEnabled()
alreadySet, err := isAddonAlreadySet(addon, addonStatus)
if !alreadySet {
if addonStatus {
t.Errorf("Did not get expected status, \n\n expected %+v already enabled", test.addonName)
} else {
t.Errorf("Did not get expected status, \n\n expected %+v already disabled", test.addonName)
}
}
if err != nil {
t.Errorf("Got unexpected error: %+v", err)
}
}
}
func TestDisableUnknownAddon(t *testing.T) {
tmpProfile := "temp-minikube-profile"
if err := Set("InvalidAddon", "false", tmpProfile); err == nil {
t.Fatalf("Disable did not return error for unknown addon")
}
}
func TestEnableUnknownAddon(t *testing.T) {
tmpProfile := "temp-minikube-profile"
if err := Set("InvalidAddon", "true", tmpProfile); err == nil {
t.Fatalf("Enable did not return error for unknown addon")
}
}
func TestEnableAndDisableAddon(t *testing.T) {
tests := []struct {
name string
enable bool
}{
{
name: "test enable",
enable: true,
}, {
name: "test disable",
enable: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
tmpProfile := "temp-minikube-profile"
if err := os.MkdirAll(config.ProfileFolderPath(tmpProfile), 0777); err != nil {
t.Fatalf("error creating temporary directory")
}
defer os.RemoveAll(config.ProfileFolderPath(tmpProfile))
if err := config.DefaultLoader.WriteConfigToFile(tmpProfile, &config.MachineConfig{}); err != nil {
t.Fatalf("error creating temporary profile config: %v", err)
}
if err := Set("dashboard", fmt.Sprintf("%t", test.enable), tmpProfile); err != nil {
t.Fatalf("Disable returned unexpected error: " + err.Error())
}
c, err := config.DefaultLoader.LoadConfigFromFile(tmpProfile)
if err != nil {
t.Fatalf("error loading config: %v", err)
}
assert.Equal(t, c.Addons["dashboard"], test.enable)
})
}
}

130
pkg/addons/config.go Normal file
View File

@ -0,0 +1,130 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
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 addons
import "k8s.io/minikube/pkg/minikube/config"
type setFn func(string, string, string) error
// Addon represents an addon
type Addon struct {
name string
set func(*config.MachineConfig, string, string) error
validations []setFn
callbacks []setFn
}
// Addons is a list of all addons
var Addons = []*Addon{
{
name: "addon-manager",
set: SetBool,
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "dashboard",
set: SetBool,
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "default-storageclass",
set: SetBool,
callbacks: []setFn{enableOrDisableStorageClasses},
},
{
name: "efk",
set: SetBool,
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "freshpod",
set: SetBool,
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "gvisor",
set: SetBool,
validations: []setFn{IsContainerdRuntime},
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "helm-tiller",
set: SetBool,
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "ingress",
set: SetBool,
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "ingress-dns",
set: SetBool,
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "istio-provisioner",
set: SetBool,
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "istio",
set: SetBool,
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "logviewer",
set: SetBool,
},
{
name: "metrics-server",
set: SetBool,
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "nvidia-driver-installer",
set: SetBool,
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "nvidia-gpu-device-plugin",
set: SetBool,
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "registry",
set: SetBool,
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "registry-creds",
set: SetBool,
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "storage-provisioner",
set: SetBool,
callbacks: []setFn{enableOrDisableAddon},
},
{
name: "storage-provisioner-gluster",
set: SetBool,
callbacks: []setFn{enableOrDisableStorageClasses},
},
}

62
pkg/addons/validations.go Normal file
View File

@ -0,0 +1,62 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
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 addons
import (
"fmt"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/cruntime"
)
// containerdOnlyMsg is the message shown when a containerd-only addon is enabled
const containerdOnlyAddonMsg = `
This addon can only be enabled with the containerd runtime backend. To enable this backend, please first stop minikube with:
minikube stop
and then start minikube again with the following flags:
minikube start --container-runtime=containerd --docker-opt containerd=/var/run/containerd/containerd.sock`
// IsContainerdRuntime is a validator which returns an error if the current runtime is not containerd
func IsContainerdRuntime(_, _, profile string) error {
config, err := config.Load(profile)
if err != nil {
return fmt.Errorf("config.Load: %v", err)
}
r, err := cruntime.New(cruntime.Config{Type: config.KubernetesConfig.ContainerRuntime})
if err != nil {
return err
}
_, ok := r.(*cruntime.Containerd)
if !ok {
return fmt.Errorf(containerdOnlyAddonMsg)
}
return nil
}
// isAddonValid returns the addon, true if it is valid
// otherwise returns nil, false
func isAddonValid(name string) (*Addon, bool) {
for _, a := range Addons {
if a.name == name {
return a, true
}
}
return nil, false
}

View File

@ -0,0 +1,45 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
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 addons
import "testing"
func TestIsAddonValid(t *testing.T) {
tests := []struct {
description string
name string
isValid bool
}{
{
description: "valid addon",
name: "gvisor",
isValid: true,
}, {
description: "invalid addon",
name: "invalid",
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
_, valid := isAddonValid(test.name)
if test.isValid != valid {
t.Fatalf("expected: %t\nactual:%t\n", test.isValid, valid)
}
})
}
}

278
pkg/drivers/kic/kic.go Normal file
View File

@ -0,0 +1,278 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
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 kic
import (
"fmt"
"os/exec"
"strconv"
"strings"
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/state"
"github.com/pkg/errors"
pkgdrivers "k8s.io/minikube/pkg/drivers"
"k8s.io/minikube/pkg/drivers/kic/node"
"k8s.io/minikube/pkg/drivers/kic/oci"
"k8s.io/minikube/pkg/minikube/command"
)
// https://minikube.sigs.k8s.io/docs/reference/drivers/kic/
type Driver struct {
*drivers.BaseDriver
*pkgdrivers.CommonDriver
URL string
exec command.Runner
NodeConfig Config
OCIBinary string // docker,podman
}
// Config is configuration for the kic driver used by registry
type Config struct {
MachineName string // maps to the container name being created
CPU int // Number of CPU cores assigned to the container
Memory int // max memory in MB
StorePath string // lib machine store path
OCIBinary string // oci tool to use (docker, podman,...)
ImageDigest string // image name with sha to use for the node
APIServerPort int32 // port to connect to forward from container to user's machine
Mounts []oci.Mount // mounts
PortMappings []oci.PortMapping // container port mappings
Envs map[string]string // key,value of environment variables passed to the node
}
// NewDriver returns a fully configured Kic driver
func NewDriver(c Config) *Driver {
d := &Driver{
BaseDriver: &drivers.BaseDriver{
MachineName: c.MachineName,
StorePath: c.StorePath,
},
exec: command.NewKICRunner(c.MachineName, c.OCIBinary),
NodeConfig: c,
}
return d
}
// Create a host using the driver's config
func (d *Driver) Create() error {
params := node.CreateConfig{
Name: d.NodeConfig.MachineName,
Image: d.NodeConfig.ImageDigest,
ClusterLabel: node.ClusterLabelKey + "=" + d.MachineName,
CPUs: strconv.Itoa(d.NodeConfig.CPU),
Memory: strconv.Itoa(d.NodeConfig.Memory) + "mb",
Envs: d.NodeConfig.Envs,
ExtraArgs: []string{"--expose", fmt.Sprintf("%d", d.NodeConfig.APIServerPort)},
OCIBinary: d.NodeConfig.OCIBinary,
}
// control plane specific options
params.PortMappings = append(params.PortMappings, oci.PortMapping{
ListenAddress: "127.0.0.1",
HostPort: d.NodeConfig.APIServerPort,
ContainerPort: 6443,
})
_, err := node.CreateNode(params)
if err != nil {
return errors.Wrap(err, "create kic node")
}
return nil
}
// DriverName returns the name of the driver
func (d *Driver) DriverName() string {
if d.NodeConfig.OCIBinary == "podman" {
return "podman"
}
return "docker"
}
// GetIP returns an IP or hostname that this host is available at
func (d *Driver) GetIP() (string, error) {
node, err := node.Find(d.OCIBinary, d.MachineName, d.exec)
if err != nil {
return "", fmt.Errorf("ip not found for nil node")
}
ip, _, err := node.IP()
return ip, err
}
// GetSSHHostname returns hostname for use with ssh
func (d *Driver) GetSSHHostname() (string, error) {
return "", fmt.Errorf("driver does not have SSHHostName")
}
// GetSSHPort returns port for use with ssh
func (d *Driver) GetSSHPort() (int, error) {
return 0, fmt.Errorf("driver does not support GetSSHPort")
}
// GetURL returns ip of the container running kic control-panel
func (d *Driver) GetURL() (string, error) {
return d.GetIP()
}
// GetState returns the state that the host is in (running, stopped, etc)
func (d *Driver) GetState() (state.State, error) {
cmd := exec.Command(d.NodeConfig.OCIBinary, "inspect", "-f", "{{.State.Status}}", d.MachineName)
out, err := cmd.CombinedOutput()
o := strings.Trim(string(out), "\n")
if err != nil {
return state.Error, errors.Wrapf(err, "error stop node %s", d.MachineName)
}
switch o {
case "running":
return state.Running, nil
case "exited":
return state.Stopped, nil
case "paused":
return state.Paused, nil
case "restarting":
return state.Starting, nil
case "dead":
return state.Error, nil
default:
return state.None, fmt.Errorf("unknown state")
}
}
// Kill stops a host forcefully, including any containers that we are managing.
func (d *Driver) Kill() error {
cmd := exec.Command(d.NodeConfig.OCIBinary, "kill", d.MachineName)
if err := cmd.Run(); err != nil {
return errors.Wrapf(err, "killing kic node %s", d.MachineName)
}
return nil
}
// Remove will delete the Kic Node Container
func (d *Driver) Remove() error {
if _, err := d.nodeID(d.MachineName); err != nil {
return errors.Wrapf(err, "not found node %s", d.MachineName)
}
cmd := exec.Command(d.NodeConfig.OCIBinary, "rm", "-f", "-v", d.MachineName)
if err := cmd.Run(); err != nil {
return errors.Wrapf(err, "error removing node %s", d.MachineName)
}
return nil
}
// Restart a host
func (d *Driver) Restart() error {
s, err := d.GetState()
if err != nil {
return errors.Wrap(err, "get kic state")
}
switch s {
case state.Paused:
return d.Unpause()
case state.Stopped:
return d.Start()
case state.Running, state.Error:
if err = d.Stop(); err != nil {
return fmt.Errorf("restarting a kic stop phase %v", err)
}
if err = d.Start(); err != nil {
return fmt.Errorf("restarting a kic start phase %v", err)
}
return nil
}
return fmt.Errorf("restarted not implemented for kic state %s yet", s)
}
// Unpause a kic container
func (d *Driver) Unpause() error {
cmd := exec.Command(d.NodeConfig.OCIBinary, "unpause", d.MachineName)
if err := cmd.Run(); err != nil {
return errors.Wrapf(err, "unpausing %s", d.MachineName)
}
return nil
}
// Start a _stopped_ kic container
// not meant to be used for Create().
func (d *Driver) Start() error {
s, err := d.GetState()
if err != nil {
return errors.Wrap(err, "get kic state")
}
if s == state.Stopped {
cmd := exec.Command(d.NodeConfig.OCIBinary, "start", d.MachineName)
if err := cmd.Run(); err != nil {
return errors.Wrapf(err, "starting a stopped kic node %s", d.MachineName)
}
return nil
}
// TODO:medyagh maybe make it idempotent
return fmt.Errorf("cant start a not-stopped (%s) kic node", s)
}
// Stop a host gracefully, including any containers that we are managing.
func (d *Driver) Stop() error {
cmd := exec.Command(d.NodeConfig.OCIBinary, "stop", d.MachineName)
if err := cmd.Run(); err != nil {
return errors.Wrapf(err, "stopping %s", d.MachineName)
}
return nil
}
// RunSSHCommandFromDriver implements direct ssh control to the driver
func (d *Driver) RunSSHCommandFromDriver() error {
return fmt.Errorf("driver does not support RunSSHCommandFromDriver commands")
}
// looks up for a container node by name, will return error if not found.
func (d *Driver) nodeID(nameOrID string) (string, error) {
cmd := exec.Command(d.NodeConfig.OCIBinary, "inspect", "-f", "{{.Id}}", nameOrID)
id, err := cmd.CombinedOutput()
if err != nil {
id = []byte{}
}
return string(id), err
}
func ImageForVersion(ver string) (string, error) {
switch ver {
case "v1.11.10":
return "medyagh/kic:v1.11.10@sha256:23bb7f5e8dd2232ec829132172e87f7b9d8de65269630989e7dac1e0fe993b74", nil
case "v1.12.8":
return "medyagh/kic:v1.12.8@sha256:c74bc5f3efe3539f6e1ad7f11bf7c09f3091c0547cb28071f4e43067053e5898", nil
case "v1.12.9":
return "medyagh/kic:v1.12.9@sha256:ff82f58e18dcb22174e8eb09dae14f7edd82d91a83c7ef19e33298d0eba6a0e3", nil
case "v1.12.10":
return "medyagh/kic:v1.12.10@sha256:2d174bae7c20698e59791e7cca9b6db234053d1a92a009d5bb124e482540c70b", nil
case "v1.13.6":
return "medyagh/kic:v1.13.6@sha256:cf63e50f824fe17b90374d38d64c5964eb9fe6b3692669e1201fcf4b29af4964", nil
case "v1.13.7":
return "medyagh/kic:v1.13.7@sha256:1a6a5e1c7534cf3012655e99df680496df9bcf0791a304adb00617d5061233fa", nil
case "v1.14.3":
return "medyagh/kic:v1.14.3@sha256:cebec21f6af23d5dfa3465b88ddf4a1acb94c2c20a0a6ff8cc1c027b0a4e2cec", nil
case "v1.15.0":
return "medyagh/kic:v1.15.0@sha256:40d433d00a2837c8be829bd3cb0576988e377472062490bce0b18281c7f85303", nil
case "v1.15.3":
return "medyagh/kic:v1.15.3@sha256:f05ce52776a86c6ead806942d424de7076af3f115b0999332981a446329e6cf1", nil
case "v1.16.1":
return "medyagh/kic:v1.16.1@sha256:e74530d22e6a04442a97a09bdbba885ad693fcc813a0d1244da32666410d1ad1", nil
case "v1.16.2":
return "medyagh/kic:v1.16.2@sha256:3374a30971bf5b0011441a227fa56ef990b76125b36ca0ab8316a3c7e4f137a3", nil
default:
return "medyagh/kic:v1.16.2@sha256:3374a30971bf5b0011441a227fa56ef990b76125b36ca0ab8316a3c7e4f137a3", nil
}
}

View File

@ -0,0 +1,193 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
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 node
import (
"fmt"
"os/exec"
"path/filepath"
"strings"
"k8s.io/minikube/pkg/drivers/kic/oci"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/command"
"github.com/pkg/errors"
)
const (
// Docker default bridge network is named "bridge" (https://docs.docker.com/network/bridge/#use-the-default-bridge-network)
DefaultNetwork = "bridge"
ClusterLabelKey = "io.x-k8s.kic.cluster" // ClusterLabelKey is applied to each node docker container for identification
NodeRoleKey = "io.k8s.sigs.kic.role"
)
// Node represents a handle to a kic node
// This struct must be created by one of: CreateControlPlane
type Node struct {
// must be one of docker container ID or name
name string
r command.Runner // Runner
ociBinary string
}
type CreateConfig struct {
Name string // used for container name and hostname
Image string // container image to use to create the node.
ClusterLabel string // label the containers we create using minikube so we can clean up
Role string // currently only role supported is control-plane
Mounts []oci.Mount // volume mounts
PortMappings []oci.PortMapping // ports to map to container from host
CPUs string // number of cpu cores assign to container
Memory string // memory (mbs) to assign to the container
Envs map[string]string // environment variables to pass to the container
ExtraArgs []string // a list of any extra option to pass to oci binary during creation time, for example --expose 8080...
OCIBinary string // docker or podman
}
// CreateNode creates a new container node
func CreateNode(p CreateConfig) (*Node, error) {
cmder := command.NewKICRunner(p.Name, p.OCIBinary)
runArgs := []string{
fmt.Sprintf("--cpus=%s", p.CPUs),
fmt.Sprintf("--memory=%s", p.Memory),
"-d", // run the container detached
"-t", // allocate a tty for entrypoint logs
// running containers in a container requires privileged
// NOTE: we could try to replicate this with --cap-add, and use less
// privileges, but this flag also changes some mounts that are necessary
// including some ones docker would otherwise do by default.
// for now this is what we want. in the future we may revisit this.
"--privileged",
"--security-opt", "seccomp=unconfined", // also ignore seccomp
"--tmpfs", "/tmp", // various things depend on working /tmp
"--tmpfs", "/run", // systemd wants a writable /run
// logs,pods be stroed on filesystem vs inside container,
"--volume", "/var",
// some k8s things want /lib/modules
"-v", "/lib/modules:/lib/modules:ro",
"--hostname", p.Name, // make hostname match container name
"--name", p.Name, // ... and set the container name
// label the node with the cluster ID
"--label", p.ClusterLabel,
// label the node with the role ID
"--label", fmt.Sprintf("%s=%s", NodeRoleKey, p.Role),
}
for key, val := range p.Envs {
runArgs = append(runArgs, "-e", fmt.Sprintf("%s=%s", key, val))
}
// adds node specific args
runArgs = append(runArgs, p.ExtraArgs...)
if oci.UsernsRemap(p.OCIBinary) {
// We need this argument in order to make this command work
// in systems that have userns-remap enabled on the docker daemon
runArgs = append(runArgs, "--userns=host")
}
_, err := oci.CreateContainer(p.OCIBinary,
p.Image,
oci.WithRunArgs(runArgs...),
oci.WithMounts(p.Mounts),
oci.WithPortMappings(p.PortMappings),
)
if err != nil {
return nil, errors.Wrap(err, "oci create ")
}
// we should return a handle so the caller can clean it up
node, err := Find(p.OCIBinary, p.Name, cmder)
if err != nil {
return node, errors.Wrap(err, "find node")
}
return node, nil
}
// Find finds a node
func Find(ociBinary string, name string, cmder command.Runner) (*Node, error) {
_, err := oci.Inspect(ociBinary, name, "{{.Id}}")
if err != nil {
return nil, fmt.Errorf("can't find node %v", err)
}
return &Node{
name: name,
r: cmder,
}, nil
}
// WriteFile writes content to dest on the node
func (n *Node) WriteFile(dest, content string, perm string) error {
// create destination directory
cmd := exec.Command("mkdir", "-p", filepath.Dir(dest))
rr, err := n.r.RunCmd(cmd)
if err != nil {
return errors.Wrapf(err, "failed to create directory %s cmd: %v output:%q", cmd.Args, dest, rr.Output())
}
cmd = exec.Command("cp", "/dev/stdin", dest)
cmd.Stdin = strings.NewReader(content)
if rr, err := n.r.RunCmd(cmd); err != nil {
return errors.Wrapf(err, "failed to run: cp /dev/stdin %s cmd: %v output:%q", dest, cmd.Args, rr.Output())
}
cmd = exec.Command("chmod", perm, dest)
_, err = n.r.RunCmd(cmd)
if err != nil {
return errors.Wrapf(err, "failed to run: chmod %s %s", perm, dest)
}
return nil
}
// IP returns the IP address of the node
func (n *Node) IP() (ipv4 string, ipv6 string, err error) {
// retrieve the IP address of the node using docker inspect
lines, err := oci.Inspect(n.ociBinary, n.name, "{{range .NetworkSettings.Networks}}{{.IPAddress}},{{.GlobalIPv6Address}}{{end}}")
if err != nil {
return "", "", errors.Wrap(err, "node ips")
}
if len(lines) != 1 {
return "", "", errors.Errorf("file should only be one line, got %d lines", len(lines))
}
ips := strings.Split(lines[0], ",")
if len(ips) != 2 {
return "", "", errors.Errorf("container addresses should have 2 values, got %d values: %+v", len(ips), ips)
}
return ips[0], ips[1], nil
}
// Copy copies a local asset into the node
func (n *Node) Copy(ociBinary string, asset assets.CopyableFile) error {
if err := oci.Copy(ociBinary, n.name, asset); err != nil {
return errors.Wrap(err, "failed to copy file/folder")
}
cmd := exec.Command("chmod", asset.GetPermissions(), asset.GetTargetName())
if _, err := n.r.RunCmd(cmd); err != nil {
return errors.Wrap(err, "failed to chmod file permissions")
}
return nil
}
// Remove removes the node
func (n *Node) Remove() error {
return oci.Remove(n.ociBinary, n.name)
}

397
pkg/drivers/kic/oci/oci.go Normal file
View File

@ -0,0 +1,397 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
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 oci
import (
"os"
"github.com/docker/machine/libmachine/state"
"k8s.io/minikube/pkg/minikube/assets"
"bufio"
"bytes"
"github.com/pkg/errors"
"fmt"
"net"
"os/exec"
"strings"
"time"
"github.com/cenkalti/backoff"
)
// Stop stops a container
func Stop(ociBinary, ociID string) error {
cmd := exec.Command(ociBinary, "stop", ociID)
err := cmd.Run()
if err != nil {
return errors.Wrapf(err, "error stop node %s", ociID)
}
return nil
}
// Status returns the status of the container
func Status(ociBinary string, ociID string) (state.State, error) {
cmd := exec.Command(ociBinary, "inspect", "-f", "{{.State.Status}}", ociID)
out, err := cmd.CombinedOutput()
o := strings.Trim(string(out), "\n")
s := state.Error
switch o {
case "running":
s = state.Running
case "exited":
s = state.Stopped
case "paused":
s = state.Paused
case "restaring":
s = state.Starting
}
if err != nil {
return state.Error, errors.Wrapf(err, "error getting node %s status", ociID)
}
return s, nil
}
// SystemStatus checks if the oci container engine is running
func SystemStatus(ociBinary string, ociID string) (state.State, error) {
_, err := exec.LookPath(ociBinary)
if err != nil {
return state.Error, err
}
err = exec.Command("docker", "info").Run()
if err != nil {
return state.Error, err
}
return state.Running, nil
}
// Remove removes a container
func Remove(ociBinary string, ociID string) error {
// TODO: force remove should be an option
cmd := exec.Command(ociBinary, "rm", "-f", "-v", ociID)
if err := cmd.Run(); err != nil {
return errors.Wrapf(err, "error removing node %s", ociID)
}
return nil
}
// Pause pauses a container
func Pause(ociBinary string, ociID string) error {
cmd := exec.Command(ociBinary, "pause", ociID)
if err := cmd.Run(); err != nil {
return errors.Wrapf(err, "error pausing node %s", ociID)
}
return nil
}
// Inspect return low-level information on containers
func Inspect(ociBinary string, containerNameOrID, format string) ([]string, error) {
cmd := exec.Command(ociBinary, "inspect",
"-f", format,
containerNameOrID) // ... against the "node" container
var buff bytes.Buffer
cmd.Stdout = &buff
cmd.Stderr = &buff
err := cmd.Run()
scanner := bufio.NewScanner(&buff)
var lines []string
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines, err
}
// NetworkInspect displays detailed information on one or more networks
func NetworkInspect(networkNames []string, format string) ([]string, error) {
cmd := exec.Command("docker", "network", "inspect",
"-f", format,
strings.Join(networkNames, " "))
var buff bytes.Buffer
cmd.Stdout = &buff
cmd.Stderr = &buff
err := cmd.Run()
scanner := bufio.NewScanner(&buff)
var lines []string
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines, err
}
// GetSubnets returns a slice of subnets for a specified network name
// For example the command : docker network inspect -f '{{range (index (index . "IPAM") "Config")}}{{index . "Subnet"}} {{end}}' bridge
// returns 172.17.0.0/16
func GetSubnets(networkName string) ([]string, error) {
format := `{{range (index (index . "IPAM") "Config")}}{{index . "Subnet"}} {{end}}`
lines, err := NetworkInspect([]string{networkName}, format)
if err != nil {
return nil, err
}
return strings.Split(lines[0], " "), nil
}
// ImageInspect return low-level information on containers images
func ImageInspect(containerNameOrID, format string) ([]string, error) {
cmd := exec.Command("docker", "image", "inspect",
"-f", format,
containerNameOrID,
)
var buff bytes.Buffer
cmd.Stdout = &buff
cmd.Stderr = &buff
err := cmd.Run()
scanner := bufio.NewScanner(&buff)
var lines []string
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines, err
}
// ImageID return the Id of the container image
func ImageID(containerNameOrID string) (string, error) {
lines, err := ImageInspect(containerNameOrID, "{{ .Id }}")
if err != nil {
return "", err
}
if len(lines) != 1 {
return "", fmt.Errorf("docker image ID should only be one line, got %d lines", len(lines))
}
return lines[0], nil
}
/*
This is adapated from:
https://github.com/kubernetes/kubernetes/blob/07a5488b2a8f67add543da72e8819407d8314204/pkg/kubelet/dockershim/helpers.go#L115-L155
*/
// generateMountBindings converts the mount list to a list of strings that
// can be understood by docker
// '<HostPath>:<ContainerPath>[:options]', where 'options'
// is a comma-separated list of the following strings:
// 'ro', if the path is read only
// 'Z', if the volume requires SELinux relabeling
func generateMountBindings(mounts ...Mount) []string {
result := make([]string, 0, len(mounts))
for _, m := range mounts {
bind := fmt.Sprintf("%s:%s", m.HostPath, m.ContainerPath)
var attrs []string
if m.Readonly {
attrs = append(attrs, "ro")
}
// Only request relabeling if the pod provides an SELinux context. If the pod
// does not provide an SELinux context relabeling will label the volume with
// the container's randomly allocated MCS label. This would restrict access
// to the volume to the container which mounts it first.
if m.SelinuxRelabel {
attrs = append(attrs, "Z")
}
switch m.Propagation {
case MountPropagationNone:
// noop, private is default
case MountPropagationBidirectional:
attrs = append(attrs, "rshared")
case MountPropagationHostToContainer:
attrs = append(attrs, "rslave")
default:
// Falls back to "private"
}
if len(attrs) > 0 {
bind = fmt.Sprintf("%s:%s", bind, strings.Join(attrs, ","))
}
// our specific modification is the following line: make this a docker flag
bind = fmt.Sprintf("--volume=%s", bind)
result = append(result, bind)
}
return result
}
// PullIfNotPresent pulls docker image if not present back off exponentially
func PullIfNotPresent(ociBinary string, image string, forceUpdate bool, maxWait time.Duration) error {
cmd := exec.Command(ociBinary, "inspect", "--type=image", image)
err := cmd.Run()
if err == nil && !forceUpdate {
return nil // if presents locally and not force
}
b := backoff.NewExponentialBackOff()
b.MaxElapsedTime = maxWait
f := func() error {
return pull(ociBinary, image)
}
return backoff.Retry(f, b)
}
// Pull pulls an image, retrying up to retries times
func pull(ociBinary string, image string) error {
cmd := exec.Command(ociBinary, "pull", image)
err := cmd.Run()
if err != nil {
return fmt.Errorf("error pull image %s : %v", image, err)
}
return err
}
// UsernsRemap checks if userns-remap is enabled in dockerd
func UsernsRemap(ociBinary string) bool {
cmd := exec.Command(ociBinary, "info", "--format", "'{{json .SecurityOptions}}'")
var buff bytes.Buffer
cmd.Stdout = &buff
cmd.Stderr = &buff
err := cmd.Run()
scanner := bufio.NewScanner(&buff)
var lines []string
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err != nil {
return false
}
if len(lines) > 0 {
if strings.Contains(lines[0], "name=userns") {
return true
}
}
return false
}
func generatePortMappings(portMappings ...PortMapping) []string {
result := make([]string, 0, len(portMappings))
for _, pm := range portMappings {
var hostPortBinding string
if pm.ListenAddress != "" {
hostPortBinding = net.JoinHostPort(pm.ListenAddress, fmt.Sprintf("%d", pm.HostPort))
} else {
hostPortBinding = fmt.Sprintf("%d", pm.HostPort)
}
publish := fmt.Sprintf("--publish=%s:%d", hostPortBinding, pm.ContainerPort)
result = append(result, publish)
}
return result
}
// Save saves an image archive "docker/podman save"
func Save(ociBinary string, image, dest string) error {
cmd := exec.Command(ociBinary, "save", "-o", dest, image)
var buff bytes.Buffer
cmd.Stdout = &buff
cmd.Stderr = &buff
err := cmd.Run()
scanner := bufio.NewScanner(&buff)
var lines []string
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err != nil {
return errors.Wrapf(err, "saving image to tar failed, output %s", lines[0])
}
return nil
}
// CreateOpt is an option for Create
type CreateOpt func(*createOpts) *createOpts
// actual options struct
type createOpts struct {
RunArgs []string
ContainerArgs []string
Mounts []Mount
PortMappings []PortMapping
}
// CreateContainer creates a container with "docker/podman run"
func CreateContainer(ociBinary string, image string, opts ...CreateOpt) ([]string, error) {
o := &createOpts{}
for _, opt := range opts {
o = opt(o)
}
// convert mounts to container run args
runArgs := o.RunArgs
for _, mount := range o.Mounts {
runArgs = append(runArgs, generateMountBindings(mount)...)
}
for _, portMapping := range o.PortMappings {
runArgs = append(runArgs, generatePortMappings(portMapping)...)
}
// construct the actual docker run argv
args := []string{"run"}
args = append(args, runArgs...)
args = append(args, image)
args = append(args, o.ContainerArgs...)
cmd := exec.Command(ociBinary, args...)
var buff bytes.Buffer
cmd.Stdout = &buff
cmd.Stderr = &buff
err := cmd.Run()
scanner := bufio.NewScanner(&buff)
var output []string
for scanner.Scan() {
output = append(output, scanner.Text())
}
if err != nil {
return output, errors.Wrapf(err, "CreateContainer %v ", args)
}
return output, nil
}
// WithRunArgs sets the args for docker run
// as in the args portion of `docker run args... image containerArgs...`
func WithRunArgs(args ...string) CreateOpt {
return func(r *createOpts) *createOpts {
r.RunArgs = args
return r
}
}
// WithMounts sets the container mounts
func WithMounts(mounts []Mount) CreateOpt {
return func(r *createOpts) *createOpts {
r.Mounts = mounts
return r
}
}
// WithPortMappings sets the container port mappings to the host
func WithPortMappings(portMappings []PortMapping) CreateOpt {
return func(r *createOpts) *createOpts {
r.PortMappings = portMappings
return r
}
}
// Copy copies a local asset into the container
func Copy(ociBinary string, ociID string, asset assets.CopyableFile) error {
if _, err := os.Stat(asset.GetAssetName()); os.IsNotExist(err) {
return errors.Wrapf(err, "error source %s does not exist", asset.GetAssetName())
}
destination := fmt.Sprintf("%s:%s", ociID, asset.GetTargetDir())
cmd := exec.Command(ociBinary, "cp", asset.GetAssetName(), destination)
err := cmd.Run()
if err != nil {
return errors.Wrapf(err, "error copying %s into node", asset.GetAssetName())
}
return nil
}

View File

@ -0,0 +1,96 @@
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
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 oci
/*
These types are from
https://github.com/kubernetes/kubernetes/blob/063e7ff358fdc8b0916e6f39beedc0d025734cb1/pkg/kubelet/apis/cri/runtime/v1alpha2/api.pb.go#L183
*/
// Mount specifies a host volume to mount into a container.
// This is a close copy of the upstream cri Mount type
// see: k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2
// It additionally serializes the "propagation" field with the string enum
// names on disk as opposed to the int32 values, and the serlialzed field names
// have been made closer to core/v1 VolumeMount field names
// In yaml this looks like:
// containerPath: /foo
// hostPath: /bar
// readOnly: true
// selinuxRelabel: false
// propagation: None
// Propagation may be one of: None, HostToContainer, Bidirectional
type Mount struct {
// Path of the mount within the container.
ContainerPath string `protobuf:"bytes,1,opt,name=container_path,json=containerPath,proto3" json:"containerPath,omitempty"`
// Path of the mount on the host. If the hostPath doesn't exist, then runtimes
// should report error. If the hostpath is a symbolic link, runtimes should
// follow the symlink and mount the real destination to container.
HostPath string `protobuf:"bytes,2,opt,name=host_path,json=hostPath,proto3" json:"hostPath,omitempty"`
// If set, the mount is read-only.
Readonly bool `protobuf:"varint,3,opt,name=readonly,proto3,json=readOnly,proto3" json:"readOnly,omitempty"`
// If set, the mount needs SELinux relabeling.
SelinuxRelabel bool `protobuf:"varint,4,opt,name=selinux_relabel,json=selinuxRelabel,proto3" json:"selinuxRelabel,omitempty"`
// Requested propagation mode.
Propagation MountPropagation `protobuf:"varint,5,opt,name=propagation,proto3,enum=runtime.v1alpha2.MountPropagation" json:"propagation,omitempty"`
}
// PortMapping specifies a host port mapped into a container port.
// In yaml this looks like:
// containerPort: 80
// hostPort: 8000
// listenAddress: 127.0.0.1
type PortMapping struct {
// Port within the container.
ContainerPort int32 `protobuf:"varint,1,opt,name=container_port,json=containerPort,proto3" json:"containerPort,omitempty"`
// Port on the host.
HostPort int32 `protobuf:"varint,2,opt,name=host_path,json=hostPort,proto3" json:"hostPort,omitempty"`
ListenAddress string `protobuf:"bytes,3,opt,name=listenAddress,json=hostPort,proto3" json:"listenAddress,omitempty"`
}
// MountPropagation represents an "enum" for mount propagation options,
// see also Mount.
type MountPropagation int32
const (
// MountPropagationNone specifies that no mount propagation
// ("private" in Linux terminology).
MountPropagationNone MountPropagation = 0
// MountPropagationHostToContainer specifies that mounts get propagated
// from the host to the container ("rslave" in Linux).
MountPropagationHostToContainer MountPropagation = 1
// MountPropagationBidirectional specifies that mounts get propagated from
// the host to the container and from the container to the host
// ("rshared" in Linux).
MountPropagationBidirectional MountPropagation = 2
)
// MountPropagationValueToName is a map of valid MountPropogation values to
// their string names
var MountPropagationValueToName = map[MountPropagation]string{
MountPropagationNone: "None",
MountPropagationHostToContainer: "HostToContainer",
MountPropagationBidirectional: "Bidirectional",
}
// MountPropagationNameToValue is a map of valid MountPropogation names to
// their values
var MountPropagationNameToValue = map[string]MountPropagation{
"None": MountPropagationNone,
"HostToContainer": MountPropagationHostToContainer,
"Bidirectional": MountPropagationBidirectional,
}

View File

@ -22,9 +22,9 @@ import (
"path"
"path/filepath"
"runtime"
"strconv"
"github.com/pkg/errors"
"github.com/spf13/viper"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/localpath"
@ -54,15 +54,13 @@ func (a *Addon) Name() string {
return a.addonName
}
// IsEnabled checks if an Addon is enabled
// IsEnabled checks if an Addon is enabled for the current profile
func (a *Addon) IsEnabled() (bool, error) {
addonStatusText, err := config.Get(a.addonName)
c, err := config.Load(viper.GetString(config.MachineProfile))
if err == nil {
addonStatus, err := strconv.ParseBool(addonStatusText)
if err != nil {
return false, err
if status, ok := c.Addons[a.Name()]; ok {
return status, nil
}
return addonStatus, nil
}
return a.enabled, nil
}
@ -190,6 +188,22 @@ var Addons = map[string]*Addon{
"0640",
true),
}, false, "ingress"),
"istio-provisioner": NewAddon([]*BinAsset{
MustBinAsset(
"deploy/addons/istio-provisioner/istio-operator.yaml.tmpl",
vmpath.GuestAddonsDir,
"istio-operator.yaml",
"0640",
true),
}, true, "istio-provisioner"),
"istio": NewAddon([]*BinAsset{
MustBinAsset(
"deploy/addons/istio/istio-default-profile.yaml.tmpl",
vmpath.GuestAddonsDir,
"istio-default-profile.yaml",
"0640",
false),
}, false, "istio"),
"metrics-server": NewAddon([]*BinAsset{
MustBinAsset(
"deploy/addons/metrics-server/metrics-apiservice.yaml.tmpl",

View File

@ -49,14 +49,15 @@ type Bootstrapper interface {
}
const (
// BootstrapperTypeKubeadm is the kubeadm bootstrapper type
BootstrapperTypeKubeadm = "kubeadm"
// Kubeadm is the kubeadm bootstrapper type
Kubeadm = "kubeadm"
KIC = "kic"
)
// GetCachedBinaryList returns the list of binaries
func GetCachedBinaryList(bootstrapper string) []string {
switch bootstrapper {
case BootstrapperTypeKubeadm:
case Kubeadm:
return constants.KubeadmBinaries
default:
return []string{}
@ -66,8 +67,10 @@ func GetCachedBinaryList(bootstrapper string) []string {
// GetCachedImageList returns the list of images for a version
func GetCachedImageList(imageRepository string, version string, bootstrapper string) ([]string, error) {
switch bootstrapper {
case BootstrapperTypeKubeadm:
case Kubeadm:
return images.Kubeadm(imageRepository, version)
case KIC:
return []string{"alpine"}, nil // for testing purpose just caching alpine for kicbs
default:
return []string{}, nil
}

View File

@ -0,0 +1,57 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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.
*/
// bsutil package will eventually be renamed to kubeadm package after getting rid of older one
package bsutil
import (
"path"
"runtime"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
"k8s.io/minikube/pkg/minikube/command"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/machine"
"k8s.io/minikube/pkg/minikube/vmpath"
)
// TransferBinaries transfers all required Kubernetes binaries
func TransferBinaries(cfg config.KubernetesConfig, c command.Runner) error {
var g errgroup.Group
for _, name := range constants.KubeadmBinaries {
name := name
g.Go(func() error {
src, err := machine.CacheBinary(name, cfg.KubernetesVersion, "linux", runtime.GOARCH)
if err != nil {
return errors.Wrapf(err, "downloading %s", name)
}
dst := path.Join(binRoot(cfg.KubernetesVersion), name)
if err := machine.CopyBinary(c, src, dst); err != nil {
return errors.Wrapf(err, "copybinary %s -> %s", src, dst)
}
return nil
})
}
return g.Wait()
}
// binRoot returns the persistent path binaries are stored in
func binRoot(version string) string {
return path.Join(vmpath.GuestPersistentDir, "binaries", version)
}

View File

@ -0,0 +1,193 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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.
*/
// bsutil package will eventually be renamed to kubeadm package after getting rid of older one
package bsutil
import (
"fmt"
"sort"
"strings"
"github.com/blang/semver"
"github.com/golang/glog"
"github.com/pkg/errors"
"k8s.io/minikube/pkg/minikube/config"
)
const (
KubeadmCmdParam = iota
KubeadmConfigParam = iota
)
// componentExtraArgs holds extra args for a component
type componentExtraArgs struct {
Component string
Options map[string]string
}
// mapping of component to the section name in kubeadm.
var componentToKubeadmConfigKey = map[string]string{
Apiserver: "apiServer",
ControllerManager: "controllerManager",
Scheduler: "scheduler",
Kubeadm: "kubeadm",
// The Kubelet is not configured in kubeadm, only in systemd.
Kubelet: "",
}
// KubeadmExtraArgsWhitelist is a whitelist of supported kubeadm params that can be supplied to kubeadm through
// minikube's ExtraArgs parameter. The list is split into two parts - params that can be supplied as flags on the
// command line and params that have to be inserted into the kubeadm config file. This is because of a kubeadm
// constraint which allows only certain params to be provided from the command line when the --config parameter
// is specified
var KubeadmExtraArgsWhitelist = map[int][]string{
KubeadmCmdParam: {
"ignore-preflight-errors",
"dry-run",
"kubeconfig",
"kubeconfig-dir",
"node-name",
"cri-socket",
"experimental-upload-certs",
"certificate-key",
"rootfs",
},
KubeadmConfigParam: {
"pod-network-cidr",
},
}
// CreateFlagsFromExtraArgs converts kubeadm extra args into flags to be supplied from the command linne
func CreateFlagsFromExtraArgs(extraOptions config.ExtraOptionSlice) string {
kubeadmExtraOpts := extraOptions.AsMap().Get(Kubeadm)
// kubeadm allows only a small set of parameters to be supplied from the command line when the --config param
// is specified, here we remove those that are not allowed
for opt := range kubeadmExtraOpts {
if !config.ContainsParam(KubeadmExtraArgsWhitelist[KubeadmCmdParam], opt) {
// kubeadmExtraOpts is a copy so safe to delete
delete(kubeadmExtraOpts, opt)
}
}
return convertToFlags(kubeadmExtraOpts)
}
// extraConfigForComponent generates a map of flagname-value pairs for a k8s
// component.
func extraConfigForComponent(component string, opts config.ExtraOptionSlice, version semver.Version) (map[string]string, error) {
versionedOpts, err := defaultOptionsForComponentAndVersion(component, version)
if err != nil {
return nil, errors.Wrapf(err, "setting version specific options for %s", component)
}
for _, opt := range opts {
if opt.Component == component {
if val, ok := versionedOpts[opt.Key]; ok {
glog.Infof("Overwriting default %s=%s with user provided %s=%s for component %s", opt.Key, val, opt.Key, opt.Value, component)
}
versionedOpts[opt.Key] = opt.Value
}
}
return versionedOpts, nil
}
// defaultOptionsForComponentAndVersion returns the default option for a component and version
func defaultOptionsForComponentAndVersion(component string, version semver.Version) (map[string]string, error) {
versionedOpts := map[string]string{}
for _, opts := range versionSpecificOpts {
if opts.Option.Component == component {
if versionIsBetween(version, opts.GreaterThanOrEqual, opts.LessThanOrEqual) {
if val, ok := versionedOpts[opts.Option.Key]; ok {
return nil, fmt.Errorf("flag %s=%q already set %s=%q", opts.Option.Key, opts.Option.Value, opts.Option.Key, val)
}
versionedOpts[opts.Option.Key] = opts.Option.Value
}
}
}
return versionedOpts, nil
}
// newComponentExtraArgs creates a new ComponentExtraArgs
func newComponentExtraArgs(opts config.ExtraOptionSlice, version semver.Version, featureGates string) ([]componentExtraArgs, error) {
var kubeadmExtraArgs []componentExtraArgs
for _, extraOpt := range opts {
if _, ok := componentToKubeadmConfigKey[extraOpt.Component]; !ok {
return nil, fmt.Errorf("unknown component %q. valid components are: %v", componentToKubeadmConfigKey, componentToKubeadmConfigKey)
}
}
keys := []string{}
for k := range componentToKubeadmConfigKey {
keys = append(keys, k)
}
sort.Strings(keys)
for _, component := range keys {
kubeadmComponentKey := componentToKubeadmConfigKey[component]
if kubeadmComponentKey == "" {
continue
}
extraConfig, err := extraConfigForComponent(component, opts, version)
if err != nil {
return nil, errors.Wrapf(err, "getting kubeadm extra args for %s", component)
}
if featureGates != "" {
extraConfig["feature-gates"] = featureGates
}
if len(extraConfig) > 0 {
kubeadmExtraArgs = append(kubeadmExtraArgs, componentExtraArgs{
Component: kubeadmComponentKey,
Options: extraConfig,
})
}
}
return kubeadmExtraArgs, nil
}
// createExtraComponentConfig generates a map of component to extra args for all of the components except kubeadm
func createExtraComponentConfig(extraOptions config.ExtraOptionSlice, version semver.Version, componentFeatureArgs string) ([]componentExtraArgs, error) {
extraArgsSlice, err := newComponentExtraArgs(extraOptions, version, componentFeatureArgs)
if err != nil {
return nil, err
}
// kubeadm extra args should not be included in the kubeadm config in the extra args section (instead, they must
// be inserted explicitly in the appropriate places or supplied from the command line); here we remove all of the
// kubeadm extra args from the slice
for i, extraArgs := range extraArgsSlice {
if extraArgs.Component == Kubeadm {
extraArgsSlice = append(extraArgsSlice[:i], extraArgsSlice[i+1:]...)
break
}
}
return extraArgsSlice, nil
}
func convertToFlags(opts map[string]string) string {
var flags []string
var keys []string
for k := range opts {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
flags = append(flags, fmt.Sprintf("--%s=%s", k, opts[k]))
}
return strings.Join(flags, " ")
}

View File

@ -0,0 +1,69 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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.
*/
// bsutil package will eventually be renamed to kubeadm package after getting rid of older one
package bsutil
import (
"fmt"
"strconv"
"strings"
"github.com/pkg/errors"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
)
// supportedFG indicates whether a feature name is supported by the bootstrapper
func supportedFG(featureName string) bool {
for k := range features.InitFeatureGates {
if featureName == k {
return true
}
}
return false
}
// parseFeatureArgs parses feature args into extra args
func parseFeatureArgs(featureGates string) (map[string]bool, string, error) {
kubeadmFeatureArgs := map[string]bool{}
componentFeatureArgs := ""
for _, s := range strings.Split(featureGates, ",") {
if len(s) == 0 {
continue
}
fg := strings.SplitN(s, "=", 2)
if len(fg) != 2 {
return nil, "", fmt.Errorf("missing value for key \"%v\"", s)
}
k := strings.TrimSpace(fg[0])
v := strings.TrimSpace(fg[1])
if !supportedFG(k) {
componentFeatureArgs = fmt.Sprintf("%s%s,", componentFeatureArgs, s)
continue
}
boolValue, err := strconv.ParseBool(v)
if err != nil {
return nil, "", errors.Wrapf(err, "failed to convert bool value \"%v\"", v)
}
kubeadmFeatureArgs[k] = boolValue
}
componentFeatureArgs = strings.TrimRight(componentFeatureArgs, ",")
return kubeadmFeatureArgs, componentFeatureArgs, nil
}

View File

@ -0,0 +1,94 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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.
*/
// bsutil package will eventually be renamed to kubeadm package after getting rid of older one
package bsutil
import (
"reflect"
"testing"
)
func TestParseFeatureArgs(t *testing.T) {
tests := []struct {
description string
featureGates string
expectedKubeadmFeatureArgs map[string]bool
expectedComponentFeatureArgs string
}{
{
description: "CoreDNS enabled",
featureGates: "CoreDNS=true",
expectedKubeadmFeatureArgs: map[string]bool{
"CoreDNS": true,
},
expectedComponentFeatureArgs: "",
},
{
description: "CoreDNS disabled",
featureGates: "CoreDNS=false",
expectedKubeadmFeatureArgs: map[string]bool{
"CoreDNS": false,
},
expectedComponentFeatureArgs: "",
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
kubeadm, component, err := parseFeatureArgs(test.featureGates)
if err != nil {
t.Fatalf("Error parsing feature args: %v", err)
}
if !reflect.DeepEqual(kubeadm, test.expectedKubeadmFeatureArgs) {
t.Errorf("Kubeadm Actual: %v, Expected: %v", kubeadm, test.expectedKubeadmFeatureArgs)
}
if !reflect.DeepEqual(component, test.expectedComponentFeatureArgs) {
t.Errorf("Component Actual: %v, Expected: %v", component, test.expectedComponentFeatureArgs)
}
})
}
}
func TestSupport(t *testing.T) {
tests := []struct {
name string
expected bool
}{
{
name: "CoreDNS",
expected: true,
},
{
name: "Life is Beautiful !",
expected: false,
},
{
name: "",
expected: false,
},
}
for _, tc := range tests {
if supportedFG(tc.name) != tc.expected {
t.Errorf("expected supportedFG(%s) to be %t ! ", tc.name, tc.expected)
}
}
}

View File

@ -0,0 +1,54 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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.
*/
// bsutil package will eventually be renamed to kubeadm package after getting rid of older one
package bsutil
import (
"path"
"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/vmpath"
)
// KubeadmYamlPath is the path to the kubeadm configuration
var KubeadmYamlPath = path.Join(vmpath.GuestEphemeralDir, "kubeadm.yaml")
const (
DefaultCNIConfigPath = "/etc/cni/net.d/k8s.conf"
KubeletServiceFile = "/lib/systemd/system/kubelet.service"
// enum to differentiate kubeadm command line parameters from kubeadm config file parameters (see the
// KubeadmExtraArgsWhitelist variable for more info)
KubeletSystemdConfFile = "/etc/systemd/system/kubelet.service.d/10-kubeadm.conf"
)
// ConfigFileAssets returns configuration file assets
func ConfigFileAssets(cfg config.KubernetesConfig, kubeadm []byte, kubelet []byte, kubeletSvc []byte, defaultCNIConfig []byte) []assets.CopyableFile {
fs := []assets.CopyableFile{
assets.NewMemoryAssetTarget(kubeadm, KubeadmYamlPath, "0640"),
assets.NewMemoryAssetTarget(kubelet, KubeletSystemdConfFile, "0644"),
assets.NewMemoryAssetTarget(kubeletSvc, KubeletServiceFile, "0644"),
assets.NewMemoryAssetTarget(defaultCNIConfig, DefaultCNIConfigPath, "0644"),
}
// Copy the default CNI config (k8s.conf), so that kubelet can successfully
// start a Pod in the case a user hasn't manually installed any CNI plugin
// and minikube was started with "--extra-config=kubelet.network-plugin=cni".
if defaultCNIConfig != nil {
fs = append(fs, assets.NewMemoryAssetTarget(defaultCNIConfig, DefaultCNIConfigPath, "0644"))
}
return fs
}

View File

@ -0,0 +1,133 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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.
*/
// bsutil package will eventually be renamed to kubeadm package after getting rid of older one
package bsutil
import (
"bytes"
"fmt"
"path"
"github.com/blang/semver"
"github.com/pkg/errors"
"k8s.io/minikube/pkg/minikube/bootstrapper/bsutil/template"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/cruntime"
"k8s.io/minikube/pkg/minikube/vmpath"
"k8s.io/minikube/pkg/util"
)
// Container runtimes
const remoteContainerRuntime = "remote"
// GenerateKubeadmYAML generates the kubeadm.yaml file
func GenerateKubeadmYAML(k8s config.KubernetesConfig, r cruntime.Manager) ([]byte, error) {
version, err := ParseKubernetesVersion(k8s.KubernetesVersion)
if err != nil {
return nil, errors.Wrap(err, "parsing kubernetes version")
}
// parses a map of the feature gates for kubeadm and component
kubeadmFeatureArgs, componentFeatureArgs, err := parseFeatureArgs(k8s.FeatureGates)
if err != nil {
return nil, errors.Wrap(err, "parses feature gate config for kubeadm and component")
}
extraComponentConfig, err := createExtraComponentConfig(k8s.ExtraOptions, version, componentFeatureArgs)
if err != nil {
return nil, errors.Wrap(err, "generating extra component config for kubeadm")
}
// In case of no port assigned, use util.APIServerPort
nodePort := k8s.NodePort
if nodePort <= 0 {
nodePort = constants.APIServerPort
}
opts := struct {
CertDir string
ServiceCIDR string
PodSubnet string
AdvertiseAddress string
APIServerPort int
KubernetesVersion string
EtcdDataDir string
NodeName string
DNSDomain string
CRISocket string
ImageRepository string
ExtraArgs []componentExtraArgs
FeatureArgs map[string]bool
NoTaintMaster bool
}{
CertDir: vmpath.GuestCertsDir,
ServiceCIDR: util.DefaultServiceCIDR,
PodSubnet: k8s.ExtraOptions.Get("pod-network-cidr", Kubeadm),
AdvertiseAddress: k8s.NodeIP,
APIServerPort: nodePort,
KubernetesVersion: k8s.KubernetesVersion,
EtcdDataDir: EtcdDataDir(),
NodeName: k8s.NodeName,
CRISocket: r.SocketPath(),
ImageRepository: k8s.ImageRepository,
ExtraArgs: extraComponentConfig,
FeatureArgs: kubeadmFeatureArgs,
NoTaintMaster: false, // That does not work with k8s 1.12+
DNSDomain: k8s.DNSDomain,
}
if k8s.ServiceCIDR != "" {
opts.ServiceCIDR = k8s.ServiceCIDR
}
opts.NoTaintMaster = true
b := bytes.Buffer{}
configTmpl := template.KubeAdmConfigTmplV1Alpha1
if version.GTE(semver.MustParse("1.12.0")) {
configTmpl = template.KubeAdmConfigTmplV1Alpha3
}
// v1beta1 works in v1.13, but isn't required until v1.14.
if version.GTE(semver.MustParse("1.14.0-alpha.0")) {
configTmpl = template.KubeAdmConfigTmplV1Beta1
}
if err := configTmpl.Execute(&b, opts); err != nil {
return nil, err
}
return b.Bytes(), nil
}
// These are the components that can be configured
// through the "extra-config"
const (
Kubelet = "kubelet"
Kubeadm = "kubeadm"
Apiserver = "apiserver"
Scheduler = "scheduler"
ControllerManager = "controller-manager"
)
// InvokeKubeadm returns the invocation command for Kubeadm
func InvokeKubeadm(version string) string {
return fmt.Sprintf("sudo env PATH=%s:$PATH kubeadm", binRoot(version))
}
// EtcdDataDir is where etcd data is stored.
func EtcdDataDir() string {
return path.Join(vmpath.GuestPersistentDir, "etcd")
}

View File

@ -0,0 +1,224 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 bsutil
import (
"fmt"
"io/ioutil"
"strings"
"testing"
"github.com/pmezard/go-difflib/difflib"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/cruntime"
)
func getExtraOpts() []config.ExtraOption {
return config.ExtraOptionSlice{
config.ExtraOption{
Component: Apiserver,
Key: "fail-no-swap",
Value: "true",
},
config.ExtraOption{
Component: ControllerManager,
Key: "kube-api-burst",
Value: "32",
},
config.ExtraOption{
Component: Scheduler,
Key: "scheduler-name",
Value: "mini-scheduler",
},
config.ExtraOption{
Component: Kubeadm,
Key: "ignore-preflight-errors",
Value: "true",
},
config.ExtraOption{
Component: Kubeadm,
Key: "dry-run",
Value: "true",
},
}
}
func getExtraOptsPodCidr() []config.ExtraOption {
return config.ExtraOptionSlice{
config.ExtraOption{
Component: Kubeadm,
Key: "pod-network-cidr",
Value: "192.168.32.0/20",
},
}
}
func recentReleases() ([]string, error) {
// test the 6 most recent releases
versions := []string{"v1.17", "v1.16", "v1.15", "v1.14", "v1.13", "v1.12", "v1.11"}
foundNewest := false
foundDefault := false
for _, v := range versions {
if strings.HasPrefix(constants.NewestKubernetesVersion, v) {
foundNewest = true
}
if strings.HasPrefix(constants.DefaultKubernetesVersion, v) {
foundDefault = true
}
}
if !foundNewest {
return nil, fmt.Errorf("No tests exist yet for newest minor version: %s", constants.NewestKubernetesVersion)
}
if !foundDefault {
return nil, fmt.Errorf("No tests exist yet for default minor version: %s", constants.DefaultKubernetesVersion)
}
return versions, nil
}
/**
Need a separate test function to test the DNS server IP
as v1.11 yaml file is very different compared to v1.12+.
This test case has only 1 thing to test and that is the
nnetworking/dnsDomain value
*/
func TestGenerateKubeadmYAMLDNS(t *testing.T) {
versions := []string{"v1.16", "v1.15", "v1.14", "v1.13", "v1.12"}
tests := []struct {
name string
runtime string
shouldErr bool
cfg config.KubernetesConfig
}{
{"dns", "docker", false, config.KubernetesConfig{DNSDomain: "1.1.1.1"}},
}
for _, version := range versions {
for _, tc := range tests {
runtime, err := cruntime.New(cruntime.Config{Type: tc.runtime})
if err != nil {
t.Fatalf("runtime: %v", err)
}
tname := tc.name + "_" + version
t.Run(tname, func(t *testing.T) {
cfg := tc.cfg
cfg.NodeIP = "1.1.1.1"
cfg.NodeName = "mk"
cfg.KubernetesVersion = version + ".0"
got, err := GenerateKubeadmYAML(cfg, runtime)
if err != nil && !tc.shouldErr {
t.Fatalf("got unexpected error generating config: %v", err)
}
if err == nil && tc.shouldErr {
t.Fatalf("expected error but got none, config: %s", got)
}
if tc.shouldErr {
return
}
expected, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s/%s.yaml", version, tc.name))
if err != nil {
t.Fatalf("unable to read testdata: %v", err)
}
diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
A: difflib.SplitLines(string(expected)),
B: difflib.SplitLines(string(got)),
FromFile: "Expected",
ToFile: "Got",
Context: 1,
})
if err != nil {
t.Fatalf("diff error: %v", err)
}
if diff != "" {
t.Errorf("unexpected diff:\n%s\n===== [RAW OUTPUT] =====\n%s", diff, got)
}
})
}
}
}
func TestGenerateKubeadmYAML(t *testing.T) {
extraOpts := getExtraOpts()
extraOptsPodCidr := getExtraOptsPodCidr()
versions, err := recentReleases()
if err != nil {
t.Errorf("versions: %v", err)
}
tests := []struct {
name string
runtime string
shouldErr bool
cfg config.KubernetesConfig
}{
{"default", "docker", false, config.KubernetesConfig{}},
{"containerd", "containerd", false, config.KubernetesConfig{}},
{"crio", "crio", false, config.KubernetesConfig{}},
{"options", "docker", false, config.KubernetesConfig{ExtraOptions: extraOpts}},
{"crio-options-gates", "crio", false, config.KubernetesConfig{ExtraOptions: extraOpts, FeatureGates: "a=b"}},
{"unknown-component", "docker", true, config.KubernetesConfig{ExtraOptions: config.ExtraOptionSlice{config.ExtraOption{Component: "not-a-real-component", Key: "killswitch", Value: "true"}}}},
{"containerd-api-port", "containerd", false, config.KubernetesConfig{NodePort: 12345}},
{"containerd-pod-network-cidr", "containerd", false, config.KubernetesConfig{ExtraOptions: extraOptsPodCidr}},
{"image-repository", "docker", false, config.KubernetesConfig{ImageRepository: "test/repo"}},
}
for _, version := range versions {
for _, tc := range tests {
runtime, err := cruntime.New(cruntime.Config{Type: tc.runtime})
if err != nil {
t.Fatalf("runtime: %v", err)
}
tname := tc.name + "_" + version
t.Run(tname, func(t *testing.T) {
cfg := tc.cfg
cfg.NodeIP = "1.1.1.1"
cfg.NodeName = "mk"
cfg.KubernetesVersion = version + ".0"
got, err := GenerateKubeadmYAML(cfg, runtime)
if err != nil && !tc.shouldErr {
t.Fatalf("got unexpected error generating config: %v", err)
}
if err == nil && tc.shouldErr {
t.Fatalf("expected error but got none, config: %s", got)
}
if tc.shouldErr {
return
}
expected, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s/%s.yaml", version, tc.name))
if err != nil {
t.Fatalf("unable to read testdata: %v", err)
}
diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
A: difflib.SplitLines(string(expected)),
B: difflib.SplitLines(string(got)),
FromFile: "Expected",
ToFile: "Got",
Context: 1,
})
if err != nil {
t.Fatalf("diff error: %v", err)
}
if diff != "" {
t.Errorf("unexpected diff:\n%s\n===== [RAW OUTPUT] =====\n%s", diff, got)
}
})
}
}
}

View File

@ -0,0 +1,94 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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.
*/
// bsutil package will eventually be renamed to kubeadm package after getting rid of older one
package bsutil
import (
"bytes"
"path"
"github.com/pkg/errors"
"k8s.io/minikube/pkg/minikube/bootstrapper/bsutil/template"
"k8s.io/minikube/pkg/minikube/bootstrapper/images"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/cruntime"
)
// NewKubeletConfig generates a new systemd unit containing a configured kubelet
// based on the options present in the KubernetesConfig.
func NewKubeletConfig(k8s config.KubernetesConfig, r cruntime.Manager) ([]byte, error) {
version, err := ParseKubernetesVersion(k8s.KubernetesVersion)
if err != nil {
return nil, errors.Wrap(err, "parsing kubernetes version")
}
extraOpts, err := extraConfigForComponent(Kubelet, k8s.ExtraOptions, version)
if err != nil {
return nil, errors.Wrap(err, "generating extra configuration for kubelet")
}
for k, v := range r.KubeletOptions() {
extraOpts[k] = v
}
if k8s.NetworkPlugin != "" {
extraOpts["network-plugin"] = k8s.NetworkPlugin
}
if _, ok := extraOpts["node-ip"]; !ok {
extraOpts["node-ip"] = k8s.NodeIP
}
pauseImage := images.Pause(k8s.ImageRepository)
if _, ok := extraOpts["pod-infra-container-image"]; !ok && k8s.ImageRepository != "" && pauseImage != "" && k8s.ContainerRuntime != remoteContainerRuntime {
extraOpts["pod-infra-container-image"] = pauseImage
}
// parses a map of the feature gates for kubelet
_, kubeletFeatureArgs, err := parseFeatureArgs(k8s.FeatureGates)
if err != nil {
return nil, errors.Wrap(err, "parses feature gate config for kubelet")
}
if kubeletFeatureArgs != "" {
extraOpts["feature-gates"] = kubeletFeatureArgs
}
b := bytes.Buffer{}
opts := struct {
ExtraOptions string
ContainerRuntime string
KubeletPath string
}{
ExtraOptions: convertToFlags(extraOpts),
ContainerRuntime: k8s.ContainerRuntime,
KubeletPath: path.Join(binRoot(k8s.KubernetesVersion), "kubelet"),
}
if err := template.KubeletSystemdTemplate.Execute(&b, opts); err != nil {
return nil, err
}
return b.Bytes(), nil
}
// NewKubeletService returns a generated systemd unit file for the kubelet
func NewKubeletService(cfg config.KubernetesConfig) ([]byte, error) {
var b bytes.Buffer
opts := struct{ KubeletPath string }{KubeletPath: path.Join(binRoot(cfg.KubernetesVersion), "kubelet")}
if err := template.KubeletServiceTemplate.Execute(&b, opts); err != nil {
return nil, errors.Wrap(err, "template execute")
}
return b.Bytes(), nil
}

View File

@ -14,12 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package kubeadm
// bsutil package will eventually be renamed to kubeadm package after getting rid of older one
package bsutil
import (
"fmt"
"io/ioutil"
"strings"
"testing"
"github.com/pmezard/go-difflib/difflib"
@ -168,198 +166,3 @@ ExecStart=/var/lib/minikube/binaries/v1.17.0/kubelet --authorization-mode=Webhoo
})
}
}
func getExtraOpts() []config.ExtraOption {
return config.ExtraOptionSlice{
config.ExtraOption{
Component: Apiserver,
Key: "fail-no-swap",
Value: "true",
},
config.ExtraOption{
Component: ControllerManager,
Key: "kube-api-burst",
Value: "32",
},
config.ExtraOption{
Component: Scheduler,
Key: "scheduler-name",
Value: "mini-scheduler",
},
config.ExtraOption{
Component: Kubeadm,
Key: "ignore-preflight-errors",
Value: "true",
},
config.ExtraOption{
Component: Kubeadm,
Key: "dry-run",
Value: "true",
},
}
}
func getExtraOptsPodCidr() []config.ExtraOption {
return config.ExtraOptionSlice{
config.ExtraOption{
Component: Kubeadm,
Key: "pod-network-cidr",
Value: "192.168.32.0/20",
},
}
}
func recentReleases() ([]string, error) {
// test the 6 most recent releases
versions := []string{"v1.17", "v1.16", "v1.15", "v1.14", "v1.13", "v1.12", "v1.11"}
foundNewest := false
foundDefault := false
for _, v := range versions {
if strings.HasPrefix(constants.NewestKubernetesVersion, v) {
foundNewest = true
}
if strings.HasPrefix(constants.DefaultKubernetesVersion, v) {
foundDefault = true
}
}
if !foundNewest {
return nil, fmt.Errorf("No tests exist yet for newest minor version: %s", constants.NewestKubernetesVersion)
}
if !foundDefault {
return nil, fmt.Errorf("No tests exist yet for default minor version: %s", constants.DefaultKubernetesVersion)
}
return versions, nil
}
/**
Need a separate test function to test the DNS server IP
as v1.11 yaml file is very different compared to v1.12+.
This test case has only 1 thing to test and that is the
nnetworking/dnsDomain value
*/
func TestGenerateConfigDNS(t *testing.T) {
versions := []string{"v1.16", "v1.15", "v1.14", "v1.13", "v1.12"}
tests := []struct {
name string
runtime string
shouldErr bool
cfg config.KubernetesConfig
}{
{"dns", "docker", false, config.KubernetesConfig{DNSDomain: "1.1.1.1"}},
}
for _, version := range versions {
for _, tc := range tests {
runtime, err := cruntime.New(cruntime.Config{Type: tc.runtime})
if err != nil {
t.Fatalf("runtime: %v", err)
}
tname := tc.name + "_" + version
t.Run(tname, func(t *testing.T) {
cfg := tc.cfg
cfg.NodeIP = "1.1.1.1"
cfg.NodeName = "mk"
cfg.KubernetesVersion = version + ".0"
got, err := generateConfig(cfg, runtime)
if err != nil && !tc.shouldErr {
t.Fatalf("got unexpected error generating config: %v", err)
}
if err == nil && tc.shouldErr {
t.Fatalf("expected error but got none, config: %s", got)
}
if tc.shouldErr {
return
}
expected, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s/%s.yaml", version, tc.name))
if err != nil {
t.Fatalf("unable to read testdata: %v", err)
}
diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
A: difflib.SplitLines(string(expected)),
B: difflib.SplitLines(string(got)),
FromFile: "Expected",
ToFile: "Got",
Context: 1,
})
if err != nil {
t.Fatalf("diff error: %v", err)
}
if diff != "" {
t.Errorf("unexpected diff:\n%s\n===== [RAW OUTPUT] =====\n%s", diff, got)
}
})
}
}
}
func TestGenerateConfig(t *testing.T) {
extraOpts := getExtraOpts()
extraOptsPodCidr := getExtraOptsPodCidr()
versions, err := recentReleases()
if err != nil {
t.Errorf("versions: %v", err)
}
tests := []struct {
name string
runtime string
shouldErr bool
cfg config.KubernetesConfig
}{
{"default", "docker", false, config.KubernetesConfig{}},
{"containerd", "containerd", false, config.KubernetesConfig{}},
{"crio", "crio", false, config.KubernetesConfig{}},
{"options", "docker", false, config.KubernetesConfig{ExtraOptions: extraOpts}},
{"crio-options-gates", "crio", false, config.KubernetesConfig{ExtraOptions: extraOpts, FeatureGates: "a=b"}},
{"unknown-component", "docker", true, config.KubernetesConfig{ExtraOptions: config.ExtraOptionSlice{config.ExtraOption{Component: "not-a-real-component", Key: "killswitch", Value: "true"}}}},
{"containerd-api-port", "containerd", false, config.KubernetesConfig{NodePort: 12345}},
{"containerd-pod-network-cidr", "containerd", false, config.KubernetesConfig{ExtraOptions: extraOptsPodCidr}},
{"image-repository", "docker", false, config.KubernetesConfig{ImageRepository: "test/repo"}},
}
for _, version := range versions {
for _, tc := range tests {
runtime, err := cruntime.New(cruntime.Config{Type: tc.runtime})
if err != nil {
t.Fatalf("runtime: %v", err)
}
tname := tc.name + "_" + version
t.Run(tname, func(t *testing.T) {
cfg := tc.cfg
cfg.NodeIP = "1.1.1.1"
cfg.NodeName = "mk"
cfg.KubernetesVersion = version + ".0"
got, err := generateConfig(cfg, runtime)
if err != nil && !tc.shouldErr {
t.Fatalf("got unexpected error generating config: %v", err)
}
if err == nil && tc.shouldErr {
t.Fatalf("expected error but got none, config: %s", got)
}
if tc.shouldErr {
return
}
expected, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s/%s.yaml", version, tc.name))
if err != nil {
t.Fatalf("unable to read testdata: %v", err)
}
diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
A: difflib.SplitLines(string(expected)),
B: difflib.SplitLines(string(got)),
FromFile: "Expected",
ToFile: "Got",
Context: 1,
})
if err != nil {
t.Fatalf("diff error: %v", err)
}
if diff != "" {
t.Errorf("unexpected diff:\n%s\n===== [RAW OUTPUT] =====\n%s", diff, got)
}
})
}
}
}

View File

@ -0,0 +1,46 @@
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 template
import "text/template"
// KubeletSystemdTemplate hosts the override kubelet flags, written to kubeletSystemdConfFile
var KubeletSystemdTemplate = template.Must(template.New("kubeletSystemdTemplate").Parse(`[Unit]
{{if or (eq .ContainerRuntime "cri-o") (eq .ContainerRuntime "cri")}}Wants=crio.service{{else if eq .ContainerRuntime "containerd"}}Wants=containerd.service{{else}}Wants=docker.socket{{end}}
[Service]
ExecStart=
ExecStart={{.KubeletPath}}{{if .ExtraOptions}} {{.ExtraOptions}}{{end}}
[Install]
`))
// KubeletServiceTemplate is the base kubelet systemd template, written to kubeletServiceFile
var KubeletServiceTemplate = template.Must(template.New("kubeletServiceTemplate").Parse(`[Unit]
Description=kubelet: The Kubernetes Node Agent
Documentation=http://kubernetes.io/docs/
[Service]
ExecStart={{.KubeletPath}}
Restart=always
StartLimitInterval=0
# Tuned for local dev: faster than upstream default (10s), but slower than systemd default (100ms)
RestartSec=600ms
[Install]
WantedBy=multi-user.target
`))

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package kubeadm
package template
import (
"fmt"
@ -22,8 +22,8 @@ import (
"text/template"
)
// configTmplV1Alpha1 is for Kubernetes v1.11
var configTmplV1Alpha1 = template.Must(template.New("configTmpl-v1alpha1").Funcs(template.FuncMap{
// KubeAdmConfigTmplV1Alpha1 is for Kubernetes v1.11
var KubeAdmConfigTmplV1Alpha1 = template.Must(template.New("configTmpl-v1alpha1").Funcs(template.FuncMap{
"printMapInOrder": printMapInOrder,
}).Parse(`apiVersion: kubeadm.k8s.io/v1alpha1
kind: MasterConfiguration
@ -47,8 +47,8 @@ nodeName: {{.NodeName}}
{{$i}}: {{$val}}{{end}}
{{end}}`))
// configTmplV1Alpha3 is for Kubernetes v1.12
var configTmplV1Alpha3 = template.Must(template.New("configTmpl-v1alpha3").Funcs(template.FuncMap{
// KubeAdmConfigTmplV1Alpha3 is for Kubernetes v1.12
var KubeAdmConfigTmplV1Alpha3 = template.Must(template.New("configTmpl-v1alpha3").Funcs(template.FuncMap{
"printMapInOrder": printMapInOrder,
}).Parse(`apiVersion: kubeadm.k8s.io/v1alpha3
kind: InitConfiguration
@ -96,8 +96,8 @@ evictionHard:
imagefs.available: "0%"
`))
// configTmplV1Beta1 is for Kubernetes v1.13+
var configTmplV1Beta1 = template.Must(template.New("configTmpl-v1beta1").Funcs(template.FuncMap{
// KubeAdmConfigTmplV1Beta1 is for Kubernetes v1.13+
var KubeAdmConfigTmplV1Beta1 = template.Must(template.New("configTmpl-v1beta1").Funcs(template.FuncMap{
"printMapInOrder": printMapInOrder,
}).Parse(`apiVersion: kubeadm.k8s.io/v1beta1
kind: InitConfiguration
@ -151,33 +151,6 @@ evictionHard:
imagefs.available: "0%"
`))
// kubeletSystemdTemplate hosts the override kubelet flags, written to kubeletSystemdConfFile
var kubeletSystemdTemplate = template.Must(template.New("kubeletSystemdTemplate").Parse(`[Unit]
{{if or (eq .ContainerRuntime "cri-o") (eq .ContainerRuntime "cri")}}Wants=crio.service{{else if eq .ContainerRuntime "containerd"}}Wants=containerd.service{{else}}Wants=docker.socket{{end}}
[Service]
ExecStart=
ExecStart={{.KubeletPath}}{{if .ExtraOptions}} {{.ExtraOptions}}{{end}}
[Install]
`))
// kubeletServiceTemplate is the base kubelet systemd template, written to kubeletServiceFile
var kubeletServiceTemplate = template.Must(template.New("kubeletServiceTemplate").Parse(`[Unit]
Description=kubelet: The Kubernetes Node Agent
Documentation=http://kubernetes.io/docs/
[Service]
ExecStart={{.KubeletPath}}
Restart=always
StartLimitInterval=0
# Tuned for local dev: faster than upstream default (10s), but slower than systemd default (100ms)
RestartSec=600ms
[Install]
WantedBy=multi-user.target
`))
// printMapInOrder sorts the keys and prints the map in order, combining key
// value pairs with the separator character
//

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package kubeadm
package template
import (
"reflect"

Some files were not shown because too many files have changed in this diff Show More