Skip to content

Commit

Permalink
run in docker if not root or sudo (fix #5 #9 #11)
Browse files Browse the repository at this point in the history
set user permissions on image if run with sudo or in docker
run/vbox & run/hetzner: run qemu-img in docker if not available in path

Signed-off-by: Adphi <philippe.adrien.nousse@gmail.com>
  • Loading branch information
Adphi committed Sep 13, 2022
1 parent 43f2dd5 commit d18e68b
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 21 deletions.
13 changes: 10 additions & 3 deletions cmd/d2vm/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ var (
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
// TODO(adphi): resolve context path
if runtime.GOOS != "linux" {
if runtime.GOOS != "linux" || !isRoot() {
ctxAbsPath, err := filepath.Abs(args[0])
if err != nil {
return err
Expand Down Expand Up @@ -96,7 +96,7 @@ var (
if err := docker.Build(cmd.Context(), tag, file, args[0], buildArgs...); err != nil {
return err
}
return d2vm.Convert(
if err := d2vm.Convert(
cmd.Context(),
tag,
d2vm.WithSize(size),
Expand All @@ -105,7 +105,14 @@ var (
d2vm.WithCmdLineExtra(cmdLineExtra),
d2vm.WithNetworkManager(d2vm.NetworkManager(networkManager)),
d2vm.WithRaw(raw),
)
); err != nil {
return err
}
uid, ok := sudoUser()
if !ok {
return nil
}
return os.Chown(output, uid, uid)
},
}
)
Expand Down
14 changes: 11 additions & 3 deletions cmd/d2vm/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ var (
Args: cobra.ExactArgs(1),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
if runtime.GOOS != "linux" {
if runtime.GOOS != "linux" || !isRoot() {
abs, err := filepath.Abs(output)
if err != nil {
return err
Expand Down Expand Up @@ -86,7 +86,7 @@ var (
return err
}
}
return d2vm.Convert(
if err := d2vm.Convert(
cmd.Context(),
img,
d2vm.WithSize(size),
Expand All @@ -95,7 +95,15 @@ var (
d2vm.WithCmdLineExtra(cmdLineExtra),
d2vm.WithNetworkManager(d2vm.NetworkManager(networkManager)),
d2vm.WithRaw(raw),
)
); err != nil {
return err
}
// set user permissions on the output file if the command was run with sudo
uid, ok := sudoUser()
if !ok {
return nil
}
return os.Chown(output, uid, uid)
},
}
)
Expand Down
24 changes: 24 additions & 0 deletions cmd/d2vm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"fmt"
"os"
"os/signal"
"runtime"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -133,3 +135,25 @@ func (f *logfmtFormatter) Format(entry *logrus.Entry) ([]byte, error) {
}
return b.Bytes(), nil
}

func isRoot() bool {
return os.Geteuid() == 0
}

func sudoUser() (uid int, sudo bool) {
// if we are not running on linux, docker handle files user's permissions,
// so we don't need to check for sudo here
if runtime.GOOS != "linux" {
return
}
v := os.Getenv("SUDO_UID")
if v == "" {
return 0, false
}
uid, err := strconv.Atoi(v)
if err != nil {
logrus.Errorf("invalid SUDO_UID: %s", v)
return 0, false
}
return uid, true
}
8 changes: 3 additions & 5 deletions cmd/d2vm/run/hetzner.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ import (
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/svenwiltink/sparsecat"

exec2 "go.linka.cloud/d2vm/pkg/exec"
)

const (
Expand Down Expand Up @@ -79,7 +77,7 @@ func Hetzner(cmd *cobra.Command, args []string) {
}

func runHetzner(ctx context.Context, imgPath string, stdin io.Reader, stderr io.Writer, stdout io.Writer) error {
i, err := ImgInfo(ctx, imgPath)
i, err := QemuImgInfo(ctx, imgPath)
if err != nil {
return err
}
Expand All @@ -91,11 +89,11 @@ func runHetzner(ctx context.Context, imgPath string, stdin io.Reader, stderr io.
}
defer os.RemoveAll(rawPath)
logrus.Infof("converting image to raw: %s", rawPath)
if err := exec2.Run(ctx, "qemu-img", "convert", "-O", "raw", imgPath, rawPath); err != nil {
if err := QemuImgConvert(ctx, "raw", imgPath, rawPath); err != nil {
return err
}
imgPath = rawPath
i, err = ImgInfo(ctx, imgPath)
i, err = QemuImgInfo(ctx, imgPath)
if err != nil {
return err
}
Expand Down
72 changes: 70 additions & 2 deletions cmd/d2vm/run/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,17 @@ import (
"io"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"sync"
"time"

"golang.org/x/crypto/ssh"

"go.linka.cloud/d2vm"
"go.linka.cloud/d2vm/pkg/docker"
exec2 "go.linka.cloud/d2vm/pkg/exec"
)

//go:embed sparsecat-linux-amd64
Expand Down Expand Up @@ -349,8 +354,36 @@ type QemuInfo struct {
DirtyFlag bool `json:"dirty-flag"`
}

func ImgInfo(ctx context.Context, path string) (*QemuInfo, error) {
o, err := exec.CommandContext(ctx, "qemu-img", "info", path, "--output", "json").CombinedOutput()
func QemuImgInfo(ctx context.Context, in string) (*QemuInfo, error) {
var (
o []byte
err error
)
if path, _ := exec.LookPath("qemu-img"); path == "" {
inAbs, err := filepath.Abs(in)
if err != nil {
return nil, fmt.Errorf("failed to get absolute path for %q: %v", path, err)
}
inMount := filepath.Dir(inAbs)
in := filepath.Join("/in", filepath.Base(inAbs))
o, err = exec2.CommandContext(
ctx,
"docker",
"run",
"--rm",
"-v",
inMount+":/in",
"--entrypoint",
"qemu-img",
fmt.Sprintf("%s:%s", d2vm.Image, d2vm.Version),
"info",
in,
"--output",
"json",
).CombinedOutput()
} else {
o, err = exec2.CommandContext(ctx, "qemu-img", "info", path, "--output", "json").CombinedOutput()
}
if err != nil {
return nil, fmt.Errorf("%v: %s", err, string(o))
}
Expand All @@ -360,3 +393,38 @@ func ImgInfo(ctx context.Context, path string) (*QemuInfo, error) {
}
return &i, nil
}

func QemuImgConvert(ctx context.Context, format, in, out string) error {
if path, _ := exec.LookPath("qemu-img"); path != "" {
return exec2.Run(ctx, "qemu-img", "convert", "-O", format, in, out)
}
inAbs, err := filepath.Abs(in)
if err != nil {
return fmt.Errorf("failed to get absolute path for %q: %v", in, err)
}
inMount := filepath.Dir(inAbs)
in = filepath.Join("/in", filepath.Base(inAbs))

outAbs, err := filepath.Abs(out)
if err != nil {
return fmt.Errorf("failed to get absolute path for %q: %v", out, err)
}
outMount := filepath.Dir(outAbs)
out = filepath.Join("/out", filepath.Base(outAbs))

return docker.RunAndRemove(
ctx,
"-v",
fmt.Sprintf("%s:/in", inMount),
"-v",
fmt.Sprintf("%s:/out", outMount),
"--entrypoint",
"qemu-img",
fmt.Sprintf("%s:%s", d2vm.Image, d2vm.Version),
"convert",
"-O",
format,
in,
out,
)
}
6 changes: 2 additions & 4 deletions cmd/d2vm/run/vbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"go.linka.cloud/console"

exec2 "go.linka.cloud/d2vm/pkg/exec"
)

var (
Expand Down Expand Up @@ -74,7 +72,7 @@ func vbox(ctx context.Context, path string) error {
if err != nil {
return fmt.Errorf("Cannot find management binary %s: %v", vboxmanageFlag, err)
}
i, err := ImgInfo(ctx, path)
i, err := QemuImgInfo(ctx, path)
if err != nil {
return fmt.Errorf("failed to get image info: %v", err)
}
Expand All @@ -86,7 +84,7 @@ func vbox(ctx context.Context, path string) error {
}
defer os.RemoveAll(vdi)
logrus.Infof("converting image to raw: %s", vdi)
if err := exec2.Run(ctx, "qemu-img", "convert", "-O", "vdi", path, vdi); err != nil {
if err := QemuImgConvert(ctx, "vdi", path, vdi); err != nil {
return err
}
path = vdi
Expand Down
4 changes: 3 additions & 1 deletion pkg/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ func Pull(ctx context.Context, tag string) error {
}

func RunInteractiveAndRemove(ctx context.Context, args ...string) error {
logrus.Tracef("running 'docker run --rm -i -t %s'", strings.Join(args, " "))
cmd := exec.CommandContext(ctx, "docker", append([]string{"run", "--rm", "-it"}, args...)...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
Expand Down Expand Up @@ -129,6 +128,9 @@ func RunD2VM(ctx context.Context, image, version, in, out, cmd string, args ...s
}
a := []string{
"--privileged",
"-e",
// yes... it is kind of a dirty hack
fmt.Sprintf("SUDO_UID=%d", os.Getuid()),
"-v",
fmt.Sprintf("%s:/var/run/docker.sock", dockerSocket()),
"-v",
Expand Down
7 changes: 5 additions & 2 deletions pkg/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ import (

var (
Run = RunNoOut

CommandContext = exec.CommandContext
)

func SetDebug(debug bool) {
Expand All @@ -39,6 +37,11 @@ func SetDebug(debug bool) {
}
}

func CommandContext(ctx context.Context, c string, args ...string) *exec.Cmd {
logrus.Debugf("$ %s %s", c, strings.Join(args, " "))
return exec.CommandContext(ctx, c, args...)
}

func RunDebug(ctx context.Context, c string, args ...string) error {
logrus.Debugf("$ %s %s", c, strings.Join(args, " "))
cmd := exec.CommandContext(ctx, c, args...)
Expand Down
2 changes: 1 addition & 1 deletion version.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ package d2vm
var (
Version = ""
BuildDate = ""
Image = ""
Image = "linkacloud/d2vm"
)

0 comments on commit d18e68b

Please sign in to comment.