From 8c28ca4202eabe0ab3e2fc55524dbd02221267cc Mon Sep 17 00:00:00 2001 From: Zhongcheng Lao Date: Tue, 5 Mar 2019 00:32:09 +0800 Subject: [PATCH 1/2] Select an accessible image repository for some users Now it's possible to fallback to a mirror site for those users who cannot access the main k8s.gcr.io repository. You may also set the --image-mirror-country to forcibly use a known local mirror in the specific location. Signed-off-by: Zhongcheng Lao --- cmd/minikube/cmd/start.go | 87 ++++++++++++++++++++++++++++- pkg/minikube/constants/constants.go | 6 ++ 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index 8a3b56530be3..9692e81e25e5 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -34,6 +34,9 @@ import ( "github.com/docker/machine/libmachine/host" "github.com/docker/machine/libmachine/state" "github.com/golang/glog" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/spf13/cobra" "github.com/spf13/viper" "golang.org/x/sync/errgroup" @@ -77,6 +80,7 @@ const ( dnsDomain = "dns-domain" serviceCIDR = "service-cluster-ip-range" imageRepository = "image-repository" + imageMirrorCountry = "image-mirror-country" mountString = "mount-string" disableDriverMounts = "disable-driver-mounts" cacheImages = "cache-images" @@ -128,6 +132,7 @@ func init() { startCmd.Flags().StringSliceVar(&insecureRegistry, "insecure-registry", nil, "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.") startCmd.Flags().StringSliceVar(®istryMirror, "registry-mirror", nil, "Registry mirrors to pass to the Docker daemon") startCmd.Flags().String(imageRepository, "", "Alternative image repository to pull docker images from. This can be used when you have limited access to gcr.io. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers") + startCmd.Flags().String(imageMirrorCountry, "", "Country code of the image mirror to be used. Leave empty to use the global one. For Chinese mainland users, set it to cn") startCmd.Flags().String(containerRuntime, "docker", "The container runtime to be used (docker, crio, containerd)") startCmd.Flags().String(criSocket, "", "The cri socket path to be used") startCmd.Flags().String(kubernetesVersion, constants.DefaultKubernetesVersion, "The kubernetes version that the minikube VM will use (ex: v1.2.3)") @@ -172,8 +177,30 @@ func runStart(cmd *cobra.Command, args []string) { exit.WithError("Failed to generate config", err) } + if config.KubernetesConfig.ImageRepository == "" { + console.OutStyle("connectivity", "checking main repository and mirrors for images") + found, repository, err := selectImageRepository(config) + if err != nil { + exit.WithError("Failed to check main repository and mirrors for images for images", err) + } + + if !found { + if repository == "" { + exit.WithCode(exit.Failure, "None of known repositories is accessible. Consider specifying an alternative image repository with --image-repository flag") + } else { + console.Warning("None of known repositories in your location is accessible. Use %s as fallback.", repository) + } + } + + config.KubernetesConfig.ImageRepository = repository + } + + if config.KubernetesConfig.ImageRepository != "" { + console.OutStyle("success", "using image repository %s", config.KubernetesConfig.ImageRepository) + } + var cacheGroup errgroup.Group - beginCacheImages(&cacheGroup, k8sVersion) + beginCacheImages(&cacheGroup, config.KubernetesConfig.ImageRepository, k8sVersion) // Abstraction leakage alert: startHost requires the config to be saved, to satistfy pkg/provision/buildroot. // Hence, saveConfig must be called before startHost, and again afterwards when we know the IP. @@ -228,6 +255,60 @@ func runStart(cmd *cobra.Command, args []string) { console.OutStyle("ready", "Done! Thank you for using minikube!") } +func selectImageRepository(config cfg.Config) (bool, string, error) { + mirrorCountry := strings.ToLower(viper.GetString(imageMirrorCountry)) + repos := constants.ImageRepositories + var countries []string + if mirrorCountry != "" { + _, ok := repos[mirrorCountry] + if !ok { + return false, "", fmt.Errorf("invalid image mirror country code: %s", mirrorCountry) + } + countries = []string{mirrorCountry, ""} + + } else { + // make sure global is preferred + countries = []string{""} + for k := range repos { + if k != "" { + countries = append(countries, k) + } + } + } + + checkRepository := func(repo string) error { + podInfraContainerImage, _ := constants.GetKubeadmCachedImages(repo, config.KubernetesConfig.KubernetesVersion) + + ref, err := name.ParseReference(podInfraContainerImage, name.WeakValidation) + if err != nil { + return err + } + + _, err = remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain)) + return err + } + + for _, code := range countries { + localRepos := repos[code] + for _, repo := range localRepos { + err := checkRepository(repo) + if err == nil { + return true, repo, nil + } + } + } + + if mirrorCountry != "" { + if localRepos, ok := constants.ImageRepositories[mirrorCountry]; ok && len(localRepos) > 0 { + // none of the mirrors in the given location is available + // use the first as fallback + return false, localRepos[0], nil + } + } + + return false, "", nil +} + // validateConfig validates the supplied configuration against known bad combinations func validateConfig() { diskSizeMB := pkgutil.CalculateDiskSizeInMB(viper.GetString(humanReadableDiskSize)) @@ -241,13 +322,13 @@ func validateConfig() { } // beginCacheImages caches Docker images in the background -func beginCacheImages(g *errgroup.Group, k8sVersion string) { +func beginCacheImages(g *errgroup.Group, imageRepository string, k8sVersion string) { if !viper.GetBool(cacheImages) { return } console.OutStyle("caching", "Downloading Kubernetes %s images in the background ...", k8sVersion) g.Go(func() error { - return machine.CacheImagesForBootstrapper(viper.GetString(imageRepository), k8sVersion, viper.GetString(cmdcfg.Bootstrapper)) + return machine.CacheImagesForBootstrapper(imageRepository, k8sVersion, viper.GetString(cmdcfg.Bootstrapper)) }) } diff --git a/pkg/minikube/constants/constants.go b/pkg/minikube/constants/constants.go index 3972ea318ebf..931bc30f7914 100644 --- a/pkg/minikube/constants/constants.go +++ b/pkg/minikube/constants/constants.go @@ -205,6 +205,12 @@ const ( DefaultMountVersion = "9p2000.L" ) +// ImageRepositories contains all known image repositories +var ImageRepositories = map[string][]string{ + "": {""}, // global + "cn": {"registry.cn-hangzhou.aliyuncs.com/google_containers"}, +} + // GetKubernetesReleaseURL gets the location of a kubernetes client func GetKubernetesReleaseURL(binaryName, version string) string { return fmt.Sprintf("https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/%s/%s", version, runtime.GOARCH, binaryName) From 144f6513856750901a4bc2f9f4e3701e8801c696 Mon Sep 17 00:00:00 2001 From: Zhongcheng Lao Date: Thu, 2 May 2019 18:49:49 +0800 Subject: [PATCH 2/2] Triggers repository detection only when --image-repository=auto --- cmd/minikube/cmd/start.go | 95 ++++++++++++++--------------- pkg/minikube/constants/constants.go | 4 +- 2 files changed, 49 insertions(+), 50 deletions(-) diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index b798c6c185ea..466fb10e5a4a 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -133,7 +133,7 @@ func init() { startCmd.Flags().String(serviceCIDR, pkgutil.DefaultServiceCIDR, "The CIDR to be used for service cluster IPs.") startCmd.Flags().StringSliceVar(&insecureRegistry, "insecure-registry", nil, "Insecure Docker registries to pass to the Docker daemon. The default service CIDR range will automatically be added.") startCmd.Flags().StringSliceVar(®istryMirror, "registry-mirror", nil, "Registry mirrors to pass to the Docker daemon") - startCmd.Flags().String(imageRepository, "", "Alternative image repository to pull docker images from. This can be used when you have limited access to gcr.io. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers") + startCmd.Flags().String(imageRepository, "", "Alternative image repository to pull docker images from. This can be used when you have limited access to gcr.io. Set it to \"auto\" to let minikube decide one for you. For Chinese mainland users, you may use local gcr.io mirrors such as registry.cn-hangzhou.aliyuncs.com/google_containers") startCmd.Flags().String(imageMirrorCountry, "", "Country code of the image mirror to be used. Leave empty to use the global one. For Chinese mainland users, set it to cn") startCmd.Flags().String(containerRuntime, "docker", "The container runtime to be used (docker, crio, containerd)") startCmd.Flags().String(criSocket, "", "The cri socket path to be used") @@ -181,28 +181,6 @@ func runStart(cmd *cobra.Command, args []string) { exit.WithError("Failed to generate config", err) } - if config.KubernetesConfig.ImageRepository == "" { - console.OutStyle("connectivity", "checking main repository and mirrors for images") - found, repository, err := selectImageRepository(config) - if err != nil { - exit.WithError("Failed to check main repository and mirrors for images for images", err) - } - - if !found { - if repository == "" { - exit.WithCode(exit.Failure, "None of known repositories is accessible. Consider specifying an alternative image repository with --image-repository flag") - } else { - console.Warning("None of known repositories in your location is accessible. Use %s as fallback.", repository) - } - } - - config.KubernetesConfig.ImageRepository = repository - } - - if config.KubernetesConfig.ImageRepository != "" { - console.OutStyle("success", "using image repository %s", config.KubernetesConfig.ImageRepository) - } - // For non-"none", the ISO is required to boot, so block until it is downloaded if viper.GetString(vmDriver) != constants.DriverNone { if err := cluster.CacheISO(config.MachineConfig); err != nil { @@ -293,29 +271,34 @@ func showKubectlConnectInfo(kubeconfig *pkgutil.KubeConfigSetup) { } } -func selectImageRepository(config cfg.Config) (bool, string, error) { - mirrorCountry := strings.ToLower(viper.GetString(imageMirrorCountry)) - repos := constants.ImageRepositories - var countries []string +func selectImageRepository(mirrorCountry string, k8sVersion string) (bool, string, error) { + var tryCountries []string + var fallback string + if mirrorCountry != "" { - _, ok := repos[mirrorCountry] - if !ok { + localRepos, ok := constants.ImageRepositories[mirrorCountry] + if !ok || len(localRepos) <= 0 { return false, "", fmt.Errorf("invalid image mirror country code: %s", mirrorCountry) } - countries = []string{mirrorCountry, ""} + + tryCountries = append(tryCountries, mirrorCountry) + + // we'll use the first repository as fallback + // when none of the mirrors in the given location is available + fallback = localRepos[0] } else { - // make sure global is preferred - countries = []string{""} - for k := range repos { - if k != "" { - countries = append(countries, k) + // always make sure global is preferred + tryCountries = append(tryCountries, "global") + for k := range constants.ImageRepositories { + if strings.ToLower(k) != "global" { + tryCountries = append(tryCountries, k) } } } checkRepository := func(repo string) error { - podInfraContainerImage, _ := constants.GetKubeadmCachedImages(repo, config.KubernetesConfig.KubernetesVersion) + podInfraContainerImage, _ := constants.GetKubeadmCachedImages(repo, k8sVersion) ref, err := name.ParseReference(podInfraContainerImage, name.WeakValidation) if err != nil { @@ -326,8 +309,8 @@ func selectImageRepository(config cfg.Config) (bool, string, error) { return err } - for _, code := range countries { - localRepos := repos[code] + for _, code := range tryCountries { + localRepos := constants.ImageRepositories[code] for _, repo := range localRepos { err := checkRepository(repo) if err == nil { @@ -336,15 +319,7 @@ func selectImageRepository(config cfg.Config) (bool, string, error) { } } - if mirrorCountry != "" { - if localRepos, ok := constants.ImageRepositories[mirrorCountry]; ok && len(localRepos) > 0 { - // none of the mirrors in the given location is available - // use the first as fallback - return false, localRepos[0], nil - } - } - - return false, "", nil + return false, fallback, nil } // validateConfig validates the supplied configuration against known bad combinations @@ -417,6 +392,30 @@ func generateConfig(cmd *cobra.Command, k8sVersion string) (cfg.Config, error) { } } + repository := viper.GetString(imageRepository) + mirrorCountry := strings.ToLower(viper.GetString(imageMirrorCountry)) + if strings.ToLower(repository) == "auto" || mirrorCountry != "" { + console.OutStyle("connectivity", "checking main repository and mirrors for images") + found, autoSelectedRepository, err := selectImageRepository(mirrorCountry, k8sVersion) + if err != nil { + exit.WithError("Failed to check main repository and mirrors for images for images", err) + } + + if !found { + if autoSelectedRepository == "" { + exit.WithCode(exit.Failure, "None of known repositories is accessible. Consider specifying an alternative image repository with --image-repository flag") + } else { + console.Warning("None of known repositories in your location is accessible. Use %s as fallback.", autoSelectedRepository) + } + } + + repository = autoSelectedRepository + } + + if repository != "" { + console.OutStyle("success", "using image repository %s", repository) + } + cfg := cfg.Config{ MachineConfig: cfg.MachineConfig{ MinikubeISO: viper.GetString(isoURL), @@ -457,7 +456,7 @@ func generateConfig(cmd *cobra.Command, k8sVersion string) (cfg.Config, error) { CRISocket: viper.GetString(criSocket), NetworkPlugin: selectedNetworkPlugin, ServiceCIDR: viper.GetString(serviceCIDR), - ImageRepository: viper.GetString(imageRepository), + ImageRepository: repository, ExtraOptions: extraOptions, ShouldLoadCachedImages: viper.GetBool(cacheImages), EnableDefaultCNI: selectedEnableDefaultCNI, diff --git a/pkg/minikube/constants/constants.go b/pkg/minikube/constants/constants.go index 4df9352ce8ec..0cd03b81da75 100644 --- a/pkg/minikube/constants/constants.go +++ b/pkg/minikube/constants/constants.go @@ -213,8 +213,8 @@ const ( // ImageRepositories contains all known image repositories var ImageRepositories = map[string][]string{ - "": {""}, // global - "cn": {"registry.cn-hangzhou.aliyuncs.com/google_containers"}, + "global": {""}, + "cn": {"registry.cn-hangzhou.aliyuncs.com/google_containers"}, } // GetKubernetesReleaseURL gets the location of a kubernetes client