def verify_issue_status(jira, current_environment): if current_environment.status: issues_to_verify = [] # If they have a single issue listed, add it to be verified if current_environment.issue: issue = retrieve_jira_issue(jira, current_environment.issue) issues_to_verify.append(issue) # If they have a jql query specified, retrieve the list of issues to be verified if current_environment.jql_query: issues_to_verify.extend(jql_query(jira, current_environment)) print("\nVerify issue transition status") print("Desired Status: " + str(current_environment.status)) for current_issue in issues_to_verify: if str(current_issue.fields.status) == current_environment.status: print(str(current_issue) + ": " + str(current_issue.fields.status)) else: StepUtility.printFail("Exiting Step - Jira Issue " + str(current_issue) + "\n Current Status: " + str(current_issue.fields.status) + "\n Desired status: " + str(current_environment.status)) sys.exit(1) print("Successfully verified status") else: StepUtility.printFail("Exiting Step - Please specify a valid status name") sys.exit(1)
def retrieve_jira_issue(jira, current_issue): try: issue = jira.issue(current_issue) return issue except Exception as exc: StepUtility.printCleanException(exc) StepUtility.printFail("Exiting Step - Failed to retrieve issue") sys.exit(1)
def update_comment(jira, current_environment): print("\nUpdate comment") try: comment = jira.comment(current_environment.issue, current_environment.existing_comment_id) comment.update(body = current_environment.comment_body) except Exception as exc: StepUtility.printCleanException(exc) StepUtility.printFail("Exiting Step - Failed to update comment") sys.exit(1)
def vault_authentication(current_environment): # Authenticate the client - exit program if authentication fails print("\nAuthentication") client = hvac.Client(url=current_environment.vault_addr) # Check for client certs and apply them if current_environment.vault_client_cert_base64 != "" and current_environment.vault_client_key_base64 != "": try: client_certs = (base64.b64decode( current_environment.vault_client_cert_base64), base64.b64decode( current_environment.vault_client_key_base64)) client = hvac.Client(url=current_environment.vault_addr, cert=client_certs) except Exception as exc: StepUtility.printCleanException(exc) StepUtility.printFail( "Exiting Step - Failed to authenticate with certificates to Vault instance" ) sys.exit(1) if current_environment.vault_auth_method.upper() == "TOKEN": print("Mode: TOKEN") client.token = current_environment.vault_token elif current_environment.vault_auth_method.upper() == "APPROLE": print("Mode: APPROLE") try: client.auth_approle(current_environment.approle_role_id, current_environment.approle_secret_id) except Exception as exc: StepUtility.printCleanException(exc) StepUtility.printFail( "Exiting Step - Failed to authenticate with Approle to Vault instance" ) sys.exit(1) else: print("Authentication Mode not passed, defaulting to Token auth") client.token = current_environment.vault_token # Verify that we connected successfully to Vault, otherwise exit the step if not client.is_authenticated(): StepUtility.printFail( "Exiting Step - Failed to authenticate with Vault instance") sys.exit(1) else: print("Successfully authenticated with Vault instance") return client
def perform_jira_update(jira, current_environment, issue): print("Updating issue: " + str(issue)) update_issue_dict = {} if current_environment.issue_summary: update_issue_dict.update(summary = current_environment.issue_summary) if current_environment.issue_description: update_issue_dict.update(description = current_environment.issue_description) if current_environment.issue_type: update_issue_dict.update(issuetype = ast.literal_eval(current_environment.issue_type)) if current_environment.issue_components: update_issue_dict.update(components = current_environment.issue_components) try: issue.update(update_issue_dict) except Exception as exc: StepUtility.printCleanException(exc) StepUtility.printFail("Exiting Step - Failed to update issue: " + str(issue)) sys.exit(1)
def step_action(action, authenticated_jira, current_environment): print("Step Action: " + action) actions = { 'issue_create': create_issue, 'issue_update': update_issue, 'issue_transition': transition_issue, 'issue_transition_and_update': transition_issue, 'comment_create': create_comment, 'comment_update': update_comment, 'verify_status': verify_issue_status, 'update_all_from_jql_query': update_multiple_issues, } action_func = actions.get(action, action_required) if action_func: action_func(authenticated_jira, current_environment) else: StepUtility.printFail("Exiting Step - invalid ACTION environment variable") sys.exit(1)
def create_issue(jira, current_environment): print("\nCreate issue") new_issue_dict = {} new_issue_dict.update( project=ast.literal_eval(current_environment.issue_project)) new_issue_dict.update(summary=current_environment.issue_summary) new_issue_dict.update(description=current_environment.issue_description) new_issue_dict.update( issuetype=ast.literal_eval(current_environment.issue_type)) new_issue_dict.update(components=current_environment.issue_components) try: created_issue = jira.create_issue(new_issue_dict) print("Jira issue " + str(created_issue) + " created") except Exception as exc: StepUtility.printCleanException(exc) StepUtility.printFail("Exiting Step - Failed to create issue") sys.exit(1)
def create_comment(jira, current_environment): print("\nCreate comment") try: comment_id = jira.add_comment(current_environment.issue, current_environment.comment_body) print("Comment id: " + str(comment_id)) StepUtility.export_variable("JIRA_COMMENT_ID", comment_id) except Exception as exc: StepUtility.printCleanException(exc) StepUtility.printFail("Exiting Step - Failed to insert a comment") sys.exit(1)
def create_issue(jira, current_environment): print("\nCreate issue") new_issue_dict = {} new_issue_dict.update(project = ast.literal_eval(current_environment.issue_project)) new_issue_dict.update(summary = current_environment.issue_summary) new_issue_dict.update(description = current_environment.issue_description) new_issue_dict.update(issuetype = ast.literal_eval(current_environment.issue_type)) if (current_environment.issue_components): new_issue_dict.update(components = current_environment.issue_components) # print(current_environment.issue_customfields) # sys.exit(1) if current_environment.issue_customfields: url = '{}/rest/api/3/field'.format(current_environment.jira_base_url) response = requests.request('GET', url, auth=HTTPBasicAuth(current_environment.jira_username, current_environment.jira_api_key)) data = response.json() customfields = current_environment.issue_customfields.lstrip('[').rstrip(']') kv_pairs = re.findall(r'(\w+.*?)\s*=\s*(.*?)(?=(?:\s[^\s=]+|$))', customfields) for k, v in kv_pairs: if 'customfield' not in k: for item in data: if k in item['name']: customfield_id = item['id'] new_issue_dict[customfield_id] = v else: new_issue_dict[k] = v try: created_issue = jira.create_issue(new_issue_dict) print("Jira issue " + str(created_issue) + " created") StepUtility.export_variable("JIRA_ISSUE_ID", created_issue) StepUtility.export_variable("main_CF_OUTPUT_URL", str(current_environment.jira_base_url) + "/browse/" + str(created_issue)) except Exception as exc: StepUtility.printCleanException(exc) StepUtility.printFail("Exiting Step - Failed to create issue") sys.exit(1)
def transition_issue(jira, current_environment): print("\nTransition issue status") issue = retrieve_jira_issue(jira, current_environment.issue) print("Current issue status: " + str(issue.fields.status)) print("Desired issue status: " + current_environment.status) if str(issue.fields.status) == current_environment.status: print("Skipping transition as desired state is already met") else: # Verbose: Print the list of viable transitions before the transition takes place if current_environment.verbose == "true": transition_list = jira.transitions(current_environment.issue) print("Viable transition statuses before:") for transition in transition_list: print("\t" + transition['id'], transition['name']) print() transition_id = jira.find_transitionid_by_name( current_environment.issue, current_environment.status) if transition_id: try: jira.transition_issue(issue, transition_id) except Exception as exc: StepUtility.printCleanException(exc) StepUtility.printFail( "Exiting Step - Failed to transition issue") sys.exit(1) else: StepUtility.printFail( "Exiting Step - Failed to find transition id for transition name: " + current_environment.status) sys.exit(1) # Verbose: Print the list of viable transitions after the transition takes place if current_environment.verbose == "true": print() transition_list = jira.transitions(current_environment.issue) print("Viable transition statuses after:") for transition in transition_list: print("\t" + transition['id'], transition['name']) print("Successfully transitioned issue") # Issue was successfully transitioned - attempt to update the issue now if required if current_environment.action == "issue_transition_and_update": update_issue(jira, current_environment)
def environment_setup(): # Grab all of the environment variables env = os.environ jira_base_url = StepUtility.getEnvironmentVariable('JIRA_BASE_URL', env) jira_username = StepUtility.getEnvironmentVariable('JIRA_USERNAME', env) jira_api_key = StepUtility.getEnvironmentVariable('JIRA_API_KEY', env) action = StepUtility.getEnvironmentVariable('ACTION', env) # Logic here to use the regex to grab the jira issue key and assign it to issue jira_issue_source_field = StepUtility.getEnvironmentVariable('JIRA_ISSUE_SOURCE_FIELD', env) jira_issue_source_field_regex = StepUtility.getEnvironmentVariable('JIRA_ISSUE_SOURCE_FIELD_REGEX', env) ## TODO - Brandon - need to do regex work here issue = jira_issue_source_field # Issue fields below # Retrieve the project environment variable and add the project to a dict representation issue_project_env = StepUtility.getEnvironmentVariable('ISSUE_PROJECT', env) issue_project = None if issue_project_env: issue_project = "{'key': '" + issue_project_env + "'}" issue_summary = StepUtility.getEnvironmentVariable('ISSUE_SUMMARY', env) issue_description = StepUtility.getEnvironmentVariable('ISSUE_DESCRIPTION', env) # Retrieve the type environment variable and add the type to a dict representation issue_type_env = StepUtility.getEnvironmentVariable('ISSUE_TYPE', env) issue_type = None if issue_type_env: issue_type = "{'name': '" + issue_type_env + "'}" # Retrieve the components, split the list, and create an array of dicts with each component issue_components_env = StepUtility.getEnvironmentVariable('ISSUE_COMPONENTS', env) issue_components = None if issue_components_env: issue_components = [] split_components = issue_components_env.split(",") for component in split_components: component_string = "{'name': '" + component + "'}" issue_components.append(ast.literal_eval(component_string)) # Retrieve customfields issue_customfields = StepUtility.getEnvironmentVariable('ISSUE_CUSTOMFIELDS', env) # Retrieve the comment information existing_comment_id = StepUtility.getEnvironmentVariable('JIRA_COMMENT_ID', env) comment_body = StepUtility.getEnvironmentVariable('COMMENT_BODY', env) # Desired Jira status information status = StepUtility.getEnvironmentVariable('DESIRED_ISSUE_STATUS', env) jql_query = StepUtility.getEnvironmentVariable('JQL_QUERY', env) jql_query_max_results = 50 jql_query_max_results_env = StepUtility.getEnvironmentVariable('JQL_QUERY_MAX_RESULTS', env) if jql_query_max_results_env: jql_query_max_results = jql_query_max_results_env verbose = StepUtility.getEnvironmentVariable('VERBOSE', env) current_environment = Environment( jira_base_url, jira_username, jira_api_key, action, issue, issue_project, issue_summary, issue_description, issue_type, issue_components, issue_customfields, existing_comment_id, comment_body, status, jql_query, jql_query_max_results, verbose) return current_environment
def action_required(jira, current_environment): StepUtility.printFail("Exiting Step - Please specify a valid action type") sys.exit(1)
def environment_setup(): # Grab all of the environment variables env = os.environ vault_addr = StepUtility.getEnvironmentVariable('VAULT_ADDR', env) vault_auth_method = StepUtility.getEnvironmentVariable( 'VAULT_AUTH_METHOD', env) vault_token = StepUtility.getEnvironmentVariable('VAULT_TOKEN', env) approle_role_id = StepUtility.getEnvironmentVariable( 'APPROLE_ROLE_ID', env) approle_secret_id = StepUtility.getEnvironmentVariable( 'APPROLE_SECRET_ID', env) vault_client_cert_base64 = StepUtility.getEnvironmentVariable( 'VAULT_CLIENT_CERT_BASE64', env) vault_client_key_base64 = StepUtility.getEnvironmentVariable( 'VAULT_CLIENT_KEY_BASE64', env) mount_point = StepUtility.getEnvironmentVariable('MOUNT_POINT', env) vault_kv_version = StepUtility.getEnvironmentVariable( 'VAULT_KV_VERSION', env) new_line_replacement_string = StepUtility.getEnvironmentVariable( 'NEW_LINE_REPLACEMENT_STRING', env) verbose = StepUtility.getEnvironmentVariable('VERBOSE', env) current_environment = Environment( vault_addr, vault_auth_method, vault_token, approle_role_id, approle_secret_id, vault_client_cert_base64, vault_client_key_base64, mount_point, vault_kv_version, new_line_replacement_string, verbose) return env, current_environment
def get_secrets(client, current_environment, secrets, path_set): if current_environment.verbose == "true": # Test printing of desired secret information when debug is enabled print("\n---- VERBOSE ---- Unique Paths") print(path_set) print("\n---- VERBOSE ---- Requested Secrets") for current_secret in secrets: print(json.dumps(current_secret.__dict__)) print() # This will store all the key/values retrieved from the paths vault_retrieved_values = dict() # Iterate over each unique path passed in through the environment variables print("\nSecret Retrieval") for current_path in path_set: print("Retrieving secrets from path: " + current_path) if current_environment.vault_kv_version == "1": try: secret_response = client.secrets.kv.v1.read_secret( mount_point=current_environment.mount_point, path=current_path) vault_retrieved_values = secret_response['data'] except Exception as exc: StepUtility.printCleanException(exc) StepUtility.printFail( "Exiting Step - Failed to retrieve secrets from v1 path: " + current_path) StepUtility.printFail( "Please verify mount point, path, and kv version") sys.exit(1) else: try: secret_version_response = client.secrets.kv.v2.read_secret_version( mount_point=current_environment.mount_point, path=current_path) vault_retrieved_values = secret_version_response['data'][ 'data'] except Exception as exc: StepUtility.printCleanException(exc) StepUtility.printFail( "Exiting Step - Failed to retrieve secrets from v2 path: " + current_path) StepUtility.printFail( "Please verify mount point, path, and kv version") sys.exit(1) # Loop through the list of secret requests and find keys that match for current_secret in secrets: for key, value in vault_retrieved_values.items(): if current_secret.path == current_path and current_secret.secret_name == key: print("Successful retrieval for \n\tpath: " + current_secret.path + "\n\tsecret: " + current_secret.secret_name) current_secret.secret_value = value # Secrets should be retrieved now, check to make sure they all have a value for retrieved_secret in secrets: if retrieved_secret.secret_value == "": StepUtility.printFail( "Exiting Step - Failed retrieval for \n\tpath: " + retrieved_secret.path + "\n\tsecret: " + retrieved_secret.secret_name) sys.exit(1) return secrets
def vault_authentication(current_environment): # Authenticate the client - exit program if authentication fails print("\nAuthentication") client = hvac.Client(url=current_environment.vault_addr) # Check for client certs and apply them if current_environment.vault_client_cert_base64 != "" and current_environment.vault_client_key_base64 != "": try: client_certs = (base64.b64decode( current_environment.vault_client_cert_base64), base64.b64decode( current_environment.vault_client_key_base64)) client = hvac.Client(url=current_environment.vault_addr, cert=client_certs) except Exception as exc: StepUtility.printCleanException(exc) StepUtility.printFail( "Exiting Step - Failed to authenticate with certificates to Vault instance" ) sys.exit(1) if current_environment.vault_auth_method.upper() == "TOKEN": print("Mode: TOKEN") client.token = current_environment.vault_token elif current_environment.vault_auth_method.upper() == "APPROLE": print("Mode: APPROLE") try: client.auth_approle(current_environment.approle_role_id, current_environment.approle_secret_id) except Exception as exc: StepUtility.printCleanException(exc) StepUtility.printFail( "Exiting Step - Failed to authenticate with Approle to Vault instance" ) sys.exit(1) elif current_environment.vault_auth_method.upper() == "GCP": with open(current_environment.vault_auth_creds_path, 'r') as f: creds = json.load(f) project = creds['project_id'] service_account = creds['client_email'] now = int(time.time()) expires = now + 900 # 15 mins in seconds payload = { 'iat': now, 'exp': expires, 'sub': service_account, 'aud': f"vault/{current_environment.vault_role}" } credentials = google_service_account.Credentials \ .from_service_account_file(current_environment.vault_auth_creds_path) iam = googleapiclient.discovery.build('iam', 'v1', credentials=credentials, cache_discovery=False) name = f'projects/{project}/serviceAccounts/{service_account}' body = {'payload': json.dumps(payload)} resp = iam.projects().serviceAccounts().signJwt(name=name, body=body).execute() login = client.auth.gcp.login(current_environment.vault_role, resp['signedJwt']) client.token = login['auth']['client_token'] else: print("Authentication Mode not passed, defaulting to Token auth") client.token = current_environment.vault_token # Verify that we connected successfully to Vault, otherwise exit the step if not client.is_authenticated(): StepUtility.printFail( "Exiting Step - Failed to authenticate with Vault instance") sys.exit(1) else: print("Successfully authenticated with Vault instance") return client