diff --git a/.env.example b/.env.example index 77a32c5..e9fb980 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,11 @@ ACCESS_TOKEN="" + AWS_KEY_ID="" + JSON_AUTH_TOKEN=" tuple: err_log.warning("No userName") return ("No userName", 400) - # Check for env - try: - userName = data["env"] - except KeyError as e: - err_log.warning(e) - err_log.warning("No env") - return ("No env", 400) + # Check for env + try: + userName = data["env"] + except KeyError as e: + err_log.warning(e) + err_log.warning("No env") + return ("No env", 400) return ("JSON response accepted", 200) -def which_env(data: str) -> tuple: +def which_env(env: str) -> tuple: """Returns a tuple whose contents are dependent on which env tag was provided in the request.json. Returns None if env is invalid. - data: authToken from request.json['authToken'] + env: string of env from request.json Return (git_uri, filename) """ - data = json.loads(data) - env = data["env"] - if env == "test": git_uri = os.getenv("TEST_REPO_URI") filename = os.getenv("TEST_FILENAME") @@ -103,6 +99,22 @@ def token_is_valid(authToken: str) -> bool: return True +def generate_directory_name(data: dict) -> str: + """Generates the local directory name for the git repo + based on the env and userName supplied. + + data: Dict containing either env and userInfo : { userName : } + or the entire request.json + + This function should NEVER fail, because it should always be called AFTER + is_json_allowed returns 200, OK and never upon any other result of is_json_allowed. + Therefore, no error checking should be included here as errors should be caught + before this function is ever invoked. + """ + target_dir = f"{data['env']}-repo-{data['userInfo']['userName']}" + return target_dir + + @app.route("/getParams", methods=["POST"]) def getParams(): # Log the endpoint access @@ -114,7 +126,7 @@ def getParams(): if status != 200: return (msg, status) - # Validate authToken + # Validate authToken before proceeding if not token_is_valid(request.json["authToken"]): return ("Token is not valid. Please provide a valid authToken", 403) @@ -125,10 +137,8 @@ def getParams(): if git_uri is None: return ("Invalid env", 400) - # Generate local directory name - target_dir = ( - f"{request.json['env']}-repo-{request.json['userInfo']['userName']}" - ) + # Generate directory name + target_dir = generate_directory_name(request.json) # Ensure that the local directory does not exist if git.dirname_exists(target_dir): @@ -170,52 +180,65 @@ def putParams(): if status != 200: return msg, status - # Validate supplied authToken + # Validate authToken before proceeding if not token_is_valid(request.json["authToken"]): app_log.info(f"Unauthorized request at /putParams. {request.json}") return "Invalid authToken. This incident has been logged.", 403 - else: - now = datetime.now().strftime("%m-%d-%Y %H:%M:%S") - data = request.json - params = data["parameters"] - user = data["userInfo"] - title = f"Config Change - {now}" - msg = f"Created by: {user['userName']} at {now}" - params = file.check_secret(json.dumps(params), delete=True) - # Git Functions Required Order - # 1. Switch to main - # 2. git pull - # 3. git checkout -b - # 4. Update file - # 5. Add and commit new file - # 6. Create PR - # Switching back to main after PR is unecessary now - # These notes have too much future chaos potential to remove. - git.reset_to_main("git_repo") - git.pull("git_repo") - branch = git.new_branch("git_repo") - file.write_file(params, "git_repo/example.yml") - git.add_commit( - "git_repo", - ["example.yml"], - "Update to example.yml parameters", - user["userName"], - user["userEmail"], - ) - git.create_pr( - "devblueray/TestConfigs", - "git_repo", - os.getenv("ACCESS_TOKEN"), - title, - msg, - branch, - "main", + + # Get filename based on env + git_uri, filename = which_env(request.json["env"]) + + # Generate directory name + target_dir = generate_directory_name(request.json) + + # Checking that the directory name generated is present, if not, the request was bad. + if not os.path.exists(target_dir): + err_log.warning( + f"{target_dir} does not exist. Bad JSON request. \n {request.json}" ) return ( - jsonify({"fileUpdate": "Success", "Add and Commit": "Success"}), - 200, + "Something went wrong. Please ensure your authToken, userName and env are the same as when you created your clone.", + 400, ) + # Encrypt secret parameters + enc_params = file.check_secret( + json.dumps(request.json["parameters"]), delete=True + ) + + # I think that some of this needs to be moved out of putParams + # I'm thinking that create_pr could be in a cleanup endpoint that also deletes + # the local directory, called AFTER storeParams. + # Required Order of Git Functions + # 1. Switch to main + # 2. git pull + # 3. git checkout -b + # 4. Update file + # 5. Add and commit new file + # 6. Create PR + # Switching back to main after PR is unecessary now + # These notes have too much future chaos potential to remove. + git.reset_to_main(target_dir) + git.pull(target_dir) + new_branch = git.new_branch(target_dir) + file.write_file(enc_params, filename=f"{target_dir}/{filename}") + git.add_commit( + target_dir, + [filename], + "Updated yaml parameters", + request.json["userInfo"]["userName"], + request.json["userInfo"]["userEmail"], + ) + git.create_pr( + uri=git_uri, + user=request.json["userInfo"]["userName"], + dir=target_dir, + branch_name=new_branch, + ) + + app_log.info(f"Created PR for {git_uri}") + return "PR Created", 200 + @app.route("/storeParams", methods=["POST"]) def storeParams(): diff --git a/configApi/fileUtils/aws.py b/configApi/fileUtils/aws.py index 5ac9340..038dae8 100644 --- a/configApi/fileUtils/aws.py +++ b/configApi/fileUtils/aws.py @@ -21,7 +21,7 @@ def encrypt(data: str) -> str: data = bytes(data.encode("ascii")) kms = boto3.client("kms", region_name="us-east-1") res = kms.encrypt( - KeyId=os.getenv("KEY_ID"), + KeyId=os.getenv("AWS_KEY_ID"), Plaintext=data, EncryptionAlgorithm="SYMMETRIC_DEFAULT", ) @@ -39,7 +39,7 @@ def decrypt(data: str) -> str: kms = boto3.client("kms", region_name="us-east-1") res = kms.decrypt( CiphertextBlob=data, - KeyId=os.getenv("KEY_ID"), + KeyId=os.getenv("AWS_AWS_KEY_ID"), EncryptionAlgorithm="SYMMETRIC_DEFAULT", ) @@ -60,16 +60,16 @@ def store(data: str, prefix: str) -> dict: for key in data.keys(): if data[key]["secret"]: res = ssm.put_parameter( - Name=f'/{prefix}/{key}', + Name=f"/{prefix}/{key}", Description=data[key]["comment"], Value=data[key]["value"], Type="SecureString", - KeyId=os.getenv("KEY_ID"), + KeyId=os.getenv("AWS_KEY_ID"), Overwrite=True, ) else: res = ssm.put_parameter( - Name=f'/{prefix}/{key}', + Name=f"/{prefix}/{key}", Description=data[key]["comment"], Value=data[key]["value"], Type="String", diff --git a/configApi/fileUtils/file.py b/configApi/fileUtils/file.py index 167e36a..05ca614 100644 --- a/configApi/fileUtils/file.py +++ b/configApi/fileUtils/file.py @@ -94,7 +94,7 @@ def write_file(data: str, filename: str) -> None: return f"Invalid JSON: {e}" yaml = ruamel.yaml.YAML() - original = json.loads(read_file(filename)) + original = json.loads(read_yaml(filename)) # Updates the original dict, not data # Does not "return" a value so cannot reassign to data original.update(data) diff --git a/configApi/gitUtils/git.py b/configApi/gitUtils/git.py index e37585a..311350c 100644 --- a/configApi/gitUtils/git.py +++ b/configApi/gitUtils/git.py @@ -2,6 +2,7 @@ from github import Github from git import Actor from shutil import rmtree +from datetime import datetime import traceback import git import sys @@ -52,7 +53,7 @@ def clone(uri: str, target: str) -> None: """ token = os.getenv("ACCESS_TOKEN") - remote = f"https://{token}:x-oauth-basic@{uri}" + remote = f"https://{token}:x-oauth-basic@github.com/{uri}.git" try: git.Repo.clone_from(remote, target) git_log.info(f"Repository '{uri}' has been cloned to {target}") @@ -127,27 +128,30 @@ def pull(repo: str) -> None: origin.pull() -def create_pr( - lrepo: str, - dir: str, - token: str, - title: str, - body: str, - head: str, - base: str, -) -> None: - """Create a new pull request - - repo: Repository in User/Repository format - dir: local directory name - token: Github access token - title: Title of new pull request - body: Body of pull request - head: Branch to merge (dev -> main, this is dev) - base: Branch to merge new branch into (dev -> main, this is main) +def create_pr(uri: str, dir: str, user: str, branch_name: str) -> None: + """Create a new pull request, always from to
+ + uri: Git repo uri, User/Repository + dir: Local directory name + user: Username to include in PR + branch_name: Branch to create PR from """ + # Generating variables + now = datetime.now().strftime("%m-%d-%Y %H:%M:%S") + title = f"Config Change - {now}" + msg = f"Created by: {user} at {now}" + + # Move into local directory os.chdir(dir) - g = Github(token) - repo = g.get_repo(lrepo) - repo.create_pull(title=title, body=body, head=head, base=base) + + # Instanciate Github using current local directory + g = Github(os.getenv("ACCESS_TOKEN")) + + # Instanciate repository in current local directory + repo = g.get_repo(uri) + + # Create the PR + repo.create_pull(title=title, body=msg, head=branch_name, base="main") + + # Moved back out of current local directory os.chdir("../") diff --git a/requests.rest b/requests.rest index 09b1d15..e11a1a6 100644 --- a/requests.rest +++ b/requests.rest @@ -71,24 +71,24 @@ POST http://127.0.0.1:8000/putParams content-type: application/json { - "authToken": "FXbWZBmGstqCJP8D7P3kzguthu479dFv", - "userInfo": { - "userName": "Bob Pickles", - "userEmail": "bob@pickles.com" + "authToken": "FXbWZBmGstqCJP8D7P3kzguthu479dFv", + "userInfo": { + "userName": "Bob Pickles", + "userEmail": "bob@pickles.com" + }, + "env": "test", + "parameters": { + "a": { + "value": "some dumb value", + "secret": true, + "comment": "some dumb comment" }, - "env": "prod", - "parameters": { - "a": { - "value": "some dumb value", - "secret": true, - "comment": "some dumb comment" - }, - "b": { - "value": "another dumb value", - "secret": false, - "comment": "another dumb comment" - } + "b": { + "value": "another dumb value", + "secret": false, + "comment": "another dumb comment" } + } } ###