Skip to content

Commit

Permalink
implement --static-ip flag
Browse files Browse the repository at this point in the history
  • Loading branch information
spowelljr committed Dec 27, 2022
1 parent 69757f6 commit b07eaed
Show file tree
Hide file tree
Showing 22 changed files with 197 additions and 6 deletions.
30 changes: 30 additions & 0 deletions cmd/minikube/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,12 @@ func validateFlags(cmd *cobra.Command, drvName string) {
validateCNI(cmd, viper.GetString(containerRuntime))
}

if cmd.Flags().Changed(staticIP) {
if err := validateStaticIP(viper.GetString(staticIP), drvName, viper.GetString(subnet)); err != nil {
exit.Message(reason.Usage, "{{.err}}", out.V{"err": err})
}
}

if driver.BareMetal(drvName) {
if ClusterFlagValue() != constants.DefaultClusterName {
exit.Message(reason.DrvUnsupportedProfile, "The '{{.name}} driver does not support multiple profiles: https://minikube.sigs.k8s.io/docs/reference/drivers/none/", out.V{"name": drvName})
Expand Down Expand Up @@ -1763,6 +1769,30 @@ func validateDockerStorageDriver(drvName string) {
viper.Set(preload, false)
}

func validateStaticIP(staticIP, drvName, subnet string) error {
if !driver.IsKIC(drvName) {
if staticIP != "" {
out.WarningT("--static-ip is only implemented on Docker and Podman drivers, flag will be ignored")
}
return nil
}
if subnet != "" {
out.WarningT("--static-ip overrides --subnet, --subnet will be ignored")
}
ip := net.ParseIP(staticIP)
if !ip.IsPrivate() {
return fmt.Errorf("static IP must be private")
}
if ip.To4() == nil {
return fmt.Errorf("static IP must be IPv4")
}
lastOctet, _ := strconv.Atoi(strings.Split(ip.String(), ".")[3])
if lastOctet < 2 || lastOctet > 254 {
return fmt.Errorf("static IPs last octet must be between 2 and 254 (ex. X.X.X.2 - X.X.X.254)")
}
return nil
}

func exitIfNotForced(r reason.Kind, message string, v ...out.V) {
if !viper.GetBool(force) {
exit.Message(r, message, v...)
Expand Down
7 changes: 7 additions & 0 deletions cmd/minikube/cmd/start_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ const (
qemuFirmwarePath = "qemu-firmware-path"
socketVMnetClientPath = "socket-vmnet-client-path"
socketVMnetPath = "socket-vmnet-path"
staticIP = "static-ip"
)

var (
Expand Down Expand Up @@ -200,6 +201,7 @@ func initMinikubeFlags() {
startCmd.Flags().String(binaryMirror, "", "Location to fetch kubectl, kubelet, & kubeadm binaries from.")
startCmd.Flags().Bool(disableOptimizations, false, "If set, disables optimizations that are set for local Kubernetes. Including decreasing CoreDNS replicas from 2 to 1. Defaults to false.")
startCmd.Flags().Bool(disableMetrics, false, "If set, disables metrics reporting (CPU and memory usage), this can improve CPU usage. Defaults to false.")
startCmd.Flags().String(staticIP, "", "Set a static IP for the minikube cluster, the IP must be: private, IPv4, and the last octet must be between 2 and 254 (Docker and Podman drivers only)")
}

// initKubernetesFlags inits the commandline flags for Kubernetes related options
Expand Down Expand Up @@ -571,6 +573,7 @@ func generateNewConfigFromFlags(cmd *cobra.Command, k8sVersion string, rtime str
CustomQemuFirmwarePath: viper.GetString(qemuFirmwarePath),
SocketVMnetClientPath: viper.GetString(socketVMnetClientPath),
SocketVMnetPath: viper.GetString(socketVMnetPath),
StaticIP: viper.GetString(staticIP),
KubernetesConfig: config.KubernetesConfig{
KubernetesVersion: k8sVersion,
ClusterName: ClusterFlagValue(),
Expand Down Expand Up @@ -747,6 +750,10 @@ func updateExistingConfigFromFlags(cmd *cobra.Command, existing *config.ClusterC
out.WarningT("You cannot add or remove extra disks for an existing minikube cluster. Please first delete the cluster.")
}

if cmd.Flags().Changed(staticIP) && viper.GetString(staticIP) != existing.StaticIP {
out.WarningT("You cannot change the static IP of an existing minikube cluster. Please first delete the cluster.")
}

updateBoolFromFlag(cmd, &cc.KeepContext, keepContext)
updateBoolFromFlag(cmd, &cc.EmbedCerts, embedCerts)
updateStringFromFlag(cmd, &cc.MinikubeISO, isoURL)
Expand Down
59 changes: 59 additions & 0 deletions cmd/minikube/cmd/start_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -628,3 +628,62 @@ func TestValidatePorts(t *testing.T) {
})
}
}

func TestValidateStaticIP(t *testing.T) {
tests := []struct {
staticIP string
drvName string
errorMsg string
}{
{
staticIP: "8.8.8.8",
drvName: "docker",
errorMsg: "static IP must be private",
},
{
staticIP: "8.8.8.8",
drvName: "hyperkit",
errorMsg: "",
},
{
staticIP: "fdfc:a4c0:e99e:7ad3::",
drvName: "docker",
errorMsg: "static IP must be IPv4",
},
{
staticIP: "192.168.49.0",
drvName: "docker",
errorMsg: "static IPs last octet must be between 2 and 254 (ex. X.X.X.2 - X.X.X.254)",
},
{
staticIP: "192.168.49.1",
drvName: "docker",
errorMsg: "static IPs last octet must be between 2 and 254 (ex. X.X.X.2 - X.X.X.254)",
},
{
staticIP: "192.168.49.255",
drvName: "docker",
errorMsg: "static IPs last octet must be between 2 and 254 (ex. X.X.X.2 - X.X.X.254)",
},
{
staticIP: "192.168.49.2",
drvName: "docker",
errorMsg: "",
},
{
staticIP: "192.168.49.254",
drvName: "docker",
errorMsg: "",
},
}
for _, tt := range tests {
gotError := ""
got := validateStaticIP(tt.staticIP, tt.drvName, "")
if got != nil {
gotError = got.Error()
}
if gotError != tt.errorMsg {
t.Errorf("validateStaticIP(%s, %s): got %v, expected %v", tt.staticIP, tt.drvName, got, tt.errorMsg)
}
}
}
15 changes: 13 additions & 2 deletions pkg/drivers/kic/kic.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ import (
"k8s.io/minikube/pkg/minikube/cruntime"
"k8s.io/minikube/pkg/minikube/download"
"k8s.io/minikube/pkg/minikube/driver"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/sysinit"
"k8s.io/minikube/pkg/util/retry"
Expand Down Expand Up @@ -92,8 +94,17 @@ func (d *Driver) Create() error {
if networkName == "" {
networkName = d.NodeConfig.ClusterName
}
if gateway, err := oci.CreateNetwork(d.OCIBinary, networkName, d.NodeConfig.Subnet); err != nil {
out.WarningT("Unable to create dedicated network, this might result in cluster IP change after restart: {{.error}}", out.V{"error": err})
staticIP := d.NodeConfig.StaticIP
if gateway, err := oci.CreateNetwork(d.OCIBinary, networkName, d.NodeConfig.Subnet, staticIP); err != nil {
msg := "Unable to create dedicated network, this might result in cluster IP change after restart: {{.error}}"
args := out.V{"error": err}
if staticIP != "" {
exit.Message(reason.IfDedicatedNetwork, msg, args)
}
out.WarningT(msg, args)
} else if gateway != nil && staticIP != "" {
params.Network = networkName
params.IP = staticIP
} else if gateway != nil {
params.Network = networkName
ip := gateway.To4()
Expand Down
12 changes: 10 additions & 2 deletions pkg/drivers/kic/oci/network_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func firstSubnetAddr(subnet string) string {
}

// CreateNetwork creates a network returns gateway and error, minikube creates one network per cluster
func CreateNetwork(ociBin, networkName, subnet string) (net.IP, error) {
func CreateNetwork(ociBin, networkName, subnet, staticIP string) (net.IP, error) {
defaultBridgeName := defaultBridgeName(ociBin)
if networkName == defaultBridgeName {
klog.Infof("skipping creating network since default network %s was specified", networkName)
Expand All @@ -84,12 +84,20 @@ func CreateNetwork(ociBin, networkName, subnet string) (net.IP, error) {
klog.Warningf("failed to get mtu information from the %s's default network %q: %v", ociBin, defaultBridgeName, err)
}

tries := 20

// we don't want to increment the subnet IP on network creation failure if the user specifies a static IP, so set tries to 1
if staticIP != "" {
tries = 1
subnet = staticIP
}

// retry up to 5 times to create container network
for attempts, subnetAddr := 0, firstSubnetAddr(subnet); attempts < 5; attempts++ {
// Rather than iterate through all of the valid subnets, give up at 20 to avoid a lengthy user delay for something that is unlikely to work.
// will be like 192.168.49.0/24,..., 192.168.220.0/24 (in increment steps of 9)
var subnet *network.Parameters
subnet, err = network.FreeSubnet(subnetAddr, 9, 20)
subnet, err = network.FreeSubnet(subnetAddr, 9, tries)
if err != nil {
klog.Errorf("failed to find free subnet for %s network %s after %d attempts: %v", ociBin, networkName, 20, err)
return nil, fmt.Errorf("un-retryable: %w", err)
Expand Down
3 changes: 2 additions & 1 deletion pkg/drivers/kic/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ type Config struct {
Envs map[string]string // key,value of environment variables passed to the node
KubernetesVersion string // Kubernetes version to install
ContainerRuntime string // container runtime kic is running
Network string // network to run with kic
Network string // network to run with kic
Subnet string // subnet to be used on kic cluster
StaticIP string // static IP for the kic cluster
ExtraArgs []string // a list of any extra option to pass to oci binary during creation time, for example --expose 8080...
ListenAddress string // IP Address to listen to
}
1 change: 1 addition & 0 deletions pkg/minikube/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ type ClusterConfig struct {
CustomQemuFirmwarePath string
SocketVMnetClientPath string
SocketVMnetPath string
StaticIP string
}

// KubernetesConfig contains the parameters used to configure the VM Kubernetes.
Expand Down
2 changes: 2 additions & 0 deletions pkg/minikube/reason/reason.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,8 @@ var (
IfMountPort = Kind{ID: "IF_MOUNT_PORT", ExitCode: ExLocalNetworkError}
// minikube failed to access an ssh client on the host machine
IfSSHClient = Kind{ID: "IF_SSH_CLIENT", ExitCode: ExLocalNetworkError}
// minikube failed to create a dedicated network
IfDedicatedNetwork = Kind{ID: "IF_DEDICATED_NETWORK", ExitCode: ExLocalNetworkError}

// minikube failed to cache kubernetes binaries for the current runtime
InetCacheBinaries = Kind{ID: "INET_CACHE_BINARIES", ExitCode: ExInternetError}
Expand Down
1 change: 1 addition & 0 deletions pkg/minikube/registry/drvs/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func configure(cc config.ClusterConfig, n config.Node) (interface{}, error) {
ExtraArgs: extraArgs,
Network: cc.Network,
Subnet: cc.Subnet,
StaticIP: cc.StaticIP,
ListenAddress: cc.ListenAddress,
}), nil
}
Expand Down
1 change: 1 addition & 0 deletions site/content/en/docs/commands/start.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ minikube start [flags]
--ssh-key string SSH key (ssh driver only)
--ssh-port int SSH port (ssh driver only) (default 22)
--ssh-user string SSH user (ssh driver only) (default "root")
--static-ip string Set a static IP for the minikube cluster, the IP must be: private, IPv4, and the last octet must be between 2 and 254 (Docker and Podman drivers only)
--subnet string Subnet to be used on kic cluster. If left empty, minikube will choose subnet address, beginning from 192.168.49.0. (docker and podman driver only)
--trace string Send trace events. Options include: [gcp]
--uuid string Provide VM UUID to restore MAC address (hyperkit driver only)
Expand Down
3 changes: 3 additions & 0 deletions site/content/en/docs/contrib/errorcodes.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,9 @@ minikube failed to parse or find port for mount
"IF_SSH_CLIENT" (Exit code ExLocalNetworkError)
minikube failed to access an ssh client on the host machine

"IF_DEDICATED_NETWORK" (Exit code ExLocalNetworkError)
minikube failed to create a dedicated network

"INET_CACHE_BINARIES" (Exit code ExInternetError)
minikube failed to cache kubernetes binaries for the current runtime

Expand Down
3 changes: 3 additions & 0 deletions site/content/en/docs/contrib/tests.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,9 @@ verifies the docker driver and run with an existing network
## TestKicCustomSubnet
verifies the docker/podman driver works with a custom subnet

## TestKicStaticIP
starts minikube with the static IP flag

## TestingKicBaseImage
will return true if the integraiton test is running against a passed --base-image flag

Expand Down
30 changes: 29 additions & 1 deletion test/integration/kic_custom_network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func TestKicExistingNetwork(t *testing.T) {
}
// create custom network
networkName := "existing-network"
if _, err := oci.CreateNetwork(oci.Docker, networkName, ""); err != nil {
if _, err := oci.CreateNetwork(oci.Docker, networkName, "", ""); err != nil {
t.Fatalf("error creating network: %v", err)
}
defer func() {
Expand Down Expand Up @@ -117,6 +117,34 @@ func TestKicCustomSubnet(t *testing.T) {
verifySubnet(ctx, t, profile, subnet)
}

// TestKicStaticIP starts minikube with the static IP flag
func TestKicStaticIP(t *testing.T) {
if !KicDriver() {
t.Skip("only run with docker/podman driver")
}
profile := UniqueProfileName("static-ip")
ctx, cancel := context.WithTimeout(context.Background(), Minutes(5))
defer Cleanup(t, profile, cancel)

staticIP := "192.168.200.200"
startArgs := []string{"start", "-p", profile, fmt.Sprintf("--static-ip=%s", staticIP)}
c := exec.CommandContext(ctx, Target(), startArgs...)
rr, err := Run(t, c)
if err != nil {
t.Fatalf("%v failed: %v\n%v", rr.Command(), err, rr.Output())
}

c = exec.CommandContext(ctx, Target(), "-p", profile, "ip")
rr, err = Run(t, c)
if err != nil {
t.Fatalf("%s failed: %v\n%s", rr.Command(), err, rr.Output())
}

if !strings.Contains(rr.Output(), staticIP) {
t.Errorf("static IP (%s) not found in output %s", staticIP, rr.Output())
}
}

func verifyNetworkExists(ctx context.Context, t *testing.T, networkName string) {
c := exec.CommandContext(ctx, "docker", "network", "ls", "--format", "{{.Name}}")
rr, err := Run(t, c)
Expand Down
4 changes: 4 additions & 0 deletions translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
"--network flag is only valid with the docker/podman and KVM drivers, it will be ignored": "Der Parameter --network kann nur mit dem docker/podman und den KVM Treibern verwendet werden, er wird ignoriert werden",
"--network flag is only valid with the docker/podman, KVM and Qemu drivers, it will be ignored": "",
"--network with QEMU must be 'user' or 'socket_vmnet'": "",
"--static-ip is only implemented on Docker and Podman drivers, flag will be ignored": "",
"--static-ip overrides --subnet, --subnet will be ignored": "",
"1) Recreate the cluster with Kubernetes {{.new}}, by running:\n\t \n\t\t minikube delete{{.profile}}\n\t\t minikube start{{.profile}} --kubernetes-version={{.prefix}}{{.new}}\n\t \n\t\t2) Create a second cluster with Kubernetes {{.new}}, by running:\n\t \n\t\t minikube start -p {{.suggestedName}} --kubernetes-version={{.prefix}}{{.new}}\n\t \n\t\t3) Use the existing cluster at version Kubernetes {{.old}}, by running:\n\t \n\t\t minikube start{{.profile}} --kubernetes-version={{.prefix}}{{.old}}\n\t\t": "1) Erstellen Sie den Cluster mit Kubernetes {{.new}} neu, indem Sie folgende Befehle ausführen:\n\t \n\t\t minikube delete{{.profile}}\n\t\t minikube start{{.profile}} --kubernetes-version={{.prefix}}{{.new}}\n\t \n\t\t2) Erstellen Sie einen zweiten Cluster mit Kubernetes {{.new}}, indem Sie folgende Befehle ausführen:\n\t \n\t\t minikube start -p {{.suggestedName}} --kubernetes-version={{.prefix}}{{.new}}\n\t \n\t\t3) Verwenden Sie den existierenden Cluster mit Version {{.old}} von Kubernetes, indem Sie folgende Befehle ausführen:\n\t \n\t\t minikube start{{.profile}} --kubernetes-version={{.prefix}}{{.old}}\n\t\t",
"1. Click on \"Docker for Desktop\" menu icon\n\t\t\t2. Click \"Preferences\"\n\t\t\t3. Click \"Resources\"\n\t\t\t4. Increase \"CPUs\" slider bar to 2 or higher\n\t\t\t5. Click \"Apply \u0026 Restart\"": "1. Klicken Sie auf das \"Docker für Desktop\" Menu Icon\n\t\t\t2. Klicken Sie auf \"Einstellungen\"\n\t\t\t3. Klicken Sie auf \"Resourcen\"\n\t\t\t4. Erhöhen Sie den Wert von \"CPUs\" auf 2 oder mehr\n\t\t\t5. Klicken Sie auf \"Anwenden \u0026 Neustarten\"",
"1. Click on \"Docker for Desktop\" menu icon\n\t\t\t2. Click \"Preferences\"\n\t\t\t3. Click \"Resources\"\n\t\t\t4. Increase \"Memory\" slider bar to {{.recommend}} or higher\n\t\t\t5. Click \"Apply \u0026 Restart\"": "1. Klicken Sie auf das \"Docker für Desktop\" Menu Icon\n\t\t\t2. Klicken Sie auf \"Einstellungen\"\n\t\t\t3. Klicken Sie auf \"Resourcen\"\n\t\t\t4. Erhöhen Sie den Wert von \"Speicher\" auf {{.recommend}} oder mehr\n\t\t\t5. Klicken Sie auf \"Anwenden \u0026 Neustarten\"",
Expand Down Expand Up @@ -584,6 +586,7 @@
"Select a valid value for --dnsdomain": "Wähle einen gültigen Wert für --dnsdomain",
"Send trace events. Options include: [gcp]": "Schicke Trace Events. Mögliche Optionen sind [gcp]",
"Service '{{.service}}' was not found in '{{.namespace}}' namespace.\nYou may select another namespace by using 'minikube service {{.service}} -n \u003cnamespace\u003e'. Or list out all the services using 'minikube service list'": "Service '{{.service}}' konnte nicht im Namespace '{{.namespace}} gefunden werden.\nEs ist möglich einen anderen Namespace mit 'minikube service {{.service}} -n \u003cnamespace\u003e' auszuwählen. Oder die Liste aller Services anzuzeigen mit 'minikube service list'",
"Set a static IP for the minikube cluster, the IP must be: private, IPv4, and the last octet must be between 2 and 254 (Docker and Podman drivers only)": "",
"Set failed": "Setzen fehlgeschlagen",
"Set flag to delete all profiles": "Setze Flag um alle Profile zu löschen",
"Set flag to stop all profiles (clusters)": "Setze Flag um alle Profile (Cluster) zu stoppen",
Expand Down Expand Up @@ -899,6 +902,7 @@
"You cannot change the CPUs for an existing minikube cluster. Please first delete the cluster.": "Die Anzahl der CPUs eines existierenden Minikube Clusters kann nicht geändert werden. Bitte löschen Sie den Cluster zuerst.",
"You cannot change the disk size for an existing minikube cluster. Please first delete the cluster.": "Die Plattengröße eines existierenden Minikube Clusters kann nicht geändert werden. Bitte löschen Sie den Cluster zuerst.",
"You cannot change the memory size for an existing minikube cluster. Please first delete the cluster.": "Die Speichergröße eines existierenden Minikube Clusters kann nicht geändert werden. Bitte löschen Sie den Cluster zuerst.",
"You cannot change the static IP of an existing minikube cluster. Please first delete the cluster.": "",
"You have authenticated with a service account that does not have an associated JSON file. The GCP Auth addon requires credentials with a JSON file in order to continue.": "",
"You have authenticated with a service account that does not have an associated JSON. The GCP Auth requires credentials with a JSON file to in order to continue. The image pull secret has been imported.": "Sie haben sich mit einem Service Account authentifiziert, welcher kein zugehöriges JSON besitzt. GCP Auth benötigt Zugangsdaten in einer JSON-Datei um weitermachen zu können. Das Image Pull Secret wurde importiert.",
"You have chosen to disable the CNI but the \"{{.name}}\" container runtime requires CNI": "Sie haben den CNI Treiber deaktiviert, aber die \"{{.name}}\" Container Laufzeitumgebung benötigt ein CNI",
Expand Down
Loading

0 comments on commit b07eaed

Please sign in to comment.