|
@@ -3,6 +3,8 @@ package graph
|
|
|
import (
|
|
|
"bufio"
|
|
|
"compress/gzip"
|
|
|
+ "encoding/json"
|
|
|
+ "errors"
|
|
|
"fmt"
|
|
|
"io"
|
|
|
"io/ioutil"
|
|
@@ -205,6 +207,11 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
|
|
|
p.layersPushed[dgst] = true
|
|
|
}
|
|
|
|
|
|
+ // Fix parent chain if necessary
|
|
|
+ if err = fixHistory(m); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
logrus.Infof("Signed manifest for %s:%s using daemon's key: %s", p.repo.Name(), tag, p.trustKey.KeyID())
|
|
|
signed, err := schema1.Sign(m, p.trustKey)
|
|
|
if err != nil {
|
|
@@ -226,6 +233,90 @@ func (p *v2Pusher) pushV2Tag(tag string) error {
|
|
|
return manSvc.Put(signed)
|
|
|
}
|
|
|
|
|
|
+// fixHistory makes sure that the manifest has parent IDs that are consistent
|
|
|
+// with its image IDs. Because local image IDs are generated from the
|
|
|
+// configuration and filesystem contents, but IDs in the manifest are preserved
|
|
|
+// from the original pull, it's possible to have inconsistencies where parent
|
|
|
+// IDs don't match up with the other IDs in the manifest. This happens in the
|
|
|
+// case where an engine pulls images where are identical except the IDs from the
|
|
|
+// manifest - the local ID will be the same, and one of the v1Compatibility
|
|
|
+// files gets discarded.
|
|
|
+func fixHistory(m *schema1.Manifest) error {
|
|
|
+ var lastID string
|
|
|
+
|
|
|
+ for i := len(m.History) - 1; i >= 0; i-- {
|
|
|
+ var historyEntry map[string]*json.RawMessage
|
|
|
+ if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), &historyEntry); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ idJSON, present := historyEntry["id"]
|
|
|
+ if !present || idJSON == nil {
|
|
|
+ return errors.New("missing id key in v1compatibility file")
|
|
|
+ }
|
|
|
+ var id string
|
|
|
+ if err := json.Unmarshal(*idJSON, &id); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ parentJSON, present := historyEntry["parent"]
|
|
|
+
|
|
|
+ if i == len(m.History)-1 {
|
|
|
+ // The base layer must not reference a parent layer,
|
|
|
+ // otherwise the manifest is incomplete. There is an
|
|
|
+ // exception for Windows to handle base layers.
|
|
|
+ if !allowBaseParentImage && present && parentJSON != nil {
|
|
|
+ var parent string
|
|
|
+ if err := json.Unmarshal(*parentJSON, &parent); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if parent != "" {
|
|
|
+ logrus.Debugf("parent id mismatch detected; fixing. parent reference: %s", parent)
|
|
|
+ delete(historyEntry, "parent")
|
|
|
+ fixedHistory, err := json.Marshal(historyEntry)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ m.History[i].V1Compatibility = string(fixedHistory)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // For all other layers, the parent ID should equal the
|
|
|
+ // ID of the next item in the history list. If it
|
|
|
+ // doesn't, fix it up (but preserve all other fields,
|
|
|
+ // possibly including fields that aren't known to this
|
|
|
+ // engine version).
|
|
|
+ if !present || parentJSON == nil {
|
|
|
+ return errors.New("missing parent key in v1compatibility file")
|
|
|
+ }
|
|
|
+ var parent string
|
|
|
+ if err := json.Unmarshal(*parentJSON, &parent); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if parent != lastID {
|
|
|
+ logrus.Debugf("parent id mismatch detected; fixing. parent reference: %s actual id: %s", parent, id)
|
|
|
+ historyEntry["parent"] = rawJSON(lastID)
|
|
|
+ fixedHistory, err := json.Marshal(historyEntry)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ m.History[i].V1Compatibility = string(fixedHistory)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ lastID = id
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func rawJSON(value interface{}) *json.RawMessage {
|
|
|
+ jsonval, err := json.Marshal(value)
|
|
|
+ if err != nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ return (*json.RawMessage)(&jsonval)
|
|
|
+}
|
|
|
+
|
|
|
func (p *v2Pusher) pushV2Image(bs distribution.BlobService, img *image.Image) (digest.Digest, error) {
|
|
|
out := p.config.OutStream
|
|
|
|