Skip to content

Commit 1f34c5f

Browse files
committed
Added sub-step logging to adm init step on start
1 parent c0a092e commit 1f34c5f

File tree

8 files changed

+271
-17
lines changed

8 files changed

+271
-17
lines changed

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

+42-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,25 @@ 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+
sc, err := k.c.StartCmd(c)
236+
if err != nil {
237+
return errors.Wrap(err, "start")
238+
}
239+
go outputAdmInitSteps(admInitLogReader)
240+
if _, err := k.c.WaitCmd(sc); err != nil {
232241
if ctx.Err() == context.DeadlineExceeded {
233242
return ErrInitTimedout
234243
}
235244

236245
if strings.Contains(err.Error(), "'kubeadm': Permission denied") {
237246
return ErrNoExecLinux
238247
}
239-
return errors.Wrap(err, "run")
248+
return errors.Wrap(err, "wait")
240249
}
241-
242250
if err := k.applyCNI(cfg); err != nil {
243251
return errors.Wrap(err, "apply cni")
244252
}
@@ -272,6 +280,37 @@ func (k *Bootstrapper) init(cfg config.ClusterConfig) error {
272280
return nil
273281
}
274282

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

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
@@ -86,6 +86,53 @@ func (*execRunner) RunCmd(cmd *exec.Cmd) (*RunResult, error) {
8686
return rr, fmt.Errorf("%s: %v\nstdout:\n%s\nstderr:\n%s", rr.Command(), err, rr.Stdout.String(), rr.Stderr.String())
8787
}
8888

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

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

