Adds unit tests and K8s artifacts
[icn.git] / cmd / bpa-restapi-agent / internal / app / image.go
1 package app
2
3 import (
4         "bpa-restapi-agent/internal/db"
5         "encoding/json"
6         "os"
7         "os/user"
8         "path"
9
10         pkgerrors "github.com/pkg/errors"
11 )
12
13 // Image contains the parameters needed for Image information
14 type Image struct {
15         Owner          string          `json:"owner"`
16         ClusterName    string          `json:"cluster_name"`
17         Type           string          `json:"type"`
18         ImageName      string          `json:"image_name"`
19         ImageOffset    *int            `json:"image_offset"`
20         ImageLength    int             `json:"image_length"`
21         UploadComplete *bool           `json:"upload_complete"`
22         Description    ImageRecordList `json:"description"`
23 }
24
25 type ImageRecordList struct {
26         ImageRecords []map[string]string `json:"image_records"`
27 }
28
29 // ImageKey is the key structure that is used in the database
30 type ImageKey struct {
31         ImageName string `json:"image_name"`
32 }
33
34 // We will use json marshalling to convert to string to
35 // preserve the underlying structure.
36 func (dk ImageKey) String() string {
37         out, err := json.Marshal(dk)
38         if err != nil {
39                 return ""
40         }
41
42         return string(out)
43 }
44
45 // ImageManager is an interface that exposes the Image functionality
46 type ImageManager interface {
47         Create(c Image) (Image, error)
48         Get(imageName string) (Image, error)
49         Delete(imageName string) error
50         Update(imageName string, c Image) (Image, error)
51         GetImageRecordByName(imgname, imageName string) (map[string]string, error)
52         GetDirPath(imageName string) (string, string, error)
53 }
54
55 // Interface to aid unit test by mocking third party packages
56 type Utility interface {
57         GetCurrentUser() (*user.User, error)
58         DBCreate(storeName string, key ImageKey, meta string, c Image) error
59         DBRead(storeName string, key ImageKey, meta string) ([]byte, error)
60         DBUnmarshal(value []byte) (Image, error)
61         OSMakeDir(dirpath string, perm int) error
62         OSCreateFile(filePath string) error
63         GetPath(user *user.User, imageName string, storeName string) (string, string)
64         DBDelete(storeName string, key ImageKey, meta string) error
65         OSRemove(filePath string) error
66         DBUpdate(storeName string, key ImageKey, tagMeta string, c Image) error
67 }
68
69 // ImageClient implements the ImageManager
70 // It will also be used to maintain some localized state
71 type ImageClient struct {
72         util      Utility
73         storeName string
74         tagMeta   string
75 }
76
77 type DBService struct {
78 }
79
80 // To Do - Fix repetition in
81 // NewImageClient returns an instance of the ImageClient
82 // which implements the ImageManager
83 func NewBinaryImageClient() *ImageClient {
84     service := DBService{}
85         return &ImageClient{
86         util: service,
87                 storeName: "binary_images",
88                 tagMeta:   "metadata",
89         }
90 }
91
92 func NewContainerImageClient() *ImageClient {
93     service := DBService{}
94         return &ImageClient{
95         util: service,
96                 storeName: "container_images",
97                 tagMeta:   "metadata",
98         }
99 }
100
101 func NewOSImageClient() *ImageClient {
102     service := DBService{}
103         return &ImageClient{
104         util: service,
105                 storeName: "os_images",
106                 tagMeta:   "metadata",
107         }
108 }
109
110 // Create an entry for the Image resource in the database`
111 func (v *ImageClient) Create(c Image) (Image, error) {
112         //Construct composite key consisting of name
113         key := ImageKey{
114                 ImageName: c.ImageName,
115         }
116
117         //Check if this Image already exists
118         _, err := v.Get(c.ImageName)
119         if err == nil {
120                 return Image{}, pkgerrors.New("Image already exists")
121         }
122
123         err = v.util.DBCreate(v.storeName, key, v.tagMeta, c)
124         if err != nil {
125                 return Image{}, pkgerrors.Wrap(err, "Creating DB Entry")
126         }
127
128         err = v.CreateFile(c)
129         if err != nil {
130                 return Image{}, pkgerrors.Wrap(err, "Creating File in FS")
131         }
132
133         return c, nil
134 }
135
136 func (d DBService) DBCreate(storeName string, key ImageKey, meta string, c Image) error {
137
138         //Construct composite key consisting of name
139         err := db.DBconn.Create(storeName, key, meta, c)
140         if err != nil {
141                 return pkgerrors.Wrap(err, "Creating DB Entry")
142         }
143
144         return nil
145 }
146
147 // Create file
148
149 func (v *ImageClient) CreateFile(c Image) error {
150         filePath, dirPath, err := v.GetDirPath(c.ImageName)
151         if err != nil {
152                 return pkgerrors.Wrap(err, "Get file path")
153         }
154         err = v.util.OSMakeDir(dirPath, 0744)
155         if err != nil {
156                 return pkgerrors.Wrap(err, "Make image directory")
157         }
158         err = v.util.OSCreateFile(filePath)
159         if err != nil {
160                 return pkgerrors.Wrap(err, "Create image file")
161         }
162
163         return nil
164 }
165
166 func (d DBService) OSMakeDir(dirPath string, perm int) error {
167         err := os.MkdirAll(dirPath, 0744)
168         if err != nil {
169                 return pkgerrors.Wrap(err, "Make image directory")
170         }
171         return nil
172 }
173
174 func (d DBService) OSCreateFile(filePath string) error {
175         file1, err := os.Create(filePath)
176         if err != nil {
177                 return pkgerrors.Wrap(err, "Create image file")
178         }
179         defer file1.Close()
180
181         return nil
182 }
183
184 // Get returns Image for corresponding to name
185 func (v *ImageClient) Get(imageName string) (Image, error) {
186
187         //Construct the composite key to select the entry
188         key := ImageKey{
189                 // Owner:       ownerName,
190                 // ClusterName: clusterName,
191                 ImageName: imageName,
192         }
193
194         value, err := v.util.DBRead(v.storeName, key, v.tagMeta)
195         if err != nil {
196                 return Image{}, pkgerrors.Wrap(err, "Get Image")
197         }
198
199         //value is a byte array
200         if value != nil {
201                 c, err := v.util.DBUnmarshal(value)
202                 if err != nil {
203                         return Image{}, pkgerrors.Wrap(err, "Unmarshaling Value")
204                 }
205                 return c, nil
206         }
207
208         return Image{}, pkgerrors.New("Error getting Connection")
209 }
210
211 func (d DBService) DBRead(storeName string, key ImageKey, meta string) ([]byte, error) {
212         value, err := db.DBconn.Read(storeName, key, meta)
213         if err != nil {
214                 return []byte{}, pkgerrors.Wrap(err, "Get Image")
215         }
216
217         return value, nil
218 }
219
220 func (d DBService) DBUnmarshal(value []byte) (Image, error) {
221         c := Image{}
222         err := db.DBconn.Unmarshal(value, &c)
223         if err != nil {
224                 return Image{}, pkgerrors.Wrap(err, "Unmarshaling Value")
225         }
226
227         return c, nil
228 }
229
230 func (v *ImageClient) GetImageRecordByName(imgName string,
231         imageRecordName string) (map[string]string, error) {
232
233         img, err := v.Get(imgName)
234         if err != nil {
235                 return nil, pkgerrors.Wrap(err, "Error getting image")
236         }
237
238         for _, value := range img.Description.ImageRecords {
239                 if imageRecordName == value["image_record_name"] {
240                         return value, nil
241                 }
242         }
243
244         return nil, pkgerrors.New("Image record " + imageRecordName + " not found")
245 }
246
247 func (v *ImageClient) GetDirPath(imageName string) (string, string, error) {
248         u, err := v.util.GetCurrentUser()
249         filePath, dirPath := v.util.GetPath(u, imageName, v.storeName)
250
251         return filePath, dirPath, err
252 }
253
254 // Delete the Image from database
255 func (v *ImageClient) Delete(imageName string) error {
256
257         //Construct the composite key to select the entry
258         key := ImageKey{
259                 // Owner:       ownerName,
260                 // ClusterName: clusterName,
261                 ImageName: imageName,
262         }
263         err := v.util.DBDelete(v.storeName, key, v.tagMeta)
264
265         //Delete image from FS
266         filePath, _, err := v.GetDirPath(imageName)
267         if err != nil {
268                 return pkgerrors.Wrap(err, "Get file path")
269         }
270         err = v.util.OSRemove(filePath)
271
272         return nil
273 }
274
275 func (d DBService) OSRemove(filePath string) error {
276         err := os.Remove(filePath)
277         if err != nil {
278                 return pkgerrors.Wrap(err, "Delete image file")
279         }
280
281         return nil
282 }
283
284 func (d DBService) DBDelete(storeName string, key ImageKey, tagMeta string) error {
285         err := db.DBconn.Delete(storeName, key, tagMeta)
286         if err != nil {
287                 return pkgerrors.Wrap(err, "Delete Image")
288         }
289
290         return nil
291 }
292
293 // Update an entry for the image in the database
294 func (v *ImageClient) Update(imageName string, c Image) (Image, error) {
295
296         key := ImageKey{
297                 ImageName: imageName,
298         }
299
300         //Check if this Image exists
301         _, err := v.Get(imageName)
302         if err != nil {
303                 return Image{}, pkgerrors.New("Update Error - Image doesn't exist")
304         }
305
306         err = v.util.DBUpdate(v.storeName, key, v.tagMeta, c)
307
308         return c, nil
309 }
310
311 func (d DBService) DBUpdate(storeName string, key ImageKey, tagMeta string, c Image) error {
312         err := db.DBconn.Update(storeName, key, tagMeta, c)
313         if err != nil {
314                 return pkgerrors.Wrap(err, "Updating DB Entry")
315         }
316
317         return nil
318 }
319
320 // Define GetCurrentUser
321 func (d DBService) GetCurrentUser() (*user.User, error) {
322         u, err := user.Current()
323         if err != nil {
324                 return nil, pkgerrors.Wrap(err, "Current user")
325         }
326
327         return u, nil
328 }
329
330 func (d DBService) GetPath(user *user.User, imageName string, storeName string) (string, string) {
331         home := user.HomeDir
332         dirPath := path.Join(home, "images", storeName)
333         filePath := path.Join(dirPath, imageName)
334
335         return filePath, dirPath
336 }