preliminary unit tests
This commit is contained in:
parent
2ec4c0629b
commit
eb9f4d5400
@ -1,4 +1,4 @@
|
|||||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
@ -6,3 +6,13 @@ go_library(
|
|||||||
importpath = "github.com/motiejus/code/undocker/lxcconfig",
|
importpath = "github.com/motiejus/code/undocker/lxcconfig",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["lxcconfig_test.go"],
|
||||||
|
embed = [":go_default_library"],
|
||||||
|
deps = [
|
||||||
|
"@com_github_stretchr_testify//assert:go_default_library",
|
||||||
|
"@com_github_stretchr_testify//require:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@ -7,32 +7,89 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
const _manifestJSON = "manifest.json"
|
const (
|
||||||
|
_json = ".json"
|
||||||
|
_manifestJSON = "manifest.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_lxcTemplate = template.Must(
|
||||||
|
template.New("lxcconfig").Parse("" +
|
||||||
|
"lxc.include = LXC_TEMPLATE_CONFIG/common.conf\n" +
|
||||||
|
"lxc.architecture = {{ .Architecture }}\n" +
|
||||||
|
"lxc.execute.cmd = '{{ .Cmd }}'\n" +
|
||||||
|
"{{ if .Cwd }}lxc.init.cwd = {{ .Cwd }}\n{{ end }}" +
|
||||||
|
"{{ range .Env }}lxc.environment = {{ . }}\n{{ end }}"))
|
||||||
|
errBadManifest = errors.New("bad or missing manifest.json")
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// lxcConfig is passed to _lxcTemplate
|
||||||
|
lxcConfig struct {
|
||||||
|
Architecture string
|
||||||
|
Cmd string
|
||||||
|
Cwd string
|
||||||
|
Env []string
|
||||||
|
}
|
||||||
|
|
||||||
// dockerManifest is manifest.json
|
// dockerManifest is manifest.json
|
||||||
type dockerManifest []struct {
|
dockerManifest []struct {
|
||||||
Config string `json:"Config"`
|
Config string `json:"Config"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// dockerConfig returns interesting configs for the container. user/group are
|
// dockerConfig returns interesting configs for the container. user/group are
|
||||||
// skipped, since Docker allows specifying them by name, which would require
|
// skipped, since Docker allows specifying them by name, which would require
|
||||||
// peeking into the container image's /etc/passwd to resolve the names to ints.
|
// peeking into the container image's /etc/passwd to resolve the names to ints.
|
||||||
type dockerConfig struct {
|
dockerConfig struct {
|
||||||
Architecture string `json:"architecture"`
|
Architecture string `json:"architecture"`
|
||||||
Config struct {
|
Config dockerConfigConfig `json:"config"`
|
||||||
Entrypoint []string `json:"Entrypoint"`
|
|
||||||
Cmd []string `json:"Cmd"`
|
|
||||||
Env []string `json:"Env"`
|
|
||||||
WorkingDir string `json:"WorkingDir"`
|
|
||||||
} `json:"config"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
dockerConfigConfig struct {
|
||||||
errBadManifest = errors.New("bad or missing manifest.json")
|
Entrypoint []string `json:"Entrypoint"`
|
||||||
|
Cmd []string `json:"Cmd"`
|
||||||
|
WorkingDir string `json:"WorkingDir"`
|
||||||
|
Env []string `json:"Env"`
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// LXCConfig accepts a Docker container image and returns lxc configuration.
|
||||||
|
func LXCConfig(in io.ReadSeeker, wr io.Writer) error {
|
||||||
|
dockerCfg, err := getDockerConfig(in)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lxcCfg := docker2lxc(dockerCfg)
|
||||||
|
return lxcCfg.WriteTo(wr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func docker2lxc(d dockerConfig) lxcConfig {
|
||||||
|
// cmd/entrypoint logic is copied from lxc-oci template
|
||||||
|
ep := strings.Join(d.Config.Entrypoint, " ")
|
||||||
|
cmd := strings.Join(d.Config.Cmd, " ")
|
||||||
|
if len(ep) == 0 {
|
||||||
|
ep = cmd
|
||||||
|
if len(ep) == 0 {
|
||||||
|
ep = "/bin/sh"
|
||||||
|
}
|
||||||
|
} else if len(cmd) != 0 {
|
||||||
|
ep = ep + " " + cmd
|
||||||
|
}
|
||||||
|
return lxcConfig{
|
||||||
|
Architecture: d.Architecture,
|
||||||
|
Cmd: ep,
|
||||||
|
Env: d.Config.Env,
|
||||||
|
Cwd: d.Config.WorkingDir,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l lxcConfig) WriteTo(wr io.Writer) error {
|
||||||
|
return _lxcTemplate.Execute(wr, l)
|
||||||
|
}
|
||||||
|
|
||||||
func getDockerConfig(in io.ReadSeeker) (dockerConfig, error) {
|
func getDockerConfig(in io.ReadSeeker) (dockerConfig, error) {
|
||||||
tr := tar.NewReader(in)
|
tr := tar.NewReader(in)
|
||||||
// get offsets to all json files in the archive
|
// get offsets to all json files in the archive
|
||||||
@ -45,7 +102,7 @@ func getDockerConfig(in io.ReadSeeker) (dockerConfig, error) {
|
|||||||
if hdr.Typeflag != tar.TypeReg {
|
if hdr.Typeflag != tar.TypeReg {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !strings.HasSuffix(".json", hdr.Name) {
|
if !strings.HasSuffix(_json, hdr.Name) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
here, err := in.Seek(0, io.SeekCurrent)
|
here, err := in.Seek(0, io.SeekCurrent)
|
||||||
|
58
lxcconfig/lxcconfig_test.go
Normal file
58
lxcconfig/lxcconfig_test.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package lxcconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLXCConfig(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
docker dockerConfig
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "just architecture",
|
||||||
|
docker: dockerConfig{
|
||||||
|
Architecture: "amd64",
|
||||||
|
},
|
||||||
|
want: `lxc.include = LXC_TEMPLATE_CONFIG/common.conf
|
||||||
|
lxc.architecture = amd64
|
||||||
|
lxc.execute.cmd = '/bin/sh'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all fields",
|
||||||
|
docker: dockerConfig{
|
||||||
|
Architecture: "amd64",
|
||||||
|
Config: dockerConfigConfig{
|
||||||
|
Entrypoint: []string{"/entrypoint.sh"},
|
||||||
|
Cmd: []string{"/bin/sh", "-c", "echo foo"},
|
||||||
|
WorkingDir: "/x",
|
||||||
|
Env: []string{
|
||||||
|
`LONGNAME="Foo Bar"`,
|
||||||
|
"SHELL=/bin/tcsh",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `lxc.include = LXC_TEMPLATE_CONFIG/common.conf
|
||||||
|
lxc.architecture = amd64
|
||||||
|
lxc.execute.cmd = '/entrypoint.sh /bin/sh -c echo foo'
|
||||||
|
lxc.init.cwd = /x
|
||||||
|
lxc.environment = LONGNAME="Foo Bar"
|
||||||
|
lxc.environment = SHELL=/bin/tcsh
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
require.NoError(t, docker2lxc(tt.docker).WriteTo(&buf))
|
||||||
|
assert.Equal(t, tt.want, string(buf.Bytes()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -36,9 +36,9 @@ type dockerManifestJSON []struct {
|
|||||||
// I) layer name
|
// I) layer name
|
||||||
// II) offset (0 being the first file in the layer)
|
// II) offset (0 being the first file in the layer)
|
||||||
// 4. go through
|
// 4. go through
|
||||||
func RootFS(in io.ReadSeeker, out io.Writer) (err error) {
|
func RootFS(in io.ReadSeeker, wr io.Writer) (err error) {
|
||||||
tr := tar.NewReader(in)
|
tr := tar.NewReader(in)
|
||||||
tw := tar.NewWriter(out)
|
tw := tar.NewWriter(wr)
|
||||||
defer func() { err = multierr.Append(err, tw.Close()) }()
|
defer func() { err = multierr.Append(err, tw.Close()) }()
|
||||||
|
|
||||||
// layerOffsets maps a layer name (a9b123c0daa/layer.tar) to it's offset
|
// layerOffsets maps a layer name (a9b123c0daa/layer.tar) to it's offset
|
||||||
|
Loading…
Reference in New Issue
Block a user