diff --git a/src/mas/devops/mas.py b/src/mas/devops/mas.py index 1a2a8f75..841150ff 100644 --- a/src/mas/devops/mas.py +++ b/src/mas/devops/mas.py @@ -179,6 +179,26 @@ def verifyMasInstance(dynClient: DynamicClient, instanceId: str) -> bool: return False +def verifyAiServiceInstance(dynClient: DynamicClient, instanceId: str) -> bool: + """ + Validate that the chosen AI Service instance exists + """ + try: + aiserviceAPI = dynClient.resources.get(api_version="aiservice.ibm.com/v1", kind="AIServiceApp") + aiserviceAPI.get(name=instanceId, namespace=f"aiservice-{instanceId}") + return True + except NotFoundError: + print("NOT FOUND") + return False + except ResourceNotFoundError: + # The AIServiceApp CRD has not even been installed in the cluster + print("RESOURCE NOT FOUND") + return False + except UnauthorizedError as e: + logger.error(f"Error: Unable to verify AI Service instance due to failed authorization: {e}") + return False + + def verifyAppInstance(dynClient: DynamicClient, instanceId: str, applicationId: str) -> bool: """ Validate that the chosen app instance exists @@ -260,6 +280,17 @@ def getAppsSubscriptionChannel(dynClient: DynamicClient, instanceId: str) -> lis return [] +def getAiserviceChannel(dynClient: DynamicClient, instanceId: str) -> str: + """ + Get the AI Service channel from the subscription + """ + aiserviceSubscription = getSubscription(dynClient, f"aiservice-{instanceId}", "ibm-aiservice") + if aiserviceSubscription is None: + return None + else: + return aiserviceSubscription.spec.channel + + def updateIBMEntitlementKey(dynClient: DynamicClient, namespace: str, icrUsername: str, icrPassword: str, artifactoryUsername: str = None, artifactoryPassword: str = None, secretName: str = "ibm-entitlement") -> ResourceInstance: if secretName is None: secretName = "ibm-entitlement" diff --git a/src/mas/devops/tekton.py b/src/mas/devops/tekton.py index 3b4eb1eb..f489d5b5 100644 --- a/src/mas/devops/tekton.py +++ b/src/mas/devops/tekton.py @@ -462,3 +462,35 @@ def launchAiServiceInstallPipeline(dynClient: DynamicClient, params: dict) -> st pipelineURL = f"{getConsoleURL(dynClient)}/k8s/ns/aiservice-{instanceId}-pipelines/tekton.dev~v1beta1~PipelineRun/{instanceId}-install-{timestamp}" return pipelineURL + + +def launchAiServiceUpgradePipeline(dynClient: DynamicClient, + aiserviceInstanceId: str, + skipPreCheck: bool = False, + aiserviceChannel: str = "", + params: dict = {}) -> str: + """ + Create a PipelineRun to upgrade the chosen AI Service instance + """ + pipelineRunsAPI = dynClient.resources.get(api_version="tekton.dev/v1beta1", kind="PipelineRun") + namespace = f"aiservice-{aiserviceInstanceId}-pipelines" + timestamp = datetime.now().strftime("%y%m%d-%H%M") + # Create the PipelineRun + templateDir = path.join(path.abspath(path.dirname(__file__)), "templates") + env = Environment( + loader=FileSystemLoader(searchpath=templateDir) + ) + template = env.get_template("pipelinerun-aiservice-upgrade.yml.j2") + renderedTemplate = template.render( + timestamp=timestamp, + aiservice_instance_id=aiserviceInstanceId, + skip_pre_check=skipPreCheck, + aiservice_channel=aiserviceChannel, + **params + ) + logger.debug(renderedTemplate) + pipelineRun = yaml.safe_load(renderedTemplate) + pipelineRunsAPI.apply(body=pipelineRun, namespace=namespace) + + pipelineURL = f"{getConsoleURL(dynClient)}/k8s/ns/aiservice-{aiserviceInstanceId}-pipelines/tekton.dev~v1beta1~PipelineRun/{aiserviceInstanceId}-upgrade-{timestamp}" + return pipelineURL diff --git a/src/mas/devops/templates/pipelinerun-aiservice-upgrade.yml.j2 b/src/mas/devops/templates/pipelinerun-aiservice-upgrade.yml.j2 new file mode 100644 index 00000000..47bfe1d6 --- /dev/null +++ b/src/mas/devops/templates/pipelinerun-aiservice-upgrade.yml.j2 @@ -0,0 +1,56 @@ +--- +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + name: "{{ aiservice_instance_id }}-upgrade-{{ timestamp }}" + labels: + tekton.dev/pipeline: aiservice-upgrade +spec: + pipelineRef: + name: aiservice-upgrade + + serviceAccountName: "{{ service_account_name | default('pipeline', True) }}" + timeouts: + pipeline: "0" + + params: + # Target AI Service Instance + # ------------------------------------------------------------------------- + - name: aiservice_instance_id + value: "{{ aiservice_instance_id }}" + - name: aiservice_channel + value: "{{ aiservice_channel }}" + + # IBM Entitlement Key + # ------------------------------------------------------------------------- + - name: ibm_entitlement_key + value: "{{ ibm_entitlement_key }}" + +{%- if skip_pre_check is defined and skip_pre_check != "" %} + # Skip pre-check + # ------------------------------------------------------------------------- + - name: skip_pre_check + value: "{{ skip_pre_check }}" +{%- endif %} +{%- if artifactory_username is defined and artifactory_username != "" %} + + # Enable development catalogs + # ------------------------------------------------------------------------- + - name: artifactory_username + value: "{{ artifactory_username }}" + - name: artifactory_token + value: "{{ artifactory_token }}" +{%- endif %} + + workspaces: + # The generated configuration files + # ------------------------------------------------------------------------- + - name: shared-configs + persistentVolumeClaim: + claimName: config-pvc + + # PodTemplates configurations + # ------------------------------------------------------------------------- + - name: shared-pod-templates + secret: + secretName: pipeline-pod-templates