diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index efbbc278fac0..f575b88c57ad 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -77,6 +77,7 @@ const ( apiServerPort = "apiserver-port" dnsDomain = "dns-domain" serviceCIDR = "service-cluster-ip-range" + podSubnet = "pod-network-cidr" mountString = "mount-string" disableDriverMounts = "disable-driver-mounts" cacheImages = "cache-images" @@ -122,6 +123,7 @@ func init() { startCmd.Flags().IPSliceVar(&apiServerIPs, "apiserver-ips", nil, "A set of apiserver IP Addresses which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine") startCmd.Flags().String(dnsDomain, constants.ClusterDNSDomain, "The cluster dns domain name used in the kubernetes cluster") startCmd.Flags().String(serviceCIDR, pkgutil.DefaultServiceCIDR, "The CIDR to be used for service cluster IPs.") + startCmd.Flags().String(podSubnet, "", "Specify range of IP addresses for the pod network. If set, the control plane will automatically allocate CIDRs for every node.") startCmd.Flags().StringSliceVar(&insecureRegistry, "insecure-registry", nil, "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.") startCmd.Flags().StringSliceVar(®istryMirror, "registry-mirror", nil, "Registry mirrors to pass to the Docker daemon") startCmd.Flags().String(containerRuntime, "docker", "The container runtime to be used (docker, crio, containerd)") @@ -299,6 +301,7 @@ func generateConfig(cmd *cobra.Command, kVersion string) (cfg.Config, error) { CRISocket: viper.GetString(criSocket), NetworkPlugin: selectedNetworkPlugin, ServiceCIDR: viper.GetString(serviceCIDR), + PodSubnet: viper.GetString(podSubnet), ExtraOptions: extraOptions, ShouldLoadCachedImages: viper.GetBool(cacheImages), EnableDefaultCNI: selectedEnableDefaultCNI, diff --git a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go index fb686b3afd90..0d1fc43c5ebb 100644 --- a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go +++ b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go @@ -477,6 +477,7 @@ func generateConfig(k8s config.KubernetesConfig, r cruntime.Manager) (string, er opts := struct { CertDir string ServiceCIDR string + PodSubnet string AdvertiseAddress string APIServerPort int KubernetesVersion string @@ -489,6 +490,7 @@ func generateConfig(k8s config.KubernetesConfig, r cruntime.Manager) (string, er }{ CertDir: util.DefaultCertPath, ServiceCIDR: util.DefaultServiceCIDR, + PodSubnet: k8s.PodSubnet, AdvertiseAddress: k8s.NodeIP, APIServerPort: nodePort, KubernetesVersion: k8s.KubernetesVersion, diff --git a/pkg/minikube/bootstrapper/kubeadm/kubeadm_test.go b/pkg/minikube/bootstrapper/kubeadm/kubeadm_test.go index 01ad19cd03dc..a2c1ddb162f6 100644 --- a/pkg/minikube/bootstrapper/kubeadm/kubeadm_test.go +++ b/pkg/minikube/bootstrapper/kubeadm/kubeadm_test.go @@ -255,6 +255,110 @@ apiServerExtraArgs: admission-control: "Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota" `, }, + { + description: "no PodSubnet", + cfg: config.KubernetesConfig{ + NodeIP: "192.168.1.100", + KubernetesVersion: "v1.12.0", + NodeName: "minikube", + PodSubnet: " ", + }, + expectedCfg: `apiVersion: kubeadm.k8s.io/v1alpha3 +kind: InitConfiguration +apiEndpoint: + advertiseAddress: 192.168.1.100 + bindPort: 8443 +bootstrapTokens: +- groups: + - system:bootstrappers:kubeadm:default-node-token + ttl: 24h0m0s + usages: + - signing + - authentication +nodeRegistration: + criSocket: /var/run/dockershim.sock + name: minikube + taints: [] +--- +apiVersion: kubeadm.k8s.io/v1alpha3 +kind: ClusterConfiguration +apiServerExtraArgs: + enable-admission-plugins: "Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota" + +certificatesDir: /var/lib/minikube/certs/ +clusterName: kubernetes +controlPlaneEndpoint: localhost:8443 +etcd: + local: + dataDir: /data/minikube +kubernetesVersion: v1.12.0 +networking: + dnsDomain: cluster.local + podSubnet: "" + serviceSubnet: 10.96.0.0/12 +--- +apiVersion: kubelet.config.k8s.io/v1beta1 +kind: KubeletConfiguration +# disable disk resource management by default, as it doesn't work well within the minikube environment. +imageGCHighThresholdPercent: 100 +# Don't evict jobs, as we only have a single node to run on. +evictionHard: + nodefs.available: "0%" + nodefs.inodesFree: "0%" + imagefs.available: "0%"`, + }, + { + description: "with PodSubnet", + cfg: config.KubernetesConfig{ + NodeIP: "192.168.1.100", + KubernetesVersion: "v1.12.0", + NodeName: "minikube", + PodSubnet: "192.168.32.0/20", + }, + expectedCfg: `apiVersion: kubeadm.k8s.io/v1alpha3 +kind: InitConfiguration +apiEndpoint: + advertiseAddress: 192.168.1.100 + bindPort: 8443 +bootstrapTokens: +- groups: + - system:bootstrappers:kubeadm:default-node-token + ttl: 24h0m0s + usages: + - signing + - authentication +nodeRegistration: + criSocket: /var/run/dockershim.sock + name: minikube + taints: [] +--- +apiVersion: kubeadm.k8s.io/v1alpha3 +kind: ClusterConfiguration +apiServerExtraArgs: + enable-admission-plugins: "Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota" + +certificatesDir: /var/lib/minikube/certs/ +clusterName: kubernetes +controlPlaneEndpoint: localhost:8443 +etcd: + local: + dataDir: /data/minikube +kubernetesVersion: v1.12.0 +networking: + dnsDomain: cluster.local + podSubnet: 192.168.32.0/20 + serviceSubnet: 10.96.0.0/12 +--- +apiVersion: kubelet.config.k8s.io/v1beta1 +kind: KubeletConfiguration +# disable disk resource management by default, as it doesn't work well within the minikube environment. +imageGCHighThresholdPercent: 100 +# Don't evict jobs, as we only have a single node to run on. +evictionHard: + nodefs.available: "0%" + nodefs.inodesFree: "0%" + imagefs.available: "0%"`, + }, } for _, test := range tests { diff --git a/pkg/minikube/bootstrapper/kubeadm/templates.go b/pkg/minikube/bootstrapper/kubeadm/templates.go index 8b35b2b827b5..5bb50b9fe558 100644 --- a/pkg/minikube/bootstrapper/kubeadm/templates.go +++ b/pkg/minikube/bootstrapper/kubeadm/templates.go @@ -47,8 +47,7 @@ nodeName: {{.NodeName}} var kubeadmConfigTemplateV1Alpha3 = template.Must(template.New("kubeadmConfigTemplate-v1alpha3").Funcs(template.FuncMap{ "printMapInOrder": printMapInOrder, -}).Parse(` -apiVersion: kubeadm.k8s.io/v1alpha3 +}).Parse(`apiVersion: kubeadm.k8s.io/v1alpha3 kind: InitConfiguration apiEndpoint: advertiseAddress: {{.AdvertiseAddress}} @@ -81,7 +80,7 @@ etcd: kubernetesVersion: {{.KubernetesVersion}} networking: dnsDomain: cluster.local - podSubnet: "" + podSubnet: {{if .PodSubnet}}{{.PodSubnet}}{{else}}""{{end}} serviceSubnet: {{.ServiceCIDR}} --- apiVersion: kubelet.config.k8s.io/v1beta1 @@ -92,8 +91,7 @@ imageGCHighThresholdPercent: 100 evictionHard: nodefs.available: "0%" nodefs.inodesFree: "0%" - imagefs.available: "0%" - `)) + imagefs.available: "0%"`)) var kubeletSystemdTemplate = template.Must(template.New("kubeletSystemdTemplate").Parse(` [Unit] diff --git a/pkg/minikube/config/types.go b/pkg/minikube/config/types.go index 2be009f7bf89..c9f56f143253 100644 --- a/pkg/minikube/config/types.go +++ b/pkg/minikube/config/types.go @@ -70,6 +70,7 @@ type KubernetesConfig struct { NetworkPlugin string FeatureGates string ServiceCIDR string + PodSubnet string ExtraOptions util.ExtraOptionSlice ShouldLoadCachedImages bool diff --git a/test/integration/start_stop_delete_test.go b/test/integration/start_stop_delete_test.go index 851c455a4ef0..aa5643b6ae26 100644 --- a/test/integration/start_stop_delete_test.go +++ b/test/integration/start_stop_delete_test.go @@ -19,23 +19,25 @@ limitations under the License. package integration import ( + "encoding/json" + "github.com/docker/machine/libmachine/state" + "k8s.io/minikube/test/integration/util" "net" "strings" "testing" "time" - - "github.com/docker/machine/libmachine/state" - "k8s.io/minikube/test/integration/util" ) func TestStartStop(t *testing.T) { tests := []struct { - name string - args []string + name string + args []string + assertCustom func(t *testing.T) }{ - {"docker+cache", []string{"--container-runtime=docker", "--cache-images"}}, - {"containerd+cache", []string{"--container-runtime=containerd", "--docker-opt containerd=/var/run/containerd/containerd.sock", "--cache-images"}}, - {"crio+cache", []string{"--container-runtime=crio", "--cache-images"}}, + {"docker+cache", []string{"--container-runtime=docker", "--cache-images"}, nil}, + {"containerd+cache", []string{"--container-runtime=containerd", "--docker-opt containerd=/var/run/containerd/containerd.sock", "--cache-images"}, nil}, + {"crio+cache", []string{"--container-runtime=crio", "--cache-images"}, nil}, + {"podCidr", []string{"--pod-network-cidr=192.168.111.111/16"}, assertPodCIDR}, } for _, test := range tests { @@ -51,6 +53,10 @@ func TestStartStop(t *testing.T) { r.Start(test.args...) r.CheckStatus(state.Running.String()) + if test.assertCustom != nil { + test.assertCustom(t) + } + ip := r.RunCommand("ip", true) ip = strings.TrimRight(ip, "\n") if net.ParseIP(ip) == nil { @@ -74,3 +80,24 @@ func TestStartStop(t *testing.T) { }) } } + +func assertPodCIDR(t *testing.T) { + kr := util.NewKubectlRunner(t) + out, err := kr.RunCommand([]string{"get", "nodes", "-o", "json"}) + if err != nil { + t.Fatalf("Failed to obtain nodes info") + } + + var result map[string]interface{} + json.Unmarshal([]byte(out), &result) + + items := result["items"].([]interface{}) + for _, item := range items { + spec := item.(map[string]interface{})["spec"] + podCidr := spec.(map[string]interface{})["podCIDR"].(string) + + if !strings.HasPrefix(podCidr, "192.168.0.0") { + t.Errorf("Unexpected podCIDR: %s", podCidr) + } + } +}