File file.obscpio of Package dapper

07070100000000000081a400000000000000000000000167a5f310000005ee000000000000000000000000000000000000000c00000000file/env.gopackage file

import (
	"strings"
)

type Context map[string]string

func (c Context) Source() string {
	source := "/source/"
	if v, ok := c["DAPPER_SOURCE"]; ok && v != "" {
		source = v
	}

	if !strings.HasSuffix(source, "/") {
		source += "/"
	}

	return source
}

func (c Context) Cp() string {
	if v, ok := c["DAPPER_CP"]; ok && v != "" {
		return v
	}
	return "."
}

func (c Context) Socket() bool {
	if v, ok := c["DAPPER_DOCKER_SOCKET"]; ok && v != "" {
		return "true" == v
	}
	return false
}

func (c Context) Mode(mode string) string {
	switch mode {
	case "cp", "bind":
		return mode
	}
	return "cp"
}

func (c Context) Env() []string {
	val := []string{}
	if v, ok := c["DAPPER_ENV"]; ok && v != "" {
		val = strings.Split(v, " ")
	}

	ret := []string{}

	for _, i := range val {
		i = strings.TrimSpace(i)
		if i != "" {
			ret = append(ret, i)
		}
	}

	return ret
}

func (c Context) Shell() string {
	if shell, ok := c["SHELL"]; ok && shell != "" {
		return shell
	}
	return "/bin/bash"
}

func (c Context) Output() []string {
	if v, ok := c["DAPPER_OUTPUT"]; ok {
		ret := []string{}
		for _, i := range strings.Split(v, " ") {
			i = strings.TrimSpace(i)
			if i != "" {
				ret = append(ret, i)
			}
		}
		return ret
	}
	return []string{}
}

func (c Context) RunArgs() []string {
	if v, ok := c["DAPPER_RUN_ARGS"]; ok {
		ret := []string{}
		for _, i := range strings.Split(v, " ") {
			i = strings.TrimSpace(i)
			if i != "" {
				ret = append(ret, i)
			}
		}
		return ret
	}
	return []string{}
}
07070100000001000081a400000000000000000000000167a5f3100000010d000000000000000000000000000000000000001100000000file/env_unix.go// +build linux freebsd openbsd darwin

package file

import (
	"os"
	"strings"
)

func (c Context) HostSocket() string {
	s := os.Getenv("DOCKER_HOST")
	if strings.HasPrefix(s, "unix://") {
		return strings.TrimPrefix(s, "unix://")
	}
	return "/var/run/docker.sock"
}
07070100000002000081a400000000000000000000000167a5f310000000e9000000000000000000000000000000000000001400000000file/env_windows.gopackage file

import (
	"os"
	"strings"
)

func (c Context) HostSocket() string {
	s := os.Getenv("DOCKER_HOST")
	if strings.HasPrefix(s, "npipe://") {
		return strings.TrimPrefix(s, "npipe://")
	}
	return "//./pipe/docker_engine"
}
07070100000003000081a400000000000000000000000167a5f310000025cb000000000000000000000000000000000000000d00000000file/file.gopackage file

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"syscall"

	"github.com/mattn/go-isatty"
	"github.com/sirupsen/logrus"
)

var (
	re           = regexp.MustCompile("[^a-zA-Z0-9]")
	ErrSkipBuild = errors.New("skip build")
)

type Dapperfile struct {
	File        string
	Mode        string
	docker      string
	env         Context
	Socket      bool
	NoOut       bool
	Args        []string
	From        string
	Quiet       bool
	hostArch    string
	Keep        bool
	NoContext   bool
	MountSuffix string
	Target      string
}

func Lookup(file string) (*Dapperfile, error) {
	if _, err := os.Stat(file); err != nil {
		return nil, err
	}

	d := &Dapperfile{
		File: file,
	}

	return d, d.init()
}

func (d *Dapperfile) init() error {
	docker, err := exec.LookPath("docker")
	if err != nil {
		return err
	}
	d.docker = docker
	if d.Args, err = d.argsFromEnv(d.File); err != nil {
		return err
	}
	if d.hostArch == "" {
		d.hostArch = d.findHostArch()
	}
	return nil
}

