Skip to content

Commit

Permalink
Add support for Jenkins declarative pipelines (#127)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: mohanajuhi166 <mohanab@microsoft.com>
Co-authored-by: mohanajuhi166 <mohanajuhi166@gmail.com>
Co-authored-by: Hayward van Biljon <hvanbiljon@microsoft.com>
  • Loading branch information
4 people authored Apr 5, 2024
1 parent 8e48fa2 commit b97f867
Show file tree
Hide file tree
Showing 29 changed files with 1,587 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ jobs:
secrets:
azure_credentials: ${{ secrets.AZURE_CREDENTIALS }}
connection_details: ${{ secrets.COMMON_DEV_CONNECTIONS }}
registry_details: ${{ secrets.DOCKER_IMAGE_REGISTRY }}
registry_details: ${{ secrets.DOCKER_IMAGE_REGISTRY }}
100 changes: 100 additions & 0 deletions .jenkins/jobs/aml_real_deployment
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
@Library('shared-library') _
pipeline {
agent any

parameters {
string(name: 'env_name', defaultValue: "dev", description: 'env stage e.g. dev, test, prod')
string(name: 'flow_type', defaultValue: 'named_entity_recognition', description: 'flow type to be registered and deployed')
string(name: 'model_version', description: 'flow version in registry to be deployed')
string(name: 'run_id', description: 'run id of the flow to be deployed')
}

// Install requirements and provision AML Managed Online Endpoint
stages {
stage('Provision AML Online Endpoint') {
steps {
installRequirements('execute_job_requirements')
withCredentials([azureServicePrincipal('AZURE_CREDENTIALS')]) {
withPythonEnv('/usr/bin/python3.9') {
sh """
python -m llmops.common.deployment.provision_endpoint \
--subscription_id $AZURE_SUBSCRIPTION_ID \
--build_id $run_id \
--output_file "endpoint_principal.txt" \
--env_name $env_name \
--flow_to_execute $flow_type
"""
}
}
}
}

// Assign Azure resource permissions to endpoint principal
stage('Assign Azure resource permissions') {
steps {
azureLogin()
withCredentials([azureServicePrincipal('AZURE_CREDENTIALS')]) {
sh '''
endpoint_principal=$(cat endpoint_principal.txt)
echo $endpoint_principal
file_path="./$flow_type/llmops_config.json"
echo $file_path
env_type="$env_name"

selected_object=\$(jq ".envs[] | select(.ENV_NAME == \"$env_type\")" "\$file_path")
echo $selected_object

key_vault_name=$(echo "$selected_object" | jq -r ".KEYVAULT_NAME")
resource_group_name=$(echo "$selected_object" | jq -r ".RESOURCE_GROUP_NAME")
workspace_name=$(echo "$selected_object" | jq -r ".WORKSPACE_NAME")

az role assignment create --assignee $endpoint_principal --role "AzureML Data Scientist" --scope "/subscriptions/$AZURE_SUBSCRIPTION_ID/resourcegroups/$resource_group_name/providers/Microsoft.MachineLearningServices/workspaces/$workspace_name"

auth_type=$(az keyvault show -n $key_vault_name -g $resource_group_name --query "properties.enableRbacAuthorization")
if [[ -z "$auth_type" ]]; then
echo "assigning RBAC permission"
az role assignment create --assignee $endpoint_principal --role "Key Vault Reader" --scope "/subscriptions/$AZURE_SUBSCRIPTION_ID/resourcegroups/$resource_group_name/providers/Microsoft.KeyVault/vaults/$key_vault_name"
else
echo "assigning policy permission"
az keyvault set-policy --name $key_vault_name --resource-group $resource_group_name --object-id $endpoint_principal --secret-permissions get list
fi
'''
}
}
}

// Provision AML Online Deployment for given model version and run id
stage('Provision AML Online Deployment') {
steps {
withCredentials([azureServicePrincipal('AZURE_CREDENTIALS')]) {
withPythonEnv('/usr/bin/python3.9') {
sh """
python -m llmops.common.deployment.provision_deployment \
--subscription_id $AZURE_SUBSCRIPTION_ID \
--model_version $model_version \
--build_id $run_id \
--env_name $env_name \
--flow_to_execute $flow_type
"""
}
}
}
}

// Test Online AML Deployment
stage('Test AML Deployment') {
steps {
withCredentials([azureServicePrincipal('AZURE_CREDENTIALS')]) {
withPythonEnv('/usr/bin/python3.9') {
sh """
python -m llmops.common.deployment.test_model_on_aml \
--subscription_id $AZURE_SUBSCRIPTION_ID \
--env_name $env_name \
--flow_to_execute $flow_type
"""
}
}
}
}
}
}
62 changes: 62 additions & 0 deletions .jenkins/jobs/build_validation
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
@Library('shared-library') _
pipeline {
agent any

options {
// This is required if you want to clean before build
skipDefaultCheckout(true)
}

parameters {
string(name: 'flow_type', defaultValue: 'named_entity_recognition', description: 'The flow use-case to validate')
}

stages {
stage('Checkout') {
steps {
cleanWs()
checkout scm
}
}

// Load dependencies, login to Azure and load subscription details
stage('Load Dependencies') {
steps {
installRequirements('build_validation_requirements')
sh 'az version'
}
}

stage('Azure Login') {
steps {
azureLogin()
}
}

stage('Load Azure Subscription Details') {
steps {
withCredentials([azureServicePrincipal('AZURE_CREDENTIALS')]) {
sh '''
export subscriptionId=$(az account show --query id -o tsv)
echo "SUBSCRIPTION_ID=$AZURE_SUBSCRIPTION_ID"
'''
}
}
}

// Execute unit tests and publish results
stage('Execute Unit Tests') {
steps {
withPythonEnv('/usr/bin/python3.9') {
sh "pytest ${params.flow_type}/tests --junitxml=junit/test-results.xml --cov=. --cov-report=xml"
}
}
}

stage('Publish Unit Test Results') {
steps {
archiveArtifacts artifacts: '**/test-*.xml', fingerprint: true
}
}
}
}
99 changes: 99 additions & 0 deletions .jenkins/jobs/kubernetes_deployment
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
@Library('shared-library') _
pipeline {
agent any

parameters {
string(name: 'env_name', defaultValue: "dev", description: 'env stage e.g. dev, test, prod')
string(name: 'flow_type', defaultValue: 'named_entity_recognition', description: 'flow type to be registered and deployed')
string(name: 'model_version', description: 'flow version in registry to be deployed')
string(name: 'run_id', description: 'run id of the flow to be deployed')
}

// Install requirements and provision Kubernetes Online Endpoint
stages {
stage('Provision Kubernetes Online Endpoint') {
steps {
installRequirements('execute_job_requirements')
withCredentials([azureServicePrincipal('AZURE_CREDENTIALS')]) {
withPythonEnv('/usr/bin/python3.9') {
sh """
python -m llmops.common.deployment.kubernetes_endpoint \
--subscription_id $AZURE_SUBSCRIPTION_ID \
--build_id $run_id \
--output_file "endpoint_principal.txt" \
--env_name $env_name \
--flow_to_execute $flow_type
"""
}
}
}
}

// Assign Azure resource permissions to endpoint principal
stage('Assign Azure resource permissions') {
steps {
withCredentials([azureServicePrincipal('AZURE_CREDENTIALS')]) {
sh '''
endpoint_principal=$(cat endpoint_principal.txt)
echo $endpoint_principal
file_path="./$flow_type/llmops_config.json"
echo $file_path
env_type="$env_name"

selected_object=\$(jq ".envs[] | select(.ENV_NAME == \"$env_type\")" "\$file_path")
echo $selected_object

key_vault_name=$(echo "$selected_object" | jq -r ".KEYVAULT_NAME")
resource_group_name=$(echo "$selected_object" | jq -r ".RESOURCE_GROUP_NAME")
workspace_name=$(echo "$selected_object" | jq -r ".WORKSPACE_NAME")

az role assignment create --assignee $endpoint_principal --role "AzureML Data Scientist" --scope "/subscriptions/$AZURE_SUBSCRIPTION_ID/resourcegroups/$resource_group_name/providers/Microsoft.MachineLearningServices/workspaces/$workspace_name"

auth_type=$(az keyvault show -n $key_vault_name -g $resource_group_name --query "properties.enableRbacAuthorization")
if [[ -z "$auth_type" ]]; then
echo "assigning RBAC permission"
az role assignment create --assignee $endpoint_principal --role "Key Vault Reader" --scope "/subscriptions/$AZURE_SUBSCRIPTION_ID/resourcegroups/$resource_group_name/providers/Microsoft.KeyVault/vaults/$key_vault_name"
else
echo "assigning policy permission"
az keyvault set-policy --name $key_vault_name --resource-group $resource_group_name --object-id $endpoint_principal --secret-permissions get list
fi
'''
}
}
}

// Provision Kubernetes Online Deployment
stage('Provision Kubernetes Online Deployment') {
steps {
withCredentials([azureServicePrincipal('AZURE_CREDENTIALS')]) {
withPythonEnv('/usr/bin/python3.9') {
sh """
python -m llmops.common.deployment.kubernetes_deployment \
--subscription_id $AZURE_SUBSCRIPTION_ID \
--model_version $model_version \
--build_id $run_id \
--env_name $env_name \
--flow_to_execute $flow_type
"""
}
}
}
}

// Test Kubernetes Deployment
stage('Test Kubernetes Deployment') {
steps {
withCredentials([azureServicePrincipal('AZURE_CREDENTIALS')]) {
withPythonEnv('/usr/bin/python3.9') {
sh """
python -m llmops.common.deployment.test_model_on_kubernetes \
--subscription_id $AZURE_SUBSCRIPTION_ID \
--env_name $env_name \
--flow_to_execute $flow_type
"""
}
}
}
}
}
}
63 changes: 63 additions & 0 deletions .jenkins/jobs/prepare_docker_image
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
@Library('shared-library') _
pipeline {
agent any
parameters {
string(name: 'flow_to_execute', defaultValue: 'named_entity_recognition', description: 'The flow use-case to execute')
string(name: 'deploy_environment', defaultValue: 'dev', description: 'Execution Environment')
}

environment {
dev_connections = credentials('COMMON_DEV_CONNECTIONS')
registry_details = credentials('DOCKER_IMAGE_REGISTRY')
}

// Install requirements for job execution and build validation and create local promptflow connections
stages {
stage('Create local promptflow connections') {
steps {
installRequirements('execute_job_requirements')
installRequirements('build_validation_requirements')
withCredentials([azureServicePrincipal('AZURE_CREDENTIALS')]) {
withPythonEnv('/usr/bin/python3.9') {
sh '''
python -m llmops.common.prompt_local_connections \\
--flow_to_execute ${flow_to_execute} \\
--env_name ${deploy_environment} \\
--connection_details "$dev_connections"
'''
}
}
}
}

// Create Docker image and push to Azure Container Registry
stage('Create Docker Image') {
steps {
installRequirements('build_validation_requirements')
withCredentials([string(credentialsId: 'DOCKER_IMAGE_REGISTRY', variable: 'registry_details'),
string(credentialsId: 'COMMON_DEV_CONNECTIONS', variable: 'connection_details')]) {
withPythonEnv('/usr/bin/python3.9') {
sh """
# Your Docker image creation command here
echo "build no script:"
echo ${BUILD_NUMBER}
#!/bin/bash
./llmops/common/scripts/gen_docker_image.sh --flow_to_execute $flow_to_execute --deploy_environment $deploy_environment --build_id ${BUILD_NUMBER} --REGISTRY_DETAILS '${registry_details}' --CONNECTION_DETAILS '${connection_details}'
"""
}
}
}
}

// Deploy Docker image to Azure Webapp
stage('Create Webapp') {
steps {
withCredentials([string(credentialsId: 'COMMON_DEV_CONNECTIONS', variable: 'connection_details')]) {
sh """
/bin/bash ./llmops/common/scripts/az_webapp_deploy.sh --flow_to_execute $flow_to_execute --deploy_environment $deploy_environment --build_id ${BUILD_NUMBER} --CONNECTION_DETAILS '${connection_details}'
"""
}
}
}
}
}
Loading

0 comments on commit b97f867

Please sign in to comment.