Skip to content

Commit a6509d1

Browse files
committed
Added sub-step logging to adm init step on start
1 parent 129eefd commit a6509d1

File tree

14 files changed

+417
-17
lines changed

14 files changed

+417
-17
lines changed

Diff for: pkg/minikube/bootstrapper/kubeadm/kubeadm.go

+43-3
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ limitations under the License.
1717
package kubeadm
1818

1919
import (
20+
"bufio"
2021
"context"
2122
"fmt"
23+
"io"
2224
"net"
2325
"os/exec"
2426
"path"
@@ -226,19 +228,26 @@ func (k *Bootstrapper) init(cfg config.ClusterConfig) error {
226228
conf := bsutil.KubeadmYamlPath
227229
ctx, cancel := context.WithTimeout(context.Background(), initTimeoutMinutes*time.Minute)
228230
defer cancel()
231+
admInitLogReader, admInitLogWriter := io.Pipe()
229232
c := exec.CommandContext(ctx, "/bin/bash", "-c", fmt.Sprintf("%s init --config %s %s --ignore-preflight-errors=%s",
230233
bsutil.InvokeKubeadm(cfg.KubernetesConfig.KubernetesVersion), conf, extraFlags, strings.Join(ignore, ",")))
231-
if _, err := k.c.RunCmd(c); err != nil {
234+
c.Stdout = admInitLogWriter
235+
c.Stderr = admInitLogWriter
236+
sc, err := k.c.StartCmd(c)
237+
if err != nil {
238+
return errors.Wrap(err, "start")
239+
}
240+
go outputKubeadmInitSteps(admInitLogReader)
241+
if _, err := k.c.WaitCmd(sc); err != nil {
232242
if ctx.Err() == context.DeadlineExceeded {
233243
return ErrInitTimedout
234244
}
235245

236246
if strings.Contains(err.Error(), "'kubeadm': Permission denied") {
237247
return ErrNoExecLinux
238248
}
239-
return errors.Wrap(err, "run")
249+
return errors.Wrap(err, "wait")
240250
}
241-
242251
if err := k.applyCNI(cfg); err != nil {
243252
return errors.Wrap(err, "apply cni")
244253
}
@@ -272,6 +281,37 @@ func (k *Bootstrapper) init(cfg config.ClusterConfig) error {
272281
return nil
273282
}
274283

284+
// outputKubeadmInitSteps streams the pipe and outputs the current step
285+
func outputKubeadmInitSteps(logs io.Reader) {
286+
type step struct {
287+
logTag string
288+
registerStep register.RegStep
289+
stepMessage string
290+
}
291+
292+
steps := []step{
293+
{logTag: "certs", registerStep: register.PreparingKubernetesCerts, stepMessage: "Generating certificates and keys ..."},
294+
{logTag: "control-plane", registerStep: register.PreparingKubernetesControlPlane, stepMessage: "Booting up control plane ..."},
295+
{logTag: "bootstrap-token", registerStep: register.PreparingKubernetesBootstrapToken, stepMessage: "Configuring RBAC rules ..."},
296+
}
297+
nextStepIndex := 0
298+
299+
scanner := bufio.NewScanner(logs)
300+
for scanner.Scan() {
301+
if nextStepIndex >= len(steps) {
302+
scanner.Text()
303+
continue
304+
}
305+
nextStep := steps[nextStepIndex]
306+
if !strings.Contains(scanner.Text(), fmt.Sprintf("[%s]", nextStep.logTag)) {
307+
continue
308+
}
309+
register.Reg.SetStep(nextStep.registerStep)
310+
out.Step(style.SubStep, nextStep.stepMessage)
311+
nextStepIndex++
312+
}
313+
}
314+
275315
// applyCNI applies CNI to a cluster. Needs to be done every time a VM is powered up.
276316
func (k *Bootstrapper) applyCNI(cfg config.ClusterConfig) error {
277317
cnm, err := cni.New(cfg)

Diff for: pkg/minikube/command/.fake_runner.go.swp

16 KB
Binary file not shown.

Diff for: pkg/minikube/command/command_runner.go

+14
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,26 @@ type RunResult struct {
5151
Args []string // the args that was passed to Runner
5252
}
5353

54+
// StartedCmd holds the contents of a started command
55+
type StartedCmd struct {
56+
cmd *exec.Cmd
57+
rr *RunResult
58+
}
59+
5460
// Runner represents an interface to run commands.
5561
type Runner interface {
5662
// RunCmd runs a cmd of exec.Cmd type. allowing user to set cmd.Stdin, cmd.Stdout,...
5763
// not all implementors are guaranteed to handle all the properties of cmd.
5864
RunCmd(cmd *exec.Cmd) (*RunResult, error)
5965

66+
// StartCmd starts a cmd of exec.Cmd type.
67+
// This func in non-blocking, use WaitCmd to block until complete.
68+
// Not all implementors are guaranteed to handle all the properties of cmd.
69+
StartCmd(cmd *exec.Cmd) (*StartedCmd, error)
70+
71+
// WaitCmd will prevent further execution until the started command has completed.
72+
WaitCmd(startedCmd *StartedCmd) (*RunResult, error)
73+
6074
// Copy is a convenience method that runs a command to copy a file
6175
Copy(assets.CopyableFile) error
6276

Diff for: pkg/minikube/command/exec_runner.go

+47
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,53 @@ func (e *execRunner) RunCmd(cmd *exec.Cmd) (*RunResult, error) {
9191
return rr, fmt.Errorf("%s: %v\nstdout:\n%s\nstderr:\n%s", rr.Command(), err, rr.Stdout.String(), rr.Stderr.String())
9292
}
9393

94+
// StartCmd implements the Command Runner interface to start a exec.Cmd object
95+
func (*execRunner) StartCmd(cmd *exec.Cmd) (*StartedCmd, error) {
96+
rr := &RunResult{Args: cmd.Args}
97+
sc := &StartedCmd{cmd: cmd, rr: rr}
98+
klog.Infof("Start: %v", rr.Command())
99+
100+
var outb, errb io.Writer
101+
if cmd.Stdout == nil {
102+
var so bytes.Buffer
103+
outb = io.MultiWriter(&so, &rr.Stdout)
104+
} else {
105+
outb = io.MultiWriter(cmd.Stdout, &rr.Stdout)
106+
}
107+
108+
if cmd.Stderr == nil {
109+
var se bytes.Buffer
110+
errb = io.MultiWriter(&se, &rr.Stderr)
111+
} else {
112+
errb = io.MultiWriter(cmd.Stderr, &rr.Stderr)
113+
}
114+
115+
cmd.Stdout = outb
116+
cmd.Stderr = errb
117+
118+
if err := cmd.Start(); err != nil {
119+
return sc, errors.Wrap(err, "start")
120+
}
121+
122+
return sc, nil
123+
}
124+
125+
// WaitCmd implements the Command Runner interface to wait until a started exec.Cmd object finishes
126+
func (*execRunner) WaitCmd(sc *StartedCmd) (*RunResult, error) {
127+
rr := sc.rr
128+
129+
err := sc.cmd.Wait()
130+
if exitError, ok := err.(*exec.ExitError); ok {
131+
rr.ExitCode = exitError.ExitCode()
132+
}
133+
134+
if err == nil {
135+
return rr, nil
136+
}
137+
138+
return rr, fmt.Errorf("%s: %v\nstdout:\n%s\nstderr:\n%s", rr.Command(), err, rr.Stdout.String(), rr.Stderr.String())
139+
}
140+
94141
// Copy copies a file and its permissions
95142
func (e *execRunner) Copy(f assets.CopyableFile) error {
96143
dst := path.Join(f.GetTargetDir(), f.GetTargetName())

Diff for: pkg/minikube/command/fake_runner.go

+41
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,47 @@ func (f *FakeCommandRunner) RunCmd(cmd *exec.Cmd) (*RunResult, error) {
9090
return rr, nil
9191
}
9292

93+
// StartCmd implements the Command Runner interface to start a exec.Cmd object
94+
func (f *FakeCommandRunner) StartCmd(cmd *exec.Cmd) (*StartedCmd, error) {
95+
rr := &RunResult{Args: cmd.Args}
96+
sc := &StartedCmd{cmd: cmd, rr: rr}
97+
klog.Infof("(FakeCommandRunner) Start: %v", rr.Command())
98+
99+
key := rr.Command()
100+
out, ok := f.cmdMap.Load(key)
101+
if !ok {
102+
cmds := f.commands()
103+
if len(cmds) == 0 {
104+
return sc, fmt.Errorf("asked to execute %s, but FakeCommandRunner has no commands stored", rr.Command())
105+
}
106+
107+
var txt strings.Builder
108+
for _, c := range f.commands() {
109+
txt.WriteString(fmt.Sprintf(" `%s`\n", c))
110+
}
111+
return sc, fmt.Errorf("unregistered command:\n `%s`\nexpected one of:\n%s", key, txt.String())
112+
}
113+
114+
var buf bytes.Buffer
115+
outStr := ""
116+
if out != nil {
117+
outStr = out.(string)
118+
}
119+
_, err := buf.WriteString(outStr)
120+
if err != nil {
121+
return sc, errors.Wrap(err, "Writing outStr to FakeCommandRunner's buffer")
122+
}
123+
rr.Stdout = buf
124+
rr.Stderr = buf
125+
126+
return sc, nil
127+
}
128+
129+
// WaitCmd implements the Command Runner interface to wait until a started exec.Cmd object finishes
130+
func (f *FakeCommandRunner) WaitCmd(sc *StartedCmd) (*RunResult, error) {
131+
return sc.rr, nil
132+
}
133+
93134
// Copy adds the filename, file contents key value pair to the stored map.
94135
func (f *FakeCommandRunner) Copy(file assets.CopyableFile) error {
95136
var b bytes.Buffer

Diff for: pkg/minikube/command/fake_runner_test.go

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package command
18+
19+
import (
20+
"os/exec"
21+
"testing"
22+
23+
"k8s.io/minikube/pkg/minikube/assets"
24+
)
25+
26+
func TestFakeRunnerFile(t *testing.T) {
27+
fakeCommandRunner := NewFakeCommandRunner()
28+
cmdArg := "test"
29+
cmdToOutput := make(map[string]string)
30+
cmdToOutput[cmdArg] = "123"
31+
fakeCommandRunner.SetCommandToOutput(cmdToOutput)
32+
33+
t.Run("SetGetFileContents", func(t *testing.T) {
34+
fileToContentsMap := make(map[string]string)
35+
fileName := "fileName"
36+
expectedFileContents := "fileContents"
37+
fileToContentsMap[fileName] = expectedFileContents
38+
39+
fakeCommandRunner.SetFileToContents(fileToContentsMap)
40+
41+
retrievedFileContents, err := fakeCommandRunner.GetFileToContents(fileName)
42+
if err != nil {
43+
t.Fatal(err)
44+
}
45+
46+
if expectedFileContents != retrievedFileContents {
47+
t.Errorf("expected %q, retrieved %q", expectedFileContents, retrievedFileContents)
48+
}
49+
})
50+
51+
t.Run("CopyRemoveFile", func(t *testing.T) {
52+
expectedFileContents := "test contents"
53+
fileName := "memory"
54+
file := assets.NewMemoryAssetTarget([]byte(expectedFileContents), "", "")
55+
56+
if err := fakeCommandRunner.Copy(file); err != nil {
57+
t.Fatal(err)
58+
}
59+
60+
retrievedFileContents, err := fakeCommandRunner.GetFileToContents(fileName)
61+
if err != nil {
62+
t.Fatal(err)
63+
}
64+
65+
if expectedFileContents != retrievedFileContents {
66+
t.Errorf("expected %q, retrieved %q", expectedFileContents, retrievedFileContents)
67+
}
68+
69+
if err := fakeCommandRunner.Remove(file); err != nil {
70+
t.Fatal(err)
71+
}
72+
73+
if _, err := fakeCommandRunner.GetFileToContents(fileName); err == nil {
74+
t.Errorf("file was not removed")
75+
}
76+
})
77+
78+
t.Run("RunCmd", func(t *testing.T) {
79+
expectedOutput := "123"
80+
command := &exec.Cmd{Args: []string{cmdArg}}
81+
82+
rr, err := fakeCommandRunner.RunCmd(command)
83+
if err != nil {
84+
t.Fatal(err)
85+
}
86+
87+
retrievedOutput := rr.Stdout.String()
88+
if expectedOutput != retrievedOutput {
89+
t.Errorf("expected %q, retrieved %q", expectedOutput, retrievedOutput)
90+
}
91+
})
92+
93+
t.Run("StartWaitCmd", func(t *testing.T) {
94+
expectedOutput := "123"
95+
command := &exec.Cmd{Args: []string{cmdArg}}
96+
97+
sc, err := fakeCommandRunner.StartCmd(command)
98+
if err != nil {
99+
t.Fatal(err)
100+
}
101+
102+
retrievedOutput := sc.rr.Stdout.String()
103+
if expectedOutput != retrievedOutput {
104+
t.Errorf("expected %q, retrieved %q", expectedOutput, retrievedOutput)
105+
}
106+
107+
rr, err := fakeCommandRunner.WaitCmd(sc)
108+
if err != nil {
109+
t.Fatal(err)
110+
}
111+
112+
retrievedOutput = rr.Stdout.String()
113+
if expectedOutput != retrievedOutput {
114+
t.Errorf("expected %q, retrieved %q", expectedOutput, retrievedOutput)
115+
}
116+
117+
})
118+
}

Diff for: pkg/minikube/command/kic_runner.go

+8
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@ func (k *kicRunner) RunCmd(cmd *exec.Cmd) (*RunResult, error) {
131131

132132
}
133133

134+
func (k *kicRunner) StartCmd(cmd *exec.Cmd) (*StartedCmd, error) {
135+
return nil, fmt.Errorf("kicRunner does not support StartCmd - you could be the first to add it")
136+
}
137+
138+
func (k *kicRunner) WaitCmd(sc *StartedCmd) (*RunResult, error) {
139+
return nil, fmt.Errorf("kicRunner does not support WaitCmd - you could be the first to add it")
140+
}
141+
134142
// Copy copies a file and its permissions
135143
func (k *kicRunner) Copy(f assets.CopyableFile) error {
136144
dst := path.Join(path.Join(f.GetTargetDir(), f.GetTargetName()))

0 commit comments

Comments
 (0)