func (d *Dapperfile) argsFromEnv(dockerfile string) ([]string, error) {
	file, err := os.Open(dockerfile)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	r := []string{}
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		fields := strings.Fields(line)
		if len(fields) <= 1 {
			continue
		}

		command := fields[0]
		if command != "ARG" {
			continue
		}

		key := strings.Split(fields[1], "=")[0]
		value := os.Getenv(key)

		if key == "DAPPER_HOST_ARCH" && value == "" {
			value = d.findHostArch()
		}

		if key == "DAPPER_HOST_ARCH" {
			d.hostArch = value
		}

		if value != "" {
			r = append(r, fmt.Sprintf("%s=%s", key, value))
		}
	}

	return r, nil
}

func (d *Dapperfile) Run(commandArgs []string) error {
	tag, err := d.build(nil, true)
	if err != nil {
		return err
	}

	logrus.Debugf("Running build in %s", tag)
	name, args := d.runArgs(tag, "", commandArgs)
	defer func() {
		if d.Keep {
			logrus.Infof("Keeping build container %s", name)
		} else {
			logrus.Debugf("Deleting temp container %s", name)
			if _, err := d.execWithOutput("rm", "-fv", name); err != nil {
				logrus.Debugf("Error deleting temp container: %s", err)
			}
		}
	}()

	if err := d.run(args...); err != nil {
		return err
	}

	source := d.env.Source()
	output := d.env.Output()
	if !d.IsBind() && !d.NoOut {
		for _, i := range output {
			p := i
			if !strings.HasPrefix(p, "/") {
				p = path.Join(source, i)
			}
			targetDir := path.Dir(i)
			if err := os.MkdirAll(targetDir, 0755); err != nil {
				return err
			}
			logrus.Infof("docker cp %s %s", p, targetDir)
			if err := d.exec("cp", name+":"+p, targetDir); err != nil {
				logrus.Debugf("Error copying back '%s': %s", i, err)
			}
		}
	}

	return nil
}

func (d *Dapperfile) Shell(commandArgs []string) error {
	tag, err := d.build(nil, true)
	if err != nil {
		return err
	}

	logrus.Debugf("Running shell in %s", tag)
	_, args := d.runArgs(tag, d.env.Shell(), nil)
	args = append([]string{"--rm"}, args...)

	return d.runExec(args...)
}

func (d *Dapperfile) runArgs(tag, shell string, commandArgs []string) (string, []string) {
	name := fmt.Sprintf("%s-%s", strings.Split(tag, ":")[0], randString())

	args := []string{"-i", "--name", name}

	if isatty.IsTerminal(os.Stdout.Fd()) {
		args = append(args, "-t")
	}

	if d.env.Socket() || d.Socket {
		args = append(args, "-v", d.vSocket())
	}

	if d.IsBind() {
		wd, err := os.Getwd()
		if err == nil {
			suffix := ""
			if d.MountSuffix != "" {
				suffix = ":" + d.MountSuffix
			}
			args = append(args, "-v", fmt.Sprintf("%s:%s%s", fmt.Sprintf("%s/%s", wd, d.env.Cp()), d.env.Source(), suffix))
		}
	}

	args = append(args, "-e", fmt.Sprintf("DAPPER_UID=%d", os.Getuid()))
	args = append(args, "-e", fmt.Sprintf("DAPPER_GID=%d", os.Getgid()))

	for _, env := range d.env.Env() {
		args = append(args, "-e", env)
	}

	if shell != "" {
		args = append(args, "--entrypoint", shell)
		args = append(args, "-e", "TERM")
	}

	args = append(args, d.env.RunArgs()...)
	args = append(args, tag)

	if shell != "" && len(commandArgs) == 0 {
		args = append(args, "-")
	} else {
		args = append(args, commandArgs...)
	}

	return name, args
}

func (d *Dapperfile) findHostArch() string {
	output, err := d.execWithOutput("version", "-f", "{{.Server.Arch}}")
	if err != nil {
		return runtime.GOARCH
	}
	return strings.TrimSpace(string(output))
}

