Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions src/mas/devops/tekton.py
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,83 @@ def prepareAiServicePipelinesNamespace(
logger.info(f"Storage class {storageClass} uses volumeBindingMode={volumeBindingMode}, skipping PVC bind wait")


def prepareMcpiPipelinesNamespace(
dynClient: DynamicClient,
instanceId: str = None,
storageClass: str = None,
accessMode: str = None,
waitForBind: bool = True,
configureRBAC: bool = True,
):
"""
Prepare a namespace for MCPI pipelines by creating RBAC and PVC resources.

Creates MCPI-specific pipeline namespace with necessary role bindings
and persistent volume claims.

Args:
dynClient (DynamicClient): OpenShift Dynamic Client
instanceId (str, optional): MCPI instance ID. Defaults to None.
storageClass (str, optional): Storage class for the PVC. Defaults to None.
accessMode (str, optional): Access mode for the PVC. Defaults to None.
waitForBind (bool, optional): Whether to wait for PVC to bind. Defaults to True.
configureRBAC (bool, optional): Whether to configure RBAC. Defaults to True.

Raises:
NotFoundError: If resources cannot be created
"""
templateDir = path.join(path.abspath(path.dirname(__file__)), "templates")
env = Environment(loader=FileSystemLoader(searchpath=templateDir))
namespace = f"mcpi-{instanceId}-pipelines"
template = env.get_template("mcpi-pipelines-rbac.yml.j2")

if configureRBAC:
renderedTemplate = template.render(mcpi_instance_id=instanceId)
logger.debug(renderedTemplate)
crb = yaml.safe_load(renderedTemplate)
applyResource(
dynClient=dynClient,
apiVersion="rbac.authorization.k8s.io/v1",
kind="ClusterRoleBinding",
body=crb,
namespace=namespace,
)

template = env.get_template("mcpi-pipelines-pvc.yml.j2")
renderedTemplate = template.render(
mcpi_instance_id=instanceId,
pipeline_storage_class=storageClass,
pipeline_storage_accessmode=accessMode,
)
logger.debug(renderedTemplate)
pvc = yaml.safe_load(renderedTemplate)
pvcAPI = dynClient.resources.get(api_version="v1", kind="PersistentVolumeClaim")
applyResource(
dynClient=dynClient,
apiVersion="v1",
kind="PersistentVolumeClaim",
body=pvc,
namespace=namespace,
)

volumeBindingMode = getStorageClassVolumeBindingMode(dynClient, storageClass)
waitForBind = volumeBindingMode == "Immediate"

if waitForBind:
logger.info(f"Storage class {storageClass} uses volumeBindingMode={volumeBindingMode}, waiting for PVC to bind")
pvcIsBound = False
while not pvcIsBound:
configPVC = pvcAPI.get(name="config-pvc", namespace=namespace)
if configPVC.status.phase == "Bound":
pvcIsBound = True
else:
logger.debug("Waiting 15s before checking status of PVC again")
logger.debug(configPVC)
sleep(15)
else:
logger.info(f"Storage class {storageClass} uses volumeBindingMode={volumeBindingMode}, skipping PVC bind wait")


def prepareRestoreSecrets(dynClient: DynamicClient, namespace: str, restoreConfigs: dict = None):
"""
Create or update secret required for MAS Restore pipeline.
Expand Down Expand Up @@ -1342,6 +1419,26 @@ def launchInstallPipeline(dynClient: DynamicClient, params: dict) -> str:
return pipelineURL


def launchMcpiInstallPipeline(dynClient: DynamicClient, params: dict) -> str:
"""
Create a PipelineRun to install an MCPI instance.

Args:
dynClient (DynamicClient): OpenShift Dynamic Client
params (dict): Installation parameters including mas_instance_id and mcpi_channel

Returns:
str: URL to the PipelineRun in the OpenShift console

Raises:
NotFoundError: If resources cannot be created
"""
instanceId = params["mas_instance_id"]
namespace = f"mcpi-{instanceId}-pipelines"
timestamp = launchPipelineRun(dynClient, namespace, "pipelinerun-mcpi-install", params)
return f"{getConsoleURL(dynClient)}/k8s/ns/mcpi-{instanceId}-pipelines/tekton.dev~v1beta1~PipelineRun/{instanceId}-install-{timestamp}"


def launchUpdatePipeline(dynClient: DynamicClient, params: dict) -> str:
"""
Create a PipelineRun to update the Maximo Operator Catalog.
Expand Down
15 changes: 15 additions & 0 deletions src/mas/devops/templates/mcpi-pipelines-pvc.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
# 1. Set up a PVC for shared storage
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: config-pvc
namespace: mcpi-{{ mcpi_instance_id }}-pipelines
spec:
accessModes:
- {{ pipeline_storage_accessmode }}
volumeMode: Filesystem
storageClassName: {{ pipeline_storage_class }}
resources:
requests:
storage: 500Mi
14 changes: 14 additions & 0 deletions src/mas/devops/templates/mcpi-pipelines-rbac.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
# 1. Configure RBAC for the pipeline tasks
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: mcpi-pipeline-{{ mcpi_instance_id }}
subjects:
- kind: ServiceAccount
name: pipeline
namespace: mcpi-{{ mcpi_instance_id }}-pipelines
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
150 changes: 150 additions & 0 deletions src/mas/devops/templates/pipelinerun-mcpi-install.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
---
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: "{{mas_instance_id}}-install-{{ timestamp }}"
labels:
tekton.dev/pipeline: mcpi-install
spec:
pipelineRef:
name: mcpi-install

taskRunTemplate:
serviceAccountName: "{{ service_account_name | default('pipeline', True) }}"
timeouts:
pipeline: "0"

params:
# IBM Entitlement Key
# -------------------------------------------------------------------------
- name: ibm_entitlement_key
value: "{{ ibm_entitlement_key }}"
{%- if skip_pre_check is defined and skip_pre_check != "" %}

# Pipeline config
# -------------------------------------------------------------------------
- name: skip_pre_check
value: "{{ skip_pre_check }}"
{%- endif %}
{%- if image_pull_policy is defined and image_pull_policy != "" %}

# Image Pull Policy
# -------------------------------------------------------------------------
- name: image_pull_policy
value: "{{ image_pull_policy }}"
{%- 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 %}

# Storage Classes
# -------------------------------------------------------------------------
- name: storage_class_rwx
value: "{{ storage_class_rwx }}"
- name: storage_class_rwo
value: "{{ storage_class_rwo }}"

# Dependencies - SLS
# -------------------------------------------------------------------------
{%- if sls_channel is defined and sls_channel != "" %}
- name: sls_channel
value: "{{ sls_channel }}"
{%- endif %}
{%- if sls_entitlement_file is defined and sls_entitlement_file != "" %}
- name: sls_entitlement_file
value: "{{ sls_entitlement_file }}"
{%- endif %}
{%- if sls_namespace is defined and sls_namespace != "" %}
- name: sls_namespace
value: "{{ sls_namespace }}"
{%- endif %}
{%- if sls_action is defined and sls_action != "" %}
- name: sls_action
value: "{{ sls_action }}"
{%- endif %}

# Dependencies - DRO (Required)
# -------------------------------------------------------------------------
- name: dro_action
value: "{{ dro_action }}"
- name: dro_contact_email
value: "{{ dro_contact_email }}"
- name: dro_contact_firstname
value: "{{ dro_contact_firstname }}"
- name: dro_contact_lastname
value: "{{ dro_contact_lastname }}"
{%- if dro_namespace is defined and dro_namespace != "" %}
- name: dro_namespace
value: "{{ dro_namespace }}"
{%- endif %}

# Dependencies - Certificate Manager
# -------------------------------------------------------------------------
- name: cert_manager_provider
value: "{{ cert_manager_provider }}"
- name: cert_manager_action
value: "{{ cert_manager_action }}"

# MAS Catalog
# -------------------------------------------------------------------------
- name: mas_catalog_version
value: "{{ mas_catalog_version }}"
{%- if mas_catalog_digest is defined and mas_catalog_digest != "" %}
- name: mas_catalog_digest
value: "{{ mas_catalog_digest }}"
{%- endif %}

# MAS Core
# -------------------------------------------------------------------------
- name: mas_instance_id
value: "{{ mas_instance_id }}"

# MCPI Add-On
# -------------------------------------------------------------------------
- name: mcpi_channel
value: "{{ mcpi_channel }}"
{%- if routing_mode is defined and routing_mode != "" %}
- name: routing_mode
value: "{{ routing_mode }}"
{%- endif %}
{%- if manual_route_mgmt is defined and manual_route_mgmt != "" %}
- name: manual_route_mgmt
value: "{{ manual_route_mgmt }}"
{%- endif %}

workspaces:
# The generated configuration files
# -------------------------------------------------------------------------
- name: shared-configs
persistentVolumeClaim:
claimName: config-pvc

# User-provided configurations
# -------------------------------------------------------------------------
- name: shared-additional-configs
secret:
secretName: pipeline-additional-configs

# SLS entitlement
# -------------------------------------------------------------------------
- name: shared-entitlement
secret:
secretName: pipeline-sls-entitlement

# PodTemplates configurations
# -------------------------------------------------------------------------
- name: shared-pod-templates
secret:
secretName: pipeline-pod-templates

# Certificates configurations
# -------------------------------------------------------------------------
- name: shared-certificates
secret:
secretName: pipeline-certificates
Loading
Loading