From ac2488d2420769a4c59d23ab78c7d53117930719 Mon Sep 17 00:00:00 2001
From: Michael Kelly <mkelly@arista.com>
Date: Wed, 17 Aug 2022 22:06:31 -0700
Subject: [PATCH] Allow the specification of storageClassName in PVCs

Not every cluster has a default storage class and even if they do, we
don't always want to use that default storage class for our persistent
volumes.  Instead, allow the user to specific this via the cluster
configuration itself for the three cases we require (zookeeper,
scheduler, and registry).

Change-Id: I948d57ce59d9b16c1c70fc52af2e22bd6131e6e2
---
 deploy/crds/zuul-ci_v1alpha2_zuul_crd.yaml |  6 ++++++
 doc/source/index.rst                       | 18 ++++++++++++++++++
 zuul_operator/templates/zookeeper.yaml     |  3 +++
 zuul_operator/templates/zuul-registry.yaml |  3 +++
 zuul_operator/templates/zuul.yaml          |  3 +++
 zuul_operator/zookeeper.py                 |  5 +++--
 zuul_operator/zuul.py                      |  9 +++++++--
 7 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/deploy/crds/zuul-ci_v1alpha2_zuul_crd.yaml b/deploy/crds/zuul-ci_v1alpha2_zuul_crd.yaml
index fd3da25..5f79088 100644
--- a/deploy/crds/zuul-ci_v1alpha2_zuul_crd.yaml
+++ b/deploy/crds/zuul-ci_v1alpha2_zuul_crd.yaml
@@ -58,6 +58,8 @@ spec:
                       type: string
                     secretName:
                       type: string
+                    storageClassName:
+                      type: string
                 env:
                   type: object
                   x-kubernetes-preserve-unknown-fields: true
@@ -73,6 +75,8 @@ spec:
                       type: integer
                       default: 1
                       minimum: 1
+                    storageClassName:
+                      type: string
                 launcher:
                   type: object
                   properties:
@@ -176,3 +180,5 @@ spec:
                       properties:
                         secretName:
                           type: string
+                    storageClassName:
+                      type: string
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 766f8e0..3acb1ba 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -378,6 +378,12 @@ verbatim):
             * ``tls.crt``
             * ``tls.key``
 
+         .. attr:: storageClassName
+            :default: ''
+
+            The name of the Kubernetes storage class to use when
+            running using the managed ZooKeeper instance.
+
       .. attr:: env
 
          A list of environment variables.  This will be passed through
@@ -396,6 +402,12 @@ verbatim):
 
                The key name in the secret should be ``main.yaml``.
 
+         .. attr:: storageClassName
+            :default: ''
+
+            The name of the Kubernetes storage class to use when
+            running using the managed ZooKeeper instance.
+
       .. attr:: launcher
 
          .. attr:: config
@@ -617,6 +629,12 @@ verbatim):
 
             The requested size of the registry storage volume.
 
+         .. attr:: storageClassName
+            :default: ''
+
+            The name of the Kubernetes storage class to use when
+            running using the managed ZooKeeper instance.
+
          .. attr:: tls
 
             .. attr:: secretName
diff --git a/zuul_operator/templates/zookeeper.yaml b/zuul_operator/templates/zookeeper.yaml
index 4ffdb90..1136c5b 100644
--- a/zuul_operator/templates/zookeeper.yaml
+++ b/zuul_operator/templates/zookeeper.yaml
@@ -363,3 +363,6 @@ spec:
         resources:
           requests:
             storage: "5Gi"
+        {%- if spec.storageClassName != "" %}
+        storageClassName: {{ spec.zookeeper.storageClassName }}
+        {%- endif %}
diff --git a/zuul_operator/templates/zuul-registry.yaml b/zuul_operator/templates/zuul-registry.yaml
index eefbfa8..a33636e 100644
--- a/zuul_operator/templates/zuul-registry.yaml
+++ b/zuul_operator/templates/zuul-registry.yaml
@@ -106,3 +106,6 @@ spec:
       resources:
         requests:
           storage: {{ spec.registry.volumeSize }} #80Gi
+      {%- if spec.registry.storageClassName != "" %}
+      storageClassName: {{ spec.registry.storageClassName }}
+      {%- endif %}
diff --git a/zuul_operator/templates/zuul.yaml b/zuul_operator/templates/zuul.yaml
index e1aaac5..89e9612 100644
--- a/zuul_operator/templates/zuul.yaml
+++ b/zuul_operator/templates/zuul.yaml
@@ -169,6 +169,9 @@ spec:
       resources:
         requests:
           storage: 80Gi
+      {%- if spec.scheduler.storageClassName != "" %}
+      storageClassName: {{ spec.scheduler.storageClassName }}
+      {%- endif %}
 ---
 apiVersion: apps/v1
 kind: Deployment
diff --git a/zuul_operator/zookeeper.py b/zuul_operator/zookeeper.py
index 8c7106e..fdd790d 100644
--- a/zuul_operator/zookeeper.py
+++ b/zuul_operator/zookeeper.py
@@ -19,14 +19,15 @@ from . import utils
 
 
 class ZooKeeper:
-    def __init__(self, api, namespace, logger):
+    def __init__(self, api, namespace, logger, spec):
         self.api = api
         self.namespace = namespace
         self.log = logger
+        self.spec = spec
 
     def create(self):
         utils.apply_file(self.api, 'zookeeper.yaml',
-                         namespace=self.namespace)
+                         namespace=self.namespace, spec=self.spec)
 
     def wait_for_cluster(self):
         while True:
diff --git a/zuul_operator/zuul.py b/zuul_operator/zuul.py
index 25beef5..6779e3c 100644
--- a/zuul_operator/zuul.py
+++ b/zuul_operator/zuul.py
@@ -49,6 +49,7 @@ class Zuul:
             get('secretName')
 
         zk_spec = self.spec.setdefault('zookeeper', {})
+        zk_spec.setdefault('storageClassName', '')
         zk_str = spec.get('zookeeper', {}).get('hosts')
         if zk_str:
             self.manage_zk = False
@@ -67,7 +68,8 @@ class Zuul:
 
         self.spec.setdefault('scheduler', {})['tenant_config'] = \
             '/etc/zuul/tenant/main.yaml'
-
+        self.spec.setdefault('scheduler', {}).setdefault(
+            'storageClassName', '')
         self.spec.setdefault('executor', {}).setdefault('count', 1)
         self.spec.setdefault('executor', {}).setdefault(
             'terminationGracePeriodSeconds', 21600)
@@ -78,6 +80,8 @@ class Zuul:
         registry = self.spec.setdefault('registry', {})
         registry.setdefault('count', 0)
         registry.setdefault('volumeSize', '80Gi')
+        registry.setdefault('storageClassName', '')
+
         registry_tls = registry.setdefault('tls', {})
         self.manage_registry_cert = ('secretName' not in registry_tls)
         registry_tls.setdefault('secretName', 'zuul-registry-tls')
@@ -126,7 +130,8 @@ class Zuul:
         if not self.manage_zk:
             self.log.info("ZK is externally managed")
             return
-        self.zk = zookeeper.ZooKeeper(self.api, self.namespace, self.log)
+        self.zk = zookeeper.ZooKeeper(self.api, self.namespace, self.log,
+                                      self.spec['zookeeper'])
         self.zk.create()
 
     def wait_for_zk(self):