From f13d9279fc6f7035f85b67f3bf7d9c1e338072de Mon Sep 17 00:00:00 2001
From: terc1997 <64480693+terc1997@users.noreply.github.com>
Date: Fri, 26 Dec 2025 11:24:03 -0300
Subject: [PATCH 1/3] [patch] implement cluster issuer override
---
python/src/mas/cli/install/app.py | 66 +++++++++++++++++--------------
1 file changed, 36 insertions(+), 30 deletions(-)
diff --git a/python/src/mas/cli/install/app.py b/python/src/mas/cli/install/app.py
index 531dce149e7..d9d9423f585 100644
--- a/python/src/mas/cli/install/app.py
+++ b/python/src/mas/cli/install/app.py
@@ -519,7 +519,12 @@ def configDNSAndCerts(self):
"Unless you see an error during the ocp-verify stage indicating that the secret can not be determined you do not need to set this and can leave the response empty"
])
self.promptForString("Cluster ingress certificate secret name", "ocp_ingress_tls_secret_name", default="")
-
+ self.printH1("Override Cluster Issuer")
+ self.printDescription([
+ "The cluster issuer is defined by the DNS Provider. This option is to override the configuration of the DNS provider if you're using a custom cluster issuer",
+ "If you're not using a custom cluster issuer, you can leave the response empty."
+ ])
+ self.promptForString("Cluster Issuer Name", "mas_cluster_issuer", default="")
self.printH1("Configure Domain & Certificate Management")
configureDomainAndCertMgmt = self.yesOrNo('Configure domain & certificate management')
if configureDomainAndCertMgmt:
@@ -546,7 +551,6 @@ def configDNSAndCerts(self):
elif dnsProvider == 4:
# Use MAS default self-signed cluster issuer with a custom domain
self.setParam("dns_provider", "")
- self.setParam("mas_cluster_issuer", "")
if dnsProvider in [1, 2]:
self.printDescription([
@@ -559,7 +563,6 @@ def configDNSAndCerts(self):
# Use MAS default self-signed cluster issuer with the default domain
self.setParam("dns_provider", "")
self.setParam("mas_domain", "")
- self.setParam("mas_cluster_issuer", "")
self.manualCerts = self.yesOrNo("Configure manual certificates")
self.setParam("mas_manual_cert_mgmt", self.manualCerts)
if self.getParam("mas_manual_cert_mgmt"):
@@ -576,19 +579,20 @@ def configDNSAndCertsCloudflare(self):
self.promptForString("Cloudflare zone", "cloudflare_zone")
self.promptForString("Cloudflare subdomain", "cloudflare_subdomain")
- self.printDescription([
- "Certificate Issuer:",
- " 1. LetsEncrypt (Production)",
- " 2. LetsEncrypt (Staging)",
- " 3. Self-Signed"
- ])
- certIssuer = self.promptForInt("Certificate issuer", min=1, max=3)
- certIssuerOptions = [
- f"{self.getParam('mas_instance_id')}-cloudflare-le-prod",
- f"{self.getParam('mas_instance_id')}-cloudflare-le-stg",
- ""
- ]
- self.setParam("mas_cluster_issuer", certIssuerOptions[certIssuer - 1])
+ if not self.getParam("mas_cluster_issuer"):
+ self.printDescription([
+ "Certificate Issuer:",
+ " 1. LetsEncrypt (Production)",
+ " 2. LetsEncrypt (Staging)",
+ " 3. Self-Signed"
+ ])
+ certIssuer = self.promptForInt("Certificate issuer", min=1, max=3)
+ certIssuerOptions = [
+ f"{self.getParam('mas_instance_id')}-cloudflare-le-prod",
+ f"{self.getParam('mas_instance_id')}-cloudflare-le-stg",
+ ""
+ ]
+ self.setParam("mas_cluster_issuer", certIssuerOptions[certIssuer - 1])
@logMethodCall
def configDNSAndCertsCIS(self):
@@ -598,19 +602,20 @@ def configDNSAndCertsCIS(self):
self.promptForString("CIS CRN", "cis_crn")
self.promptForString("CIS subdomain", "cis_subdomain")
- self.printDescription([
- "Certificate Issuer:",
- " 1. LetsEncrypt (Production)",
- " 2. LetsEncrypt (Staging)",
- " 3. Self-Signed"
- ])
- certIssuer = self.promptForInt("Certificate issuer", min=1, max=3)
- certIssuerOptions = [
- f"{self.getParam('mas_instance_id')}-cis-le-prod",
- f"{self.getParam('mas_instance_id')}-cis-le-stg",
- ""
- ]
- self.setParam("mas_cluster_issuer", certIssuerOptions[certIssuer - 1])
+ if not self.getParam("mas_cluster_issuer"):
+ self.printDescription([
+ "Certificate Issuer:",
+ " 1. LetsEncrypt (Production)",
+ " 2. LetsEncrypt (Staging)",
+ " 3. Self-Signed"
+ ])
+ certIssuer = self.promptForInt("Certificate issuer", min=1, max=3)
+ certIssuerOptions = [
+ f"{self.getParam('mas_instance_id')}-cis-le-prod",
+ f"{self.getParam('mas_instance_id')}-cis-le-stg",
+ ""
+ ]
+ self.setParam("mas_cluster_issuer", certIssuerOptions[certIssuer - 1])
@logMethodCall
def configDNSAndCertsRoute53(self):
@@ -635,7 +640,8 @@ def configDNSAndCertsRoute53(self):
self.promptForString("AWS Route 53 subdomain", "route53_subdomain")
self.promptForString("AWS Route 53 e-mail", "route53_email")
- self.setParam("mas_cluster_issuer", f"{self.getParam('mas_instance_id')}-route53-le-prod")
+ if not self.getParam("mas_cluster_issuer"):
+ self.setParam("mas_cluster_issuer", f"{self.getParam('mas_instance_id')}-route53-le-prod")
@logMethodCall
def configApps(self):
From 5f46485eeb1a9cdb57252a66214e7b4734c98213 Mon Sep 17 00:00:00 2001
From: terc1997 <64480693+terc1997@users.noreply.github.com>
Date: Tue, 30 Dec 2025 15:20:38 -0300
Subject: [PATCH 2/3] [patch] add condition to reuse deployed cluster issuer
---
python/src/mas/cli/install/app.py | 33 ++++++++++++++++++++++++-------
python/src/mas/cli/validators.py | 17 +++++++++++++++-
2 files changed, 42 insertions(+), 8 deletions(-)
diff --git a/python/src/mas/cli/install/app.py b/python/src/mas/cli/install/app.py
index 7fe365fd4eb..7ba08156d3d 100644
--- a/python/src/mas/cli/install/app.py
+++ b/python/src/mas/cli/install/app.py
@@ -37,6 +37,7 @@
from .catalogs import supportedCatalogs
from mas.cli.validators import (
+ ClusterIssuerValidator,
InstanceIDFormatValidator,
WorkspaceIDFormatValidator,
WorkspaceNameFormatValidator,
@@ -51,7 +52,8 @@
createNamespace,
getStorageClasses,
getClusterVersion,
- isClusterVersionInRange
+ isClusterVersionInRange,
+ getClusterIssuers
)
from mas.devops.mas import (
getCurrentCatalog,
@@ -537,12 +539,6 @@ def configDNSAndCerts(self):
"Unless you see an error during the ocp-verify stage indicating that the secret can not be determined you do not need to set this and can leave the response empty"
])
self.promptForString("Cluster ingress certificate secret name", "ocp_ingress_tls_secret_name", default="")
- self.printH1("Override Cluster Issuer")
- self.printDescription([
- "The cluster issuer is defined by the DNS Provider. This option is to override the configuration of the DNS provider if you're using a custom cluster issuer",
- "If you're not using a custom cluster issuer, you can leave the response empty."
- ])
- self.promptForString("Cluster Issuer Name", "mas_cluster_issuer", default="")
self.printH1("Configure Domain & Certificate Management")
configureDomainAndCertMgmt = self.yesOrNo('Configure domain & certificate management')
if configureDomainAndCertMgmt:
@@ -569,6 +565,7 @@ def configDNSAndCerts(self):
elif dnsProvider == 4:
# Use MAS default self-signed cluster issuer with a custom domain
self.setParam("dns_provider", "")
+ self.setParam("mas_cluster_issuer", "")
if dnsProvider in [1, 2]:
self.printDescription([
@@ -587,6 +584,28 @@ def configDNSAndCerts(self):
self.manualCertsDir = self.promptForDir("Enter the path containing the manual certificates", mustExist=True)
else:
self.manualCertsDir = None
+ else:
+ # Configuring domain
+ if self.yesOrNo('Configure custom domain'):
+ self.promptForString("MAS top-level domain", "mas_domain")
+ else:
+ self.setParam("mas_domain", "")
+
+ # Configuring DNS
+ if self.yesOrNo("Do you want MAS to set up its own (self-signed) cluster issuer?"):
+ self.setParam("mas_cluster_issuer", "")
+ else:
+ self.printDescription([
+ "Select the ClusterIssuer to use from the list below:",
+ ])
+ clusterIssuers = getClusterIssuers(self.dynamicClient)
+ if clusterIssuers is not None and len(clusterIssuers) > 0:
+ for clusterIssuer in clusterIssuers:
+ print_formatted_text(HTML(f" - {clusterIssuer.metadata.name}"))
+ self.params['mas_cluster_issuer'] = prompt(HTML('MAS Cluster Issuer '), validator=ClusterIssuerValidator(), validate_while_typing=False)
+ else:
+ print_formatted_text(HTML("No ClusterIssuers found on this cluster. MAS will use self-signed certificates."))
+ self.setParam("mas_cluster_issuer", "")
@logMethodCall
def configDNSAndCertsCloudflare(self):
diff --git a/python/src/mas/cli/validators.py b/python/src/mas/cli/validators.py
index 446349b63b0..202dc432b8e 100644
--- a/python/src/mas/cli/validators.py
+++ b/python/src/mas/cli/validators.py
@@ -19,7 +19,7 @@
from prompt_toolkit.validation import Validator, ValidationError
-from mas.devops.ocp import getStorageClass
+from mas.devops.ocp import getStorageClass, getClusterIssuer
from mas.devops.mas import verifyMasInstance
from mas.devops.aiservice import verifyAiServiceInstance, verifyAiServiceTenantInstance
@@ -238,3 +238,18 @@ def validate(self, document):
if not match(r"^.{1,4}$", bucketPrefix):
raise ValidationError(message='Bucket prefix does not meet the requirement', cursor_position=len(bucketPrefix))
+
+
+class ClusterIssuerValidator(Validator):
+ def validate(self, document):
+ """
+ Validate that a ClusterIssuer exists on the target cluster
+ """
+ name = document.text
+
+ dynClient = dynamic.DynamicClient(
+ api_client.ApiClient(configuration=config.load_kube_config())
+ )
+
+ if getClusterIssuer(dynClient, name) is None:
+ raise ValidationError(message='Specified cluster issuer is not available on this cluster', cursor_position=len(name))
From b19160bebaf4fdef7e53e115714018d5ddb0d9b2 Mon Sep 17 00:00:00 2001
From: terc1997 <64480693+terc1997@users.noreply.github.com>
Date: Tue, 30 Dec 2025 15:24:29 -0300
Subject: [PATCH 3/3] [patch] remove the override option for cluster issuer
---
python/src/mas/cli/install/app.py | 57 +++++++++++++++----------------
1 file changed, 27 insertions(+), 30 deletions(-)
diff --git a/python/src/mas/cli/install/app.py b/python/src/mas/cli/install/app.py
index 7ba08156d3d..8d72b21c31a 100644
--- a/python/src/mas/cli/install/app.py
+++ b/python/src/mas/cli/install/app.py
@@ -616,20 +616,19 @@ def configDNSAndCertsCloudflare(self):
self.promptForString("Cloudflare zone", "cloudflare_zone")
self.promptForString("Cloudflare subdomain", "cloudflare_subdomain")
- if not self.getParam("mas_cluster_issuer"):
- self.printDescription([
- "Certificate Issuer:",
- " 1. LetsEncrypt (Production)",
- " 2. LetsEncrypt (Staging)",
- " 3. Self-Signed"
- ])
- certIssuer = self.promptForInt("Certificate issuer", min=1, max=3)
- certIssuerOptions = [
- f"{self.getParam('mas_instance_id')}-cloudflare-le-prod",
- f"{self.getParam('mas_instance_id')}-cloudflare-le-stg",
- ""
- ]
- self.setParam("mas_cluster_issuer", certIssuerOptions[certIssuer - 1])
+ self.printDescription([
+ "Certificate Issuer:",
+ " 1. LetsEncrypt (Production)",
+ " 2. LetsEncrypt (Staging)",
+ " 3. Self-Signed"
+ ])
+ certIssuer = self.promptForInt("Certificate issuer", min=1, max=3)
+ certIssuerOptions = [
+ f"{self.getParam('mas_instance_id')}-cloudflare-le-prod",
+ f"{self.getParam('mas_instance_id')}-cloudflare-le-stg",
+ ""
+ ]
+ self.setParam("mas_cluster_issuer", certIssuerOptions[certIssuer - 1])
@logMethodCall
def configDNSAndCertsCIS(self):
@@ -639,20 +638,19 @@ def configDNSAndCertsCIS(self):
self.promptForString("CIS CRN", "cis_crn")
self.promptForString("CIS subdomain", "cis_subdomain")
- if not self.getParam("mas_cluster_issuer"):
- self.printDescription([
- "Certificate Issuer:",
- " 1. LetsEncrypt (Production)",
- " 2. LetsEncrypt (Staging)",
- " 3. Self-Signed"
- ])
- certIssuer = self.promptForInt("Certificate issuer", min=1, max=3)
- certIssuerOptions = [
- f"{self.getParam('mas_instance_id')}-cis-le-prod",
- f"{self.getParam('mas_instance_id')}-cis-le-stg",
- ""
- ]
- self.setParam("mas_cluster_issuer", certIssuerOptions[certIssuer - 1])
+ self.printDescription([
+ "Certificate Issuer:",
+ " 1. LetsEncrypt (Production)",
+ " 2. LetsEncrypt (Staging)",
+ " 3. Self-Signed"
+ ])
+ certIssuer = self.promptForInt("Certificate issuer", min=1, max=3)
+ certIssuerOptions = [
+ f"{self.getParam('mas_instance_id')}-cis-le-prod",
+ f"{self.getParam('mas_instance_id')}-cis-le-stg",
+ ""
+ ]
+ self.setParam("mas_cluster_issuer", certIssuerOptions[certIssuer - 1])
@logMethodCall
def configDNSAndCertsRoute53(self):
@@ -677,8 +675,7 @@ def configDNSAndCertsRoute53(self):
self.promptForString("AWS Route 53 subdomain", "route53_subdomain")
self.promptForString("AWS Route 53 e-mail", "route53_email")
- if not self.getParam("mas_cluster_issuer"):
- self.setParam("mas_cluster_issuer", f"{self.getParam('mas_instance_id')}-route53-le-prod")
+ self.setParam("mas_cluster_issuer", f"{self.getParam('mas_instance_id')}-route53-le-prod")
@logMethodCall
def configApps(self):