diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index 298f66af3c..a9a64de1a8 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -42,6 +42,7 @@ import ( cmdcfg "k8s.io/minikube/cmd/minikube/cmd/config" cmdutil "k8s.io/minikube/cmd/util" "k8s.io/minikube/pkg/minikube/bootstrapper" + "k8s.io/minikube/pkg/minikube/bootstrapper/kubeadm" "k8s.io/minikube/pkg/minikube/cluster" cfg "k8s.io/minikube/pkg/minikube/config" "k8s.io/minikube/pkg/minikube/console" @@ -145,7 +146,8 @@ func init() { startCmd.Flags().Var(&extraOptions, "extra-config", `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 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], ","))) startCmd.Flags().String(uuid, "", "Provide VM UUID to restore MAC address (only supported with Hyperkit driver).") startCmd.Flags().String(vpnkitSock, "", "Location of the VPNKit socket used for networking. If empty, disables Hyperkit VPNKitSock, if 'auto' uses Docker for Mac VPNKit connection, otherwise uses the specified VSock.") startCmd.Flags().StringSlice(vsockPorts, []string{}, "List of guest VSock ports that should be exposed as sockets on the host (Only supported on with hyperkit now).") @@ -336,6 +338,14 @@ func validateConfig() { if viper.GetBool(hidden) && viper.GetString(vmDriver) != "kvm2" { exit.Usage("Sorry, the --hidden feature is currently only supported with --vm-driver=kvm2") } + + // check that kubeadm extra args contain only whitelisted parameters + for param := range extraOptions.AsMap().Get(kubeadm.Kubeadm) { + if !pkgutil.ContainsString(kubeadm.KubeadmExtraArgsWhitelist[kubeadm.KubeadmCmdParam], param) && + !pkgutil.ContainsString(kubeadm.KubeadmExtraArgsWhitelist[kubeadm.KubeadmConfigParam], param) { + exit.Usage("Sorry, the kubeadm.%s parameter is currently not supported by --extra-config", param) + } + } } // doCacheBinaries caches Kubernetes binaries in the foreground diff --git a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go index 1775cebba2..e559ac044c 100644 --- a/pkg/minikube/bootstrapper/kubeadm/kubeadm.go +++ b/pkg/minikube/bootstrapper/kubeadm/kubeadm.go @@ -44,6 +44,35 @@ import ( "k8s.io/minikube/pkg/util" ) +// enum to differentiate kubeadm command line parameters from kubeadm config file parameters (see the +// KubeadmExtraArgsWhitelist variable below for more info) +const ( + KubeadmCmdParam = iota + KubeadmConfigParam = iota +) + +// 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", + }, +} + // SkipPreflights are preflight checks we always skip. var SkipPreflights = []string{ // We use --ignore-preflight-errors=DirAvailable since we have our own custom addons @@ -163,6 +192,21 @@ func (k *Bootstrapper) LogCommands(o bootstrapper.LogOptions) map[string]string } } +// createFlagsFromExtraArgs converts kubeadm extra args into flags to be supplied from the commad linne +func createFlagsFromExtraArgs(extraOptions util.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 !util.ContainsString(KubeadmExtraArgsWhitelist[KubeadmCmdParam], opt) { + // kubeadmExtraOpts is a copy so safe to delete + delete(kubeadmExtraOpts, opt) + } + } + return convertToFlags(kubeadmExtraOpts) +} + // StartCluster starts the cluster func (k *Bootstrapper) StartCluster(k8s config.KubernetesConfig) error { version, err := ParseKubernetesVersion(k8s.KubernetesVersion) @@ -170,11 +214,7 @@ func (k *Bootstrapper) StartCluster(k8s config.KubernetesConfig) error { return errors.Wrap(err, "parsing kubernetes version") } - extraOpts, err := ExtraConfigForComponent(Kubeadm, k8s.ExtraOptions, version) - if err != nil { - return errors.Wrap(err, "generating extra configuration for kubelet") - } - extraFlags := convertToFlags(extraOpts) + extraFlags := createFlagsFromExtraArgs(k8s.ExtraOptions) r, err := cruntime.New(cruntime.Config{Type: k8s.ContainerRuntime}) if err != nil { @@ -475,6 +515,25 @@ sudo systemctl start kubelet return nil } +// createExtraComponentConfig generates a map of component to extra args for all of the components except kubeadm +func createExtraComponentConfig(extraOptions util.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 generateConfig(k8s config.KubernetesConfig, r cruntime.Manager) (string, error) { version, err := ParseKubernetesVersion(k8s.KubernetesVersion) if err != nil { @@ -487,8 +546,7 @@ func generateConfig(k8s config.KubernetesConfig, r cruntime.Manager) (string, er return "", errors.Wrap(err, "parses feature gate config for kubeadm and component") } - // generates a map of component to extra args for apiserver, controller-manager, and scheduler - extraComponentConfig, err := NewComponentExtraArgs(k8s.ExtraOptions, version, componentFeatureArgs) + extraComponentConfig, err := createExtraComponentConfig(k8s.ExtraOptions, version, componentFeatureArgs) if err != nil { return "", errors.Wrap(err, "generating extra component config for kubeadm") } @@ -502,6 +560,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 @@ -515,6 +574,7 @@ func generateConfig(k8s config.KubernetesConfig, r cruntime.Manager) (string, er }{ CertDir: util.DefaultCertPath, ServiceCIDR: util.DefaultServiceCIDR, + PodSubnet: k8s.ExtraOptions.Get("pod-network-cidr", Kubeadm), 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 188351f1f8..7785cb8f52 100644 --- a/pkg/minikube/bootstrapper/kubeadm/kubeadm_test.go +++ b/pkg/minikube/bootstrapper/kubeadm/kubeadm_test.go @@ -153,6 +153,24 @@ func TestGenerateConfig(t *testing.T) { Key: "scheduler-name", Value: "mini-scheduler", }, + util.ExtraOption{ + Component: Kubeadm, + Key: "ignore-preflight-errors", + Value: "true", + }, + util.ExtraOption{ + Component: Kubeadm, + Key: "dry-run", + Value: "true", + }, + } + + extraOptsPodCidr := util.ExtraOptionSlice{ + util.ExtraOption{ + Component: Kubeadm, + Key: "pod-network-cidr", + Value: "192.168.32.0/20", + }, } // Test version policy: Last 4 major releases (slightly looser than our general policy) @@ -177,6 +195,7 @@ func TestGenerateConfig(t *testing.T) { {"crio-options-gates", "crio", false, config.KubernetesConfig{ExtraOptions: extraOpts, FeatureGates: "a=b"}}, {"unknown-component", "docker", true, config.KubernetesConfig{ExtraOptions: util.ExtraOptionSlice{util.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 vname, version := range versions { diff --git a/pkg/minikube/bootstrapper/kubeadm/templates.go b/pkg/minikube/bootstrapper/kubeadm/templates.go index e5a63efc44..e1b14bc184 100644 --- a/pkg/minikube/bootstrapper/kubeadm/templates.go +++ b/pkg/minikube/bootstrapper/kubeadm/templates.go @@ -85,7 +85,7 @@ etcd: kubernetesVersion: {{.KubernetesVersion}} networking: dnsDomain: cluster.local - podSubnet: "" + podSubnet: {{if .PodSubnet}}{{.PodSubnet}}{{else}}""{{end}} serviceSubnet: {{.ServiceCIDR}} --- apiVersion: kubelet.config.k8s.io/v1beta1 diff --git a/pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__default.yaml b/pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__default.yaml new file mode 100644 index 0000000000..17fde398aa --- /dev/null +++ b/pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__default.yaml @@ -0,0 +1,43 @@ +apiVersion: kubeadm.k8s.io/v1beta1 +kind: InitConfiguration +localAPIEndpoint: + advertiseAddress: 1.1.1.1 + bindPort: 8443 +bootstrapTokens: + - groups: + - system:bootstrappers:kubeadm:default-node-token + ttl: 24h0m0s + usages: + - signing + - authentication +nodeRegistration: + criSocket: /run/containerd/containerd.sock + name: mk + taints: [] +--- +apiVersion: kubeadm.k8s.io/v1beta1 +kind: ClusterConfiguration +apiServer: + extraArgs: + enable-admission-plugins: "NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota" +certificatesDir: /var/lib/minikube/certs/ +clusterName: kubernetes +controlPlaneEndpoint: localhost:8443 +dns: + type: CoreDNS +etcd: + local: + dataDir: /data/minikube +kubernetesVersion: v1.14.1 +networking: + dnsDomain: cluster.local + podSubnet: "" + serviceSubnet: 10.96.0.0/12 +--- +apiVersion: kubelet.config.k8s.io/v1beta1 +kind: KubeletConfiguration +imageGCHighThresholdPercent: 100 +evictionHard: + nodefs.available: "0%" + nodefs.inodesFree: "0%" + imagefs.available: "0%" diff --git a/pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__new.yaml b/pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__new.yaml new file mode 100644 index 0000000000..147a6ccbec --- /dev/null +++ b/pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__new.yaml @@ -0,0 +1,43 @@ +apiVersion: kubeadm.k8s.io/v1beta1 +kind: InitConfiguration +localAPIEndpoint: + advertiseAddress: 1.1.1.1 + bindPort: 8443 +bootstrapTokens: + - groups: + - system:bootstrappers:kubeadm:default-node-token + ttl: 24h0m0s + usages: + - signing + - authentication +nodeRegistration: + criSocket: /run/containerd/containerd.sock + name: mk + taints: [] +--- +apiVersion: kubeadm.k8s.io/v1beta1 +kind: ClusterConfiguration +apiServer: + extraArgs: + enable-admission-plugins: "NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota" +certificatesDir: /var/lib/minikube/certs/ +clusterName: kubernetes +controlPlaneEndpoint: localhost:8443 +dns: + type: CoreDNS +etcd: + local: + dataDir: /data/minikube +kubernetesVersion: v1.14.0 +networking: + dnsDomain: cluster.local + podSubnet: "" + serviceSubnet: 10.96.0.0/12 +--- +apiVersion: kubelet.config.k8s.io/v1beta1 +kind: KubeletConfiguration +imageGCHighThresholdPercent: 100 +evictionHard: + nodefs.available: "0%" + nodefs.inodesFree: "0%" + imagefs.available: "0%" diff --git a/pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__obsolete.yaml b/pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__obsolete.yaml new file mode 100644 index 0000000000..b2d73f912b --- /dev/null +++ b/pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__obsolete.yaml @@ -0,0 +1,17 @@ +apiVersion: kubeadm.k8s.io/v1alpha1 +kind: MasterConfiguration +noTaintMaster: true +api: + advertiseAddress: 1.1.1.1 + bindPort: 8443 + controlPlaneEndpoint: localhost +kubernetesVersion: v1.10.0 +certificatesDir: /var/lib/minikube/certs/ +networking: + serviceSubnet: 10.96.0.0/12 +etcd: + dataDir: /data/minikube +nodeName: mk +criSocket: /run/containerd/containerd.sock +apiServerExtraArgs: + admission-control: "Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota" diff --git a/pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__old.yaml b/pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__old.yaml new file mode 100644 index 0000000000..64bf678f02 --- /dev/null +++ b/pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__old.yaml @@ -0,0 +1,39 @@ +apiVersion: kubeadm.k8s.io/v1alpha3 +kind: InitConfiguration +apiEndpoint: + advertiseAddress: 1.1.1.1 + bindPort: 8443 +bootstrapTokens: + - groups: + - system:bootstrappers:kubeadm:default-node-token + ttl: 24h0m0s + usages: + - signing + - authentication +nodeRegistration: + criSocket: /run/containerd/containerd.sock + name: mk + 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 +evictionHard: + nodefs.available: "0%" + nodefs.inodesFree: "0%" + imagefs.available: "0%" diff --git a/pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__recent.yaml b/pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__recent.yaml new file mode 100644 index 0000000000..ecc9a14631 --- /dev/null +++ b/pkg/minikube/bootstrapper/kubeadm/testdata/containerd-pod-network-cidr__recent.yaml @@ -0,0 +1,39 @@ +apiVersion: kubeadm.k8s.io/v1alpha3 +kind: InitConfiguration +apiEndpoint: + advertiseAddress: 1.1.1.1 + bindPort: 8443 +bootstrapTokens: + - groups: + - system:bootstrappers:kubeadm:default-node-token + ttl: 24h0m0s + usages: + - signing + - authentication +nodeRegistration: + criSocket: /run/containerd/containerd.sock + name: mk + 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.13.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 +evictionHard: + nodefs.available: "0%" + nodefs.inodesFree: "0%" + imagefs.available: "0%" diff --git a/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__default.yaml b/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__default.yaml index 311baf5223..0ed6fe561c 100644 --- a/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__default.yaml +++ b/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__default.yaml @@ -26,9 +26,6 @@ controllerManager: extraArgs: feature-gates: "a=b" kube-api-burst: "32" -kubeadm: - extraArgs: - feature-gates: "a=b" scheduler: extraArgs: feature-gates: "a=b" diff --git a/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__new.yaml b/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__new.yaml index b593f313d3..1b35bce5c6 100644 --- a/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__new.yaml +++ b/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__new.yaml @@ -26,9 +26,6 @@ controllerManager: extraArgs: feature-gates: "a=b" kube-api-burst: "32" -kubeadm: - extraArgs: - feature-gates: "a=b" scheduler: extraArgs: feature-gates: "a=b" diff --git a/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__obsolete.yaml b/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__obsolete.yaml index 0635574f4c..e71f7dde0f 100644 --- a/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__obsolete.yaml +++ b/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__obsolete.yaml @@ -20,8 +20,6 @@ apiServerExtraArgs: controllerManagerExtraArgs: feature-gates: "a=b" kube-api-burst: "32" -kubeadmExtraArgs: - feature-gates: "a=b" schedulerExtraArgs: feature-gates: "a=b" scheduler-name: "mini-scheduler" diff --git a/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__old.yaml b/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__old.yaml index cf204e307a..6f933f84cc 100644 --- a/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__old.yaml +++ b/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__old.yaml @@ -24,8 +24,6 @@ apiServerExtraArgs: controllerManagerExtraArgs: feature-gates: "a=b" kube-api-burst: "32" -kubeadmExtraArgs: - feature-gates: "a=b" schedulerExtraArgs: feature-gates: "a=b" scheduler-name: "mini-scheduler" diff --git a/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__recent.yaml b/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__recent.yaml index 49e8c06a9f..9e5812dbda 100644 --- a/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__recent.yaml +++ b/pkg/minikube/bootstrapper/kubeadm/testdata/crio-options-gates__recent.yaml @@ -24,8 +24,6 @@ apiServerExtraArgs: controllerManagerExtraArgs: feature-gates: "a=b" kube-api-burst: "32" -kubeadmExtraArgs: - feature-gates: "a=b" schedulerExtraArgs: feature-gates: "a=b" scheduler-name: "mini-scheduler" diff --git a/pkg/util/extra_options.go b/pkg/util/extra_options.go index fc14abddfc..2a60155261 100644 --- a/pkg/util/extra_options.go +++ b/pkg/util/extra_options.go @@ -35,6 +35,9 @@ func (e *ExtraOption) String() string { // ExtraOptionSlice is a slice of ExtraOption type ExtraOptionSlice []ExtraOption +// ComponentExtraOptionMap maps components to their extra opts, which is a map of keys to values +type ComponentExtraOptionMap map[string]map[string]string + // Set parses the string value into a slice func (es *ExtraOptionSlice) Set(value string) error { // The component is the value before the first dot. @@ -68,7 +71,39 @@ func (es *ExtraOptionSlice) String() string { return strings.Join(s, " ") } +// Get finds and returns the value of an argument with the specified key and component (optional) or an empty string +// if not found. If component contains more than one value, the value for the first component found is returned. If +// component is not specified, all of the components are used. +func (es *ExtraOptionSlice) Get(key string, component ...string) string { + for _, opt := range *es { + if component == nil || ContainsString(component, opt.Component) { + if opt.Key == key { + return opt.Value + } + } + } + return "" +} + +// AsMap converts the slice to a map of components to a map of keys and values. +func (es *ExtraOptionSlice) AsMap() ComponentExtraOptionMap { + ret := ComponentExtraOptionMap{} + for _, opt := range *es { + if _, ok := ret[opt.Component]; !ok { + ret[opt.Component] = map[string]string{opt.Key: opt.Value} + } else { + ret[opt.Component][opt.Key] = opt.Value + } + } + return ret +} + // Type returns the type func (es *ExtraOptionSlice) Type() string { return "ExtraOption" } + +// Get returns the extra option map of keys to values for the specified component +func (cm ComponentExtraOptionMap) Get(component string) map[string]string { + return cm[component] +} diff --git a/pkg/util/extra_options_test.go b/pkg/util/extra_options_test.go index 852dcfc053..4e54d99d13 100644 --- a/pkg/util/extra_options_test.go +++ b/pkg/util/extra_options_test.go @@ -78,3 +78,57 @@ func TestValidFlags(t *testing.T) { } } } + +func TestGet(t *testing.T) { + extraOptions := ExtraOptionSlice{ + ExtraOption{Component: "c1", Key: "bar", Value: "c1-bar"}, + ExtraOption{Component: "c1", Key: "bar-baz", Value: "c1-bar-baz"}, + ExtraOption{Component: "c2", Key: "bar", Value: "c2-bar"}, + ExtraOption{Component: "c3", Key: "bar", Value: "c3-bar"}, + } + + for _, tc := range []struct { + searchKey string + searchComponent []string + expRes string + values ExtraOptionSlice + }{ + {"nonexistent", nil, "", extraOptions}, + {"nonexistent", []string{"c1"}, "", extraOptions}, + {"bar", []string{"c2"}, "c2-bar", extraOptions}, + {"bar", []string{"c2", "c3"}, "c2-bar", extraOptions}, + {"bar", nil, "c1-bar", extraOptions}, + } { + if res := tc.values.Get(tc.searchKey, tc.searchComponent...); res != tc.expRes { + t.Errorf("Unexpected value. Expected %s, got %s", tc.expRes, res) + } + } +} + +func TestAsMap(t *testing.T) { + extraOptions := ExtraOptionSlice{ + ExtraOption{Component: "c1", Key: "bar", Value: "c1-bar"}, + ExtraOption{Component: "c1", Key: "bar-baz", Value: "c1-bar-baz"}, + ExtraOption{Component: "c2", Key: "bar", Value: "c2-bar"}, + ExtraOption{Component: "c3", Key: "bar", Value: "c3-bar"}, + } + + expectedRes := ComponentExtraOptionMap{ + "c1": { + "bar": "c1-bar", + "bar-baz": "c1-bar-baz", + }, + "c2": { + "bar": "c2-bar", + }, + "c3": { + "bar": "c3-bar", + }, + } + + res := extraOptions.AsMap() + + if !reflect.DeepEqual(expectedRes, res) { + t.Errorf("Unexpected value. Expected %s, got %s", expectedRes, res) + } +} diff --git a/pkg/util/utils.go b/pkg/util/utils.go index cf81251219..0f2fc50399 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -273,3 +273,14 @@ func ConcatStrings(src []string, prefix string, postfix string) []string { } return ret } + +// ContainsString checks if a given slice of strings contains the provided string. +// If a modifier func is provided, it is called with the slice item before the comparation. +func ContainsString(slice []string, s string) bool { + for _, item := range slice { + if item == s { + return true + } + } + return false +} diff --git a/test/integration/start_stop_delete_test.go b/test/integration/start_stop_delete_test.go index 4a740a50c2..eb2ec5f645 100644 --- a/test/integration/start_stop_delete_test.go +++ b/test/integration/start_stop_delete_test.go @@ -44,6 +44,7 @@ func TestStartStop(t *testing.T) { "ServerSideApply=true", "--network-plugin=cni", "--extra-config=kubelet.network-plugin=cni", + "--extra-config=kubeadm.pod-network-cidr=192.168.111.111/16", fmt.Sprintf("--kubernetes-version=%s", constants.NewestKubernetesVersion), }}, {"containerd_and_non_default_apiserver_port", []string{