14 image "bpa-restapi-agent/internal/app"
15 minioc "bpa-restapi-agent/internal/storage"
17 "github.com/gorilla/mux"
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
27 minioI minioc.MinIOInfo
28 storeName string // as minio client bucketname
31 // CreateHandler handles creation of the image entry in the database
33 func (h imageHandler) createHandler(w http.ResponseWriter, r *http.Request) {
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)
41 http.Error(w, err.Error(), http.StatusUnprocessableEntity)
45 jsn := bytes.NewBuffer([]byte(r.FormValue("metadata")))
46 err = json.NewDecoder(jsn).Decode(&v)
49 http.Error(w, "Empty body", http.StatusBadRequest)
52 http.Error(w, err.Error(), http.StatusUnprocessableEntity)
57 if v.ImageName == "" {
58 http.Error(w, "Missing name in POST request", http.StatusBadRequest)
64 http.Error(w, "Missing Owner in POST request", http.StatusBadRequest)
68 if v.ImageLength == 0 {
69 e := "Improper upload length"
70 w.WriteHeader(http.StatusBadRequest)
75 //Read the file section and ignore the header
76 file, _, err := r.FormFile("file")
78 http.Error(w, "Unable to process file", http.StatusUnprocessableEntity)
85 ret, err := h.client.Create(v)
87 http.Error(w, err.Error(), http.StatusInternalServerError)
91 w.Header().Set("Content-Type", "application/json")
92 w.WriteHeader(http.StatusCreated)
93 err = json.NewEncoder(w).Encode(ret)
95 http.Error(w, err.Error(), http.StatusInternalServerError)
100 // getHandler handles GET operations on a particular name
102 func (h imageHandler) getHandler(w http.ResponseWriter, r *http.Request) {
104 imageName := vars["imgname"]
106 ret, err := h.client.Get(imageName)
108 http.Error(w, err.Error(), http.StatusInternalServerError)
112 w.Header().Set("Content-Type", "application/json")
113 w.WriteHeader(http.StatusOK)
114 err = json.NewEncoder(w).Encode(ret)
116 http.Error(w, err.Error(), http.StatusInternalServerError)
121 // deleteHandler handles DELETE operations on a particular record
122 func (h imageHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
124 imageName := vars["imgname"]
126 err := h.client.Delete(imageName)
128 http.Error(w, err.Error(), http.StatusInternalServerError)
132 h.minioI.DeleteImage(h.storeName, imageName)
134 w.WriteHeader(http.StatusNoContent)
137 // UpdateHandler handles Update operations on a particular image
138 func (h imageHandler) updateHandler(w http.ResponseWriter, r *http.Request) {
141 imageName := vars["imgname"]
143 err := r.ParseMultipartForm(16777216)
145 http.Error(w, err.Error(), http.StatusUnprocessableEntity)
149 jsn := bytes.NewBuffer([]byte(r.FormValue("metadata")))
150 err = json.NewDecoder(jsn).Decode(&v)
153 http.Error(w, "Empty body", http.StatusBadRequest)
156 http.Error(w, err.Error(), http.StatusUnprocessableEntity)
161 if v.ImageName == "" {
162 http.Error(w, "Missing name in PUT request", http.StatusBadRequest)
166 // Owner is required.
168 http.Error(w, "Missing Owner in PUT request", http.StatusBadRequest)
172 //Read the file section and ignore the header
173 file, _, err := r.FormFile("file")
175 http.Error(w, "Unable to process file", http.StatusUnprocessableEntity)
181 ret, err := h.client.Update(imageName, v)
183 http.Error(w, err.Error(), http.StatusInternalServerError)
187 w.Header().Set("Content-Type", "application/json")
188 w.WriteHeader(http.StatusCreated)
189 err = json.NewEncoder(w).Encode(ret)
191 http.Error(w, err.Error(), http.StatusInternalServerError)
196 // File upload is handled by the patchHandler
198 func (h imageHandler) patchHandler(w http.ResponseWriter, r *http.Request) {
199 log.Println("going to patch file")
201 imageName := vars["imgname"]
202 file, err := h.client.Get(imageName)
204 http.Error(w, err.Error(), http.StatusInternalServerError)
207 if *file.UploadComplete == true {
208 e := "Upload already completed"
209 w.WriteHeader(http.StatusUnprocessableEntity)
211 log.Println("Upload already completed")
214 off, err := strconv.Atoi(r.Header.Get("Upload-Offset"))
216 log.Println("Improper upload offset", err)
217 w.WriteHeader(http.StatusBadRequest)
220 log.Printf("Upload offset %d\n", off)
221 if *file.ImageOffset != off {
222 e := fmt.Sprintf("Expected Offset %d, actual offset %d", *file.ImageOffset, off)
223 w.WriteHeader(http.StatusConflict)
225 log.Printf("Expected Offset:%d doesn't match got offset:%d\n", *file.ImageOffset, off)
229 log.Println("Content length is", r.Header.Get("Content-Length"))
230 clh := r.Header.Get("Content-Length")
231 cl, err := strconv.Atoi(clh)
233 log.Println("unknown content length")
234 w.WriteHeader(http.StatusInternalServerError)
238 if cl != (file.ImageLength - *file.ImageOffset) {
239 e := fmt.Sprintf("Content length doesn't match upload length. Expected content length %d got %d", file.ImageLength-*file.ImageOffset, cl)
241 w.WriteHeader(http.StatusBadRequest)
246 body, err := ioutil.ReadAll(r.Body)
248 log.Printf("Received file partially %s\n", err)
249 log.Println("Size of received file ", len(body))
252 fp, _, err := h.client.GetDirPath(imageName)
254 log.Printf("unable to get file path %s\n", err)
255 w.WriteHeader(http.StatusInternalServerError)
258 f, err := os.OpenFile(fp, os.O_WRONLY, 0644)
260 log.Printf("unable to open file %s\n", err)
261 w.WriteHeader(http.StatusInternalServerError)
266 n, err := f.WriteAt(body, int64(off))
268 log.Printf("unable to write %s", err)
269 w.WriteHeader(http.StatusInternalServerError)
273 log.Printf("Start to Patch image, bucket: %s, image: %s, dirpath: %s, offset: %d, n: %d\n",
274 h.storeName, imageName, fp, *file.ImageOffset, n)
275 uploadbytes, err := h.minioI.PatchImage(h.storeName, imageName, fp, int64(*file.ImageOffset), int64(n))
276 if err != nil || uploadbytes == 0 {
277 log.Printf("MinIO upload with offset %d failed: %s", *file.ImageOffset, err)
278 w.WriteHeader(http.StatusInternalServerError)
282 log.Println("number of bytes written ", n)
283 no := *file.ImageOffset + n
284 file.ImageOffset = &no
286 uo := strconv.Itoa(*file.ImageOffset)
287 w.Header().Set("Upload-Offset", uo)
288 if *file.ImageOffset == file.ImageLength {
289 log.Println("upload completed successfully")
290 *file.UploadComplete = true
293 _, err = h.client.Update(imageName, file)
295 log.Println("Error while updating file", err)
296 w.WriteHeader(http.StatusInternalServerError)
299 w.WriteHeader(http.StatusNoContent)