fa7f6975b0188dde9f1b1541beb9b352012b6fc9
[icn.git] / cmd / bpa-restapi-agent / api / imagehandler.go
1 package api
2
3 import (
4         "bytes"
5         //"encoding/base64"
6         "encoding/json"
7         "fmt"
8         "io"
9         "io/ioutil"
10         "net/http"
11         "os"
12         "log"
13         "strconv"
14
15         image "bpa-restapi-agent/internal/app"
16
17         "github.com/gorilla/mux"
18 )
19
20 // imageHandler is used to store backend implementations objects
21 // Also simplifies mocking for unit testing purposes
22 type imageHandler struct {
23         // Interface that implements Image operations
24         // We will set this variable with a mock interface for testing
25         client image.ImageManager
26         dirPath string
27 }
28
29 // CreateHandler handles creation of the image entry in the database
30
31 func (h imageHandler) createHandler(w http.ResponseWriter, r *http.Request) {
32         var v image.Image
33
34         // Implemenation using multipart form
35         // Review and enable/remove at a later date
36         // Set Max size to 16mb here
37         err := r.ParseMultipartForm(16777216)
38         if err != nil {
39                 http.Error(w, err.Error(), http.StatusUnprocessableEntity)
40                 return
41         }
42
43         jsn := bytes.NewBuffer([]byte(r.FormValue("metadata")))
44         err = json.NewDecoder(jsn).Decode(&v)
45         switch {
46         case err == io.EOF:
47                 http.Error(w, "Empty body", http.StatusBadRequest)
48                 return
49         case err != nil:
50                 http.Error(w, err.Error(), http.StatusUnprocessableEntity)
51                 return
52         }
53
54         // Name is required.
55         if v.ImageName == "" {
56                 http.Error(w, "Missing name in POST request", http.StatusBadRequest)
57                 return
58         }
59
60         // Owner is required.
61         if v.Owner == "" {
62                 http.Error(w, "Missing Owner in POST request", http.StatusBadRequest)
63                 return
64         }
65
66         if v.ImageLength == 0 {
67                 e := "Improper upload length"
68                 w.WriteHeader(http.StatusBadRequest)
69                 w.Write([]byte(e))
70                 return
71         }
72
73         //Read the file section and ignore the header
74         file, _, err := r.FormFile("file")
75         if err != nil {
76                 http.Error(w, "Unable to process file", http.StatusUnprocessableEntity)
77                 return
78         }
79
80         defer file.Close()
81
82
83         ret, err := h.client.Create(v)
84         if err != nil {
85                 http.Error(w, err.Error(), http.StatusInternalServerError)
86                 return
87         }
88
89         w.Header().Set("Content-Type", "application/json")
90         w.WriteHeader(http.StatusCreated)
91         err = json.NewEncoder(w).Encode(ret)
92         if err != nil {
93                 http.Error(w, err.Error(), http.StatusInternalServerError)
94                 return
95         }
96 }
97
98
99 // getHandler handles GET operations on a particular name
100 // Returns an Image
101 func (h imageHandler) getHandler(w http.ResponseWriter, r *http.Request) {
102         vars := mux.Vars(r)
103         imageName := vars["imgname"]
104
105         ret, err := h.client.Get(imageName)
106         if err != nil {
107                 http.Error(w, err.Error(), http.StatusInternalServerError)
108                 return
109         }
110
111         w.Header().Set("Content-Type", "application/json")
112         w.WriteHeader(http.StatusOK)
113         err = json.NewEncoder(w).Encode(ret)
114         if err != nil {
115                 http.Error(w, err.Error(), http.StatusInternalServerError)
116                 return
117         }
118 }
119
120 // deleteHandler handles DELETE operations on a particular record
121 func (h imageHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
122         vars := mux.Vars(r)
123         imageName := vars["imgname"]
124
125         err := h.client.Delete(imageName)
126         if err != nil {
127                 http.Error(w, err.Error(), http.StatusInternalServerError)
128                 return
129         }
130
131         w.WriteHeader(http.StatusNoContent)
132 }
133
134 // UpdateHandler handles Update operations on a particular image
135 func (h imageHandler) updateHandler(w http.ResponseWriter, r *http.Request) {
136         var v image.Image
137         vars := mux.Vars(r)
138         imageName := vars["imgname"]
139
140         err := r.ParseMultipartForm(16777216)
141         if err != nil {
142                 http.Error(w, err.Error(), http.StatusUnprocessableEntity)
143                 return
144         }
145
146         jsn := bytes.NewBuffer([]byte(r.FormValue("metadata")))
147         err = json.NewDecoder(jsn).Decode(&v)
148         switch {
149         case err == io.EOF:
150                 http.Error(w, "Empty body", http.StatusBadRequest)
151                 return
152         case err != nil:
153                 http.Error(w, err.Error(), http.StatusUnprocessableEntity)
154                 return
155         }
156
157         // Name is required.
158         if v.ImageName == "" {
159                 http.Error(w, "Missing name in PUT request", http.StatusBadRequest)
160                 return
161         }
162
163         // Owner is required.
164         if v.Owner == "" {
165                 http.Error(w, "Missing Owner in PUT request", http.StatusBadRequest)
166                 return
167         }
168
169         //Read the file section and ignore the header
170         file, _, err := r.FormFile("file")
171         if err != nil {
172                 http.Error(w, "Unable to process file", http.StatusUnprocessableEntity)
173                 return
174         }
175
176         defer file.Close()
177
178         ret, err := h.client.Update(imageName, v)
179         if err != nil {
180                 http.Error(w, err.Error(), http.StatusInternalServerError)
181                 return
182         }
183
184         w.Header().Set("Content-Type", "application/json")
185         w.WriteHeader(http.StatusCreated)
186         err = json.NewEncoder(w).Encode(ret)
187         if err != nil {
188                 http.Error(w, err.Error(), http.StatusInternalServerError)
189                 return
190         }
191 }
192
193 // File upload is handled by the patchHandler
194
195 func (h imageHandler) patchHandler(w http.ResponseWriter, r *http.Request) {
196         log.Println("going to patch file")
197         vars := mux.Vars(r)
198         imageName := vars["imgname"]
199         file, err := h.client.Get(imageName)
200         if err != nil {
201                 http.Error(w, err.Error(), http.StatusInternalServerError)
202                 return
203         }
204         if *file.UploadComplete == true {
205                 e := "Upload already completed"
206                 w.WriteHeader(http.StatusUnprocessableEntity)
207                 w.Write([]byte(e))
208                 return
209         }
210         off, err := strconv.Atoi(r.Header.Get("Upload-Offset"))
211         if err != nil {
212                 log.Println("Improper upload offset", err)
213                 w.WriteHeader(http.StatusBadRequest)
214                 return
215         }
216         log.Printf("Upload offset %d\n", off)
217         if *file.ImageOffset != off {
218                 e := fmt.Sprintf("Expected Offset %d got offset %d", *file.ImageOffset, off)
219                 w.WriteHeader(http.StatusConflict)
220                 w.Write([]byte(e))
221                 return
222         }
223
224         log.Println("Content length is", r.Header.Get("Content-Length"))
225         clh := r.Header.Get("Content-Length")
226         cl, err := strconv.Atoi(clh)
227         if err != nil {
228                 log.Println("unknown content length")
229                 w.WriteHeader(http.StatusInternalServerError)
230                 return
231         }
232
233         if cl != (file.ImageLength - *file.ImageOffset) {
234                 e := fmt.Sprintf("Content length doesn't match upload length. Expected content length %d got %d", file.ImageLength-*file.ImageOffset, cl)
235                 log.Println(e)
236                 w.WriteHeader(http.StatusBadRequest)
237                 w.Write([]byte(e))
238                 return
239         }
240
241         body, err := ioutil.ReadAll(r.Body)
242         if err != nil {
243                 log.Printf("Received file partially %s\n", err)
244                 log.Println("Size of received file ", len(body))
245         }
246
247         fp, _, err := h.client.GetDirPath(imageName)
248         if err != nil {
249                 log.Printf("unable to get file path %s\n", err)
250                 w.WriteHeader(http.StatusInternalServerError)
251                 return
252         }
253         f, err := os.OpenFile(fp, os.O_APPEND|os.O_WRONLY, 0644)
254         if err != nil {
255                 log.Printf("unable to open file %s\n", err)
256                 w.WriteHeader(http.StatusInternalServerError)
257                 return
258         }
259         defer f.Close()
260
261         n, err := f.WriteAt(body, int64(off))
262         if err != nil {
263                 log.Printf("unable to write %s", err)
264                 w.WriteHeader(http.StatusInternalServerError)
265                 return
266         }
267         log.Println("number of bytes written ", n)
268         no := *file.ImageOffset + n
269         file.ImageOffset = &no
270
271         uo := strconv.Itoa(*file.ImageOffset)
272         w.Header().Set("Upload-Offset", uo)
273         if *file.ImageOffset == file.ImageLength {
274                 log.Println("upload completed successfully")
275                 *file.UploadComplete = true
276         }
277
278         _, err = h.client.Update(imageName, file)
279         if err != nil {
280                 log.Println("Error while updating file", err)
281                 w.WriteHeader(http.StatusInternalServerError)
282                 return
283         }
284         w.WriteHeader(http.StatusNoContent)
285
286         return
287
288 }