Deploy using pushk only images with the latest commit hash

Bug: skia:9514
Change-Id: Ie0e1e16f21e704460a29344699efc2247507b8c7
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/261927
Reviewed-by: Ravi Mistry <rmistry@google.com>
Commit-Queue: Ravi Mistry <rmistry@google.com>
diff --git a/docker_pushes_watcher/go/docker_pushes_watcher/main.go b/docker_pushes_watcher/go/docker_pushes_watcher/main.go
index c7a9a95..a41173f 100644
--- a/docker_pushes_watcher/go/docker_pushes_watcher/main.go
+++ b/docker_pushes_watcher/go/docker_pushes_watcher/main.go
@@ -45,7 +45,7 @@
 	hang          = flag.Bool("hang", false, "If true, just hang and do nothing.")
 
 	tagProdImages = common.NewMultiStringFlag("tag_prod_image", nil, "Docker image that the docker_pushes_watcher app should tag as 'prod' if it is newer than the last hash tagged as 'prod'.")
-	deployImages  = common.NewMultiStringFlag("deploy_image", nil, "Docker image that the docker_pushes_watcher app should deploy when it's docker image is built, if it is newer than the last encountered hash.")
+	deployImages  = common.NewMultiStringFlag("deploy_image", nil, "Docker image that the docker_pushes_watcher app should deploy when it's docker image is built, if it is has been tagged as 'prod' using the tag_prod_image flag.")
 
 	fsNamespace = flag.String("fs_namespace", "", "Typically the instance id. e.g. 'docker_pushes_watcher'")
 	fsProjectID = flag.String("fs_project_id", "skia-firestore", "The project with the firestore instance. Datastore and Firestore can't be in the same project.")
@@ -135,7 +135,8 @@
 // tagProdToImage adds the "prod" tag to docker image if:
 // * It's commit hash is newer than the entry in Firestore for the specified image.
 // * There is no entry in Firestore and it is the first time we have seen this image.
-func tagProdToImage(ctx context.Context, fsClient *firestore.Client, gitRepo *gitiles.Repo, ts oauth2.TokenSource, buildInfo docker_pubsub.BuildInfo) error {
+// Returns a bool that indicates whether this image has been tagged with "prod" or not.
+func tagProdToImage(ctx context.Context, fsClient *firestore.Client, gitRepo *gitiles.Repo, ts oauth2.TokenSource, buildInfo docker_pubsub.BuildInfo) (bool, error) {
 	taggingMtx.Lock()
 	defer taggingMtx.Unlock()
 
@@ -151,53 +152,55 @@
 		if err == iterator.Done {
 			break
 		} else if err != nil {
-			return skerr.Wrap(err)
+			return false, skerr.Wrap(err)
 		}
 		docs = append(docs, doc)
 	}
 
+	taggedWithProd := false
 	if len(docs) > 1 {
-		return fmt.Errorf("For %s found %d entries in firestore. There should be only 1 entry.", baseName, len(docs))
+		return false, fmt.Errorf("For %s found %d entries in firestore. There should be only 1 entry.", baseName, len(docs))
 	} else if len(docs) == 0 {
 		// First time we have seen this image. Add it to firestore.
 		if _, createErr := fsClient.Create(ctx, col.Doc(id), buildInfo, DEFAULT_ATTEMPTS, PUT_SINGLE_TIMEOUT); createErr != nil {
-			return skerr.Wrap(createErr)
+			return false, skerr.Wrap(createErr)
 		}
 		sklog.Infof("Going to apply the prod tag to %s:%s", buildInfo.ImageName, buildInfo.Tag)
 		if err := addDockerProdTag(ctx, ts, buildInfo); err != nil {
-			return skerr.Wrap(err)
+			return false, skerr.Wrap(err)
 		}
 	} else {
 		// There is an existing entry for this image. See if the commit hash in the received image is newer.
 		if err := docs[0].DataTo(&fromDB); err != nil {
-			return skerr.Wrap(err)
+			return false, skerr.Wrap(err)
 		}
 		if fromDB.Tag == buildInfo.Tag {
 			sklog.Infof("We have already in the past tagged %s:%s with prod", buildInfo.ImageName, buildInfo.Tag)
 		} else {
 			log, err := gitRepo.LogLinear(ctx, fromDB.Tag, buildInfo.Tag)
 			if err != nil {
-				return fmt.Errorf("Could not query gitiles of %s: %s", common.REPO_SKIA, err)
+				return false, fmt.Errorf("Could not query gitiles of %s: %s", common.REPO_SKIA, err)
 			}
 			if len(log) > 0 {
 				// This means that the commit hash in the received image is newer than the one in datastore.
 				sklog.Infof("Applying the prod tag to %s:%s", buildInfo.ImageName, buildInfo.Tag)
 				if err := addDockerProdTag(ctx, ts, buildInfo); err != nil {
-					return skerr.Wrap(err)
+					return false, skerr.Wrap(err)
 				}
 				sklog.Infof("%s is newer than %s for %s. Replacing the entry in firestore", buildInfo.Tag, fromDB.Tag, buildInfo.ImageName)
 				if _, deleteErr := fsClient.Delete(ctx, col.Doc(id), DEFAULT_ATTEMPTS, DELETE_SINGLE_TIMEOUT); deleteErr != nil {
-					return fmt.Errorf("Could not delete %s in firestore: %s", buildInfo.ImageName, deleteErr)
+					return false, fmt.Errorf("Could not delete %s in firestore: %s", buildInfo.ImageName, deleteErr)
 				}
 				if _, createErr := fsClient.Create(ctx, col.Doc(id), buildInfo, DEFAULT_ATTEMPTS, PUT_SINGLE_TIMEOUT); createErr != nil {
-					return skerr.Wrap(err)
+					return false, skerr.Wrap(err)
 				}
+				taggedWithProd = true
 			} else {
 				sklog.Infof("Existing firestore entry %s is newer than %s for %s", fromDB.Tag, buildInfo.Tag, buildInfo.ImageName)
 			}
 		}
 	}
-	return nil
+	return taggedWithProd, nil
 }
 
 // deployImage deploys the specified fully qualified image name using pushk.
