mirror of
https://github.com/clearlinux/clr-installer.git
synced 2026-09-02 11:41:44 +00:00
When running the "swupd verify" command to install the base OS and the selected bundles, the progress of the command execution was just being shown as a progress bar that was looping until the swupd command finished. This approach was not ideal since this process can take several minutes to complete, and since the progress bar was just spinning it didn't provide a real feedback for the user to know in what part the process was. This can trigger some anxiety in users. This commit solves this problem by using the machine readable output from swupd to determine the exact part of the process where the install is and report this to the user using a series of steps along with a progress bar (or percentage value in not TUI installer) that shows how far the step has gotten. Signed-off-by: Castulo Martinez <castulo.martinez@intel.com>
180 lines
3.8 KiB
Go
180 lines
3.8 KiB
Go
// Copyright © 2018 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/clearlinux/clr-installer/log"
|
|
)
|
|
|
|
// Output interface allows implementors to process the output from a
|
|
// command according to their specific case
|
|
type Output interface {
|
|
Process(line string)
|
|
}
|
|
|
|
type runLogger struct{}
|
|
|
|
var (
|
|
httpsProxy string
|
|
)
|
|
|
|
// SetHTTPSProxy defines the HTTPS_PROXY env var value for all the cmd executions
|
|
func SetHTTPSProxy(addr string) {
|
|
httpsProxy = addr
|
|
}
|
|
|
|
func (rl runLogger) Write(p []byte) (n int, err error) {
|
|
for _, curr := range strings.Split(string(p), "\n") {
|
|
if curr == "" {
|
|
continue
|
|
}
|
|
|
|
log.Debug(curr)
|
|
}
|
|
return len(p), nil
|
|
}
|
|
|
|
// RunAndLog executes a command (similar to Run) but takes care of writing
|
|
// the output to default logger
|
|
func RunAndLog(args ...string) error {
|
|
return Run(runLogger{}, args...)
|
|
}
|
|
|
|
// RunAndLogWithEnv does the same as RunAndLog but it changes the execution's environment
|
|
// variables adding the provided ones by the env argument
|
|
func RunAndLogWithEnv(env map[string]string, args ...string) error {
|
|
return run(nil, runLogger{}, env, args...)
|
|
}
|
|
|
|
// PipeRunAndLog is similar to RunAndLog runs a command and writes the output
|
|
// to default logger and also writes in to the process stdin
|
|
func PipeRunAndLog(in string, args ...string) error {
|
|
return run(func(cmd *exec.Cmd) error {
|
|
stdin, err := cmd.StdinPipe()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
go func() {
|
|
defer func() {
|
|
_ = stdin.Close()
|
|
}()
|
|
|
|
_, _ = io.WriteString(stdin, in)
|
|
}()
|
|
|
|
return nil
|
|
}, runLogger{}, nil, args...)
|
|
}
|
|
|
|
func run(sw func(cmd *exec.Cmd) error, writer io.Writer, env map[string]string, args ...string) error {
|
|
var exe string
|
|
var cmdArgs []string
|
|
|
|
log.Debug("%s", strings.Join(args, " "))
|
|
|
|
exe = args[0]
|
|
cmdArgs = args[1:]
|
|
|
|
cmd := exec.Command(exe, cmdArgs...)
|
|
|
|
if httpsProxy != "" {
|
|
cmd.Env = append(cmd.Env, fmt.Sprintf("https_proxy=%s", httpsProxy))
|
|
}
|
|
|
|
if sw != nil {
|
|
if err := sw(cmd); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
cmd.Stdout = writer
|
|
cmd.Stderr = writer
|
|
|
|
if cmd.Stdin == nil {
|
|
cmd.Stdin = os.Stdin
|
|
}
|
|
|
|
for k, v := range env {
|
|
curr := fmt.Sprintf("%s=%s", k, v)
|
|
cmd.Args = append(cmd.Args, curr)
|
|
cmd.Env = append(cmd.Env, curr)
|
|
}
|
|
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Run executes a command and uses writer to write both stdout and stderr
|
|
// args are the actual command and its arguments
|
|
func Run(writer io.Writer, args ...string) error {
|
|
return run(nil, writer, nil, args...)
|
|
}
|
|
|
|
// RunAndProcessOutput executes a command and process the output from
|
|
// Stdout and Stderr according to the implementor
|
|
// args are the actual command and its arguments
|
|
func RunAndProcessOutput(output Output, args ...string) error {
|
|
|
|
var exe string
|
|
var cmdArgs []string
|
|
|
|
log.Debug("%s", strings.Join(args, " "))
|
|
|
|
exe = args[0]
|
|
cmdArgs = args[1:]
|
|
|
|
cmd := exec.Command(exe, cmdArgs...)
|
|
|
|
if httpsProxy != "" {
|
|
cmd.Env = append(cmd.Env, fmt.Sprintf("https_proxy=%s", httpsProxy))
|
|
}
|
|
|
|
stdout, err := cmd.StdoutPipe()
|
|
if err != nil {
|
|
log.Error("Could not connect a pipe to Stdout")
|
|
return err
|
|
}
|
|
|
|
// run the command but don't wait for it to finish
|
|
if err := cmd.Start(); err != nil {
|
|
log.Error("Failed to start command execution")
|
|
return err
|
|
}
|
|
|
|
// start scanning stdout for messages
|
|
scannerOut := bufio.NewScanner(stdout)
|
|
for scannerOut.Scan() {
|
|
|
|
// specific processing implementation
|
|
output.Process(scannerOut.Text())
|
|
|
|
}
|
|
|
|
if err := scannerOut.Err(); err != nil {
|
|
log.Error("An error occurred while reading stdout")
|
|
return err
|
|
}
|
|
|
|
// wait for the command to finish running
|
|
if err := cmd.Wait(); err != nil {
|
|
log.Error("An error occurred executing command: \"%s\". Error: %s", strings.Join(args, " "), err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|