VOL-3052 Onu Software upgrade extensions with http download

Signed-off-by: mpagenko <michael.pagenkopf@adtran.com>
Change-Id: Iebcbdc0fc01c83d8293862486acb12fd9f7f8f61
diff --git a/internal/pkg/onuadaptercore/adapter_download_manager.go b/internal/pkg/onuadaptercore/adapter_download_manager.go
index 3db9cc7..a49698f 100644
--- a/internal/pkg/onuadaptercore/adapter_download_manager.go
+++ b/internal/pkg/onuadaptercore/adapter_download_manager.go
@@ -21,10 +21,12 @@
 	"bufio"
 	"context"
 	"errors"
+	"io"
+	"net/http"
+	"net/url"
 	"os"
 	"sync"
-
-	//"time"
+	"time"
 
 	"github.com/opencord/voltha-protos/v4/go/voltha"
 
@@ -97,12 +99,23 @@
 //startDownload returns true if the download of the requested image could be started
 func (dm *adapterDownloadManager) startDownload(ctx context.Context, apImageDsc *voltha.ImageDownload) error {
 	if apImageDsc.LocalDir != "" {
-		logger.Infow(ctx, "image download-to-adapter requested", log.Fields{"image-name": apImageDsc.Name})
+		logger.Infow(ctx, "image download-to-adapter requested", log.Fields{
+			"image-path": apImageDsc.LocalDir, "image-name": apImageDsc.Name})
 		newImageDscPos := len(dm.downloadImageDscSlice)
 		dm.downloadImageDscSlice = append(dm.downloadImageDscSlice, apImageDsc)
 		dm.downloadImageDscSlice[newImageDscPos].DownloadState = voltha.ImageDownload_DOWNLOAD_STARTED
-		//just some basic test file simulation
-		go dm.writeFileToLFS(ctx, apImageDsc.Name, apImageDsc.LocalDir)
+		if apImageDsc.LocalDir == "/intern" {
+			//just for initial 'internal' test verification
+			//just some basic test file simulation
+			dm.downloadImageDscSlice[newImageDscPos].LocalDir = "/tmp"
+			go dm.writeFileToLFS(ctx, "/tmp", apImageDsc.Name)
+			return nil
+		} else if apImageDsc.LocalDir == "/reboot" {
+			dm.downloadImageDscSlice[newImageDscPos].LocalDir = "/tmp"
+		}
+		//try to download from http
+		urlName := apImageDsc.Url + "/" + apImageDsc.Name
+		go dm.downloadFile(ctx, urlName, apImageDsc.LocalDir, apImageDsc.Name)
 		//return success to comfort the core processing during integration
 		return nil
 	}
@@ -112,8 +125,77 @@
 	return errors.New("could not start download: no valid local directory to write to")
 }
 
+//downloadFile downloads the specified file from the given http location
+func (dm *adapterDownloadManager) downloadFile(ctx context.Context, aURLName string, aFilePath string, aFileName string) {
+	// Get the data
+	logger.Infow(ctx, "downloading from http", log.Fields{"url": aURLName, "localPath": aFilePath})
+	// http command is already part of the aURLName argument
+	urlBase, err1 := url.Parse(aURLName)
+	if err1 != nil {
+		logger.Errorw(ctx, "could not set base url command", log.Fields{"url": aURLName, "error": err1})
+	}
+	urlParams := url.Values{}
+	urlBase.RawQuery = urlParams.Encode()
+	req, err2 := http.NewRequest("GET", urlBase.String(), nil)
+	if err2 != nil {
+		logger.Errorw(ctx, "could not generate http request", log.Fields{"url": urlBase.String(), "error": err2})
+		return
+	}
+	ctx, cancel := context.WithDeadline(ctx, time.Now().Add(10*time.Second)) //timeout to be discussed
+	defer cancel()
+	_ = req.WithContext(ctx)
+
+	resp, err3 := http.DefaultClient.Do(req)
+	if err3 != nil {
+		logger.Errorw(ctx, "could not http get from url", log.Fields{"url": urlBase.String(), "error": err3})
+		return
+	}
+	defer func() {
+		deferredErr := resp.Body.Close()
+		if deferredErr != nil {
+			logger.Errorw(ctx, "error at closing http response body", log.Fields{"url": urlBase.String(), "error": deferredErr})
+		}
+	}()
+
+	// Create the file
+	aLocalPathName := aFilePath + "/" + aFileName
+	file, err := os.Create(aLocalPathName)
+	if err != nil {
+		logger.Errorw(ctx, "could not create local file", log.Fields{"path_file": aLocalPathName, "error": err})
+		return
+	}
+	defer func() {
+		deferredErr := file.Close()
+		if deferredErr != nil {
+			logger.Errorw(ctx, "error at closing new file", log.Fields{"path_file": aLocalPathName, "error": deferredErr})
+		}
+	}()
+
+	// Write the body to file
+	_, err = io.Copy(file, resp.Body)
+	if err != nil {
+		logger.Errorw(ctx, "could not copy file content", log.Fields{"url": urlBase.String(), "file": aLocalPathName, "error": err})
+		return
+	}
+
+	fileStats, statsErr := file.Stat()
+	if err != nil {
+		logger.Errorw(ctx, "created file can't be accessed", log.Fields{"file": aLocalPathName, "stat-error": statsErr})
+	}
+	logger.Infow(ctx, "written file size is", log.Fields{"file": aLocalPathName, "length": fileStats.Size()})
+
+	for _, pDnldImgDsc := range dm.downloadImageDscSlice {
+		if (*pDnldImgDsc).Name == aFileName {
+			//image found (by name)
+			(*pDnldImgDsc).DownloadState = voltha.ImageDownload_DOWNLOAD_SUCCEEDED
+			return //can leave directly
+		}
+	}
+}
+
 //writeFileToLFS writes the downloaded file to the local file system
-func (dm *adapterDownloadManager) writeFileToLFS(ctx context.Context, aFileName string, aLocalPath string) {
+//  this is just an internal test function and can be removed if other download capabilities exist
+func (dm *adapterDownloadManager) writeFileToLFS(ctx context.Context, aLocalPath string, aFileName string) {
 	// by now just a simulation to write a file with predefined 'variable' content
 	totalFileLength := 0
 	logger.Debugw(ctx, "writing fixed size simulation file locally", log.Fields{
@@ -138,8 +220,13 @@
 		logger.Errorw(ctx, "created file can't be accessed", log.Fields{"stat-error": statsErr})
 	}
 	logger.Infow(ctx, "written file size is", log.Fields{"file": aLocalPath + "/" + aFileName, "length": fileStats.Size()})
-	//nolint:gosec,errcheck
-	file.Close()
+
+	defer func() {
+		deferredErr := file.Close()
+		if deferredErr != nil {
+			logger.Errorw(ctx, "error at closing test file", log.Fields{"file": aLocalPath + "/" + aFileName, "error": deferredErr})
+		}
+	}()
 
 	for _, pDnldImgDsc := range dm.downloadImageDscSlice {
 		if (*pDnldImgDsc).Name == aFileName {