Tech

MuleSoft Flex Gateway and Jenkins: The Power Duo for DevOps

Reading time: 16 minutes

MuleSoft’s Flex Gateway offers unprecedented flexibility, allowing you to manage and secure APIs running anywhere: on-premises, in Docker, or Kubernetes, hosted on AWS, or Heroku. But flexibility often comes with complexity. Managing the lifecycle of these gateways and their APIs manually can quickly become cumbersome and error-prone.

This is where Continuous Integration and Continuous Deployment (CI/CD) shines.

By integrating the MuleSoft Anypoint platform APIs into a Jenkins Pipeline, we can fully automate Exchange asset creation, API instance creation, apply policy and SLA tiers and deploy API instance to the runtime process for a Flex Gateway operating in Connected app mode with on-behalf of a user.

Why automate Flex Gateway deployment?

Flex Gateway is designed to be decoupled from the Mule runtime, meaning its lifecycle management needs to be handled externally. Automating this process provides critical benefits:

  • Consistency: Every deployment follows the exact same API calls, eliminating configuration drift
  • speed: Deploy APIs in seconds, not minutes, from creating Exchange assets to managing them at runtime with Flex Gateway
  • Auditability: Jenkins provides a clear, auditable trail for every API interaction
  • Flexibility: Direct API calls allow for complex logic and dynamic payload creation not easily achievable with a CLI
Illustrating the sequence of Jenkins pipeline stages

Prerequisites

To perform actions against the CloudHub Anypoint Platform, we need to set up authentication and gather the key identifiers.

Connected app (that acts on behalf of a user) and Scopes: A secure automation setup relies on the Connected App utilizing the JWT Bearer grant type. This configuration functions as a non-interactive service account, specifically for tools like Jenkins, and configuration requires authorization from a specific user with the necessary permissions, allowing the automation to operate on that user’s behalf. It must include the following minimum scopes:

  • API Manager: Application Creator, Manage APIs Configuration, Manage Policies, View Policies, Manage Client Applications and Deploy API Proxies
  • Flex Gateway: Read Servers and Manage Servers
  • Runtime manager: Read Servers, Manage Servers and CloudHub Network Viewer
  • Exchange: Exchange Contributor
  • General: View Organization and View Environment
  • OpenID: Profiles

Obtain authorization token (Bearer token): The Connected App client ID and JWT claims are exchanged for a temporary bearer token using the Access Management API endpoint. After generating the authentication token, you must pass the JWT bearer token as the ACCESS_TOKEN parameter in the successful pipeline steps. This generated token’s validity is based on the user’s permissions, and the pipeline will fail if the user lacks the necessary access to the organization and environment ID provided.

The Process Flow: Generating an Access Token via a Connected App

Required parameters: Your Jenkins Pipeline will rely on these values, ideally stored as Jenkins secrets or parameters:

parameter Source Purpose
ACCESS_TOKEN Connected app User authorization.
ORG_ID and ENV_ID Anypoint Platform Targets the specific organization and environment.
EXCHANGE_ASSET_NAME and EXCHANGE_ASSET_VERSION Anypoint Exchange Asset name and Asset version coordinates of the API asset being deployed.
UPLOAD_FILE Design Center Download a valid RAML or OAS file which is required to create REST API exchange asset
FLEXGATEWAY_ID Runtime Manager (Managed Flex Gateways) A unique managed flex gateway ID of your Flex Gateway instance.

The Jenkins Pipeline stages

The automation workflow is executed in several stages within your Jenkinsfile. We’ll focus on the powerful curl commands needed to interact with the Anypoint Platform APIs.

Stage 1: Parameter setup

Add the parameter definitions to your Jenkinsfile to validate whether all parameters are present. Define the parameters in the pipeline block before the stages block:

