2017-02-14 05:12:02 +08:00
|
|
|
package buildah
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
|
|
"github.com/containers/storage/pkg/ioutils"
|
2017-02-18 02:58:34 +08:00
|
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
2017-02-14 05:12:02 +08:00
|
|
|
"github.com/opencontainers/runtime-tools/generate"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// DefaultWorkingDir is used if none was specified.
|
|
|
|
DefaultWorkingDir = "/"
|
2017-02-18 02:54:49 +08:00
|
|
|
// DefaultRuntime is the default command to use to run the container.
|
|
|
|
DefaultRuntime = "runc"
|
2017-02-14 05:12:02 +08:00
|
|
|
)
|
|
|
|
|
2017-04-05 06:36:55 +08:00
|
|
|
const (
|
2017-04-04 01:43:34 +08:00
|
|
|
// DefaultTerminal indicates that this Run invocation should be
|
|
|
|
// connected to a pseudoterminal if we're connected to a terminal.
|
2017-04-05 06:36:55 +08:00
|
|
|
DefaultTerminal = iota
|
2017-04-04 01:43:34 +08:00
|
|
|
// WithoutTerminal indicates that this Run invocation should NOT be
|
|
|
|
// connected to a pseudoterminal.
|
2017-04-05 06:36:55 +08:00
|
|
|
WithoutTerminal
|
2017-04-04 01:43:34 +08:00
|
|
|
// WithTerminal indicates that this Run invocation should be connected
|
|
|
|
// to a pseudoterminal.
|
2017-04-05 06:36:55 +08:00
|
|
|
WithTerminal
|
|
|
|
)
|
|
|
|
|
2017-02-14 05:12:02 +08:00
|
|
|
// RunOptions can be used to alter how a command is run in the container.
|
|
|
|
type RunOptions struct {
|
|
|
|
// Hostname is the hostname we set for the running container.
|
|
|
|
Hostname string
|
2017-02-18 02:54:49 +08:00
|
|
|
// Runtime is the name of the command to run. It should accept the same arguments that runc does.
|
|
|
|
Runtime string
|
|
|
|
// Args adds global arguments for the runtime.
|
|
|
|
Args []string
|
2017-02-18 02:58:34 +08:00
|
|
|
// Mounts are additional mount points which we want to provide.
|
|
|
|
Mounts []specs.Mount
|
2017-03-28 02:46:35 +08:00
|
|
|
// Env is additional environment variables to set.
|
|
|
|
Env []string
|
|
|
|
// User is the user as whom to run the command.
|
|
|
|
User string
|
|
|
|
// WorkingDir is an override for the working directory.
|
|
|
|
WorkingDir string
|
|
|
|
// Cmd is an override for the configured default command.
|
|
|
|
Cmd []string
|
|
|
|
// Entrypoint is an override for the configured entry point.
|
|
|
|
Entrypoint []string
|
|
|
|
// NetworkDisabled puts the container into its own network namespace.
|
|
|
|
NetworkDisabled bool
|
2017-04-05 06:36:55 +08:00
|
|
|
// Terminal provides a way to specify whether or not the command should
|
|
|
|
// be run with a pseudoterminal. By default (DefaultTerminal), a
|
|
|
|
// terminal is used if os.Stdout is connected to a terminal, but that
|
|
|
|
// decision can be overridden by specifying either WithTerminal or
|
|
|
|
// WithoutTerminal.
|
|
|
|
Terminal int
|
2017-02-14 05:12:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run runs the specified command in the container's root filesystem.
|
|
|
|
func (b *Builder) Run(command []string, options RunOptions) error {
|
2017-03-28 02:46:35 +08:00
|
|
|
var user specs.User
|
2017-02-14 05:12:02 +08:00
|
|
|
path, err := ioutil.TempDir(os.TempDir(), Package)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
logrus.Debugf("using %q to hold bundle data", path)
|
|
|
|
defer func() {
|
|
|
|
if err2 := os.RemoveAll(path); err2 != nil {
|
|
|
|
logrus.Errorf("error removing %q: %v", path, err2)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
g := generate.New()
|
|
|
|
|
Maintain multiple working container configs
Maintain the container configuration in multiple formats in the Buildah
object, initializing one based on the other, depending on which format
the source image used for its configuration.
Replace directly manipulated fields in the Buildah object (Annotations,
CreatedBy, OS, Architecture, Maintainer, User, Workdir, Env, Cmd,
Entrypoint, Expose, Labels, and Volumes) with accessor functions which
update both configurations and which read from whichever one we consider
to be authoritative. Drop Args because we weren't using them.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #102
Approved by: rhatdan
2017-05-16 23:08:52 +08:00
|
|
|
if b.OS() != "" {
|
|
|
|
g.SetPlatformOS(b.OS())
|
2017-02-14 05:12:02 +08:00
|
|
|
}
|
Maintain multiple working container configs
Maintain the container configuration in multiple formats in the Buildah
object, initializing one based on the other, depending on which format
the source image used for its configuration.
Replace directly manipulated fields in the Buildah object (Annotations,
CreatedBy, OS, Architecture, Maintainer, User, Workdir, Env, Cmd,
Entrypoint, Expose, Labels, and Volumes) with accessor functions which
update both configurations and which read from whichever one we consider
to be authoritative. Drop Args because we weren't using them.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #102
Approved by: rhatdan
2017-05-16 23:08:52 +08:00
|
|
|
if b.Architecture() != "" {
|
|
|
|
g.SetPlatformArch(b.Architecture())
|
2017-02-14 05:12:02 +08:00
|
|
|
}
|
Maintain multiple working container configs
Maintain the container configuration in multiple formats in the Buildah
object, initializing one based on the other, depending on which format
the source image used for its configuration.
Replace directly manipulated fields in the Buildah object (Annotations,
CreatedBy, OS, Architecture, Maintainer, User, Workdir, Env, Cmd,
Entrypoint, Expose, Labels, and Volumes) with accessor functions which
update both configurations and which read from whichever one we consider
to be authoritative. Drop Args because we weren't using them.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #102
Approved by: rhatdan
2017-05-16 23:08:52 +08:00
|
|
|
for _, envSpec := range append(b.Env(), options.Env...) {
|
2017-02-14 05:12:02 +08:00
|
|
|
env := strings.SplitN(envSpec, "=", 2)
|
|
|
|
if len(env) > 1 {
|
|
|
|
g.AddProcessEnv(env[0], env[1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(command) > 0 {
|
|
|
|
g.SetProcessArgs(command)
|
2017-03-28 02:46:35 +08:00
|
|
|
} else if len(options.Cmd) != 0 {
|
|
|
|
g.SetProcessArgs(options.Cmd)
|
Maintain multiple working container configs
Maintain the container configuration in multiple formats in the Buildah
object, initializing one based on the other, depending on which format
the source image used for its configuration.
Replace directly manipulated fields in the Buildah object (Annotations,
CreatedBy, OS, Architecture, Maintainer, User, Workdir, Env, Cmd,
Entrypoint, Expose, Labels, and Volumes) with accessor functions which
update both configurations and which read from whichever one we consider
to be authoritative. Drop Args because we weren't using them.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #102
Approved by: rhatdan
2017-05-16 23:08:52 +08:00
|
|
|
} else if len(b.Cmd()) != 0 {
|
|
|
|
g.SetProcessArgs(b.Cmd())
|
2017-03-28 02:46:35 +08:00
|
|
|
} else if len(options.Entrypoint) != 0 {
|
|
|
|
g.SetProcessArgs(options.Entrypoint)
|
Maintain multiple working container configs
Maintain the container configuration in multiple formats in the Buildah
object, initializing one based on the other, depending on which format
the source image used for its configuration.
Replace directly manipulated fields in the Buildah object (Annotations,
CreatedBy, OS, Architecture, Maintainer, User, Workdir, Env, Cmd,
Entrypoint, Expose, Labels, and Volumes) with accessor functions which
update both configurations and which read from whichever one we consider
to be authoritative. Drop Args because we weren't using them.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #102
Approved by: rhatdan
2017-05-16 23:08:52 +08:00
|
|
|
} else if len(b.Entrypoint()) != 0 {
|
|
|
|
g.SetProcessArgs(b.Entrypoint())
|
2017-02-14 05:12:02 +08:00
|
|
|
}
|
2017-03-28 02:46:35 +08:00
|
|
|
if options.WorkingDir != "" {
|
|
|
|
g.SetProcessCwd(options.WorkingDir)
|
Maintain multiple working container configs
Maintain the container configuration in multiple formats in the Buildah
object, initializing one based on the other, depending on which format
the source image used for its configuration.
Replace directly manipulated fields in the Buildah object (Annotations,
CreatedBy, OS, Architecture, Maintainer, User, Workdir, Env, Cmd,
Entrypoint, Expose, Labels, and Volumes) with accessor functions which
update both configurations and which read from whichever one we consider
to be authoritative. Drop Args because we weren't using them.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #102
Approved by: rhatdan
2017-05-16 23:08:52 +08:00
|
|
|
} else if b.WorkDir() != "" {
|
|
|
|
g.SetProcessCwd(b.WorkDir())
|
2017-02-14 05:12:02 +08:00
|
|
|
}
|
|
|
|
if options.Hostname != "" {
|
|
|
|
g.SetHostname(options.Hostname)
|
2017-05-24 06:03:19 +08:00
|
|
|
} else if b.Hostname() != "" {
|
|
|
|
g.SetHostname(b.Hostname())
|
2017-02-14 05:12:02 +08:00
|
|
|
}
|
Maintain multiple working container configs
Maintain the container configuration in multiple formats in the Buildah
object, initializing one based on the other, depending on which format
the source image used for its configuration.
Replace directly manipulated fields in the Buildah object (Annotations,
CreatedBy, OS, Architecture, Maintainer, User, Workdir, Env, Cmd,
Entrypoint, Expose, Labels, and Volumes) with accessor functions which
update both configurations and which read from whichever one we consider
to be authoritative. Drop Args because we weren't using them.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #102
Approved by: rhatdan
2017-05-16 23:08:52 +08:00
|
|
|
for _, volume := range b.Volumes() {
|
2017-02-18 02:58:34 +08:00
|
|
|
g.AddTmpfsMount(volume, nil)
|
|
|
|
}
|
2017-02-14 05:12:02 +08:00
|
|
|
mountPoint, err := b.Mount("")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
if err2 := b.Unmount(); err2 != nil {
|
|
|
|
logrus.Errorf("error unmounting container: %v", err2)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
g.SetRootPath(mountPoint)
|
2017-04-05 06:36:55 +08:00
|
|
|
switch options.Terminal {
|
|
|
|
case DefaultTerminal:
|
|
|
|
g.SetProcessTerminal(logrus.IsTerminal(os.Stdout))
|
|
|
|
case WithTerminal:
|
|
|
|
g.SetProcessTerminal(true)
|
|
|
|
case WithoutTerminal:
|
|
|
|
g.SetProcessTerminal(false)
|
|
|
|
}
|
2017-03-28 02:46:35 +08:00
|
|
|
if !options.NetworkDisabled {
|
2017-04-04 05:44:23 +08:00
|
|
|
if err = g.RemoveLinuxNamespace("network"); err != nil {
|
|
|
|
return fmt.Errorf("error removing network namespace for run: %v)", err)
|
|
|
|
}
|
2017-03-28 02:46:35 +08:00
|
|
|
}
|
2017-04-05 05:31:02 +08:00
|
|
|
if options.User != "" {
|
|
|
|
user, err = getUser(mountPoint, options.User)
|
|
|
|
} else {
|
Maintain multiple working container configs
Maintain the container configuration in multiple formats in the Buildah
object, initializing one based on the other, depending on which format
the source image used for its configuration.
Replace directly manipulated fields in the Buildah object (Annotations,
CreatedBy, OS, Architecture, Maintainer, User, Workdir, Env, Cmd,
Entrypoint, Expose, Labels, and Volumes) with accessor functions which
update both configurations and which read from whichever one we consider
to be authoritative. Drop Args because we weren't using them.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #102
Approved by: rhatdan
2017-05-16 23:08:52 +08:00
|
|
|
user, err = getUser(mountPoint, b.User())
|
2017-04-05 05:31:02 +08:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
g.SetProcessUID(user.UID)
|
|
|
|
g.SetProcessGID(user.GID)
|
2017-02-14 05:12:02 +08:00
|
|
|
spec := g.Spec()
|
|
|
|
if spec.Process.Cwd == "" {
|
|
|
|
spec.Process.Cwd = DefaultWorkingDir
|
|
|
|
}
|
Maintain multiple working container configs
Maintain the container configuration in multiple formats in the Buildah
object, initializing one based on the other, depending on which format
the source image used for its configuration.
Replace directly manipulated fields in the Buildah object (Annotations,
CreatedBy, OS, Architecture, Maintainer, User, Workdir, Env, Cmd,
Entrypoint, Expose, Labels, and Volumes) with accessor functions which
update both configurations and which read from whichever one we consider
to be authoritative. Drop Args because we weren't using them.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
Closes: #102
Approved by: rhatdan
2017-05-16 23:08:52 +08:00
|
|
|
if err = os.MkdirAll(filepath.Join(mountPoint, b.WorkDir()), 0755); err != nil {
|
|
|
|
return fmt.Errorf("error ensuring working directory %q exists: %v)", b.WorkDir(), err)
|
2017-03-24 01:47:07 +08:00
|
|
|
}
|
2017-02-18 02:58:34 +08:00
|
|
|
mounts := options.Mounts
|
2017-03-24 03:48:26 +08:00
|
|
|
boundMounts := []specs.Mount{}
|
|
|
|
for _, boundFile := range []string{"/etc/hosts", "/etc/resolv.conf"} {
|
|
|
|
for _, mount := range mounts {
|
|
|
|
if mount.Destination == boundFile {
|
|
|
|
// Already have an override for it, so skip this one.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
boundMount := specs.Mount{
|
|
|
|
Source: boundFile,
|
|
|
|
Destination: boundFile,
|
|
|
|
Type: "bind",
|
|
|
|
Options: []string{"rbind", "ro"},
|
|
|
|
}
|
|
|
|
boundMounts = append(boundMounts, boundMount)
|
|
|
|
}
|
2017-02-18 02:58:34 +08:00
|
|
|
for _, specMount := range spec.Mounts {
|
|
|
|
override := false
|
|
|
|
for _, mount := range mounts {
|
|
|
|
if specMount.Destination == mount.Destination {
|
|
|
|
// Already have an override for it, so skip this one.
|
|
|
|
override = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !override {
|
|
|
|
mounts = append(mounts, specMount)
|
|
|
|
}
|
|
|
|
}
|
2017-03-24 03:48:26 +08:00
|
|
|
spec.Mounts = append(mounts, boundMounts...)
|
2017-02-14 05:12:02 +08:00
|
|
|
specbytes, err := json.Marshal(spec)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = ioutils.AtomicWriteFile(filepath.Join(path, "config.json"), specbytes, 0600)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error storing runtime configuration: %v", err)
|
|
|
|
}
|
|
|
|
logrus.Debugf("config = %v", string(specbytes))
|
2017-02-18 02:54:49 +08:00
|
|
|
runtime := options.Runtime
|
|
|
|
if runtime == "" {
|
|
|
|
runtime = DefaultRuntime
|
|
|
|
}
|
|
|
|
args := append(options.Args, "run", "-b", path, Package+"-"+b.ContainerID)
|
|
|
|
cmd := exec.Command(runtime, args...)
|
2017-02-14 05:12:02 +08:00
|
|
|
cmd.Dir = mountPoint
|
|
|
|
cmd.Stdin = os.Stdin
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
err = cmd.Run()
|
|
|
|
if err != nil {
|
|
|
|
logrus.Debugf("error running runc %v: %v", spec.Process.Args, err)
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|