+95
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ var (
4646
type SSHRunner struct {
4747
d drivers.Driver
4848
c *ssh.Client
49+
s *ssh.Session
4950
}
5051

5152
// NewSSHRunner returns a new SSHRunner that will run commands
@@ -194,6 +195,100 @@ func (s *SSHRunner) RunCmd(cmd *exec.Cmd) (*RunResult, error) {
194195
return rr, fmt.Errorf("%s: %v\nstdout:\n%s\nstderr:\n%s", rr.Command(), err, rr.Stdout.String(), rr.Stderr.String())
195196
}
196197

198+
// teeSSHStart starts a non-blocking SSH command, streaming stdout, stderr to logs
199+
func teeSSHStart(s *ssh.Session, cmd string, outB io.Writer, errB io.Writer) error {
200+
outPipe, err := s.StdoutPipe()
201+
if err != nil {
202+
return errors.Wrap(err, "stdout")
203+
}
204+
205+
errPipe, err := s.StderrPipe()
206+
if err != nil {
207+
return errors.Wrap(err, "stderr")
208+
}
209+
210+
go func() {
211+
if err := teePrefix(ErrPrefix, errPipe, errB, klog.V(8).Infof); err != nil {
212+
klog.Errorf("tee stderr: %v", err)
213+
}
214+
}()
215+
go func() {
216+
if err := teePrefix(OutPrefix, outPipe, outB, klog.V(8).Infof); err != nil {
217+
klog.Errorf("tee stdout: %v", err)
218+
}
219+
}()
220+
221+
return s.Start(cmd)
222+
}
223+
224+
// StartCmd implements the Command Runner interface to start a exec.Cmd object
225+
func (s *SSHRunner) StartCmd(cmd *exec.Cmd) (*StartedCmd, error) {
226+
if cmd.Stdin != nil {
227+
return nil, fmt.Errorf("SSHRunner does not support stdin - you could be the first to add it")
228+
}
229+
230+
if s.s != nil {
231+
return nil, fmt.Errorf("Another SSH command has been started and is currently running")
232+
}
233+
234+
rr := &RunResult{Args: cmd.Args}
235+
sc := &StartedCmd{cmd: cmd, rr: rr}
236+
klog.Infof("Start: %v", rr.Command())
237+
238+
var outb, errb io.Writer
239+
240+
if cmd.Stdout == nil {
241+
var so bytes.Buffer
242+
outb = io.MultiWriter(&so, &rr.Stdout)
243+
} else {
244+
outb = io.MultiWriter(cmd.Stdout, &rr.Stdout)
245+
}
246+
247+
if cmd.Stderr == nil {
248+
var se bytes.Buffer
249+
errb = io.MultiWriter(&se, &rr.Stderr)
250+
} else {
251+
errb = io.MultiWriter(cmd.Stderr, &rr.Stderr)
252+
}
253+
254+
sess, err := s.session()
255+
if err != nil {
256+
return sc, errors.Wrap(err, "NewSession")
257+
}
258+
259+
s.s = sess
260+
261+
err = teeSSHStart(s.s, shellquote.Join(cmd.Args...), outb, errb)
262+
263+
return sc, err
264+
}
265+
266+
// WaitCmd implements the Command Runner interface to wait until a started exec.Cmd object finishes
267+
func (s *SSHRunner) WaitCmd(sc *StartedCmd) (*RunResult, error) {
268+
if s.s == nil {
269+
return nil, fmt.Errorf("There is no SSH command started")
270+
}
271+
272+
rr := sc.rr
273+
274+
err := s.s.Wait()
275+
if exitError, ok := err.(*exec.ExitError); ok {
276+
rr.ExitCode = exitError.ExitCode()
277+
}
278+
279+
if err := s.s.Close(); err != io.EOF {
280+
klog.Errorf("session close: %v", err)
281+
}
282+
283+
s.s = nil
284+
285+
if err == nil {
286+
return rr, nil
287+
}
288+
289+
return rr, fmt.Errorf("%s: %v\nstdout:\n%s\nstderr:\n%s", rr.Command(), err, rr.Stdout.String(), rr.Stderr.String())
290+
}
291+
197292
// Copy copies a file to the remote over SSH.
198293
func (s *SSHRunner) Copy(f assets.CopyableFile) error {
199294
dst := path.Join(path.Join(f.GetTargetDir(), f.GetTargetName()))

Diff for: pkg/minikube/cruntime/cruntime.go

+4
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ func ValidRuntimes() []string {
5555
// CommandRunner is the subset of command.Runner this package consumes
5656
type CommandRunner interface {
5757
RunCmd(cmd *exec.Cmd) (*command.RunResult, error)
58+
// StartCmd is a non-blocking method that starts a command
59+
StartCmd(cmd *exec.Cmd) (*command.StartedCmd, error)
60+
// WaitCmd blocks until the started command completes
61+
WaitCmd(sc *command.StartedCmd) (*command.RunResult, error)
5862
// Copy is a convenience method that runs a command to copy a file
5963
Copy(assets.CopyableFile) error
6064
// Remove is a convenience method that runs a command to remove a file

Diff for: pkg/minikube/out/register/register.go

+20-14
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,23 @@ import (
2626

2727
// If you add a new step here, please also add it to register.Reg registry inside the init() function
2828
const (
29-
InitialSetup RegStep = "Initial Minikube Setup"
30-
SelectingDriver RegStep = "Selecting Driver"
31-
DownloadingArtifacts RegStep = "Downloading Artifacts"
32-
StartingNode RegStep = "Starting Node"
33-
PullingBaseImage RegStep = "Pulling Base Image"
34-
RunningLocalhost RegStep = "Running on Localhost"
35-
LocalOSRelease RegStep = "Local OS Release"
36-
CreatingContainer RegStep = "Creating Container"
37-
CreatingVM RegStep = "Creating VM"
38-
ConfiguringLHEnv RegStep = "Configuring Localhost Environment"
39-
PreparingKubernetes RegStep = "Preparing Kubernetes"
40-
VerifyingKubernetes RegStep = "Verifying Kubernetes"
41-
EnablingAddons RegStep = "Enabling Addons"
42-
Done RegStep = "Done"
29+
InitialSetup RegStep = "Initial Minikube Setup"
30+
SelectingDriver RegStep = "Selecting Driver"
31+
DownloadingArtifacts RegStep = "Downloading Artifacts"
32+
StartingNode RegStep = "Starting Node"
33+
PullingBaseImage RegStep = "Pulling Base Image"
34+
RunningLocalhost RegStep = "Running on Localhost"
35+
LocalOSRelease RegStep = "Local OS Release"
36+
CreatingContainer RegStep = "Creating Container"
37+
CreatingVM RegStep = "Creating VM"
38+
ConfiguringLHEnv RegStep = "Configuring Localhost Environment"
39+
PreparingKubernetes RegStep = "Preparing Kubernetes"
40+
PreparingKubernetesCerts RegStep = "Generating certificates"
41+
PreparingKubernetesControlPlane RegStep = "Booting control plane"
42+
PreparingKubernetesBootstrapToken RegStep = "Configuring RBAC rules"
43+
VerifyingKubernetes RegStep = "Verifying Kubernetes"
44+
EnablingAddons RegStep = "Enabling Addons"
45+
Done RegStep = "Done"
4346

4447
Stopping RegStep = "Stopping"
4548
PowerOff RegStep = "PowerOff"
@@ -77,6 +80,9 @@ func init() {
7780
CreatingContainer,
7881
CreatingVM,
7982
PreparingKubernetes,
83+
PreparingKubernetesCerts,
84+
PreparingKubernetesControlPlane,
85+
PreparingKubernetesBootstrapToken,
8086
ConfiguringLHEnv,
8187
VerifyingKubernetes,
8288
EnablingAddons,

0 commit comments

Comments
 (0)