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