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"` }