pipeline {
    agent any
parameters {
    string(name: 'ORG_ID', defaultValue: '', description: 'Your organization id')
    string(name: 'ENV_ID', defaultValue: '', description: 'Your environment id')
    string(name: 'ACCESS_TOKEN', defaultValue: '', description: 'Your jwt access token')
    string(name: 'ASSET_TYPE', defaultValue: 'rest-api', description: 'Enter a valid asset type like rest-api, mcp, a2a, llm')
    file(name: 'UPLOAD_FILE', description: 'Choose a file to upload if asset type is rest-api')
    string(name: 'EXCHANGE_ASSET_NAME', defaultValue: '', description: 'The name of the exchange asset')
    string(name: 'EXCHANGE_ASSET_VERSION', defaultValue: '', description: 'The version of the exchange asset')
    string(name: 'FLEXGATEWAY_ID', defaultValue: '', description: 'Your Flex Gateway target ID')
    string(name: 'UPSTREAM_URI', defaultValue: '', description: 'Provide a valid upstream url')
}

Stage 2: Create Asset in Exchange

An asset must first be available in Anypoint Exchange before you can create an API instance in API Manager. This initial step involves creating the asset in Exchange, ensuring the correct asset type is selected.

stage('Create Asset in Exchange') {
    steps {
	script {
def uploadedFilePath = params.UPLOAD_FILE
        def cmd = """
curl -s -w "%{http_code}" --location --request POST --location ' \
--header 'Accept: application/json, text/plain, */*' \
--header 'Authorization: Bearer ${params.ACCESS_TOKEN}' \
--form 'type="${params.ASSET_TYPE}"' \
--form 'name="${params.EXCHANGE_ASSET_NAME}"' \
--form 'status="published"' \
--form 'properties.apiVersion="v1"' \
--form 'properties.mainFile="${uploadedFilePath}"' \
--form 'files.oas.json=@"${uploadedFilePath}"' \
--form 'contactName="${env.BUILD_USER}"' \
--form 'contactEmail="${env.BUILD_USER_EMAIL}"'
"""
def status_code = sh(returnStdout: true, script: cmd).trim().toInteger()
if (status_code > 399) {
error("Failed to create the asset in Anypoint Exchange due to an error")
 			}
   		}
}
}

Stage 3: Create API Instance

This is the core deployment step, where we use the Bearer Token to instruct the API Manager to create a new API instance.

stage("Create API instance") {
    steps {
        script {
            def cmd = """
curl -s -w "%{http_code}" --location --request POST ' -o 'api-creation-response' \
--header 'Authorization: Bearer ${params.ACCESS_TOKEN}' \
--header 'Content-Type: application/json' \
--data-raw '{
    "spec": {
        "groupId": "${params.ORG_ID}",
        "assetId": "${params.EXCHANGE_ASSET_NAME}",
        "version": "${params.EXCHANGE_ASSET_VERSION}"
    },
    "endpoint": {
        "deploymentType": "HY",
        "uri": "${params.UPSTREAM_URI}",
        "proxyUri": "
        "isCloudHub": null
    },
    "technology": "flexGateway"
}'
"""
def status_code = sh(returnStdout: true, script: cmd).trim().toInteger()
if (status_code > 399) {
error("Failed to create the API instance in API Manager due to an error")
            }
        }
    }
}

Stage 4: Apply a policy and SLA tier (optional, but recommended)

In this stage, we will automatically secure the recently deployed API. This is accomplished by applying a policy, specifically a Client ID enforcement policy, which includes an SLA tier limiting it to a maximum of 1000 requests per 60,000 milliseconds.

stage("Apply Policy & SLA Tier") {
  when {
     expression { env.API_ID != null }
  }
  steps {
  	script {
		def api_response = readJSON file: 'api-creation-response'
		def env.api_id = api_response.id

#Apply policy
		def cmd = """curl -s -w "%{http_code}" -o /dev/null --location --request POST ' \
		  --header 'Authorization: Bearer ${params.ACCESS_TOKEN}' \
		  --header 'Content-Type: application/json' \
		  --data-raw '{
    		"configurationData": {
"credentialsOriginHasHttpBasicAuthenticationHeader": "customExpression",
        		"clientIdExpression": "#[attributes.headers['client_id']]",
        		"clientSecretExpression": "#[attributes.headers['client_secret']]"
    },
    "pointcutData": null,
    "assetId": "",
    "assetVersion": "",
    "groupId": ""
}'
	"""
    def status_code = sh(returnStdout: true, script: cmd).trim().toInteger()
    if (status_code > 399) {
error("Failed to apply Client ID enforcement policy to API instance")
    }

#Apply SLA tier
		def cmd = """curl -s -w "%{http_code}" -o /dev/null --location --request POST ' \
	    	--header 'Authorization: Bearer ${params.ACCESS_TOKEN}' \
	    	--header 'Content-Type: application/json' \
	    	--data-raw '{
    "status": "ACTIVE",
    "autoApprove": false,
    "apiVersionId": env.API_ID,
    "name": "silver",
    "description": null,
    "limits": [
        {
            "maximumRequests": 1000,
            "timePeriodInMilliseconds": 60000,
            "visible": true
        }
    ]
}'
		"""
    def status_code = sh(returnStdout: true, script: cmd).trim().toInteger()
    if (status_code > 399) {
			error("Failed to apply SLA Tier to API instance")
    }
   }
  }
}

Stage 5: Deploy API instance

The next step, after successfully creating the API instance, is deployment. The instance must be deployed to a Flex Gateway that is available within Runtime Manager.

stage("Apply Policy & SLA Tier") {
  when {
     expression { env.API_ID != null }
  }
  steps {
  	script {
		def cmd = """curl -s -w "%{http_code}" -o /dev/null --location --request POST ' \
	    	--header 'Authorization: Bearer ${params.ACCESS_TOKEN}' \
	    	--header 'Content-Type: application/json' \
	    	--data-raw '{
    "gatewayVersion": "latest",
    "environmentId": env.ENV_ID,
    "targetId": params.FLEXGATEWAY_ID,
    "type": "HY"
}'
			"""
    def status_code = sh(returnStdout: true, script: cmd).trim().toInteger()
    if (status_code > 399) {
error("Failed to deploy API instance in provided Managed Flex Gateway")
    }
   else {
	env.DEPLOYMENT_SUCCESSFUL = "true"
     }
   }
  }
}

Stage 6: Post-failure cleanup

If a deployment fails, immediate deletion of the newly created API instance from API Manager is mandatory. This rollback prevents stale resources, maintains system integrity, ensures idempotency for retries, and upholds governance. Therefore, the automated deployment pipeline must include robust error-handling to trigger API instance removal upon failure.

post {
    always {
	script {
	  if (env.DEPLOYMENT_SUCCESSFUL == "false" || env.API_ID == null) {
	echo 'Cleaning up failed deployment...'
	def cmd = """curl -s -w "%{http_code}" -o /dev/null --location --request DELETE ' \
	 --header 'Authorization: Bearer ${params.ACCESS_TOKEN}' \
	 --header 'Content-Type: application/json'
	"""
     def status_code = sh(returnStdout: true, script: cmd).trim().toInteger()
	if (status_code > 399) {
		error("Failed to delete API instance from Anypoint API Manager")
	}
      }
  else {
echo 'No cleanup needed'
}
    }
   }
  }
 }
}

Fully automated, integrated, and governed

Adopting a Jenkins Pipeline for MuleSoft’s Flex Gateway management transforms it from an ad-hoc component into a fully automated, integrated, and governed part of your enterprise infrastructure.

This pipeline-driven approach offers:

  • Full automation and consistency: Codifying deployment eliminates manual errors, ensuring identical, consistent Flex Gateway configurations across all environments
  • CI/CD integration: Seamlessly embeds Flex Gateway operations into existing CI/CD workflows, subjecting configurations to the same rigorous, version-controlled testing and promotion standards as core application code.
  • Enhanced governance and compliance: The pipeline provides an auditable single source of truth. Mandatory approval gates ensure only validated, authorized changes reach production, crucial for regulatory compliance

Leveraging a Jenkins Pipeline operationalizes the Flex Gateway’s flexibility, elevating it to a reliable, enterprise-grade asset managed with high standards of discipline.

Source link

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button
Close

Adblock Detected

kindly turn off ad blocker to browse freely