Merge branch 'kubernetes:master' into Hyperkit-StaticWarning
commit
6d7f04ebfd
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
|
||||
- name: Install bom
|
||||
uses: kubernetes-sigs/release-actions/setup-bom@a30d93cf2aa029e1e4c8a6c79f766aebf429fddb # main
|
||||
uses: kubernetes-sigs/release-actions/setup-bom@8af7b2a5596dff526de9db59b2c4b8457e9f52a1 # main
|
||||
- name: Generage SBOM
|
||||
run: |
|
||||
bom generate -o minikube_${{github.ref_name}}_sbom.spdx \
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -70,7 +70,7 @@ gsutil.cmd -m cp -r gs://minikube-builds/$env:MINIKUBE_LOCATION/installers/check
|
|||
|
||||
# Download gopogh and gotestsum
|
||||
go install github.com/medyagh/gopogh/cmd/gopogh@v0.29.0
|
||||
go install gotest.tools/gotestsum@v1.12.3
|
||||
go install gotest.tools/gotestsum@v1.13.0
|
||||
# temporary: remove the old install of gopogh & gotestsum as it's taking priority over our current install, preventing updating
|
||||
if (Test-Path "C:\Go") {
|
||||
Remove-Item "C:\Go" -Recurse -Force
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
set -eux -o pipefail
|
||||
|
||||
GH_VERSION="2.78.0"
|
||||
GH_VERSION="2.80.0"
|
||||
|
||||
echo "Installing latest version of gh"
|
||||
curl -qLO "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ set -eux -o pipefail
|
|||
|
||||
function install_gotestsum() {
|
||||
rm -f $(which gotestsum)
|
||||
GOBIN="$GOROOT/bin" go install gotest.tools/gotestsum@v1.12.3
|
||||
GOBIN="$GOROOT/bin" go install gotest.tools/gotestsum@v1.13.0
|
||||
}
|
||||
|
||||
which gotestsum || install_gotestsum
|
||||
|
|
|
|||
|
|
@ -72,6 +72,14 @@ type interfaceInfo struct {
|
|||
// The returned error.Kind can be used to provide a suggestion for resolving the
|
||||
// issue.
|
||||
func ValidateHelper() error {
|
||||
// Ideally minikube will not try to validate in download-only mode, but this
|
||||
// is called from different places in different drivers, so the easier way
|
||||
// to skip validation is to skip it here.
|
||||
if viper.GetBool("download-only") {
|
||||
log.Debug("Skipping vmnet-helper validation in download-only mode")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Is it installed?
|
||||
if _, err := os.Stat(executablePath); err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@ var Addons = map[string]*Addon{
|
|||
"0640"),
|
||||
}, false, "kong", "3rd party (Kong HQ)", "@gAmUssA", "https://minikube.sigs.k8s.io/docs/handbook/addons/kong-ingress/", map[string]string{
|
||||
"Kong": "kong:3.9.1@sha256:14c689c0caf1b8da1403a742016ec64d2f5b5b12ecdec2989f36b2c2c4aa1ac0",
|
||||
"KongIngress": "kong/kubernetes-ingress-controller:3.5.1@sha256:4dc74b6fd7bcea07196900b53b6e1d5fabf6b7e56a7ed5d786e0d1c0ab950d1d",
|
||||
"KongIngress": "kong/kubernetes-ingress-controller:3.5.2@sha256:472012b0f7dca838f2b9cd8fcf6e6daeb2572631b37adb0dbb0770defeb89f04",
|
||||
}, map[string]string{
|
||||
"Kong": "docker.io",
|
||||
"KongIngress": "docker.io",
|
||||
|
|
@ -580,9 +580,9 @@ var Addons = map[string]*Addon{
|
|||
"volcano-deployment.yaml",
|
||||
"0640"),
|
||||
}, false, "volcano", "third-party (volcano)", "hwdef", "", map[string]string{
|
||||
"vc_webhook_manager": "volcanosh/vc-webhook-manager:v1.12.2@sha256:b7c3bd73e2d9240cf17662451d50e0d73654342235a66cdfb2ec221f1628ae35",
|
||||
"vc_controller_manager": "volcanosh/vc-controller-manager:v1.12.2@sha256:286112e70bdbf88174a66895bb3c64dd9026b5a762025b61bcd8f6cac04e1b90",
|
||||
"vc_scheduler": "volcanosh/vc-scheduler:v1.12.2@sha256:6e28f0f79d4cd09c1c34afaba41623c1b4d0fd7087cc99d6449a8a62e073b50e",
|
||||
"vc_webhook_manager": "volcanosh/vc-webhook-manager:v1.13.0@sha256:03e36eb220766397b4cd9c2f42bab8666661a0112fa9033ae9bd80d2a9611001",
|
||||
"vc_controller_manager": "volcanosh/vc-controller-manager:v1.13.0@sha256:8dd7ce0cef2df19afb14ba26bec90e2999a3c0397ebe5c9d75a5f68d1c80d242",
|
||||
"vc_scheduler": "volcanosh/vc-scheduler:v1.13.0@sha256:b05b30b3c25eff5af77e1859f47fc6acfc3520d62dc2838f0623aa4309c40b34",
|
||||
}, map[string]string{
|
||||
"vc_webhook_manager": "docker.io",
|
||||
"vc_controller_manager": "docker.io",
|
||||
|
|
@ -751,7 +751,7 @@ var Addons = map[string]*Addon{
|
|||
MustBinAsset(addons.NvidiaDevicePlugin, "nvidia-device-plugin/nvidia-device-plugin.yaml.tmpl", vmpath.GuestAddonsDir, "nvidia-device-plugin.yaml", "0640"),
|
||||
}, false, "nvidia-device-plugin", "3rd party (NVIDIA)", "", "https://minikube.sigs.k8s.io/docs/tutorials/nvidia/",
|
||||
map[string]string{
|
||||
"NvidiaDevicePlugin": "nvidia/k8s-device-plugin:v0.17.3@sha256:630596340f8e83aa10b0bc13a46db76772e31b7dccfc34d3a4e41ab7e0aa6117",
|
||||
"NvidiaDevicePlugin": "nvidia/k8s-device-plugin:v0.17.4@sha256:3c54348fe5a57e5700e7d8068e7531d2ef2d5f3ccb70c8f6bac0953432527abd",
|
||||
}, map[string]string{
|
||||
"NvidiaDevicePlugin": "nvcr.io",
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -866,7 +866,7 @@ spec:
|
|||
type: Unconfined
|
||||
containers:
|
||||
- name: cilium-agent
|
||||
image: "quay.io/cilium/cilium:v1.18.1@sha256:65ab17c052d8758b2ad157ce766285e04173722df59bdee1ea6d5fda7149f0e9"
|
||||
image: "quay.io/cilium/cilium:v1.18.2@sha256:858f807ea4e20e85e3ea3240a762e1f4b29f1cb5bbd0463b8aa77e7b097c0667"
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- cilium-agent
|
||||
|
|
@ -1030,7 +1030,7 @@ spec:
|
|||
|
||||
initContainers:
|
||||
- name: config
|
||||
image: "quay.io/cilium/cilium:v1.18.1@sha256:65ab17c052d8758b2ad157ce766285e04173722df59bdee1ea6d5fda7149f0e9"
|
||||
image: "quay.io/cilium/cilium:v1.18.2@sha256:858f807ea4e20e85e3ea3240a762e1f4b29f1cb5bbd0463b8aa77e7b097c0667"
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- cilium-dbg
|
||||
|
|
@ -1053,7 +1053,7 @@ spec:
|
|||
# Required to mount cgroup2 filesystem on the underlying Kubernetes node.
|
||||
# We use nsenter command with host's cgroup and mount namespaces enabled.
|
||||
- name: mount-cgroup
|
||||
image: "quay.io/cilium/cilium:v1.18.1@sha256:65ab17c052d8758b2ad157ce766285e04173722df59bdee1ea6d5fda7149f0e9"
|
||||
image: "quay.io/cilium/cilium:v1.18.2@sha256:858f807ea4e20e85e3ea3240a762e1f4b29f1cb5bbd0463b8aa77e7b097c0667"
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: CGROUP_ROOT
|
||||
|
|
@ -1090,7 +1090,7 @@ spec:
|
|||
drop:
|
||||
- ALL
|
||||
- name: apply-sysctl-overwrites
|
||||
image: "quay.io/cilium/cilium:v1.18.1@sha256:65ab17c052d8758b2ad157ce766285e04173722df59bdee1ea6d5fda7149f0e9"
|
||||
image: "quay.io/cilium/cilium:v1.18.2@sha256:858f807ea4e20e85e3ea3240a762e1f4b29f1cb5bbd0463b8aa77e7b097c0667"
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: BIN_PATH
|
||||
|
|
@ -1128,7 +1128,7 @@ spec:
|
|||
# from a privileged container because the mount propagation bidirectional
|
||||
# only works from privileged containers.
|
||||
- name: mount-bpf-fs
|
||||
image: "quay.io/cilium/cilium:v1.18.1@sha256:65ab17c052d8758b2ad157ce766285e04173722df59bdee1ea6d5fda7149f0e9"
|
||||
image: "quay.io/cilium/cilium:v1.18.2@sha256:858f807ea4e20e85e3ea3240a762e1f4b29f1cb5bbd0463b8aa77e7b097c0667"
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- 'mount | grep "/sys/fs/bpf type bpf" || mount -t bpf bpf /sys/fs/bpf'
|
||||
|
|
@ -1144,7 +1144,7 @@ spec:
|
|||
mountPath: /sys/fs/bpf
|
||||
mountPropagation: Bidirectional
|
||||
- name: clean-cilium-state
|
||||
image: "quay.io/cilium/cilium:v1.18.1@sha256:65ab17c052d8758b2ad157ce766285e04173722df59bdee1ea6d5fda7149f0e9"
|
||||
image: "quay.io/cilium/cilium:v1.18.2@sha256:858f807ea4e20e85e3ea3240a762e1f4b29f1cb5bbd0463b8aa77e7b097c0667"
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /init-container.sh
|
||||
|
|
@ -1191,7 +1191,7 @@ spec:
|
|||
mountPath: /var/run/cilium # wait-for-kube-proxy
|
||||
# Install the CNI binaries in an InitContainer so we don't have a writable host mount in the agent
|
||||
- name: install-cni-binaries
|
||||
image: "quay.io/cilium/cilium:v1.18.1@sha256:65ab17c052d8758b2ad157ce766285e04173722df59bdee1ea6d5fda7149f0e9"
|
||||
image: "quay.io/cilium/cilium:v1.18.2@sha256:858f807ea4e20e85e3ea3240a762e1f4b29f1cb5bbd0463b8aa77e7b097c0667"
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- "/install-plugin.sh"
|
||||
|
|
@ -1216,6 +1216,7 @@ spec:
|
|||
automountServiceAccountToken: true
|
||||
terminationGracePeriodSeconds: 1
|
||||
hostNetwork: true
|
||||
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
|
|
@ -1374,7 +1375,7 @@ spec:
|
|||
type: Unconfined
|
||||
containers:
|
||||
- name: cilium-envoy
|
||||
image: "quay.io/cilium/cilium-envoy:v1.34.4-1754895458-68cffdfa568b6b226d70a7ef81fc65dda3b890bf@sha256:247e908700012f7ef56f75908f8c965215c26a27762f296068645eb55450bda2"
|
||||
image: "quay.io/cilium/cilium-envoy:v1.34.7-1757592137-1a52bb680a956879722f48c591a2ca90f7791324@sha256:7932d656b63f6f866b6732099d33355184322123cfe1182e6f05175a3bc2e0e0"
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /usr/bin/cilium-envoy-starter
|
||||
|
|
@ -1552,7 +1553,7 @@ spec:
|
|||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: cilium-operator
|
||||
image: "quay.io/cilium/operator-generic:v1.18.1@sha256:97f4553afa443465bdfbc1cc4927c93f16ac5d78e4dd2706736e7395382201bc"
|
||||
image: "quay.io/cilium/operator-generic:v1.18.2@sha256:cb4e4ffc5789fd5ff6a534e3b1460623df61cba00f5ea1c7b40153b5efb81805"
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- cilium-operator-generic
|
||||
|
|
@ -1633,6 +1634,8 @@ spec:
|
|||
operator: Exists
|
||||
- key: node.kubernetes.io/not-ready
|
||||
operator: Exists
|
||||
- key: node.cloudprovider.kubernetes.io/uninitialized
|
||||
operator: Exists
|
||||
- key: node.cilium.io/agent-not-ready
|
||||
operator: Exists
|
||||
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@ var (
|
|||
|
||||
const (
|
||||
// DefaultKubernetesVersion is the default Kubernetes version
|
||||
DefaultKubernetesVersion = "v1.34.0"
|
||||
DefaultKubernetesVersion = "v1.34.1"
|
||||
// NewestKubernetesVersion is the newest Kubernetes version to test against
|
||||
// NOTE: You may need to update coreDNS & etcd versions in pkg/minikube/bootstrapper/images/images.go
|
||||
NewestKubernetesVersion = "v1.34.0"
|
||||
NewestKubernetesVersion = "v1.34.1"
|
||||
// OldestKubernetesVersion is the oldest Kubernetes version to test against
|
||||
// TODO: upodate to 6 releases before from DefaultKubernetesVersion
|
||||
OldestKubernetesVersion = "v1.28.0"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,26 @@ package constants
|
|||
|
||||
var (
|
||||
KubeadmImages = map[string]map[string]string{
|
||||
"v1.34.1": {
|
||||
"coredns/coredns": "v1.12.1",
|
||||
"etcd": "3.6.4-0",
|
||||
"pause": "3.10.1",
|
||||
},
|
||||
"v1.33.5": {
|
||||
"coredns/coredns": "v1.12.0",
|
||||
"etcd": "3.5.21-0",
|
||||
"pause": "3.10",
|
||||
},
|
||||
"v1.32.9": {
|
||||
"coredns/coredns": "v1.11.3",
|
||||
"etcd": "3.5.16-0",
|
||||
"pause": "3.10",
|
||||
},
|
||||
"v1.31.13": {
|
||||
"coredns/coredns": "v1.11.3",
|
||||
"etcd": "3.5.15-0",
|
||||
"pause": "3.10",
|
||||
},
|
||||
"v1.34.0": {
|
||||
"coredns/coredns": "v1.12.1",
|
||||
"etcd": "3.6.4-0",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ package constants
|
|||
// ValidKubernetesVersions is a list of Kubernetes versions in order from newest to oldest
|
||||
// This is used when outputting Kubernetes versions and to select the latest patch version when unspecified
|
||||
var ValidKubernetesVersions = []string{
|
||||
"v1.34.1",
|
||||
"v1.34.0",
|
||||
"v1.34.0-rc.2",
|
||||
"v1.34.0-rc.1",
|
||||
|
|
@ -29,6 +30,7 @@ var ValidKubernetesVersions = []string{
|
|||
"v1.34.0-alpha.3",
|
||||
"v1.34.0-alpha.2",
|
||||
"v1.34.0-alpha.1",
|
||||
"v1.33.5",
|
||||
"v1.33.4",
|
||||
"v1.33.3",
|
||||
"v1.33.2",
|
||||
|
|
@ -40,6 +42,7 @@ var ValidKubernetesVersions = []string{
|
|||
"v1.33.0-alpha.3",
|
||||
"v1.33.0-alpha.2",
|
||||
"v1.33.0-alpha.1",
|
||||
"v1.32.9",
|
||||
"v1.32.8",
|
||||
"v1.32.7",
|
||||
"v1.32.6",
|
||||
|
|
@ -56,6 +59,7 @@ var ValidKubernetesVersions = []string{
|
|||
"v1.32.0-alpha.3",
|
||||
"v1.32.0-alpha.2",
|
||||
"v1.32.0-alpha.1",
|
||||
"v1.31.13",
|
||||
"v1.31.12",
|
||||
"v1.31.11",
|
||||
"v1.31.10",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"time"
|
||||
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/minikube/localpath"
|
||||
)
|
||||
|
||||
// Force download tests to run in serial.
|
||||
|
|
@ -33,7 +34,6 @@ func TestDownload(t *testing.T) {
|
|||
t.Run("PreloadDownloadPreventsMultipleDownload", testPreloadDownloadPreventsMultipleDownload)
|
||||
t.Run("ImageToCache", testImageToCache)
|
||||
t.Run("PreloadNotExists", testPreloadNotExists)
|
||||
t.Run("PreloadChecksumMismatch", testPreloadChecksumMismatch)
|
||||
t.Run("PreloadExistsCaching", testPreloadExistsCaching)
|
||||
t.Run("PreloadWithCachedSizeZero", testPreloadWithCachedSizeZero)
|
||||
}
|
||||
|
|
@ -48,7 +48,31 @@ func mockSleepDownload(downloadsCounter *int) func(src, dst string) error {
|
|||
}
|
||||
}
|
||||
|
||||
// point each subtest at an isolated MINIKUBE_HOME, pre-create the preload cache directory,
|
||||
//
|
||||
// and automatically restore the global download/preload mocks after each run.
|
||||
// Applied the helper across all download-related tests
|
||||
func setupTestMiniHome(t *testing.T) {
|
||||
t.Helper()
|
||||
tmpHome := t.TempDir()
|
||||
t.Setenv(localpath.MinikubeHome, tmpHome)
|
||||
if err := os.MkdirAll(targetDir(), 0o755); err != nil {
|
||||
t.Fatalf("failed to create preload cache dir: %v", err)
|
||||
}
|
||||
origDownloadMock := DownloadMock
|
||||
origCheckCache := checkCache
|
||||
origCheckPreloadExists := checkPreloadExists
|
||||
origGetChecksumGCS := getChecksumGCS
|
||||
t.Cleanup(func() {
|
||||
DownloadMock = origDownloadMock
|
||||
checkCache = origCheckCache
|
||||
checkPreloadExists = origCheckPreloadExists
|
||||
getChecksumGCS = origGetChecksumGCS
|
||||
})
|
||||
}
|
||||
|
||||
func testBinaryDownloadPreventsMultipleDownload(t *testing.T) {
|
||||
setupTestMiniHome(t)
|
||||
downloadNum := 0
|
||||
DownloadMock = mockSleepDownload(&downloadNum)
|
||||
|
||||
|
|
@ -79,6 +103,8 @@ func testBinaryDownloadPreventsMultipleDownload(t *testing.T) {
|
|||
}
|
||||
|
||||
func testPreloadDownloadPreventsMultipleDownload(t *testing.T) {
|
||||
setupTestMiniHome(t)
|
||||
|
||||
downloadNum := 0
|
||||
DownloadMock = mockSleepDownload(&downloadNum)
|
||||
f, err := os.CreateTemp("", "preload")
|
||||
|
|
@ -97,8 +123,7 @@ func testPreloadDownloadPreventsMultipleDownload(t *testing.T) {
|
|||
return os.Stat(f.Name())
|
||||
}
|
||||
checkPreloadExists = func(_, _, _ string, _ ...bool) bool { return true }
|
||||
getChecksum = func(_, _ string) ([]byte, error) { return []byte("check"), nil }
|
||||
ensureChecksumValid = func(_, _, _ string, _ []byte) error { return nil }
|
||||
getChecksumGCS = func(_, _ string) ([]byte, error) { return []byte("check"), nil }
|
||||
|
||||
var group sync.WaitGroup
|
||||
group.Add(2)
|
||||
|
|
@ -120,13 +145,13 @@ func testPreloadDownloadPreventsMultipleDownload(t *testing.T) {
|
|||
}
|
||||
|
||||
func testPreloadNotExists(t *testing.T) {
|
||||
setupTestMiniHome(t)
|
||||
downloadNum := 0
|
||||
DownloadMock = mockSleepDownload(&downloadNum)
|
||||
|
||||
checkCache = func(_ string) (fs.FileInfo, error) { return nil, fmt.Errorf("cache not found") }
|
||||
checkPreloadExists = func(_, _, _ string, _ ...bool) bool { return false }
|
||||
getChecksum = func(_, _ string) ([]byte, error) { return []byte("check"), nil }
|
||||
ensureChecksumValid = func(_, _, _ string, _ []byte) error { return nil }
|
||||
getChecksumGCS = func(_, _ string) ([]byte, error) { return []byte("check"), nil }
|
||||
|
||||
err := Preload(constants.DefaultKubernetesVersion, constants.Docker, "docker")
|
||||
if err != nil {
|
||||
|
|
@ -138,27 +163,8 @@ func testPreloadNotExists(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func testPreloadChecksumMismatch(t *testing.T) {
|
||||
downloadNum := 0
|
||||
DownloadMock = mockSleepDownload(&downloadNum)
|
||||
|
||||
checkCache = func(_ string) (fs.FileInfo, error) { return nil, fmt.Errorf("cache not found") }
|
||||
checkPreloadExists = func(_, _, _ string, _ ...bool) bool { return true }
|
||||
getChecksum = func(_, _ string) ([]byte, error) { return []byte("check"), nil }
|
||||
ensureChecksumValid = func(_, _, _ string, _ []byte) error {
|
||||
return fmt.Errorf("checksum mismatch")
|
||||
}
|
||||
|
||||
err := Preload(constants.DefaultKubernetesVersion, constants.Docker, "docker")
|
||||
expectedErrMsg := "checksum mismatch"
|
||||
if err == nil {
|
||||
t.Errorf("Expected error when checksum mismatches")
|
||||
} else if err.Error() != expectedErrMsg {
|
||||
t.Errorf("Expected error to be %s, got %s", expectedErrMsg, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func testImageToCache(t *testing.T) {
|
||||
setupTestMiniHome(t)
|
||||
downloadNum := 0
|
||||
DownloadMock = mockSleepDownload(&downloadNum)
|
||||
|
||||
|
|
@ -184,44 +190,72 @@ func testImageToCache(t *testing.T) {
|
|||
}
|
||||
|
||||
// Validates that preload existence checks correctly caches values retrieved by remote checks.
|
||||
// testPreloadExistsCaching verifies the caching semantics of PreloadExists when
|
||||
// the local cache is absent and remote existence checks are required.
|
||||
// In summary, this test enforces that:
|
||||
// - PreloadExists performs remote checks only on cache misses.
|
||||
// - Negative and positive results are cached per (k8sVersion, containerVersion, runtime) key.
|
||||
// - GitHub is only consulted when GCS reports the preload as not existing.
|
||||
// - Global state is correctly restored after the test.
|
||||
func testPreloadExistsCaching(t *testing.T) {
|
||||
setupTestMiniHome(t)
|
||||
checkCache = func(_ string) (fs.FileInfo, error) {
|
||||
return nil, fmt.Errorf("cache not found")
|
||||
}
|
||||
doesPreloadExist := false
|
||||
checkCalled := false
|
||||
checkRemotePreloadExists = func(_, _ string) bool {
|
||||
checkCalled = true
|
||||
gcsCheckCalls := 0
|
||||
ghCheckCalls := 0
|
||||
savedGCSCheck := checkRemotePreloadExistsGCS
|
||||
savedGHCheck := checkRemotePreloadExistsGitHub
|
||||
preloadStates = make(map[string]map[string]preloadState)
|
||||
checkRemotePreloadExistsGCS = func(_, _ string) bool {
|
||||
gcsCheckCalls++
|
||||
return doesPreloadExist
|
||||
}
|
||||
existence := PreloadExists("v1", "c1", "docker", true)
|
||||
if existence || !checkCalled {
|
||||
t.Errorf("Expected preload not to exist and a check to be performed. Existence: %v, Check: %v", existence, checkCalled)
|
||||
checkRemotePreloadExistsGitHub = func(_, _ string) bool {
|
||||
ghCheckCalls++
|
||||
return false
|
||||
}
|
||||
checkCalled = false
|
||||
t.Cleanup(func() {
|
||||
checkRemotePreloadExistsGCS = savedGCSCheck
|
||||
checkRemotePreloadExistsGitHub = savedGHCheck
|
||||
preloadStates = make(map[string]map[string]preloadState)
|
||||
})
|
||||
|
||||
existence := PreloadExists("v1", "c1", "docker", true)
|
||||
if existence || gcsCheckCalls != 1 || ghCheckCalls != 1 {
|
||||
t.Errorf("Expected preload not to exist and checks to be performed. Existence: %v, GCS Calls: %d, GH Calls: %d", existence, gcsCheckCalls, ghCheckCalls)
|
||||
}
|
||||
gcsCheckCalls = 0
|
||||
ghCheckCalls = 0
|
||||
existence = PreloadExists("v1", "c1", "docker", true)
|
||||
if existence || checkCalled {
|
||||
t.Errorf("Expected preload not to exist and no check to be performed. Existence: %v, Check: %v", existence, checkCalled)
|
||||
if existence || gcsCheckCalls != 0 || ghCheckCalls != 0 {
|
||||
t.Errorf("Expected preload not to exist and no checks to be performed. Existence: %v, GCS Calls: %d, GH Calls: %d", existence, gcsCheckCalls, ghCheckCalls)
|
||||
}
|
||||
doesPreloadExist = true
|
||||
checkCalled = false
|
||||
gcsCheckCalls = 0
|
||||
ghCheckCalls = 0
|
||||
existence = PreloadExists("v2", "c1", "docker", true)
|
||||
if !existence || !checkCalled {
|
||||
t.Errorf("Expected preload to exist and a check to be performed. Existence: %v, Check: %v", existence, checkCalled)
|
||||
if !existence || gcsCheckCalls != 1 || ghCheckCalls != 0 {
|
||||
t.Errorf("Expected preload to exist via GCS. Existence: %v, GCS Calls: %d, GH Calls: %d", existence, gcsCheckCalls, ghCheckCalls)
|
||||
}
|
||||
checkCalled = false
|
||||
gcsCheckCalls = 0
|
||||
ghCheckCalls = 0
|
||||
existence = PreloadExists("v2", "c2", "docker", true)
|
||||
if !existence || !checkCalled {
|
||||
t.Errorf("Expected preload to exist and a check to be performed. Existence: %v, Check: %v", existence, checkCalled)
|
||||
if !existence || gcsCheckCalls != 1 || ghCheckCalls != 0 {
|
||||
t.Errorf("Expected preload to exist via GCS for new runtime. Existence: %v, GCS Calls: %d, GH Calls: %d", existence, gcsCheckCalls, ghCheckCalls)
|
||||
}
|
||||
checkCalled = false
|
||||
gcsCheckCalls = 0
|
||||
ghCheckCalls = 0
|
||||
existence = PreloadExists("v2", "c2", "docker", true)
|
||||
if !existence || checkCalled {
|
||||
t.Errorf("Expected preload to exist and no check to be performed. Existence: %v, Check: %v", existence, checkCalled)
|
||||
if !existence || gcsCheckCalls != 0 || ghCheckCalls != 0 {
|
||||
t.Errorf("Expected preload to exist and no checks to be performed. Existence: %v, GCS Calls: %d, GH Calls: %d", existence, gcsCheckCalls, ghCheckCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func testPreloadWithCachedSizeZero(t *testing.T) {
|
||||
setupTestMiniHome(t)
|
||||
|
||||
downloadNum := 0
|
||||
DownloadMock = mockSleepDownload(&downloadNum)
|
||||
f, err := os.CreateTemp("", "preload")
|
||||
|
|
@ -231,8 +265,7 @@ func testPreloadWithCachedSizeZero(t *testing.T) {
|
|||
|
||||
checkCache = func(_ string) (fs.FileInfo, error) { return os.Stat(f.Name()) }
|
||||
checkPreloadExists = func(_, _, _ string, _ ...bool) bool { return true }
|
||||
getChecksum = func(_, _ string) ([]byte, error) { return []byte("check"), nil }
|
||||
ensureChecksumValid = func(_, _, _ string, _ []byte) error { return nil }
|
||||
getChecksumGCS = func(_, _ string) ([]byte, error) { return []byte("check"), nil }
|
||||
|
||||
if err := Preload(constants.DefaultKubernetesVersion, constants.Docker, "docker"); err != nil {
|
||||
t.Errorf("Expected no error with cached preload of size zero")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
Copyright 2025 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 gh provides helper utilities for interacting with the GitHub API
|
||||
package gh
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-github/v74/github"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// ReleaseAssets retrieves a GitHub release by tag from org/project.
|
||||
// Try to not call this too often. preferably cache and re-use. to avoid rate limits.
|
||||
func ReleaseAssets(org, project, tag string) ([]*github.ReleaseAsset, error) {
|
||||
ctx := context.Background()
|
||||
// Use an authenticated client when GITHUB_TOKEN is set to avoid low rate limits.
|
||||
httpClient := oauthClient(ctx, os.Getenv("GITHUB_TOKEN"))
|
||||
ghc := github.NewClient(httpClient)
|
||||
|
||||
rel, _, err := ghc.Repositories.GetReleaseByTag(ctx, org, project, tag)
|
||||
return rel.Assets, err
|
||||
}
|
||||
|
||||
// AssetSHA256 returns the SHA-256 digest for the asset with the given name
|
||||
// from the provided release assets from github API.
|
||||
// to avoid rate limits. encouraged to call pass results of ReleaseAssets here.
|
||||
func AssetSHA256(assetName string, assets []*github.ReleaseAsset) ([]byte, error) {
|
||||
for _, asset := range assets {
|
||||
if asset.GetName() != assetName {
|
||||
continue
|
||||
}
|
||||
d := asset.GetDigest() // e.g. "sha256:fdcb..."
|
||||
if d == "" {
|
||||
return []byte(""), fmt.Errorf("asset %q has no digest; id=%d url=%s", assetName, asset.GetID(), asset.GetBrowserDownloadURL())
|
||||
}
|
||||
const prefix = "sha256:"
|
||||
d = strings.TrimPrefix(d, prefix)
|
||||
return []byte(d), nil
|
||||
}
|
||||
return []byte(""), fmt.Errorf("asset %q not found", assetName)
|
||||
}
|
||||
|
||||
func oauthClient(ctx context.Context, token string) *http.Client {
|
||||
if token == "" {
|
||||
return nil // unauthenticated client (lower rate limit)
|
||||
}
|
||||
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
|
||||
return oauth2.NewClient(ctx, ts)
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
Copyright 2025 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 gh
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-github/v74/github"
|
||||
)
|
||||
|
||||
func TestAssetSHA256(t *testing.T) {
|
||||
t.Run("found_with_sha256_prefix", func(t *testing.T) {
|
||||
assets := []*github.ReleaseAsset{
|
||||
{
|
||||
Name: github.Ptr("minikube-linux-amd64"),
|
||||
Digest: github.Ptr("sha256:abcdef123456"),
|
||||
ID: github.Ptr(int64(101)),
|
||||
BrowserDownloadURL: github.Ptr("http://example/minikube-linux-amd64"),
|
||||
},
|
||||
}
|
||||
got, err := AssetSHA256("minikube-linux-amd64", assets)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if string(got) != "abcdef123456" {
|
||||
t.Fatalf("expected digest %q, got %q", "abcdef123456", string(got))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("found_without_prefix", func(t *testing.T) {
|
||||
assets := []*github.ReleaseAsset{
|
||||
{
|
||||
Name: github.Ptr("minikube-darwin-arm64"),
|
||||
Digest: github.Ptr("1234abcd"),
|
||||
ID: github.Ptr(int64(102)),
|
||||
BrowserDownloadURL: github.Ptr("http://example/minikube-darwin-arm64"),
|
||||
},
|
||||
}
|
||||
got, err := AssetSHA256("minikube-darwin-arm64", assets)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if string(got) != "1234abcd" {
|
||||
t.Fatalf("expected digest %q, got %q", "1234abcd", string(got))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("asset_missing_digest", func(t *testing.T) {
|
||||
assets := []*github.ReleaseAsset{
|
||||
{
|
||||
Name: github.Ptr("minikube-windows-amd64.exe"),
|
||||
Digest: github.Ptr(""),
|
||||
ID: github.Ptr(int64(103)),
|
||||
BrowserDownloadURL: github.Ptr("http://example/minikube-windows-amd64.exe"),
|
||||
},
|
||||
}
|
||||
_, err := AssetSHA256("minikube-windows-amd64.exe", assets)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "has no digest") {
|
||||
t.Fatalf("unexpected error message: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("asset_not_found", func(t *testing.T) {
|
||||
assets := []*github.ReleaseAsset{
|
||||
{
|
||||
Name: github.Ptr("unrelated"),
|
||||
Digest: github.Ptr("sha256:deadbeef"),
|
||||
ID: github.Ptr(int64(104)),
|
||||
BrowserDownloadURL: github.Ptr("http://example/unrelated"),
|
||||
},
|
||||
}
|
||||
_, err := AssetSHA256("missing", assets)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), `asset "missing" not found`) {
|
||||
t.Fatalf("unexpected error message: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@ package download
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
|
@ -34,6 +33,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/minikube/pkg/minikube/download/gh"
|
||||
"k8s.io/minikube/pkg/minikube/driver"
|
||||
"k8s.io/minikube/pkg/minikube/localpath"
|
||||
"k8s.io/minikube/pkg/minikube/out"
|
||||
|
|
@ -46,11 +46,27 @@ const (
|
|||
// NOTE: You may need to bump this version up when upgrading auxiliary docker images
|
||||
PreloadVersion = "v18"
|
||||
// PreloadBucket is the name of the GCS bucket where preloaded volume tarballs exist
|
||||
PreloadBucket = "minikube-preloaded-volume-tarballs"
|
||||
PreloadBucket = "minikube-preloaded-volume-tarballs"
|
||||
PreloadGitHubOrg = "kubernetes-sigs"
|
||||
PreloadGitHubRepo = "minikube-preloads"
|
||||
)
|
||||
|
||||
type preloadSource string
|
||||
|
||||
const (
|
||||
preloadSourceNone preloadSource = ""
|
||||
preloadSourceLocal preloadSource = "local"
|
||||
preloadSourceGCS preloadSource = "gcs"
|
||||
preloadSourceGitHub preloadSource = "github"
|
||||
)
|
||||
|
||||
type preloadState struct {
|
||||
exists bool
|
||||
source preloadSource
|
||||
}
|
||||
|
||||
var (
|
||||
preloadStates = make(map[string]map[string]bool)
|
||||
preloadStates = make(map[string]map[string]preloadState)
|
||||
)
|
||||
|
||||
// TarballName returns name of the tarball
|
||||
|
|
@ -67,42 +83,56 @@ func TarballName(k8sVersion, containerRuntime string) string {
|
|||
return fmt.Sprintf("preloaded-images-k8s-%s-%s-%s-%s-%s.tar.lz4", PreloadVersion, k8sVersion, containerRuntime, storageDriver, runtime.GOARCH)
|
||||
}
|
||||
|
||||
// returns the name of the checksum file
|
||||
func checksumName(k8sVersion, containerRuntime string) string {
|
||||
return fmt.Sprintf("%s.checksum", TarballName(k8sVersion, containerRuntime))
|
||||
}
|
||||
|
||||
// returns target dir for all cached items related to preloading
|
||||
func targetDir() string {
|
||||
return localpath.MakeMiniPath("cache", "preloaded-tarball")
|
||||
}
|
||||
|
||||
// PreloadChecksumPath returns the local path to the cached checksum file
|
||||
func PreloadChecksumPath(k8sVersion, containerRuntime string) string {
|
||||
return filepath.Join(targetDir(), checksumName(k8sVersion, containerRuntime))
|
||||
}
|
||||
|
||||
// TarballPath returns the local path to the cached preload tarball
|
||||
func TarballPath(k8sVersion, containerRuntime string) string {
|
||||
return filepath.Join(targetDir(), TarballName(k8sVersion, containerRuntime))
|
||||
}
|
||||
|
||||
// remoteTarballURL returns the URL for the remote tarball in GCS
|
||||
func remoteTarballURL(k8sVersion, containerRuntime string) string {
|
||||
// remoteTarballURLGCS returns the URL for the remote tarball in GCS
|
||||
func remoteTarballURLGCS(k8sVersion, containerRuntime string) string {
|
||||
return fmt.Sprintf("https://%s/%s/%s/%s/%s", downloadHost, PreloadBucket, PreloadVersion, k8sVersion, TarballName(k8sVersion, containerRuntime))
|
||||
}
|
||||
|
||||
func setPreloadState(k8sVersion, containerRuntime string, value bool) {
|
||||
cRuntimes, ok := preloadStates[k8sVersion]
|
||||
if !ok {
|
||||
cRuntimes = make(map[string]bool)
|
||||
preloadStates[k8sVersion] = cRuntimes
|
||||
}
|
||||
cRuntimes[containerRuntime] = value
|
||||
// remoteTarballURLGitHub returns the URL for the remote tarball hosted on GitHub releases
|
||||
func remoteTarballURLGitHub(k8sVersion, containerRuntime string) string {
|
||||
return fmt.Sprintf("https://github.com/%s/%s/releases/download/%s/%s", PreloadGitHubOrg, PreloadGitHubRepo, PreloadVersion, TarballName(k8sVersion, containerRuntime))
|
||||
}
|
||||
|
||||
var checkRemotePreloadExists = func(k8sVersion, containerRuntime string) bool {
|
||||
url := remoteTarballURL(k8sVersion, containerRuntime)
|
||||
func remoteTarballURL(k8sVersion, containerRuntime string, source preloadSource) string {
|
||||
switch source {
|
||||
case preloadSourceGitHub:
|
||||
return remoteTarballURLGitHub(k8sVersion, containerRuntime)
|
||||
case preloadSourceGCS:
|
||||
return remoteTarballURLGCS(k8sVersion, containerRuntime)
|
||||
default:
|
||||
return string(preloadSourceNone)
|
||||
}
|
||||
}
|
||||
|
||||
func setPreloadState(k8sVersion, containerRuntime string, state preloadState) {
|
||||
cRuntimes, ok := preloadStates[k8sVersion]
|
||||
if !ok {
|
||||
cRuntimes = make(map[string]preloadState)
|
||||
preloadStates[k8sVersion] = cRuntimes
|
||||
}
|
||||
cRuntimes[containerRuntime] = state
|
||||
}
|
||||
|
||||
func getPreloadState(k8sVersion, containerRuntime string) (preloadState, bool) {
|
||||
if cRuntimes, ok := preloadStates[k8sVersion]; ok {
|
||||
if state, ok := cRuntimes[containerRuntime]; ok {
|
||||
return state, true
|
||||
}
|
||||
}
|
||||
return preloadState{}, false
|
||||
}
|
||||
|
||||
func remotePreloadExists(url string) bool {
|
||||
resp, err := http.Head(url)
|
||||
if err != nil {
|
||||
klog.Warningf("%s fetch error: %v", url, err)
|
||||
|
|
@ -119,6 +149,28 @@ var checkRemotePreloadExists = func(k8sVersion, containerRuntime string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// this is a function variable so it can be overridden in tests
|
||||
var checkRemotePreloadExistsGCS = func(k8sVersion, containerRuntime string) bool {
|
||||
url := remoteTarballURLGCS(k8sVersion, containerRuntime)
|
||||
return remotePreloadExists(url)
|
||||
}
|
||||
|
||||
// this is a function variable so it can be overridden in tests
|
||||
var checkRemotePreloadExistsGitHub = func(k8sVersion, containerRuntime string) bool {
|
||||
url := remoteTarballURLGitHub(k8sVersion, containerRuntime)
|
||||
return remotePreloadExists(url)
|
||||
}
|
||||
|
||||
// PreloadExistsGCS returns true if there is a preloaded tarball in GCS that can be used
|
||||
func PreloadExistsGCS(k8sVersion, containerRuntime string) bool {
|
||||
return checkRemotePreloadExistsGCS(k8sVersion, containerRuntime)
|
||||
}
|
||||
|
||||
// PreloadExistsGH returns true if there is a preloaded tarball in GitHub releases that can be used
|
||||
func PreloadExistsGH(k8sVersion, containerRuntime string) bool {
|
||||
return checkRemotePreloadExistsGitHub(k8sVersion, containerRuntime)
|
||||
}
|
||||
|
||||
// PreloadExists returns true if there is a preloaded tarball that can be used
|
||||
func PreloadExists(k8sVersion, containerRuntime, driverName string, forcePreload ...bool) bool {
|
||||
// TODO (#8166): Get rid of the need for this and viper at all
|
||||
|
|
@ -136,21 +188,30 @@ func PreloadExists(k8sVersion, containerRuntime, driverName string, forcePreload
|
|||
}
|
||||
|
||||
// If the preload existence is cached, just return that value.
|
||||
if preloadState, ok := preloadStates[k8sVersion][containerRuntime]; ok {
|
||||
return preloadState
|
||||
if state, ok := getPreloadState(k8sVersion, containerRuntime); ok {
|
||||
return state.exists
|
||||
}
|
||||
|
||||
// Omit remote check if tarball exists locally
|
||||
targetPath := TarballPath(k8sVersion, containerRuntime)
|
||||
if f, err := checkCache(targetPath); err == nil && f.Size() != 0 {
|
||||
klog.Infof("Found local preload: %s", targetPath)
|
||||
setPreloadState(k8sVersion, containerRuntime, true)
|
||||
setPreloadState(k8sVersion, containerRuntime, preloadState{exists: true, source: preloadSourceLocal})
|
||||
return true
|
||||
}
|
||||
|
||||
existence := checkRemotePreloadExists(k8sVersion, containerRuntime)
|
||||
setPreloadState(k8sVersion, containerRuntime, existence)
|
||||
return existence
|
||||
if PreloadExistsGCS(k8sVersion, containerRuntime) {
|
||||
setPreloadState(k8sVersion, containerRuntime, preloadState{exists: true, source: preloadSourceGCS})
|
||||
return true
|
||||
}
|
||||
|
||||
if PreloadExistsGH(k8sVersion, containerRuntime) {
|
||||
setPreloadState(k8sVersion, containerRuntime, preloadState{exists: true, source: preloadSourceGitHub})
|
||||
return true
|
||||
}
|
||||
|
||||
setPreloadState(k8sVersion, containerRuntime, preloadState{exists: false, source: preloadSourceNone})
|
||||
return false
|
||||
}
|
||||
|
||||
var checkPreloadExists = PreloadExists
|
||||
|
|
@ -180,31 +241,34 @@ func Preload(k8sVersion, containerRuntime, driverName string) error {
|
|||
}
|
||||
|
||||
out.Step(style.FileDownload, "Downloading Kubernetes {{.version}} preload ...", out.V{"version": k8sVersion})
|
||||
url := remoteTarballURL(k8sVersion, containerRuntime)
|
||||
state, ok := getPreloadState(k8sVersion, containerRuntime)
|
||||
source := preloadSourceNone
|
||||
if ok && state.source != preloadSourceNone {
|
||||
source = state.source
|
||||
}
|
||||
url := remoteTarballURL(k8sVersion, containerRuntime, source)
|
||||
var checksum []byte
|
||||
var chksErr error
|
||||
checksum, chksErr = getChecksum(source, k8sVersion, containerRuntime)
|
||||
|
||||
checksum, err := getChecksum(k8sVersion, containerRuntime)
|
||||
var realPath string
|
||||
if err != nil {
|
||||
klog.Warningf("No checksum for preloaded tarball for k8s version %s: %v", k8sVersion, err)
|
||||
if chksErr != nil {
|
||||
klog.Warningf("No checksum for preloaded tarball for k8s version %s: %v", k8sVersion, chksErr)
|
||||
realPath = targetPath
|
||||
tmp, err := os.CreateTemp(targetDir(), TarballName(k8sVersion, containerRuntime)+".*")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "tempfile")
|
||||
}
|
||||
targetPath = tmp.Name()
|
||||
} else if checksum != nil {
|
||||
// add URL parameter for go-getter to automatically verify the checksum
|
||||
url += fmt.Sprintf("?checksum=md5:%s", hex.EncodeToString(checksum))
|
||||
} else if checksum != nil { // add URL parameter for go-getter to automatically verify the checksum
|
||||
url = addChecksumToURL(url, source, checksum)
|
||||
}
|
||||
|
||||
if err := download(url, targetPath); err != nil {
|
||||
return errors.Wrapf(err, "download failed: %s", url)
|
||||
}
|
||||
|
||||
if err := ensureChecksumValid(k8sVersion, containerRuntime, targetPath, checksum); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// to avoid partial/corrupt files in final dest. only rename tmp if download didn't error out.
|
||||
if realPath != "" {
|
||||
klog.Infof("renaming tempfile to %s ...", TarballName(k8sVersion, containerRuntime))
|
||||
err := os.Rename(targetPath, realPath)
|
||||
|
|
@ -214,10 +278,23 @@ func Preload(k8sVersion, containerRuntime, driverName string) error {
|
|||
}
|
||||
|
||||
// If the download was successful, mark off that the preload exists in the cache.
|
||||
setPreloadState(k8sVersion, containerRuntime, true)
|
||||
setPreloadState(k8sVersion, containerRuntime, preloadState{exists: true, source: source})
|
||||
return nil
|
||||
}
|
||||
|
||||
// addChecksumToURL appends the checksum query parameter to the URL for go-getter (so it can verify before/after download)
|
||||
func addChecksumToURL(url string, ps preloadSource, checksum []byte) string {
|
||||
switch ps {
|
||||
case preloadSourceGCS: // GCS API gives us MD5 checksums only
|
||||
url += fmt.Sprintf("?checksum=md5:%s", hex.EncodeToString(checksum))
|
||||
klog.Infof("Got checksum from GCS API %q", hex.EncodeToString(checksum))
|
||||
case preloadSourceGitHub: // GCS API gives us sha256
|
||||
url += fmt.Sprintf("?checksum=sha256:%s", checksum)
|
||||
klog.Infof("Got checksum from Github API %q", checksum)
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
func getStorageAttrs(name string) (*storage.ObjectAttrs, error) {
|
||||
ctx := context.Background()
|
||||
client, err := storage.NewClient(ctx, option.WithoutAuthentication())
|
||||
|
|
@ -231,9 +308,9 @@ func getStorageAttrs(name string) (*storage.ObjectAttrs, error) {
|
|||
return attrs, nil
|
||||
}
|
||||
|
||||
// getChecksum returns the MD5 checksum of the preload tarball
|
||||
var getChecksum = func(k8sVersion, containerRuntime string) ([]byte, error) {
|
||||
klog.Infof("getting checksum for %s ...", TarballName(k8sVersion, containerRuntime))
|
||||
// getChecksumGCS returns the MD5 checksum of the preload tarball
|
||||
var getChecksumGCS = func(k8sVersion, containerRuntime string) ([]byte, error) {
|
||||
klog.Infof("getting checksum for %s from gcs api...", TarballName(k8sVersion, containerRuntime))
|
||||
filename := fmt.Sprintf("%s/%s/%s", PreloadVersion, k8sVersion, TarballName(k8sVersion, containerRuntime))
|
||||
attrs, err := getStorageAttrs(filename)
|
||||
if err != nil {
|
||||
|
|
@ -242,46 +319,25 @@ var getChecksum = func(k8sVersion, containerRuntime string) ([]byte, error) {
|
|||
return attrs.MD5, nil
|
||||
}
|
||||
|
||||
// saveChecksumFile saves the checksum to a local file for later verification
|
||||
func saveChecksumFile(k8sVersion, containerRuntime string, checksum []byte) error {
|
||||
klog.Infof("saving checksum for %s ...", TarballName(k8sVersion, containerRuntime))
|
||||
return os.WriteFile(PreloadChecksumPath(k8sVersion, containerRuntime), checksum, 0o644)
|
||||
// getChecksumGithub returns the SHA256 checksum of the preload tarball
|
||||
var getChecksumGithub = func(k8sVersion, containerRuntime string) ([]byte, error) {
|
||||
klog.Infof("getting checksum for %s from github api...", TarballName(k8sVersion, containerRuntime))
|
||||
assets, err := gh.ReleaseAssets(PreloadGitHubRepo, PreloadGitHubRepo, PreloadVersion)
|
||||
if err != nil { // could not find release or rate limited
|
||||
return nil, err
|
||||
}
|
||||
return gh.AssetSHA256(TarballName(k8sVersion, containerRuntime), assets)
|
||||
}
|
||||
|
||||
// verifyChecksum returns true if the checksum of the local binary matches
|
||||
// the checksum of the remote binary
|
||||
func verifyChecksum(k8sVersion, containerRuntime, binaryPath string) error {
|
||||
klog.Infof("verifying checksum of %s ...", binaryPath)
|
||||
// get md5 checksum of tarball path
|
||||
contents, err := os.ReadFile(binaryPath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "reading tarball")
|
||||
func getChecksum(ps preloadSource, k8sVersion, containerRuntime string) ([]byte, error) {
|
||||
switch ps {
|
||||
case preloadSourceGCS:
|
||||
return getChecksumGCS(k8sVersion, containerRuntime)
|
||||
case preloadSourceGitHub:
|
||||
return getChecksumGithub(k8sVersion, containerRuntime)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown preload source: %s", ps)
|
||||
}
|
||||
checksum := md5.Sum(contents)
|
||||
|
||||
remoteChecksum, err := os.ReadFile(PreloadChecksumPath(k8sVersion, containerRuntime))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "reading checksum file")
|
||||
}
|
||||
|
||||
// create a slice of checksum, which is [16]byte
|
||||
if string(remoteChecksum) != string(checksum[:]) {
|
||||
return fmt.Errorf("checksum of %s does not match remote checksum (%s != %s)", binaryPath, string(remoteChecksum), string(checksum[:]))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureChecksumValid saves and verifies local binary checksum matches remote binary checksum
|
||||
var ensureChecksumValid = func(k8sVersion, containerRuntime, targetPath string, checksum []byte) error {
|
||||
if err := saveChecksumFile(k8sVersion, containerRuntime, checksum); err != nil {
|
||||
return errors.Wrap(err, "saving checksum file")
|
||||
}
|
||||
|
||||
if err := verifyChecksum(k8sVersion, containerRuntime, targetPath); err != nil {
|
||||
return errors.Wrap(err, "verify")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUpOlderPreloads deletes preload files belonging to older minikube versions
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ minikube start [flags]
|
|||
--interactive Allow user prompts for more information (default true)
|
||||
--iso-url strings Locations to fetch the minikube ISO from. The list depends on the machine architecture.
|
||||
--keep-context This will keep the existing kubectl context and will create a minikube context.
|
||||
--kubernetes-version string The Kubernetes version that the minikube VM will use (ex: v1.2.3, 'stable' for v1.34.0, 'latest' for v1.34.0). Defaults to 'stable'.
|
||||
--kubernetes-version string The Kubernetes version that the minikube VM will use (ex: v1.2.3, 'stable' for v1.34.1, 'latest' for v1.34.1). Defaults to 'stable'.
|
||||
--kvm-gpu Enable experimental NVIDIA GPU support in minikube
|
||||
--kvm-hidden Hide the hypervisor signature from the guest in minikube (kvm2 driver only)
|
||||
--kvm-network string The KVM default network name. (kvm2 driver only) (default "default")
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
|
@ -235,23 +234,19 @@ func TestDownloadOnlyKic(t *testing.T) {
|
|||
|
||||
// Make sure the downloaded image tarball exists
|
||||
tarball := download.TarballPath(constants.DefaultKubernetesVersion, cRuntime)
|
||||
contents, err := os.ReadFile(tarball)
|
||||
fi, err := os.Stat(tarball)
|
||||
if err != nil {
|
||||
t.Errorf("failed to read tarball file %q: %v", tarball, err)
|
||||
t.Errorf("expected tarball file %q to exist, but got error: %v", tarball, err)
|
||||
} else {
|
||||
const minSize int64 = 200 * 1024 * 1024 // 200 MB
|
||||
if fi.Size() <= minSize {
|
||||
t.Errorf("expected tarball file %q to be larger than 200MB (>%d bytes), got %d bytes", tarball, minSize, fi.Size())
|
||||
}
|
||||
}
|
||||
|
||||
if arm64Platform() {
|
||||
t.Skip("Skip for arm64 platform. See https://github.com/kubernetes/minikube/issues/10144")
|
||||
}
|
||||
// Make sure it has the correct checksum
|
||||
checksum := md5.Sum(contents)
|
||||
remoteChecksum, err := os.ReadFile(download.PreloadChecksumPath(constants.DefaultKubernetesVersion, cRuntime))
|
||||
if err != nil {
|
||||
t.Errorf("failed to read checksum file %q : %v", download.PreloadChecksumPath(constants.DefaultKubernetesVersion, cRuntime), err)
|
||||
}
|
||||
if string(remoteChecksum) != string(checksum[:]) {
|
||||
t.Errorf("failed to verify checksum. checksum of %q does not match remote checksum (%q != %q)", tarball, string(remoteChecksum), string(checksum[:]))
|
||||
}
|
||||
}
|
||||
|
||||
// createSha256File is a helper function which creates sha256 checksum file from given file
|
||||
|
|
|
|||
Loading…
Reference in New Issue