commit
993504e054
|
|
@ -811,7 +811,6 @@ func suggestMemoryAllocation(sysLimit int, containerLimit int, nodes int) int {
|
|||
|
||||
// validateMemorySize validates the memory size matches the minimum recommended
|
||||
func validateMemorySize(req int, drvName string) {
|
||||
|
||||
sysLimit, containerLimit, err := memoryLimits(drvName)
|
||||
if err != nil {
|
||||
glog.Warningf("Unable to query memory limits: %v", err)
|
||||
|
|
@ -822,7 +821,7 @@ func validateMemorySize(req int, drvName string) {
|
|||
out.V{"requested": req, "mininum": minUsableMem})
|
||||
}
|
||||
if req < minRecommendedMem && !viper.GetBool(force) {
|
||||
out.T(out.Notice, "Requested memory allocation ({{.requested}}MB) is less than the recommended minimum {{.recommended}}MB. Kubernetes may crash unexpectedly.",
|
||||
out.WarningT("Requested memory allocation ({{.requested}}MB) is less than the recommended minimum {{.recommended}}MB. Kubernetes may crash unexpectedly.",
|
||||
out.V{"requested": req, "recommended": minRecommendedMem})
|
||||
}
|
||||
|
||||
|
|
@ -830,13 +829,13 @@ func validateMemorySize(req int, drvName string) {
|
|||
// in Docker Desktop if you allocate 2 GB the docker info shows: Total Memory: 1.945GiB which becomes 1991 when we calculate the MBs
|
||||
// thats why it is not same number as other drivers which is 2 GB
|
||||
if containerLimit < 1991 {
|
||||
out.T(out.Tip, `Increase Docker for Desktop memory to at least 2.5GB or more:
|
||||
out.WarningT(`Increase Docker for Desktop memory to at least 2.5GB or more:
|
||||
|
||||
Docker for Desktop > Settings > Resources > Memory
|
||||
|
||||
`)
|
||||
} else if containerLimit < 2997 && sysLimit > 8000 { // for users with more than 8 GB advice 3 GB
|
||||
out.T(out.Tip, `Your system has {{.system_limit}}MB memory but Docker has only {{.container_limit}}MB. For a better performance increase to at least 3GB.
|
||||
out.WarningT(`Your system has {{.system_limit}}MB memory but Docker has only {{.container_limit}}MB. For a better performance increase to at least 3GB.
|
||||
|
||||
Docker for Desktop > Settings > Resources > Memory
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/shirou/gopsutil/mem"
|
||||
"k8s.io/minikube/pkg/minikube/command"
|
||||
"k8s.io/minikube/pkg/minikube/out"
|
||||
"k8s.io/minikube/pkg/minikube/out/register"
|
||||
)
|
||||
|
||||
type hostInfo struct {
|
||||
|
|
@ -77,6 +78,7 @@ func showLocalOsRelease() {
|
|||
return
|
||||
}
|
||||
|
||||
register.Reg.SetStep(register.LocalOSRelease)
|
||||
out.T(out.Provisioner, "OS release is {{.pretty_name}}", out.V{"pretty_name": osReleaseInfo.PrettyName})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -253,6 +253,7 @@ func showHostInfo(cfg config.ClusterConfig) {
|
|||
if driver.BareMetal(cfg.Driver) {
|
||||
info, err := getHostInfo()
|
||||
if err == nil {
|
||||
register.Reg.SetStep(register.RunningLocalhost)
|
||||
out.T(out.StartingNone, "Running on localhost (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB, Disk={{.disk_size}}MB) ...", out.V{"number_of_cpus": info.CPUs, "memory_size": info.Memory, "disk_size": info.DiskSize})
|
||||
}
|
||||
return
|
||||
|
|
|
|||
|
|
@ -510,6 +510,7 @@ func tryRegistry(r command.Runner, driverName string, imageRepository string) {
|
|||
|
||||
// prepareNone prepares the user and host for the joy of the "none" driver
|
||||
func prepareNone() {
|
||||
register.Reg.SetStep(register.ConfiguringLHEnv)
|
||||
out.T(out.StartingNone, "Configuring local host environment ...")
|
||||
if viper.GetBool(config.WantNoneDriverWarning) {
|
||||
out.ErrT(out.Empty, "")
|
||||
|
|
|
|||
|
|
@ -26,8 +26,11 @@ const (
|
|||
SelectingDriver RegStep = "Selecting Driver"
|
||||
DownloadingArtifacts RegStep = "Downloading Artifacts"
|
||||
StartingNode RegStep = "Starting Node"
|
||||
RunningLocalhost RegStep = "Running on Localhost"
|
||||
LocalOSRelease RegStep = "Local OS Release"
|
||||
CreatingContainer RegStep = "Creating Container"
|
||||
CreatingVM RegStep = "Creating VM"
|
||||
ConfiguringLHEnv RegStep = "Configuring Localhost Environment"
|
||||
PreparingKubernetes RegStep = "Preparing Kubernetes"
|
||||
VerifyingKubernetes RegStep = "Verifying Kubernetes"
|
||||
EnablingAddons RegStep = "Enabling Addons"
|
||||
|
|
@ -54,9 +57,12 @@ func init() {
|
|||
SelectingDriver,
|
||||
DownloadingArtifacts,
|
||||
StartingNode,
|
||||
RunningLocalhost,
|
||||
LocalOSRelease,
|
||||
CreatingContainer,
|
||||
CreatingVM,
|
||||
PreparingKubernetes,
|
||||
ConfiguringLHEnv,
|
||||
VerifyingKubernetes,
|
||||
EnablingAddons,
|
||||
Done,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
title: "Minikube JSON Output"
|
||||
date: 2019-07-31
|
||||
weight: 4
|
||||
description: >
|
||||
How to add logs to facilitate JSON output
|
||||
---
|
||||
|
||||
This document is written for minikube contributors who need to add logs to the minikube log registry for sucessful JSON output.
|
||||
You may need to add logs to the registry if the `TestJSONOutput` integration test is failing on your PR.
|
||||
|
||||
### Background
|
||||
minikube provides JSON output for `minikube start`, accesible via the `--output` flag:
|
||||
|
||||
```
|
||||
minikube start --output json
|
||||
```
|
||||
|
||||
This converts regular output:
|
||||
|
||||
```
|
||||
$ minikube start
|
||||
|
||||
😄 minikube v1.12.1 on Darwin 10.14.6
|
||||
✨ Automatically selected the hyperkit driver
|
||||
👍 Starting control plane node minikube in cluster minikube
|
||||
🔥 Creating hyperkit VM (CPUs=2, Memory=6000MB, Disk=20000MB) ...
|
||||
```
|
||||
|
||||
into a [Cloud Events](https://cloudevents.io/) compatible JSON output:
|
||||
|
||||
```
|
||||
$ minikube start --output json
|
||||
|
||||
{"data":{"currentstep":"0","message":"minikube v1.12.1 on Darwin 10.14.6\n","name":"Initial Minikube Setup","totalsteps":"10"},"datacontenttype":"application/json","id":"68ff70ae-202b-4b13-8351-e9f060e8c56e","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.step"}
|
||||
{"data":{"currentstep":"1","message":"Automatically selected the hyperkit driver\n","name":"Selecting Driver","totalsteps":"10"},"datacontenttype":"application/json","id":"39bed8e9-3c1a-444e-997c-2ec19bdb1ca1","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.step"}
|
||||
{"data":{"currentstep":"3","message":"Starting control plane node minikube in cluster minikube\n","name":"Starting Node","totalsteps":"10"},"datacontenttype":"application/json","id":"7c80bc53-3ac4-4a42-a493-92e269cc56c9","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.step"}
|
||||
{"data":{"currentstep":"6","message":"Creating hyperkit VM (CPUs=2, Memory=6000MB, Disk=20000MB) ...\n","name":"Creating VM","totalsteps":"10"},"datacontenttype":"application/json","id":"7f5f23a4-9a09-4954-8abc-d29bda2cc569","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.step"}
|
||||
```
|
||||
|
||||
There are a few key points to note in the above output:
|
||||
1. Each log of type `io.k8s.sigs.minikube.step` indicates a distinct step in the `minikube start` process
|
||||
1. Each step has a `currentstep` field which allows clients to track `minikube start` progress
|
||||
1. Each `currentstep` is distinct and increasing in order
|
||||
|
||||
To achieve this output, minikube maintains a registry of logs.
|
||||
This way, minikube knows how many expected `totalsteps` there are at the beginning of the process, and what the current step is.
|
||||
|
||||
If you change logs, or add a new log, you need to update the minikube registry to pass integration tests.
|
||||
|
||||
|
||||
### Adding a Log to the Registry
|
||||
|
||||
There are three steps to adding a log to the registry, which exists in [register.go](https://github.com/kubernetes/minikube/blob/master/pkg/minikube/out/register/register.go).
|
||||
|
||||
You will need to add your new log in two places:
|
||||
1. As a constant of type `RegStep` [here](https://github.com/kubernetes/minikube/blob/master/pkg/minikube/out/register/register.go#L24)
|
||||
1. In the register itself in the `init()` function, [here](https://github.com/kubernetes/minikube/blob/master/pkg/minikube/out/register/register.go#L52)
|
||||
|
||||
**Note: It's important that the order of steps matches the expected order they will be called in. So, if you add a step that is supposed to be called after "Preparing Kubernetes", the new step should be place after "Preparing Kubernetes".
|
||||
|
||||
Finally, set your new step in the cod by placing this line before you call `out.T`:
|
||||
|
||||
```
|
||||
register.Reg.SetStep(register.MyNewStep)
|
||||
```
|
||||
|
||||
You can see an example of setting the registry step in the code in [config.go](https://github.com/kubernetes/minikube/blob/master/pkg/minikube/node/config.go):
|
||||
|
||||
```go
|
||||
register.Reg.SetStep(register.PreparingKubernetes)
|
||||
out.T(cr.Style(), "Preparing Kubernetes {{.k8sVersion}} on {{.runtime}} {{.runtimeVersion}} ...", out.V{"k8sVersion": k8sVersion, "runtime": cr.Name(), "runtimeVersion": version})
|
||||
```
|
||||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
|
@ -43,21 +44,60 @@ func TestJSONOutput(t *testing.T) {
|
|||
t.Errorf("failed to clean up: args %q: %v", rr.Command(), err)
|
||||
}
|
||||
|
||||
type validateJSONOutputFunc func(context.Context, *testing.T, *RunResult)
|
||||
ces, err := cloudEvents(t, rr)
|
||||
if err != nil {
|
||||
t.Fatalf("converting to cloud events: %v\n", err)
|
||||
}
|
||||
|
||||
type validateJSONOutputFunc func(context.Context, *testing.T, []*cloudEvent)
|
||||
t.Run("serial", func(t *testing.T) {
|
||||
serialTests := []struct {
|
||||
name string
|
||||
validator validateJSONOutputFunc
|
||||
}{
|
||||
{"CloudEvents", validateCloudEvents},
|
||||
{"DistinctCurrentSteps", validateDistinctCurrentSteps},
|
||||
{"IncreasingCurrentSteps", validateIncreasingCurrentSteps},
|
||||
}
|
||||
for _, stc := range serialTests {
|
||||
t.Run(stc.name, func(t *testing.T) {
|
||||
stc.validator(ctx, t, rr)
|
||||
stc.validator(ctx, t, ces)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// make sure each step has a distinct step number
|
||||
func validateDistinctCurrentSteps(ctx context.Context, t *testing.T, ces []*cloudEvent) {
|
||||
steps := map[string]string{}
|
||||
for _, ce := range ces {
|
||||
currentStep, exists := ce.data["currentstep"]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
if msg, alreadySeen := steps[currentStep]; alreadySeen {
|
||||
t.Fatalf("step %v has already been assigned to another step:\n%v\nCannot use for:\n%v\n%v", currentStep, msg, ce.data["message"], ces)
|
||||
}
|
||||
steps[currentStep] = ce.data["message"]
|
||||
}
|
||||
}
|
||||
|
||||
// for successful minikube start, 'current step' should be increasing
|
||||
func validateIncreasingCurrentSteps(ctx context.Context, t *testing.T, ces []*cloudEvent) {
|
||||
step := -1
|
||||
for _, ce := range ces {
|
||||
currentStep, exists := ce.data["currentstep"]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
cs, err := strconv.Atoi(currentStep)
|
||||
if err != nil {
|
||||
t.Fatalf("current step is not an integer: %v\n%v", currentStep, ce)
|
||||
}
|
||||
if cs <= step {
|
||||
t.Fatalf("current step is not in increasing order: %v", ces)
|
||||
}
|
||||
step = cs
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONOutputError(t *testing.T) {
|
||||
|
|
@ -78,7 +118,7 @@ func TestJSONOutputError(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
// we want the last cloud event to be of type error and have the expected exit code and message
|
||||
last := newCloudEvent(t, ces[len(ces)-1])
|
||||
last := ces[len(ces)-1]
|
||||
if last.Type() != register.NewError("").Type() {
|
||||
t.Fatalf("last cloud event is not of type error: %v", last)
|
||||
}
|
||||
|
|
@ -113,8 +153,8 @@ func (c *cloudEvent) validateData(t *testing.T, key, value string) {
|
|||
}
|
||||
}
|
||||
|
||||
func cloudEvents(t *testing.T, rr *RunResult) ([]cloudevents.Event, error) {
|
||||
ces := []cloudevents.Event{}
|
||||
func cloudEvents(t *testing.T, rr *RunResult) ([]*cloudEvent, error) {
|
||||
ces := []*cloudEvent{}
|
||||
stdout := strings.Split(rr.Stdout.String(), "\n")
|
||||
for _, s := range stdout {
|
||||
if s == "" {
|
||||
|
|
@ -125,15 +165,7 @@ func cloudEvents(t *testing.T, rr *RunResult) ([]cloudevents.Event, error) {
|
|||
t.Logf("unable to marshal output: %v", s)
|
||||
return nil, err
|
||||
}
|
||||
ces = append(ces, event)
|
||||
ces = append(ces, newCloudEvent(t, event))
|
||||
}
|
||||
return ces, nil
|
||||
}
|
||||
|
||||
// make sure all output can be marshaled as a cloud event
|
||||
func validateCloudEvents(ctx context.Context, t *testing.T, rr *RunResult) {
|
||||
_, err := cloudEvents(t, rr)
|
||||
if err != nil {
|
||||
t.Fatalf("converting to cloud events: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue