| // +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 { |
| 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. |
| // Dont 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)) |
| } |