mirror of
https://github.com/linka-cloud/d2vm.git
synced 2024-11-25 17:16:25 +00:00
feat: add --raw image creation support
refactor: use Option func pattern fix: build respect the --force flag fix: compute correct in-docker input and outpout mount paths Signed-off-by: Adphi <philippe.adrien.nousse@gmail.com>
This commit is contained in:
parent
eb36d45c35
commit
480cae12cf
@ -15,6 +15,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@ -40,7 +41,41 @@ var (
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
// TODO(adphi): resolve context path
|
||||
if runtime.GOOS != "linux" {
|
||||
return docker.RunD2VM(cmd.Context(), d2vm.Image, d2vm.Version, cmd.Name(), os.Args[2:]...)
|
||||
ctxAbsPath, err := filepath.Abs(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dockerFileAbsPath, err := filepath.Abs(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !strings.HasPrefix(dockerFileAbsPath, ctxAbsPath) {
|
||||
return fmt.Errorf("Dockerfile must be in the context directory path")
|
||||
}
|
||||
outputPath, err := filepath.Abs(output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var (
|
||||
in = ctxAbsPath
|
||||
out = filepath.Dir(outputPath)
|
||||
)
|
||||
dargs := os.Args[2:]
|
||||
for i, v := range dargs {
|
||||
switch v {
|
||||
case file:
|
||||
rel, err := filepath.Rel(in, file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to construct Dockerfile container paths: %w", err)
|
||||
}
|
||||
dargs[i] = filepath.Join("/in", rel)
|
||||
case output:
|
||||
dargs[i] = filepath.Join("/out", output)
|
||||
case args[0]:
|
||||
dargs[i] = "/in"
|
||||
}
|
||||
}
|
||||
return docker.RunD2VM(cmd.Context(), d2vm.Image, d2vm.Version, in, out, cmd.Name(), os.Args[2:]...)
|
||||
}
|
||||
size, err := parseSize(size)
|
||||
if err != nil {
|
||||
@ -49,11 +84,25 @@ var (
|
||||
if file == "" {
|
||||
file = filepath.Join(args[0], "Dockerfile")
|
||||
}
|
||||
if _, err := os.Stat(output); err == nil || !os.IsNotExist(err) {
|
||||
if !force {
|
||||
return fmt.Errorf("%s already exists", output)
|
||||
}
|
||||
}
|
||||
logrus.Infof("building docker image from %s", file)
|
||||
if err := docker.Build(cmd.Context(), tag, file, args[0], buildArgs...); err != nil {
|
||||
return err
|
||||
}
|
||||
return d2vm.Convert(cmd.Context(), tag, size, password, output, cmdLineExtra, d2vm.NetworkManager(networkManager))
|
||||
return d2vm.Convert(
|
||||
cmd.Context(),
|
||||
tag,
|
||||
d2vm.WithSize(size),
|
||||
d2vm.WithPassword(password),
|
||||
d2vm.WithOutput(output),
|
||||
d2vm.WithCmdLineExtra(cmdLineExtra),
|
||||
d2vm.WithNetworkManager(d2vm.NetworkManager(networkManager)),
|
||||
d2vm.WithRaw(raw),
|
||||
)
|
||||
},
|
||||
}
|
||||
)
|
||||
@ -70,4 +119,5 @@ func init() {
|
||||
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")
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
@ -29,6 +30,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
raw bool
|
||||
pull = false
|
||||
cmdLineExtra = ""
|
||||
|
||||
@ -39,7 +41,19 @@ var (
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if runtime.GOOS != "linux" {
|
||||
return docker.RunD2VM(cmd.Context(), d2vm.Image, d2vm.Version, cmd.Name(), os.Args[2:]...)
|
||||
abs, err := filepath.Abs(output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out := filepath.Dir(abs)
|
||||
dargs := os.Args[2:]
|
||||
for i, v := range dargs {
|
||||
if v == output {
|
||||
dargs[i] = filepath.Join("/out", filepath.Base(output))
|
||||
break
|
||||
}
|
||||
}
|
||||
return docker.RunD2VM(cmd.Context(), d2vm.Image, d2vm.Version, out, out, cmd.Name(), dargs...)
|
||||
}
|
||||
img := args[0]
|
||||
tag := "latest"
|
||||
@ -55,11 +69,6 @@ var (
|
||||
return fmt.Errorf("%s already exists", output)
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(output); err == nil || !os.IsNotExist(err) {
|
||||
if !force {
|
||||
return fmt.Errorf("%s already exists", output)
|
||||
}
|
||||
}
|
||||
found := false
|
||||
if !pull {
|
||||
imgs, err := docker.ImageList(cmd.Context(), img)
|
||||
@ -77,7 +86,16 @@ var (
|
||||
return err
|
||||
}
|
||||
}
|
||||
return d2vm.Convert(cmd.Context(), img, size, password, output, cmdLineExtra, d2vm.NetworkManager(networkManager))
|
||||
return d2vm.Convert(
|
||||
cmd.Context(),
|
||||
img,
|
||||
d2vm.WithSize(size),
|
||||
d2vm.WithPassword(password),
|
||||
d2vm.WithOutput(output),
|
||||
d2vm.WithCmdLineExtra(cmdLineExtra),
|
||||
d2vm.WithNetworkManager(d2vm.NetworkManager(networkManager)),
|
||||
d2vm.WithRaw(raw),
|
||||
)
|
||||
},
|
||||
}
|
||||
)
|
||||
@ -98,5 +116,6 @@ func init() {
|
||||
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")
|
||||
rootCmd.AddCommand(convertCmd)
|
||||
}
|
||||
|
Binary file not shown.
60
convert.go
60
convert.go
@ -28,7 +28,11 @@ import (
|
||||
"go.linka.cloud/d2vm/pkg/docker"
|
||||
)
|
||||
|
||||
func Convert(ctx context.Context, img string, size int64, password string, output string, cmdLineExtra string, networkManager NetworkManager) error {
|
||||
func Convert(ctx context.Context, img string, opts ...ConvertOption) error {
|
||||
o := &convertOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(o)
|
||||
}
|
||||
imgUUID := uuid.New().String()
|
||||
tmpPath := filepath.Join(os.TempDir(), "d2vm", imgUUID)
|
||||
if err := os.MkdirAll(tmpPath, os.ModePerm); err != nil {
|
||||
@ -41,33 +45,41 @@ func Convert(ctx context.Context, img string, size int64, password string, outpu
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d, err := NewDockerfile(r, img, password, networkManager)
|
||||
if err != nil {
|
||||
return err
|
||||
if !o.raw {
|
||||
d, err := NewDockerfile(r, img, o.password, o.networkManager)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("docker image based on %s", d.Release.Name)
|
||||
p := filepath.Join(tmpPath, docker.FormatImgName(img))
|
||||
dir := filepath.Dir(p)
|
||||
f, err := os.Create(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
if err := d.Render(f); err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("building kernel enabled image")
|
||||
if err := docker.Build(ctx, imgUUID, p, dir); err != nil {
|
||||
return err
|
||||
}
|
||||
defer docker.Remove(ctx, imgUUID)
|
||||
} else {
|
||||
// for raw images, we just tag the image with the uuid
|
||||
if err := docker.Tag(ctx, img, imgUUID); err != nil {
|
||||
return err
|
||||
}
|
||||
defer docker.Remove(ctx, imgUUID)
|
||||
}
|
||||
logrus.Infof("docker image based on %s", d.Release.Name)
|
||||
p := filepath.Join(tmpPath, docker.FormatImgName(img))
|
||||
dir := filepath.Dir(p)
|
||||
f, err := os.Create(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
if err := d.Render(f); err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("building kernel enabled image")
|
||||
if err := docker.Build(ctx, imgUUID, p, dir); err != nil {
|
||||
return err
|
||||
}
|
||||
defer docker.Remove(ctx, imgUUID)
|
||||
|
||||
logrus.Infof("creating vm image")
|
||||
format := strings.TrimPrefix(filepath.Ext(output), ".")
|
||||
format := strings.TrimPrefix(filepath.Ext(o.output), ".")
|
||||
if format == "" {
|
||||
format = "raw"
|
||||
}
|
||||
b, err := NewBuilder(ctx, tmpPath, imgUUID, "", size, r, format, cmdLineExtra)
|
||||
b, err := NewBuilder(ctx, tmpPath, imgUUID, "", o.size, r, format, o.cmdLineExtra)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -75,10 +87,10 @@ func Convert(ctx context.Context, img string, size int64, password string, outpu
|
||||
if err := b.Build(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(output); err != nil {
|
||||
if err := os.RemoveAll(o.output); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := MoveFile(filepath.Join(tmpPath, "disk0."+format), output); err != nil {
|
||||
if err := MoveFile(filepath.Join(tmpPath, "disk0."+format), o.output); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
62
convert_options.go
Normal file
62
convert_options.go
Normal file
@ -0,0 +1,62 @@
|
||||
// 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
|
||||
|
||||
type ConvertOption func(o *convertOptions)
|
||||
|
||||
type convertOptions struct {
|
||||
size int64
|
||||
password string
|
||||
output string
|
||||
cmdLineExtra string
|
||||
networkManager NetworkManager
|
||||
raw bool
|
||||
}
|
||||
|
||||
func WithSize(size int64) ConvertOption {
|
||||
return func(o *convertOptions) {
|
||||
o.size = size
|
||||
}
|
||||
}
|
||||
|
||||
func WithPassword(password string) ConvertOption {
|
||||
return func(o *convertOptions) {
|
||||
o.password = password
|
||||
}
|
||||
}
|
||||
|
||||
func WithOutput(output string) ConvertOption {
|
||||
return func(o *convertOptions) {
|
||||
o.output = output
|
||||
}
|
||||
}
|
||||
|
||||
func WithCmdLineExtra(cmdLineExtra string) ConvertOption {
|
||||
return func(o *convertOptions) {
|
||||
o.cmdLineExtra = cmdLineExtra
|
||||
}
|
||||
}
|
||||
|
||||
func WithNetworkManager(networkManager NetworkManager) ConvertOption {
|
||||
return func(o *convertOptions) {
|
||||
o.networkManager = networkManager
|
||||
}
|
||||
}
|
||||
|
||||
func WithRaw(raw bool) ConvertOption {
|
||||
return func(o *convertOptions) {
|
||||
o.raw = raw
|
||||
}
|
||||
}
|
@ -62,6 +62,19 @@ func Build(ctx context.Context, tag, dockerfile, dir string, buildArgs ...string
|
||||
return Cmd(ctx, args...)
|
||||
}
|
||||
|
||||
func Tag(ctx context.Context, img string, tags ...string) error {
|
||||
if len(tags) == 0 {
|
||||
return fmt.Errorf("no tags specified")
|
||||
}
|
||||
args := []string{"image", "tag"}
|
||||
for _, tag := range tags {
|
||||
if err := Cmd(ctx, append(args, img, tag)...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Remove(ctx context.Context, tag string) error {
|
||||
return Cmd(ctx, "image", "rm", tag)
|
||||
}
|
||||
@ -97,11 +110,17 @@ func RunAndRemove(ctx context.Context, args ...string) error {
|
||||
return Cmd(ctx, append([]string{"run", "--rm"}, args...)...)
|
||||
}
|
||||
|
||||
func RunD2VM(ctx context.Context, image, version, cmd string, args ...string) error {
|
||||
func RunD2VM(ctx context.Context, image, version, in, out, cmd string, args ...string) error {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if in == "" {
|
||||
in = pwd
|
||||
}
|
||||
if out == "" {
|
||||
out = pwd
|
||||
}
|
||||
if image == "" {
|
||||
image = "linkacloud/d2vm"
|
||||
}
|
||||
@ -113,7 +132,9 @@ func RunD2VM(ctx context.Context, image, version, cmd string, args ...string) er
|
||||
"-v",
|
||||
fmt.Sprintf("%s:/var/run/docker.sock", dockerSocket()),
|
||||
"-v",
|
||||
fmt.Sprintf("%s:/d2vm", pwd),
|
||||
fmt.Sprintf("%s:/in", in),
|
||||
"-v",
|
||||
fmt.Sprintf("%s:/out", out),
|
||||
"-w",
|
||||
"/d2vm",
|
||||
fmt.Sprintf("%s:%s", image, version),
|
||||
|
Loading…
Reference in New Issue
Block a user