93 lines
2.2 KiB
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
|
|
}
|