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