From ec156428897bd4cf47cd5349837851e73595b6c5 Mon Sep 17 00:00:00 2001 From: Ed Smith Date: Wed, 11 Aug 2021 21:31:19 +0100 Subject: [PATCH 1/3] Add the ability to check a command for existence Output is suppressed for readability, unless the check fails. --- commands.go | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/commands.go b/commands.go index c8430fb0..519ba1fb 100644 --- a/commands.go +++ b/commands.go @@ -32,12 +32,13 @@ type Command struct { type commandWrapper struct { label string + writeBeforeFlush bool buffer *bytes.Buffer } -func newCommandWrapper(label string) *commandWrapper { +func newCommandWrapper(label string, writeBeforeFlush bool) *commandWrapper { b := bytes.Buffer{} - return &commandWrapper{label, &b} + return &commandWrapper{label, writeBeforeFlush, &b} } func (w commandWrapper) out(atEOF bool) { @@ -60,7 +61,9 @@ func (w commandWrapper) out(atEOF bool) { func (w commandWrapper) Write(p []byte) (n int, err error) { n, err = w.buffer.Write(p) - w.out(false) + if w.writeBeforeFlush { + w.out(false) + } return } @@ -208,7 +211,7 @@ func (cmd *Command) restoreResolvConf(sum *[sha256.Size]byte) error { return nil } -func (cmd Command) Run(label string, cmdline ...string) error { +func (cmd Command) run(label string, w *commandWrapper, cmdline []string) error { q := newQemuHelper(cmd) q.Setup() defer q.Cleanup() @@ -241,14 +244,11 @@ func (cmd Command) Run(label string, cmdline ...string) error { } exe := exec.Command(options[0], options[1:]...) - w := newCommandWrapper(label) exe.Stdin = nil exe.Stdout = w exe.Stderr = w - defer w.flush() - if len(cmd.extraEnv) > 0 && cmd.ChrootMethod != CHROOT_METHOD_NSPAWN { exe.Env = append(os.Environ(), cmd.extraEnv...) } @@ -278,6 +278,21 @@ func (cmd Command) Run(label string, cmdline ...string) error { return nil } +func (cmd Command) Run(label string, cmdline ...string) error { + w := newCommandWrapper(label, true) + defer w.flush() + return cmd.run(label, w, cmdline) +} + +func (cmd Command) CheckExists(label string, cmdline ...string) error { + w := newCommandWrapper(label, false) + if err := cmd.run(label, w, cmdline); err != nil { + w.flush() + return fmt.Errorf("Unable to find %[1]s, %[2]v", label, err) + } + return nil +} + type qemuHelper struct { qemusrc string qemutarget string From 67bf64b7eed1dfd0c06b757ab90bcbfbc79ae63a Mon Sep 17 00:00:00 2001 From: Ed Smith Date: Sat, 14 Aug 2021 11:38:53 +0100 Subject: [PATCH 2/3] Add a new CheckEnv stage for checking command availability Verify (which should be called Prepare) must run before the fakemachine is created, in order to setup the arguments for the fakemachine itself. It then has to run again in the fakemachine in order to rebuild any necessary internal state. Command checking can only occur in the same environment as run, as commands that are in the path when running may not be available to the regular user (e.g. Debian installs debootstrap in sbin). --- action.go | 5 +++++ cmd/debos/debos.go | 40 +++++++++++++++++++++++++++++----------- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/action.go b/action.go index ea6ddd3a..0ccdbcd0 100644 --- a/action.go +++ b/action.go @@ -54,8 +54,12 @@ func (c *DebosContext) Origin(o string) (string, bool) { } type Action interface { + // Verify runs first /* FIXME verify should probably be prepare or somesuch */ Verify(context *DebosContext) error + // CheckEnv runs in the environment that Run will execute in, + // prior to Run commencing + CheckEnv(context *DebosContext) error PreMachine(context *DebosContext, m *fakemachine.Machine, args *[]string) error PreNoMachine(context *DebosContext) error Run(context *DebosContext) error @@ -75,6 +79,7 @@ type BaseAction struct { } func (b *BaseAction) Verify(context *DebosContext) error { return nil } +func (b *BaseAction) CheckEnv(context *DebosContext) error { return nil } func (b *BaseAction) PreMachine(context *DebosContext, m *fakemachine.Machine, args *[]string) error { diff --git a/cmd/debos/debos.go b/cmd/debos/debos.go index d7e1b5aa..353e5e4a 100644 --- a/cmd/debos/debos.go +++ b/cmd/debos/debos.go @@ -244,7 +244,34 @@ func main() { return } + if !runInFakeMachine && !fakemachine.InMachine() { + // We will run on the host + for _, a := range r.Actions { + // Stack PostMachineCleanup methods + defer a.PostMachineCleanup(&context) + + err = a.PreNoMachine(&context) + if exitcode = checkError(&context, err, a, "PreNoMachine"); exitcode != 0 { + return + } + } + } + + if !runInFakeMachine { + // Either we're on the host and intend to run on it, + // or we're in the fake machine and intend to run on + // it. In the former case, PreNoMachine has just run; + // in the latter PreMachine has already run. + for _, a := range r.Actions { + err = a.CheckEnv(&context) + if exitcode = checkError(&context, err, a, "CheckEnv"); exitcode != 0 { + return + } + } + } + if runInFakeMachine { + // We're on the host and intend to run on the fakemachine var args []string if options.Memory == "" { @@ -338,17 +365,8 @@ func main() { return } - if !fakemachine.InMachine() { - for _, a := range r.Actions { - // Stack PostMachineCleanup methods - defer a.PostMachineCleanup(&context) - - err = a.PreNoMachine(&context) - if exitcode = checkError(&context, err, a, "PreNoMachine"); exitcode != 0 { - return - } - } - } + // We're on the host and intend to run on the host, or we're + // in the fakemachine and intend to run on the fakemachine. // Create Rootdir if _, err = os.Stat(context.Rootdir); os.IsNotExist(err) { From 9527ef30503128a64f43b8c7307645b8947c3082 Mon Sep 17 00:00:00 2001 From: Ed Smith Date: Sat, 14 Aug 2021 11:15:52 +0100 Subject: [PATCH 3/3] Add CheckEnv steps for used commands to the actions that need it --- actions/debootstrap_action.go | 9 ++++++++ actions/filesystem_deploy_action.go | 8 +++++++ actions/image_partition_action.go | 34 +++++++++++++++++++++++++---- actions/ostree_deploy_action.go | 8 +++++++ actions/pack_action.go | 8 +++++++ actions/recipe_action.go | 9 ++++++++ 6 files changed, 72 insertions(+), 4 deletions(-) diff --git a/actions/debootstrap_action.go b/actions/debootstrap_action.go index 728fa87d..45d1dd03 100644 --- a/actions/debootstrap_action.go +++ b/actions/debootstrap_action.go @@ -125,6 +125,15 @@ func (d *DebootstrapAction) Verify(context *debos.DebosContext) error { return nil } +func (d *DebootstrapAction) CheckEnv(context *debos.DebosContext) error { + // Check that debootstrap is available + cmd := debos.Command{} + if err := cmd.CheckExists("Debootstrap", "debootstrap", "--version"); err != nil { + return err + } + return nil +} + func (d *DebootstrapAction) PreMachine(context *debos.DebosContext, m *fakemachine.Machine, args *[]string) error { mounts := d.listOptionFiles(context) diff --git a/actions/filesystem_deploy_action.go b/actions/filesystem_deploy_action.go index 57215599..d40c62d4 100644 --- a/actions/filesystem_deploy_action.go +++ b/actions/filesystem_deploy_action.go @@ -54,6 +54,14 @@ func NewFilesystemDeployAction() *FilesystemDeployAction { return fd } +func (fd *FilesystemDeployAction) CheckEnv(context *debos.DebosContext) error { + cmd := debos.Command{} + if err := cmd.CheckExists("Cp", "cp", "--help"); err != nil { + return err + } + return nil +} + func (fd *FilesystemDeployAction) setupFSTab(context *debos.DebosContext) error { if context.ImageFSTab.Len() == 0 { return errors.New("Fstab not generated, missing image-partition action?") diff --git a/actions/image_partition_action.go b/actions/image_partition_action.go index ea3d6219..f90c4114 100644 --- a/actions/image_partition_action.go +++ b/actions/image_partition_action.go @@ -332,10 +332,7 @@ func (i ImagePartitionAction) PreMachine(context *debos.DebosContext, m *fakemac return nil } -func (i ImagePartitionAction) formatPartition(p *Partition, context debos.DebosContext) error { - label := fmt.Sprintf("Formatting partition %d", p.number) - path := i.getPartitionDevice(p.number, context) - +func (i ImagePartitionAction) getPartitionCommand(p *Partition) []string { cmdline := []string{} switch p.FS { case "vfat": @@ -385,6 +382,14 @@ func (i ImagePartitionAction) formatPartition(p *Partition, context debos.DebosC } } } + return cmdline +} + +func (i ImagePartitionAction) formatPartition(p *Partition, context debos.DebosContext) error { + label := fmt.Sprintf("Formatting partition %d", p.number) + path := i.getPartitionDevice(p.number, context) + + cmdline := i.getPartitionCommand(p) if len(cmdline) != 0 { cmdline = append(cmdline, path) @@ -655,6 +660,27 @@ func (i ImagePartitionAction) PostMachineCleanup(context *debos.DebosContext) er return nil } +func (i *ImagePartitionAction) CheckEnv(context *debos.DebosContext) error { + commands := []string{ + "blkid", "parted", "sfdisk", "udevadm" } + + cmd := debos.Command{} + for _, c := range commands { + if err := cmd.CheckExists(strings.Title(c), c, "--help"); err != nil { + return err + } + } + for idx, _ := range i.Partitions { + p := &i.Partitions[idx] + cmdline := i.getPartitionCommand(p) + cmd := debos.Command{} + if err := cmd.CheckExists(strings.Title(cmdline[0]), cmdline[0], "-V"); err != nil { + return err + } + } + return nil +} + func (i *ImagePartitionAction) Verify(context *debos.DebosContext) error { if len(i.GptGap) > 0 { log.Println("WARNING: special version of parted is needed for 'gpt_gap' option") diff --git a/actions/ostree_deploy_action.go b/actions/ostree_deploy_action.go index 1ba9b135..95428c79 100644 --- a/actions/ostree_deploy_action.go +++ b/actions/ostree_deploy_action.go @@ -81,6 +81,14 @@ func NewOstreeDeployAction() *OstreeDeployAction { return ot } +func (ot *OstreeDeployAction) CheckEnv(context *debos.DebosContext) error { + cmd := debos.Command{} + if err := cmd.CheckExists("Cp", "cp", "--help"); err != nil { + return err + } + return nil +} + func (ot *OstreeDeployAction) setupFSTab(deployment *ostree.Deployment, context *debos.DebosContext) error { deploymentDir := fmt.Sprintf("ostree/deploy/%s/deploy/%s.%d", deployment.Osname(), deployment.Csum(), deployment.Deployserial()) diff --git a/actions/pack_action.go b/actions/pack_action.go index 8d349796..c8178390 100644 --- a/actions/pack_action.go +++ b/actions/pack_action.go @@ -67,6 +67,14 @@ func (pf *PackAction) Verify(context *debos.DebosContext) error { pf.Compression, strings.Join(possibleTypes, ", ")) } +func (pf *PackAction) CheckEnv(context *debos.DebosContext) error { + cmd := debos.Command{} + if err := cmd.CheckExists("Tar", "tar", "--help"); err != nil { + return err + } + return nil +} + func (pf *PackAction) Run(context *debos.DebosContext) error { usePigz := false if pf.Compression == "gz" { diff --git a/actions/recipe_action.go b/actions/recipe_action.go index beacae82..1c7ef393 100644 --- a/actions/recipe_action.go +++ b/actions/recipe_action.go @@ -90,6 +90,15 @@ func (recipe *RecipeAction) Verify(context *debos.DebosContext) error { return nil } +func (recipe *RecipeAction) CheckEnv(context *debos.DebosContext) error { + for _, a := range recipe.Actions.Actions { + if err := a.CheckEnv(&recipe.context); err != nil { + return err + } + } + return nil +} + func (recipe *RecipeAction) PreMachine(context *debos.DebosContext, m *fakemachine.Machine, args *[]string) error { // TODO: check args?