17 image "bpa-restapi-agent/internal/app"
18 minioc "bpa-restapi-agent/internal/storage"
20 "github.com/gorilla/mux"
23 // imageHandler is used to store backend implementations objects
24 // Also simplifies mocking for unit testing purposes
25 type imageHandler struct {
26 // Interface that implements Image operations
27 // We will set this variable with a mock interface for testing
28 client image.ImageManager
30 minioI minioc.MinIOInfo
31 storeName string // as minio client bucketname
34 // CreateHandler handles creation of the image entry in the database
36 func (h imageHandler) createHandler(w http.ResponseWriter, r *http.Request) {
39 // Implemenation using multipart form
40 // Review and enable/remove at a later date
41 // Set Max size to 16mb here
42 err := r.ParseMultipartForm(16777216)
44 http.Error(w, err.Error(), http.StatusUnprocessableEntity)
48 jsn := bytes.NewBuffer([]byte(r.FormValue("metadata")))
49 err = json.NewDecoder(jsn).Decode(&v)
52 http.Error(w, "Empty body", http.StatusBadRequest)
55 http.Error(w, err.Error(), http.StatusUnprocessableEntity)
60 if v.ImageName == "" {
61 http.Error(w, "Missing name in POST request", http.StatusBadRequest)
67 http.Error(w, "Missing Owner in POST request", http.StatusBadRequest)
71 if v.ImageLength == 0 {
72 e := "Improper upload length"
73 w.WriteHeader(http.StatusBadRequest)
78 //Create file directory
79 dir, err := createFileDir(v.Type)
81 log.Fatal("Error creating file server directory", err)
84 //Read the file section and ignore the header
85 file, _, err := r.FormFile("file")
87 http.Error(w, "Unable to process file", http.StatusUnprocessableEntity)
93 //Convert the file content to base64 for storage
94 content, err := ioutil.ReadAll(file)
96 http.Error(w, "Unable to read file", http.StatusUnprocessableEntity)
100 v.Config = base64.StdEncoding.EncodeToString(content)
102 ret, err := h.client.Create(v)
104 http.Error(w, err.Error(), http.StatusInternalServerError)
108 filePath := path.Join(h.dirPath, v.ImageName)
109 file1, err := os.Create(filePath)
111 e := "Error creating file in filesystem"
112 log.Printf("%s %s\n", e, err)
113 w.WriteHeader(http.StatusInternalServerError)
119 w.Header().Set("Content-Type", "application/json")
120 w.WriteHeader(http.StatusCreated)
121 err = json.NewEncoder(w).Encode(ret)
123 http.Error(w, err.Error(), http.StatusInternalServerError)
130 func createFileDir(dirName string) (string, error) {
131 u, err := user.Current()
133 log.Println("Error while fetching user home directory", err)
137 dirPath := path.Join(home, "images", dirName)
138 err = os.MkdirAll(dirPath, 0744)
140 log.Println("Error while creating file server directory", err)
146 // getHandler handles GET operations on a particular name
148 func (h imageHandler) getHandler(w http.ResponseWriter, r *http.Request) {
150 // ownerName := vars["owner"]
151 // clusterName := vars["clustername"]
152 imageName := vars["imgname"]
154 ret, err := h.client.Get(imageName)
156 http.Error(w, err.Error(), http.StatusInternalServerError)
160 w.Header().Set("Content-Type", "application/json")
161 w.WriteHeader(http.StatusOK)
162 err = json.NewEncoder(w).Encode(ret)
164 http.Error(w, err.Error(), http.StatusInternalServerError)
169 // deleteHandler handles DELETE operations on a particular record
170 func (h imageHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
172 // ownerName := vars["owner"]
173 // clusterName := vars["clustername"]
174 imageName := vars["imgname"]
176 err := h.client.Delete(imageName)
178 http.Error(w, err.Error(), http.StatusInternalServerError)
182 h.minioI.DeleteImage(h.storeName, imageName)
184 w.WriteHeader(http.StatusNoContent)
187 // UpdateHandler handles Update operations on a particular image
188 func (h imageHandler) updateHandler(w http.ResponseWriter, r *http.Request) {
191 imageName := vars["imgname"]
193 err := r.ParseMultipartForm(16777216)
195 http.Error(w, err.Error(), http.StatusUnprocessableEntity)
199 jsn := bytes.NewBuffer([]byte(r.FormValue("metadata")))
200 err = json.NewDecoder(jsn).Decode(&v)
203 http.Error(w, "Empty body", http.StatusBadRequest)
206 http.Error(w, err.Error(), http.StatusUnprocessableEntity)
211 if v.ImageName == "" {
212 http.Error(w, "Missing name in PUT request", http.StatusBadRequest)
216 // Owner is required.
218 http.Error(w, "Missing Owner in PUT request", http.StatusBadRequest)
222 //Read the file section and ignore the header
223 file, _, err := r.FormFile("file")
225 http.Error(w, "Unable to process file", http.StatusUnprocessableEntity)
231 //Convert the file content to base64 for storage
232 content, err := ioutil.ReadAll(file)
234 http.Error(w, "Unable to read file", http.StatusUnprocessableEntity)
238 v.Config = base64.StdEncoding.EncodeToString(content)
240 ret, err := h.client.Update(imageName, v)
242 http.Error(w, err.Error(), http.StatusInternalServerError)
246 w.Header().Set("Content-Type", "application/json")
247 w.WriteHeader(http.StatusCreated)
248 err = json.NewEncoder(w).Encode(ret)
250 http.Error(w, err.Error(), http.StatusInternalServerError)
255 // File upload is handled by the patchHandler
257 func (h imageHandler) patchHandler(w http.ResponseWriter, r *http.Request) {
258 log.Println("going to patch file")
260 imageName := vars["imgname"]
261 file, err := h.client.Get(imageName)
263 http.Error(w, err.Error(), http.StatusInternalServerError)
266 if *file.UploadComplete == true {
267 e := "Upload already completed"
268 w.WriteHeader(http.StatusUnprocessableEntity)
270 log.Println("Upload already completed")
273 off, err := strconv.Atoi(r.Header.Get("Upload-Offset"))
275 log.Println("Improper upload offset", err)
276 w.WriteHeader(http.StatusBadRequest)
279 log.Printf("Upload offset %d\n", off)
280 if *file.ImageOffset != off {
281 e := fmt.Sprintf("Expected Offset %d, actual offset %d", *file.ImageOffset, off)
282 w.WriteHeader(http.StatusConflict)
284 log.Printf("Expected Offset:%d doesn't match got offset:%d\n", *file.ImageOffset, off)
288 log.Println("Content length is", r.Header.Get("Content-Length"))
289 clh := r.Header.Get("Content-Length")
290 cl, err := strconv.Atoi(clh)
292 log.Println("unknown content length")
293 w.WriteHeader(http.StatusInternalServerError)
297 if cl != (file.ImageLength - *file.ImageOffset) {
298 e := fmt.Sprintf("Content length doesn't match upload length. Expected content length %d got %d", file.ImageLength-*file.ImageOffset, cl)
300 w.WriteHeader(http.StatusBadRequest)
305 body, err := ioutil.ReadAll(r.Body)
307 log.Printf("Received file partially %s\n", err)
308 log.Println("Size of received file ", len(body))
311 u, err := user.Current()
313 log.Println("Error while fetching user home directory", err)
317 dir := path.Join(home, "images", file.Type)
319 fp := fmt.Sprintf("%s/%s", h.dirPath, imageName)
320 f, err := os.OpenFile(fp, os.O_APPEND|os.O_WRONLY, 0644)
322 log.Printf("unable to open file %s\n", err)
323 w.WriteHeader(http.StatusInternalServerError)
328 n, err := f.WriteAt(body, int64(off))
330 log.Printf("unable to write %s", err)
331 w.WriteHeader(http.StatusInternalServerError)
335 log.Printf("Start to Patch image, bucket: %s, image: %s, dirpath: %s, offset: %d, n: %d\n",
336 h.storeName, imageName, fp, *file.ImageOffset, n)
337 uploadbytes, err := h.minioI.PatchImage(h.storeName, imageName, fp, int64(*file.ImageOffset), int64(n))
338 if err != nil || uploadbytes == 0 {
339 log.Printf("MinIO upload with offset %d failed: %s", *file.ImageOffset, err)
340 w.WriteHeader(http.StatusInternalServerError)
344 log.Println("number of bytes written ", n)
345 no := *file.ImageOffset + n
346 file.ImageOffset = &no
348 uo := strconv.Itoa(*file.ImageOffset)
349 w.Header().Set("Upload-Offset", uo)
350 if *file.ImageOffset == file.ImageLength {
351 log.Println("upload completed successfully")
352 *file.UploadComplete = true
355 // err = h.updateFile(file)
357 // log.Println("Error while updating file", err)
358 // w.WriteHeader(http.StatusInternalServerError)
361 w.WriteHeader(http.StatusNoContent)