undocker/lxc-config/lxcconfig.go

93 lines
2.2 KiB
Go

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
}