337 lines
11 KiB
Python
337 lines
11 KiB
Python
import itertools
|
|
import json
|
|
import logging
|
|
import pprint
|
|
import re
|
|
from collections import defaultdict
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
from urllib.parse import quote_plus
|
|
|
|
import boto3
|
|
import httpx
|
|
|
|
import urllib.parse
|
|
import subprocess
|
|
import typing
|
|
|
|
|
|
def list_repo_tags(*, repository_name: str, region: str, username: str, password: str) -> typing.List[dict]:
|
|
"""
|
|
Fetches the list of tags in a Codecommit repo using git ls-remote command
|
|
|
|
:returns: List of {tag, commit} items
|
|
"""
|
|
url = "https://{username}:{password}@git-codecommit.{region}.amazonaws.com/v1/repos/{repository_name}".format(
|
|
username=quote_plus(username),
|
|
password=quote_plus(password), # passwords are base64-encoded and might contain "/"
|
|
region=region,
|
|
repository_name=repository_name,
|
|
)
|
|
result = subprocess.run(["git", "ls-remote", "--refs", "--tags", url], text=True, stdout=subprocess.PIPE)
|
|
|
|
tags = []
|
|
for line in result.stdout.splitlines(keepends=False):
|
|
# lines are formatted as:
|
|
# 070161940636bab5d934af2702f4248e5a13eb29 refs/tags/ui_sandbox_acc104
|
|
# and columns are delimited with "\t"
|
|
if "refs/tags/" in line:
|
|
commit_hash, tag_ref = line.split("\t", maxsplit=1)
|
|
tag = tag_ref.replace("refs/tags/", "")
|
|
tags.append({"commit": commit_hash, "tag": tag})
|
|
|
|
# natural-sort by tag name
|
|
# flora_prod_1 < ... < flora_prod_9 > flora_prod_10
|
|
# vs lexical sort would give:
|
|
# flora_prod_1 < flora_prod_10 < ... < flora_prod_9
|
|
tags = sorted(tags, key=lambda t: [int(it) if it.isdigit() else it for it in re.split(r"(\d+)", t["tag"])])
|
|
|
|
return tags
|
|
|
|
|
|
def chunk(it, size):
|
|
it = iter(it)
|
|
sentinel = ()
|
|
return iter(lambda: tuple(itertools.islice(it, size)), sentinel)
|
|
|
|
|
|
def summarize_tags(tags: list[dict], take: int = 10) -> list[dict]:
|
|
re_prefix = re.compile(r"^(\D*)")
|
|
re_digits = re.compile(r"(\d+)")
|
|
grouped = defaultdict(list)
|
|
for t in tags:
|
|
tag_name = t["tag"]
|
|
if m := re_prefix.search(tag_name):
|
|
prefix = m.group(1)
|
|
grouped[prefix].append(t)
|
|
else:
|
|
print("no match", m)
|
|
flattened = [*grouped.pop("", [])]
|
|
for g, items in grouped.items():
|
|
sorted_items = sorted(
|
|
items, key=lambda t: [int(it) if it.isdigit() else it for it in re_digits.split(t["tag"])], reverse=True
|
|
)
|
|
flattened.extend(sorted_items[:take])
|
|
|
|
return flattened
|
|
|
|
|
|
abdus_creds = {
|
|
"username": "coder+1-at-400344683105",
|
|
"password": "4xdnGOtghKiT0M5CjulWf5raWwBSqT0Df2YkIY9Ws68=",
|
|
}
|
|
|
|
|
|
def get_commit_content():
|
|
client = boto3.client("codecommit")
|
|
|
|
res = client.get_file(
|
|
repositoryName="testing",
|
|
filePath="content.txt1",
|
|
commitSpecifier="vnext",
|
|
)
|
|
print(res)
|
|
|
|
|
|
def main():
|
|
import logging
|
|
from botocore.endpoint import Endpoint
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
raw_make_request = Endpoint.make_request
|
|
|
|
def intercepted_make_request(*args, **kwargs):
|
|
aws_response, parsed_response = raw_make_request(*args, **kwargs)
|
|
logging.info('AWS response: %s', parsed_response)
|
|
return aws_response, parsed_response
|
|
|
|
Endpoint.make_request = intercepted_make_request
|
|
|
|
session = boto3.Session(
|
|
aws_access_key_id="AKIAQKZTNEVLBHDG5Q3X",
|
|
aws_secret_access_key="Q7K9r45M9kh8bGqU8gIslAWIA1eaNo2M7w3xOZ6j",
|
|
region_name="eu-central-1",
|
|
)
|
|
cc = session.client("rds")
|
|
_ = cc.describe_db_clusters()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
exit()
|
|
# get_commit_content()
|
|
# flora b1cd47a221b84b2884b3d24198085a9f-f0dc2e747ff241649715c20d48232d8e
|
|
# oms 36363cc2f43b4cf5b1965af9f8925a4d-6deff091ce2d4c2483d8834c532a6f36
|
|
# omnitron 36363cc2f43b4cf5b1965af9f8925a4d-97400227a37940539c55489e1354ffc2
|
|
all_tags = list_repo_tags(
|
|
repository_name="b1cd47a221b84b2884b3d24198085a9f-f0dc2e747ff241649715c20d48232d8e",
|
|
username="sandbox-acc-api-user-at-023193265494",
|
|
password="sYPWKdhSx4JliXNI4ffRb4qWYBHLxA54nczSJ6CmYCY=",
|
|
region="eu-central-1",
|
|
)
|
|
print(all_tags)
|
|
exit(0)
|
|
all_tags.append({"tag": "1asd", "commit": "asdf"})
|
|
summarized = summarize_tags(all_tags, take=5)
|
|
commits = {t["commit"] for t in summarized}
|
|
session = boto3.Session(
|
|
aws_access_key_id="AKIAQKZTNEVLBHDG5Q3X",
|
|
aws_secret_access_key="Q7K9r45M9kh8bGqU8gIslAWIA1eaNo2M7w3xOZ6j",
|
|
region_name="eu-central-1",
|
|
)
|
|
client = session.client("codecommit")
|
|
|
|
def get_commit_infos(commits: list[str]):
|
|
res = client.batch_get_commits(
|
|
repositoryName="36363cc2f43b4cf5b1965af9f8925a4d-97400227a37940539c55489e1354ffc2",
|
|
commitIds=commits,
|
|
)
|
|
return res["commits"]
|
|
|
|
with ThreadPoolExecutor() as pool:
|
|
results = list(pool.map(get_commit_infos, chunk(commits, 100)))
|
|
|
|
clone_ssh_url = repo["repositoryMetadata"]["cloneUrlSsh"]
|
|
print(repo)
|
|
exit(0)
|
|
# exit()
|
|
from botocore.awsrequest import AWSRequest
|
|
from botocore.signers import RequestSigner
|
|
|
|
# AWS Version 4 signing example
|
|
|
|
# DynamoDB API (CreateTable)
|
|
|
|
# See: http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
|
|
# This version makes a POST request and passes request parameters
|
|
# in the body (payload) of the request. Auth information is passed in
|
|
# an Authorization header.
|
|
import sys, os, base64, datetime, hashlib, hmac
|
|
import requests # pip install requests
|
|
|
|
# ************* REQUEST VALUES *************
|
|
method = "POST"
|
|
service = "codecommit"
|
|
host = "codecommit.eu-central-1.amazonaws.com"
|
|
region = "eu-central-1"
|
|
endpoint = "https://codecommit.eu-central-1.amazonaws.com/"
|
|
# POST requests use a content type header. For DynamoDB,
|
|
# the content is JSON.
|
|
content_type = "application/x-amz-json-1.1"
|
|
# DynamoDB requires an x-amz-target header that has this format:
|
|
# DynamoDB_<API version>.<operationName>
|
|
amz_target = "CodeCommit_20150413.GetReferences"
|
|
|
|
# Request parameters for CreateTable--passed in a JSON block.
|
|
request_body = {"repositoryName": "36363cc2f43b4cf5b1965af9f8925a4d-6deff091ce2d4c2483d8834c532a6f36"}
|
|
request_parameters = json.dumps(request_body)
|
|
|
|
|
|
# Key derivation functions. See:
|
|
# http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-python
|
|
def sign(key, msg):
|
|
return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()
|
|
|
|
|
|
def getSignatureKey(key, date_stamp, regionName, serviceName):
|
|
kDate = sign(("AWS4" + key).encode("utf-8"), date_stamp)
|
|
kRegion = sign(kDate, regionName)
|
|
kService = sign(kRegion, serviceName)
|
|
kSigning = sign(kService, "aws4_request")
|
|
return kSigning
|
|
|
|
|
|
# Read AWS access key from env. variables or configuration file. Best practice is NOT
|
|
# to embed credentials in code.
|
|
access_key = os.environ.get("AWS_ACCESS_KEY_ID", "AKIAQKZTNEVLBHDG5Q3X")
|
|
secret_key = os.environ.get("AWS_SECRET_ACCESS_KEY", "Q7K9r45M9kh8bGqU8gIslAWIA1eaNo2M7w3xOZ6j")
|
|
if access_key is None or secret_key is None:
|
|
print("No access key is available.")
|
|
sys.exit()
|
|
|
|
# Create a date for headers and the credential string
|
|
t = datetime.datetime.utcnow()
|
|
amz_date = t.strftime("%Y%m%dT%H%M%SZ")
|
|
date_stamp = t.strftime("%Y%m%d") # Date w/o time, used in credential scope
|
|
|
|
# ************* TASK 1: CREATE A CANONICAL REQUEST *************
|
|
# http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
|
|
|
|
# Step 1 is to define the verb (GET, POST, etc.)--already done.
|
|
|
|
# Step 2: Create canonical URI--the part of the URI from domain to query
|
|
# string (use '/' if no path)
|
|
canonical_uri = "/"
|
|
|
|
## Step 3: Create the canonical query string. In this example, request
|
|
# parameters are passed in the body of the request and the query string
|
|
# is blank.
|
|
canonical_querystring = ""
|
|
|
|
# Step 4: Create the canonical headers. Header names must be trimmed
|
|
# and lowercase, and sorted in code point order from low to high.
|
|
# Note that there is a trailing \n.
|
|
canonical_headers = (
|
|
"content-type:"
|
|
+ content_type
|
|
+ "\n"
|
|
+ "host:"
|
|
+ host
|
|
+ "\n"
|
|
+ "x-amz-date:"
|
|
+ amz_date
|
|
+ "\n"
|
|
+ "x-amz-target:"
|
|
+ amz_target
|
|
+ "\n"
|
|
)
|
|
|
|
# Step 5: Create the list of signed headers. This lists the headers
|
|
# in the canonical_headers list, delimited with ";" and in alpha order.
|
|
# Note: The request can include any headers; canonical_headers and
|
|
# signed_headers include those that you want to be included in the
|
|
# hash of the request. "Host" and "x-amz-date" are always required.
|
|
# For DynamoDB, content-type and x-amz-target are also required.
|
|
signed_headers = "content-type;host;x-amz-date;x-amz-target"
|
|
|
|
# Step 6: Create payload hash. In this example, the payload (body of
|
|
# the request) contains the request parameters.
|
|
payload_hash = hashlib.sha256(request_parameters.encode("utf-8")).hexdigest()
|
|
|
|
# Step 7: Combine elements to create canonical request
|
|
canonical_request = (
|
|
method
|
|
+ "\n"
|
|
+ canonical_uri
|
|
+ "\n"
|
|
+ canonical_querystring
|
|
+ "\n"
|
|
+ canonical_headers
|
|
+ "\n"
|
|
+ signed_headers
|
|
+ "\n"
|
|
+ payload_hash
|
|
)
|
|
|
|
# ************* TASK 2: CREATE THE STRING TO SIGN*************
|
|
# Match the algorithm to the hashing algorithm you use, either SHA-1 or
|
|
# SHA-256 (recommended)
|
|
algorithm = "AWS4-HMAC-SHA256"
|
|
credential_scope = date_stamp + "/" + region + "/" + service + "/" + "aws4_request"
|
|
string_to_sign = (
|
|
algorithm
|
|
+ "\n"
|
|
+ amz_date
|
|
+ "\n"
|
|
+ credential_scope
|
|
+ "\n"
|
|
+ hashlib.sha256(canonical_request.encode("utf-8")).hexdigest()
|
|
)
|
|
|
|
# ************* TASK 3: CALCULATE THE SIGNATURE *************
|
|
# Create the signing key using the function defined above.
|
|
signing_key = getSignatureKey(secret_key, date_stamp, region, service)
|
|
|
|
# Sign the string_to_sign using the signing_key
|
|
signature = hmac.new(signing_key, (string_to_sign).encode("utf-8"), hashlib.sha256).hexdigest()
|
|
|
|
# ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST *************
|
|
# Put the signature information in a header named Authorization.
|
|
authorization_header = (
|
|
algorithm
|
|
+ " "
|
|
+ "Credential="
|
|
+ access_key
|
|
+ "/"
|
|
+ credential_scope
|
|
+ ", "
|
|
+ "SignedHeaders="
|
|
+ signed_headers
|
|
+ ", "
|
|
+ "Signature="
|
|
+ signature
|
|
)
|
|
|
|
# For DynamoDB, the request can include any headers, but MUST include "host", "x-amz-date",
|
|
# "x-amz-target", "content-type", and "Authorization". Except for the authorization
|
|
# header, the headers must be included in the canonical_headers and signed_headers values, as
|
|
# noted earlier. Order here is not significant.
|
|
# # Python note: The 'host' header is added automatically by the Python 'requests' library.
|
|
headers = {
|
|
"User-Agent": "aws-sdk-js/2.627.0 promise",
|
|
"Content-Type": content_type,
|
|
"X-Amz-Date": amz_date,
|
|
"X-Amz-Target": amz_target,
|
|
"Authorization": authorization_header,
|
|
}
|
|
|
|
# ************* SEND THE REQUEST *************
|
|
print("\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++")
|
|
print("Request URL = " + endpoint)
|
|
|
|
r = httpx.post(endpoint, json=request_body, headers=headers)
|
|
|
|
print("\nRESPONSE++++++++++++++++++++++++++++++++++++")
|
|
print("Response code: %d\n" % r.status_code)
|
|
print(r.text)
|