Merge pull request #3136 from rn/sources

Allow external directories for 'linuxkit pkg build'
This commit is contained in:
Rolf Neugebauer 2018-07-26 18:06:15 +01:00 committed by GitHub
commit bfa8be505b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 143 additions and 4 deletions

View File

@ -37,6 +37,7 @@ A package source consists of a directory containing at least two files:
- `image` _(string)_: *(mandatory)* The name of the image to build - `image` _(string)_: *(mandatory)* The name of the image to build
- `org` _(string)_: The hub/registry organisation to which this package belongs - `org` _(string)_: The hub/registry organisation to which this package belongs
- `arches` _(list of string)_: The architectures which this package should be built for (valid entries are `GOARCH` names) - `arches` _(list of string)_: The architectures which this package should be built for (valid entries are `GOARCH` names)
- `extra-sources` _(list of strings)_: Additional sources for the package outside the package directory. The format is `src:dst`, where `src` can be relative to the package directory and `dst` is the destination in the build context. This is useful for sharing files, such as vendored go code, between packages.
- `gitrepo` _(string)_: The git repository where the package source is kept. - `gitrepo` _(string)_: The git repository where the package source is kept.
- `network` _(bool)_: Allow network access during the package build (default: no) - `network` _(bool)_: Allow network access during the package build (default: no)
- `disable-content-trust` _(bool)_: Disable Docker content trust for this package (default: no) - `disable-content-trust` _(bool)_: Disable Docker content trust for this package (default: no)

View File

