package operator import ( "bytes" "context" "fmt" "io" "net/http" "strconv" "mstore/app/descr" ocidigest "github.com/opencontainers/go-digest" ) const ( ddmMimeType = "application/vnd.docker.distribution.manifest.v2+json" oimMimeType = "application/vnd.oci.image.manifest.v1+json" ) type ManifestExistsParams struct { Name string Reference string } type ManifestExistsResult struct { ContentLength string ContentType string DockerContentDigest string Exists bool } func (oper *Operator) ManifestExists(ctx context.Context, params *ManifestExistsParams) (*ManifestExistsResult, int, error) { var err error res := &ManifestExistsResult{} if params.Name == "" { err = fmt.Errorf("Empty name") return res, http.StatusBadRequest, err } if params.Reference == "" { err = fmt.Errorf("Empty reference") return res, http.StatusBadRequest, err } oper.logg.Debugf("Head manifest [%s:%s]", params.Name, params.Reference) var manifest descr.Manifest var exists bool if stringLikeSHA256Digest(params.Reference) { digest := fmt.Sprintf("%s:%s", sha256prefix, params.Reference) oper.logg.Debugf("Find manifest %s by digest %s", params.Name, params.Reference) exists, manifest, err = oper.mdb.GetManifestByDigest(ctx, params.Name, digest) if err != nil { return res, http.StatusInternalServerError, err } if !exists { return res, http.StatusNotFound, err } } else { oper.logg.Debugf("Find manifest %s by reference %s", params.Name, params.Reference) exists, manifest, err = oper.mdb.GetManifestByReference(ctx, params.Name, params.Reference) if err != nil { return res, http.StatusInternalServerError, err } if !exists { return res, http.StatusNotFound, err } } digest := ocidigest.SHA256.FromString(manifest.Payload) payloadSize := len(manifest.Payload) res.ContentLength = strconv.FormatInt(int64(payloadSize), 10) res.ContentType = manifest.ContentType res.DockerContentDigest = digest.String() res.Exists = exists return res, http.StatusOK, err } type PutManifestParams struct { ContentType string Name string Reference string Reader io.Reader } type PutManifestResult struct { Location string } // TODO: control size 413 Payload Too Large func (oper *Operator) PutManifest(ctx context.Context, params *PutManifestParams) (*PutManifestResult, int, error) { var err error res := &PutManifestResult{} oper.logg.Debugf("Put manifest %s:%s", params.Name, params.Reference) // Check Content-Type if params.ContentType != ddmMimeType && params.ContentType != oimMimeType { err = fmt.Errorf("Unknown or empty Content-Type: %s", params.ContentType) return res, http.StatusNotFound, err } // Copy manifest data buffer := bytes.NewBuffer(nil) _, err = io.Copy(buffer, params.Reader) if err != nil { return res, http.StatusInternalServerError, err } inBytes := buffer.Bytes() oper.logg.Debugf("Manifest data: [%s]", string(inBytes)) res.Location = fmt.Sprintf(`/v2/%s/manifests/%s`, params.Name, params.Reference) return res, http.StatusCreated, err }