Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 56 additions & 30 deletions .github/workflows/smoke-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,39 +35,42 @@ concurrency:
cancel-in-progress: true
jobs:
minikube-vm-boot:
name: Boot
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- driver: qemu
- name: qemu-docker-macos-15-x86
driver: qemu
os: macos-15-intel
network_flag: --network socket_vmnet
- driver: vfkit
start_flags: --network socket_vmnet --force
sudo: "sudo"
- name: vfkit-docker-macos-15-x86
driver: vfkit
os: macos-15-intel
network_flag: --network vmnet-shared
- driver: docker
start_flags: --network vmnet-shared --force
sudo: "sudo"
- name: docker-docker-ubuntu-24.04-x86
driver: docker
os: ubuntu-24.04
network_flag: ""
- driver: docker
start_flags: ""
sudo: ""
- name: docker-docker-ubuntu-24.04-arm
driver: docker
os: ubuntu-24.04-arm
network_flag: ""
start_flags: ""
sudo: ""
steps:
- id: info-block
uses: medyagh/info-block@main
- name: Load avg and free memory
- name: Disable spotlight indexing (macos)
if: contains(matrix.os, 'macos')
run: |
echo "memory_gb=${{ steps.info-block.outputs.memory_gb }}"
echo "cpu_cores=${{ steps.info-block.outputs.cpu_cores }}"
echo "load_average=${{ steps.info-block.outputs.load_average }}"
echo "free_mem=${{ steps.info-block.outputs.free_mem }}"
load_avg=${{ steps.info-block.outputs.load_average }}
cores=${{ steps.info-block.outputs.cpu_cores }}
if (( $(echo "$load_avg / $cores > 2" | bc -l) )); then
echo "Load average per core is above 2; stopping job early."
exit 1
fi
# Disable indexing globally
sudo mdutil -a -i off
# Erase Existing Indexes
sudo mdutil -E /
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c
with:
Expand All @@ -93,28 +96,51 @@ jobs:
run: |
brew install qemu socket_vmnet
HOMEBREW=$(which brew) && sudo ${HOMEBREW} services start socket_vmnet
- name: Inspect host before test (macos)
if: contains(matrix.os, 'macos')
run: |
echo "=== Top ==="
top -l 2 -s 10 -n 20 -stats pid,command,cpu,time,mem
echo "=== Interfaces ==="
ifconfig -u
echo "=== Routing table ==="
netstat -rn -f inet
echo "=== Packet Filter ==="
sudo pfctl -s all || true
- name: Start minikube (1st boot)
run: |
./out/minikube start \
${{ matrix.sudo }} ./out/minikube start \
${{ env.LOG_ARGS }} \
--no-kubernetes \
--memory 4gb \
--cpus 2 \
--memory 3gb \
--driver ${{ matrix.driver }} \
${{ matrix.network_flag }} \
${{ env.LOG_ARGS }}
${{ matrix.start_flags }}
- name: Inspect host after test (macos)
if: always() && contains(matrix.os, 'macos')
run: |
echo "=== Top ==="
top -l 2 -s 10 -n 20 -stats pid,command,cpu,time,mem
echo "=== Interfaces ==="
ifconfig -u
echo "=== Routing table ==="
netstat -rn -f inet
echo "=== Packet Filter ==="
sudo pfctl -s all || true
- name: Inspect minikube
if: always()
run: |
tree -h ~/.minikube
${{ matrix.sudo }} tree -h ~/.minikube
machine="$HOME/.minikube/machines/minikube"
machine_logs=$(find "$machine" -name "*.log")
machine_logs=$(${{ matrix.sudo }} find "$machine" -name "*.log")
minikube_logs="$HOME/.minikube/logs/lastStart.txt"
for f in $machine_logs $minikube_logs /var/db/dhcpd_leases; do
echo "==> $f <=="
head -n 1000 "$f" || true
${{ matrix.sudo }} head -n 1000 "$f" || true
done
- name: Stop minikube
run: ./out/minikube stop ${{ env.LOG_ARGS }}
run: ${{ matrix.sudo }} ./out/minikube stop ${{ env.LOG_ARGS }}
- name: Start minikube again (2nd boot)
run: ./out/minikube start ${{ env.LOG_ARGS }}
run: ${{ matrix.sudo }} ./out/minikube start ${{ env.LOG_ARGS }} ${{ matrix.start_flags }}
- name: Delete minikube
run: ./out/minikube delete ${{ env.LOG_ARGS }}
run: ${{ matrix.sudo }} ./out/minikube delete ${{ env.LOG_ARGS }}
112 changes: 112 additions & 0 deletions pkg/drivers/common/ssh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
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 common

import (
"errors"
"fmt"
"io"
"net"
"os"
"strconv"
"time"

"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/log"
)

var (
retryDelay = time.Second

// We have 2 cases:
// - First start: called after a DHCP lease was created. The host is up and
// has an IP address. The SSH server is accessible in 10-1000
// milliseconds locally, and few seconds in the GitHub macOS runners.
// - Second start: The DHCP lease is found immediately in DHCP leases
// database but the host is not up yet. SSH is accessible in few seconds
// locally, and 2-3 minutes in GitHub macOS runners.
timeout = 5 * time.Minute
)

