diff --git a/cmd/install.go b/cmd/install.go index 636c6d4d..0792494e 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -72,6 +72,7 @@ To force install a package: concurrent, _ := cmd.Flags().GetBool("solver-concurrent") yes := LuetCfg.Viper.GetBool("yes") downloadOnly, _ := cmd.Flags().GetBool("download-only") + finalizerEnvs, _ := cmd.Flags().GetStringArray("finalizer-env") util.SetSystemConfig() util.SetSolverConfig() @@ -88,6 +89,12 @@ To force install a package: // Load config protect configs installer.LoadConfigProtectConfs(LuetCfg) + // Load finalizer runtime environments + err := util.SetCliFinalizerEnvs(finalizerEnvs) + if err != nil { + Fatal(err.Error()) + } + inst := installer.NewLuetInstaller(installer.LuetInstallerOptions{ Concurrency: LuetCfg.GetGeneral().Concurrency, SolverOptions: *LuetCfg.GetSolverOptions(), @@ -101,7 +108,7 @@ To force install a package: inst.Repositories(repos) system := &installer.System{Database: LuetCfg.GetSystemDB(), Target: LuetCfg.GetSystem().Rootfs} - err := inst.Install(toInstall, system) + err = inst.Install(toInstall, system) if err != nil { Fatal("Error: " + err.Error()) } @@ -123,6 +130,8 @@ func init() { installCmd.Flags().Bool("solver-concurrent", false, "Use concurrent solver (experimental)") installCmd.Flags().BoolP("yes", "y", false, "Don't ask questions") installCmd.Flags().Bool("download-only", false, "Download only") + installCmd.Flags().StringArray("finalizer-env", []string{}, + "Set finalizer environment in the format key=value.") RootCmd.AddCommand(installCmd) } diff --git a/cmd/util/cli.go b/cmd/util/cli.go index 8a123697..2134bb1b 100644 --- a/cmd/util/cli.go +++ b/cmd/util/cli.go @@ -16,6 +16,9 @@ package util import ( + "errors" + "strings" + "github.com/spf13/cobra" "github.com/spf13/viper" @@ -72,3 +75,19 @@ func SetSolverConfig() (c *config.LuetSolverOptions) { MaxAttempts: attempts, } } + +func SetCliFinalizerEnvs(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) + } + + LuetCfg.SetFinalizerEnv(v[0:idx], v[idx+1:]) + } + + } + + return nil +} diff --git a/contrib/config/luet.yaml b/contrib/config/luet.yaml index 95171f98..fdda1a9a 100644 --- a/contrib/config/luet.yaml +++ b/contrib/config/luet.yaml @@ -99,6 +99,14 @@ # If set to false rootfs path is used as prefix. # config_from_host: true # +# +# ------------------------------------------------ +# Finalizer Environment Variables +# ----------------------------------------------- +# finalizer_envs: +# - key: "BUILD_ISO" +# value: "1" +# # System repositories # --------------------------------------------- # In alternative to define repositories files diff --git a/pkg/config/config.go b/pkg/config/config.go index 2f50c067..66573110 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -221,6 +221,11 @@ func (r *LuetRepository) String() string { r.Name, r.Priority, r.Type, r.Enable, r.Cached) } +type LuetKV struct { + Key string `json:"key" yaml:"key" mapstructure:"key"` + Value string `json:"value" yaml:"value" mapstructure:"value"` +} + type LuetConfig struct { Viper *v.Viper @@ -236,6 +241,8 @@ type LuetConfig struct { CacheRepositories []LuetRepository `mapstructure:"repetitors"` SystemRepositories []LuetRepository `mapstructure:"repositories"` + FinalizerEnvs []LuetKV `json:"finalizer_envs,omitempty" yaml:"finalizer_envs,omitempty" mapstructure:"finalizer_envs,omitempty"` + ConfigProtectConfFiles []ConfigProtectConfFile } @@ -284,6 +291,7 @@ func GenDefault(viper *v.Viper) { viper.SetDefault("config_from_host", true) viper.SetDefault("cache_repositories", []string{}) viper.SetDefault("system_repositories", []string{}) + viper.SetDefault("finalizer_envs", make(map[string]string, 0)) viper.SetDefault("solver.type", "") viper.SetDefault("solver.rate", 0.7) @@ -305,6 +313,58 @@ func (c *LuetConfig) AddSystemRepository(r LuetRepository) { c.SystemRepositories = append(c.SystemRepositories, r) } +func (c *LuetConfig) GetFinalizerEnvsMap() map[string]string { + ans := make(map[string]string, 0) + + for _, kv := range c.FinalizerEnvs { + ans[kv.Key] = kv.Value + } + return ans +} + +func (c *LuetConfig) SetFinalizerEnv(k, v string) { + keyPresent := false + envs := []LuetKV{} + + for _, kv := range c.FinalizerEnvs { + if kv.Key == k { + keyPresent = true + envs = append(envs, LuetKV{Key: kv.Key, Value: v}) + } else { + envs = append(envs, kv) + } + } + if !keyPresent { + envs = append(envs, LuetKV{Key: k, Value: v}) + } + + c.FinalizerEnvs = envs +} + +func (c *LuetConfig) GetFinalizerEnvs() []string { + ans := []string{} + for _, kv := range c.FinalizerEnvs { + ans = append(ans, fmt.Sprintf("%s=%s", kv.Key, kv.Value)) + } + return ans +} + +func (c *LuetConfig) GetFinalizerEnv(k string) (string, error) { + keyNotPresent := true + ans := "" + for _, kv := range c.FinalizerEnvs { + if kv.Key == k { + keyNotPresent = false + ans = kv.Value + } + } + + if keyNotPresent { + return "", errors.New("Finalizer key " + k + " not found") + } + return ans, nil +} + func (c *LuetConfig) GetLogging() *LuetLoggingConfig { return &c.Logging } diff --git a/pkg/installer/finalizer.go b/pkg/installer/finalizer.go index b68bba73..94e57e7d 100644 --- a/pkg/installer/finalizer.go +++ b/pkg/installer/finalizer.go @@ -21,6 +21,7 @@ import ( "github.com/ghodss/yaml" box "github.com/mudler/luet/pkg/box" + . "github.com/mudler/luet/pkg/config" . "github.com/mudler/luet/pkg/logger" "github.com/pkg/errors" @@ -51,13 +52,14 @@ func (f *LuetFinalizer) RunInstall(s *System) error { Info(":shell: Executing finalizer on ", s.Target, cmd, toRun) if s.Target == string(os.PathSeparator) { cmd := exec.Command(cmd, toRun...) + cmd.Env = LuetCfg.GetFinalizerEnvs() stdoutStderr, err := cmd.CombinedOutput() if err != nil { return errors.Wrap(err, "Failed running command: "+string(stdoutStderr)) } Info(string(stdoutStderr)) } else { - b := box.NewBox(cmd, toRun, []string{}, []string{}, s.Target, false, true, true) + b := box.NewBox(cmd, toRun, []string{}, LuetCfg.GetFinalizerEnvs(), s.Target, false, true, true) err := b.Run() if err != nil { return errors.Wrap(err, "Failed running command ") diff --git a/tests/fixtures/finalizers_envs/alpine/build.yaml b/tests/fixtures/finalizers_envs/alpine/build.yaml new file mode 100644 index 00000000..19a2ec56 --- /dev/null +++ b/tests/fixtures/finalizers_envs/alpine/build.yaml @@ -0,0 +1,2 @@ +image: "alpine" +unpack: true diff --git a/tests/fixtures/finalizers_envs/alpine/definition.yaml b/tests/fixtures/finalizers_envs/alpine/definition.yaml new file mode 100644 index 00000000..c0a33bdb --- /dev/null +++ b/tests/fixtures/finalizers_envs/alpine/definition.yaml @@ -0,0 +1,3 @@ +category: "seed" +name: "alpine-finalizer-envs" +version: "1.0" diff --git a/tests/fixtures/finalizers_envs/alpine/finalize.yaml b/tests/fixtures/finalizers_envs/alpine/finalize.yaml new file mode 100644 index 00000000..9e4c7f48 --- /dev/null +++ b/tests/fixtures/finalizers_envs/alpine/finalize.yaml @@ -0,0 +1,6 @@ +install: +- env +- echo "$@" +- echo "$0" > /tmp/foo +- if [ -z "$BUILD_ISO" ] ; then echo "test" > /tmp/foo2 ; fi +- if [ -z "$CLI_ENV" ] ; then echo "test" > /tmp/foo3 ; fi diff --git a/tests/integration/07_finalizer_envs.sh b/tests/integration/07_finalizer_envs.sh new file mode 100755 index 00000000..68a40e31 --- /dev/null +++ b/tests/integration/07_finalizer_envs.sh @@ -0,0 +1,102 @@ +#!/bin/bash + +export LUET_NOLOCK=true +export luetbin="$ROOT_DIR/tests/integration/bin/luet" + +oneTimeSetUp() { +export tmpdir="$(mktemp -d)" +} + +oneTimeTearDown() { + rm -rf "$tmpdir" +} + +testBuild() { + + # Ensure thet repos_confdir is empty to avoid reading + # repositories availables on host. + + mkdir $tmpdir/repos + cat < $tmpdir/luet-build.yaml +general: + debug: true + database_path: "/" + database_engine: "boltdb" +config_from_host: true +finalizer_envs: + BUILD_ISO: "1" +repos_confdir: + - "$tmpdir/repos" +EOF + + mkdir $tmpdir/testbuild + ${luetbin} build --config $tmpdir/luet-build.yaml --tree "$ROOT_DIR/tests/fixtures/finalizers_envs" --destination $tmpdir/testbuild --compression gzip --all + buildst=$? + assertEquals 'builds successfully' "$buildst" "0" + assertTrue 'create package' "[ -e '$tmpdir/testbuild/alpine-finalizer-envs-seed-1.0.package.tar.gz' ]" +} + +testRepo() { + assertTrue 'no repository' "[ ! -e '$tmpdir/testbuild/repository.yaml' ]" + ${luetbin} create-repo --tree "$ROOT_DIR/tests/fixtures/finalizers_envs" \ + --output $tmpdir/testbuild \ + --packages $tmpdir/testbuild \ + --name "test" \ + --descr "Test Repo" \ + --urls $tmpdir/testrootfs \ + --type disk > /dev/null + + createst=$? + assertEquals 'create repo successfully' "$createst" "0" + assertTrue 'create repository' "[ -e '$tmpdir/testbuild/repository.yaml' ]" +} + +testConfig() { + mkdir $tmpdir/testrootfs + cat < $tmpdir/luet.yaml +general: + debug: true +system: + rootfs: $tmpdir/testrootfs + database_path: "/" + database_engine: "boltdb" +config_from_host: true +finalizer_envs: + - key: "BUILD_ISO" + value: "1" + +repos_confdir: + - "$tmpdir/repos" + +repositories: + - name: "main" + type: "disk" + enable: true + urls: + - "$tmpdir/testbuild" +EOF + ${luetbin} config --config $tmpdir/luet.yaml + res=$? + assertEquals 'config test successfully' "$res" "0" +} + +testInstall() { + ${luetbin} install -y --finalizer-env "CLI_ENV=1" --config $tmpdir/luet.yaml seed/alpine-finalizer-envs@1.0 + installst=$? + assertEquals 'install test successfully' "$installst" "0" + assertTrue 'package installed' "[ -e '$tmpdir/testrootfs/bin/busybox' ]" + assertTrue 'finalizer does not run' "[ -e '$tmpdir/testrootfs/tmp/foo' ]" + assertTrue 'finalizer env var is not present' "[ ! -e '$tmpdir/testrootfs/tmp/foo2' ]" + assertTrue 'finalizer env var cli is not present' "[ ! -e '$tmpdir/testrootfs/tmp/foo3' ]" +} + + +testCleanup() { + ${luetbin} cleanup --config $tmpdir/luet.yaml + installst=$? + assertEquals 'install test successfully' "$installst" "0" +} + +# Load shUnit2. +. "$ROOT_DIR/tests/integration/shunit2"/shunit2 +