|
5 | 5 | package lfsclient
|
6 | 6 |
|
7 | 7 | import (
|
| 8 | + "bytes" |
| 9 | + "context" |
8 | 10 | "encoding/json"
|
9 |
| - "bytes" |
10 | 11 | "fmt"
|
11 | 12 | "io"
|
12 | 13 | "net/http"
|
13 |
| - "context" |
14 | 14 | "strconv"
|
15 | 15 |
|
16 | 16 | "code.gitea.io/gitea/models"
|
17 | 17 | "code.gitea.io/gitea/modules/log"
|
18 |
| - "code.gitea.io/gitea/modules/storage" |
19 | 18 | )
|
20 | 19 |
|
21 | 20 | const (
|
22 | 21 | metaMediaType = "application/vnd.git-lfs+json"
|
23 | 22 | )
|
24 | 23 |
|
| 24 | +// BatchRequest encodes json object using in a lfs batch api request |
25 | 25 | type BatchRequest struct {
|
26 | 26 | Operation string `json:"operation"`
|
27 | 27 | Transfers []string `json:"transfers,omitempty"`
|
28 |
| - Ref *Reference `json:"ref,omitempty"` |
29 |
| - Objects []*models.LFSMetaObjectBasic `json:"objects"` |
| 28 | + Ref *Reference `json:"ref,omitempty"` |
| 29 | + Objects []*models.LFSMetaObjectBasic `json:"objects"` |
30 | 30 | }
|
31 | 31 |
|
| 32 | +// Reference is a reference field of BatchRequest |
32 | 33 | type Reference struct {
|
33 |
| - Name string `json:"name"` |
| 34 | + Name string `json:"name"` |
34 | 35 | }
|
35 | 36 |
|
| 37 | +// packbatch packs lfs batch request to json encoded as bytes |
36 | 38 | func packbatch(operation string, transfers []string, ref *Reference, metaObjects []*models.LFSMetaObject) (*bytes.Buffer, error) {
|
37 |
| - metaObjectsBasic := []*models.LFSMetaObjectBasic{} |
| 39 | + metaObjectsBasic := []*models.LFSMetaObjectBasic{} |
38 | 40 | for _, meta := range metaObjects {
|
39 |
| - metaBasic := &models.LFSMetaObjectBasic{meta.Oid, meta.Size} |
| 41 | + metaBasic := &models.LFSMetaObjectBasic{Oid: meta.Oid, Size: meta.Size} |
40 | 42 | metaObjectsBasic = append(metaObjectsBasic, metaBasic)
|
41 | 43 | }
|
42 | 44 |
|
43 |
| - reqobj := &BatchRequest{operation, transfers, ref, metaObjectsBasic} |
| 45 | + reqobj := &BatchRequest{operation, transfers, ref, metaObjectsBasic} |
44 | 46 |
|
45 |
| - buf := &bytes.Buffer{} |
46 |
| - if err := json.NewEncoder(buf).Encode(reqobj); err != nil { |
47 |
| - return buf, fmt.Errorf("Failed to encode BatchRequest as json. Error: %v", err) |
| 47 | + buf := &bytes.Buffer{} |
| 48 | + if err := json.NewEncoder(buf).Encode(reqobj); err != nil { |
| 49 | + return buf, fmt.Errorf("Failed to encode BatchRequest as json. Error: %v", err) |
48 | 50 | }
|
49 |
| - return buf, nil |
| 51 | + return buf, nil |
50 | 52 | }
|
51 | 53 |
|
| 54 | +// BasicTransferAdapter makes request to lfs server and returns io.ReadCLoser |
52 | 55 | func BasicTransferAdapter(ctx context.Context, client *http.Client, href string, size int64) (io.ReadCloser, error) {
|
53 |
| - req, err := http.NewRequestWithContext(ctx, http.MethodGet, href, nil) |
54 |
| - if err != nil { |
| 56 | + req, err := http.NewRequestWithContext(ctx, http.MethodGet, href, nil) |
| 57 | + if err != nil { |
55 | 58 | return nil, err
|
56 | 59 | }
|
57 | 60 | req.Header.Set("Content-type", "application/octet-stream")
|
58 | 61 | req.Header.Set("Content-Length", strconv.Itoa(int(size)))
|
59 | 62 |
|
60 |
| - resp, err := client.Do(req) |
61 |
| - if err != nil { |
62 |
| - select { |
63 |
| - case <-ctx.Done(): |
64 |
| - return nil, ctx.Err() |
65 |
| - default: |
66 |
| - } |
67 |
| - return nil, err |
68 |
| - } |
69 |
| - defer resp.Body.Close() |
70 |
| - |
71 |
| - if resp.StatusCode != http.StatusOK { |
72 |
| - return nil, fmt.Errorf("Failed to query BasicTransferAdapter with response: %s", resp.Status) |
| 63 | + resp, err := client.Do(req) |
| 64 | + if err != nil { |
| 65 | + select { |
| 66 | + case <-ctx.Done(): |
| 67 | + return nil, ctx.Err() |
| 68 | + default: |
| 69 | + } |
| 70 | + return nil, err |
| 71 | + } |
| 72 | + defer resp.Body.Close() |
| 73 | + |
| 74 | + if resp.StatusCode != http.StatusOK { |
| 75 | + return nil, fmt.Errorf("Failed to query BasicTransferAdapter with response: %s", resp.Status) |
73 | 76 | }
|
74 |
| - return resp.Body, nil |
| 77 | + return resp.Body, nil |
75 | 78 | }
|
76 | 79 |
|
77 |
| -func FetchLFSFilesToContentStore(ctx context.Context, metaObjects []*models.LFSMetaObject, userName string, repo *models.Repository, LFSServer string) error { |
78 |
| - client := http.DefaultClient |
| 80 | +// FetchLFSFilesToContentStore downloads []LFSMetaObject from lfsServer to ContentStore |
| 81 | +func FetchLFSFilesToContentStore(ctx context.Context, metaObjects []*models.LFSMetaObject, userName string, repo *models.Repository, lfsServer string, contentStore *models.ContentStore) error { |
| 82 | + client := http.DefaultClient |
79 | 83 |
|
80 |
| - rv, err := packbatch("download", nil, nil, metaObjects) |
81 |
| - if err != nil { |
82 |
| - return err |
83 |
| - } |
84 |
| - batchAPIURL := LFSServer + "/objects/batch" |
85 |
| - req, err := http.NewRequestWithContext(ctx, http.MethodGet, batchAPIURL, rv) |
86 |
| - if err != nil { |
| 84 | + rv, err := packbatch("download", nil, nil, metaObjects) |
| 85 | + if err != nil { |
| 86 | + return err |
| 87 | + } |
| 88 | + batchAPIURL := lfsServer + "/objects/batch" |
| 89 | + req, err := http.NewRequestWithContext(ctx, http.MethodGet, batchAPIURL, rv) |
| 90 | + if err != nil { |
87 | 91 | return err
|
88 | 92 | }
|
89 | 93 | req.Header.Set("Content-type", metaMediaType)
|
90 | 94 | req.Header.Set("Accept", metaMediaType)
|
91 | 95 |
|
92 |
| - resp, err := client.Do(req) |
93 |
| - if err != nil { |
94 |
| - select { |
95 |
| - case <-ctx.Done(): |
96 |
| - return ctx.Err() |
97 |
| - default: |
98 |
| - } |
99 |
| - return err |
100 |
| - } |
101 |
| - defer resp.Body.Close() |
102 |
| - |
103 |
| - if resp.StatusCode != http.StatusOK { |
104 |
| - return fmt.Errorf("Failed to query Batch with response: %s", resp.Status) |
| 96 | + resp, err := client.Do(req) |
| 97 | + if err != nil { |
| 98 | + select { |
| 99 | + case <-ctx.Done(): |
| 100 | + return ctx.Err() |
| 101 | + default: |
| 102 | + } |
| 103 | + return err |
105 | 104 | }
|
| 105 | + defer resp.Body.Close() |
106 | 106 |
|
107 |
| - var respBatch models.BatchResponse |
108 |
| - err = json.NewDecoder(resp.Body).Decode(&respBatch) |
109 |
| - if err != nil { |
110 |
| - return err |
111 |
| - } |
| 107 | + if resp.StatusCode != http.StatusOK { |
| 108 | + return fmt.Errorf("Failed to query Batch with response: %s", resp.Status) |
| 109 | + } |
112 | 110 |
|
113 |
| - if len(respBatch.Transfer) == 0 { |
114 |
| - respBatch.Transfer = "basic" |
| 111 | + var respBatch models.BatchResponse |
| 112 | + err = json.NewDecoder(resp.Body).Decode(&respBatch) |
| 113 | + if err != nil { |
| 114 | + return err |
115 | 115 | }
|
116 | 116 |
|
117 |
| - contentStore := &models.ContentStore{ObjectStorage: storage.LFS} |
| 117 | + if len(respBatch.Transfer) == 0 { |
| 118 | + respBatch.Transfer = "basic" |
| 119 | + } |
118 | 120 |
|
119 |
| - for _, rep := range respBatch.Objects { |
120 |
| - rc, err := BasicTransferAdapter(ctx, client, rep.Actions["download"].Href, rep.Size) |
| 121 | + for _, rep := range respBatch.Objects { |
| 122 | + rc, err := BasicTransferAdapter(ctx, client, rep.Actions["download"].Href, rep.Size) |
| 123 | + if err != nil { |
| 124 | + log.Error("Unable to use BasicTransferAdapter. Error: %v", err) |
| 125 | + return err |
| 126 | + } |
| 127 | + meta, err := repo.GetLFSMetaObjectByOid(rep.Oid) |
121 | 128 | if err != nil {
|
122 |
| - log.Error("Unable to use BasicTransferAdapter. Error: %v", err) |
123 |
| - return err |
124 |
| - } |
125 |
| - meta, err := repo.GetLFSMetaObjectByOid(rep.Oid) |
126 |
| - if err != nil { |
127 |
| - log.Error("Unable to get LFS OID[%s] Error: %v", rep.Oid, err) |
128 |
| - return err |
129 |
| - } |
130 |
| - |
131 |
| - // put LFS file to contentStore |
132 |
| - exist, err := contentStore.Exists(meta) |
133 |
| - if err != nil { |
134 |
| - log.Error("Unable to check if LFS OID[%s] exist on %s/%s. Error: %v", meta.Oid, userName, repo.Name, err) |
135 |
| - return err |
136 |
| - } |
137 |
| - |
138 |
| - if exist { |
139 |
| - // remove collision |
140 |
| - if _, err := repo.RemoveLFSMetaObjectByOid(meta.Oid); err != nil { |
141 |
| - return fmt.Errorf("Error whilst removing matched LFS object %s: %v", meta.Oid, err) |
142 |
| - } |
143 |
| - } |
144 |
| - |
145 |
| - if err := contentStore.Put(meta, rc); err != nil { |
146 |
| - if _, err2 := repo.RemoveLFSMetaObjectByOid(meta.Oid); err2 != nil { |
147 |
| - return fmt.Errorf("Error whilst removing failed inserted LFS object %s: %v (Prev Error: %v)", meta.Oid, err2, err) |
148 |
| - } |
149 |
| - return err |
150 |
| - } |
151 |
| - } |
| 129 | + log.Error("Unable to get LFS OID[%s] Error: %v", rep.Oid, err) |
| 130 | + return err |
| 131 | + } |
| 132 | + |
| 133 | + // put LFS file to contentStore |
| 134 | + if err := contentStore.Put(meta, rc); err != nil { |
| 135 | + if _, err2 := repo.RemoveLFSMetaObjectByOid(meta.Oid); err2 != nil { |
| 136 | + return fmt.Errorf("Error whilst removing failed inserted LFS object %s: %v (Prev Error: %v)", meta.Oid, err2, err) |
| 137 | + } |
| 138 | + return err |
| 139 | + } |
| 140 | + } |
152 | 141 | return nil
|
153 | 142 | }
|
0 commit comments