Merge pull request #183 from mudler/docker-debug-output

Add realtime output for building phase
This commit is contained in:
Ettore Di Giacinto 2021-02-13 12:02:20 +01:00 committed by GitHub
commit 101df40eec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 164 additions and 26 deletions

View File

@ -83,6 +83,8 @@ Build packages specifying multiple definition trees:
LuetCfg.Viper.BindPFlag("solver.discount", cmd.Flags().Lookup("solver-discount"))
LuetCfg.Viper.BindPFlag("solver.rate", cmd.Flags().Lookup("solver-rate"))
LuetCfg.Viper.BindPFlag("solver.max_attempts", cmd.Flags().Lookup("solver-attempts"))
LuetCfg.Viper.BindPFlag("general.show_build_output", cmd.Flags().Lookup("live-output"))
},
Run: func(cmd *cobra.Command, args []string) {
@ -155,6 +157,8 @@ Build packages specifying multiple definition trees:
LuetCfg.GetSolverOptions().Discount = float32(discount)
LuetCfg.GetSolverOptions().MaxAttempts = attempts
LuetCfg.GetGeneral().ShowBuildOutput = LuetCfg.Viper.GetBool("general.show_build_output")
Debug("Solver", LuetCfg.GetSolverOptions().CompactString())
opts := compiler.NewDefaultCompilerOptions()
@ -317,6 +321,7 @@ func init() {
buildCmd.Flags().Float32("solver-discount", 1.0, "Solver discount rate")
buildCmd.Flags().Int("solver-attempts", 9000, "Solver maximum attempts")
buildCmd.Flags().Bool("solver-concurrent", false, "Use concurrent solver (experimental)")
buildCmd.Flags().Bool("live-output", LuetCfg.GetGeneral().ShowBuildOutput, "Enable live output of the build phase.")
buildCmd.Flags().Bool("pretend", false, "Just print what packages will be compiled")

View File

@ -16,8 +16,14 @@
package backend
import (
"github.com/google/go-containerregistry/pkg/crane"
"fmt"
"os/exec"
"github.com/mudler/luet/pkg/compiler"
"github.com/mudler/luet/pkg/config"
"github.com/google/go-containerregistry/pkg/crane"
"github.com/pkg/errors"
)
const (
@ -41,3 +47,38 @@ func NewBackend(s string) compiler.CompilerBackend {
}
return compilerBackend
}
func runCommand(cmd *exec.Cmd) (string, error) {
ans := ""
writer := NewBackendWriter(!config.LuetCfg.GetGeneral().ShowBuildOutput)
cmd.Stdout = writer
cmd.Stderr = writer
err := cmd.Start()
if err != nil {
return "", errors.Wrap(err, "Failed starting build")
}
err = cmd.Wait()
if err != nil {
return "", errors.Wrap(err, "Failed waiting for building command")
}
res := cmd.ProcessState.ExitCode()
if res != 0 {
errMsg := fmt.Sprintf("Failed building image (exiting with %d)", res)
if !config.LuetCfg.GetGeneral().ShowBuildOutput {
errMsg = fmt.Sprintf("Failed building image (exiting with %d): %s",
res, writer.GetCombinedOutput())
}
return "", errors.Wrap(err, errMsg)
}
if !config.LuetCfg.GetGeneral().ShowBuildOutput {
ans = writer.GetCombinedOutput()
}
return ans, nil
}

View File

@ -53,19 +53,31 @@ func (*SimpleDocker) BuildImage(opts compiler.CompilerBackendOptions) error {
}
buildarg := []string{"build", "-f", dockerfileName, "-t", name, context}
if !config.LuetCfg.GetGeneral().ShowBuildOutput {
Spinner(22)
defer SpinnerStop()
}
Info(":whale2: Building image " + name)
cmd := exec.Command("docker", buildarg...)
cmd.Dir = path
out, err := cmd.CombinedOutput()
_, err := runCommand(cmd)
if err != nil {
return errors.Wrap(err, "Failed building image: "+string(out))
return err
}
if !config.LuetCfg.GetGeneral().ShowBuildOutput {
SpinnerStop()
}
Info(":whale: Building image " + name + " done")
if os.Getenv("DOCKER_SQUASH") == "true" {
Info(":whale: Squashing image " + name)
var client *docker.Client
Spinner(22)
client, err = docker.NewClientFromEnv()
if err != nil {
return errors.Wrap(err, "could not connect to the Docker daemon")
@ -74,13 +86,8 @@ func (*SimpleDocker) BuildImage(opts compiler.CompilerBackendOptions) error {
if err != nil {
return errors.Wrap(err, "Failed squashing image")
}
Info(":whale: Squashing image " + name + " done")
}
if config.LuetCfg.GetGeneral().ShowBuildOutput {
Info(string(out))
} else {
Debug(string(out))
Info(":whale: Squashing image " + name + " done")
}
return nil
@ -101,11 +108,16 @@ func (*SimpleDocker) DownloadImage(opts compiler.CompilerBackendOptions) error {
name := opts.ImageName
buildarg := []string{"pull", name}
Debug(":whale: Downloading image " + name)
Spinner(22)
defer SpinnerStop()
cmd := exec.Command("docker", buildarg...)
out, err := cmd.CombinedOutput()
if err != nil {
return errors.Wrap(err, "Failed pulling image: "+string(out))
}
Info(":whale: Downloaded image:", name)
return nil
}
@ -142,6 +154,10 @@ func (*SimpleDocker) RemoveImage(opts compiler.CompilerBackendOptions) error {
func (*SimpleDocker) Push(opts compiler.CompilerBackendOptions) error {
name := opts.ImageName
pusharg := []string{"push", name}
Spinner(22)
defer SpinnerStop()
out, err := exec.Command("docker", pusharg...).CombinedOutput()
if err != nil {
return errors.Wrap(err, "Failed pushing image: "+string(out))
@ -170,6 +186,10 @@ func (*SimpleDocker) ExportImage(opts compiler.CompilerBackendOptions) error {
buildarg := []string{"save", name, "-o", path}
Debug(":whale: Saving image " + name)
Spinner(22)
defer SpinnerStop()
out, err := exec.Command("docker", buildarg...).CombinedOutput()
if err != nil {
return errors.Wrap(err, "Failed exporting image: "+string(out))
@ -194,10 +214,16 @@ func (b *SimpleDocker) ExtractRootfs(opts compiler.CompilerBackendOptions, keepP
defer os.RemoveAll(tempexport) // clean up
imageExport := filepath.Join(tempexport, "image.tar")
Spinner(22)
defer SpinnerStop()
if err := b.ExportImage(compiler.CompilerBackendOptions{ImageName: name, Destination: imageExport}); err != nil {
return errors.Wrap(err, "failed while extracting rootfs for "+name)
}
SpinnerStop()
src := imageExport
if src == "" && opts.ImageName != "" {

View File

@ -21,6 +21,7 @@ import (
"strings"
"github.com/mudler/luet/pkg/compiler"
"github.com/mudler/luet/pkg/config"
. "github.com/mudler/luet/pkg/logger"
"github.com/pkg/errors"
@ -44,17 +45,23 @@ func (*SimpleImg) BuildImage(opts compiler.CompilerBackendOptions) error {
dockerfileName := opts.DockerFileName
buildarg := []string{"build", "-f", dockerfileName, "-t", name, context}
Spinner(22)
defer SpinnerStop()
Info(":tea: Building image " + name)
if !config.LuetCfg.GetGeneral().ShowBuildOutput {
Spinner(22)
defer SpinnerStop()
}
cmd := exec.Command("img", buildarg...)
cmd.Dir = path
out, err := cmd.CombinedOutput()
_, err := runCommand(cmd)
if err != nil {
return errors.Wrap(err, "Failed building image: "+string(out))
return err
}
Info(":tea: Building image " + name + " done")
return nil
}
@ -73,11 +80,13 @@ func (*SimpleImg) RemoveImage(opts compiler.CompilerBackendOptions) error {
}
func (*SimpleImg) DownloadImage(opts compiler.CompilerBackendOptions) error {
name := opts.ImageName
buildarg := []string{"pull", name}
Debug(":tea: Downloading image " + name)
Spinner(22)
defer SpinnerStop()
cmd := exec.Command("img", buildarg...)
out, err := cmd.CombinedOutput()
if err != nil {
@ -138,6 +147,10 @@ func (*SimpleImg) ExportImage(opts compiler.CompilerBackendOptions) error {
path := opts.Destination
buildarg := []string{"save", "-o", path, name}
Debug(":tea: Saving image " + name)
Spinner(22)
defer SpinnerStop()
out, err := exec.Command("img", buildarg...).CombinedOutput()
if err != nil {
return errors.Wrap(err, "Failed exporting image: "+string(out))
@ -158,8 +171,13 @@ func (s *SimpleImg) ExtractRootfs(opts compiler.CompilerBackendOptions, keepPerm
}
os.RemoveAll(path)
buildarg := []string{"unpack", "-o", path, name}
Debug(":tea: Extracting image " + name)
Spinner(22)
defer SpinnerStop()
out, err := exec.Command("img", buildarg...).CombinedOutput()
if err != nil {
return errors.Wrap(err, "Failed extracting image: "+string(out))

View File

@ -0,0 +1,45 @@
// Copyright © 2021 Daniele Rondina <geaaru@sabayonlinux.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see <http://www.gnu.org/licenses/>.
package backend
import (
"bytes"
. "github.com/mudler/luet/pkg/logger"
)
type BackendWriter struct {
BufferedOutput bool
Buffer bytes.Buffer
}
func NewBackendWriter(buffered bool) *BackendWriter {
return &BackendWriter{
BufferedOutput: buffered,
}
}
func (b *BackendWriter) Write(p []byte) (int, error) {
if b.BufferedOutput {
return b.Buffer.Write(p)
} else {
Msg("info", false, false, (string(p)))
}
return len(p), nil
}
func (b *BackendWriter) Close() error { return nil }
func (b *BackendWriter) GetCombinedOutput() string { return b.Buffer.String() }

View File

@ -148,8 +148,6 @@ func (cs *LuetCompiler) CompileWithReverseDeps(keepPermissions bool, ps Compilat
}
func (cs *LuetCompiler) CompileParallel(keepPermissions bool, ps CompilationSpecs) ([]Artifact, []error) {
Spinner(22)
defer SpinnerStop()
all := make(chan CompilationSpec)
artifacts := []Artifact{}
mutex := &sync.Mutex{}

View File

@ -173,7 +173,7 @@ func level2AtomicLevel(level string) zap.AtomicLevel {
}
}
func msg(level string, withoutColor bool, msg ...interface{}) {
func Msg(level string, withoutColor, ln bool, msg ...interface{}) {
var message string
var confLevel, msgLevel int
@ -219,11 +219,16 @@ func msg(level string, withoutColor bool, msg ...interface{}) {
log2File(level, message)
}
fmt.Println(levelMsg)
if ln {
fmt.Println(levelMsg)
} else {
fmt.Print(levelMsg)
}
}
func Warning(mess ...interface{}) {
msg("warning", false, mess...)
Msg("warning", false, true, mess...)
if LuetCfg.GetGeneral().FatalWarns {
os.Exit(2)
}
@ -235,23 +240,23 @@ func Debug(mess ...interface{}) {
mess = append([]interface{}{fmt.Sprintf("DEBUG (%s:#%d:%v)",
path.Base(file), line, runtime.FuncForPC(pc).Name())}, mess...)
}
msg("debug", false, mess...)
Msg("debug", false, true, mess...)
}
func DebugC(mess ...interface{}) {
msg("debug", true, mess...)
Msg("debug", true, true, mess...)
}
func Info(mess ...interface{}) {
msg("info", false, mess...)
Msg("info", false, true, mess...)
}
func InfoC(mess ...interface{}) {
msg("info", true, mess...)
Msg("info", true, true, mess...)
}
func Error(mess ...interface{}) {
msg("error", false, mess...)
Msg("error", false, true, mess...)
}
func Fatal(mess ...interface{}) {