@ -1,12 +1,18 @@
package pkglib package pkglib
import ( import (
"archive/tar"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"os" "os"
"path"
"path/filepath"
"runtime" "runtime"
"strings"
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/version" "github.com/linuxkit/linuxkit/src/cmd/linuxkit/version"
log "github.com/sirupsen/logrus"
) )
type buildOpts struct { type buildOpts struct {
@ -141,6 +147,8 @@ func (p Pkg) Build(bos ...BuildOpt) error {
args = append(args, "--label=org.mobyproject.linuxkit.version="+version.Version) args = append(args, "--label=org.mobyproject.linuxkit.version="+version.Version)
args = append(args, "--label=org.mobyproject.linuxkit.revision="+version.GitCommit) args = append(args, "--label=org.mobyproject.linuxkit.revision="+version.GitCommit)
d.ctx = &buildCtx{sources: p.sources}
if err := d.build(p.Tag()+suffix, p.path, args...); err != nil { if err := d.build(p.Tag()+suffix, p.path, args...); err != nil {
return err return err
} }
@ -191,3 +199,59 @@ func (p Pkg) Build(bos ...BuildOpt) error {
return nil return nil
} }
type buildCtx struct {
sources []pkgSource
}
// Copy iterates over the sources, tars up the content after rewriting the paths.
// It assumes that sources is sane, ie is well formed and the first part is an absolute path
// and that it exists. NewFromCLI() ensures that.
func (c *buildCtx) Copy(w io.WriteCloser) error {
tw := tar.NewWriter(w)
defer func() {
tw.Close()
w.Close()
}()
for _, s := range c.sources {
log.Debugf("Adding to build context: %s -> %s", s.src, s.dst)
f := func(p string, i os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("ctx: Walk error on %s: %v", p, err)
}
h, err := tar.FileInfoHeader(i, "")
if err != nil {
return fmt.Errorf("ctx: Converting FileInfo for %s: %v", p, err)
}
h.Name = path.Join(s.dst, strings.TrimPrefix(p, s.src))
if err := tw.WriteHeader(h); err != nil {
return fmt.Errorf("ctx: Writing header for %s: %v", p, err)
}
if !i.Mode().IsRegular() {
return nil
}
f, err := os.Open(p)
if err != nil {
return fmt.Errorf("ctx: Open %s: %v", p, err)
}
defer f.Close()
_, err = io.Copy(tw, f)
if err != nil {
return fmt.Errorf("ctx: Writing %s: %v", p, err)
}
return nil
}
if err := filepath.Walk(s.src, f); err != nil {
return err
}
}
return nil
}

View File

@ -6,10 +6,12 @@ package pkglib
import ( import (
"fmt" "fmt"
"io"
"os" "os"
"os/exec" "os/exec"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup"
) )
const dctEnableEnv = "DOCKER_CONTENT_TRUST=1" const dctEnableEnv = "DOCKER_CONTENT_TRUST=1"
@ -17,6 +19,14 @@ const dctEnableEnv = "DOCKER_CONTENT_TRUST=1"
type dockerRunner struct { type dockerRunner struct {
dct bool dct bool
cache bool cache bool
// Optional build context to use
ctx buildContext
}
type buildContext interface {
// Copy copies the build context to the supplied WriterCloser
Copy(io.WriteCloser) error
} }
func newDockerRunner(dct, cache bool) dockerRunner { func newDockerRunner(dct, cache bool) dockerRunner {
@ -52,6 +62,8 @@ func (dr dockerRunner) command(args ...string) error {
dct = dctEnableEnv + " " dct = dctEnableEnv + " "
} }
var eg errgroup.Group
if args[0] == "build" { if args[0] == "build" {
buildArgs := []string{} buildArgs := []string{}
for _, proxyVarName := range proxyEnvVars { for _, proxyVarName := range proxyEnvVars {
@ -61,15 +73,30 @@ func (dr dockerRunner) command(args ...string) error {
} }
} }
cmd.Args = append(append(cmd.Args[:2], buildArgs...), cmd.Args[2:]...) cmd.Args = append(append(cmd.Args[:2], buildArgs...), cmd.Args[2:]...)
if dr.ctx != nil {
stdin, err := cmd.StdinPipe()
if err != nil {
return err
}
eg.Go(func() error {
defer stdin.Close()
return dr.ctx.Copy(stdin)
})
cmd.Args = append(cmd.Args[:len(cmd.Args)-1], "-")
}
} }
log.Debugf("Executing: %s%v", dct, cmd.Args) log.Debugf("Executing: %s%v", dct, cmd.Args)
err := cmd.Run() if err := cmd.Run(); err != nil {
if isExecErrNotFound(err) { if isExecErrNotFound(err) {
return fmt.Errorf("linuxkit pkg requires docker to be installed") return fmt.Errorf("linuxkit pkg requires docker to be installed")
}
return err
} }
return err return eg.Wait()
} }
func (dr dockerRunner) pull(img string) (bool, error) { func (dr dockerRunner) pull(img string) (bool, error) {

View File

@ -1,11 +1,13 @@
package pkglib package pkglib
import ( import (
"crypto/sha1"
"flag" "flag"
"fmt" "fmt"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strings" "strings"
@ -17,6 +19,7 @@ type pkgInfo struct {
Image string `yaml:"image"` Image string `yaml:"image"`
Org string `yaml:"org"` Org string `yaml:"org"`
Arches []string `yaml:"arches"` Arches []string `yaml:"arches"`
ExtraSources []string `yaml:"extra-sources"`
GitRepo string `yaml:"gitrepo"` // ?? GitRepo string `yaml:"gitrepo"` // ??
Network bool `yaml:"network"` Network bool `yaml:"network"`
DisableContentTrust bool `yaml:"disable-content-trust"` DisableContentTrust bool `yaml:"disable-content-trust"`
@ -32,12 +35,19 @@ type pkgInfo struct {
} `yaml:"depends"` } `yaml:"depends"`
} }
// Specifies the source directory for a package and their destination in the build context.
type pkgSource struct {
src string
dst string
}
// Pkg encapsulates information about a package's source // Pkg encapsulates information about a package's source
type Pkg struct { type Pkg struct {
// These correspond to pkgInfo fields // These correspond to pkgInfo fields
image string image string
org string org string
arches []string arches []string
sources []pkgSource
gitRepo string gitRepo string
network bool network bool
trust bool trust bool
@ -169,6 +179,37 @@ func NewFromCLI(fs *flag.FlagSet, args ...string) (Pkg, error) {
} }
}) })
var srcHashes string
sources := []pkgSource{{src: pkgPath, dst: "/"}}
for _, source := range pi.ExtraSources {
tmp := strings.Split(source, ":")
if len(tmp) != 2 {
return Pkg{}, fmt.Errorf("Bad source format in %s", source)
}
srcPath := filepath.Clean(tmp[0]) // Should work with windows paths
dstPath := path.Clean(tmp[1]) // 'path' here because this should be a Unix path
if !filepath.IsAbs(srcPath) {
srcPath = filepath.Join(pkgPath, srcPath)
}
g, err := newGit(srcPath)
if err != nil {
return Pkg{}, err
}
if g == nil {
return Pkg{}, fmt.Errorf("Source %s not in a git repository", srcPath)
}
h, err := g.treeHash(srcPath, hashCommit)
if err != nil {
return Pkg{}, err
}
srcHashes += h
sources = append(sources, pkgSource{src: srcPath, dst: dstPath})
}
git, err := newGit(pkgPath) git, err := newGit(pkgPath)
if err != nil { if err != nil {
return Pkg{}, err return Pkg{}, err
@ -187,6 +228,11 @@ func NewFromCLI(fs *flag.FlagSet, args ...string) (Pkg, error) {
return Pkg{}, err return Pkg{}, err
} }
if srcHashes != "" {
hash += srcHashes
hash = fmt.Sprintf("%x", sha1.Sum([]byte(hash)))
}
if dirty { if dirty {
hash += "-dirty" hash += "-dirty"
} }
@ -199,6 +245,7 @@ func NewFromCLI(fs *flag.FlagSet, args ...string) (Pkg, error) {
hash: hash, hash: hash,
commitHash: hashCommit, commitHash: hashCommit,
arches: pi.Arches, arches: pi.Arches,
sources: sources,
gitRepo: pi.GitRepo, gitRepo: pi.GitRepo,
network: pi.Network, network: pi.Network,
trust: !pi.DisableContentTrust, trust: !pi.DisableContentTrust,