blob: 4948323adc69acb8c029390a632c50d69a150199 [file] [log] [blame]
mpagenkoc26d4c02021-05-06 14:27:57 +00001/*
2 * Copyright 2020-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//Package adaptercoreonu provides the utility for onu devices, flows and statistics
18package adaptercoreonu
19
20import (
21 "bufio"
22 "context"
23 "fmt"
24 "io"
25 "net/http"
26 "net/url"
27 "os"
Andrey Pozolotin1394a1c2021-06-01 00:54:18 +030028 "path/filepath"
mpagenkoc26d4c02021-05-06 14:27:57 +000029 "sync"
30 "time"
31
Girish Gowdra50e56422021-06-01 16:46:04 -070032 "github.com/opencord/voltha-lib-go/v5/pkg/log"
mpagenkoc26d4c02021-05-06 14:27:57 +000033)
34
35const cDefaultLocalDir = "/tmp" //this is the default local dir to download to
36
37type fileState uint32
38
39//nolint:varcheck, deadcode
40const (
41 cFileStateUnknown fileState = iota
42 cFileStateDlStarted
43 cFileStateDlSucceeded
44 cFileStateDlFailed
45 cFileStateDlAborted
mpagenkoc26d4c02021-05-06 14:27:57 +000046)
47
48type downloadImageParams struct {
mpagenko9c225032021-10-15 14:26:49 +000049 downloadImageName string
Holger Hildebrandt33f701d2021-12-22 10:07:58 +000050 downloadImageURL string
mpagenko9c225032021-10-15 14:26:49 +000051 downloadImageState fileState
52 downloadImageLen int64
Holger Hildebrandt33f701d2021-12-22 10:07:58 +000053 downloadImageCRC uint32
mpagenko9c225032021-10-15 14:26:49 +000054 downloadActive bool
55 downloadContextCancelFn context.CancelFunc
mpagenkoc26d4c02021-05-06 14:27:57 +000056}
57
58type requesterChannelMap map[chan<- bool]struct{} //using an empty structure map for easier (unique) element appending
59
60//fileDownloadManager structure holds information needed for downloading to and storing images within the adapter
61type fileDownloadManager struct {
Holger Hildebrandt33f701d2021-12-22 10:07:58 +000062 mutexFileState sync.RWMutex
mpagenkoc26d4c02021-05-06 14:27:57 +000063 mutexDownloadImageDsc sync.RWMutex
64 downloadImageDscSlice []downloadImageParams
65 dnldImgReadyWaiting map[string]requesterChannelMap
66 dlToAdapterTimeout time.Duration
67}
68
69//newFileDownloadManager constructor returns a new instance of a fileDownloadManager
70//mib_db (as well as not inluded alarm_db not really used in this code? VERIFY!!)
71func newFileDownloadManager(ctx context.Context) *fileDownloadManager {
72 logger.Debug(ctx, "init-fileDownloadManager")
73 var localDnldMgr fileDownloadManager
74 localDnldMgr.downloadImageDscSlice = make([]downloadImageParams, 0)
75 localDnldMgr.dnldImgReadyWaiting = make(map[string]requesterChannelMap)
76 localDnldMgr.dlToAdapterTimeout = 10 * time.Second //default timeout, should be overwritten immediately after start
77 return &localDnldMgr
78}
79
80//SetDownloadTimeout configures the timeout used to supervice the download of the image to the adapter (assumed in seconds)
81func (dm *fileDownloadManager) SetDownloadTimeout(ctx context.Context, aDlTimeout time.Duration) {
82 dm.mutexDownloadImageDsc.Lock()
83 defer dm.mutexDownloadImageDsc.Unlock()
84 logger.Debugw(ctx, "setting download timeout", log.Fields{"timeout": aDlTimeout})
85 dm.dlToAdapterTimeout = aDlTimeout
86}
87
88//GetDownloadTimeout delivers the timeout used to supervice the download of the image to the adapter (assumed in seconds)
89func (dm *fileDownloadManager) GetDownloadTimeout(ctx context.Context) time.Duration {
90 dm.mutexDownloadImageDsc.RLock()
91 defer dm.mutexDownloadImageDsc.RUnlock()
92 return dm.dlToAdapterTimeout
93}
94
Holger Hildebrandt33f701d2021-12-22 10:07:58 +000095//StartDownload returns FileState and error code from download request for the given file name and URL
96func (dm *fileDownloadManager) StartDownload(ctx context.Context, aImageName string, aURLCommand string) (fileState, error) {
mpagenkoc26d4c02021-05-06 14:27:57 +000097 logger.Infow(ctx, "image download-to-adapter requested", log.Fields{
98 "image-name": aImageName, "url-command": aURLCommand})
Holger Hildebrandt33f701d2021-12-22 10:07:58 +000099 // keep a semaphore over the complete method in order to avoid parallel entrance to this method
100 // otherwise a temporary file state 'Started' could be indicated allowing start of ONU upgrade handling
101 // even though the download-start to adapter may fail (e.g on wrong URL) (delivering inconsistent download results)
102 // so once called the download-start of the first call must have been completely checked before another execution
103 // could still be limited to the same imageName, but that should be no real gain
104 dm.mutexFileState.Lock()
105 defer dm.mutexFileState.Unlock()
106 dm.mutexDownloadImageDsc.Lock()
107 var fileState fileState
108 var exists bool
109 if fileState, exists = dm.imageExists(ctx, aImageName, aURLCommand); !exists {
110 loDownloadImageParams := downloadImageParams{
111 downloadImageName: aImageName, downloadImageURL: aURLCommand, downloadImageState: cFileStateDlStarted,
112 downloadImageLen: 0, downloadImageCRC: 0}
mpagenko9c225032021-10-15 14:26:49 +0000113 dm.downloadImageDscSlice = append(dm.downloadImageDscSlice, loDownloadImageParams)
114 dm.mutexDownloadImageDsc.Unlock()
Holger Hildebrandt33f701d2021-12-22 10:07:58 +0000115 //start downloading from server
116 var err error
117 if err = dm.downloadFile(ctx, aURLCommand, cDefaultLocalDir, aImageName); err == nil {
118 //indicate download started correctly, complete download may run in background
119 return cFileStateDlStarted, nil
120 }
121 //return the error result of the download-request
122 return cFileStateUnknown, err
mpagenko9c225032021-10-15 14:26:49 +0000123 }
Holger Hildebrandt33f701d2021-12-22 10:07:58 +0000124 dm.mutexDownloadImageDsc.Unlock()
125 if fileState == cFileStateUnknown {
126 //cannot simply remove the existing file here - might still be used for running upgrades on different devices!
127 // (has to be removed by operator - cancel API)
128 logger.Errorw(ctx, "image download requested for existing file with different URL",
129 log.Fields{"image-description": aImageName, "url": aURLCommand})
130 return fileState, fmt.Errorf("existing file is based on different URL, requested URL: %s", aURLCommand)
131 }
132 logger.Debugw(ctx, "image download already started or done", log.Fields{"image-description": aImageName})
133 return fileState, nil
mpagenkoc26d4c02021-05-06 14:27:57 +0000134}
135
136//GetImageBufferLen returns the length of the specified file in bytes (file size) - as detected after download
137func (dm *fileDownloadManager) GetImageBufferLen(ctx context.Context, aFileName string) (int64, error) {
138 dm.mutexDownloadImageDsc.RLock()
139 defer dm.mutexDownloadImageDsc.RUnlock()
140 for _, dnldImgDsc := range dm.downloadImageDscSlice {
141 if dnldImgDsc.downloadImageName == aFileName && dnldImgDsc.downloadImageState == cFileStateDlSucceeded {
142 //image found (by name) and fully downloaded
143 return dnldImgDsc.downloadImageLen, nil
144 }
145 }
146 return 0, fmt.Errorf("no downloaded image found: %s", aFileName)
147}
148
149//GetDownloadImageBuffer returns the content of the requested file as byte slice
Holger Hildebrandt33f701d2021-12-22 10:07:58 +0000150//precondition: it is assumed that a check is done immediately before if the file was downloaded to adapter correctly from caller
151// straightforward approach is here to e.g. immediately call and verify GetImageBufferLen() before this
mpagenkoc26d4c02021-05-06 14:27:57 +0000152func (dm *fileDownloadManager) GetDownloadImageBuffer(ctx context.Context, aFileName string) ([]byte, error) {
Andrey Pozolotin1394a1c2021-06-01 00:54:18 +0300153 file, err := os.Open(filepath.Clean(cDefaultLocalDir + "/" + aFileName))
mpagenkoc26d4c02021-05-06 14:27:57 +0000154 if err != nil {
155 return nil, err
156 }
Andrey Pozolotin1394a1c2021-06-01 00:54:18 +0300157 defer func() {
158 err := file.Close()
159 if err != nil {
160 logger.Errorw(ctx, "failed to close file", log.Fields{"error": err})
161 }
162 }()
mpagenkoc26d4c02021-05-06 14:27:57 +0000163
164 stats, statsErr := file.Stat()
165 if statsErr != nil {
166 return nil, statsErr
167 }
168
169 var size int64 = stats.Size()
170 bytes := make([]byte, size)
171
172 buffer := bufio.NewReader(file)
173 _, err = buffer.Read(bytes)
174
175 return bytes, err
176}
177
178//RequestDownloadReady receives a channel that has to be used to inform the requester in case the concerned file is downloaded
179func (dm *fileDownloadManager) RequestDownloadReady(ctx context.Context, aFileName string, aWaitChannel chan<- bool) {
mpagenko9c225032021-10-15 14:26:49 +0000180 //mutexDownloadImageDsc must already be locked here to avoid an update of the dnldImgReadyWaiting map
181 // just after returning false on imageLocallyDownloaded() (not found) and immediate handling of the
182 // download success (within updateFileState())
183 // so updateFileState() can't interfere here just after imageLocallyDownloaded() before setting the requester map
184 dm.mutexDownloadImageDsc.Lock()
185 defer dm.mutexDownloadImageDsc.Unlock()
mpagenkoc26d4c02021-05-06 14:27:57 +0000186 if dm.imageLocallyDownloaded(ctx, aFileName) {
187 //image found (by name) and fully downloaded
188 logger.Debugw(ctx, "file ready - immediate response", log.Fields{"image-name": aFileName})
189 aWaitChannel <- true
190 return
191 }
192 //when we are here the image was not yet found or not fully downloaded -
193 // add the device specific channel to the list of waiting requesters
mpagenkoc26d4c02021-05-06 14:27:57 +0000194 if loRequesterChannelMap, ok := dm.dnldImgReadyWaiting[aFileName]; ok {
195 //entry for the file name already exists
196 if _, exists := loRequesterChannelMap[aWaitChannel]; !exists {
197 // requester channel does not yet exist for the image
198 loRequesterChannelMap[aWaitChannel] = struct{}{}
199 dm.dnldImgReadyWaiting[aFileName] = loRequesterChannelMap
200 logger.Debugw(ctx, "file not ready - adding new requester", log.Fields{
201 "image-name": aFileName, "number-of-requesters": len(dm.dnldImgReadyWaiting[aFileName])})
202 }
203 } else {
204 //entry for the file name does not even exist
205 addRequesterChannelMap := make(map[chan<- bool]struct{})
206 addRequesterChannelMap[aWaitChannel] = struct{}{}
207 dm.dnldImgReadyWaiting[aFileName] = addRequesterChannelMap
208 logger.Debugw(ctx, "file not ready - setting first requester", log.Fields{
209 "image-name": aFileName})
210 }
211}
212
213//RemoveReadyRequest removes the specified channel from the requester(channel) map for the given file name
214func (dm *fileDownloadManager) RemoveReadyRequest(ctx context.Context, aFileName string, aWaitChannel chan bool) {
215 dm.mutexDownloadImageDsc.Lock()
216 defer dm.mutexDownloadImageDsc.Unlock()
217 for imageName, channelMap := range dm.dnldImgReadyWaiting {
218 if imageName == aFileName {
219 for channel := range channelMap {
220 if channel == aWaitChannel {
221 delete(dm.dnldImgReadyWaiting[imageName], channel)
222 logger.Debugw(ctx, "channel removed from the requester map", log.Fields{
223 "image-name": aFileName, "new number-of-requesters": len(dm.dnldImgReadyWaiting[aFileName])})
224 return //can leave directly
225 }
226 }
227 return //can leave directly
228 }
229 }
230}
231
232// FileDownloadManager private (unexported) methods -- start
233
Holger Hildebrandt33f701d2021-12-22 10:07:58 +0000234//imageExists returns current ImageState and if the requested image already exists within the adapter
235//precondition: at calling this method mutexDownloadImageDsc must already be at least RLocked by the caller
236func (dm *fileDownloadManager) imageExists(ctx context.Context, aImageName string, aURL string) (fileState, bool) {
237 logger.Debugw(ctx, "checking on existence of the image", log.Fields{"image-name": aImageName})
238 for _, dnldImgDsc := range dm.downloadImageDscSlice {
239 if dnldImgDsc.downloadImageName == aImageName {
240 //image found (by name)
241 if dnldImgDsc.downloadImageURL == aURL {
242 //image found (by name and URL)
243 return dnldImgDsc.downloadImageState, true
244 }
245 return cFileStateUnknown, true //use fileState to indicate URL mismatch for existing file
246 }
247 }
248 //image not found (by name)
249 return cFileStateUnknown, false
250}
251
mpagenkoc26d4c02021-05-06 14:27:57 +0000252//imageLocallyDownloaded returns true if the requested image already exists within the adapter
mpagenko9c225032021-10-15 14:26:49 +0000253// requires mutexDownloadImageDsc to be locked (at least RLocked)
mpagenkoc26d4c02021-05-06 14:27:57 +0000254func (dm *fileDownloadManager) imageLocallyDownloaded(ctx context.Context, aImageName string) bool {
255 logger.Debugw(ctx, "checking if image is fully downloaded to adapter", log.Fields{"image-name": aImageName})
mpagenkoc26d4c02021-05-06 14:27:57 +0000256 for _, dnldImgDsc := range dm.downloadImageDscSlice {
257 if dnldImgDsc.downloadImageName == aImageName {
258 //image found (by name)
259 if dnldImgDsc.downloadImageState == cFileStateDlSucceeded {
260 logger.Debugw(ctx, "image has been fully downloaded", log.Fields{"image-name": aImageName})
261 return true
262 }
263 logger.Debugw(ctx, "image not yet fully downloaded", log.Fields{"image-name": aImageName})
264 return false
265 }
266 }
267 //image not found (by name)
268 logger.Errorw(ctx, "image does not exist", log.Fields{"image-name": aImageName})
269 return false
270}
271
mpagenko9c225032021-10-15 14:26:49 +0000272//updateDownloadCancel sets context cancel function to be used in case the download is to be aborted
273func (dm *fileDownloadManager) updateDownloadCancel(ctx context.Context,
274 aImageName string, aCancelFn context.CancelFunc) {
275 dm.mutexDownloadImageDsc.Lock()
276 defer dm.mutexDownloadImageDsc.Unlock()
277 for imgKey, dnldImgDsc := range dm.downloadImageDscSlice {
278 if dnldImgDsc.downloadImageName == aImageName {
279 //image found (by name) - need to write changes on the original map
280 dm.downloadImageDscSlice[imgKey].downloadContextCancelFn = aCancelFn
281 dm.downloadImageDscSlice[imgKey].downloadActive = true
282 logger.Debugw(ctx, "downloadContextCancelFn set", log.Fields{
283 "image-name": aImageName})
284 return //can leave directly
285 }
286 }
287}
288
289//updateFileState sets the new active (downloaded) file state and informs possibly waiting requesters on this change
290func (dm *fileDownloadManager) updateFileState(ctx context.Context, aImageName string, aFileSize int64) {
291 dm.mutexDownloadImageDsc.Lock()
292 defer dm.mutexDownloadImageDsc.Unlock()
293 for imgKey, dnldImgDsc := range dm.downloadImageDscSlice {
294 if dnldImgDsc.downloadImageName == aImageName {
295 //image found (by name) - need to write changes on the original map
296 dm.downloadImageDscSlice[imgKey].downloadActive = false
297 dm.downloadImageDscSlice[imgKey].downloadImageState = cFileStateDlSucceeded
298 dm.downloadImageDscSlice[imgKey].downloadImageLen = aFileSize
299 logger.Debugw(ctx, "imageState download succeeded", log.Fields{
300 "image-name": aImageName, "image-size": aFileSize})
301 //in case upgrade process(es) was/were waiting for the file, inform them
302 for imageName, channelMap := range dm.dnldImgReadyWaiting {
303 if imageName == aImageName {
304 for channel := range channelMap {
305 // use all found channels to inform possible requesters about the existence of the file
306 channel <- true
307 delete(dm.dnldImgReadyWaiting[imageName], channel) //requester served
308 }
309 return //can leave directly
310 }
311 }
312 return //can leave directly
313 }
314 }
315}
316
mpagenkoc26d4c02021-05-06 14:27:57 +0000317//downloadFile downloads the specified file from the given http location
318func (dm *fileDownloadManager) downloadFile(ctx context.Context, aURLCommand string, aFilePath string, aFileName string) error {
319 // Get the data
320 logger.Infow(ctx, "downloading with URL", log.Fields{"url": aURLCommand, "localPath": aFilePath})
321 // verifying the complete URL by parsing it to its URL elements
322 urlBase, err1 := url.Parse(aURLCommand)
323 if err1 != nil {
324 logger.Errorw(ctx, "could not set base url command", log.Fields{"url": aURLCommand, "error": err1})
Holger Hildebrandt33f701d2021-12-22 10:07:58 +0000325 dm.removeImage(ctx, aFileName, false) //wo FileSystem access
mpagenkoc26d4c02021-05-06 14:27:57 +0000326 return fmt.Errorf("could not set base url command: %s, error: %s", aURLCommand, err1)
327 }
328 urlParams := url.Values{}
329 urlBase.RawQuery = urlParams.Encode()
330
331 //pre-check on file existence - assuming http location here
332 reqExist, errExist2 := http.NewRequest("HEAD", urlBase.String(), nil)
333 if errExist2 != nil {
334 logger.Errorw(ctx, "could not generate http head request", log.Fields{"url": urlBase.String(), "error": errExist2})
Holger Hildebrandt33f701d2021-12-22 10:07:58 +0000335 dm.removeImage(ctx, aFileName, false) //wo FileSystem access
mpagenkoc26d4c02021-05-06 14:27:57 +0000336 return fmt.Errorf("could not generate http head request: %s, error: %s", aURLCommand, errExist2)
337 }
338 ctxExist, cancelExist := context.WithDeadline(ctx, time.Now().Add(3*time.Second)) //waiting for some fast answer
339 defer cancelExist()
340 _ = reqExist.WithContext(ctxExist)
341 respExist, errExist3 := http.DefaultClient.Do(reqExist)
342 if errExist3 != nil || respExist.StatusCode != http.StatusOK {
mpagenko9c225032021-10-15 14:26:49 +0000343 if respExist == nil {
344 logger.Errorw(ctx, "http head from url error - no status, aborting", log.Fields{"url": urlBase.String(),
345 "error": errExist3})
Holger Hildebrandt33f701d2021-12-22 10:07:58 +0000346 dm.removeImage(ctx, aFileName, false) //wo FileSystem access
mpagenko9c225032021-10-15 14:26:49 +0000347 return fmt.Errorf("http head from url error - no status, aborting: %s, error: %s",
348 aURLCommand, errExist3)
349 }
mpagenkoc26d4c02021-05-06 14:27:57 +0000350 logger.Infow(ctx, "could not http head from url", log.Fields{"url": urlBase.String(),
351 "error": errExist3, "status": respExist.StatusCode})
352 //if head is not supported by server we cannot use this test and just try to continue
353 if respExist.StatusCode != http.StatusMethodNotAllowed {
354 logger.Errorw(ctx, "http head from url: file does not exist here, aborting", log.Fields{"url": urlBase.String(),
355 "error": errExist3, "status": respExist.StatusCode})
Holger Hildebrandt33f701d2021-12-22 10:07:58 +0000356 dm.removeImage(ctx, aFileName, false) //wo FileSystem access
mpagenkoc26d4c02021-05-06 14:27:57 +0000357 return fmt.Errorf("http head from url: file does not exist here, aborting: %s, error: %s, status: %d",
mpagenko9c225032021-10-15 14:26:49 +0000358 aURLCommand, errExist3, respExist.StatusCode)
mpagenkoc26d4c02021-05-06 14:27:57 +0000359 }
360 }
361 defer func() {
362 deferredErr := respExist.Body.Close()
363 if deferredErr != nil {
364 logger.Errorw(ctx, "error at closing http head response body", log.Fields{"url": urlBase.String(), "error": deferredErr})
365 }
366 }()
367
368 //trying to download - do it in background as it may take some time ...
369 go func() {
370 req, err2 := http.NewRequest("GET", urlBase.String(), nil)
371 if err2 != nil {
372 logger.Errorw(ctx, "could not generate http request", log.Fields{"url": urlBase.String(), "error": err2})
mpagenko9c225032021-10-15 14:26:49 +0000373 dm.removeImage(ctx, aFileName, false) //wo FileSystem access
mpagenkoc26d4c02021-05-06 14:27:57 +0000374 return
375 }
376 ctx, cancel := context.WithDeadline(ctx, time.Now().Add(dm.dlToAdapterTimeout)) //timeout as given from SetDownloadTimeout()
mpagenko9c225032021-10-15 14:26:49 +0000377 dm.updateDownloadCancel(ctx, aFileName, cancel)
mpagenkoc26d4c02021-05-06 14:27:57 +0000378 defer cancel()
379 _ = req.WithContext(ctx)
380 resp, err3 := http.DefaultClient.Do(req)
mpagenko9c225032021-10-15 14:26:49 +0000381 if err3 != nil || resp.StatusCode != http.StatusOK {
382 if resp == nil {
383 logger.Errorw(ctx, "http get error - no status, aborting", log.Fields{"url": urlBase.String(),
384 "error": err3})
385 } else {
386 logger.Errorw(ctx, "could not http get from url", log.Fields{"url": urlBase.String(),
387 "error": err3, "status": resp.StatusCode})
388 }
389 dm.removeImage(ctx, aFileName, false) //wo FileSystem access
mpagenkoc26d4c02021-05-06 14:27:57 +0000390 return
391 }
392 defer func() {
393 deferredErr := resp.Body.Close()
394 if deferredErr != nil {
395 logger.Errorw(ctx, "error at closing http get response body", log.Fields{"url": urlBase.String(), "error": deferredErr})
396 }
397 }()
398
399 // Create the file
400 aLocalPathName := aFilePath + "/" + aFileName
401 file, err := os.Create(aLocalPathName)
402 if err != nil {
403 logger.Errorw(ctx, "could not create local file", log.Fields{"path_file": aLocalPathName, "error": err})
mpagenko9c225032021-10-15 14:26:49 +0000404 dm.removeImage(ctx, aFileName, false) //wo FileSystem access
mpagenkoc26d4c02021-05-06 14:27:57 +0000405 return
406 }
407 defer func() {
408 deferredErr := file.Close()
409 if deferredErr != nil {
410 logger.Errorw(ctx, "error at closing new file", log.Fields{"path_file": aLocalPathName, "error": deferredErr})
411 }
412 }()
413
414 // Write the body to file
415 _, err = io.Copy(file, resp.Body)
416 if err != nil {
417 logger.Errorw(ctx, "could not copy file content", log.Fields{"url": urlBase.String(), "file": aLocalPathName, "error": err})
mpagenko9c225032021-10-15 14:26:49 +0000418 dm.removeImage(ctx, aFileName, true)
mpagenkoc26d4c02021-05-06 14:27:57 +0000419 return
420 }
421
422 fileStats, statsErr := file.Stat()
423 if err != nil {
424 logger.Errorw(ctx, "created file can't be accessed", log.Fields{"file": aLocalPathName, "stat-error": statsErr})
mpagenko9c225032021-10-15 14:26:49 +0000425 return
mpagenkoc26d4c02021-05-06 14:27:57 +0000426 }
427 fileSize := fileStats.Size()
428 logger.Infow(ctx, "written file size is", log.Fields{"file": aLocalPathName, "length": fileSize})
429
mpagenko9c225032021-10-15 14:26:49 +0000430 dm.updateFileState(ctx, aFileName, fileSize)
mpagenkoc26d4c02021-05-06 14:27:57 +0000431 //TODO:!!! further extension could be provided here, e.g. already computing and possibly comparing the CRC, vendor check
432 }()
433 return nil
434}
mpagenkoaa3afe92021-05-21 16:20:58 +0000435
mpagenko9c225032021-10-15 14:26:49 +0000436//removeImage deletes the given image according to the Image name from filesystem and downloadImageDscSlice
437func (dm *fileDownloadManager) removeImage(ctx context.Context, aImageName string, aDelFs bool) {
Holger Hildebrandt33f701d2021-12-22 10:07:58 +0000438 logger.Debugw(ctx, "remove the image from Adapter", log.Fields{"image-name": aImageName, "del-fs": aDelFs})
mpagenkoaa3afe92021-05-21 16:20:58 +0000439 dm.mutexDownloadImageDsc.RLock()
440 defer dm.mutexDownloadImageDsc.RUnlock()
441
442 tmpSlice := dm.downloadImageDscSlice[:0]
443 for _, dnldImgDsc := range dm.downloadImageDscSlice {
444 if dnldImgDsc.downloadImageName == aImageName {
mpagenko9c225032021-10-15 14:26:49 +0000445 //image found (by name)
mpagenkoaa3afe92021-05-21 16:20:58 +0000446 logger.Debugw(ctx, "removing image", log.Fields{"image-name": aImageName})
mpagenko9c225032021-10-15 14:26:49 +0000447 if aDelFs {
448 //remove the image from filesystem
449 aLocalPathName := cDefaultLocalDir + "/" + aImageName
450 if err := os.Remove(aLocalPathName); err != nil {
451 // might be a temporary situation, when the file was not yet (completely) written
452 logger.Debugw(ctx, "image not removed from filesystem", log.Fields{
453 "image-name": aImageName, "error": err})
454 }
mpagenkoaa3afe92021-05-21 16:20:58 +0000455 }
mpagenko9c225032021-10-15 14:26:49 +0000456 // and remove from the imageDsc slice by just not appending
mpagenkoaa3afe92021-05-21 16:20:58 +0000457 } else {
458 tmpSlice = append(tmpSlice, dnldImgDsc)
459 }
460 }
461 dm.downloadImageDscSlice = tmpSlice
462 //image not found (by name)
463}
mpagenko9c225032021-10-15 14:26:49 +0000464
465//CancelDownload stops the download and clears all entires concerning this aimageName
466func (dm *fileDownloadManager) CancelDownload(ctx context.Context, aImageName string) {
467 // for the moment that would only support to wait for the download end and remove the image then
468 // further reactions while still downloading can be considered with some effort, but does it make sense (synchronous load here!)
469 dm.mutexDownloadImageDsc.RLock()
470 for imgKey, dnldImgDsc := range dm.downloadImageDscSlice {
471 if dnldImgDsc.downloadImageName == aImageName {
472 //image found (by name) - need to to check on ongoing download
473 if dm.downloadImageDscSlice[imgKey].downloadActive {
474 //then cancel the download using the context cancel function
475 dm.downloadImageDscSlice[imgKey].downloadContextCancelFn()
476 }
477 //and remove possibly stored traces of this image
478 dm.mutexDownloadImageDsc.RUnlock()
479 go dm.removeImage(ctx, aImageName, true) //including the chance that nothing was yet written to FS, should not matter
480 return //can leave directly
481 }
482 }
483 dm.mutexDownloadImageDsc.RUnlock()
484}