2021-10-20 22:13:02 +00:00
|
|
|
// Copyright © 2021 Ettore Di Giacinto <mudler@mocaccino.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/>.
|
|
|
|
|
2021-10-19 20:26:23 +00:00
|
|
|
package util
|
|
|
|
|
|
|
|
import (
|
2021-12-17 14:21:03 +00:00
|
|
|
"errors"
|
2021-10-19 20:26:23 +00:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"os/user"
|
|
|
|
"path/filepath"
|
|
|
|
"runtime"
|
|
|
|
"strings"
|
|
|
|
|
2021-12-17 14:21:03 +00:00
|
|
|
"github.com/ipfs/go-log/v2"
|
2021-10-19 20:26:23 +00:00
|
|
|
extensions "github.com/mudler/cobra-extensions"
|
2021-12-17 14:21:03 +00:00
|
|
|
"github.com/mudler/luet/pkg/api/core/context"
|
|
|
|
gc "github.com/mudler/luet/pkg/api/core/garbagecollector"
|
|
|
|
"github.com/mudler/luet/pkg/api/core/logger"
|
2021-10-20 22:13:02 +00:00
|
|
|
"github.com/mudler/luet/pkg/api/core/types"
|
2021-12-17 14:21:03 +00:00
|
|
|
"github.com/mudler/luet/pkg/solver"
|
|
|
|
"github.com/pterm/pterm"
|
|
|
|
"go.uber.org/zap/zapcore"
|
2021-10-19 20:26:23 +00:00
|
|
|
|
|
|
|
helpers "github.com/mudler/luet/pkg/helpers"
|
|
|
|
fileHelper "github.com/mudler/luet/pkg/helpers/file"
|
|
|
|
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
LuetEnvPrefix = "LUET"
|
|
|
|
)
|
|
|
|
|
|
|
|
var cfgFile string
|
|
|
|
|
|
|
|
// initConfig reads in config file and ENV variables if set.
|
|
|
|
func initConfig() {
|
2021-10-20 22:13:02 +00:00
|
|
|
setDefaults(viper.GetViper())
|
2021-10-19 20:26:23 +00:00
|
|
|
// Luet support these priorities on read configuration file:
|
|
|
|
// - command line option (if available)
|
|
|
|
// - $PWD/.luet.yaml
|
|
|
|
// - $HOME/.luet.yaml
|
|
|
|
// - /etc/luet/luet.yaml
|
|
|
|
//
|
|
|
|
// Note: currently a single viper instance support only one config name.
|
|
|
|
|
|
|
|
viper.SetEnvPrefix(LuetEnvPrefix)
|
|
|
|
viper.SetConfigType("yaml")
|
|
|
|
|
|
|
|
if cfgFile != "" { // enable ability to specify config file via flag
|
|
|
|
viper.SetConfigFile(cfgFile)
|
|
|
|
} else {
|
|
|
|
// Retrieve pwd directory
|
|
|
|
pwdDir, err := os.Getwd()
|
|
|
|
if err != nil {
|
2021-10-20 22:13:02 +00:00
|
|
|
fmt.Println(err)
|
2021-10-19 20:26:23 +00:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
homeDir := helpers.GetHomeDir()
|
|
|
|
|
|
|
|
if fileHelper.Exists(filepath.Join(pwdDir, ".luet.yaml")) || (homeDir != "" && fileHelper.Exists(filepath.Join(homeDir, ".luet.yaml"))) {
|
|
|
|
viper.AddConfigPath(".")
|
|
|
|
if homeDir != "" {
|
|
|
|
viper.AddConfigPath(homeDir)
|
|
|
|
}
|
|
|
|
viper.SetConfigName(".luet")
|
|
|
|
} else {
|
|
|
|
viper.SetConfigName("luet")
|
|
|
|
viper.AddConfigPath("/etc/luet")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
viper.AutomaticEnv() // read in environment variables that match
|
|
|
|
|
|
|
|
// Create EnvKey Replacer for handle complex structure
|
|
|
|
replacer := strings.NewReplacer(".", "__")
|
|
|
|
viper.SetEnvKeyReplacer(replacer)
|
|
|
|
viper.SetTypeByDefaultValue(true)
|
2021-10-20 22:13:02 +00:00
|
|
|
// If a config file is found, read it in.
|
|
|
|
viper.ReadInConfig()
|
|
|
|
|
2021-10-19 20:26:23 +00:00
|
|
|
}
|
|
|
|
|
2021-12-17 14:21:03 +00:00
|
|
|
var DefaultContext *context.Context
|
|
|
|
|
2021-10-20 22:13:02 +00:00
|
|
|
// InitContext inits the context by parsing the configurations from viper
|
|
|
|
// this is meant to be run before each command to be able to parse any override from
|
|
|
|
// the CLI/ENV
|
2021-12-17 14:21:03 +00:00
|
|
|
func InitContext(cmd *cobra.Command) (ctx *context.Context, err error) {
|
2021-10-19 20:26:23 +00:00
|
|
|
|
2021-12-17 14:21:03 +00:00
|
|
|
c := &types.LuetConfig{}
|
|
|
|
err = viper.Unmarshal(c)
|
2021-10-19 20:26:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-17 14:21:03 +00:00
|
|
|
// Converts user-defined config into paths
|
|
|
|
// and creates the required directory on the system if necessary
|
|
|
|
c.Init()
|
|
|
|
|
|
|
|
finalizerEnvs, _ := cmd.Flags().GetStringArray("finalizer-env")
|
|
|
|
setCliFinalizerEnvs(c, finalizerEnvs)
|
|
|
|
|
2022-01-06 22:57:56 +00:00
|
|
|
c.Solver.SolverOptions = types.SolverOptions{Type: types.SolverSingleCoreSimple, Concurrency: c.General.Concurrency}
|
2021-12-17 14:21:03 +00:00
|
|
|
|
|
|
|
ctx = context.NewContext(
|
|
|
|
context.WithConfig(c),
|
|
|
|
context.WithGarbageCollector(gc.GarbageCollector(c.System.TmpDirBase)),
|
|
|
|
)
|
|
|
|
|
2021-10-20 22:13:02 +00:00
|
|
|
// Inits the context with the configurations loaded
|
|
|
|
// It reads system repositories, sets logging, and all the
|
|
|
|
// context which is required to perform luet actions
|
2021-12-17 14:21:03 +00:00
|
|
|
return ctx, initContext(cmd, ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
func setCliFinalizerEnvs(c *types.LuetConfig, finalizerEnvs []string) error {
|
|
|
|
if len(finalizerEnvs) > 0 {
|
|
|
|
for _, v := range finalizerEnvs {
|
|
|
|
idx := strings.Index(v, "=")
|
|
|
|
if idx < 0 {
|
|
|
|
return errors.New("Found invalid runtime finalizer environment: " + v)
|
|
|
|
}
|
|
|
|
|
|
|
|
c.SetFinalizerEnv(v[0:idx], v[idx+1:])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
CommandProcessOutput = "command.process.output"
|
|
|
|
)
|
|
|
|
|
|
|
|
func initContext(cmd *cobra.Command, c *context.Context) (err error) {
|
|
|
|
if logger.IsTerminal() {
|
|
|
|
if !c.Config.Logging.Color {
|
|
|
|
pterm.DisableColor()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pterm.DisableColor()
|
|
|
|
c.Debug("Not a terminal, colors disabled")
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.Config.General.Quiet {
|
|
|
|
pterm.DisableColor()
|
|
|
|
pterm.DisableStyling()
|
2021-10-19 20:26:23 +00:00
|
|
|
}
|
|
|
|
|
2021-12-17 14:21:03 +00:00
|
|
|
level := c.Config.Logging.Level
|
|
|
|
if c.Config.General.Debug {
|
|
|
|
level = "debug"
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := cmd.Annotations[CommandProcessOutput]; ok {
|
|
|
|
// Note: create-repo output is different, so we annotate in the cmd of create-repo CommandNoProcess
|
|
|
|
// to avoid
|
|
|
|
out, _ := cmd.Flags().GetString("output")
|
|
|
|
if out != "terminal" {
|
|
|
|
level = zapcore.Level(log.LevelFatal).String()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Init logging
|
|
|
|
opts := []logger.LoggerOptions{
|
|
|
|
logger.WithLevel(level),
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.Config.Logging.NoSpinner {
|
|
|
|
opts = append(opts, logger.NoSpinner)
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.Config.Logging.EnableLogFile && c.Config.Logging.Path != "" {
|
|
|
|
f := "console"
|
2022-01-06 22:57:56 +00:00
|
|
|
if c.Config.Logging.JSONFormat {
|
2021-12-17 14:21:03 +00:00
|
|
|
f = "json"
|
|
|
|
}
|
|
|
|
opts = append(opts, logger.WithFileLogging(c.Config.Logging.Path, f))
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.Config.Logging.EnableEmoji {
|
|
|
|
opts = append(opts, logger.EnableEmoji())
|
|
|
|
}
|
|
|
|
|
|
|
|
l, err := logger.New(opts...)
|
|
|
|
|
|
|
|
c.Logger = l
|
|
|
|
|
|
|
|
c.Debug("System rootfs:", c.Config.System.Rootfs)
|
|
|
|
c.Debug("Colors", c.Config.Logging.Color)
|
|
|
|
c.Debug("Logging level", c.Config.Logging.Level)
|
|
|
|
c.Debug("Debug mode", c.Config.General.Debug)
|
|
|
|
|
2021-10-19 20:26:23 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func setDefaults(viper *viper.Viper) {
|
|
|
|
viper.SetDefault("logging.level", "info")
|
|
|
|
viper.SetDefault("logging.enable_logfile", false)
|
|
|
|
viper.SetDefault("logging.path", "/var/log/luet.log")
|
|
|
|
viper.SetDefault("logging.json_format", false)
|
|
|
|
viper.SetDefault("logging.enable_emoji", true)
|
|
|
|
viper.SetDefault("logging.color", true)
|
|
|
|
|
|
|
|
viper.SetDefault("general.concurrency", runtime.NumCPU())
|
|
|
|
viper.SetDefault("general.debug", false)
|
2021-12-04 20:35:34 +00:00
|
|
|
viper.SetDefault("general.quiet", false)
|
2021-12-27 22:11:01 +00:00
|
|
|
viper.SetDefault("general.show_build_output", true)
|
2021-10-19 20:26:23 +00:00
|
|
|
viper.SetDefault("general.fatal_warnings", false)
|
2021-10-23 09:41:15 +00:00
|
|
|
viper.SetDefault("general.http_timeout", 360)
|
2021-10-19 20:26:23 +00:00
|
|
|
|
|
|
|
u, err := user.Current()
|
|
|
|
// os/user doesn't work in from scratch environments
|
|
|
|
if err != nil || (u != nil && u.Uid == "0") {
|
|
|
|
viper.SetDefault("general.same_owner", true)
|
|
|
|
} else {
|
|
|
|
viper.SetDefault("general.same_owner", false)
|
|
|
|
}
|
|
|
|
|
|
|
|
viper.SetDefault("system.database_engine", "boltdb")
|
|
|
|
viper.SetDefault("system.database_path", "/var/cache/luet")
|
|
|
|
viper.SetDefault("system.rootfs", "/")
|
|
|
|
viper.SetDefault("system.tmpdir_base", filepath.Join(os.TempDir(), "tmpluet"))
|
|
|
|
viper.SetDefault("system.pkgs_cache_path", "packages")
|
|
|
|
|
|
|
|
viper.SetDefault("repos_confdir", []string{"/etc/luet/repos.conf.d"})
|
|
|
|
viper.SetDefault("config_protect_confdir", []string{"/etc/luet/config.protect.d"})
|
|
|
|
viper.SetDefault("config_protect_skip", false)
|
|
|
|
// TODO: Set default to false when we are ready for migration.
|
|
|
|
viper.SetDefault("config_from_host", true)
|
|
|
|
viper.SetDefault("cache_repositories", []string{})
|
|
|
|
viper.SetDefault("system_repositories", []string{})
|
2021-10-20 22:13:02 +00:00
|
|
|
viper.SetDefault("finalizer_envs", make(map[string]string))
|
2021-10-19 20:26:23 +00:00
|
|
|
|
|
|
|
viper.SetDefault("solver.type", "")
|
|
|
|
viper.SetDefault("solver.rate", 0.7)
|
|
|
|
viper.SetDefault("solver.discount", 1.0)
|
|
|
|
viper.SetDefault("solver.max_attempts", 9000)
|
|
|
|
}
|
|
|
|
|
2021-10-20 22:13:02 +00:00
|
|
|
// InitViper inits a new viper
|
|
|
|
// this is meant to be run just once at beginning to setup the root command
|
2021-12-17 14:21:03 +00:00
|
|
|
func InitViper(RootCmd *cobra.Command) {
|
2021-10-19 20:26:23 +00:00
|
|
|
cobra.OnInitialize(initConfig)
|
|
|
|
pflags := RootCmd.PersistentFlags()
|
|
|
|
pflags.StringVar(&cfgFile, "config", "", "config file (default is $HOME/.luet.yaml)")
|
2021-12-04 20:35:34 +00:00
|
|
|
pflags.BoolP("debug", "d", false, "debug output")
|
|
|
|
pflags.BoolP("quiet", "q", false, "quiet output")
|
2021-10-19 20:26:23 +00:00
|
|
|
pflags.Bool("fatal", false, "Enables Warnings to exit")
|
|
|
|
pflags.Bool("enable-logfile", false, "Enable log to file")
|
|
|
|
pflags.Bool("no-spinner", false, "Disable spinner.")
|
2021-12-17 14:21:03 +00:00
|
|
|
pflags.Bool("color", true, "Enable/Disable color.")
|
|
|
|
pflags.Bool("emoji", true, "Enable/Disable emoji.")
|
|
|
|
pflags.Bool("skip-config-protect", true, "Disable config protect analysis.")
|
|
|
|
pflags.StringP("logfile", "l", "", "Logfile path. Empty value disable log to file.")
|
2021-10-19 20:26:23 +00:00
|
|
|
pflags.StringSlice("plugin", []string{}, "A list of runtime plugins to load")
|
|
|
|
|
2021-12-17 14:21:03 +00:00
|
|
|
pflags.String("system-dbpath", "", "System db path")
|
|
|
|
pflags.String("system-target", "", "System rootpath")
|
|
|
|
pflags.String("system-engine", "", "System DB engine")
|
|
|
|
|
2022-01-06 22:57:56 +00:00
|
|
|
pflags.String("solver-type", "", "Solver strategy ( Defaults none, available: "+solver.AvailableResolvers+" )")
|
2021-12-17 14:21:03 +00:00
|
|
|
pflags.Float32("solver-rate", 0.7, "Solver learning rate")
|
|
|
|
pflags.Float32("solver-discount", 1.0, "Solver discount rate")
|
|
|
|
pflags.Int("solver-attempts", 9000, "Solver maximum attempts")
|
2021-12-27 22:11:01 +00:00
|
|
|
pflags.Bool("live-output", true, "Show live output during build")
|
2021-12-17 14:21:03 +00:00
|
|
|
|
|
|
|
pflags.Bool("same-owner", true, "Maintain same owner on uncompress.")
|
2021-10-19 20:26:23 +00:00
|
|
|
pflags.Int("concurrency", runtime.NumCPU(), "Concurrency")
|
2021-12-17 14:21:03 +00:00
|
|
|
pflags.Int("http-timeout", 360, "Default timeout for http(s) requests")
|
|
|
|
|
|
|
|
viper.BindPFlag("system.database_path", pflags.Lookup("system-dbpath"))
|
|
|
|
viper.BindPFlag("system.rootfs", pflags.Lookup("system-target"))
|
|
|
|
viper.BindPFlag("system.database_engine", pflags.Lookup("system-engine"))
|
|
|
|
viper.BindPFlag("solver.type", pflags.Lookup("solver-type"))
|
|
|
|
viper.BindPFlag("solver.discount", pflags.Lookup("solver-discount"))
|
|
|
|
viper.BindPFlag("solver.rate", pflags.Lookup("solver-rate"))
|
|
|
|
viper.BindPFlag("solver.max_attempts", pflags.Lookup("solver-attempts"))
|
2021-10-19 20:26:23 +00:00
|
|
|
|
|
|
|
viper.BindPFlag("logging.color", pflags.Lookup("color"))
|
|
|
|
viper.BindPFlag("logging.enable_emoji", pflags.Lookup("emoji"))
|
|
|
|
viper.BindPFlag("logging.enable_logfile", pflags.Lookup("enable-logfile"))
|
|
|
|
viper.BindPFlag("logging.path", pflags.Lookup("logfile"))
|
2021-12-17 14:21:03 +00:00
|
|
|
viper.BindPFlag("logging.no_spinner", pflags.Lookup("no-spinner"))
|
2021-10-19 20:26:23 +00:00
|
|
|
viper.BindPFlag("general.concurrency", pflags.Lookup("concurrency"))
|
|
|
|
viper.BindPFlag("general.debug", pflags.Lookup("debug"))
|
2021-12-04 20:35:34 +00:00
|
|
|
viper.BindPFlag("general.quiet", pflags.Lookup("quiet"))
|
2021-10-19 20:26:23 +00:00
|
|
|
viper.BindPFlag("general.fatal_warnings", pflags.Lookup("fatal"))
|
|
|
|
viper.BindPFlag("general.same_owner", pflags.Lookup("same-owner"))
|
|
|
|
viper.BindPFlag("plugin", pflags.Lookup("plugin"))
|
2021-10-23 09:41:15 +00:00
|
|
|
viper.BindPFlag("general.http_timeout", pflags.Lookup("http-timeout"))
|
2021-12-27 22:11:01 +00:00
|
|
|
viper.BindPFlag("general.show_build_output", pflags.Lookup("live-output"))
|
2021-10-19 20:26:23 +00:00
|
|
|
|
|
|
|
// Currently I maintain this only from cli.
|
|
|
|
viper.BindPFlag("no_spinner", pflags.Lookup("no-spinner"))
|
|
|
|
viper.BindPFlag("config_protect_skip", pflags.Lookup("skip-config-protect"))
|
|
|
|
|
|
|
|
// Extensions must be binary with the "luet-" prefix to be able to be shown in the help.
|
|
|
|
// we also accept extensions in the relative path where luet is being started, "extensions/"
|
|
|
|
exts := extensions.Discover("luet", "extensions")
|
|
|
|
for _, ex := range exts {
|
|
|
|
cobraCmd := ex.CobraCommand()
|
|
|
|
RootCmd.AddCommand(cobraCmd)
|
|
|
|
}
|
|
|
|
}
|