cuelabs.dev/go/oci/ociregistry@v0.0.0-20240906074133-82eb438dd565/internal/ocirequest/create.go (about) 1 // Copyright 2023 CUE Labs AG 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package ocirequest 16 17 import ( 18 "encoding/base64" 19 "fmt" 20 "net/url" 21 ) 22 23 func (req *Request) Construct() (method string, ustr string, err error) { 24 method, ustr = req.construct() 25 u, err := url.Parse(ustr) 26 if err != nil { 27 return "", "", fmt.Errorf("invalid OCI request: %v", err) 28 } 29 if _, err := Parse(method, u); err != nil { 30 return "", "", fmt.Errorf("invalid OCI request: %v", err) 31 } 32 return method, ustr, nil 33 } 34 35 func (req *Request) MustConstruct() (method string, ustr string) { 36 method, ustr, err := req.Construct() 37 if err != nil { 38 panic(err) 39 } 40 return method, ustr 41 } 42 43 func (req *Request) construct() (method string, url string) { 44 switch req.Kind { 45 case ReqPing: 46 return "GET", "/v2/" 47 case ReqBlobGet: 48 return "GET", "/v2/" + req.Repo + "/blobs/" + req.Digest 49 case ReqBlobHead: 50 return "HEAD", "/v2/" + req.Repo + "/blobs/" + req.Digest 51 case ReqBlobDelete: 52 return "DELETE", "/v2/" + req.Repo + "/blobs/" + req.Digest 53 case ReqBlobStartUpload: 54 return "POST", "/v2/" + req.Repo + "/blobs/uploads/" 55 case ReqBlobUploadBlob: 56 return "POST", "/v2/" + req.Repo + "/blobs/uploads/?digest=" + req.Digest 57 case ReqBlobMount: 58 return "POST", "/v2/" + req.Repo + "/blobs/uploads/?mount=" + req.Digest + "&from=" + req.FromRepo 59 case ReqBlobUploadInfo: 60 // Note: this is specific to the ociserver implementation. 61 return "GET", req.uploadPath() 62 case ReqBlobUploadChunk: 63 // Note: this is specific to the ociserver implementation. 64 return "PATCH", req.uploadPath() 65 case ReqBlobCompleteUpload: 66 // Note: this is specific to the ociserver implementation. 67 // TODO this is bogus when the upload ID contains query parameters. 68 return "PUT", req.uploadPath() + "?digest=" + req.Digest 69 case ReqManifestGet: 70 return "GET", "/v2/" + req.Repo + "/manifests/" + req.tagOrDigest() 71 case ReqManifestHead: 72 return "HEAD", "/v2/" + req.Repo + "/manifests/" + req.tagOrDigest() 73 case ReqManifestPut: 74 return "PUT", "/v2/" + req.Repo + "/manifests/" + req.tagOrDigest() 75 case ReqManifestDelete: 76 return "DELETE", "/v2/" + req.Repo + "/manifests/" + req.tagOrDigest() 77 case ReqTagsList: 78 return "GET", "/v2/" + req.Repo + "/tags/list" + req.listParams() 79 case ReqReferrersList: 80 return "GET", "/v2/" + req.Repo + "/referrers/" + req.Digest 81 case ReqCatalogList: 82 return "GET", "/v2/_catalog" + req.listParams() 83 default: 84 panic("invalid request kind") 85 } 86 } 87 88 func (req *Request) uploadPath() string { 89 return "/v2/" + req.Repo + "/blobs/uploads/" + base64.RawURLEncoding.EncodeToString([]byte(req.UploadID)) 90 } 91 92 func (req *Request) listParams() string { 93 q := make(url.Values) 94 if req.ListN >= 0 { 95 q.Set("n", fmt.Sprint(req.ListN)) 96 } 97 if req.ListLast != "" { 98 q.Set("last", req.ListLast) 99 } 100 if len(q) > 0 { 101 return "?" + q.Encode() 102 } 103 return "" 104 } 105 106 func (req *Request) tagOrDigest() string { 107 if req.Tag != "" { 108 return req.Tag 109 } 110 return req.Digest 111 }