From b76a500f2eff4395824cceb280d7b92974ddff8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Mon, 24 May 2021 00:11:58 +0300 Subject: [PATCH] wip lxc-config --- lxc-config/BUILD | 8 ++++ lxc-config/lxcconfig.go | 92 +++++++++++++++++++++++++++++++++++++++++ rootfs/rootfs.go | 2 +- 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 lxc-config/BUILD create mode 100644 lxc-config/lxcconfig.go diff --git a/lxc-config/BUILD b/lxc-config/BUILD new file mode 100644 index 0000000..e360101 --- /dev/null +++ b/lxc-config/BUILD @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["lxcconfig.go"], + importpath = "github.com/motiejus/code/undocker/lxc-config", + visibility = ["//visibility:public"], +) diff --git a/lxc-config/lxcconfig.go b/lxc-config/lxcconfig.go new file mode 100644 index 0000000..6a529e9 --- /dev/null +++ b/lxc-config/lxcconfig.go @@ -0,0 +1,92 @@ +package lxcconfig + +import ( + "archive/tar" + "encoding/json" + "errors" + "fmt" + "io" + "strings" +) + +const _manifestJSON = "manifest.json" + +// dockerManifest is manifest.json +type dockerManifest []struct { + Config string `json:"Config"` +} + +// dockerConfig returns interesting configs for the container. user/group are +// 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. +type dockerConfig struct { + Architecture string `json:"architecture"` + Config struct { + Entrypoint []string `json:"Entrypoint"` + Cmd []string `json:"Cmd"` + Env []string `json:"Env"` + WorkingDir string `json:"WorkingDir"` + } `json:"config"` +} + +var ( + errBadManifest = errors.New("bad or missing manifest.json") +) + +func getDockerConfig(in io.ReadSeeker) (dockerConfig, error) { + tr := tar.NewReader(in) + // get offsets to all json files in the archive + jsonOffsets := map[string]int64{} + for { + hdr, err := tr.Next() + if err == io.EOF { + break + } + if hdr.Typeflag != tar.TypeReg { + continue + } + if !strings.HasSuffix(".json", hdr.Name) { + continue + } + here, err := in.Seek(0, io.SeekCurrent) + if err != nil { + return dockerConfig{}, err + } + jsonOffsets[hdr.Name] = here + } + + // manifest is the docker manifest in the image + var manifest dockerManifest + if err := parseJSON(in, jsonOffsets, _manifestJSON, &manifest); err != nil { + return dockerConfig{}, err + } + if len(manifest) == 0 { + return dockerConfig{}, errBadManifest + } + + var config dockerConfig + if err := parseJSON(in, jsonOffsets, manifest[0].Config, &config); err != nil { + return dockerConfig{}, err + } + + return config, nil +} + +func parseJSON(in io.ReadSeeker, offsets map[string]int64, fname string, c interface{}) error { + configOffset, ok := offsets[fname] + if !ok { + return fmt.Errorf("file %s not found", fname) + } + if _, err := in.Seek(configOffset, io.SeekStart); err != nil { + return fmt.Errorf("seek to %s: %w", fname, err) + } + tr := tar.NewReader(in) + if _, err := tr.Next(); err != nil { + return err + } + dec := json.NewDecoder(tr) + if err := dec.Decode(c); err != nil { + return fmt.Errorf("decode %s: %w", fname, err) + } + return nil +} diff --git a/rootfs/rootfs.go b/rootfs/rootfs.go index 4848eb0..41839b8 100644 --- a/rootfs/rootfs.go +++ b/rootfs/rootfs.go @@ -61,7 +61,7 @@ func RootFS(in io.ReadSeeker, out io.Writer) (err error) { case filepath.Clean(hdr.Name) == _manifestJSON: dec := json.NewDecoder(tr) if err := dec.Decode(&manifest); err != nil { - return fmt.Errorf("parse %s: %w", _manifestJSON, err) + return fmt.Errorf("decode %s: %w", _manifestJSON, err) } case strings.HasSuffix(hdr.Name, _layerSuffix): here, err := in.Seek(0, io.SeekCurrent)