func (d *Dapperfile) Build(args []string) error {
	_, err := d.build(args, false)
	return err
}

func (d *Dapperfile) build(args []string, copy bool) (string, error) {
	dapperFile, err := d.dapperFile()
	if err != nil {
		return "", err
	}

	tag := d.tag()
	logrus.Debugf("Building %s using %s", tag, d.File)
	buildArgs := []string{"build"}
	if len(args) == 0 {
		buildArgs = append(buildArgs, "-t", tag)
	}

	if d.Quiet {
		buildArgs = append(buildArgs, "-q")
	}

	if d.Target != "" {
		buildArgs = append(buildArgs, "--target", d.Target)
	}

	for _, v := range d.Args {
		buildArgs = append(buildArgs, "--build-arg", v)
	}

	if d.NoContext {
		buildArgs = append(buildArgs, "-")
		buildArgs = append(buildArgs, args...)
		if err := d.execWithStdin(bytes.NewBuffer(dapperFile), buildArgs...); err != nil {
			return "", err
		}
	} else {
		tempfile, err := d.tempfile(dapperFile)
		if err != nil {
			return "", err
		}
		defer os.Remove(tempfile)

		buildArgs = append(buildArgs, "-f", tempfile)
		if len(args) > 0 {
			buildArgs = append(buildArgs, args...)
		} else {
			buildArgs = append(buildArgs, ".")
		}

		if err := d.exec(buildArgs...); err != nil {
			return "", err
		}
	}

	if !copy {
		return tag, nil
	}

	if err := d.readEnv(tag); err != nil {
		return "", err
	}

	if !d.IsBind() {
		text := fmt.Sprintf("FROM %s\nCOPY %s %s", tag, d.env.Cp(), d.env.Source())
		if err := d.buildWithContent(tag, text); err != nil {
			return "", err
		}
	}

	return tag, nil
}

func (d *Dapperfile) buildWithContent(tag, content string) error {
	tempfile, err := d.tempfile([]byte(content))
	if err != nil {
		return err
	}

	defer func() {
		logrus.Debugf("Deleting tempfile %s", tempfile)
		if err := os.Remove(tempfile); err != nil {
			logrus.Errorf("Failed to delete tempfile %s: %v", tempfile, err)
		}
	}()

	return d.exec("build", "-t", tag, "-f", tempfile, ".")
}

func (d *Dapperfile) readEnv(tag string) error {
	var envList []string

	args := []string{"inspect", "-f", "{{json .Config.Env}}", tag}

	cmd := exec.Command(d.docker, args...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		logrus.Errorf("Failed to run docker %v: %v", args, err)
		return err
	}

	if err := json.Unmarshal(output, &envList); err != nil {
		return err
	}

	d.env = map[string]string{}

	for _, item := range envList {
		parts := strings.SplitN(item, "=", 2)
		k, v := parts[0], parts[1]
		logrus.Debugf("Reading Env: %s=%s", k, v)
		d.env[k] = v
	}

	logrus.Debugf("Source: %s", d.env.Source())
	logrus.Debugf("Cp: %s", d.env.Cp())
	logrus.Debugf("Socket: %t", d.env.Socket())
	logrus.Debugf("Mode: %s", d.env.Mode(d.Mode))
	logrus.Debugf("Env: %v", d.env.Env())
	logrus.Debugf("Output: %v", d.env.Output())

	return nil
}

func (d *Dapperfile) tag() string {
	cwd, err := os.Getwd()
	if err == nil {
		cwd = filepath.Base(cwd)
	} else {
		cwd = "dapper-unknown"
	}
	// repository name must be lowercase
	cwd = strings.ToLower(cwd)

	output, _ := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD").Output()
	tag := strings.TrimSpace(string(output))
	if tag == "" {
		tag = randString()
	}
	tag = re.ReplaceAllLiteralString(tag, "-")

	return fmt.Sprintf("%s:%s", cwd, tag)
}

func (d *Dapperfile) run(args ...string) error {
	return d.exec(append([]string{"run"}, args...)...)
}

