mirror of
https://github.com/linka-cloud/d2vm.git
synced 2024-11-22 07:46:25 +00:00
run: fix qemu-img convert path typo
build & convert: add kubevirt container disk support Signed-off-by: Adphi <philippe.adrien.nousse@gmail.com>
This commit is contained in:
parent
618b5bc861
commit
d652bf41f5
@ -30,11 +30,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
file = "Dockerfile"
|
||||
tag = "d2vm-" + uuid.New().String()
|
||||
networkManager string
|
||||
buildArgs []string
|
||||
buildCmd = &cobra.Command{
|
||||
file = "Dockerfile"
|
||||
tag = "d2vm-" + uuid.New().String()
|
||||
buildArgs []string
|
||||
buildCmd = &cobra.Command{
|
||||
Use: "build [context directory]",
|
||||
Short: "Build a vm image from Dockerfile",
|
||||
Args: cobra.ExactArgs(1),
|
||||
@ -87,6 +86,9 @@ var (
|
||||
if file == "" {
|
||||
file = filepath.Join(args[0], "Dockerfile")
|
||||
}
|
||||
if push && tag == "" {
|
||||
return fmt.Errorf("tag is required when pushing container disk image")
|
||||
}
|
||||
if _, err := os.Stat(output); err == nil || !os.IsNotExist(err) {
|
||||
if !force {
|
||||
return fmt.Errorf("%s already exists", output)
|
||||
@ -108,11 +110,12 @@ var (
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
uid, ok := sudoUser()
|
||||
if !ok {
|
||||
return nil
|
||||
if uid, ok := sudoUser(); ok {
|
||||
if err := os.Chown(output, uid, uid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return os.Chown(output, uid, uid)
|
||||
return maybeMakeContainerDisk(cmd.Context())
|
||||
},
|
||||
}
|
||||
)
|
||||
@ -123,11 +126,5 @@ func init() {
|
||||
buildCmd.Flags().StringVarP(&file, "file", "f", "", "Name of the Dockerfile")
|
||||
buildCmd.Flags().StringArrayVar(&buildArgs, "build-arg", nil, "Set build-time variables")
|
||||
|
||||
buildCmd.Flags().StringVarP(&output, "output", "o", output, "The output image, the extension determine the image format, raw will be used if none. Supported formats: "+strings.Join(d2vm.OutputFormats(), " "))
|
||||
buildCmd.Flags().StringVarP(&password, "password", "p", "", "Optional root user password")
|
||||
buildCmd.Flags().StringVarP(&size, "size", "s", "10G", "The output image size")
|
||||
buildCmd.Flags().BoolVar(&force, "force", false, "Override output image")
|
||||
buildCmd.Flags().StringVar(&cmdLineExtra, "append-to-cmdline", "", "Extra kernel cmdline arguments to append to the generated one")
|
||||
buildCmd.Flags().StringVar(&networkManager, "network-manager", "", "Network manager to use for the image: none, netplan, ifupdown")
|
||||
buildCmd.Flags().BoolVar(&raw, "raw", false, "Just convert the container to virtual machine image without installing anything more")
|
||||
buildCmd.Flags().AddFlagSet(buildFlags())
|
||||
}
|
||||
|
43
cmd/d2vm/container_disk.go
Normal file
43
cmd/d2vm/container_disk.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2022 Linka Cloud All rights reserved.
|
||||
//
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"go.linka.cloud/d2vm"
|
||||
"go.linka.cloud/d2vm/pkg/docker"
|
||||
)
|
||||
|
||||
func maybeMakeContainerDisk(ctx context.Context) error {
|
||||
if containerDiskTag == "" {
|
||||
return nil
|
||||
}
|
||||
logrus.Infof("creating container disk image %s", containerDiskTag)
|
||||
if err := d2vm.MakeContainerDisk(ctx, output, containerDiskTag); err != nil {
|
||||
return err
|
||||
}
|
||||
if !push {
|
||||
return nil
|
||||
}
|
||||
logrus.Infof("pushing container disk image %s", containerDiskTag)
|
||||
if err := docker.Push(ctx, containerDiskTag); err != nil {
|
||||
return fmt.Errorf("failed to push container disk: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -30,10 +30,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
raw bool
|
||||
pull = false
|
||||
cmdLineExtra = ""
|
||||
|
||||
convertCmd = &cobra.Command{
|
||||
Use: "convert [docker image]",
|
||||
Short: "Convert Docker image to vm image",
|
||||
@ -65,6 +61,9 @@ var (
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if push && tag == "" {
|
||||
return fmt.Errorf("tag is required when pushing container disk image")
|
||||
}
|
||||
if _, err := os.Stat(output); err == nil || !os.IsNotExist(err) {
|
||||
if !force {
|
||||
return fmt.Errorf("%s already exists", output)
|
||||
@ -100,11 +99,12 @@ var (
|
||||
return err
|
||||
}
|
||||
// set user permissions on the output file if the command was run with sudo
|
||||
uid, ok := sudoUser()
|
||||
if !ok {
|
||||
return nil
|
||||
if uid, ok := sudoUser(); ok {
|
||||
if err := os.Chown(output, uid, uid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return os.Chown(output, uid, uid)
|
||||
return maybeMakeContainerDisk(cmd.Context())
|
||||
},
|
||||
}
|
||||
)
|
||||
@ -119,12 +119,6 @@ func parseSize(s string) (int64, error) {
|
||||
|
||||
func init() {
|
||||
convertCmd.Flags().BoolVar(&pull, "pull", false, "Always pull docker image")
|
||||
convertCmd.Flags().StringVarP(&output, "output", "o", output, "The output image, the extension determine the image format, raw will be used if none. Supported formats: "+strings.Join(d2vm.OutputFormats(), " "))
|
||||
convertCmd.Flags().StringVarP(&password, "password", "p", "", "Optional root user password")
|
||||
convertCmd.Flags().StringVarP(&size, "size", "s", "10G", "The output image size")
|
||||
convertCmd.Flags().BoolVarP(&force, "force", "f", false, "Override output qcow2 image")
|
||||
convertCmd.Flags().StringVar(&cmdLineExtra, "append-to-cmdline", "", "Extra kernel cmdline arguments to append to the generated one")
|
||||
convertCmd.Flags().StringVar(&networkManager, "network-manager", "", "Network manager to use for the image: none, netplan, ifupdown")
|
||||
convertCmd.Flags().BoolVar(&raw, "raw", false, "Just convert the container to virtual machine image without installing anything more")
|
||||
convertCmd.Flags().AddFlagSet(buildFlags())
|
||||
rootCmd.AddCommand(convertCmd)
|
||||
}
|
||||
|
50
cmd/d2vm/flags.go
Normal file
50
cmd/d2vm/flags.go
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2022 Linka Cloud All rights reserved.
|
||||
//
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"go.linka.cloud/d2vm"
|
||||
)
|
||||
|
||||
var (
|
||||
output = "disk0.qcow2"
|
||||
size = "1G"
|
||||
password = ""
|
||||
force = false
|
||||
raw bool
|
||||
pull = false
|
||||
cmdLineExtra = ""
|
||||
containerDiskTag = ""
|
||||
push bool
|
||||
networkManager string
|
||||
)
|
||||
|
||||
func buildFlags() *pflag.FlagSet {
|
||||
flags := pflag.NewFlagSet("build", pflag.ExitOnError)
|
||||
flags.StringVarP(&output, "output", "o", output, "The output image, the extension determine the image format, raw will be used if none. Supported formats: "+strings.Join(d2vm.OutputFormats(), " "))
|
||||
flags.StringVarP(&password, "password", "p", "", "Optional root user password")
|
||||
flags.StringVarP(&size, "size", "s", "10G", "The output image size")
|
||||
flags.BoolVar(&force, "force", false, "Override output qcow2 image")
|
||||
flags.StringVar(&cmdLineExtra, "append-to-cmdline", "", "Extra kernel cmdline arguments to append to the generated one")
|
||||
flags.StringVar(&networkManager, "network-manager", "", "Network manager to use for the image: none, netplan, ifupdown")
|
||||
flags.BoolVar(&raw, "raw", false, "Just convert the container to virtual machine image without installing anything more")
|
||||
flags.StringVarP(&containerDiskTag, "tag", "t", "", "Container disk Docker image tag")
|
||||
flags.BoolVar(&push, "push", false, "Push the container disk image to the registry")
|
||||
return flags
|
||||
}
|
@ -34,10 +34,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
output = "disk0.qcow2"
|
||||
size = "1G"
|
||||
password = ""
|
||||
force = false
|
||||
verbose = false
|
||||
timeFormat = ""
|
||||
format = "qcow2"
|
||||
@ -86,7 +82,7 @@ func init() {
|
||||
rootCmd.PersistentFlags().BoolVarP(&verbose, "debug", "d", false, "Enable Debug output")
|
||||
rootCmd.PersistentFlags().MarkDeprecated("debug", "use -v instead")
|
||||
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable Verbose output")
|
||||
rootCmd.PersistentFlags().StringVarP(&timeFormat, "time", "t", "none", "Enable formated timed output, valide formats: 'relative (rel | r)', 'full (f)'")
|
||||
rootCmd.PersistentFlags().StringVar(&timeFormat, "time", "none", "Enable formated timed output, valide formats: 'relative (rel | r)', 'full (f)'")
|
||||
color.NoColor = false
|
||||
logrus.StandardLogger().Formatter = &logfmtFormatter{start: time.Now()}
|
||||
}
|
||||
|
@ -33,6 +33,8 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/svenwiltink/sparsecat"
|
||||
|
||||
"go.linka.cloud/d2vm/pkg/qemu_img"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -77,7 +79,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 := QemuImgInfo(ctx, imgPath)
|
||||
i, err := qemu_img.Info(ctx, imgPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -89,11 +91,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 := QemuImgConvert(ctx, "raw", imgPath, rawPath); err != nil {
|
||||
if err := qemu_img.Convert(ctx, "raw", imgPath, rawPath); err != nil {
|
||||
return err
|
||||
}
|
||||
imgPath = rawPath
|
||||
i, err = QemuImgInfo(ctx, imgPath)
|
||||
i, err = qemu_img.Info(ctx, imgPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -18,24 +18,16 @@ package run
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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
|
||||
@ -345,86 +337,3 @@ func (p *pw) Progress() int {
|
||||
defer p.mu.RUnlock()
|
||||
return p.total
|
||||
}
|
||||
|
||||
type QemuInfo struct {
|
||||
VirtualSize int `json:"virtual-size"`
|
||||
Filename string `json:"filename"`
|
||||
Format string `json:"format"`
|
||||
ActualSize int `json:"actual-size"`
|
||||
DirtyFlag bool `json:"dirty-flag"`
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
var i QemuInfo
|
||||
if err := json.Unmarshal(o, &i); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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,6 +18,8 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"go.linka.cloud/console"
|
||||
|
||||
"go.linka.cloud/d2vm/pkg/qemu_img"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -72,7 +74,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 := QemuImgInfo(ctx, path)
|
||||
i, err := qemu_img.Info(ctx, path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get image info: %v", err)
|
||||
}
|
||||
@ -84,7 +86,7 @@ func vbox(ctx context.Context, path string) error {
|
||||
}
|
||||
defer os.RemoveAll(vdi)
|
||||
logrus.Infof("converting image to raw: %s", vdi)
|
||||
if err := QemuImgConvert(ctx, "vdi", path, vdi); err != nil {
|
||||
if err := qemu_img.Convert(ctx, "vdi", path, vdi); err != nil {
|
||||
return err
|
||||
}
|
||||
path = vdi
|
||||
|
67
container_disk.go
Normal file
67
container_disk.go
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2022 Linka Cloud All rights reserved.
|
||||
//
|
||||
// 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 d2vm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"go.linka.cloud/d2vm/pkg/docker"
|
||||
"go.linka.cloud/d2vm/pkg/qemu_img"
|
||||
)
|
||||
|
||||
const (
|
||||
// https://kubevirt.io/user-guide/virtual_machines/disks_and_volumes/#containerdisk-workflow-example
|
||||
uid = 107
|
||||
containerDiskDockerfile = `FROM scratch
|
||||
|
||||
ADD --chown=%[1]d:%[1]d %[2]s /disk/
|
||||
`
|
||||
)
|
||||
|
||||
func MakeContainerDisk(ctx context.Context, path string, tag string) error {
|
||||
tmpPath := filepath.Join(os.TempDir(), "d2vm", uuid.New().String())
|
||||
if err := os.MkdirAll(tmpPath, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err := os.RemoveAll(tmpPath); err != nil {
|
||||
logrus.Errorf("failed to remove tmp dir %s: %v", tmpPath, err)
|
||||
}
|
||||
}()
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
return err
|
||||
}
|
||||
// convert may not be needed, but this will also copy the file in the tmp dir
|
||||
qcow2 := filepath.Join(tmpPath, "disk.qcow2")
|
||||
if err := qemu_img.Convert(ctx, "qcow2", path, qcow2); err != nil {
|
||||
return err
|
||||
}
|
||||
disk := filepath.Base(qcow2)
|
||||
dockerfileContent := fmt.Sprintf(containerDiskDockerfile, uid, disk)
|
||||
dockerfile := filepath.Join(tmpPath, "Dockerfile")
|
||||
if err := os.WriteFile(dockerfile, []byte(dockerfileContent), os.ModePerm); err != nil {
|
||||
return fmt.Errorf("failed to write dockerfile: %w", err)
|
||||
}
|
||||
if err := docker.Build(ctx, tag, dockerfile, tmpPath); err != nil {
|
||||
return fmt.Errorf("failed to build container disk: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -96,6 +96,10 @@ func Pull(ctx context.Context, tag string) error {
|
||||
return Cmd(ctx, "image", "pull", tag)
|
||||
}
|
||||
|
||||
func Push(ctx context.Context, tag string) error {
|
||||
return Cmd(ctx, "image", "push", tag)
|
||||
}
|
||||
|
||||
func RunInteractiveAndRemove(ctx context.Context, args ...string) error {
|
||||
cmd := exec.CommandContext(ctx, "docker", append([]string{"run", "--rm", "-it"}, args...)...)
|
||||
cmd.Stdin = os.Stdin
|
||||
|
114
pkg/qemu_img/qemu_img.go
Normal file
114
pkg/qemu_img/qemu_img.go
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright 2022 Linka Cloud All rights reserved.
|
||||
//
|
||||
// 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 qemu_img
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"go.linka.cloud/d2vm/pkg/docker"
|
||||
exec2 "go.linka.cloud/d2vm/pkg/exec"
|
||||
)
|
||||
|
||||
var (
|
||||
DockerImageName string
|
||||
DockerImageVersion string
|
||||
)
|
||||
|
||||
type ImgInfo struct {
|
||||
VirtualSize int `json:"virtual-size"`
|
||||
Filename string `json:"filename"`
|
||||
Format string `json:"format"`
|
||||
ActualSize int `json:"actual-size"`
|
||||
DirtyFlag bool `json:"dirty-flag"`
|
||||
}
|
||||
|
||||
func Info(ctx context.Context, in string) (*ImgInfo, 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", DockerImageName, DockerImageVersion),
|
||||
"info",
|
||||
in,
|
||||
"--output",
|
||||
"json",
|
||||
).CombinedOutput()
|
||||
} else {
|
||||
o, err = exec2.CommandContext(ctx, "qemu-img", "info", in, "--output", "json").CombinedOutput()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%v: %s", err, string(o))
|
||||
}
|
||||
var i ImgInfo
|
||||
if err := json.Unmarshal(o, &i); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &i, nil
|
||||
}
|
||||
|
||||
func Convert(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", DockerImageName, DockerImageVersion),
|
||||
"convert",
|
||||
"-O",
|
||||
format,
|
||||
in,
|
||||
out,
|
||||
)
|
||||
}
|
@ -14,8 +14,17 @@
|
||||
|
||||
package d2vm
|
||||
|
||||
import (
|
||||
"go.linka.cloud/d2vm/pkg/qemu_img"
|
||||
)
|
||||
|
||||
var (
|
||||
Version = ""
|
||||
BuildDate = ""
|
||||
Image = "linkacloud/d2vm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
qemu_img.DockerImageName = Image
|
||||
qemu_img.DockerImageVersion = Version
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user