// WaitForSSHAccess waits until remote SSH server is responding. Returns an
// error if the wait timed out or could not be started.
func WaitForSSHAccess(d drivers.Driver) error {
ip, err := d.GetIP()
if err != nil {
return err
}
port, err := d.GetSSHPort()
if err != nil {
return err
}
addr := net.JoinHostPort(ip, strconv.Itoa(port))
log.Infof("Waiting until SSH server %q is accessible", addr)

start := time.Now()
deadline := start.Add(timeout)
dialer := net.Dialer{Deadline: deadline}

for {
done, err := checkSSHAccess(&dialer, addr)
if err != nil {
return err
}
if done {
log.Infof("SSH server %q is accessible in %.3f seconds", addr, time.Since(start).Seconds())
return nil
}
time.Sleep(retryDelay)
}
}

// checkSSHAccess performs one check, returning:
// - (true, nil) we accessed the SSH server
// - (false, nil) failed to access the server, need to retry again
// - (false , error) timeout accessing the server, do not retry
func checkSSHAccess(dialer *net.Dialer, addr string) (bool, error) {
if time.Now().After(dialer.Deadline) {
return false, fmt.Errorf("timeout waiting for SSH server %q", addr)
}

log.Debugf("Dialing to SSH server %q", addr)
conn, err := dialer.Dial("tcp", addr)
if err != nil {
if errors.Is(err, os.ErrDeadlineExceeded) {
return false, fmt.Errorf("timeout dialing to SSH server %q", addr)
}
log.Debugf("Failed to dial: %v", err)
return false, nil
}

defer conn.Close()

if err := conn.SetReadDeadline(dialer.Deadline); err != nil {
log.Debugf("Failed to set timeout: %v", err)
return false, nil
}

log.Debugf("Reading from SSH server %q", addr)
if _, err := conn.Read(make([]byte, 1)); err != nil && err != io.EOF {
if errors.Is(err, os.ErrDeadlineExceeded) {
return false, fmt.Errorf("timeout reading from SSH server %q", addr)
}
log.Debugf("Failed to read: %v", err)
return false, nil
}

return true, nil
}
21 changes: 1 addition & 20 deletions pkg/drivers/krunkit/krunkit.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"os"
Expand Down Expand Up @@ -211,8 +210,7 @@ func (d *Driver) Start() error {
return err
}

log.Infof("Waiting for VM to start (ssh -p %d docker@%s)...", d.SSHPort, d.IPAddress)
if err := WaitForTCPWithDelay(fmt.Sprintf("%s:%d", d.IPAddress, d.SSHPort), time.Second); err != nil {
if err := common.WaitForSSHAccess(d); err != nil {
return err
}

Expand Down Expand Up @@ -566,20 +564,3 @@ func (d *Driver) setKrunkitState(desired string) error {
}
return nil
}

// TODO: duplicate from vfkit and qemu: https://github.com/kubernetes/minikube/issues/21091
func WaitForTCPWithDelay(addr string, duration time.Duration) error {
for {
conn, err := net.Dial("tcp", addr)
if err != nil {
continue
}
defer conn.Close()
if _, err := conn.Read(make([]byte, 1)); err != nil && err != io.EOF {
time.Sleep(duration)
continue
}
break
}
return nil
}
21 changes: 1 addition & 20 deletions pkg/drivers/qemu/qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"bytes"
"encoding/json"
"fmt"
"io"
"math/rand"
"net"
"os"
Expand Down Expand Up @@ -549,9 +548,7 @@ func (d *Driver) Start() error {
return fmt.Errorf("ip not found: %v", err)
}

log.Infof("Waiting for VM to start (ssh -p %d docker@%s)...", d.SSHPort, d.IPAddress)

return WaitForTCPWithDelay(fmt.Sprintf("%s:%d", d.IPAddress, d.SSHPort), time.Second)
return common.WaitForSSHAccess(d)
}

func hardwareAcceleration() string {
Expand Down Expand Up @@ -873,19 +870,3 @@ func (d *Driver) RunQMPCommand(command string) (map[string]interface{}, error) {
}
return response.Return, nil
}

func WaitForTCPWithDelay(addr string, duration time.Duration) error {
for {
conn, err := net.Dial("tcp", addr)
if err != nil {
continue
}
defer conn.Close()
if _, err := conn.Read(make([]byte, 1)); err != nil && err != io.EOF {
time.Sleep(duration)
continue
}
break
}
return nil
}
20 changes: 1 addition & 19 deletions pkg/drivers/vfkit/vfkit.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"os"
Expand Down Expand Up @@ -254,8 +253,7 @@ func (d *Driver) Start() error {
return err
}

log.Infof("Waiting for VM to start (ssh -p %d docker@%s)...", d.SSHPort, d.IPAddress)
if err := WaitForTCPWithDelay(fmt.Sprintf("%s:%d", d.IPAddress, d.SSHPort), time.Second); err != nil {
if err := common.WaitForSSHAccess(d); err != nil {
return err
}

Expand Down Expand Up @@ -660,19 +658,3 @@ func (d *Driver) SetVFKitState(s string) error {
log.Infof("Set vfkit state: %+v", vmstate)
return nil
}

func WaitForTCPWithDelay(addr string, duration time.Duration) error {
for {
conn, err := net.Dial("tcp", addr)
if err != nil {
continue
}
defer conn.Close()
if _, err := conn.Read(make([]byte, 1)); err != nil && err != io.EOF {
time.Sleep(duration)
continue
}
break
}
return nil
}
Loading