func (d *Dapperfile) exec(args ...string) error {
	logrus.Debugf("Running %s %v", d.docker, args)
	cmd := exec.Command(d.docker, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	err := cmd.Run()
	if err != nil {
		logrus.Debugf("Failed running %s %v: %v", d.docker, args, err)
	}
	return err
}

func (d *Dapperfile) execWithStdin(stdin io.Reader, args ...string) error {
	logrus.Debugf("Running %s %v", d.docker, args)
	cmd := exec.Command(d.docker, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = stdin
	err := cmd.Run()
	if err != nil {
		logrus.Debugf("Failed running %s %v: %v", d.docker, args, err)
	}
	return err
}

func (d *Dapperfile) runExec(args ...string) error {
	logrus.Debugf("Exec %s run %v", d.docker, args)
	return syscall.Exec(d.docker, append([]string{"docker", "run"}, args...), os.Environ())
}

func (d *Dapperfile) execWithOutput(args ...string) ([]byte, error) {
	cmd := exec.Command(d.docker, args...)
	return cmd.CombinedOutput()
}

func (d *Dapperfile) IsBind() bool {
	return d.env.Mode(d.Mode) == "bind"
}

func (d *Dapperfile) dapperFile() ([]byte, error) {
	var input io.Reader

	if d.NoContext {
		input = os.Stdin
	} else {
		f, err := os.Open(d.File)
		if err != nil {
			return nil, err
		}
		input = f
		defer f.Close()
	}

	buffer := &bytes.Buffer{}
	scanner := bufio.NewScanner(input)

	for scanner.Scan() {
		line := scanner.Text()
		if strings.HasPrefix(line, "FROM ") && len(strings.Fields(line)) == 2 && scanner.Scan() {
			nextLine := scanner.Text()
			if strings.HasPrefix(nextLine, "# FROM") {
				baseImage, ok := toMap(nextLine)[d.hostArch]
				if ok && baseImage == "skip" {
					return nil, ErrSkipBuild
				}
				if ok {
					line = "FROM " + baseImage
				}
			}
			line = line + "\n" + nextLine
		}

		buffer.WriteString(line)
		buffer.WriteString("\n")
	}

	return buffer.Bytes(), scanner.Err()
}
07070100000004000081a400000000000000000000000167a5f310000000b6000000000000000000000000000000000000001200000000file/file_unix.go// +build linux freebsd openbsd darwin

package file

import (
	"fmt"
)

func (d *Dapperfile) vSocket() string {
	return fmt.Sprintf("%s:/var/run/docker.sock", d.env.HostSocket())
}
07070100000005000081a400000000000000000000000167a5f31000000090000000000000000000000000000000000000001500000000file/file_windows.gopackage file

import (
	"fmt"
)

func (d *Dapperfile) vSocket() string {
	return fmt.Sprintf("%s://./pipe/docker_engine", d.env.HostSocket())
}
07070100000006000081a400000000000000000000000167a5f3100000039a000000000000000000000000000000000000000d00000000file/util.gopackage file

import (
	"github.com/sirupsen/logrus"
	"io/ioutil"
	"math/rand"
	"strings"
	"time"
)

const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func init() {
	rand.Seed(time.Now().UnixNano())
}

func randString() string {
	b := make([]byte, 7)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

func toMap(str string) map[string]string {
	kv := map[string]string{}

	for _, part := range strings.Fields(str) {
		kvs := strings.SplitN(part, "=", 2)
		if len(kvs) != 2 {
			continue
		}
		kv[kvs[0]] = kvs[1]
	}

	return kv
}

func (d *Dapperfile) tempfile(content []byte) (string, error) {
	tempfile, err := ioutil.TempFile(".", d.File)
	if err != nil {
		return "", err
	}
	defer tempfile.Close()

	logrus.Debugf("Created tempfile %s", tempfile.Name())

	if _, err := tempfile.Write(content); err != nil {
		return "", err
	}

	return tempfile.Name(), nil
}
07070100000007000041ed00000000000000000000000167a5f31000000000000000000000000000000000000000000000000500000000file07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000b00000000TRAILER!!!
openSUSE Build Service is sponsored by