From 2bd4662cc09b4aba999a550bdaf731ed894c286b Mon Sep 17 00:00:00 2001
From: Aurélien Bompard <aurelien@bompard.org>
Date: Sep 16 2019 14:12:59 +0000
Subject: Send a message when a CoreOS operation is done


Signed-off-by: Aurélien Bompard <aurelien@bompard.org>

---

diff --git a/robosignatory/cli.py b/robosignatory/cli.py
index 857781a..0c50922 100644
--- a/robosignatory/cli.py
+++ b/robosignatory/cli.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals, absolute_import
+
 import os
 
 import click
@@ -5,7 +7,9 @@ from fedora_messaging.config import conf
 
 import robosignatory.work
 from robosignatory.tag import TagSigner
-from robosignatory.coreos import CoreOSSigner, ArtifactSignerWrapper, OSTreeSignerWrapper
+from robosignatory.coreos import (
+    CoreOSSigner, ArtifactSignerWrapper, OSTreeSignerWrapper, SigningFailed
+)
 from robosignatory import utils
 
 
@@ -55,7 +59,10 @@ def coreos_artifact(file_url, checksum):
     consumer = CoreOSSigner(conf["consumer_config"])
     key = consumer.get_key(None)
     signing_wrapper = ArtifactSignerWrapper(consumer.signer, key, consumer.bucket)
-    signing_wrapper.sign(file_url, checksum)
+    try:
+        signing_wrapper.sign(file_url, checksum)
+    except SigningFailed as e:
+        click.fail(str(e))
 
 
 @cli.command("sign-coreos-ostree")
@@ -65,4 +72,7 @@ def coreos_ostree(file_url, checksum):
     consumer = CoreOSSigner(conf["consumer_config"])
     key = consumer.get_key(None)
     signing_wrapper = OSTreeSignerWrapper(consumer.signer, key, consumer.bucket)
-    signing_wrapper.sign(file_url, checksum)
+    try:
+        signing_wrapper.sign(file_url, checksum)
+    except SigningFailed as e:
+        click.fail(str(e))
diff --git a/robosignatory/coreos.py b/robosignatory/coreos.py
index 20c0438..693361f 100644
--- a/robosignatory/coreos.py
+++ b/robosignatory/coreos.py
@@ -9,6 +9,7 @@ import tempfile
 import boto3
 import robosignatory.utils as utils
 from six.moves.urllib.parse import urlparse
+from fedora_messaging.api import Message, publish
 
 
 log = logging.getLogger(__name__)
@@ -48,14 +49,35 @@ class CoreOSSigner(object):
         )
         key = self.get_key(msg)
 
-        if msg.topic.endswith('.coreos.build.request.artifacts-sign'):
-            wrapper = ArtifactSignerWrapper(self.signer, key, self.bucket)
-            for artifact in msg.body["artifacts"]:
-                wrapper.sign(artifact["file"], artifact["checksum"])
+        response = Message(
+            topic="{}.finished".format(msg.topic),
+            body={
+                "build_id": msg.body["build_id"],
+                "stream": msg.body["stream"],
+                "basearch": msg.body["basearch"],
+            }
+        )
+
+        try:
+            if msg.topic.endswith('.coreos.build.request.artifacts-sign'):
+                wrapper = ArtifactSignerWrapper(self.signer, key, self.bucket)
+                for artifact in msg.body["artifacts"]:
+                    wrapper.sign(artifact["file"], artifact["checksum"])
+
+            elif msg.topic.endswith('.coreos.build.request.ostree-sign'):
+                wrapper = OSTreeSignerWrapper(self.signer, key, self.bucket)
+                wrapper.sign(msg.body["commit_object"], msg.body["checksum"])
+        except SigningFailed as e:
+            log.error(e)
+            response.body["status"] = "FAILURE"
+            publish(response)
+        else:
+            response.body["status"] = "SUCCESS"
+            publish(response)
 
-        elif msg.topic.endswith('.coreos.build.request.ostree-sign'):
-            wrapper = OSTreeSignerWrapper(self.signer, key, self.bucket)
-            wrapper.sign(msg.body["commit_object"], msg.body["checksum"])
+
+class SigningFailed(Exception):
+    pass
 
 
 class SignerWrapper(object):
@@ -90,8 +112,7 @@ class SignerWrapper(object):
 
         log.info("Checking hash for %s", filepath)
         if utils.get_hash(local_filepath) != checksum:
-            log.error("Incorrect SHA256 for %s, not signing", filepath)
-            return
+            raise SigningFailed("Incorrect SHA256 for {}, not signing".format(filepath))
 
         log.info("Signing %s", filepath)
         sig_filepath = self._get_sig_filepath(local_filepath)
@@ -99,12 +120,14 @@ class SignerWrapper(object):
         log.info('Signing command line: %s', cmdline)
         ret, stdout, stderr = utils.run_command(cmdline)
         if ret != 0:
