Skip to content

Commit

Permalink
actions: apt-file: initial implementation of action to install local …
Browse files Browse the repository at this point in the history
…debian packages

The apt-file action allows local .deb packages to be installed using the `apt`
command, much like the apt action but for local packages rather than those from
apt repositories.

Resolves: #157
Closes: #165

Signed-off-by: Christopher Obbard <[email protected]>
  • Loading branch information
obbardc committed Jun 22, 2020
1 parent ee15bc4 commit 69ff4e0
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ of each other.

Some of the actions provided by debos to customize and produce images are:

* apt-file: installs packages and their dependencies from local 'deb' files
* apt: install packages and their dependencies with 'apt'
* debootstrap: construct the target rootfs with debootstrap
* download: download a single file from the internet
Expand Down
164 changes: 164 additions & 0 deletions actions/apt_file_action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
AptFile Action
Install packages from .deb files and their dependencies to the target rootfs
with 'apt'.
Yaml syntax:
- action: apt-file
origin: name
recommends: bool
unauthenticated: bool
packages:
- package1
- package2
Mandatory properties:
- packages -- list of packages to install. Resolves Unix-style glob patterns.
Optional properties:
- origin -- reference to named file or directory. Defaults to recipe directory.
- recommends -- boolean indicating if suggested packages will be installed. Defaults to false.
- unauthenticated -- boolean indicating if unauthenticated packages can be installed. Defaults to false.
Example to install named packages in a subdirectory under `debs/`:
- action: apt-file
description: Test install from file
packages:
- pkgs/bmap-tools_*_all.deb
- pkgs/fakemachine_*_amd64.deb
Example to download and install a package:
- action: download
url: http://ftp.us.debian.org/debian/pool/main/b/bmap-tools/bmap-tools_3.5-2_all.deb
name: bmap-tools-pkg
- action: apt-file
description: Test install from download
origin: bmap-tools-pkg
*/
package actions

import (
"fmt"
"os"
"path"
"path/filepath"
"strings"
"github.com/go-debos/debos"
)

type AptFileAction struct {
debos.BaseAction `yaml:",inline"`
Recommends bool
Unauthenticated bool
Origin string
Packages []string
}

func (apt *AptFileAction) Run(context *debos.DebosContext) error {
apt.LogStart()
var origin string
aptOptions := []string{"apt", "-oDpkg::Progress-Fancy=0", "--yes"}
pkgs := []string{}

c := debos.NewChrootCommandForContext(*context)
c.AddEnv("DEBIAN_FRONTEND=noninteractive")

// get the full path of a named origin
if len(apt.Origin) > 0 {
var found bool
if origin, found = context.Origins[apt.Origin]; !found {
return fmt.Errorf("Origin not found '%s'", apt.Origin)
}
} else {
// otherwise fallback to RecipeDir
origin = context.RecipeDir
}

/* create a list of full paths of packages to install: if the origin is a
* single file (e.g download action) then just return that package, otherwise
* append package name to the origin path and glob to create a list of packages */
file, err := os.Stat(origin)
if err != nil {
return err
}
if file.IsDir() {
if len(apt.Packages) == 0 {
return fmt.Errorf("No packages defined")
}

for _, pkg := range apt.Packages {
// resolve globs
source := path.Join(origin, pkg)
matches, err := filepath.Glob(source)
if err != nil {
return err
}
if len(matches) == 0 {
return fmt.Errorf("File(s) not found after globbing: %s", pkg)
}

pkgs = append(pkgs, matches...)
}
} else {
pkgs = append(pkgs, origin)
}

/* bind mount each package into rootfs & update the list with the
* path relative to the chroot */
for idx, pkg := range pkgs {
// check for duplicates after globbing
for j := idx + 1; j < len(pkgs); j++ {
if pkgs[j] == pkg {
return fmt.Errorf("Duplicate package found: %s", pkg)
}
}

// only bind-mount if the package is outside the rootfs
if strings.HasPrefix(pkg, context.Rootdir) {
pkg = strings.TrimPrefix(pkg, context.Rootdir)
} else {
c.AddBindMount(pkg, "")
}

// update pkgs with the resolved path
pkgs[idx] = "." + pkg
}

err = c.Run("apt-file", "apt-get", "update")
if err != nil {
return err
}

if !apt.Recommends {
aptOptions = append(aptOptions, "--no-install-recommends")
}

if apt.Unauthenticated {
aptOptions = append(aptOptions, "--allow-unauthenticated")
}

aptOptions = append(aptOptions, "install")
aptOptions = append(aptOptions, pkgs...)
err = c.Run("apt-file", aptOptions...)
if err != nil {
return err
}

err = c.Run("apt-file", "apt-get", "clean")
if err != nil {
return err
}

return nil
}
4 changes: 4 additions & 0 deletions actions/recipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Mandatory properties for receipt:
Supported actions
- apt-file -- https://godoc.org/github.com/go-debos/debos/actions#hdr-AptFile_Action
- apt -- https://godoc.org/github.com/go-debos/debos/actions#hdr-Apt_Action
- debootstrap -- https://godoc.org/github.com/go-debos/debos/actions#hdr-Debootstrap_Action
Expand Down Expand Up @@ -106,6 +108,8 @@ func (y *YamlAction) UnmarshalYAML(unmarshal func(interface{}) error) error {
y.Action = &UnpackAction{}
case "run":
y.Action = &RunAction{}
case "apt-file":
y.Action = &AptFileAction{}
case "apt":
y.Action = &AptAction{}
case "ostree-commit":
Expand Down

0 comments on commit 69ff4e0

Please sign in to comment.