88 lines
2.5 KiB
Go
88 lines
2.5 KiB
Go
package imageutil
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io/ioutil"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/containerd/containerd/remotes"
|
|
digest "github.com/opencontainers/go-digest"
|
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func readSchema1Config(ctx context.Context, ref string, desc specs.Descriptor, fetcher remotes.Fetcher, cache ContentCache) (digest.Digest, []byte, error) {
|
|
rc, err := fetcher.Fetch(ctx, desc)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
defer rc.Close()
|
|
dt, err := ioutil.ReadAll(rc)
|
|
if err != nil {
|
|
return "", nil, errors.Wrap(err, "failed to fetch schema1 manifest")
|
|
}
|
|
dt, err = convertSchema1ConfigMeta(dt)
|
|
if err != nil {
|
|
return "", nil, err
|
|
}
|
|
return desc.Digest, dt, nil
|
|
}
|
|
|
|
func convertSchema1ConfigMeta(in []byte) ([]byte, error) {
|
|
type history struct {
|
|
V1Compatibility string `json:"v1Compatibility"`
|
|
}
|
|
var m struct {
|
|
History []history `json:"history"`
|
|
}
|
|
if err := json.Unmarshal(in, &m); err != nil {
|
|
return nil, errors.Wrap(err, "failed to unmarshal schema1 manifest")
|
|
}
|
|
if len(m.History) == 0 {
|
|
return nil, errors.Errorf("invalid schema1 manifest")
|
|
}
|
|
|
|
var img specs.Image
|
|
if err := json.Unmarshal([]byte(m.History[0].V1Compatibility), &img); err != nil {
|
|
return nil, errors.Wrap(err, "failed to unmarshal image from schema 1 history")
|
|
}
|
|
|
|
img.RootFS = specs.RootFS{
|
|
Type: "layers", // filled in by exporter
|
|
}
|
|
img.History = make([]specs.History, len(m.History))
|
|
|
|
for i := range m.History {
|
|
var h v1History
|
|
if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), &h); err != nil {
|
|
return nil, errors.Wrap(err, "failed to unmarshal history")
|
|
}
|
|
img.History[len(m.History)-i-1] = specs.History{
|
|
Author: h.Author,
|
|
Comment: h.Comment,
|
|
Created: &h.Created,
|
|
CreatedBy: strings.Join(h.ContainerConfig.Cmd, " "),
|
|
EmptyLayer: (h.ThrowAway != nil && *h.ThrowAway) || (h.Size != nil && *h.Size == 0),
|
|
}
|
|
}
|
|
|
|
dt, err := json.MarshalIndent(img, "", " ")
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to marshal schema1 config")
|
|
}
|
|
return dt, nil
|
|
}
|
|
|
|
type v1History struct {
|
|
Author string `json:"author,omitempty"`
|
|
Created time.Time `json:"created"`
|
|
Comment string `json:"comment,omitempty"`
|
|
ThrowAway *bool `json:"throwaway,omitempty"`
|
|
Size *int `json:"Size,omitempty"` // used before ThrowAway field
|
|
ContainerConfig struct {
|
|
Cmd []string `json:"Cmd,omitempty"`
|
|
} `json:"container_config,omitempty"`
|
|
}
|