← Back to team overview

wordpress-charmers team mailing list archive

[Merge] ~jsimpso/charm-k8s-wordpress:master into charm-k8s-wordpress:master

 

James Simpson has proposed merging ~jsimpso/charm-k8s-wordpress:master into charm-k8s-wordpress:master.

Requested reviews:
  Wordpress Charmers (wordpress-charmers)

For more details, see:
https://code.launchpad.net/~jsimpso/charm-k8s-wordpress/+git/charm-k8s-wordpress/+merge/419738
-- 
Your team Wordpress Charmers is requested to review the proposed merge of ~jsimpso/charm-k8s-wordpress:master into charm-k8s-wordpress:master.
diff --git a/actions.yaml b/actions.yaml
index 796aa4e..16940dd 100644
--- a/actions.yaml
+++ b/actions.yaml
@@ -1,2 +1,4 @@
 get-initial-password:
   description: Retrieve the auto-generated initial password.
+rotate-wordpress-secrets:
+  description: Rotate all wordpress secrets.
\ No newline at end of file
diff --git a/src/charm.py b/src/charm.py
index f26c714..6ebff30 100755
--- a/src/charm.py
+++ b/src/charm.py
@@ -405,6 +405,15 @@ class WordpressCharm(CharmBase):
             wp_secrets[secret] = self.leader_data[secret]
         return wp_secrets
 
+    def _rotate_wordpress_secrets(self):
+        """Regenerate and overwrite currently configured wordpress secrets.
+        This action should only be run against the leader, and the pod spec will
+        need to be regenerated afterwards.
+        """
+        if self.unit.is_leader():
+            for secret in WORDPRESS_SECRETS:
+                self.leader_data[secret] = password_generator(64)
+
     def is_service_up(self):
         """Check to see if the HTTP service is up"""
         service_ip = self.get_service_ip()
@@ -432,6 +441,17 @@ class WordpressCharm(CharmBase):
         else:
             event.fail("Initial password has not been set yet.")
 
+    def _on_rotate_wordpress_secrets_action(self, event):
+        """Handle the rotate-wordpress-secrets action."""
+        if self.model.unit.is_leader():
+            event.log("Generating new secrets")
+            self._rotate_wordpress_secrets()
+            event.log("Updating pod configuration")
+            self.configure_pod()
+            event.set_results({"result": "complete"})
+        else:
+            event.fail("Only the leader can rotate wordpress secrets.")
+
 
 if __name__ == "__main__":  # pragma: no cover
     main(WordpressCharm)
diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py
index 83e1c6c..0ad2b5e 100644
--- a/tests/unit/test_charm.py
+++ b/tests/unit/test_charm.py
@@ -334,6 +334,19 @@ class TestWordpressCharm(unittest.TestCase):
         self.harness.set_leader(True)
         self.assertEqual(len(self.harness.charm._get_initial_password()), 24)
 
+    def test_rotate_wordpress_secrets(self):
+        self.harness.charm.leader_data = {"AUTH_KEY": "supersekrit", "LOGGED_IN_KEY": "changemeplease"}
+        # Confirm that running against non-leader doesn't take any action
+        self.harness.set_leader(False)
+        self.harness.charm._rotate_wordpress_secrets()
+        self.assertEqual(self.harness.charm.leader_data.get("AUTH_KEY"), "supersekrit")
+        self.assertEqual(self.harness.charm.leader_data.get("LOGGED_IN_KEY"), "changemeplease")
+        # Confirm that passwords are regenerated when run against leader
+        self.harness.set_leader(True)
+        self.harness.charm._rotate_wordpress_secrets()
+        self.assertEqual(len(self.harness.charm.leader_data.get("AUTH_KEY")), 64)
+        self.assertEqual(len(self.harness.charm.leader_data.get("LOGGED_IN_KEY")), 64)
+
     def test_on_get_initial_password_action(self):
         action_event = Mock()
         # First test with no initial password set.
@@ -347,6 +360,31 @@ class TestWordpressCharm(unittest.TestCase):
             self.harness.charm._on_get_initial_password_action(action_event)
             self.assertEqual(action_event.set_results.call_args, mock.call({"password": "passwd"}))
 
+    def test_on_rotate_wordpress_secrets_action(self):
+        action_event = Mock()
+        # Test against non-leader
+        self.harness.set_leader(False)
+        self.harness.charm._on_rotate_wordpress_secrets_action(action_event)
+        self.assertEqual(action_event.fail.call_args, mock.call("Only the leader can rotate wordpress secrets."))
+        # Now test against leader
+        with mock.patch.object(self.harness.charm, "configure_pod") as configure_pod:
+            configure_pod.return_value = None
+            self.harness.set_leader(True)
+            # Make sure secrets are set initially
+            self.harness.charm.leader_data = {}
+            wp_secrets = self.harness.charm._get_wordpress_secrets()
+            for key in WORDPRESS_SECRETS:
+                self.assertIsInstance(wp_secrets[key], str)
+                self.assertEqual(len(wp_secrets[key]), 64)
+            # Now rotate keys and make sure they've all changed
+            self.harness.charm._on_rotate_wordpress_secrets_action(action_event)
+            rotated_wp_secrets = self.harness.charm._get_wordpress_secrets()
+            for key in WORDPRESS_SECRETS:
+                self.assertIsInstance(rotated_wp_secrets[key], str)
+                self.assertEqual(len(rotated_wp_secrets[key]), 64)
+                self.assertNotEqual(wp_secrets[key], rotated_wp_secrets[key])
+            self.assertEqual(action_event.set_results.call_args, mock.call({"result": "complete"}))
+
     def test_configure_pod(self):
         # Set leader_data to an empty dict to avoid subsequent calls to
         # `leader-get` and `leader-set` in this test.

Follow ups