Remove BPA operator
[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         GetPath(user *user.User, imageName string, storeName string) (string, string)
62         DBDelete(storeName string, key ImageKey, meta string) error
63         DBUpdate(storeName string, key ImageKey, tagMeta string, c Image) error
64 }
65
66 // ImageClient implements the ImageManager
67 // It will also be used to maintain some localized state
68 type ImageClient struct {
69         util      Utility
70         storeName string
71         tagMeta   string
72 }
73
74 type DBService struct {
75 }
76
77 // To Do - Fix repetition in
78 // NewImageClient returns an instance of the ImageClient
79 // which implements the ImageManager
80 func NewBinaryImageClient() *ImageClient {
81     service := DBService{}
82         return &ImageClient{
83         util: service,
84                 storeName: "binary_images",
85                 tagMeta:   "metadata",
86         }
87 }
88
89 func NewContainerImageClient() *ImageClient {
90     service := DBService{}
91         return &ImageClient{
92         util: service,
93                 storeName: "container_images",
94                 tagMeta:   "metadata",
95         }
96 }
97
98 func NewOSImageClient() *ImageClient {
99     service := DBService{}
100         return &ImageClient{
101         util: service,
102                 storeName: "os_images",
103                 tagMeta:   "metadata",
104         }
105 }
106
107 // Create an entry for the Image resource in the database`
108 func (v *ImageClient) Create(c Image) (Image, error) {
109         //Construct composite key consisting of name
110         key := ImageKey{
111                 ImageName: c.ImageName,
112         }
113
114         //Check if this Image already exists
115         _, err := v.Get(c.ImageName)
116         if err == nil {
117                 return Image{}, pkgerrors.New("Image already exists")
118         }
119
120         err = v.util.DBCreate(v.storeName, key, v.tagMeta, c)
121         if err != nil {
122                 return Image{}, pkgerrors.Wrap(err, "Creating DB Entry")
123         }
124
125         err = v.CreateFile(c)
126         if err != nil {
127                 return Image{}, pkgerrors.Wrap(err, "Creating File in FS")
128         }
129
130         return c, nil
131 }
132
133 func (d DBService) DBCreate(storeName string, key ImageKey, meta string, c Image) error {
134
135         //Construct composite key consisting of name
136         err := db.DBconn.Create(storeName, key, meta, c)
137         if err != nil {
138                 return pkgerrors.Wrap(err, "Creating DB Entry")
139         }
140
141         return nil
142 }
143
144 // Create file
145
146 func (v *ImageClient) CreateFile(c Image) error {
147         filePath, dirPath, err := v.GetDirPath(c.ImageName)
148         if err != nil {
149                 return pkgerrors.Wrap(err, "Get file path")
150         }
151         err = os.MkdirAll(dirPath, 0744)
152         if err != nil {
153                 return pkgerrors.Wrap(err, "Make image directory")
154         }
155         file, err := os.Create(filePath)
156         if err != nil {
157                 return pkgerrors.Wrap(err, "Create image file")
158         }
159     defer file.Close()
160
161         return nil
162 }
163
164 // Get returns Image for corresponding to name
165 func (v *ImageClient) Get(imageName string) (Image, error) {
166
167         //Construct the composite key to select the entry
168         key := ImageKey{
169                 // Owner:       ownerName,
170                 // ClusterName: clusterName,
171                 ImageName: imageName,
172         }
173
174         value, err := v.util.DBRead(v.storeName, key, v.tagMeta)
175         if err != nil {
176                 return Image{}, pkgerrors.Wrap(err, "Get Image")
177         }
178
179         //value is a byte array
180         if value != nil {
181                 c, err := v.util.DBUnmarshal(value)
182                 if err != nil {
183                         return Image{}, pkgerrors.Wrap(err, "Unmarshaling Value")
184                 }
185                 return c, nil
186         }
187
188         return Image{}, pkgerrors.New("Error getting Connection")
189 }
190
191 func (d DBService) DBRead(storeName string, key ImageKey, meta string) ([]byte, error) {
192         value, err := db.DBconn.Read(storeName, key, meta)
193         if err != nil {
194                 return []byte{}, pkgerrors.Wrap(err, "Get Image")
195         }
196
197         return value, nil
198 }
199
200 func (d DBService) DBUnmarshal(value []byte) (Image, error) {
201         c := Image{}
202         err := db.DBconn.Unmarshal(value, &c)
203         if err != nil {
204                 return Image{}, pkgerrors.Wrap(err, "Unmarshaling Value")
205         }
206
207         return c, nil
208 }
209
210 func (v *ImageClient) GetImageRecordByName(imgName string,
211         imageRecordName string) (map[string]string, error) {
212
213         img, err := v.Get(imgName)
214         if err != nil {
215                 return nil, pkgerrors.Wrap(err, "Error getting image")
216         }
217
218         for _, value := range img.Description.ImageRecords {
219                 if imageRecordName == value["image_record_name"] {
220                         return value, nil
221                 }
222         }
223
224         return nil, pkgerrors.New("Image record " + imageRecordName + " not found")
225 }
226
227 func (v *ImageClient) GetDirPath(imageName string) (string, string, error) {
228         u, err := v.util.GetCurrentUser()
229         filePath, dirPath := v.util.GetPath(u, imageName, v.storeName)
230
231         return filePath, dirPath, err
232 }
233
234 // Delete the Image from database
235 func (v *ImageClient) Delete(imageName string) error {
236
237         //Construct the composite key to select the entry
238         key := ImageKey{
239                 // Owner:       ownerName,
240                 // ClusterName: clusterName,
241                 ImageName: imageName,
242         }
243         err := v.util.DBDelete(v.storeName, key, v.tagMeta)
244
245         //Delete image from FS
246         filePath, _, err := v.GetDirPath(imageName)
247         if err != nil {
248                 return pkgerrors.Wrap(err, "Get file path")
249         }
250         err = os.Remove(filePath)
251     if err != nil {
252         return pkgerrors.Wrap(err, "Delete image file")
253     }
254
255         return nil
256 }
257
258 func (d DBService) DBDelete(storeName string, key ImageKey, tagMeta string) error {
259         err := db.DBconn.Delete(storeName, key, tagMeta)
260         if err != nil {
261                 return pkgerrors.Wrap(err, "Delete Image")
262         }
263
264         return nil
265 }
266
267 // Update an entry for the image in the database
268 func (v *ImageClient) Update(imageName string, c Image) (Image, error) {
269
270         key := ImageKey{
271                 ImageName: imageName,
272         }
273
274         //Check if this Image exists
275         _, err := v.Get(imageName)
276         if err != nil {
277                 return Image{}, pkgerrors.New("Update Error - Image doesn't exist")
278         }
279
280         err = v.util.DBUpdate(v.storeName, key, v.tagMeta, c)
281
282         return c, nil
283 }
284
285 func (d DBService) DBUpdate(storeName string, key ImageKey, tagMeta string, c Image) error {
286         err := db.DBconn.Update(storeName, key, tagMeta, c)
287         if err != nil {
288                 return pkgerrors.Wrap(err, "Updating DB Entry")
289         }
290
291         return nil
292 }
293
294 // Define GetCurrentUser
295 func (d DBService) GetCurrentUser() (*user.User, error) {
296         u, err := user.Current()
297         if err != nil {
298                 return nil, pkgerrors.Wrap(err, "Current user")
299         }
300
301         return u, nil
302 }
303
304 func (d DBService) GetPath(user *user.User, imageName string, storeName string) (string, string) {
305         home := user.HomeDir
306         dirPath := path.Join(home, "images", storeName)
307         filePath := path.Join(dirPath, imageName)
308
309         return filePath, dirPath
310 }