Skip to content

Commit

Permalink
engine: added a chroot engine
Browse files Browse the repository at this point in the history
Added a chroot run engine to acbuild. This will allow people who have
problems with systemd-nspawn and people who don't have systemd to use
acbuild run, by specifying `--engine=chroot` on any run commands.
  • Loading branch information
Derek Gonyeo committed Aug 8, 2016
1 parent 83d6ca7 commit 8542b99
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 6 deletions.
11 changes: 9 additions & 2 deletions Documentation/subcommands/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,15 @@ command. The flag `--engine` can be used to select a non-default engine.
The default engine in acbuild is called `systemd-nspawn`, which rather
obviously uses `systemd-nspawn` to run the given command. This means that the
machine running acbuild must have systemd installed to be able to use `acbuild
run` with the default engine. Alternate execution tools (like `runc`) will be
added in the future.
run` with the default engine.

### chroot

An alternative engine is called `chroot`, which uses the chroot syscall to
enter into the container and run the specified command. There's no namespacing
involved, so the command will be able to see and possibly interact with other
processes on the host. This engine notably has no dependency on systemd, unlike
the `systemd-nspawn` engine.

### Exiting out of systemd-nspawn

Expand Down
2 changes: 2 additions & 0 deletions acbuild/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"strings"

"github.com/appc/acbuild/engine"
"github.com/appc/acbuild/engine/chroot"
"github.com/appc/acbuild/engine/systemdnspawn"

"github.com/spf13/cobra"
Expand All @@ -38,6 +39,7 @@ var (

engines = map[string]engine.Engine{
"systemd-nspawn": systemdnspawn.Engine{},
"chroot": chroot.Engine{},
}
)

Expand Down
1 change: 1 addition & 0 deletions build
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ fi
echo "Building acbuild..."
go build -o $GOBIN/acbuild -ldflags "${GLDFLAGS}" ${ACBUILD_BUILD_TAGS} ${REPO_PATH}/acbuild
ln -f $GOBIN/acbuild $GOBIN/acbuild-script
ln -f $GOBIN/acbuild $GOBIN/acbuild-chroot
89 changes: 89 additions & 0 deletions engine/chroot/chroot-child.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2016 The appc Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package chroot

import (
"fmt"
"os"
"os/exec"
"runtime"
"strings"
"syscall"

"github.com/spf13/cobra"
)

func init() {
cmdACBuildChroot.PersistentFlags().StringVar(&flagCmd, "cmd", "", "The command to run")
cmdACBuildChroot.PersistentFlags().StringSliceVar(&flagArgs, "args", nil, "arguments for the command")
cmdACBuildChroot.PersistentFlags().StringSliceVar(&flagEnv, "env", nil, "environment for the command")
cmdACBuildChroot.PersistentFlags().StringVar(&flagChroot, "chroot", "", "dir to chroot into")
cmdACBuildChroot.PersistentFlags().StringVar(&flagWorkingDir, "working-dir", "", "working directory for the command")
}

var (
flagCmd string
flagArgs []string
flagEnv []string
flagChroot string
flagWorkingDir string
cmdACBuildChroot = &cobra.Command{
Use: "",
Run: runChroot,
}
)

func stderr(format string, a ...interface{}) {
out := fmt.Sprintf(format, a...)
fmt.Fprintln(os.Stderr, strings.TrimSuffix(out, "\n"))
}

func errAndExit(format string, a ...interface{}) {
stderr(format, a...)
os.Exit(1)
}

func runChroot(cmd *cobra.Command, args []string) {
runtime.LockOSThread()
err := syscall.Chroot(flagChroot)
if err != nil {
errAndExit("couldn't chroot: %v", err)
}
err = os.Chdir("/")
if err != nil {
errAndExit("couldn't cd: %v", err)
}

if flagWorkingDir != "" {
err = os.Chdir(flagWorkingDir)
if err != nil {
errAndExit("couldn't cd: %v", err)
}
}

execCmd := exec.Command(flagCmd, flagArgs...)
execCmd.Env = flagEnv
execCmd.Stdin = os.Stdin
execCmd.Stdout = os.Stdout
execCmd.Stderr = os.Stderr
err = execCmd.Run()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
code := exitErr.Sys().(syscall.WaitStatus).ExitStatus()
os.Exit(code)
}
errAndExit("%v", err)
}
}
90 changes: 90 additions & 0 deletions engine/chroot/chroot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright 2016 The appc Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package chroot

import (
"os"
"os/exec"
"path/filepath"

"github.com/appc/acbuild/engine"
"github.com/appc/spec/schema/types"
"github.com/coreos/rkt/pkg/fileutil"
"github.com/coreos/rkt/pkg/multicall"
"github.com/coreos/rkt/pkg/user"
)

type Engine struct{}

func init() {
multicall.Add("acbuild-chroot", cmdACBuildChroot.Execute)
}

func (e Engine) Run(command string, args []string, environment types.Environment, chroot, workingDir string) error {
resolvConfFile := filepath.Join(chroot, "/etc/resolv.conf")
_, err := os.Stat(resolvConfFile)
switch {
case os.IsNotExist(err):
err := os.MkdirAll(filepath.Dir(resolvConfFile), 0755)
if err != nil {
return err
}
err = fileutil.CopyTree("/etc/resolv.conf", resolvConfFile, user.NewBlankUidRange())
if err != nil {
return err
}
defer os.RemoveAll(resolvConfFile)
case err != nil:
return err
}
var serializedArgs string
for _, arg := range args {
if serializedArgs != "" {
serializedArgs += ","
}
serializedArgs += arg
}
var serializedEnv string
for _, envvar := range environment {
if serializedEnv != "" {
serializedEnv += ","
}
serializedEnv += envvar.Name + "=" + envvar.Value
}
path := "PATH="
for _, p := range engine.Pathlist {
if path != "PATH=" {
path += ":"
}
path += p
}
chrootArgs := []string{
"--cmd", command,
"--chroot", chroot,
"--working-dir", workingDir,
}
if len(serializedArgs) > 0 {
chrootArgs = append(chrootArgs, "--args", serializedArgs)
}
if len(serializedEnv) > 0 {
chrootArgs = append(chrootArgs, "--env", serializedEnv)
}
cmd := exec.Command("acbuild-chroot", chrootArgs...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = []string{path}
return cmd.Run()
}
3 changes: 3 additions & 0 deletions engine/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import (
"github.com/appc/spec/schema/types"
)

var Pathlist = []string{"/usr/local/sbin", "/usr/local/bin", "/usr/sbin",
"/usr/bin", "/sbin", "/bin"}

// Engine is an interface which is accepted by lib.Run, and used to perform the
// actual execution of a binary inside the container.
type Engine interface {
Expand Down
6 changes: 2 additions & 4 deletions engine/systemdnspawn/systemdnspawn.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@ import (
"strings"
"syscall"

"github.com/appc/acbuild/engine"
"github.com/appc/spec/schema/types"
)

var pathlist = []string{"/usr/local/sbin", "/usr/local/bin", "/usr/sbin",
"/usr/bin", "/sbin", "/bin"}

type Engine struct{}

func (e Engine) Run(command string, args []string, environment types.Environment, chroot, workingDir string) error {
Expand Down Expand Up @@ -73,7 +71,7 @@ func (e Engine) Run(command string, args []string, environment types.Environment

nspawncmd = append(nspawncmd, "--setenv", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")

abscmd, err := findCmdInPath(pathlist, command, chroot)
abscmd, err := findCmdInPath(engine.Pathlist, command, chroot)
if err != nil {
return err
}
Expand Down

0 comments on commit 8542b99

Please sign in to comment.