mirror of
https://github.com/linka-cloud/d2vm.git
synced 2024-11-22 15:56:24 +00:00
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>
This commit is contained in:
parent
43f2dd5452
commit
d18e68b138
@ -40,7 +40,7 @@ var (
|
|||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
// TODO(adphi): resolve context path
|
// TODO(adphi): resolve context path
|
||||||
if runtime.GOOS != "linux" {
|
if runtime.GOOS != "linux" || !isRoot() {
|
||||||
ctxAbsPath, err := filepath.Abs(args[0])
|
ctxAbsPath, err := filepath.Abs(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -96,7 +96,7 @@ var (
|
|||||||
if err := docker.Build(cmd.Context(), tag, file, args[0], buildArgs...); err != nil {
|
if err := docker.Build(cmd.Context(), tag, file, args[0], buildArgs...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return d2vm.Convert(
|
if err := d2vm.Convert(
|
||||||
cmd.Context(),
|
cmd.Context(),
|
||||||
tag,
|
tag,
|
||||||
d2vm.WithSize(size),
|
d2vm.WithSize(size),
|
||||||
@ -105,7 +105,14 @@ var (
|
|||||||
d2vm.WithCmdLineExtra(cmdLineExtra),
|
d2vm.WithCmdLineExtra(cmdLineExtra),
|
||||||
d2vm.WithNetworkManager(d2vm.NetworkManager(networkManager)),
|
d2vm.WithNetworkManager(d2vm.NetworkManager(networkManager)),
|
||||||
d2vm.WithRaw(raw),
|
d2vm.WithRaw(raw),
|
||||||
)
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
uid, ok := sudoUser()
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return os.Chown(output, uid, uid)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -40,7 +40,7 @@ var (
|
|||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if runtime.GOOS != "linux" {
|
if runtime.GOOS != "linux" || !isRoot() {
|
||||||
abs, err := filepath.Abs(output)
|
abs, err := filepath.Abs(output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -86,7 +86,7 @@ var (
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return d2vm.Convert(
|
if err := d2vm.Convert(
|
||||||
cmd.Context(),
|
cmd.Context(),
|
||||||
img,
|
img,
|
||||||
d2vm.WithSize(size),
|
d2vm.WithSize(size),
|
||||||
@ -95,7 +95,15 @@ var (
|
|||||||
d2vm.WithCmdLineExtra(cmdLineExtra),
|
d2vm.WithCmdLineExtra(cmdLineExtra),
|
||||||
d2vm.WithNetworkManager(d2vm.NetworkManager(networkManager)),
|
d2vm.WithNetworkManager(d2vm.NetworkManager(networkManager)),
|
||||||
d2vm.WithRaw(raw),
|
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)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -20,6 +20,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -133,3 +135,25 @@ func (f *logfmtFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
return b.Bytes(), nil
|
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
|
||||||
|
}
|
||||||
|
@ -33,8 +33,6 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/svenwiltink/sparsecat"
|
"github.com/svenwiltink/sparsecat"
|
||||||
|
|
||||||
exec2 "go.linka.cloud/d2vm/pkg/exec"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -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 {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -91,11 +89,11 @@ func runHetzner(ctx context.Context, imgPath string, stdin io.Reader, stderr io.
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(rawPath)
|
defer os.RemoveAll(rawPath)
|
||||||
logrus.Infof("converting image to raw: %s", 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
|
return err
|
||||||
}
|
}
|
||||||
imgPath = rawPath
|
imgPath = rawPath
|
||||||
i, err = ImgInfo(ctx, imgPath)
|
i, err = QemuImgInfo(ctx, imgPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -25,12 +25,17 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
"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
|
//go:embed sparsecat-linux-amd64
|
||||||
@ -349,8 +354,36 @@ type QemuInfo struct {
|
|||||||
DirtyFlag bool `json:"dirty-flag"`
|
DirtyFlag bool `json:"dirty-flag"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ImgInfo(ctx context.Context, path string) (*QemuInfo, error) {
|
func QemuImgInfo(ctx context.Context, in string) (*QemuInfo, error) {
|
||||||
o, err := exec.CommandContext(ctx, "qemu-img", "info", path, "--output", "json").CombinedOutput()
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%v: %s", err, string(o))
|
return nil, fmt.Errorf("%v: %s", err, string(o))
|
||||||
}
|
}
|
||||||
@ -360,3 +393,38 @@ func ImgInfo(ctx context.Context, path string) (*QemuInfo, error) {
|
|||||||
}
|
}
|
||||||
return &i, nil
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -18,8 +18,6 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"go.linka.cloud/console"
|
"go.linka.cloud/console"
|
||||||
|
|
||||||
exec2 "go.linka.cloud/d2vm/pkg/exec"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -74,7 +72,7 @@ func vbox(ctx context.Context, path string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Cannot find management binary %s: %v", vboxmanageFlag, err)
|
return fmt.Errorf("Cannot find management binary %s: %v", vboxmanageFlag, err)
|
||||||
}
|
}
|
||||||
i, err := ImgInfo(ctx, path)
|
i, err := QemuImgInfo(ctx, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get image info: %v", err)
|
return fmt.Errorf("failed to get image info: %v", err)
|
||||||
}
|
}
|
||||||
@ -86,7 +84,7 @@ func vbox(ctx context.Context, path string) error {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(vdi)
|
defer os.RemoveAll(vdi)
|
||||||
logrus.Infof("converting image to raw: %s", 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
|
return err
|
||||||
}
|
}
|
||||||
path = vdi
|
path = vdi
|
||||||
|
@ -97,7 +97,6 @@ func Pull(ctx context.Context, tag string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RunInteractiveAndRemove(ctx context.Context, args ...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 := exec.CommandContext(ctx, "docker", append([]string{"run", "--rm", "-it"}, args...)...)
|
||||||
cmd.Stdin = os.Stdin
|
cmd.Stdin = os.Stdin
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
@ -129,6 +128,9 @@ func RunD2VM(ctx context.Context, image, version, in, out, cmd string, args ...s
|
|||||||
}
|
}
|
||||||
a := []string{
|
a := []string{
|
||||||
"--privileged",
|
"--privileged",
|
||||||
|
"-e",
|
||||||
|
// yes... it is kind of a dirty hack
|
||||||
|
fmt.Sprintf("SUDO_UID=%d", os.Getuid()),
|
||||||
"-v",
|
"-v",
|
||||||
fmt.Sprintf("%s:/var/run/docker.sock", dockerSocket()),
|
fmt.Sprintf("%s:/var/run/docker.sock", dockerSocket()),
|
||||||
"-v",
|
"-v",
|
||||||
|
@ -26,8 +26,6 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
Run = RunNoOut
|
Run = RunNoOut
|
||||||
|
|
||||||
CommandContext = exec.CommandContext
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func SetDebug(debug bool) {
|
func SetDebug(debug bool) {
|
||||||
@ -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 {
|
func RunDebug(ctx context.Context, c string, args ...string) error {
|
||||||
logrus.Debugf("$ %s %s", c, strings.Join(args, " "))
|
logrus.Debugf("$ %s %s", c, strings.Join(args, " "))
|
||||||
cmd := exec.CommandContext(ctx, c, args...)
|
cmd := exec.CommandContext(ctx, c, args...)
|
||||||
|
@ -17,5 +17,5 @@ package d2vm
|
|||||||
var (
|
var (
|
||||||
Version = ""
|
Version = ""
|
||||||
BuildDate = ""
|
BuildDate = ""
|
||||||
Image = ""
|
Image = "linkacloud/d2vm"
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user