-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpush.go
135 lines (118 loc) · 3.78 KB
/
push.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// +build experimental
package distribution
import (
"crypto/sha256"
"io"
"net/http"
"github.com/Sirupsen/logrus"
"github.com/docker/distribution"
"github.com/docker/distribution/digest"
"github.com/docker/distribution/manifest/schema2"
dockerdist "github.com/docker/docker/distribution"
"github.com/docker/docker/reference"
"github.com/docker/docker/registry"
"github.com/docker/engine-api/types"
"golang.org/x/net/context"
)
// Push pushes a plugin to a registry.
func Push(name string, rs registry.Service, metaHeader http.Header, authConfig *types.AuthConfig, config io.ReadCloser, layers io.ReadCloser) (digest.Digest, error) {
ref, err := reference.ParseNamed(name)
if err != nil {
return "", err
}
repoInfo, err := rs.ResolveRepository(ref)
if err != nil {
return "", err
}
if err := dockerdist.ValidateRepoName(repoInfo.Name()); err != nil {
return "", err
}
endpoints, err := rs.LookupPushEndpoints(repoInfo.Hostname())
if err != nil {
return "", err
}
var confirmedV2 bool
var repository distribution.Repository
for _, endpoint := range endpoints {
if confirmedV2 && endpoint.Version == registry.APIVersion1 {
logrus.Debugf("Skipping v1 endpoint %s because v2 registry was detected", endpoint.URL)
continue
}
repository, confirmedV2, err = dockerdist.NewV2Repository(context.Background(), repoInfo, endpoint, metaHeader, authConfig, "push", "pull")
if err != nil {
return "", err
}
if !confirmedV2 {
return "", ErrUnsupportedRegistry
}
logrus.Debugf("Trying to push %s to %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version)
// This means that we found an endpoint. and we are ready to push
break
}
// Returns a reference to the repository's blob service.
blobs := repository.Blobs(context.Background())
// Descriptor = {mediaType, size, digest}
var descs []distribution.Descriptor
for i, f := range []io.ReadCloser{config, layers} {
bw, err := blobs.Create(context.Background())
if err != nil {
logrus.Debugf("Error in blobs.Create: %v", err)
return "", err
}
h := sha256.New()
r := io.TeeReader(f, h)
_, err = io.Copy(bw, r)
if err != nil {
f.Close()
logrus.Debugf("Error in io.Copy: %v", err)
return "", err
}
f.Close()
mt := MediaTypeLayer
if i == 0 {
mt = MediaTypeConfig
}
// Commit completes the write process to the BlobService.
// The descriptor arg to Commit is called the "provisional" descriptor and
// used for validation.
// The returned descriptor should be the one used. Its called the "Canonical"
// descriptor.
desc, err := bw.Commit(context.Background(), distribution.Descriptor{
MediaType: mt,
// XXX: What about the Size?
Digest: digest.NewDigest("sha256", h),
})
if err != nil {
logrus.Debugf("Error in bw.Commit: %v", err)
return "", err
}
// The canonical descriptor is set the mediatype again, just in case.
// Don't touch the digest or the size here.
desc.MediaType = mt
logrus.Debugf("pushed blob: %s %s", desc.MediaType, desc.Digest)
descs = append(descs, desc)
}
// XXX: schema2.Versioned needs a MediaType as well.
// "application/vnd.docker.distribution.manifest.v2+json"
m, err := schema2.FromStruct(schema2.Manifest{Versioned: schema2.SchemaVersion, Config: descs[0], Layers: descs[1:]})
if err != nil {
logrus.Debugf("error in schema2.FromStruct: %v", err)
return "", err
}
msv, err := repository.Manifests(context.Background())
if err != nil {
logrus.Debugf("error in repository.Manifests: %v", err)
return "", err
}
_, pl, err := m.Payload()
if err != nil {
logrus.Debugf("error in m.Payload: %v", err)
return "", err
}
logrus.Debugf("Pushed manifest: %s", pl)
tag := DefaultTag
if tagged, ok := ref.(reference.NamedTagged); ok {
tag = tagged.Tag()
}
return msv.Put(context.Background(), m, distribution.WithTag(tag))
}