diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index c0ad6e0..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,101 +0,0 @@ -# Python CircleCI 2.0 configuration file -# -# Check https://circleci.com/docs/2.0/language-python/ for more details -# - -version: 2.1 - -executors: - content-executor: - docker: - - image: cimg/base:stable - auth: - username: $DOCKERHUB_LOGIN # can specify string literal values - password: $DOCKERHUB_TOKEN # or project environment variable reference - working_directory: ~/repo - -jobs: - splunk-appinspect: - executor: content-executor - steps: - - run: - name: install ucc-gen and dependencies - command: | - sudo apt-get update - sudo apt-get install -y python3-pip - pip3 install --no-input splunk-add-on-ucc-framework - - run: - name: checkout Git repository - command: | - if [ "${CIRCLE_BRANCH}" == "" ]; then - git clone https://${GITHUB_TOKEN}@github.com/guilhemmarchand/TA-azure-blob-archiving - else - git clone --branch ${CIRCLE_BRANCH} https://${GITHUB_TOKEN}@github.com/guilhemmarchand/TA-azure-blob-archiving - fi - - run: - name: checkout Splunk Appinspect shell executor - command: | - git clone https://github.com/guilhemmarchand/splunk-appinspect-pipeline.git - - restore_cache: - key: deps1-{{ .Branch }}-{{ checksum "TA-azure-blob-archiving/package/default/app.conf" }} - - run: - name: run appinspect API app vetting - command: | - cd TA-azure-blob-archiving - OUTDIR="output" - app="TA-azure-blob-archiving" - version=$(grep 'version =' version.txt | head -1 | awk '{print $3}' | sed 's/\.//g') - ta_version=$(grep 'version =' version.txt | head -1 | awk '{print $3}') - ucc-gen --ta-version "$ta_version" - cd "${OUTDIR}" - find . -name "*.pyc" -type f -exec rm -f {} \; - tar -czf ${app}.tgz ${app} - echo "Wrote: ${app}.tgz" - mkdir $HOME/dist - mv ${app}.tgz $HOME/dist/ - cd $HOME - $HOME/repo/splunk-appinspect-pipeline/appinspect_vetting.sh --username=$SPLUNK_BASE_LOGIN --password=$SPLUNK_BASE_PASSWD --app=$HOME/dist/${app}.tgz --included_tags=splunk_appinspect --excluded_tags=manual --excluded_checks=check_indexes_conf_does_not_exist --html_report_out=/tmp/appinspect_report.html - - save_cache: - key: deps1-{{ .Branch }}-{{ checksum "TA-azure-blob-archiving/package/default/app.conf" }} - paths: - - "venv" - - persist_to_workspace: - root: /home/circleci/dist - paths: - - TA-azure-blob-archiving.tgz - - store_artifacts: - path: /tmp/appinspect_report.html - destination: appinspect-report - publish-github-release: - docker: - - image: cibuilds/github:latest - auth: - username: $DOCKERHUB_LOGIN # can specify string literal values - password: $DOCKERHUB_TOKEN # or project environment variable reference - steps: - - attach_workspace: - at: ~/dist/TA-azure-blob-archiving.tar.gz - - run: - name: publish release on github - command: | - ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${CIRCLE_TAG} ~/dist/TA-azure-blob-archiving.tar.gz -workflows: - version: 2.1 - validate-and-build: - jobs: - - splunk-appinspect: - context: - - docker-hub-creds - - splunk-base-creds - filters: - tags: - only: /.*/ - - publish-github-release: - context: docker-hub-creds - requires: - - splunk-appinspect - filters: - tags: - only: /^v.*/ - branches: - ignore: /.*/ diff --git a/.github/workflows/github-actions-build.yml b/.github/workflows/github-actions-build.yml new file mode 100644 index 0000000..c95695e --- /dev/null +++ b/.github/workflows/github-actions-build.yml @@ -0,0 +1,79 @@ +name: Python package + +on: [push] + +jobs: + build_release: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10"] + + steps: + - run: echo "job automatically triggered by a ${{ github.event_name }} event." + + - uses: actions/checkout@v3 + + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Python requirements + run: | + pip install -r requirements.txt + + # + # Build release and run Appinspect + # + + - name: Build the release and submit to Splunk Appinspect vetting + env: + SPLUNK_BASE_LOGIN: ${{ vars.SPLUNK_BASE_LOGIN }} + SPLUNK_BASE_PASSWD: ${{ secrets.SPLUNK_BASE_PASSWD }} + run: | + cd build; python3 build.py --keep --submitappinspect --userappinspect "$SPLUNK_BASE_LOGIN" --passappinspect "$SPLUNK_BASE_PASSWD" + id: build_and_submit_appinspect + + # + # Archive artifacts + # + + - name: Archive Appinspect html report + if: always() + uses: actions/upload-artifact@v3 + with: + name: appinspect-report-html + path: output/report_appinspect.html + + - name: Archive Appinspect json report + if: always() + uses: actions/upload-artifact@v3 + with: + name: appinspect-report-json + path: output/report_appinspect.json + + - name: Show output directory content + run: | + ls -ltr output/ + + - name: Retrieve version number + run: | + echo "VERSION_NUMBER=$(cat output/version.txt)" >> $GITHUB_ENV + + - name: Show version number + run: | + echo "Version number is ${{ env.VERSION_NUMBER }}" + + - name: Retrieve build number + run: | + echo "BUILD_NUMBER=$(cat output/build.txt)" >> $GITHUB_ENV + + - name: Show build number + run: | + echo "Build number is ${{ env.BUILD_NUMBER }}" + + # + # + # + + - run: echo "End of process, job status ${{ job.status }}." diff --git a/build/appinspect.sh b/build/appinspect.sh deleted file mode 100755 index 47c21be..0000000 --- a/build/appinspect.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/bin/bash - -#set -x -unset username -unset uuid - -echo -n "Enter your Splunk Base login: "; read username - -echo "Attempting login to appinspect API..." - -export appinspect_token=$(curl -X GET \ - -u ${username} \ - --url "https://api.splunk.com/2.0/rest/login/splunk" -s | sed 's/%//g' | jq -r .data.token) - -case "$appinspect_token" in -"null") - echo "ERROR: login to appinspect API has failed, an authentication token could be not be generated."; exit 1 - ;; -*) - echo "SUCCESS: Authentication was successful and we got a token." - ;; -esac - -for app in $(ls *.tgz); do - - echo -n "RUN: Please confirm submitting the app ${app} to appinspect API vetting (yes / no) ? "; read submit - case ${submit} in - y|yes|Yes) - echo "RUN: Please wait while submitting to appinspect..." - uuid=$(curl -X POST \ - -H "Authorization: bearer ${appinspect_token}" \ - -H "Cache-Control: no-cache" \ - -s \ - -F "app_package=@${app}" \ - --url "https://appinspect.splunk.com/v1/app/validate" | jq -r .links | grep href | head -1 | awk -F\" '{print $4}' | awk -F\/ '{print $6}') - - if [ $? -eq 0 ]; then - echo "INFO: upload was successful, polling status..." - - status=$(curl -X GET \ - -s \ - -H "Authorization: bearer ${appinspect_token}" \ - --url https://appinspect.splunk.com/v1/app/validate/status/${uuid} | jq -r .status) - - while [ $status != "SUCCESS" ]; do - - echo -e "INFO: appinspect is currently running: \n" - echo "INFO: Sleeping 2 seconds..." - - curl -X GET \ - -s \ - -H "Authorization: bearer ${appinspect_token}" \ - --url https://appinspect.splunk.com/v1/app/validate/status/${uuid} | jq - sleep 2 - status=$(curl -X GET \ - -s \ - -H "Authorization: bearer ${appinspect_token}" \ - --url https://appinspect.splunk.com/v1/app/validate/status/${uuid} | jq -r .status) - - done - - case ${status} in - "SUCCESS") - echo "INFO: appinspect review was successfully proceeded:" - curl -X GET \ - -s \ - -H "Authorization: bearer ${appinspect_token}" \ - --url https://appinspect.splunk.com/v1/app/validate/status/${uuid} | jq . - echo -e "RUN: Download the HTML report in the current directory? (yes / no) "; read download - - case ${download} in - y|yes|Yes) - datetime=$(date '+%m%d%Y_%H%M%S') - filename="appinspect_report_${datetime}.html" - curl -X GET \ - -s \ - -H "Authorization: bearer ${appinspect_token}" \ - -H "Cache-Control: no-cache" \ - -H "Content-Type: text/html" \ - --url "https://appinspect.splunk.com/v1/app/report/${uuid}" \ - -o ${filename} - - echo "INFO: report downloaded to file ${filename} in the current directory." - - ;; - n|no|No) - echo "INFO: Operation completed for ${app} - thank you." - ;; - esac - - ;; - "*") - echo "ERROR: appinspect review was not successful!" - ;; - - esac - - else - echo "ERROR: upload has failed!" - break - - fi - - ;; - n|no|No) - - echo "INFO: Application was not submitted" - - ;; - - esac - -done - -exit 0 diff --git a/build/build.py b/build/build.py new file mode 100755 index 0000000..9beb3a9 --- /dev/null +++ b/build/build.py @@ -0,0 +1,370 @@ +#!/usr/bin/env python +# coding=utf-8 + +from __future__ import absolute_import, division, print_function, unicode_literals + +__author__ = "TrackMe Limited" +__version__ = "0.1.0" +__maintainer__ = "TrackMe Limited" +__status__ = "PRODUCTION" + +import os, sys +import time +import shutil +import tarfile +import json +import logging +import argparse +import glob +import subprocess +import configparser +import hashlib +import requests + +# load libs +sys.path.append('libs') +from tools import cd, login_appinspect, submit_appinspect, verify_appinspect, download_htmlreport_appinspect, download_jsonreport_appinspect + +# Args +parser = argparse.ArgumentParser() +parser.add_argument('--debug', dest='debug', action='store_true') +parser.add_argument('--keep', dest='keep', action='store_true') +parser.add_argument('--submitappinspect', dest='submitappinspect', action='store_true') +parser.add_argument('--userappinspect', dest='userappinspect') +parser.add_argument('--passappinspect', dest='passappinspect') +parser.set_defaults(debug=False) +parser.set_defaults(keep=False) +parser.set_defaults(submitappinspect=False) +args = parser.parse_args() + +# Set debug boolean +if args.debug: + debug = True +else: + debug = False + +# Set keep boolean +if args.keep: + keep = True +else: + keep = False + +# Set appinspect_vetting +if args.submitappinspect: + submitappinspect = True +else: + submitappinspect = False + +# Set appinspect_username +if args.userappinspect: + userappinspect = args.userappinspect +else: + userappinspect = False + +# Set appinspect_password +if args.passappinspect: + passappinspect = args.passappinspect +else: + passappinspect = False + +# set logging +root = logging.getLogger() +root.setLevel(logging.DEBUG) +handler = logging.StreamHandler(sys.stdout) +handler.setLevel(logging.DEBUG) +formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') +handler.setFormatter(formatter) +root.addHandler(handler) + +if debug: + root.setLevel(logging.DEBUG) + handler.setLevel(logging.DEBUG) +else: + root.setLevel(logging.INFO) + handler.setLevel(logging.INFO) + +# version file +version_file = "../version.json" + +# build_dir +build_dir = "../build" + +# package dir +package_dir = "../package" + +# output_dir +output_dir = "../output" + + +# +# functions +# + +# get release number +def get_release_number(): + + # Get the version release number + try: + with open(version_file) as f: + version_data = json.load(f) + version_release_number = version_data['version'] + logging.info('**** TrackMe package generation, version=\"{}\" ****'.format(version_release_number)) + + except Exception as e: + logging.error("Failed to retrieve the version release number, exception=\"{}\"".format(e)) + version_release_number = "1.0.0" + + # return + return version_release_number + + +# get app id +def get_app_id(): + + # Get the version release number + try: + with open(version_file) as f: + version_data = json.load(f) + logging.info("version_data=\"{}\"".format(version_data)) + appID = version_data['appID'] + logging.info('**** app generation, appID=\"{}\" ****'.format(appID)) + + except Exception as e: + logging.error("Failed to retrieve the appID, exception=\"{}\"".format(e)) + raise ValueError("Failed to retrieve the appID, exception=\"{}\"".format(e)) + + # return + return appID + + +# gen organisation applications +def gen_app(): + + # get release number + version_release_number = get_release_number() + + # get app ID + appID = get_app_id() + + # Purge any existing tgz in the output directory + files = glob.glob(os.path.join(output_dir, '*.tgz')) + for file_name in files: + logging.debug('Attempting to remove existing tgz archive=\"{}\"'.format(file_name)) + if os.path.isfile(file_name): + try: + os.remove(file_name) + logging.debug('Archive=\"{}\" was deleted successfully'.format(file_name)) + except Exception as e: + logging.error('Archive=\"{}\" could not be deleted, exception=\"{}\"'.format(file_name, e)) + + # Purge Appinspect previous reports + files = glob.glob(os.path.join(output_dir, 'report_*.html')) + for file_name in files: + logging.debug('Attempting to remove report=\"{}\"'.format(file_name)) + if os.path.isfile(file_name): + try: + os.remove(file_name) + logging.debug('Report=\"{}\" was deleted successfully'.format(file_name)) + except Exception as e: + logging.error('Report=\"{}\" could not be deleted, exception=\"{}\"'.format(file_name, e)) + + files = glob.glob(os.path.join(output_dir, 'report_*.json')) + for file_name in files: + logging.debug('Attempting to remove report=\"{}\"'.format(file_name)) + if os.path.isfile(file_name): + try: + os.remove(file_name) + logging.debug('Report=\"{}\" was deleted successfully'.format(file_name)) + except Exception as e: + logging.error('Report=\"{}\" could not be deleted, exception=\"{}\"'.format(file_name, e)) + + # Set app_root + app_root = os.path.join(output_dir, appID) + + # Remove current app if it exists + if os.path.isdir(app_root): + logging.debug("appID=\"{}\" purging existing directory app_root=\"{}\"".format(appID, app_root)) + try: + shutil.rmtree(app_root) + except Exception as e: + logging.error("appID=\"{}\", failed to purge the existing build directory=\"{}\" with exception=\"{}\"".format(appID, app_root, e)) + raise ValueError("appID=\"{}\", failed to purge the existing build directory=\"{}\" with exception=\"{}\"".format(appID, app_root, e)) + + # Package + with cd("../"): + logging.info("Call ucc-gen") + subprocess.run(["ucc-gen", "build", "--ta-version", version_release_number]) + + # Package + with cd(output_dir): + + # read app.conf to retrieve the current build + try: + config = configparser.ConfigParser() + config.read(os.path.join(app_root, "default", "app.conf")) + build_number = config['install']['build'] + + except Exception as e: + logging.error("failed to retrieve the build number with exception=\"{}\"".str(e)) + sys.exit(1) + + # save the version number to a simple text file for further usage + version_number_file = "version.txt" + with open(version_number_file, "w") as f: + f.write(str(version_release_number).replace(".", "") + "\n") + + # save the build number to a simple text file for further usage + build_number_file = "build.txt" + with open(build_number_file, "w") as f: + f.write(build_number + "\n") + + # gen tar + tar_file = str(app_root) + '_v' + str(version_release_number).replace(".", "") + "_" + str(build_number) + '.tgz' + out = tarfile.open(tar_file, mode='w:gz') + + try: + out.add(str(appID)) + except Exception as e: + logging.error("appID=\"{}\", archive file=\"{}\" creation failed with exception=\"{}\"".format(appID, tar_file, e)) + raise ValueError("appID=\"{}\", archive file=\"{}\" creation failed with exception=\"{}\"".format(appID, tar_file, e)) + finally: + logging.info('"appID=\"{}\", Achive tar file creation, archive_file=\"{}\"'.format(appID, tar_file)) + out.close() + + # get sha256 + logging.info("Get and store the sha256 control sum") + + with open(tar_file, "rb") as f: + bytes = f.read() # read entire file as bytes + readable_hash = hashlib.sha256(bytes).hexdigest() + logging.info("sha256 control sum=\"{}\"".format(readable_hash)) + + sha256_file = "release-sha256.txt" + with open(sha256_file, "w") as f: + f.write(readable_hash + "\t" + str(appID) + '_v' + str(version_release_number).replace(".", "") + "_" + str(build_number) + '.tgz' + "\n") + + # log info + logging.info('**** TrackMe app generation terminated, appID=\"{}\", build_number=\"{}\", sha256=\"{}\" ****'.format(appID, build_number, readable_hash)) + + # Remove build directories + if not keep: + if os.path.isdir(app_root): + logging.debug("appID=\"{}\", purging existing directory app_root=\"{}\"".format(appID, app_root)) + try: + shutil.rmtree(app_root) + except Exception as e: + logging.error("appID=\"{}\", failed to purge the build directory=\"{}\" with exception=\"{}\"".format(appID, app_root, e)) + raise ValueError("appID=\"{}\", failed to purge the build directory=\"{}\" with exception=\"{}\"".format(appID, app_root, e)) + + +# Generate the application release +gen_app() + +# If requested, perform the validation through Appinspect + +# get app ID +appID = get_app_id() + +if submitappinspect and (not userappinspect or not passappinspect): + logging.error("Appinspect vetting process request but login or password were not provided") + sys.exit(1) + +if submitappinspect and userappinspect and passappinspect: + + # login to Appinspect + appinspect_token = login_appinspect(userappinspect, passappinspect) + + if appinspect_token: + logging.info("Appsinspect: successfully logged in Appinspect API") + + # use session pooling + with requests.Session() as session: + + # loop + with cd(output_dir): + + + appinspect_requestids = [] + + # Purge any existing tgz in the output directory + files = glob.glob(os.path.join(output_dir, appID + '*.tgz')) + for file_name in files: + if os.path.isfile(file_name): + logging.info('Submitting to Appinspect API=\"{}\"'.format(file_name)) + + # set None + appinspect_response = None + + # submit + appinspect_response = submit_appinspect(session, appinspect_token, file_name) + + # append to the list + if appinspect_response: + appinspect_requestids.append(json.loads(appinspect_response)['request_id']) + + # Wait for all Appinspect vettings to be processed + logging.debug("Appinspect request_ids=\"{}\"".format(appinspect_requestids)) + + # sleep 2 seconds + time.sleep(2) + + # loop per request id + for request_id in appinspect_requestids: + + # check appinspect status + vetting_response = verify_appinspect(session, appinspect_token, request_id) + + if not vetting_response: + raise Exception("Appinspect verification has permanently failed.") + else: + vetting_status = json.loads(vetting_response)['status'] + + # init counter + attempts_counter = 0 + + # Allow up to 150 attempts + while vetting_status == 'PROCESSING' and attempts_counter<150: + attempts_counter+=1 + # sleep 2 seconds + time.sleep(2) + vetting_response = verify_appinspect(session, appinspect_token, request_id) + vetting_status = json.loads(vetting_response)['status'] + + if vetting_status == 'SUCCESS': + logging.info("Appinspect request_id=\"{}\" was successfully processed".format(request_id)) + elif vetting_status == 'FAILURE': + logging.error("Appinspect request_id=\"{}\" reported failed, vetting was not accepted!".format(request_id)) + else: + logging.error("Appinspect request_id=\"{}\" status is unknown or not expected, review the report if available".format(request_id)) + + # Download the HTML report + appinspect_report = download_htmlreport_appinspect(session, appinspect_token, request_id) + + if appinspect_report: + f = open(os.path.join(output_dir, "report_appinspect.html"), "w") + f.write(appinspect_report) + f.close() + logging.info("Appinspect written to report=\"{}\"".format(os.path.join(output_dir, "report_appinspect.html"))) + + # Download the JSON report + appinspect_report = download_jsonreport_appinspect(session, appinspect_token, request_id) + + if appinspect_report: + f = open(os.path.join(output_dir, "report_appinspect.json"), "w") + f.write(json.dumps(json.loads(appinspect_report), indent=4)) + f.close() + logging.info("Appinspect written to report=\"{}\"".format(os.path.join(output_dir, "report_appinspect.json"))) + + # Load the json dict + appinspect_report_dict = json.loads(appinspect_report) + + count_failure = int(appinspect_report_dict['summary']['failure']) + count_error = int(appinspect_report_dict['summary']['failure']) + + if count_failure == 0 and count_error == 0: + logging.info("Appinspect request_id=\"{}\" was successfully vetted, summary=\"{}\"".format(request_id, json.dumps(appinspect_report_dict['summary'], indent=4))) + else: + logging.error("Appinspect request_id=\"{}\" could not be vetted, review the report for more information, summary=\"{}\"".format(request_id, json.dumps(appinspect_report_dict['summary'], indent=4))) + raise ValueError("Appinspect request_id=\"{}\" could not be vetted, review the report for more information, summary=\"{}\"".format(request_id, json.dumps(appinspect_report_dict['summary'], indent=4))) + +sys.exit(0) diff --git a/build/build.sh b/build/build.sh deleted file mode 100755 index af4e859..0000000 --- a/build/build.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash -#set -x - -# for Mac OS X -export COPYFILE_DISABLE=true - -PWD=$(pwd) -OUTDIR="output" - -app="TA-azure-blob-archiving" -version=$(grep 'version =' ../version.txt | head -1 | awk '{print $3}' | sed 's/\.//g') -ta_version=$(grep 'version =' ../version.txt | head -1 | awk '{print $3}') - -cd ../ -ucc-gen --ta-version "$ta_version" - -cd "${OUTDIR}" -find . -name "*.pyc" -type f -exec rm -f {} \; -rm -f *.tgz -if [ -f ${app}/metadata/local.meta ]; then - rm -f ${app}/metadata/local.meta -fi -tar -czf ${app}_${version}.tgz ${app} -echo "Wrote: ${app}_${version}.tgz" - -sha256=$(sha256sum ${app}_${version}.tgz) -echo "Wrote: ${sha256} in $OUTDIR" -echo ${sha256} >release-sha256.txt - -exit 0 diff --git a/build/libs/tools.py b/build/libs/tools.py new file mode 100644 index 0000000..a391c15 --- /dev/null +++ b/build/libs/tools.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python +# coding=utf-8 + +from __future__ import absolute_import, division, print_function, unicode_literals + +__author__ = "TrackMe Limited" +__version__ = "0.1.0" +__maintainer__ = "TrackMe Limited" +__status__ = "PRODUCTION" + +import os +import time +import json +import logging +import requests +from requests.auth import HTTPBasicAuth + +# context manager +class cd: + """Context manager for changing the current working directory""" + def __init__(self, newPath): + self.newPath = os.path.expanduser(newPath) + + def __enter__(self): + self.savedPath = os.getcwd() + os.chdir(self.newPath) + + def __exit__(self, etype, value, traceback): + os.chdir(self.savedPath) + + +# login to Appinspect API and return the token +def login_appinspect(username, password): + + login_url = "https://api.splunk.com/2.0/rest/login/splunk" + + try: + response = requests.get(login_url, auth = HTTPBasicAuth(username, password), verify=True) + if response.status_code not in (200, 201, 204): + logging.error("Authentication to Splunk Appinspect API has failed, url={}, HTTP Error={}, content={}".format(login_url, response.status_code, response.text)) + else: + logging.debug("Authentication to Splunk Appinspect API was successful, url=\"{}\", token=\"{}\"".format(login_url, response.text)) + + except Exception as e: + logging.error("Authentication to Splunk Appinspect API has failed, url=\"{}\", exception=\"{}\"".format(login_url, e)) + + response_json = json.loads(response.text) + appinspect_token = response_json['data']['token'] + + return appinspect_token + + +# submit an app to Appinspect +def submit_appinspect(session, token, app): + + appinspect_headers = { + 'Authorization': 'bearer %s' % token, + } + + files = { + 'app_package': open(app, 'rb'), + 'included_tags': (None, 'cloud'), + } + + # submit + validate_url = "https://appinspect.splunk.com/v1/app/validate" + + # run + session = requests.Session() + + try: + response = session.post(validate_url, headers=appinspect_headers, files=files, verify=True) + if response.status_code not in (200, 201, 204): + logging.error("Submission to Splunk Appinspect API has failed, url={}, HTTP Error={}, content={}".format(validate_url, response.status_code, response.text)) + else: + logging.debug("Submission to Splunk Appinspect API was successful, url=\"{}\", response=\"{}\"".format(validate_url, response.text)) + + except Exception as e: + logging.error("Submission to Splunk Appinspect API has failed, url=\"{}\", exception=\"{}\"".format(validate_url, e)) + + return response.text + + +# verify an Appinspect vetting status +def verify_appinspect(session, token, request_id, max_retries=10, wait_time=5): + + appinspect_headers = { + 'Authorization': 'bearer %s' % token, + } + + # submit + validate_url = "https://appinspect.splunk.com/v1/app/validate/status/" + str(request_id) + + # run + for i in range(max_retries): + try: + response = session.get(validate_url, headers=appinspect_headers, verify=True) + if response.status_code not in (200, 201, 204): + logging.error("Request verification to Splunk Appinspect API has failed, url={}, HTTP Error={}, content={}".format(validate_url, response.status_code, response.text)) + else: + logging.debug("Request verification to Splunk Appinspect API was successful, url=\"{}\", response=\"{}\"".format(validate_url, response.text)) + return response.text + + except Exception as e: + if i < max_retries - 1: # Not the last attempt + logging.warning("Request verification to Splunk Appinspect API has failed, url=\"{}\", exception=\"{}\". Retrying...".format(validate_url, e)) + time.sleep(wait_time) + else: + logging.error("Request verification to Splunk Appinspect API has failed, url=\"{}\", exception=\"{}\"".format(validate_url, e)) + + return None + + +# download an Appinspect vetting report +def download_htmlreport_appinspect(session, token, request_id): + + appinspect_headers = { + 'Authorization': 'bearer %s' % token, + 'Content-Type': 'text/html', + } + + # submit + validate_url = "https://appinspect.splunk.com/v1/app/report/" + str(request_id) + + # run + try: + response = session.get(validate_url, headers=appinspect_headers, verify=True) + if response.status_code not in (200, 201, 204): + logging.error("Report download request to Splunk Appinspect API has failed, url={}, HTTP Error={}, content={}".format(validate_url, response.status_code, response.text)) + return None + else: + logging.debug("Report download request to Splunk Appinspect API was successful, url=\"{}\", response=\"{}\"".format(validate_url, response.text)) + return response.text + + except Exception as e: + logging.error("Report download request to Splunk Appinspect API has failed, url=\"{}\", exception=\"{}\"".format(validate_url, e)) + return None + + +# download an Appinspect vetting report +def download_jsonreport_appinspect(session, token, request_id): + + appinspect_headers = { + 'Authorization': 'bearer %s' % token, + 'Content-Type': 'application/json', + } + + # submit + validate_url = "https://appinspect.splunk.com/v1/app/report/" + str(request_id) + + # run + try: + response = session.get(validate_url, headers=appinspect_headers, verify=True) + if response.status_code not in (200, 201, 204): + logging.error("Report download request to Splunk Appinspect API has failed, url={}, HTTP Error={}, content={}".format(validate_url, response.status_code, response.text)) + return None + else: + logging.debug("Report download request to Splunk Appinspect API was successful, url=\"{}\", response=\"{}\"".format(validate_url, response.text)) + return response.text + + except Exception as e: + logging.error("Report download request to Splunk Appinspect API has failed, url=\"{}\", exception=\"{}\"".format(validate_url, e)) + return None diff --git a/docs/releasenotes.rst b/docs/releasenotes.rst index 3178191..cc809fe 100644 --- a/docs/releasenotes.rst +++ b/docs/releasenotes.rst @@ -1,6 +1,12 @@ Release notes ############# +Version 1.1.1 +============= + +- Compatibility: Splunk 9.x Compatibility +- Features: Importing all features from 1.1.0 which were not published + Version 1.0.4 ============= diff --git a/globalConfig.json b/globalConfig.json index ae0278e..4418652 100644 --- a/globalConfig.json +++ b/globalConfig.json @@ -158,8 +158,8 @@ "meta": { "name": "TA-azure-blob-archiving", "restRoot": "azure2blob", - "version": "1.1.0", + "version": "1.1.1", "displayName": "TA-azure-blob-archiving", "schemaVersion": "0.0.3" } -} \ No newline at end of file +} diff --git a/output/build.txt b/output/build.txt new file mode 100644 index 0000000..7b76acc --- /dev/null +++ b/output/build.txt @@ -0,0 +1 @@ +1700594077 diff --git a/output/version.txt b/output/version.txt new file mode 100644 index 0000000..58c9bdf --- /dev/null +++ b/output/version.txt @@ -0,0 +1 @@ +111 diff --git a/version.json b/version.json new file mode 100644 index 0000000..694f9ed --- /dev/null +++ b/version.json @@ -0,0 +1,4 @@ +{ + "version": "1.1.1", + "appID": "TA-azure-blob-archiving" +} diff --git a/version.txt b/version.txt deleted file mode 100644 index 1fb68bf..0000000 --- a/version.txt +++ /dev/null @@ -1 +0,0 @@ -version = 1.1.1 \ No newline at end of file