Skip to content

Commit

Permalink
ServiceNow CMR integration (#3392)
Browse files Browse the repository at this point in the history
* added servicenow CMR template

added servicenow CMR template for github actions

* updated template to remove skms

updated template to remove skms and swap it with ServiceNow

* started adding servicenow api calls

started adding servicenow api calls

* added the servicenow cmr calls.

added the servicenow cmr calls.

* test branch added

test branch added

* removed skms.yml where we are using servicenow

removed skms.yml where we are using servicenow

* updated function calls and parameters

updated function calls and parameters

* test update

test update

* Updated calls to output to file and check output

Updated calls to output to file and check output for successful curl calls.  Fixes jq parsing command.

* testing output

testing output

* fixed github env variable declarations

fixed github env variable declarations for token, cmr_id, transaction_id, release_title

* added output to console for response from curl

added output to console for response from curl

* fixed file existence and size check

fixed file existence and size check

* small update

small update

* fixed curl statements and timestamps

fixed curl statements and timestamps

* another small update

another small update

* testing

testing

* testing theory

testing theory

* testing arguments

testing arguments

* second attempt

second attempt

* updated branch to remove caching

updated branch to remove caching from GitHub Actions, hopefully.

* reverted changes of new branch

reverted changes of new branch

* another small fix

another small fix

* testing a thought

testing a thought

* moved release summary to last param

moved release summary to last param where it stops other parameters from being read.

* testing out semantics for env variables

testing out semantics for env variables

* updated shell commands to fix errors

updated shell commands to fix errors

* small github env fixes

small github env fixes

* updated curl statement

updated curl statement

* updated calls and timespans

updated calls and timespans

* added printouts for debugging

added printouts for debugging

* small fixes

small fixes

* updated calls per guidance from winter solstice

updated calls per guidance from winter solstice team

* added new python script and removed shell script

added new python scripts and removed shell script

* added sanitization method

added sanitization method

* updated servicenow integration to use python

updated servicenow integration to use python

* updated variables and added headers

updated variables and added headers

* fixed cmr_id parsing and cmr closing

fixed cmr_id parsing and cmr closing

* added small test change for PR

added small test change for PR

* updated execution path

* fixing environment variable access

fixing environment variable access

* fixed branches in yaml

fixed branches in yaml

* updated approvers, removed executor

updated approvers, removed executor so that the default "Change Management API Integration" user is used instead.

* changed stage variables, urls to prod

changed stage variables, urls to prod
  • Loading branch information
amauch-adobe authored Jan 3, 2025
1 parent 29d77a6 commit a2443c0
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 130 deletions.
178 changes: 178 additions & 0 deletions .github/workflows/servicenow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import requests
import time
import datetime
import timedelta
import json
import os
import sys

def find_string_in_json(json_data, target_string):
"""
Finds a target string in a JSON object.
Args:
json_data (dict or list): The JSON data to search.
target_string (str): The string to find.
Returns:
bool: True if the string is found, False otherwise.
"""

if isinstance(json_data, dict):
for key, value in json_data.items():
if isinstance(value, str) and target_string in value:
return True
elif isinstance(value, (dict, list)):
if find_string_in_json(value, target_string):
return True
elif isinstance(json_data, list):
for item in json_data:
if isinstance(item, str) and target_string in item:
return True
elif isinstance(item, (dict, list)):
if find_string_in_json(item, target_string):
return True

return False

# Execute Script logic:
# python3 servicenow.py
if __name__ == "__main__":

print("Starting CMR Action...")

print("Setting Planned Maintenance Time Windows for CMR...")
start_time = (datetime.datetime.now() + datetime.timedelta(seconds = 10)).timestamp()
end_time = (datetime.datetime.now() + datetime.timedelta(minutes = 10)).timestamp()

print("Set Release Summary for CMR...")
release_title = os.environ['PR_TITLE']
release_details = os.environ['PR_BODY']
pr_num = os.environ['PR_NUMBER']
pr_created = os.environ['PR_CREATED_AT']
pr_merged = os.environ['PR_MERGED_AT']
release_summary = f"Release_Details: {release_details} \n\nPull Request Number: {pr_num} \nPull Request Created At: {pr_created} \nPull Request Merged At: {pr_merged}"

print("Getting IMS Token")
ims_url = 'https://ims-na1.adobelogin.com/ims/token'
headers = {"Content-Type":"multipart/form-data"}
data = {
'client_id': os.environ['IMSACCESS_CLIENT_ID'],
'client_secret': os.environ['IMSACCESS_CLIENT_SECRET'],
'grant_type': "authorization_code",
'code': os.environ['IMSACCESS_AUTH_CODE']
}
response = requests.post(ims_url, data=data)
jsonParse = json.loads(response.text)

if response.status_code != 200:
print("POST failed with response code: ", response.status_code)
print(response.text)
sys.exit(1)
elif find_string_in_json(jsonParse, "error"):
print("IMS token request failed with response code: ", response.status_code)
print(response.text)
sys.exit(1)
else:
print("IMS token request was successful")
token = jsonParse["access_token"]

print("Create CMR in ServiceNow...")

servicenow_cmr_url = 'https://ipaasapi.adobe-services.com/change_management/changes'
headers = {
"Accept":"application/json",
"Authorization":token,
"Content-Type":"application/json",
"api_key":os.environ['IPAAS_KEY']
}
data = {
"title":release_title,
"description":release_summary,
"instanceIds": [ 537445 ],
"plannedStartDate": start_time,
"plannedEndDate": end_time,
"coordinator": "narcis@adobe.com",
"customerImpact": "No Impact",
"changeReason": [ "New Features", "Bug Fixes", "Enhancement", "Maintenance", "Security" ],
"preProductionTestingType": [ "End-to-End", "Functional", "Integrations", "QA", "Regression", "UAT", "Unit Test" ],
"backoutPlanType": "Roll back",
"approvedBy": [ "casalino@adobe.com", "jmichnow@adobe.com", "mauchley@adobe.com", "bbalakrishna@adobe.com", "tuscany@adobe.com", "brahmbha@adobe.com" ],
"testPlan": "Test plan is documented in the PR link in the Milo repository above. See the PR's merge checks to see Unit and Nala testing.",
"implementationPlan": "The change will be released as part of the continuous deployment of Milo's production branch, i.e., \"main\"",
"backoutPlan": "Revert merge to the Milo production branch by creating a revert commit.", "testResults": "Changes are tested and validated successfully in staging environment. Please see the link of the PR in the description for the test results and/or the \"#nala-test-results\" slack channel."
}
response = requests.post(servicenow_cmr_url, headers=headers, json=data)
jsonParse = json.loads(response.text)

if response.status_code != 200:
print("POST failed with response code: ", response.status_code)
print(response.text)
sys.exit(1)
elif find_string_in_json(jsonParse, "error"):
print("CMR creation failed with response code: ", response.status_code)
print(response.text)
sys.exit(1)
else:
print("CMR creation was successful")
transaction_id = jsonParse["id"]

print("Waiting for Transaction from Queue to ServiceNow then Retrieve CMR ID...")

servicenow_get_cmr_url = f'https://ipaasapi.adobe-services.com/change_management/transactions/{transaction_id}'
headers = {
"Accept":"application/json",
"Authorization":token,
"api_key":os.environ['IPAAS_KEY']
}

# Wait 10 seconds to provide time for the transaction to exit the queue and be saved into ServiceNow as a CMR record.
time.sleep(10)
response = requests.get(servicenow_get_cmr_url, headers=headers)
jsonParse = json.loads(response.text)

if response.status_code != 200:
print("GET failed with response code: ", response.status_code)
print(response.text)
sys.exit(1)
elif find_string_in_json(jsonParse, "error"):
print("CMR ID retrieval failed with response code: ", response.status_code)
print(response.text)
sys.exit(1)
else:
print("CMR ID retrieval was successful")
cmr_id = jsonParse["result"]["changeId"]

print("Setting Actual Maintenance Time Windows for CMR...")
actual_start_time = (datetime.datetime.now() - datetime.timedelta(seconds = 10)).timestamp()
actual_end_time = datetime.datetime.now().timestamp()

print("Closing CMR in ServiceNow...")

headers = {
"Accept":"application/json",
"Authorization":token,
"Content-Type":"application/json",
"api_key":os.environ['IPAAS_KEY']
}
data = {
"id": transaction_id,
"actualStartDate": actual_start_time,
"actualEndDate": actual_end_time,
"state": "Closed",
"closeCode": "Successful",
"notes": "The change request is closed as the change was released successfully"
}
response = requests.post(servicenow_cmr_url, headers=headers, json=data)
jsonParse = json.loads(response.text)

if response.status_code != 200:
print("POST failed with response code: ", response.status_code)
print(response.text)
sys.exit(1)
elif find_string_in_json(jsonParse, "error"):
print("CMR closure failed with response code: ", response.status_code)
print(response.text)
sys.exit(1)
else:
print("CMR closure was successful")
44 changes: 44 additions & 0 deletions .github/workflows/servicenow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# This workflow will install Python dependencies, run CMR creation in ServiceNow
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Create CMR in ServiceNow

on:
pull_request:
types:
- closed
branches:
- main

permissions:
contents: read

env:
IMSACCESS_CLIENT_ID: ${{ secrets.IMSACCESS_CLIENT_ID }}
IMSACCESS_CLIENT_SECRET: ${{ secrets.IMSACCESS_CLIENT_SECRET_PROD }}
IMSACCESS_AUTH_CODE: ${{ secrets.IMSACCESS_AUTH_CODE_PROD }}
IPAAS_KEY: ${{ secrets.IPAAS_KEY_PROD }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_BODY: ${{ github.event.pull_request.body }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_CREATED_AT: ${{ github.event.pull_request.created_at }}
PR_MERGED_AT: ${{ github.event.pull_request.merged_at }}

jobs:
build:
# Only run this workflow on pull requests that have merged and not manually closed by user
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Set up Python 3.x, latest minor release
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install dependencies
run: |
python -m pip install --upgrade pip requests timedelta
- name: Execute script for creating and closing CMR
run: |
python ./.github/workflows/servicenow.py
130 changes: 0 additions & 130 deletions .github/workflows/skms.yaml

This file was deleted.

0 comments on commit a2443c0

Please sign in to comment.