-            log.error('Error signing! Signing output: %s, stdout: %s, '
-                      'stderr: %s', ret, stdout, stderr)
-            return
+            raise SigningFailed(
+                'Error signing! Signing output: {}, stdout: {}, stderr: {}'.format(
+                    ret, stdout, stderr)
+            )
         if not os.path.exists(sig_filepath):
-            log.error("Signer did not produce any signature file for %s", filepath)
-            return
+            raise SigningFailed(
+                "Signer did not produce any signature file for {}".format(filepath)
+            )
         log.debug('Fixing signature file permissions')
         # Sigul writes it as 0600, which makes a lot of sense as a general file
         # mode for it, but this is just a signature file that we want published
diff --git a/tests/test_coreos.py b/tests/test_coreos.py
index de9375a..c3ae9c3 100644
--- a/tests/test_coreos.py
+++ b/tests/test_coreos.py
@@ -3,8 +3,9 @@ import unittest
 import copy
 from collections import namedtuple
 
-from fedora_messaging.api import Message
 import mock
+from fedora_messaging.api import Message
+from fedora_messaging.testing import mock_sends
 
 from robosignatory.coreos import CoreOSSigner
 
@@ -72,13 +73,26 @@ class TestCoreOS(unittest.TestCase):
         self.consumer = CoreOSSigner(TEST_CONFIG)
         self.consumer.bucket = mock.Mock()
 
+    def _get_response_message(self, source_msg, failed=False):
+        return Message(
+            topic=source_msg.topic + ".finished",
+            body={
+                "build_id": source_msg.body["build_id"],
+                "stream": source_msg.body["stream"],
+                "basearch": source_msg.body["basearch"],
+                "status": "FAILURE" if failed else "SUCCESS",
+            }
+        )
+
     @mock.patch('robosignatory.coreos.utils.run_command')
     def test_artifacts_sign(self, run_command):
         self.consumer.bucket.download_file.side_effect = fake_download_and_artifact_sign
         run_command.return_value = 0, "", ""
         self.consumer.bucket.objects.filter.return_value = [S3Object(size=0)]
+        expected_response = self._get_response_message(ARTIFACTS_MESSAGE)
 
-        self.consumer.consume(ARTIFACTS_MESSAGE)
+        with mock_sends(expected_response):
+            self.consumer.consume(ARTIFACTS_MESSAGE)
 
         self.consumer.bucket.download_file.assert_called()
         assert self.consumer.bucket.download_file.call_args_list[0][0][0] == "some/path/test1"
@@ -91,8 +105,10 @@ class TestCoreOS(unittest.TestCase):
         self.consumer.bucket.download_file.side_effect = fake_download_and_ostree_sign
         run_command.return_value = 0, "", ""
         self.consumer.bucket.objects.filter.return_value = [S3Object(size=0)]
+        expected_response = self._get_response_message(OSTREE_MESSAGE)
 
-        self.consumer.consume(OSTREE_MESSAGE)
+        with mock_sends(expected_response):
+            self.consumer.consume(OSTREE_MESSAGE)
 
         self.consumer.bucket.download_file.assert_called()
         assert self.consumer.bucket.download_file.call_args_list[0][0][0] == "some/path/test1"
@@ -106,8 +122,10 @@ class TestCoreOS(unittest.TestCase):
         new_body["artifacts"][0]["checksum"] = "wrong-checksum"
         msg = Message(topic=ARTIFACTS_MESSAGE.topic, body=new_body)
         self.consumer.bucket.download_file.side_effect = fake_download
+        expected_response = self._get_response_message(ARTIFACTS_MESSAGE, failed=True)
 
-        self.consumer.consume(msg)
+        with mock_sends(expected_response):
+            self.consumer.consume(msg)
 
         self.consumer.bucket.download_file.assert_called()
         run_command.assert_not_called()
@@ -117,8 +135,10 @@ class TestCoreOS(unittest.TestCase):
     def test_signing_failed(self, run_command):
         self.consumer.bucket.download_file.side_effect = fake_download
         run_command.return_value = 1, "stdout", "stderr"
+        expected_response = self._get_response_message(ARTIFACTS_MESSAGE, failed=True)
 
-        self.consumer.consume(ARTIFACTS_MESSAGE)
+        with mock_sends(expected_response):
+            self.consumer.consume(ARTIFACTS_MESSAGE)
 
         self.consumer.bucket.download_file.assert_called()
         run_command.assert_called()
@@ -128,8 +148,10 @@ class TestCoreOS(unittest.TestCase):
     def test_no_signature(self, run_command):
         self.consumer.bucket.download_file.side_effect = fake_download
         run_command.return_value = 0, "stdout", "stderr"
+        expected_response = self._get_response_message(ARTIFACTS_MESSAGE, failed=True)
 
-        self.consumer.consume(ARTIFACTS_MESSAGE)
+        with mock_sends(expected_response):
+            self.consumer.consume(ARTIFACTS_MESSAGE)
 
         self.consumer.bucket.download_file.assert_called()
         run_command.assert_called()

