[VOL-3830] Correct image state update on failure

Change-Id: I35328884417fe2b55924b0c0412c6b8180672425
diff --git a/rw_core/core/device/agent_image.go b/rw_core/core/device/agent_image.go
index eed1879..37fd080 100644
--- a/rw_core/core/device/agent_image.go
+++ b/rw_core/core/device/agent_image.go
@@ -54,7 +54,12 @@
 	clonedImg := proto.Clone(img).(*voltha.ImageDownload)
 	clonedImg.DownloadState = voltha.ImageDownload_DOWNLOAD_REQUESTED
 	cloned := agent.cloneDeviceWithoutLock()
-	cloned.ImageDownloads = append(device.ImageDownloads, clonedImg)
+	_, index, err := getImage(img, device)
+	if err != nil {
+		cloned.ImageDownloads = append(device.ImageDownloads, clonedImg)
+	} else {
+		cloned.ImageDownloads[index] = clonedImg
+	}
 
 	cloned.AdminState = voltha.AdminState_DOWNLOADING_IMAGE
 	if err := agent.updateDeviceAndReleaseLock(ctx, cloned); err != nil {
@@ -138,11 +143,6 @@
 		return nil, err
 	}
 
-	if err != nil {
-		agent.requestQueue.RequestComplete()
-		return nil, status.Errorf(codes.FailedPrecondition, "device-id:%s, image-not-registered:%s", agent.deviceID, img.Name)
-	}
-
 	if image.DownloadState != voltha.ImageDownload_DOWNLOAD_SUCCEEDED {
 		agent.requestQueue.RequestComplete()
 		return nil, status.Errorf(codes.FailedPrecondition, "device-id:%s, device-has-not-downloaded-image:%s", agent.deviceID, img.Name)
@@ -296,18 +296,23 @@
 
 // onImageFailure brings back the device to Enabled state and sets the image to image download_failed.
 func (agent *Agent) onImageFailure(ctx context.Context, rpc string, response interface{}, reqArgs ...interface{}) {
-	if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
-		logger.Errorw(ctx, "cannot-obtain-lock", log.Fields{"rpc": rpc, "device-id": agent.deviceID, "error": err, "args": reqArgs})
+	// original context has failed due to timeout , let's open a new one
+	subCtx, cancel := context.WithTimeout(log.WithSpanFromContext(context.Background(), ctx), agent.defaultTimeout)
+	subCtx = coreutils.WithRPCMetadataFromContext(subCtx, ctx)
+
+	if err := agent.requestQueue.WaitForGreenLight(subCtx); err != nil {
+		logger.Errorw(subCtx, "can't obtain lock", log.Fields{"rpc": rpc, "device-id": agent.deviceID, "error": err, "args": reqArgs})
+		cancel()
 		return
 	}
 	if res, ok := response.(error); ok {
-		logger.Errorw(ctx, "rpc-failed", log.Fields{"rpc": rpc, "device-id": agent.deviceID, "error": res, "args": reqArgs})
-		device := agent.cloneDeviceWithoutLock()
+		logger.Errorw(subCtx, "rpc-failed", log.Fields{"rpc": rpc, "device-id": agent.deviceID, "error": res, "args": reqArgs})
+		cloned := agent.cloneDeviceWithoutLock()
 		//TODO base this on IMAGE ID when created
 		var imageFailed *voltha.ImageDownload
 		var index int
-		if device.ImageDownloads != nil {
-			for pos, image := range device.ImageDownloads {
+		if cloned.ImageDownloads != nil {
+			for pos, image := range cloned.ImageDownloads {
 				if image.DownloadState == voltha.ImageDownload_DOWNLOAD_REQUESTED ||
 					image.ImageState == voltha.ImageDownload_IMAGE_ACTIVATING {
 					imageFailed = image
@@ -317,29 +322,28 @@
 		}
 
 		if imageFailed == nil {
-			logger.Errorw(ctx, "can't find image", log.Fields{"rpc": rpc, "device-id": agent.deviceID, "args": reqArgs})
+			logger.Errorw(subCtx, "can't find image", log.Fields{"rpc": rpc, "device-id": agent.deviceID, "args": reqArgs})
+			cancel()
 			return
 		}
 
-		updatedImages := removeImage(device.ImageDownloads, index)
-
-		// Save the image
-		clonedImg := proto.Clone(imageFailed).(*voltha.ImageDownload)
+		//update image state on failure
 		if imageFailed.DownloadState == voltha.ImageDownload_DOWNLOAD_REQUESTED {
-			clonedImg.DownloadState = voltha.ImageDownload_DOWNLOAD_FAILED
+			cloned.ImageDownloads[index].DownloadState = voltha.ImageDownload_DOWNLOAD_FAILED
 		} else if imageFailed.ImageState == voltha.ImageDownload_IMAGE_ACTIVATING {
-			clonedImg.ImageState = voltha.ImageDownload_IMAGE_INACTIVE
+			cloned.ImageDownloads[index].ImageState = voltha.ImageDownload_IMAGE_INACTIVE
 		}
-		cloned := agent.cloneDeviceWithoutLock()
-		cloned.ImageDownloads = append(updatedImages, clonedImg)
 		//Enabled is the only state we can go back to.
 		cloned.AdminState = voltha.AdminState_ENABLED
-		if err := agent.updateDeviceAndReleaseLock(ctx, cloned); err != nil {
-			logger.Errorw(ctx, "failed-enable-device-after-image-failure",
+		if err := agent.updateDeviceAndReleaseLock(subCtx, cloned); err != nil {
+			logger.Errorw(subCtx, "failed-enable-device-after-image-failure",
 				log.Fields{"rpc": rpc, "device-id": agent.deviceID, "error": res, "args": reqArgs})
 		}
+		cancel()
 	} else {
-		logger.Errorw(ctx, "rpc-failed-invalid-error", log.Fields{"rpc": rpc, "device-id": agent.deviceID, "args": reqArgs})
+		logger.Errorw(subCtx, "rpc-failed-invalid-error", log.Fields{"rpc": rpc, "device-id": agent.deviceID, "args": reqArgs})
+		cancel()
+		return
 	}
 	// TODO: Post failure message onto kafka
 }
@@ -351,12 +355,12 @@
 		return
 	}
 	logger.Errorw(ctx, "rpc-successful", log.Fields{"rpc": rpc, "device-id": agent.deviceID, "response": response, "args": reqArgs})
-	device := agent.cloneDeviceWithoutLock()
+	cloned := agent.cloneDeviceWithoutLock()
 	//TODO base this on IMAGE ID when created
 	var imageSucceeded *voltha.ImageDownload
 	var index int
-	if device.ImageDownloads != nil {
-		for pos, image := range device.ImageDownloads {
+	if cloned.ImageDownloads != nil {
+		for pos, image := range cloned.ImageDownloads {
 			if image.DownloadState == voltha.ImageDownload_DOWNLOAD_REQUESTED ||
 				image.ImageState == voltha.ImageDownload_IMAGE_ACTIVATING {
 				imageSucceeded = image
@@ -369,20 +373,12 @@
 		logger.Errorw(ctx, "can't find image", log.Fields{"rpc": rpc, "device-id": agent.deviceID, "args": reqArgs})
 		return
 	}
-
-	updatedImages := removeImage(device.ImageDownloads, index)
-
-	// Save the image
-	clonedImg := proto.Clone(imageSucceeded).(*voltha.ImageDownload)
+	//update image state on success
 	if imageSucceeded.DownloadState == voltha.ImageDownload_DOWNLOAD_REQUESTED {
-		clonedImg.DownloadState = voltha.ImageDownload_DOWNLOAD_SUCCEEDED
+		cloned.ImageDownloads[index].DownloadState = voltha.ImageDownload_DOWNLOAD_SUCCEEDED
 	} else if imageSucceeded.ImageState == voltha.ImageDownload_IMAGE_ACTIVATING {
-		clonedImg.ImageState = voltha.ImageDownload_IMAGE_ACTIVE
-
+		cloned.ImageDownloads[index].ImageState = voltha.ImageDownload_IMAGE_ACTIVE
 	}
-	cloned := agent.cloneDeviceWithoutLock()
-	cloned.ImageDownloads = append(updatedImages, clonedImg)
-
 	//Enabled is the only state we can go back to.
 	cloned.AdminState = voltha.AdminState_ENABLED
 	if err := agent.updateDeviceAndReleaseLock(ctx, cloned); err != nil {
@@ -391,9 +387,3 @@
 	}
 
 }
-
-func removeImage(s []*voltha.ImageDownload, i int) []*voltha.ImageDownload {
-	s[i] = s[len(s)-1]
-	// We do not need to put s[i] at the end, as it will be discarded anyway
-	return s[:len(s)-1]
-}