github.com/rentongzhang/docker@v1.8.2-rc1/graph/push_v2.go (about) 1 package graph 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 8 "github.com/Sirupsen/logrus" 9 "github.com/docker/distribution" 10 "github.com/docker/distribution/digest" 11 "github.com/docker/distribution/manifest" 12 "github.com/docker/docker/image" 13 "github.com/docker/docker/pkg/progressreader" 14 "github.com/docker/docker/pkg/streamformatter" 15 "github.com/docker/docker/pkg/stringid" 16 "github.com/docker/docker/registry" 17 "github.com/docker/docker/runconfig" 18 "github.com/docker/docker/utils" 19 "golang.org/x/net/context" 20 ) 21 22 type v2Pusher struct { 23 *TagStore 24 endpoint registry.APIEndpoint 25 localRepo Repository 26 repoInfo *registry.RepositoryInfo 27 config *ImagePushConfig 28 sf *streamformatter.StreamFormatter 29 repo distribution.Repository 30 } 31 32 func (p *v2Pusher) Push() (fallback bool, err error) { 33 p.repo, err = NewV2Repository(p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig) 34 if err != nil { 35 logrus.Debugf("Error getting v2 registry: %v", err) 36 return true, err 37 } 38 return false, p.pushV2Repository(p.config.Tag) 39 } 40 41 func (p *v2Pusher) getImageTags(askedTag string) ([]string, error) { 42 logrus.Debugf("Checking %q against %#v", askedTag, p.localRepo) 43 if len(askedTag) > 0 { 44 if _, ok := p.localRepo[askedTag]; !ok || utils.DigestReference(askedTag) { 45 return nil, fmt.Errorf("Tag does not exist for %s", askedTag) 46 } 47 return []string{askedTag}, nil 48 } 49 var tags []string 50 for tag := range p.localRepo { 51 if !utils.DigestReference(tag) { 52 tags = append(tags, tag) 53 } 54 } 55 return tags, nil 56 } 57 58 func (p *v2Pusher) pushV2Repository(tag string) error { 59 localName := p.repoInfo.LocalName 60 if _, err := p.poolAdd("push", localName); err != nil { 61 return err 62 } 63 defer p.poolRemove("push", localName) 64 65 tags, err := p.getImageTags(tag) 66 if err != nil { 67 return fmt.Errorf("error getting tags for %s: %s", localName, err) 68 } 69 if len(tags) == 0 { 70 return fmt.Errorf("no tags to push for %s", localName) 71 } 72 73 for _, tag := range tags { 74 if err := p.pushV2Tag(tag); err != nil { 75 return err 76 } 77 } 78 79 return nil 80 } 81 82 func (p *v2Pusher) pushV2Tag(tag string) error { 83 logrus.Debugf("Pushing repository: %s:%s", p.repo.Name(), tag) 84 85 layerId, exists := p.localRepo[tag] 86 if !exists { 87 return fmt.Errorf("tag does not exist: %s", tag) 88 } 89 90 layersSeen := make(map[string]bool) 91 92 layer, err := p.graph.Get(layerId) 93 if err != nil { 94 return err 95 } 96 97 m := &manifest.Manifest{ 98 Versioned: manifest.Versioned{ 99 SchemaVersion: 1, 100 }, 101 Name: p.repo.Name(), 102 Tag: tag, 103 Architecture: layer.Architecture, 104 FSLayers: []manifest.FSLayer{}, 105 History: []manifest.History{}, 106 } 107 108 var metadata runconfig.Config 109 if layer != nil && layer.Config != nil { 110 metadata = *layer.Config 111 } 112 113 out := p.config.OutStream 114 115 for ; layer != nil; layer, err = p.graph.GetParent(layer) { 116 if err != nil { 117 return err 118 } 119 120 if layersSeen[layer.ID] { 121 break 122 } 123 124 logrus.Debugf("Pushing layer: %s", layer.ID) 125 126 if layer.Config != nil && metadata.Image != layer.ID { 127 if err := runconfig.Merge(&metadata, layer.Config); err != nil { 128 return err 129 } 130 } 131 132 jsonData, err := p.graph.RawJSON(layer.ID) 133 if err != nil { 134 return fmt.Errorf("cannot retrieve the path for %s: %s", layer.ID, err) 135 } 136 137 var exists bool 138 dgst, err := p.graph.GetDigest(layer.ID) 139 switch err { 140 case nil: 141 _, err := p.repo.Blobs(context.Background()).Stat(context.Background(), dgst) 142 switch err { 143 case nil: 144 exists = true 145 out.Write(p.sf.FormatProgress(stringid.TruncateID(layer.ID), "Image already exists", nil)) 146 case distribution.ErrBlobUnknown: 147 // nop 148 default: 149 out.Write(p.sf.FormatProgress(stringid.TruncateID(layer.ID), "Image push failed", nil)) 150 return err 151 } 152 case ErrDigestNotSet: 153 // nop 154 case digest.ErrDigestInvalidFormat, digest.ErrDigestUnsupported: 155 return fmt.Errorf("error getting image checksum: %v", err) 156 } 157 158 // if digest was empty or not saved, or if blob does not exist on the remote repository, 159 // then fetch it. 160 if !exists { 161 if pushDigest, err := p.pushV2Image(p.repo.Blobs(context.Background()), layer); err != nil { 162 return err 163 } else if pushDigest != dgst { 164 // Cache new checksum 165 if err := p.graph.SetDigest(layer.ID, pushDigest); err != nil { 166 return err 167 } 168 dgst = pushDigest 169 } 170 } 171 172 m.FSLayers = append(m.FSLayers, manifest.FSLayer{BlobSum: dgst}) 173 m.History = append(m.History, manifest.History{V1Compatibility: string(jsonData)}) 174 175 layersSeen[layer.ID] = true 176 } 177 178 logrus.Infof("Signed manifest for %s:%s using daemon's key: %s", p.repo.Name(), tag, p.trustKey.KeyID()) 179 signed, err := manifest.Sign(m, p.trustKey) 180 if err != nil { 181 return err 182 } 183 184 manifestDigest, manifestSize, err := digestFromManifest(signed, p.repo.Name()) 185 if err != nil { 186 return err 187 } 188 if manifestDigest != "" { 189 out.Write(p.sf.FormatStatus("", "%s: digest: %s size: %d", tag, manifestDigest, manifestSize)) 190 } 191 192 manSvc, err := p.repo.Manifests(context.Background()) 193 if err != nil { 194 return err 195 } 196 return manSvc.Put(signed) 197 } 198 199 func (p *v2Pusher) pushV2Image(bs distribution.BlobService, img *image.Image) (digest.Digest, error) { 200 out := p.config.OutStream 201 202 out.Write(p.sf.FormatProgress(stringid.TruncateID(img.ID), "Buffering to Disk", nil)) 203 204 image, err := p.graph.Get(img.ID) 205 if err != nil { 206 return "", err 207 } 208 arch, err := p.graph.TarLayer(image) 209 if err != nil { 210 return "", err 211 } 212 213 tf, err := p.graph.newTempFile() 214 if err != nil { 215 return "", err 216 } 217 defer func() { 218 tf.Close() 219 os.Remove(tf.Name()) 220 }() 221 222 size, dgst, err := bufferToFile(tf, arch) 223 if err != nil { 224 return "", err 225 } 226 227 // Send the layer 228 logrus.Debugf("rendered layer for %s of [%d] size", img.ID, size) 229 layerUpload, err := bs.Create(context.Background()) 230 if err != nil { 231 return "", err 232 } 233 defer layerUpload.Close() 234 235 reader := progressreader.New(progressreader.Config{ 236 In: ioutil.NopCloser(tf), 237 Out: out, 238 Formatter: p.sf, 239 Size: int(size), 240 NewLines: false, 241 ID: stringid.TruncateID(img.ID), 242 Action: "Pushing", 243 }) 244 n, err := layerUpload.ReadFrom(reader) 245 if err != nil { 246 return "", err 247 } 248 if n != size { 249 return "", fmt.Errorf("short upload: only wrote %d of %d", n, size) 250 } 251 252 desc := distribution.Descriptor{Digest: dgst} 253 if _, err := layerUpload.Commit(context.Background(), desc); err != nil { 254 return "", err 255 } 256 257 out.Write(p.sf.FormatProgress(stringid.TruncateID(img.ID), "Image successfully pushed", nil)) 258 259 return dgst, nil 260 }