2222HR_VERSION = "v2"
2323HR_PLURAL = "helmreleases"
2424
25- # All of these are mandated by the ValidatingAdmissionPolicy on the management cluster
26- # and cannot be changed without updating both the VAP and the RBAC.
27- HR_NAME = "scs-kaas-certification"
28- HR_NAMESPACE = "scs-kaas-certification"
29- HR_KUBECONFIG_SECRET = "scs-kaas-certification-kubeconfig"
30- HR_CHART_REF = {
31- "kind" : "HelmChart" ,
32- "name" : "scs-kaas-certification" ,
33- "namespace" : "flux-system" ,
34- }
35- HR_VALUES_FIXED = {
36- "cloud" : "bfe2-prod" ,
37- "controlPlane" : {"hosted" : True },
38- "metadata" : {
39- "customerID" : 1111 ,
40- "customerName" : "teuto.net Netzdienste GmbH" ,
41- "friendlyName" : "scs-kaas-certification" ,
42- "serviceLevelAgreement" : "None" ,
43- },
44- "nodePools" : {
45- "pool-0" : {"flavor" : "standard.2.1905" , "replicas" : 2 },
46- },
47- }
48-
4925
5026class PluginT8s (KubernetesClusterPlugin ):
5127 """
5228 Plugin to provision Kubernetes clusters on the t8s (teuto k8s) management cluster
5329 via a Flux HelmRelease.
5430
5531 Creates a HelmRelease on mgmt-bfe2-prod and waits for the resulting cluster's
56- kubeconfig to appear in the secret scs-kaas-certification- kubeconfig.
32+ kubeconfig to appear in the secret '<name>- kubeconfig' .
5733
5834 Expected config keys:
5935 kubernetesVersion: '1.35' (major.minor)
@@ -62,13 +38,53 @@ class PluginT8s(KubernetesClusterPlugin):
6238 kubeconfig: t8s-kubeconfig.yaml (management cluster kubeconfig template)
6339 secrets:
6440 token: '{{ clouds_conf.teuto_mgmt_token }}'
41+ name: 'scs-kaas-certification' (optional, defaults to 'scs-kaas-certification';
42+ the kubeconfig secret name is derived as '<name>-kubeconfig')
43+ cloud: 'bfe2-prod' (optional, defaults to 'bfe2-prod')
44+ controlPlaneHosted: true (optional, defaults to true)
45+ nodePools: (optional, defaults to a single 'pool-0')
46+ pool-0:
47+ flavor: 'standard.2.1905'
48+ replicas: 2
49+ metadata: (optional; individual keys default as shown)
50+ customerID: 1111
51+ customerName: 'teuto.net Netzdienste GmbH'
52+ friendlyName: 'scs-kaas-certification'
53+ serviceLevelAgreement: 'None'
6554 """
6655
6756 def __init__ (self , config : dict [str , Any ], basepath : str = "." , cwd : str = "." ) -> None :
6857 self .basepath = basepath
6958 self .cwd = cwd
7059 self .env = Environment ()
7160
61+ # All of these are mandated by the ValidatingAdmissionPolicy on the management cluster
62+ # and cannot be changed without updating both the VAP and the RBAC. They are instance
63+ # (rather than module-level) attributes because, in principle, different certification
64+ # targets could be configured with different values here.
65+ self .hr_name = config .get ("name" , "scs-kaas-certification" )
66+ self .hr_namespace = "scs-kaas-certification"
67+ self .hr_kubeconfig_secret = f"{ self .hr_name } -kubeconfig"
68+ self .hr_chart_ref : dict [str , str ] = {
69+ "kind" : "HelmChart" ,
70+ "name" : "scs-kaas-certification" ,
71+ "namespace" : "flux-system" ,
72+ }
73+ metadata_config : dict [str , Any ] = config .get ("metadata" , {})
74+ self .hr_values_fixed : dict [str , Any ] = {
75+ "cloud" : config .get ("cloud" , "bfe2-prod" ),
76+ "controlPlane" : {"hosted" : config .get ("controlPlaneHosted" , True )},
77+ "metadata" : {
78+ "customerID" : metadata_config .get ("customerID" , 1111 ),
79+ "customerName" : metadata_config .get ("customerName" , "teuto.net Netzdienste GmbH" ),
80+ "friendlyName" : metadata_config .get ("friendlyName" , "scs-kaas-certification" ),
81+ "serviceLevelAgreement" : metadata_config .get ("serviceLevelAgreement" , "None" ),
82+ },
83+ "nodePools" : config .get ("nodePools" , {
84+ "pool-0" : {"flavor" : "standard.2.1905" , "replicas" : 2 },
85+ }),
86+ }
87+
7288 fn : str = config ["templates" ]["kubeconfig" ]
7389 with open (os .path .join (basepath , fn ), "r" ) as f :
7490 self .kubeconfig_template : Template = self .env .from_string (f .read ())
@@ -91,12 +107,12 @@ def _build_helmrelease(self) -> dict[str, Any]:
91107 return {
92108 "apiVersion" : f"{ HR_GROUP } /{ HR_VERSION } " ,
93109 "kind" : "HelmRelease" ,
94- "metadata" : {"name" : HR_NAME , "namespace" : HR_NAMESPACE },
110+ "metadata" : {"name" : self . hr_name , "namespace" : self . hr_namespace },
95111 "spec" : {
96- "chartRef" : HR_CHART_REF ,
112+ "chartRef" : self . hr_chart_ref ,
97113 "driftDetection" : {"mode" : "enabled" },
98114 "interval" : "1m" ,
99- "values" : {** HR_VALUES_FIXED , "version" : self .k8s_version },
115+ "values" : {** self . hr_values_fixed , "version" : self .k8s_version },
100116 },
101117 }
102118
@@ -105,17 +121,17 @@ def _delete_helmrelease(self, co_api: CustomObjectsApi) -> None:
105121 co_api .delete_namespaced_custom_object (
106122 HR_GROUP ,
107123 HR_VERSION ,
108- HR_NAMESPACE ,
124+ self . hr_namespace ,
109125 HR_PLURAL ,
110- HR_NAME ,
126+ self . hr_name ,
111127 )
112128 except ApiException as e :
113129 if e .status == 404 :
114- logger .debug (f"HelmRelease { HR_NAME } not present, nothing to delete" )
130+ logger .debug (f"HelmRelease { self . hr_name } not present, nothing to delete" )
115131 return
116132 raise
117133 # Give Flux time to begin deletion before we try to recreate
118- logger .debug (f"HelmRelease { HR_NAME } deletion requested; waiting 30s" )
134+ logger .debug (f"HelmRelease { self . hr_name } deletion requested; waiting 30s" )
119135 time .sleep (30 )
120136
121137 def _apply_helmrelease (self , api_client : ApiClient ) -> None :
@@ -132,9 +148,9 @@ def _apply_helmrelease(self, api_client: ApiClient) -> None:
132148 path_params = {
133149 'group' : HR_GROUP ,
134150 'version' : HR_VERSION ,
135- 'namespace' : HR_NAMESPACE ,
151+ 'namespace' : self . hr_namespace ,
136152 'plural' : HR_PLURAL ,
137- 'name' : HR_NAME ,
153+ 'name' : self . hr_name ,
138154 },
139155 query_params = [('fieldManager' , 'plugin_t8s' ), ('force' , 'true' )],
140156 header_params = {
@@ -148,15 +164,15 @@ def _apply_helmrelease(self, api_client: ApiClient) -> None:
148164 auth_settings = ['BearerToken' ],
149165 _return_http_data_only = True ,
150166 )
151- logger .debug (f"HelmRelease { HR_NAME } applied" )
167+ logger .debug (f"HelmRelease { self . hr_name } applied" )
152168
153169 def _get_kubeconfig_from_secret (self , core_api : CoreV1Api ) -> bytes :
154- secret = cast (V1Secret , core_api .read_namespaced_secret (HR_KUBECONFIG_SECRET , HR_NAMESPACE ))
170+ secret = cast (V1Secret , core_api .read_namespaced_secret (self . hr_kubeconfig_secret , self . hr_namespace ))
155171 data : dict [str , str ] = secret .data or {}
156172 if "value" in data :
157173 return base64 .standard_b64decode (data ["value" ].encode ())
158174 raise RuntimeError (
159- f"kubeconfig secret { HR_KUBECONFIG_SECRET } is missing key 'value'; has keys: { list (data )} "
175+ f"kubeconfig secret { self . hr_kubeconfig_secret } is missing key 'value'; has keys: { list (data )} "
160176 )
161177
162178 def _wait_for_kubeconfig_secret (self , core_api : CoreV1Api ) -> bytes :
@@ -170,10 +186,10 @@ def _wait_for_kubeconfig_secret(self, core_api: CoreV1Api) -> bytes:
170186 timeout = next (timeouts )
171187 if not timeout :
172188 raise RuntimeError (
173- f"Timeout waiting for kubeconfig secret { HR_KUBECONFIG_SECRET } "
189+ f"Timeout waiting for kubeconfig secret { self . hr_kubeconfig_secret } "
174190 )
175191 logger .debug (
176- f"waiting { timeout } s for kubeconfig secret { HR_KUBECONFIG_SECRET } "
192+ f"waiting { timeout } s for kubeconfig secret { self . hr_kubeconfig_secret } "
177193 )
178194 time .sleep (timeout )
179195
0 commit comments