@@ -330,30 +333,32 @@
 				gitRepo := gitiles.NewRepo(buildInfo.Repo, httpClient)
 
 				tagFailure := metrics2.GetCounter("docker_watcher_tag_failure", map[string]string{"image": baseImageName(imageName), "repo": buildInfo.Repo})
-				if err := tagProdToImage(ctx, fsClient, gitRepo, ts, buildInfo); err != nil {
+				taggedWithProd, err := tagProdToImage(ctx, fsClient, gitRepo, ts, buildInfo)
+				if err != nil {
 					sklog.Errorf("Failed to add the prod tag to %s: %s", buildInfo, err)
 					tagFailure.Inc(1)
 					return
 				}
 				tagFailure.Reset()
+				if taggedWithProd {
+					// See if the image is in the whitelist of images to be deployed by pushk.
+					if util.In(baseImageName(imageName), *deployImages) {
+						pushFailure := metrics2.GetCounter("docker_watcher_push_failure", map[string]string{"image": baseImageName(imageName), "repo": buildInfo.Repo})
+						fullyQualifiedImageName := fmt.Sprintf("%s:%s", imageName, tag)
+						if err := deployImage(ctx, fullyQualifiedImageName); err != nil {
+							sklog.Errorf("Failed to deploy %s: %s", buildInfo, err)
+							pushFailure.Inc(1)
+							return
+						}
+						pushFailure.Reset()
+					} else {
+						sklog.Infof("Not going to deploy %s. It is not in the whitelist of images to deploy: %s", buildInfo, *deployImages)
+					}
+				}
 			} else {
 				sklog.Infof("Not going to tag %s with prod. It is not in the whitelist of images to tag: %s", buildInfo, *tagProdImages)
 			}
 
-			// See if the image is in the whitelist of images to be deployed by pushk.
-			if util.In(baseImageName(imageName), *deployImages) {
-				pushFailure := metrics2.GetCounter("docker_watcher_push_failure", map[string]string{"image": baseImageName(imageName), "repo": buildInfo.Repo})
-				fullyQualifiedImageName := fmt.Sprintf("%s:%s", imageName, tag)
-				if err := deployImage(ctx, fullyQualifiedImageName); err != nil {
-					sklog.Errorf("Failed to deploy %s: %s", buildInfo, err)
-					pushFailure.Inc(1)
-					return
-				}
-				pushFailure.Reset()
-			} else {
-				sklog.Infof("Not going to deploy %s. It is not in the whitelist of images to deploy: %s", buildInfo, *deployImages)
-			}
-
 			pubSubReceive.Reset()
 			sklog.Infof("Done processing %s", buildInfo)
 		})