提交 4cff7900 编写于 作者: P Priya Wadhwa

fixed merge conflict

......@@ -64,11 +64,6 @@ jobs:
echo workspace $GITHUB_WORKSPACE
echo "end of debug stuff"
echo $(which jq)
# iso needs golang 1.11.3
- uses: actions/setup-go@v2
with:
go-version: '1.11.13'
stable: true
- name: Build ISO
run: |
whoami
......@@ -146,4 +141,4 @@ jobs:
if [ "$numFail" -gt 0 ];then echo "*** $numFail Failed ***";exit 2;fi
if [ "$numPass" -eq 0 ];then echo "*** 0 Passed! ***";exit 2;fi
if [ "$numPass" -lt 32 ];then echo "*** Failed to pass at least 32 ! ***";exit 2;fi
if [ "$numPass" -eq 0 ];then echo "*** Passed! ***";exit 0;fi
\ No newline at end of file
if [ "$numPass" -eq 0 ];then echo "*** Passed! ***";exit 0;fi
......@@ -412,7 +412,7 @@ jobs:
echo "---------------- ${numFail} Failures :( ----------------------------"
echo $failedTests
echo "-------------------------------------------------------"
If ($numFail -gt 0){ exit 2 }
If ($numFail -gt 0){ exit 2 }
If ($numPass -eq 0){ exit 2 }
If ($numPass -lt 33){ exit 2 }
If ($numFail -eq 0){ exit 0 }
......@@ -429,7 +429,7 @@ jobs:
shell: powershell
run: |
echo $env:computerName
ls
ls
$ErrorActionPreference = "SilentlyContinue"
cd minikube_binaries
ls
......@@ -464,14 +464,14 @@ jobs:
$docker_running = $?
}
Write-Output "Docker is running"
docker system prune -f
docker system prune -f
- name: Info
continue-on-error: true
shell: powershell
run: |
$ErrorActionPreference = "SilentlyContinue"
cd minikube_binaries
ls
ls
echo $env:computername
Get-WmiObject -class Win32_ComputerSystem
- uses: actions/setup-go@v2
......@@ -483,7 +483,7 @@ jobs:
shell: powershell
run: |
$ErrorActionPreference = "SilentlyContinue"
(New-Object Net.WebClient).DownloadFile("https://github.com/medyagh/gopogh/releases/download/v0.2.4/gopogh.exe", "C:\ProgramData\chocolatey\bin\gopogh.exe")
(New-Object Net.WebClient).DownloadFile("https://github.com/medyagh/gopogh/releases/download/v0.2.4/gopogh.exe", "C:\ProgramData\chocolatey\bin\gopogh.exe")
choco install -y kubernetes-cli
choco install -y jq
choco install -y caffeine
......@@ -549,7 +549,7 @@ jobs:
echo "---------------- ${numFail} Failures :( ----------------------------"
echo $failedTests
echo "-------------------------------------------------------"
If ($numFail -gt 0){ exit 2 }
If ($numFail -gt 0){ exit 2 }
If ($numPass -eq 0){ exit 2 }
If ($numPass -lt 33){ exit 2 }
If ($numFail -eq 0){ exit 0 }
......@@ -775,11 +775,11 @@ jobs:
run: |
hostname
VBoxManage --version
sysctl hw.physicalcpu hw.logicalcpu
sysctl hw.physicalcpu hw.logicalcpu
- name: Disable firewall
run: |
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off
sudo /usr/libexec/ApplicationFirewall/socketfilterfw -k
sudo /usr/libexec/ApplicationFirewall/socketfilterfw -k
- name: Download Binaries
uses: actions/download-artifact@v1
with:
......@@ -966,7 +966,7 @@ jobs:
run: |
hostname
VBoxManage --version
sysctl hw.physicalcpu hw.logicalcpu
sysctl hw.physicalcpu hw.logicalcpu
- name: Disable firewall
run: |
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off
......@@ -1150,7 +1150,7 @@ jobs:
run: |
hostname
VBoxManage --version
sysctl hw.physicalcpu hw.logicalcpu
sysctl hw.physicalcpu hw.logicalcpu
- name: Disable firewall
run: |
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off
......@@ -1247,4 +1247,4 @@ jobs:
- uses: actions/upload-artifact@v1
with:
name: all_reports
path: all_reports
path: all_reports
\ No newline at end of file
......@@ -410,7 +410,7 @@ jobs:
echo "---------------- ${numFail} Failures :( ----------------------------"
echo $failedTests
echo "-------------------------------------------------------"
If ($numFail -gt 0){ exit 2 }
If ($numFail -gt 0){ exit 2 }
If ($numPass -eq 0){ exit 2 }
If ($numPass -lt 33){ exit 2 }
If ($numFail -eq 0){ exit 0 }
......@@ -427,7 +427,7 @@ jobs:
shell: powershell
run: |
echo $env:computerName
ls
ls
$ErrorActionPreference = "SilentlyContinue"
cd minikube_binaries
ls
......@@ -462,14 +462,14 @@ jobs:
$docker_running = $?
}
Write-Output "Docker is running"
docker system prune -f
docker system prune -f
- name: Info
continue-on-error: true
shell: powershell
run: |
$ErrorActionPreference = "SilentlyContinue"
cd minikube_binaries
ls
ls
echo $env:computername
Get-WmiObject -class Win32_ComputerSystem
- uses: actions/setup-go@v2
......@@ -481,7 +481,7 @@ jobs:
shell: powershell
run: |
$ErrorActionPreference = "SilentlyContinue"
(New-Object Net.WebClient).DownloadFile("https://github.com/medyagh/gopogh/releases/download/v0.2.4/gopogh.exe", "C:\ProgramData\chocolatey\bin\gopogh.exe")
(New-Object Net.WebClient).DownloadFile("https://github.com/medyagh/gopogh/releases/download/v0.2.4/gopogh.exe", "C:\ProgramData\chocolatey\bin\gopogh.exe")
choco install -y kubernetes-cli
choco install -y jq
choco install -y caffeine
......@@ -547,7 +547,7 @@ jobs:
echo "---------------- ${numFail} Failures :( ----------------------------"
echo $failedTests
echo "-------------------------------------------------------"
If ($numFail -gt 0){ exit 2 }
If ($numFail -gt 0){ exit 2 }
If ($numPass -eq 0){ exit 2 }
If ($numPass -lt 33){ exit 2 }
If ($numFail -eq 0){ exit 0 }
......@@ -773,11 +773,11 @@ jobs:
run: |
hostname
VBoxManage --version
sysctl hw.physicalcpu hw.logicalcpu
sysctl hw.physicalcpu hw.logicalcpu
- name: Disable firewall
run: |
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off
sudo /usr/libexec/ApplicationFirewall/socketfilterfw -k
sudo /usr/libexec/ApplicationFirewall/socketfilterfw -k
- name: Download Binaries
uses: actions/download-artifact@v1
with:
......@@ -964,7 +964,7 @@ jobs:
run: |
hostname
VBoxManage --version
sysctl hw.physicalcpu hw.logicalcpu
sysctl hw.physicalcpu hw.logicalcpu
- name: Disable firewall
run: |
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off
......@@ -1148,7 +1148,7 @@ jobs:
run: |
hostname
VBoxManage --version
sysctl hw.physicalcpu hw.logicalcpu
sysctl hw.physicalcpu hw.logicalcpu
- name: Disable firewall
run: |
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off
......@@ -1245,4 +1245,4 @@ jobs:
- uses: actions/upload-artifact@v1
with:
name: all_reports
path: all_reports
path: all_reports
\ No newline at end of file
......@@ -305,9 +305,15 @@ html_report: ## Generate HTML report out of the last ran integration test logs.
@go tool test2json -t < "./out/testout_$(COMMIT_SHORT).txt" > "./out/testout_$(COMMIT_SHORT).json"
@gopogh -in "./out/testout_$(COMMIT_SHORT).json" -out ./out/testout_$(COMMIT_SHORT).html -name "$(shell git rev-parse --abbrev-ref HEAD)" -pr "" -repo github.com/kubernetes/minikube/ -details "${COMMIT_SHORT}"
@echo "-------------------------- Open HTML Report in Browser: ---------------------------"
ifeq ($(GOOS),windows)
@echo start $(CURDIR)/out/testout_$(COMMIT_SHORT).html
@echo "-----------------------------------------------------------------------------------"
@start $(CURDIR)/out/testout_$(COMMIT_SHORT).html || true
else
@echo open $(CURDIR)/out/testout_$(COMMIT_SHORT).html
@echo "-----------------------------------------------------------------------------------"
@open $(CURDIR)/out/testout_$(COMMIT_SHORT).html || true
endif
.PHONY: test
test: pkg/minikube/assets/assets.go pkg/minikube/translate/translations.go ## Trigger minikube test
......@@ -331,7 +337,7 @@ pkg/minikube/assets/assets.go: $(shell find "deploy/addons" -type f)
ifeq ($(MINIKUBE_BUILD_IN_DOCKER),y)
$(call DOCKER,$(BUILD_IMAGE),/usr/bin/make $@)
endif
@which go-bindata >/dev/null 2>&1 || GO111MODULE=off GOBIN="$(GOPATH)$(DIRSEP)bin" go get github.com/jteeuwen/go-bindata/...
@which go-bindata >/dev/null 2>&1 || GO111MODULE=off GOBIN="$(GOPATH)$(DIRSEP)bin" go get github.com/go-bindata/go-bindata/...
$(if $(quiet),@echo " GEN $@")
$(Q)PATH="$(PATH)$(PATHSEP)$(GOPATH)$(DIRSEP)bin" go-bindata -nomemcopy -o $@ -pkg assets deploy/addons/...
$(Q)-gofmt -s -w $@
......@@ -344,7 +350,7 @@ pkg/minikube/translate/translations.go: $(shell find "translations/" -type f)
ifeq ($(MINIKUBE_BUILD_IN_DOCKER),y)
$(call DOCKER,$(BUILD_IMAGE),/usr/bin/make $@)
endif
@which go-bindata >/dev/null 2>&1 || GO111MODULE=off GOBIN="$(GOPATH)$(DIRSEP)bin" go get github.com/jteeuwen/go-bindata/...
@which go-bindata >/dev/null 2>&1 || GO111MODULE=off GOBIN="$(GOPATH)$(DIRSEP)bin" go get github.com/go-bindata/go-bindata/...
$(if $(quiet),@echo " GEN $@")
$(Q)PATH="$(PATH)$(PATHSEP)$(GOPATH)$(DIRSEP)bin" go-bindata -nomemcopy -o $@ -pkg translate translations/...
$(Q)-gofmt -s -w $@
......@@ -579,7 +585,7 @@ storage-provisioner-image: out/storage-provisioner-$(GOARCH) ## Build storage-pr
.PHONY: kic-base-image
kic-base-image: ## builds the base image used for kic.
docker rmi -f $(KIC_BASE_IMAGE_GCR)-snapshot || true
docker build -f ./deploy/kicbase/Dockerfile -t local/kicbase:$(KIC_VERSION)-snapshot --build-arg COMMIT_SHA=${VERSION}-$(COMMIT) --cache-from $(KIC_BASE_IMAGE_GCR) --target base ./deploy/kicbase
docker build -f ./deploy/kicbase/Dockerfile -t local/kicbase:$(KIC_VERSION)-snapshot --build-arg COMMIT_SHA=${VERSION}-$(COMMIT) --cache-from $(KIC_BASE_IMAGE_GCR) ./deploy/kicbase
docker tag local/kicbase:$(KIC_VERSION)-snapshot $(KIC_BASE_IMAGE_GCR)-snapshot
docker tag local/kicbase:$(KIC_VERSION)-snapshot $(KIC_BASE_IMAGE_GCR)
docker tag local/kicbase:$(KIC_VERSION)-snapshot $(KIC_BASE_IMAGE_HUB)
......@@ -747,9 +753,9 @@ site: site/themes/docsy/assets/vendor/bootstrap/package.js out/hugo/hugo ## Serv
out/mkcmp:
GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $@ cmd/performance/mkcmp/main.go
.PHONY: out/performance-monitor
out/performance-monitor:
GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $@ cmd/performance/monitor/monitor.go
.PHONY: out/performance-bot
out/performance-bot:
GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $@ cmd/performance/pr-bot/bot.go
.PHONY: compare
compare: out/mkcmp out/minikube
......
......@@ -28,8 +28,7 @@ import (
"k8s.io/minikube/pkg/minikube/reason"
)
const longDescription = `
Outputs minikube shell completion for the given shell (bash, zsh or fish)
const longDescription = `Outputs minikube shell completion for the given shell (bash, zsh or fish)
This depends on the bash-completion binary. Example installation instructions:
OS X:
......
......@@ -32,8 +32,8 @@ import (
var addonsConfigureCmd = &cobra.Command{
Use: "configure ADDON_NAME",
Short: "Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list ",
Long: "Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list ",
Short: "Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list",
Long: "Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 1 {
exit.Message(reason.Usage, "usage: minikube addons configure ADDON_NAME")
......
......@@ -23,6 +23,7 @@ import (
"strconv"
"strings"
"k8s.io/minikube/pkg/minikube/bootstrapper/bsutil/kverify"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/driver"
"k8s.io/minikube/pkg/minikube/exit"
......@@ -31,6 +32,7 @@ import (
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/style"
"github.com/docker/machine/libmachine"
"github.com/golang/glog"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
......@@ -54,96 +56,114 @@ var profileListCmd = &cobra.Command{
},
}
var printProfilesTable = func() {
var validData [][]string
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Profile", "VM Driver", "Runtime", "IP", "Port", "Version", "Status"})
table.SetAutoFormatHeaders(false)
table.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true})
table.SetCenterSeparator("|")
func printProfilesTable() {
validProfiles, invalidProfiles, err := config.ListProfiles()
if len(validProfiles) == 0 || err != nil {
if err != nil {
glog.Warningf("error loading profiles: %v", err)
}
if len(validProfiles) == 0 {
exit.Message(reason.Usage, "No minikube profile was found. You can create one using `minikube start`.")
}
updateProfilesStatus(validProfiles)
renderProfilesTable(profilesToTableData(validProfiles))
warnInvalidProfiles(invalidProfiles)
}
func updateProfilesStatus(profiles []*config.Profile) {
api, err := machine.NewAPIClient()
if err != nil {
glog.Errorf("failed to get machine api client %v", err)
}
defer api.Close()
for _, p := range validProfiles {
cp, err := config.PrimaryControlPlane(p.Config)
if err != nil {
exit.Error(reason.GuestCpConfig, "error getting primary control plane", err)
}
p.Status, err = machine.Status(api, driver.MachineName(*p.Config, cp))
if err != nil {
glog.Warningf("error getting host status for %s: %v", p.Name, err)
}
validData = append(validData, []string{p.Name, p.Config.Driver, p.Config.KubernetesConfig.ContainerRuntime, cp.IP, strconv.Itoa(cp.Port), p.Config.KubernetesConfig.KubernetesVersion, p.Status})
for _, p := range profiles {
p.Status = profileStatus(p, api)
}
}
table.AppendBulk(validData)
table.Render()
func profileStatus(p *config.Profile, api libmachine.API) string {
cp, err := config.PrimaryControlPlane(p.Config)
if err != nil {
exit.Error(reason.GuestCpConfig, "error getting primary control plane", err)
}
if invalidProfiles != nil {
out.WarningT("Found {{.number}} invalid profile(s) ! ", out.V{"number": len(invalidProfiles)})
for _, p := range invalidProfiles {
out.ErrT(style.Empty, "\t "+p.Name)
}
out.ErrT(style.Tip, "You can delete them using the following command(s): ")
for _, p := range invalidProfiles {
out.Err(fmt.Sprintf("\t $ minikube delete -p %s \n", p.Name))
}
host, err := machine.LoadHost(api, driver.MachineName(*p.Config, cp))
if err != nil {
glog.Warningf("error loading profiles: %v", err)
return "Unknown"
}
cr, err := machine.CommandRunner(host)
if err != nil {
glog.Warningf("error loading profiles: %v", err)
return "Unknown"
}
hostname, _, port, err := driver.ControlPlaneEndpoint(p.Config, &cp, host.DriverName)
if err != nil {
glog.Warningf("error loading profiles: %v", err)
return "Unknown"
}
}
var printProfilesJSON = func() {
api, err := machine.NewAPIClient()
status, err := kverify.APIServerStatus(cr, hostname, port)
if err != nil {
glog.Errorf("failed to get machine api client %v", err)
glog.Warningf("error getting apiserver status for %s: %v", p.Name, err)
return "Unknown"
}
defer api.Close()
return status.String()
}
validProfiles, invalidProfiles, err := config.ListProfiles()
for _, v := range validProfiles {
cp, err := config.PrimaryControlPlane(v.Config)
func renderProfilesTable(ps [][]string) {
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Profile", "VM Driver", "Runtime", "IP", "Port", "Version", "Status"})
table.SetAutoFormatHeaders(false)
table.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true})
table.SetCenterSeparator("|")
table.AppendBulk(ps)
table.Render()
}
func profilesToTableData(profiles []*config.Profile) [][]string {
var data [][]string
for _, p := range profiles {
cp, err := config.PrimaryControlPlane(p.Config)
if err != nil {
exit.Error(reason.GuestCpConfig, "error getting primary control plane", err)
}
status, err := machine.Status(api, driver.MachineName(*v.Config, cp))
if err != nil {
glog.Warningf("error getting host status for %s: %v", v.Name, err)
}
v.Status = status
data = append(data, []string{p.Name, p.Config.Driver, p.Config.KubernetesConfig.ContainerRuntime, cp.IP, strconv.Itoa(cp.Port), p.Config.KubernetesConfig.KubernetesVersion, p.Status})
}
return data
}
var valid []*config.Profile
var invalid []*config.Profile
func warnInvalidProfiles(invalidProfiles []*config.Profile) {
if invalidProfiles == nil {
return
}
if validProfiles != nil {
valid = validProfiles
} else {
valid = []*config.Profile{}
out.WarningT("Found {{.number}} invalid profile(s) ! ", out.V{"number": len(invalidProfiles)})
for _, p := range invalidProfiles {
out.ErrT(style.Empty, "\t "+p.Name)
}
if invalidProfiles != nil {
invalid = invalidProfiles
} else {
invalid = []*config.Profile{}
out.ErrT(style.Tip, "You can delete them using the following command(s): ")
for _, p := range invalidProfiles {
out.Err(fmt.Sprintf("\t $ minikube delete -p %s \n", p.Name))
}
}
func printProfilesJSON() {
validProfiles, invalidProfiles, err := config.ListProfiles()
body := map[string]interface{}{}
updateProfilesStatus(validProfiles)
var body = map[string]interface{}{}
if err == nil || config.IsNotExist(err) {
body["valid"] = valid
body["invalid"] = invalid
body["valid"] = profilesOrDefault(validProfiles)
body["invalid"] = profilesOrDefault(invalidProfiles)
jsonString, _ := json.Marshal(body)
out.String(string(jsonString))
} else {
......@@ -154,6 +174,13 @@ var printProfilesJSON = func() {
}
}
func profilesOrDefault(profiles []*config.Profile) []*config.Profile {
if profiles != nil {
return profiles
}
return []*config.Profile{}
}
func init() {
profileListCmd.Flags().StringVarP(&output, "output", "o", "table", "The output format. One of 'json', 'table'")
ProfileCmd.AddCommand(profileListCmd)
......
......@@ -91,7 +91,6 @@ func init() {
if err := viper.BindPFlags(deleteCmd.Flags()); err != nil {
exit.Error(reason.InternalBindFlags, "unable to bind flags", err)
}
RootCmd.AddCommand(deleteCmd)
}
// shotgun cleanup to delete orphaned docker container data
......@@ -261,6 +260,11 @@ func deletePossibleKicLeftOver(cname string, driverName string) {
glog.Warningf("error deleting volumes (might be okay).\nTo see the list of volumes run: 'docker volume ls'\n:%v", errs)
}
errs = oci.DeleteKICNetworks()
if errs != nil {
glog.Warningf("error deleting leftover networks (might be okay).\nTo see the list of networks: 'docker network ls'\n:%v", errs)
}
if bin == oci.Podman {
// podman prune does not support --filter
return
......
......@@ -110,7 +110,7 @@ var mountCmd = &cobra.Command{
var ip net.IP
var err error
if mountIP == "" {
ip, err = cluster.HostIP(co.CP.Host)
ip, err = cluster.HostIP(co.CP.Host, co.Config.Name)
if err != nil {
exit.Error(reason.IfHostIP, "Error getting the host IP address to use from within the VM", err)
}
......
......@@ -794,7 +794,7 @@ func validateUser(drvName string) {
out.ErrT(style.Stopped, `The "{{.driver_name}}" driver should not be used with root privileges.`, out.V{"driver_name": drvName})
out.ErrT(style.Tip, "If you are running minikube within a VM, consider using --driver=none:")
out.ErrT(style.Documentation, " https://minikube.sigs.k8s.io/docs/reference/drivers/none/")
out.ErrT(style.Documentation, " {{.url}}", out.V{"url": "https://minikube.sigs.k8s.io/docs/reference/drivers/none/"})
cname := ClusterFlagValue()
_, err = config.Load(cname)
......@@ -829,7 +829,7 @@ func memoryLimits(drvName string) (int, int, error) {
if err != nil {
return -1, -1, err
}
containerLimit = int(s.TotalMemory / 1024 / 1024)
containerLimit = util.ConvertBytesToMB(s.TotalMemory)
}
return sysLimit, containerLimit, nil
......
......@@ -57,6 +57,7 @@ var (
const (
// Additional legacy states:
// Configured means configured
Configured = "Configured" // ~state.Saved
// Misconfigured means misconfigured
......@@ -67,9 +68,9 @@ const (
Irrelevant = "Irrelevant"
// New status modes, based roughly on HTTP/SMTP standards
InsufficientStorage = reason.ExInsufficientStorage
// 1xx signifies a transitional state. If retried, it will soon return a 2xx, 4xx, or 5xx
Starting = 100
Pausing = 101
Unpausing = 102
......@@ -77,27 +78,30 @@ const (
Deleting = 120
// 2xx signifies that the API Server is able to service requests
OK = 200
Warning = 203
// 4xx signifies an error that requires help from the client to resolve
NotFound = 404
Stopped = 405
Paused = 418 // I'm a teapot!
// 5xx signifies a server-side error (that may be retryable)
Error = 500
Unknown = 520
Error = 500
InsufficientStorage = 507
Unknown = 520
)
var (
codeNames = map[int]string{
reason.ExInsufficientStorage: "InsufficientStorage",
100: "Starting",
101: "Pausing",
102: "Unpausing",
110: "Stopping",
103: "Deleting",
100: "Starting",
101: "Pausing",
102: "Unpausing",
110: "Stopping",
103: "Deleting",
200: "OK",
203: "Warning",
......@@ -107,11 +111,12 @@ var (
418: "Paused",
500: "Error",
507: "InsufficientStorage",
520: "Unknown",
}
codeDetails = map[int]string{
reason.ExInsufficientStorage: "/var is almost out of disk space",
507: "/var is almost out of disk space",
}
)
......
......@@ -60,8 +60,6 @@ func init() {
if err := viper.GetViper().BindPFlags(stopCmd.Flags()); err != nil {
exit.Error(reason.InternalFlagsBind, "unable to bind flags", err)
}
RootCmd.AddCommand(stopCmd)
}
// runStop handles the executes the flow of "minikube stop"
......
......@@ -18,12 +18,17 @@ package main
import (
"context"
"fmt"
"log"
"time"
"github.com/pkg/errors"
"k8s.io/minikube/pkg/perf/monitor"
)
func main() {
for {
log.Print("~~~~~~~~~ Starting performance analysis ~~~~~~~~~~~~~~")
if err := analyzePerformance(context.Background()); err != nil {
log.Printf("error executing performance analysis: %v", err)
}
......@@ -36,5 +41,32 @@ func main() {
// 2. running mkcmp against those PRs
// 3. commenting results on those PRs
func analyzePerformance(ctx context.Context) error {
client := monitor.NewClient(ctx, monitor.GithubOwner, monitor.GithubRepo)
prs, err := client.ListOpenPRsWithLabel(monitor.OkToTestLabel)
if err != nil {
return errors.Wrap(err, "listing open prs")
}
log.Print("got prs:", prs)
for _, pr := range prs {
log.Printf("~~~ Analyzing PR %d ~~~", pr)
newCommitsExist, err := client.NewCommitsExist(pr, monitor.BotName)
if err != nil {
return err
}
if !newCommitsExist {
log.Println("New commits don't exist, skipping rerun...")
continue
}
var message string
message, err = monitor.RunMkcmp(ctx, pr)
if err != nil {
message = fmt.Sprintf("Error: %v\n%s", err, message)
}
log.Printf("message for pr %d:\n%s\n", pr, message)
if err := client.CommentOnPR(pr, message); err != nil {
return err
}
log.Print("successfully commented on PR")
}
return nil
}
......@@ -80,7 +80,7 @@ spec:
spec:
containers:
- name: gcp-auth
image: gcr.io/k8s-minikube/gcp-auth-webhook:v0.0.2
image: gcr.io/k8s-minikube/gcp-auth-webhook:v0.0.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8443
......@@ -131,10 +131,17 @@ metadata:
app: gcp-auth
webhooks:
- name: gcp-auth-mutate.k8s.io
failurePolicy: Fail
objectSelector:
matchExpressions:
- key: gcp-auth-skip-secret
operator: DoesNotExist
namespaceSelector:
matchExpressions:
- key: name
operator: NotIn
values:
- kube-system
sideEffects: None
admissionReviewVersions: ["v1","v1beta1"]
clientConfig:
......
# Turn on Source Address Verification in all interfaces to
# prevent some spoofing attacks.
net.ipv4.conf.default.rp_filter=1
net.ipv4.conf.all.rp_filter=1
# Copyright 2018 The Kubernetes Authors.
#
# 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.
# kind node base image
#
# For systemd + docker configuration used below, see the following references:
# https://www.freedesktop.org/wiki/Software/systemd/ContainerInterface/
# start from ubuntu 20.04, this image is reasonably small as a starting point
# for a kubernetes node image, it doesn't contain much we don't need
FROM ubuntu:focal-20200423
# copy in static files (configs, scripts)
COPY 10-network-security.conf /etc/sysctl.d/10-network-security.conf
COPY clean-install /usr/local/bin/clean-install
COPY entrypoint /usr/local/bin/entrypoint
# Install dependencies, first from apt, then from release tarballs.
# NOTE: we use one RUN to minimize layers.
#
# First we must ensure that our util scripts are executable.
#
# The base image already has: ssh, apt, snapd, but we need to install more packages.
# Packages installed are broken down into (each on a line):
# - packages needed to run services (systemd)
# - packages needed for kubernetes components
# - packages needed by the container runtime
# - misc packages kind uses itself
# After installing packages we cleanup by:
# - removing unwanted systemd services
# - disabling kmsg in journald (these log entries would be confusing)
#
# Next we ensure the /etc/kubernetes/manifests directory exists. Normally
# a kubeadm debain / rpm package would ensure that this exists but we install
# freshly built binaries directly when we build the node image.
#
# Finally we adjust tempfiles cleanup to be 1 minute after "boot" instead of 15m
# This is plenty after we've done initial setup for a node, but before we are
# likely to try to export logs etc.
RUN echo "Ensuring scripts are executable ..." \
&& chmod +x /usr/local/bin/clean-install /usr/local/bin/entrypoint \
&& echo "Installing Packages ..." \
&& DEBIAN_FRONTEND=noninteractive clean-install \
systemd \
conntrack iptables iproute2 ethtool socat util-linux mount ebtables udev kmod \
libseccomp2 \
bash ca-certificates curl rsync \
&& find /lib/systemd/system/sysinit.target.wants/ -name "systemd-tmpfiles-setup.service" -delete \
&& rm -f /lib/systemd/system/multi-user.target.wants/* \
&& rm -f /etc/systemd/system/*.wants/* \
&& rm -f /lib/systemd/system/local-fs.target.wants/* \
&& rm -f /lib/systemd/system/sockets.target.wants/*udev* \
&& rm -f /lib/systemd/system/sockets.target.wants/*initctl* \
&& rm -f /lib/systemd/system/basic.target.wants/* \
&& echo "ReadKMsg=no" >> /etc/systemd/journald.conf \
&& ln -s "$(which systemd)" /sbin/init \
&& echo "Ensuring /etc/kubernetes/manifests" \
&& mkdir -p /etc/kubernetes/manifests \
&& echo "Adjusting systemd-tmpfiles timer" \
&& sed -i /usr/lib/systemd/system/systemd-tmpfiles-clean.timer -e 's#OnBootSec=.*#OnBootSec=1min#' \
&& echo "Modifying /etc/nsswitch.conf to prefer hosts" \
&& sed -i /etc/nsswitch.conf -re 's#^(hosts:\s*).*#\1dns files#'
# tell systemd that it is in docker (it will check for the container env)
# https://www.freedesktop.org/wiki/Software/systemd/ContainerInterface/
ENV container docker
# systemd exits on SIGRTMIN+3, not SIGTERM (which re-executes it)
# https://bugzilla.redhat.com/show_bug.cgi?id=1201657
STOPSIGNAL SIGRTMIN+3
# NOTE: this is *only* for documentation, the entrypoint is overridden later
ENTRYPOINT [ "/usr/local/bin/entrypoint", "/sbin/init" ]
ARG COMMIT_SHA
# using base image created by kind https://github.com/kubernetes-sigs/kind/blob/v0.8.1/images/base/Dockerfile
# using base image created by kind https://github.com/kubernetes-sigs/kind/blob/2c0eee40/images/base/Dockerfile
# which is an ubuntu 20.04 with an entry-point that helps running systemd
# could be changed to any debian that can run systemd
FROM kindest/base:v20200430-2c0eee40 as base
USER root
# specify version of everything explicitly using 'apt-cache policy'
RUN apt-get update && apt-get install -y --no-install-recommends \
# install system requirements from the regular distro repositories
RUN clean-install \
lz4 \
gnupg \
sudo \
docker.io \
containerd \
openssh-server \
dnsutils \
runc \
# libglib2.0-0 is required for conmon, which is required for podman
libglib2.0-0 \
# removing kind's crictl config
&& rm /etc/crictl.yaml
libglib2.0-0
# Install cri-o/podman dependencies:
RUN sh -c "echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_20.04/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list" && \
curl -LO https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/xUbuntu_20.04/Release.key && \
apt-key add - < Release.key && apt-get update && \
apt-get install -y --no-install-recommends containers-common catatonit conmon containernetworking-plugins podman-plugins varlink
apt-key add - < Release.key && \
clean-install containers-common catatonit conmon containernetworking-plugins cri-tools podman-plugins varlink
# install cri-o based on https://github.com/cri-o/cri-o/commit/96b0c34b31a9fc181e46d7d8e34fb8ee6c4dc4e1#diff-04c6e90faac2675aa89e2176d2eec7d8R128
RUN sh -c "echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/1.18:/1.18.3/xUbuntu_20.04/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list" && \
curl -LO https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/1.18:/1.18.3/xUbuntu_20.04/Release.key && \
apt-key add - < Release.key && apt-get update && \
apt-get install -y --no-install-recommends cri-o=1.18.3~3
apt-key add - < Release.key && \
clean-install cri-o=1.18.3~3
# install podman
RUN sh -c "echo 'deb https://dl.bintray.com/afbjorklund/podman focal main' > /etc/apt/sources.list.d/podman.list" && \
curl -L https://bintray.com/user/downloadSubjectPublicKey?username=afbjorklund -o afbjorklund-public.key.asc && \
apt-key add - < afbjorklund-public.key.asc && apt-get update && \
apt-get install -y --no-install-recommends podman=1.9.3~1
apt-key add - < afbjorklund-public.key.asc && \
clean-install podman=1.9.3~1
RUN mkdir -p /usr/lib/cri-o-runc/sbin && cp /usr/local/sbin/runc /usr/lib/cri-o-runc/sbin/runc
RUN mkdir -p /usr/lib/cri-o-runc/sbin && cp /usr/sbin/runc /usr/lib/cri-o-runc/sbin/runc
COPY entrypoint /usr/local/bin/entrypoint
# automount service
COPY automount/minikube-automount /usr/sbin/minikube-automount
COPY automount/minikube-automount.service /usr/lib/systemd/system/minikube-automount.service
......@@ -71,12 +152,7 @@ USER root
# https://github.com/kubernetes-sigs/kind/blob/master/images/base/files/usr/local/bin/entrypoint
RUN mkdir -p /kind
# Deleting leftovers
RUN apt-get clean -y && rm -rf \
/var/cache/debconf/* \
/var/lib/apt/lists/* \
/var/log/* \
/tmp/* \
/var/tmp/* \
RUN rm -rf \
/usr/share/doc/* \
/usr/share/man/* \
/usr/share/local/* \
......
#!/bin/sh
# Copyright 2017 The Kubernetes Authors.
#
# 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.
# A script encapsulating a common Dockerimage pattern for installing packages
# and then cleaning up the unnecessary install artifacts.
# e.g. clean-install iptables ebtables conntrack
set -o errexit
if [ $# = 0 ]; then
echo >&2 "No packages specified"
exit 1
fi
apt-get update
apt-get install -y --no-install-recommends "$@"
apt-get clean -y
rm -rf \
/var/cache/debconf/* \
/var/lib/apt/lists/* \
/var/log/* \
/tmp/* \
/var/tmp/* \
/usr/share/doc/* \
/usr/share/man/* \
/usr/share/local/*
......@@ -81,7 +81,7 @@ for path in $(gsutil ls "gs://${ISO_BUCKET}/minikube-v${VERSION}*" || true); do
done
# Upload all end-user assets other than preload files, as they are release independent
for file in out/minikube[_-]* out/docker-machine-*; do
for file in $( find out \( -name "minikube[_-]*" -or -name "docker-machine-*" \) -and ! -name "*latest*"); do
n=0
until [ $n -ge 5 ]
do
......
......@@ -39,6 +39,7 @@ import (
func generateTarball(kubernetesVersion, containerRuntime, tarballFilename string) error {
driver := kic.NewDriver(kic.Config{
ClusterName: profile,
KubernetesVersion: kubernetesVersion,
ContainerRuntime: containerRuntime,
OCIBinary: oci.Docker,
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="702px" height="683px" viewBox="0 0 702 683" version="1.1" xmlns="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink">
<!-- Generator: Sketch 39.1 (31720) - https://www.sketchapp.com/ -->
<svg width="702px" height="683px" viewBox="0 0 702 683" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 39.1 (31720) - http://www.bohemiancoding.com/sketch -->
<title>minikube</title>
<desc>Created with Sketch.</desc>
<defs>
......
......@@ -324,7 +324,18 @@ func verifyAddonStatus(cc *config.ClusterConfig, name string, val string) error
}
func verifyGCPAuthAddon(cc *config.ClusterConfig, name string, val string) error {
return verifyAddonStatusInternal(cc, name, val, "gcp-auth")
enable, err := strconv.ParseBool(val)
if err != nil {
return errors.Wrapf(err, "parsing bool: %s", name)
}
err = verifyAddonStatusInternal(cc, name, val, "gcp-auth")
if enable && err == nil {
out.T(style.Notice, "Your GCP credentials will now be mounted into every pod created in the {{.name}} cluster.", out.V{"name": cc.Name})
out.T(style.Notice, "If you don't want your credentials mounted into a specific pod, add a label with the `gcp-auth-skip-secret` key to your pod configuration.")
}
return err
}
func verifyAddonStatusInternal(cc *config.ClusterConfig, name string, val string, ns string) error {
......@@ -394,16 +405,26 @@ func Start(wg *sync.WaitGroup, cc *config.ClusterConfig, toEnable map[string]boo
var awg sync.WaitGroup
defer func() { // making it show after verifications( not perfect till #7613 is closed)
enabledAddons := []string{}
deferredAddons := []string{}
defer func() { // making it show after verifications (see #7613)
register.Reg.SetStep(register.EnablingAddons)
out.T(style.AddonEnable, "Enabled addons: {{.addons}}", out.V{"addons": strings.Join(toEnableList, ", ")})
out.T(style.AddonEnable, "Enabled addons: {{.addons}}", out.V{"addons": strings.Join(enabledAddons, ", ")})
}()
for _, a := range toEnableList {
if a == "gcp-auth" {
deferredAddons = append(deferredAddons, a)
continue
}
awg.Add(1)
go func(name string) {
err := RunCallbacks(cc, name, "true")
if err != nil {
out.WarningT("Enabling '{{.name}}' returned an error: {{.error}}", out.V{"name": name, "error": err})
} else {
enabledAddons = append(enabledAddons, name)
}
awg.Done()
}(a)
......@@ -411,7 +432,18 @@ func Start(wg *sync.WaitGroup, cc *config.ClusterConfig, toEnable map[string]boo
// Wait until all of the addons are enabled before updating the config (not thread safe)
awg.Wait()
for _, a := range toEnableList {
// Now run the deferred addons
for _, a := range deferredAddons {
err := RunCallbacks(cc, a, "true")
if err != nil {
out.WarningT("Enabling '{{.name}}' returned an error: {{.error}}", out.V{"name": a, "error": err})
} else {
enabledAddons = append(enabledAddons, a)
}
}
for _, a := range enabledAddons {
if err := Set(cc, a, "true"); err != nil {
glog.Errorf("store failed: %v", err)
}
......
......@@ -169,7 +169,7 @@ var Addons = []*Addon{
{
name: "gcp-auth",
set: SetBool,
callbacks: []setFn{gcpauth.EnableOrDisable, enableOrDisableAddon, verifyGCPAuthAddon, gcpauth.DisplayAddonMessage},
callbacks: []setFn{gcpauth.EnableOrDisable, enableOrDisableAddon, verifyGCPAuthAddon},
},
{
name: "volumesnapshots",
......
......@@ -60,7 +60,7 @@ func enableAddon(cfg *config.ClusterConfig) error {
ctx := context.Background()
creds, err := google.FindDefaultCredentials(ctx)
if err != nil {
exit.Message(reason.InternalCredsNotFound, "Could not find any GCP credentials. Either run `gcloud auth login` or set the GOOGLE_APPLICATION_CREDENTIALS environment variable to the path of your credentials file.")
exit.Message(reason.InternalCredsNotFound, "Could not find any GCP credentials. Either run `gcloud auth application-default login` or set the GOOGLE_APPLICATION_CREDENTIALS environment variable to the path of your credentials file.")
}
f := assets.NewMemoryAssetTarget(creds.JSON, credentialsPath, "0444")
......@@ -116,16 +116,3 @@ func disableAddon(cfg *config.ClusterConfig) error {
return nil
}
// DisplayAddonMessage display an gcp auth addon specific message to the user
func DisplayAddonMessage(cfg *config.ClusterConfig, name string, val string) error {
enable, err := strconv.ParseBool(val)
if err != nil {
return errors.Wrapf(err, "parsing bool: %s", name)
}
if enable {
out.T(style.Notice, "Your GCP credentials will now be mounted into every pod created in the {{.name}} cluster.", out.V{"name": cfg.Name})
out.T(style.Notice, "If you don't want your credentials mounted into a specific pod, add a label with the `gcp-auth-skip-secret` key to your pod configuration.")
}
return nil
}
......@@ -29,6 +29,7 @@ import (
"github.com/docker/machine/libmachine/ssh"
"github.com/golang/glog"
"github.com/pkg/errors"
"k8s.io/minikube/pkg/util"
)
// This file is for common code shared among internal machine drivers
......@@ -74,7 +75,7 @@ func createRawDiskImage(sshKeyPath, diskPath string, diskSizeMb int) error {
return errors.Wrapf(err, "closing file %s", diskPath)
}
if err := os.Truncate(diskPath, int64(diskSizeMb*1000000)); err != nil {
if err := os.Truncate(diskPath, util.ConvertMBToBytes(diskSizeMb)); err != nil {
return errors.Wrap(err, "truncate")
}
return nil
......
......@@ -36,7 +36,7 @@ func Test_createDiskImage(t *testing.T) {
diskPath := filepath.Join(tmpdir, "disk")
sizeInMb := 100
sizeInBytes := int64(sizeInMb) * 1000000
sizeInBytes := int64(104857600)
if err := createRawDiskImage(sshPath, diskPath, sizeInMb); err != nil {
t.Errorf("createDiskImage() error = %v", err)
}
......
......@@ -37,6 +37,8 @@ import (
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/pkg/minikube/cruntime"
"k8s.io/minikube/pkg/minikube/download"
"k8s.io/minikube/pkg/minikube/driver"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/sysinit"
"k8s.io/minikube/pkg/util/retry"
)
......@@ -81,6 +83,17 @@ func (d *Driver) Create() error {
APIServerPort: d.NodeConfig.APIServerPort,
}
if gateway, err := oci.CreateNetwork(d.OCIBinary, d.NodeConfig.ClusterName); err != nil {
out.WarningT("Unable to create dedicated network, this might result in cluster IP change after restart: {{.error}}", out.V{"error": err})
} else {
params.Network = d.NodeConfig.ClusterName
ip := gateway.To4()
// calculate the container IP based on guessing the machine index
ip[3] += byte(driver.IndexFromMachineName(d.NodeConfig.MachineName))
glog.Infof("calculated static IP %q for the %q container", ip.String(), d.NodeConfig.MachineName)
params.IP = ip.String()
}
// control plane specific options
params.PortMappings = append(params.PortMappings, oci.PortMapping{
ListenAddress: oci.DefaultBindIPV4,
......@@ -294,6 +307,10 @@ func (d *Driver) Remove() error {
if id, err := oci.ContainerID(d.OCIBinary, d.MachineName); err == nil && id != "" {
return fmt.Errorf("expected no container ID be found for %q after delete. but got %q", d.MachineName, id)
}
if err := oci.RemoveNetwork(d.NodeConfig.ClusterName); err != nil {
glog.Warningf("failed to remove network (which might be okay) %s: %v", d.NodeConfig.ClusterName, err)
}
return nil
}
......@@ -408,7 +425,7 @@ func killAPIServerProc(runner command.Runner) error {
pid, err := strconv.Atoi(rr.Stdout.String())
if err == nil { // this means we have a valid pid
glog.Warningf("Found a kube-apiserver running with pid %d, will try to kill the proc", pid)
if _, err = runner.RunCmd(exec.Command("pkill", "-9", string(pid))); err != nil {
if _, err = runner.RunCmd(exec.Command("pkill", "-9", fmt.Sprint(pid))); err != nil {
return errors.Wrap(err, "kill")
}
}
......
......@@ -144,9 +144,11 @@ func runCmd(cmd *exec.Cmd, warnSlow ...bool) (*RunResult, error) {
}
}
if exitError, ok := err.(*exec.ExitError); ok {
rr.ExitCode = exitError.ExitCode()
if ex, ok := err.(*exec.ExitError); ok {
glog.Warningf("%s returned with exit code %d", rr.Command(), ex.ExitCode())
rr.ExitCode = ex.ExitCode()
}
// Decrease log spam
if elapsed > (1 * time.Second) {
glog.Infof("Completed: %s: (%s)", rr.Command(), elapsed)
......
......@@ -39,6 +39,9 @@ var ErrWindowsContainers = &FailFastError{errors.New("docker container type is w
// ErrCPUCountLimit is thrown when docker daemon doesn't have enough CPUs for the requested container
var ErrCPUCountLimit = &FailFastError{errors.New("not enough CPUs is available for container")}
// ErrIPinUse is thrown when the container been given an IP used by another container
var ErrIPinUse = &FailFastError{errors.New("can't create with that IP, address already in use")}
// ErrExitedUnexpectedly is thrown when container is created/started without error but later it exists and it's status is not running anymore.
var ErrExitedUnexpectedly = errors.New("container exited unexpectedly")
......@@ -48,6 +51,18 @@ var ErrDaemonInfo = errors.New("daemon info not responding")
// ErrInsufficientDockerStorage is thrown when there is not more storage for docker
var ErrInsufficientDockerStorage = &FailFastError{errors.New("insufficient docker storage, no space left on device")}
// ErrNetworkSubnetTaken is thrown when a subnet is taken by another network
var ErrNetworkSubnetTaken = errors.New("subnet is taken")
// ErrNetworkNotFound is when given network was not found
var ErrNetworkNotFound = errors.New("kic network not found")
// ErrNetworkGatewayTaken is when given network gatway is taken
var ErrNetworkGatewayTaken = errors.New("network gateway is taken")
// ErrNetworkInUse is when trying to delete a network which is attached to another container
var ErrNetworkInUse = errors.New("unable to delete a network that is attached to a running container")
// LogContainerDebug will print relevant docker/podman infos after a container fails
func LogContainerDebug(ociBin string, name string) string {
rr, err := containerInspect(ociBin, name)
......
......@@ -22,20 +22,24 @@ import (
"strings"
"time"
"github.com/golang/glog"
"github.com/pkg/errors"
)
// SysInfo Info represents common system Information between docker and podman that minikube cares
type SysInfo struct {
CPUs int // CPUs is Number of CPUs
TotalMemory int64 // TotalMemory Total available ram
OSType string // container's OsType (windows or linux)
Swarm bool // Weather or not the docker swarm is active
StorageDriver string // the storage driver for the daemon (for example overlay2)
CPUs int // CPUs is Number of CPUs
TotalMemory int64 // TotalMemory Total available ram
OSType string // container's OsType (windows or linux)
Swarm bool // Weather or not the docker swarm is active
StorageDriver string // the storage driver for the daemon (for example overlay2)
Errors []string // any server issues
}
var cachedSysInfo *SysInfo
var cachedSysInfoErr *error
var (
cachedSysInfo *SysInfo
cachedSysInfoErr *error
)
// CachedDaemonInfo will run and return a docker/podman info only once per minikube run time. to avoid performance
func CachedDaemonInfo(ociBin string) (SysInfo, error) {
......@@ -58,7 +62,7 @@ func DaemonInfo(ociBin string) (SysInfo, error) {
return *cachedSysInfo, err
}
d, err := dockerSystemInfo()
cachedSysInfo = &SysInfo{CPUs: d.NCPU, TotalMemory: d.MemTotal, OSType: d.OSType, Swarm: d.Swarm.LocalNodeState == "active", StorageDriver: d.Driver}
cachedSysInfo = &SysInfo{CPUs: d.NCPU, TotalMemory: d.MemTotal, OSType: d.OSType, Swarm: d.Swarm.LocalNodeState == "active", StorageDriver: d.Driver, Errors: d.ServerErrors}
return *cachedSysInfo, err
}
......@@ -163,6 +167,7 @@ type dockerSysInfo struct {
SecurityOptions []string `json:"SecurityOptions"`
ProductLicense string `json:"ProductLicense"`
Warnings interface{} `json:"Warnings"`
ServerErrors []string
ClientInfo struct {
Debug bool `json:"Debug"`
Plugins []interface{} `json:"Plugins"`
......@@ -245,6 +250,7 @@ func dockerSystemInfo() (dockerSysInfo, error) {
return ds, errors.Wrapf(err, "unmarshal docker system info")
}
glog.Infof("docker info: %+v", ds)
return ds, nil
}
......@@ -264,5 +270,6 @@ func podmanSystemInfo() (podmanSysInfo, error) {
if err := json.Unmarshal([]byte(strings.TrimSpace(rawJSON)), &ps); err != nil {
return ps, errors.Wrapf(err, "unmarshal podman system info")
}
glog.Infof("podman info: %+v", ps)
return ps, nil
}
......@@ -31,17 +31,26 @@ import (
// RoutableHostIPFromInside returns the ip/dns of the host that container lives on
// is routable from inside the container
func RoutableHostIPFromInside(ociBin string, containerName string) (net.IP, error) {
func RoutableHostIPFromInside(ociBin string, clusterName string, containerName string) (net.IP, error) {
if ociBin == Docker {
if runtime.GOOS == "linux" {
return dockerGatewayIP(containerName)
_, gateway, err := dockerNetworkInspect(clusterName)
if err != nil {
if errors.Is(err, ErrNetworkNotFound) {
glog.Infof("The container %s is not attached to a network, this could be because the cluster was created by minikube <v1.14, will try to get the IP using container gatway", containerName)
return containerGatewayIP(Docker, containerName)
}
return gateway, errors.Wrap(err, "network inspect")
}
return gateway, nil
}
// for windows and mac, the gateway ip is not routable so we use dns trick.
return digDNS(ociBin, containerName, "host.docker.internal")
}
// podman
if runtime.GOOS == "linux" {
return containerGatewayIP(ociBin, containerName)
return containerGatewayIP(Podman, containerName)
}
return nil, fmt.Errorf("RoutableHostIPFromInside is currently only implemented for linux")
......@@ -59,56 +68,8 @@ func digDNS(ociBin, containerName, dns string) (net.IP, error) {
return ip, nil
}
// profileInContainers checks whether the profile is within the containers list
func profileInContainers(profile string, containers []string) bool {
for _, container := range containers {
if container == profile {
return true
}
}
return false
}
// dockerGatewayIP gets the default gateway ip for the docker bridge on the user's host machine
// gets the ip from user's host docker
func dockerGatewayIP(profile string) (net.IP, error) {
var bridgeID string
rr, err := runCmd(exec.Command(Docker, "network", "ls", "--filter", "name=bridge", "--format", "{{.ID}}"))
if err != nil {
return nil, errors.Wrapf(err, "get network bridge")
}
networksOutput := strings.TrimSpace(rr.Stdout.String())
networksSlice := strings.Fields(networksOutput)
// Look for the minikube container within each docker network
for _, net := range networksSlice {
// get all containers in the network
rs, err := runCmd(exec.Command(Docker, "network", "inspect", net, "-f", "{{range $k, $v := .Containers}}{{$v.Name}} {{end}}"))
if err != nil {
return nil, errors.Wrapf(err, "get containers in network")
}
containersSlice := strings.Fields(rs.Stdout.String())
if profileInContainers(profile, containersSlice) {
bridgeID = net
break
}
}
if bridgeID == "" {
return nil, errors.Errorf("unable to determine bridge network id from %q", networksOutput)
}
rr, err = runCmd(exec.Command(Docker, "network", "inspect",
"--format", "{{(index .IPAM.Config 0).Gateway}}", bridgeID))
if err != nil {
return nil, errors.Wrapf(err, "inspect IP bridge network %q.", bridgeID)
}
ip := net.ParseIP(strings.TrimSpace(rr.Stdout.String()))
glog.Infof("got host ip for mount in container by inspect docker network: %s", ip.String())
return ip, nil
}
// containerGatewayIP gets the default gateway ip for the container
func containerGatewayIP(ociBin, containerName string) (net.IP, error) {
func containerGatewayIP(ociBin string, containerName string) (net.IP, error) {
rr, err := runCmd(exec.Command(ociBin, "container", "inspect", "--format", "{{.NetworkSettings.Gateway}}", containerName))
if err != nil {
return nil, errors.Wrapf(err, "inspect gateway")
......
/*
Copyright 2020 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 oci
import (
"bufio"
"bytes"
"fmt"
"net"
"os/exec"
"strings"
"github.com/golang/glog"
"github.com/pkg/errors"
)
// firstSubnetAddr subnet to be used on first kic cluster
// it is one octet more than the one used by KVM to avoid possible conflict
const firstSubnetAddr = "192.168.49.0"
// big enough for a cluster of 254 nodes
const defaultSubnetMask = 24
// CreateNetwork creates a network returns gateway and error, minikube creates one network per cluster
func CreateNetwork(ociBin string, name string) (net.IP, error) {
if ociBin != Docker {
return nil, fmt.Errorf("%s network not implemented yet", ociBin)
}
return createDockerNetwork(name)
}
func createDockerNetwork(clusterName string) (net.IP, error) {
// check if the network already exists
subnet, gateway, err := dockerNetworkInspect(clusterName)
if err == nil {
glog.Infof("Found existing network with subnet %s and gateway %s.", subnet, gateway)
return gateway, nil
}
attempts := 0
subnetAddr := firstSubnetAddr
// 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.239.0/24
for attempts < 20 {
gateway, err = tryCreateDockerNetwork(subnetAddr, defaultSubnetMask, clusterName)
if err == nil {
return gateway, nil
}
// don't retry if error is not adddress is taken
if !(errors.Is(err, ErrNetworkSubnetTaken) || errors.Is(err, ErrNetworkGatewayTaken)) {
glog.Errorf("error while trying to create network %v", err)
return nil, errors.Wrap(err, "un-retryable")
}
attempts++
// Find an open subnet by incrementing the 3rd octet by 10 for each try
// 13 times adding 10 firstSubnetAddr "192.168.49.0/24"
// at most it will add up to 169 which is still less than max allowed 255
// this is large enough to try more and not too small to not try enough
// can be tuned in the next iterations
newSubnet := net.ParseIP(subnetAddr).To4()
newSubnet[2] += byte(9 + attempts)
subnetAddr = newSubnet.String()
}
return gateway, fmt.Errorf("failed to create network after 20 attempts")
}
func tryCreateDockerNetwork(subnetAddr string, subnetMask int, name string) (net.IP, error) {
gateway := net.ParseIP(subnetAddr)
gateway.To4()[3]++ // first ip for gateway
glog.Infof("attempt to create network %s/%d with subnet: %s and gateway %s...", subnetAddr, subnetMask, name, gateway)
// options documentation https://docs.docker.com/engine/reference/commandline/network_create/#bridge-driver-options
rr, err := runCmd(exec.Command(Docker, "network", "create", "--driver=bridge", fmt.Sprintf("--subnet=%s", fmt.Sprintf("%s/%d", subnetAddr, subnetMask)), fmt.Sprintf("--gateway=%s", gateway), "-o", "--ip-masq", "-o", "--icc", fmt.Sprintf("--label=%s=%s", CreatedByLabelKey, "true"), name))
if err != nil {
// Pool overlaps with other one on this address space
if strings.Contains(rr.Output(), "Pool overlaps") {
return nil, ErrNetworkSubnetTaken
}
if strings.Contains(rr.Output(), "failed to allocate gateway") && strings.Contains(rr.Output(), "Address already in use") {
return nil, ErrNetworkGatewayTaken
}
return nil, errors.Wrapf(err, "create network %s", fmt.Sprintf("%s %s/%d", name, subnetAddr, subnetMask))
}
return gateway, nil
}
// returns subnet and gate if exists
func dockerNetworkInspect(name string) (*net.IPNet, net.IP, error) {
rr, err := runCmd(exec.Command(Docker, "network", "inspect", name, "--format", "{{(index .IPAM.Config 0).Subnet}},{{(index .IPAM.Config 0).Gateway}}"))
if err != nil {
if strings.Contains(rr.Output(), "No such network") {
return nil, nil, ErrNetworkNotFound
}
return nil, nil, err
}
// results looks like 172.17.0.0/16,172.17.0.1
ips := strings.Split(strings.TrimSpace(rr.Stdout.String()), ",")
if len(ips) == 0 {
return nil, nil, fmt.Errorf("empty IP list parsed from: %q", rr.Output())
}
_, subnet, err := net.ParseCIDR(ips[0])
if err != nil {
return nil, nil, errors.Wrapf(err, "parse subnet for %s", name)
}
var gateway net.IP
if len(ips) > 0 {
gateway = net.ParseIP(ips[1])
}
return subnet, gateway, nil
}
// RemoveNetwork removes a network
func RemoveNetwork(name string) error {
if !networkExists(name) {
return nil
}
rr, err := runCmd(exec.Command(Docker, "network", "remove", name))
if err != nil {
if strings.Contains(rr.Output(), "No such network") {
return ErrNetworkNotFound
}
// Error response from daemon: error while removing network: network mynet123 id f9e1c50b89feb0b8f4b687f3501a81b618252c9907bc20666e386d0928322387 has active endpoints
if strings.Contains(rr.Output(), "has active endpoints") {
return ErrNetworkInUse
}
}
return err
}
func networkExists(name string) bool {
_, _, err := dockerNetworkInspect(name)
if err != nil && !errors.Is(err, ErrNetworkNotFound) { // log unexpected error
glog.Warningf("Error inspecting docker network %s: %v", name, err)
}
return err == nil
}
// networkNamesByLabel returns all network names created by a label
func networkNamesByLabel(ociBin string, label string) ([]string, error) {
if ociBin != Docker {
return nil, fmt.Errorf("%s not supported", ociBin)
}
// docker network ls --filter='label=created_by.minikube.sigs.k8s.io=true' --format '{{.Name}}'
rr, err := runCmd(exec.Command(Docker, "network", "ls", fmt.Sprintf("--filter=label=%s", label), "--format", "{{.Name}}"))
if err != nil {
return nil, err
}
var lines []string
scanner := bufio.NewScanner(bytes.NewReader(rr.Stdout.Bytes()))
for scanner.Scan() {
lines = append(lines, strings.TrimSpace(scanner.Text()))
}
return lines, nil
}
// DeleteKICNetworks deletes all networks created by kic
func DeleteKICNetworks() []error {
var errs []error
ns, err := networkNamesByLabel(Docker, CreatedByLabelKey+"=true")
if err != nil {
return []error{errors.Wrap(err, "list all volume")}
}
for _, n := range ns {
err := RemoveNetwork(n)
if err != nil {
errs = append(errs, err)
}
}
if len(errs) > 0 {
return errs
}
return nil
}
......@@ -169,6 +169,11 @@ func CreateContainerNode(p CreateParams) error {
virtualization = "podman" // VIRTUALIZATION_PODMAN
}
if p.OCIBinary == Docker {
// to provide a static IP for docker
if p.Network != "" && p.IP != "" {
runArgs = append(runArgs, "--network", p.Network)
runArgs = append(runArgs, "--ip", p.IP)
}
runArgs = append(runArgs, "--volume", fmt.Sprintf("%s:/var", p.Name))
// ignore apparmore github actions docker: https://github.com/kubernetes/minikube/issues/7624
runArgs = append(runArgs, "--security-opt", "apparmor=unconfined")
......@@ -285,6 +290,10 @@ func createContainer(ociBin string, image string, opts ...createOpt) error {
if strings.Contains(rr.Output(), "Range of CPUs is from") && strings.Contains(rr.Output(), "CPUs available") { // CPUs available
return ErrCPUCountLimit
}
// example: docker: Error response from daemon: Address already in use.
if strings.Contains(rr.Output(), "Address already in use") {
return ErrIPinUse
}
return err
}
......
......@@ -43,6 +43,7 @@ const (
// CreateParams are parameters needed to create a container
type CreateParams struct {
ClusterName string // cluster(profile name) that this container belongs to
Name string // used for container name and hostname
Image string // container image to use to create the node.
ClusterLabel string // label the clusters we create using minikube so we can clean up
......@@ -56,6 +57,8 @@ type CreateParams struct {
Envs map[string]string // environment variables to pass to the container
ExtraArgs []string // a list of any extra option to pass to oci binary during creation time, for example --expose 8080...
OCIBinary string // docker or podman
Network string // network name that the container will attach to
IP string // static IP to assign for th container in the cluster network
}
// createOpt is an option for Create
......
......@@ -24,9 +24,9 @@ import (
const (
// Version is the current version of kic
Version = "v0.0.12-snapshot3"
Version = "v0.0.13-snapshot1"
// SHA of the kic base image
baseImageSHA = "1d687ba53e19dbe5fafe4cc18aa07f269ecc4b7b622f2251b5bf569ddb474e9b"
baseImageSHA = "4d43acbd0050148d4bc399931f1b15253b5e73815b63a67b8ab4a5c9e523403f"
)
var (
......@@ -48,6 +48,7 @@ var (
// Config is configuration for the kic driver used by registry
type Config struct {
ClusterName string // The cluster the container belongs to
MachineName string // maps to the container name being created
CPU int // Number of CPU cores assigned to the container
Memory int // max memory in MB
......
......@@ -31,8 +31,8 @@ import (
const domainTmpl = `
<domain type='kvm'>
<name>{{.MachineName}}</name>
<memory unit='MB'>{{.Memory}}</memory>
<name>{{.MachineName}}</name>
<memory unit='MiB'>{{.Memory}}</memory>
<vcpu>{{.CPU}}</vcpu>
<features>
<acpi/>
......
......@@ -34,12 +34,12 @@ import (
)
// HostIP gets the ip address to be used for mapping host -> VM and VM -> host
func HostIP(host *host.Host) (net.IP, error) {
func HostIP(host *host.Host, clusterName string) (net.IP, error) {
switch host.DriverName {
case driver.Docker:
return oci.RoutableHostIPFromInside(oci.Docker, host.Name)
return oci.RoutableHostIPFromInside(oci.Docker, clusterName, host.Name)
case driver.Podman:
return oci.RoutableHostIPFromInside(oci.Podman, host.Name)
return oci.RoutableHostIPFromInside(oci.Podman, clusterName, host.Name)
case driver.KVM2:
return net.ParseIP("192.168.39.1"), nil
case driver.HyperV:
......@@ -49,6 +49,7 @@ func HostIP(host *host.Host) (net.IP, error) {
for i := 0; i < v.NumField(); i++ {
if v.Type().Field(i).Name == "VSwitch" {
hypervVirtualSwitch = v.Field(i).Interface().(string)
break
}
}
......@@ -59,6 +60,7 @@ func HostIP(host *host.Host) (net.IP, error) {
if err != nil {
return []byte{}, errors.Wrap(err, fmt.Sprintf("ip for interface (%s)", hypervVirtualSwitch))
}
return ip, nil
case driver.VirtualBox:
vBoxManageCmd := driver.VBoxManagePath()
......@@ -74,6 +76,7 @@ func HostIP(host *host.Host) (net.IP, error) {
}
re = regexp.MustCompile(`(?sm)Name:\s*` + iface + `\s*$.+?IPAddress:\s*(\S+)`)
ip := re.FindStringSubmatch(string(ipList))[1]
return net.ParseIP(ip), nil
case driver.Parallels:
bin := "prlsrvctl"
......@@ -93,6 +96,7 @@ func HostIP(host *host.Host) (net.IP, error) {
return []byte{}, errors.Wrap(err, "Error getting the IP address of Parallels Shared network interface")
}
ip := ipMatch[1]
return net.ParseIP(ip), nil
case driver.HyperKit:
return net.ParseIP("192.168.64.1"), nil
......@@ -136,6 +140,7 @@ func DriverIP(api libmachine.API, machineName string) (net.IP, error) {
// Based on code from http://stackoverflow.com/questions/23529663/how-to-get-all-addresses-and-masks-from-local-interfaces-in-go
func getIPForInterface(name string) (net.IP, error) {
glog.Infof("getIPForInterface: searching for %q", name)
ints, err := net.Interfaces()
if err != nil {
return nil, err
......@@ -143,19 +148,25 @@ func getIPForInterface(name string) (net.IP, error) {
var i net.Interface
for _, in := range ints {
if strings.HasPrefix(in.Name, name) {
if strings.HasPrefix(strings.ToLower(in.Name), strings.ToLower(name)) {
glog.Infof("found prefix matching interface for %q: %q", name, in.Name)
i = in
break
}
glog.Infof("%q does not match prefix %q", in.Name, name)
}
// Didn't find prefix, let's try any substring
if i.Name == "" {
for _, in := range ints {
if strings.Contains(in.Name, name) {
if strings.Contains(strings.ToLower(in.Name), strings.ToLower(name)) {
glog.Infof("found substring matching interface for %q: %q", name, in.Name)
i = in
break
}
glog.Infof("%q does not match substring %q", in.Name, name)
}
}
......@@ -164,14 +175,15 @@ func getIPForInterface(name string) (net.IP, error) {
return nil, errors.Errorf("Could not find interface %s inside %+v", name, ints)
}
glog.Infof("Found hyperv interface: %+v\n", i)
glog.Infof("Found interface: %+v\n", i)
addrs, _ := i.Addrs()
for _, a := range addrs {
glog.Infof("interface addr: %+v", a)
if ipnet, ok := a.(*net.IPNet); ok {
if ip := ipnet.IP.To4(); ip != nil {
return ip, nil
}
}
}
return nil, errors.Errorf("Error finding IPV4 address for %s", name)
return nil, errors.Errorf("Unable to find a IPv4 address for interface %q", name)
}
......@@ -413,6 +413,7 @@ func containerdImagesPreloaded(runner command.Runner, images []string) bool {
return true
}
// ImagesPreloaded returns true if all images have been preloaded
func (r *Containerd) ImagesPreloaded(images []string) bool {
return containerdImagesPreloaded(r.Runner, images)
}
......@@ -326,6 +326,7 @@ func crioImagesPreloaded(runner command.Runner, images []string) bool {
return true
}
// ImagesPreloaded returns true if all images have been preloaded
func (r *CRIO) ImagesPreloaded(images []string) bool {
return crioImagesPreloaded(r.Runner, images)
}
......
......@@ -405,6 +405,7 @@ func dockerBoundToContainerd(runner command.Runner) bool {
return false
}
// ImagesPreloaded returns true if all images have been preloaded
func (r *Docker) ImagesPreloaded(images []string) bool {
return dockerImagesPreloaded(r.Runner, images)
}
......@@ -26,6 +26,7 @@ import (
"k8s.io/minikube/pkg/minikube/out/register"
)
// DefaultJSONOutput is a progress tracker with JSON output
var DefaultJSONOutput getter.ProgressTracker = &jsonOutput{}
type jsonOutput struct {
......
......@@ -21,6 +21,7 @@ import (
"os"
"runtime"
"sort"
"strconv"
"strings"
"github.com/golang/glog"
......@@ -111,7 +112,7 @@ func IsDocker(name string) bool {
return name == Docker
}
// IsKIC checks if the driver is a Docker for Desktop (Docker on windows or MacOs)
// IsDockerDesktop checks if the driver is a Docker for Desktop (Docker on windows or MacOs)
// for linux and exotic archs, this will be false
func IsDockerDesktop(name string) bool {
if IsDocker(name) {
......@@ -297,3 +298,15 @@ func MachineName(cc config.ClusterConfig, n config.Node) string {
}
return fmt.Sprintf("%s-%s", cc.Name, n.Name)
}
// IndexFromMachineName returns the order of the container based on it is name
func IndexFromMachineName(machineName string) int {
// minikube-m02
sp := strings.Split(machineName, "-")
m := strings.Trim(sp[len(sp)-1], "m") // m02
i, err := strconv.Atoi(m)
if err != nil {
return 1
}
return i
}
......@@ -23,6 +23,7 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/registry"
)
......@@ -201,3 +202,160 @@ func TestSuggest(t *testing.T) {
})
}
}
func TestMachineName(t *testing.T) {
testsCases := []struct {
ClusterConfig config.ClusterConfig
Want string
}{
{
ClusterConfig: config.ClusterConfig{Name: "minikube",
Nodes: []config.Node{
{
Name: "",
IP: "172.17.0.3",
Port: 8443,
KubernetesVersion: "v1.19.2",
ControlPlane: true,
Worker: true,
},
},
},
Want: "minikube",
},
{
ClusterConfig: config.ClusterConfig{Name: "p2",
Nodes: []config.Node{
{
Name: "",
IP: "172.17.0.3",
Port: 8443,
KubernetesVersion: "v1.19.2",
ControlPlane: true,
Worker: true,
},
{
Name: "m2",
IP: "172.17.0.4",
Port: 0,
KubernetesVersion: "v1.19.2",
ControlPlane: false,
Worker: true,
},
},
},
Want: "p2-m2",
},
}
for _, tc := range testsCases {
got := MachineName(tc.ClusterConfig, tc.ClusterConfig.Nodes[len(tc.ClusterConfig.Nodes)-1])
if got != tc.Want {
t.Errorf("Expected MachineName to be %q but got %q", tc.Want, got)
}
}
}
func TestIndexFromMachineName(t *testing.T) {
testCases := []struct {
Name string
MachineName string
Want int
}{
{
Name: "default",
MachineName: "minikube",
Want: 1},
{
Name: "second-node",
MachineName: "minikube-m02",
Want: 2},
{
Name: "funny",
MachineName: "hahaha",
Want: 1},
{
Name: "dash-profile",
MachineName: "my-dashy-minikube",
Want: 1},
{
Name: "dash-profile-second-node",
MachineName: "my-dashy-minikube-m02",
Want: 2},
{
Name: "michivious-user",
MachineName: "michivious-user-m02-m03",
Want: 3},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
got := IndexFromMachineName(tc.MachineName)
if got != tc.Want {
t.Errorf("want order %q but got %q", tc.Want, got)
}
})
}
}
// test indexFroMachine against cluster config
func TestIndexFromMachineNameClusterConfig(t *testing.T) {
testsCases := []struct {
ClusterConfig config.ClusterConfig
Want int
}{
{
ClusterConfig: config.ClusterConfig{Name: "minikube",
Nodes: []config.Node{
{
Name: "",
IP: "172.17.0.3",
Port: 8443,
KubernetesVersion: "v1.19.2",
ControlPlane: true,
Worker: true,
},
},
},
Want: 1,
},
{
ClusterConfig: config.ClusterConfig{Name: "p2",
Nodes: []config.Node{
{
Name: "",
IP: "172.17.0.3",
Port: 8443,
KubernetesVersion: "v1.19.2",
ControlPlane: true,
Worker: true,
},
{
Name: "m2",
IP: "172.17.0.4",
Port: 0,
KubernetesVersion: "v1.19.2",
ControlPlane: false,
Worker: true,
},
},
},
Want: 2,
},
}
for _, tc := range testsCases {
got := IndexFromMachineName(MachineName(tc.ClusterConfig, tc.ClusterConfig.Nodes[len(tc.ClusterConfig.Nodes)-1]))
if got != tc.Want {
t.Errorf("expected IndexFromMachineName to be %d but got %d", tc.Want, got)
}
}
}
......@@ -42,6 +42,7 @@ import (
var exclude = []string{
"{{.error}}",
"{{.url}}",
" {{.url}}",
"{{.msg}}: {{.err}}",
"{{.key}}={{.value}}",
"opt {{.docker_option}}",
......@@ -50,6 +51,7 @@ var exclude = []string{
"\\n",
"==\u003e {{.name}} \u003c==",
"- {{.profile}}",
" - {{.profile}}",
}
// ErrMapFile is a constant to refer to the err_map file, which contains the Advice strings.
......@@ -333,9 +335,6 @@ func checkString(s string) string {
// Parse out quote marks
stringToTranslate := s[1 : len(s)-1]
// Trim whitespace
stringToTranslate = strings.TrimSpace(stringToTranslate)
// Don't translate integers
if _, err := strconv.Atoi(stringToTranslate); err == nil {
return ""
......
......@@ -40,27 +40,27 @@ func MaybeDisplayAdvice(err error, driver string) {
if errors.Is(err, oci.ErrExitedUnexpectedly) || errors.Is(err, oci.ErrDaemonInfo) {
out.T(style.Tip, "If you are still interested to make {{.driver_name}} driver work. The following suggestions might help you get passed this issue:", out.V{"driver_name": driver})
if driver == oci.Docker || driver == oci.Podman {
out.T(style.Empty, `
- Prune unused {{.driver_name}} images, volumes, networks and abandoned containers.
out.String("\n\t")
out.T(style.Empty, `- Prune unused {{.driver_name}} images, volumes, networks and abandoned containers.
{{.driver_name}} system prune --volumes`, out.V{"driver_name": driver})
{{.driver_name}} system prune --volumes`, out.V{"driver_name": driver})
}
out.T(style.Empty, `
- Restart your {{.driver_name}} service`, out.V{"driver_name": driver})
out.String("\n\t")
out.T(style.Empty, `- Restart your {{.driver_name}} service`, out.V{"driver_name": driver})
if runtime.GOOS != "linux" {
out.T(style.Empty, `
- Ensure your {{.driver_name}} daemon has access to enough CPU/memory resources. `, out.V{"driver_name": driver})
out.String("\n\t")
out.T(style.Empty, `- Ensure your {{.driver_name}} daemon has access to enough CPU/memory resources.`, out.V{"driver_name": driver})
if runtime.GOOS == "darwin" && driver == oci.Docker {
out.T(style.Empty, `
- Docs https://docs.docker.com/docker-for-mac/#resources`, out.V{"driver_name": driver})
out.String("\n\t")
out.T(style.Empty, `- Docs https://docs.docker.com/docker-for-mac/#resources`, out.V{"driver_name": driver})
}
if runtime.GOOS == "windows" && driver == oci.Docker {
out.T(style.Empty, `
- Docs https://docs.docker.com/docker-for-windows/#resources`, out.V{"driver_name": driver})
out.String("\n\t")
out.T(style.Empty, `- Docs https://docs.docker.com/docker-for-windows/#resources`, out.V{"driver_name": driver})
}
}
out.T(style.Empty, `
- Delete and recreate minikube cluster
out.String("\n\t")
out.T(style.Empty, `- Delete and recreate minikube cluster
minikube delete
minikube start --driver={{.driver_name}}`, out.V{"driver_name": driver})
// TODO #8348: maybe advice user if to set the --force-systemd https://github.com/kubernetes/minikube/issues/8348
......
......@@ -46,7 +46,7 @@ func deleteOrphanedKIC(ociBin string, name string) {
glog.Infof("couldn't inspect container %q before deleting: %v", name, err)
return
}
// allow no more than 5 seconds for delting the container
// allow no more than 5 seconds for deleting the container
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
......
......@@ -29,6 +29,7 @@ import (
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/util"
)
// HostInfo holds information on the user's machine
......@@ -38,10 +39,6 @@ type HostInfo struct {
DiskSize int64
}
func megs(bytes uint64) int64 {
return int64(bytes / 1024 / 1024)
}
// CachedHostInfo returns system information such as memory,CPU, DiskSize
func CachedHostInfo() (*HostInfo, error, error, error) {
var cpuErr, memErr, diskErr error
......@@ -61,8 +58,8 @@ func CachedHostInfo() (*HostInfo, error, error, error) {
var info HostInfo
info.CPUs = len(i)
info.Memory = megs(v.Total)
info.DiskSize = megs(d.Total)
info.Memory = util.ConvertUnsignedBytesToMB(v.Total)
info.DiskSize = util.ConvertUnsignedBytesToMB(d.Total)
return &info, cpuErr, memErr, diskErr
}
......
......@@ -127,7 +127,7 @@ func beginDownloadKicBaseImage(g *errgroup.Group, cc *config.ClusterConfig, down
// If we end up using a fallback image, notify the user
defer func() {
if finalImg != "" && finalImg != baseImg {
out.WarningT(fmt.Sprintf("minikube was unable to download %s, but successfully downloaded %s as a fallback image", image.Tag(cc.KicBaseImage), image.Tag(finalImg)))
out.WarningT(fmt.Sprintf("minikube was unable to download %s, but successfully downloaded %s as a fallback image", image.Tag(baseImg), image.Tag(finalImg)))
cc.KicBaseImage = finalImg
}
}()
......@@ -136,7 +136,7 @@ func beginDownloadKicBaseImage(g *errgroup.Group, cc *config.ClusterConfig, down
glog.Infof("successfully loaded %s from cached tarball", img)
// strip the digest from the img before saving it in the config
// because loading an image from tarball to daemon doesn't load the digest
finalImg = image.Tag(img)
finalImg = img
return nil
}
glog.Infof("Downloading %s to local daemon", img)
......
......@@ -94,7 +94,7 @@ func Start(starter Starter, apiServer bool) (*kubeconfig.Settings, error) {
showVersionInfo(starter.Node.KubernetesVersion, cr)
// Add "host.minikube.internal" DNS alias (intentionally non-fatal)
hostIP, err := cluster.HostIP(starter.Host)
hostIP, err := cluster.HostIP(starter.Host, starter.Cfg.Name)
if err != nil {
glog.Errorf("Unable to get host IP: %v", err)
} else if err := machine.AddHostAlias(starter.Runner, constants.HostAlias, hostIP); err != nil {
......
......@@ -33,7 +33,9 @@ const (
var (
outputFile io.Writer = os.Stdout
GetUUID = randomID
//GetUUID returns the UUID function to use
GetUUID = randomID
eventFile *os.File
)
......
......@@ -110,6 +110,7 @@ type Info struct {
data map[string]string
}
// Type returns the cloud events compatible type of this struct
func (s *Info) Type() string {
return "io.k8s.sigs.minikube.info"
}
......@@ -128,6 +129,7 @@ type Error struct {
data map[string]string
}
// NewError returns a new Error type
func NewError(err string) *Error {
return &Error{
map[string]string{
......@@ -148,6 +150,7 @@ func NewErrorExitCode(err string, exitcode int, additionalData ...map[string]str
return e
}
// Type returns the cloud events compatible type of this struct
func (s *Error) Type() string {
return "io.k8s.sigs.minikube.error"
}
......@@ -62,7 +62,7 @@ func init() {
Reg = Register{
// Expected step orders, organized by the initial step seen
steps: map[RegStep][]RegStep{
InitialSetup: []RegStep{
InitialSetup: {
InitialSetup,
SelectingDriver,
DownloadingArtifacts,
......@@ -78,10 +78,10 @@ func init() {
Done,
},
Stopping: []RegStep{Stopping, Done},
Pausing: []RegStep{Pausing, Done},
Unpausing: []RegStep{Unpausing, Done},
Deleting: []RegStep{Deleting, Stopping, Deleting, Done},
Stopping: {Stopping, Done},
Pausing: {Pausing, Done},
Unpausing: {Unpausing, Done},
Deleting: {Deleting, Stopping, Deleting, Done},
},
}
}
......
......@@ -38,7 +38,8 @@ func TestTimeCommandLogs(t *testing.T) {
if !ok {
t.Fatalf("expected log %s but didn't find it", log)
}
if actualTime < time {
// Let's give a little wiggle room so we don't fail if time is 3 and actualTime is 2.999
if actualTime < time && time-actualTime > 0.001 {
t.Fatalf("expected log \"%s\" to take more time than it actually did. got %v, expected > %v", log, actualTime, time)
}
}
......
......@@ -34,6 +34,7 @@ package reason
const (
// Reserved UNIX exit codes
ExFailure = 1 // Failure represents a general failure code
ExInterrupted = 2 // Ctrl-C (SIGINT)
......@@ -56,6 +57,7 @@ const (
// navailableOff = 9 // (~EX_UNAVAILABLE)
// Error codes specific to the minikube program
ExProgramError = 10 // generic error
ExProgramUsage = 14 // bad command-line options
ExProgramConflict = 11 // can't do what you want because of existing data
......@@ -64,6 +66,7 @@ const (
ExProgramConfig = 18 // bad configuration specified
// Error codes specific to resource limits (exit code layout follows no rules)
ExResourceError = 20
ExInsufficientMemory = 23
ExInsufficientStorage = 26
......@@ -71,6 +74,7 @@ const (
ExInsufficientCores = 29
// Error codes specific to the host
ExHostError = 30
ExHostConflict = 31
ExHostTimeout = 32
......@@ -81,6 +85,7 @@ const (
ExHostConfig = 38
// Error codes specific to remote networking
ExInternetError = 40
ExInternetConflict = 41
ExInternetTimeout = 42
......@@ -89,6 +94,7 @@ const (
ExInternetUnavailable = 49
// Error codes specific to the libmachine driver
ExDriverError = 50
ExDriverConflict = 51
ExDriverTimeout = 52
......@@ -100,11 +106,14 @@ const (
ExDriverUnavailable = 59
// Error codes specific to the driver provider
ExProviderError = 60
ExProviderConflict = 61
ExProviderTimeout = 62
ExProviderNotRunning = 63
// Reserve 64 for the moment as it used to be usage
ExProviderNotFound = 65
ExProviderUnsupported = 66
ExProviderPermission = 67
......@@ -112,6 +121,7 @@ const (
ExProviderUnavailable = 69 // In common use
// Error codes specific to local networking
ExLocalNetworkError = 70
ExLocalNetworkConflict = 71
ExLocalNetworkTimeout = 72
......@@ -121,6 +131,7 @@ const (
ExLocalNetworkUnavailable = 79
// Error codes specific to the guest host
ExGuestError = 80
ExGuestConflict = 81
ExGuestTimeout = 82
......@@ -132,12 +143,14 @@ const (
ExGuestUnavailable = 89
// Error codes specific to the container runtime
ExRuntimeError = 90
ExRuntimeNotRunning = 93
ExRuntimeNotFound = 95
ExRuntimeUnavailable = 99
// Error codes specific to the Kubernetes control plane
ExControlPlaneError = 100
ExControlPlaneConflict = 101
ExControlPlaneTimeout = 102
......@@ -148,6 +161,7 @@ const (
ExControlPlaneUnavailable = 109
// Error codes specific to a Kubernetes service
ExSvcError = 110
ExSvcConflict = 111
ExSvcTimeout = 112
......
......@@ -61,6 +61,7 @@ type Kind struct {
NoMatch bool
}
// IssueURLs returns URLs for issues
func (k *Kind) IssueURLs() []string {
is := []string{}
for _, i := range k.Issues {
......
......@@ -27,6 +27,7 @@ import (
"github.com/docker/machine/libmachine/drivers"
"github.com/golang/glog"
"github.com/pkg/errors"
"k8s.io/minikube/pkg/drivers/kic"
"k8s.io/minikube/pkg/drivers/kic/oci"
"k8s.io/minikube/pkg/minikube/config"
......@@ -60,6 +61,7 @@ func configure(cc config.ClusterConfig, n config.Node) (interface{}, error) {
}
return kic.NewDriver(kic.Config{
ClusterName: cc.Name,
MachineName: driver.MachineName(cc, n),
StorePath: localpath.MiniPath(),
ImageDigest: cc.KicBaseImage,
......@@ -86,34 +88,41 @@ func status() registry.State {
ctx, cancel := context.WithTimeout(context.Background(), 6*time.Second)
defer cancel()
// Quickly returns an error code if server is not running
cmd := exec.CommandContext(ctx, oci.Docker, "version", "--format", "{{.Server.Os}}-{{.Server.Version}}")
o, err := cmd.Output()
output := string(o)
if strings.Contains(output, "windows-") {
return registry.State{Error: oci.ErrWindowsContainers, Installed: true, Healthy: false, Fix: "Change container type to \"linux\" in Docker Desktop settings", Doc: docURL + "#verify-docker-container-type-is-linux"}
if err != nil {
if ctx.Err() == context.DeadlineExceeded {
err = errors.Wrapf(err, "deadline exceeded running %q", strings.Join(cmd.Args, " "))
}
}
if err == nil {
glog.Infof("docker version: %s", output)
return checkNeedsImprovement()
}
glog.Warningf("docker version returned error: %v", err)
glog.Warningf("docker returned error: %v", err)
if exitErr, ok := err.(*exec.ExitError); ok {
stderr := strings.TrimSpace(string(exitErr.Stderr))
newErr := fmt.Errorf(`%q %v: %s`, strings.Join(cmd.Args, " "), exitErr, stderr)
return suggestFix(stderr, newErr)
}
// Basic timeout
if ctx.Err() == context.DeadlineExceeded {
glog.Warningf("%q timed out. ", strings.Join(cmd.Args, " "))
return registry.State{Error: err, Installed: true, Healthy: false, Fix: "Restart the Docker service", Doc: docURL}
}
if exitErr, ok := err.(*exec.ExitError); ok {
stderr := strings.TrimSpace(string(exitErr.Stderr))
newErr := fmt.Errorf(`%q %v: %s`, strings.Join(cmd.Args, " "), exitErr, stderr)
return suggestFix(stderr, newErr)
glog.Infof("docker version: %s", o)
if strings.Contains(string(o), "windows-") {
return registry.State{Error: oci.ErrWindowsContainers, Installed: true, Healthy: false, Fix: "Change container type to \"linux\" in Docker Desktop settings", Doc: docURL + "#verify-docker-container-type-is-linux"}
}
return registry.State{Error: err, Installed: true, Healthy: false, Doc: docURL}
si, err := oci.CachedDaemonInfo("docker")
if err != nil {
// No known fix because we haven't yet seen a failure here
return registry.State{Error: errors.Wrap(err, "docker info"), Installed: true, Healthy: false, Doc: docURL}
}
for _, serr := range si.Errors {
return suggestFix(serr, fmt.Errorf("docker info error: %s", serr))
}
return checkNeedsImprovement()
}
// checkNeedsImprovement if overlay mod is installed on a system
......@@ -121,6 +130,7 @@ func checkNeedsImprovement() registry.State {
if runtime.GOOS == "linux" {
return checkOverlayMod()
}
return registry.State{Installed: true, Healthy: true}
}
......@@ -128,15 +138,18 @@ func checkNeedsImprovement() registry.State {
func checkOverlayMod() registry.State {
if _, err := os.Stat("/sys/module/overlay"); err == nil {
glog.Info("overlay module found")
return registry.State{Installed: true, Healthy: true}
}
if _, err := os.Stat("/sys/module/overlay2"); err == nil {
glog.Info("overlay2 module found")
return registry.State{Installed: true, Healthy: true}
}
glog.Warningf("overlay modules were not found")
return registry.State{NeedsImprovement: true, Installed: true, Healthy: true, Fix: "enable the overlay Linux kernel module using 'modprobe overlay'"}
}
......@@ -150,10 +163,14 @@ func suggestFix(stderr string, err error) registry.State {
return registry.State{Error: err, Installed: true, Running: false, Healthy: false, Fix: "Start the Docker service. If Docker is already running, you may need to reset Docker to factory settings with: Settings > Reset.", Doc: "https://github.com/docker/for-win/issues/1825#issuecomment-450501157"}
}
if strings.Contains(stderr, "Cannot connect") || strings.Contains(stderr, "refused") || strings.Contains(stderr, "Is the docker daemon running") || strings.Contains(stderr, "docker daemon is not running") {
if dockerNotRunning(stderr) {
return registry.State{Error: err, Installed: true, Running: false, Healthy: false, Fix: "Start the Docker service", Doc: docURL}
}
// We don't have good advice, but at least we can provide a good error message
return registry.State{Error: err, Installed: true, Running: true, Healthy: false, Doc: docURL}
}
func dockerNotRunning(s string) bool {
return strings.Contains(s, "Cannot connect") || strings.Contains(s, "refused") || strings.Contains(s, "Is the docker daemon running") || strings.Contains(s, "docker daemon is not running")
}
......@@ -73,6 +73,7 @@ func configure(cc config.ClusterConfig, n config.Node) (interface{}, error) {
}
return kic.NewDriver(kic.Config{
ClusterName: cc.Name,
MachineName: driver.MachineName(cc, n),
StorePath: localpath.MiniPath(),
ImageDigest: strings.Split(cc.KicBaseImage, "@")[0], // for podman does not support docker images references with both a tag and digest.
......
......@@ -45,6 +45,7 @@ func MakeTempDir() string {
return localpath.MiniPath()
}
// RemoveTempDir removes the temp dir
func RemoveTempDir(tempdir string) {
if filepath.Base(tempdir) == ".minikube" {
tempdir = filepath.Dir(tempdir)
......
/*
Copyright 2020 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 monitor
const (
// GithubAccessTokenEnvVar is the env var name to use
GithubAccessTokenEnvVar = "GITHUB_ACCESS_TOKEN"
// OkToTestLabel is the github label for ok-to-test
OkToTestLabel = "ok-to-test"
// GithubOwner is the owner of the github repository
GithubOwner = "kubernetes"
// GithubRepo is the name of the github repository
GithubRepo = "minikube"
// BotName is the name of the minikube Pull Request bot
BotName = "minikube-pr-bot"
)
/*
Copyright 2020 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 monitor
import (
"bytes"
"context"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"github.com/pkg/errors"
)
// RunMkcmp runs minikube built at the given pr against minikube at master
func RunMkcmp(ctx context.Context, pr int) (string, error) {
// run 'git pull' so that minikube dir is up to date
if _, err := runCmdInMinikube(ctx, []string{"git", "pull", "origin", "master"}); err != nil {
return "", errors.Wrap(err, "running git pull")
}
mkcmpPath := "out/mkcmp"
minikubePath := "out/minikube"
if _, err := runCmdInMinikube(ctx, []string{"make", mkcmpPath, minikubePath}); err != nil {
return "", errors.Wrap(err, "building minikube and mkcmp at head")
}
return runCmdInMinikube(ctx, []string{mkcmpPath, minikubePath, fmt.Sprintf("pr://%d", pr)})
}
// runCmdInMinikube runs the cmd and return stdout
func runCmdInMinikube(ctx context.Context, command []string) (string, error) {
cmd := exec.CommandContext(ctx, command[0], command[1:]...)
cmd.Dir = minikubeDir()
cmd.Env = os.Environ()
buf := bytes.NewBuffer([]byte{})
cmd.Stdout = buf
log.Printf("Running: %v", cmd.Args)
if err := cmd.Run(); err != nil {
return "", errors.Wrapf(err, "running %v: %v", cmd.Args, buf.String())
}
return buf.String(), nil
}
func minikubeDir() string {
return filepath.Join(os.Getenv("HOME"), "minikube")
}
/*
Copyright 2020 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 monitor
import (
"context"
"log"
"os"
"time"
"github.com/google/go-github/github"
"github.com/pkg/errors"
"golang.org/x/oauth2"
)
// Client provides the context and client with necessary auth
// for interacting with the Github API
type Client struct {
ctx context.Context
*github.Client
owner string
repo string
}
// NewClient returns a github client with the necessary auth
func NewClient(ctx context.Context, owner, repo string) *Client {
githubToken := os.Getenv(GithubAccessTokenEnvVar)
// Setup the token for github authentication
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: githubToken},
)
tc := oauth2.NewClient(context.Background(), ts)
// Return a client instance from github
client := github.NewClient(tc)
return &Client{
ctx: ctx,
Client: client,
owner: owner,
repo: repo,
}
}
// CommentOnPR comments message on the PR
func (g *Client) CommentOnPR(pr int, message string) error {
comment := &github.IssueComment{
Body: &message,
}
log.Printf("Creating comment on PR %d: %s", pr, message)
_, _, err := g.Client.Issues.CreateComment(g.ctx, g.owner, g.repo, pr, comment)
if err != nil {
return errors.Wrap(err, "creating github comment")
}
log.Printf("Successfully commented on PR %d.", pr)
return nil
}
// ListOpenPRsWithLabel returns all open PRs with the specified label
func (g *Client) ListOpenPRsWithLabel(label string) ([]int, error) {
validPrs := []int{}
prs, _, err := g.Client.PullRequests.List(g.ctx, g.owner, g.repo, &github.PullRequestListOptions{})
if err != nil {
return nil, errors.Wrap(err, "listing pull requests")
}
for _, pr := range prs {
if prContainsLabel(pr.Labels, label) {
validPrs = append(validPrs, pr.GetNumber())
}
}
return validPrs, nil
}
func prContainsLabel(labels []*github.Label, label string) bool {
for _, l := range labels {
if l == nil {
continue
}
if l.GetName() == label {
return true
}
}
return false
}
// NewCommitsExist checks if new commits exist since minikube-pr-bot
// commented on the PR. If so, return true.
func (g *Client) NewCommitsExist(pr int, login string) (bool, error) {
lastCommentTime, err := g.timeOfLastComment(pr, login)
if err != nil {
return false, errors.Wrapf(err, "getting time of last comment by %s on pr %d", login, pr)
}
lastCommitTime, err := g.timeOfLastCommit(pr)
if err != nil {
return false, errors.Wrapf(err, "getting time of last commit on pr %d", pr)
}
return lastCommentTime.Before(lastCommitTime), nil
}
func (g *Client) timeOfLastCommit(pr int) (time.Time, error) {
var commits []*github.RepositoryCommit
page := 0
resultsPerPage := 30
for {
c, _, err := g.Client.PullRequests.ListCommits(g.ctx, g.owner, g.repo, pr, &github.ListOptions{
Page: page,
PerPage: resultsPerPage,
})
if err != nil {
return time.Time{}, err
}
commits = append(commits, c...)
if len(c) < resultsPerPage {
break
}
page++
}
lastCommitTime := time.Time{}
for _, c := range commits {
if newCommitTime := c.GetCommit().GetAuthor().GetDate(); newCommitTime.After(lastCommitTime) {
lastCommitTime = newCommitTime
}
}
return lastCommitTime, nil
}
func (g *Client) timeOfLastComment(pr int, login string) (time.Time, error) {
var comments []*github.IssueComment
page := 0
resultsPerPage := 30
for {
c, _, err := g.Client.Issues.ListComments(g.ctx, g.owner, g.repo, pr, &github.IssueListCommentsOptions{
ListOptions: github.ListOptions{
Page: page,
PerPage: resultsPerPage,
},
})
if err != nil {
return time.Time{}, err
}
comments = append(comments, c...)
if len(c) < resultsPerPage {
break
}
page++
}
// go through comments backwards to find the most recent
lastCommentTime := time.Time{}
for _, c := range comments {
if u := c.GetUser(); u != nil {
if u.GetLogin() == login {
if c.GetCreatedAt().After(lastCommentTime) {
lastCommentTime = c.GetCreatedAt()
}
}
}
}
return lastCommentTime, nil
}
......@@ -24,7 +24,7 @@ import (
"strconv"
"github.com/blang/semver"
"github.com/docker/go-units"
units "github.com/docker/go-units"
"github.com/pkg/errors"
)
......@@ -47,6 +47,21 @@ func CalculateSizeInMB(humanReadableSize string) (int, error) {
return int(size / units.MiB), nil
}
// ConvertMBToBytes converts MB to bytes
func ConvertMBToBytes(mbSize int) int64 {
return int64(mbSize) * units.MiB
}
// ConvertBytesToMB converts bytes to MB
func ConvertBytesToMB(byteSize int64) int {
return int(ConvertUnsignedBytesToMB(uint64(byteSize)))
}
// ConvertUnsignedBytesToMB converts bytes to MB
func ConvertUnsignedBytesToMB(byteSize uint64) int64 {
return int64(byteSize / units.MiB)
}
// GetBinaryDownloadURL returns a suitable URL for the platform
func GetBinaryDownloadURL(version, platform string) string {
switch platform {
......
......@@ -39,11 +39,11 @@ minikube addons SUBCOMMAND [flags]
## minikube addons configure
Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list
Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list
### Synopsis
Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list
Configures the addon w/ADDON_NAME within minikube (example: minikube addons configure registry-creds). For a list of available addons use: minikube addons list
```
minikube addons configure ADDON_NAME [flags]
......
......@@ -11,8 +11,7 @@ Generate command completion for a shell
### Synopsis
Outputs minikube shell completion for the given shell (bash, zsh or fish)
Outputs minikube shell completion for the given shell (bash, zsh or fish)
This depends on the bash-completion binary. Example installation instructions:
OS X:
......
......@@ -26,7 +26,7 @@ minikube start [flags]
--apiserver-names stringArray A set of apiserver names which are used in the generated certificate for kubernetes. This can be used if you want to make the apiserver available from outside the machine
--apiserver-port int The apiserver listening port (default 8443)
--auto-update-drivers If set, automatically updates drivers to the latest version. Defaults to true. (default true)
--base-image string The base image to use for docker/podman drivers. Intended for local development. (default "gcr.io/k8s-minikube/kicbase:v0.0.12-snapshot3@sha256:1d687ba53e19dbe5fafe4cc18aa07f269ecc4b7b622f2251b5bf569ddb474e9b")
--base-image string The base image to use for docker/podman drivers. Intended for local development. (default "gcr.io/k8s-minikube/kicbase:v0.0.13-snapshot1@sha256:4d43acbd0050148d4bc399931f1b15253b5e73815b63a67b8ab4a5c9e523403f")
--cache-images If true, cache docker images for the current bootstrapper and load them into the machine. Always false with --driver=none. (default true)
--cni string CNI plug-in to use. Valid options: auto, bridge, calico, cilium, flannel, kindnet, or path to a CNI manifest (default: auto)
--container-runtime string The container runtime to be used (docker, cri-o, containerd). (default "docker")
......
......@@ -41,7 +41,7 @@ The list of outstanding items are at http://tinyurl.com/mk-tparty/daily-triage -
The most important level of categorizing the issue is defining what type it is.
We typically want at least one of the following labels on every issue, and some issues may fall into multiple categories:
- `triage/support` - The default for most incoming issues
- `kind/support` - The default for most incoming issues
- `kind/bug` - When it’s a bug or we aren’t delivering the best user experience
Other possibilities:
......@@ -104,7 +104,7 @@ Suspected **Root cause**:
## Prioritization
If the issue is not `triage/support`, it needs a [priority label](https://github.com/kubernetes/community/blob/master/contributors/guide/issue-triage.md#define-priority):
If the issue is not `kind/support`, it needs a [priority label](https://github.com/kubernetes/community/blob/master/contributors/guide/issue-triage.md#define-priority):
`priority/critical-urgent` - someones top priority ASAP, such as security issue, user-visible bug, or build breakage. Rarely used.
......
......@@ -39,7 +39,7 @@ Also see [co/kvm2 open issues](https://github.com/kubernetes/minikube/labels/co%
### Nested Virtulization
If you are running KVM in a nested virtualization environment ensure your config the kernel modules correctly follow either [this](https://stafwag.github.io/blog/blog/2018/06/04/nested-virtualization-in-kvm/) or [this](VM follow to config the kernel modules. also https://computingforgeeks.com/how-to-install-kvm-virtualization-on-debian/) tutorial.
If you are running KVM in a nested virtualization environment ensure your config the kernel modules correctly follow either [this](https://stafwag.github.io/blog/blog/2018/06/04/nested-virtualization-in-kvm/) or [this](https://computingforgeeks.com/how-to-install-kvm-virtualization-on-debian/) tutorial.
## Troubleshooting
* Run `virt-host-validate` and check for the suggestions.
......
......@@ -5,7 +5,7 @@ weight: 1
date: 2020-07-15
---
If you have a containerized GCP app with a Kubernetes yaml, you can automatically add your credentials to all your deployed pods dynamically with this minikube addon. You just need to have a credentials file, which can be generated with `gcloud auth login`. If you already have a json credentials file you want specify, use the GOOGLE_APPLICATION_CREDENTIALS environment variable.
If you have a containerized GCP app with a Kubernetes yaml, you can automatically add your credentials to all your deployed pods dynamically with this minikube addon. You just need to have a credentials file, which can be generated with `gcloud auth application-default login`. If you already have a json credentials file you want specify, use the GOOGLE_APPLICATION_CREDENTIALS environment variable.
- Start a cluster:
```
......
......@@ -22,6 +22,7 @@ The NO_PROXY variable here is important: Without setting it, minikube may not be
* **192.168.99.0/24**: Used by the minikube VM. Configurable for some hypervisors via `--host-only-cidr`
* **192.168.39.0/24**: Used by the minikube kvm2 driver.
* **192.168.49.0/24**: Used by the minikube docker driver's first cluster.
* **10.96.0.0/12**: Used by service cluster IP's. Configurable via `--service-cluster-ip-range`
One important note: If NO_PROXY is required by non-Kubernetes applications, such as Firefox or Chrome, you may want to specifically add the minikube IP to the comma-separated list, as they may not understand IP ranges ([#3827](https://github.com/kubernetes/minikube/issues/3827)).
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册