jira 티켓에서 담당자가 할당되면 automation이 동작하여 api gateway를 webhook으로 호출
값이 올바르다면 lambda가 동작하여 최신 tag push, notification
tag가 push 되면 circleci start
circleci job complete가 되면 webhook기능으로 api gateway 호출
bitbucket에서 commit 값으로 tag를 찾는 api는 제공하지 않음.
tag 검색 api로는 commit 값이 확인됨.
circleci webhook으로 commit을 비교하여 tag를 찾고 spinnaker api 호출
spinnaker api 호출 후 slack으로 noti
spinnaker에 설정해둔 Automated Triggers가 동작됨
처음에는 lambda 대신 ec2에 go rest api로 하려고 했었다.
- bitbucket api로 원하는 tag값을 검색하는게 어려웠었음.
- 그런데 ec2에 git clone 하는게 보안상 안좋을것 같았다.
결국 api로 돌아가서 사용 방법을 삽질끝에 찾음
bitbucket api의 q변수를 사용하면 특정 문자로 시작하는 값을 가져올 수 있었음.
티켓 내용에 따라 조건들을 설정한다.
티켓에서 필요한 값을 가져다가 lambda와 연결된 api gateway를 호출한다.
이때 보안강화를 위해 api gateway에 ip 접근제한을 추가한다.
api gateway 참고
아래는 생략된 누추한 코드...
def is_ip_allowed(source_ip):
# source_ip를 IPv4Address 객체로 파싱
ip = ipaddress.IPv4Address(source_ip)
# 허용된 CIDR 범위와 비교
for cidr in allowed_ips:
network = ipaddress.IPv4Network(cidr, strict=False)
if ip in network:
return True
return False
def increment_patch(version):
major, minor, patch = map(int, version.split("."))
# 패치 값이 99보다 경우 마이너 값을 +1 증가하고 패치는 0으로 설정
if patch > 99:
minor += 1
patch = 0
else:
patch += 1
return f"{major}.{minor}.{patch}"
# major.minor.patch값을 sort하기 위해 int로 변환하여 저장
def parse_version(version_str):
return tuple(map(int, version_str.split('.')))
def lambda_handler(event, context):
request_body = json.loads(event["body"])
request_ip = event['requestContext']["http"]["sourceIp"]
if not is_ip_allowed(request_ip):
# 허용되지 않은 IP 주소 확인
error_message = "Access denied. Your IP address is not allowed."
return {
"statusCode": 403,
"body": error_message
}
# 요청 본문에서 <REPO> <commit> 값을 추출
repo = request_body.get("repo")
commit = request_body.get("commit")
#repo, commit 값이 없으면 실패
if not commit or not your_repo:
return {
"statusCode": 403,
"body": "post value is empty."
}
# Bitbucket API 엔드포인트 및 헤더 설정
url = f'https://api.bitbucket.org/2.0/repositories/{proj}/{repo}/refs?q=name~"stg"&sort=-target.date'
commit_url=f'https://api.bitbucket.org/2.0/repositories/{proj}/{repo}/commit/{commit}'
push_url = f'https://api.bitbucket.org/2.0/repositories/{proj}/{repo}/refs/tags'
headers = {
"Authorization": f"Basic {token}"
}
commit_response=requests.get(commit_url, headers=headers)
# commit을 확인하고 없으면 실패
if commit_response.status_code == 404:
return {
"statusCode": 404,
"body": f'no search commit: {commit}'
}
# Bitbucket API에 GET 요청 보내기
response = requests.get(url, headers=headers)
# API 응답이 성공적인지 확인
if response.status_code == 200:
# API 응답을 JSON으로 파싱
data = response.json()
# stg로 시작하는 값을 추출하여 정렬
stg_refs = [ref["name"] for ref in data["values"] if ref["name"].startswith("stg")]
tag_list = [item.replace('stg', '') for item in stg_v_refs]
sorted_versions = sorted(tag_list, key=parse_version, reverse=True)
print(sorted_versions)
if sorted_versions:
largest_ref = "v"+sorted_versions[0]
print("마지막 tag는 stg", sorted_versions[0])
# 메이저, 마이너, 패치 값을 추출하여 패치 값을 +1 증가
version_numbers = largest_ref.split("v")[1]
incremented_patch = increment_patch(version_numbers)
# POST 요청을 보내기 위한 데이터 구성
post_data = {
"name": f"stg{incremented_patch}",
"target": {
"hash": f"{commit}"
}
}
# POST 요청 보내기
response = requests.post(push_url, headers=headers, json=post_data)
steps: - run: name: "webhook spinnaker" command: | curl --location --request POST "https://api.spinnaker.com/webhooks/webhook/test" \ --header 'Content-Type: application/json' \ --data-raw '{ "parameters": { "tag": $CIRCLE_TAG } }'
circleci project webhook에는 두가지 옵션이 존재한다.
- workflow, job
둘중 하나만(job) 체크해서 진행함.
post값을 확인해보니 tag 값을 보내주지 않음.... ㅠㅠㅠㅠ
그리고 bitbucket api에서 원하는 commit값으로 tag값을 가져올 수 없음..
결국 앞에서 작성했던 labmda 코드를 가져다가 응용
아래는 생략된 누추한 코드
def find_commit_index(job_commit, check_commit):
for index, commit in enumerate(check_commit):
if commit.startswith(job_commit):
return index
return -1
# circleci에서 빌드완료환 tag값 찾기
# build 성공 실패 확인 필요
def tag_4_commit(event):
request_body=json.loads(event["body"])
#status확인
job_status=request_body["job"]["status"]
#repo name
repo=request_body["project"]["name"]
#circleci job name
job_name=request_body["job"]["name"]
#job commit
job_commit=request_body["pipeline"]["vcs"]["revision"]
url = f'https://api.bitbucket.org/2.0/repositories/{proj}/{repo}/refs?q=name~"stg"&sort=-target.date'
headers = {
"Authorization": f"Basic {token}"
}
#tag값 호출
response = requests.get(url, headers=headers)
if response.status_code == 200:
data = response.json()
#tag값을 추출하여 저장
check_tag = [ref["name"] for ref in data["values"] if ref["name"].startswith("stg")]
#commit값을 추출하여 저장
check_commit = [abc["target"]['hash'] for abc in data["values"] if abc["name"].startswith("stg")]
#commit과 같은 인덱스에 있는 tag 인덱스 값 추출
index = find_commit_index(job_commit, check_commit)
if index != -1:
# job_commit를 찾은 경우
result = check_tag[index]
return result, repo
def spinnaker_trigger(result):
tag=result[0]
repo=result[1]
url = f"https://api.spinnaker.com/webhooks/webhook/test"
post_data = {
"parameters": {
"tag": f"{tag}"
}
}
response = requests.post(url, json=post_data)
print(response.text)
def lambda_handler(event, context):
result=tag_4_commit(event)
print(f"commit search {result}")
if result!=False:
res=spinnaker_trigger(result)
else:
return f"someting fail : {result}"
pip3 install -t ./python requests
다운로드 받은 파일을 zip으로 압축
Lambda > 계층에서 생성하여 위 압축파일 및 런타임을 선택
Lambda > 함수 > 계층부분에 앞에서 만든 계층 추가
api의 url은 서브 도메인 첫번째 값 뒤에 '-api' 가 붙음
ex) deploy-stg.kkk.com > deploy-api-stg.kkk.com
configuration에 automatied trigger 설정
trigger enabled 체크
parameters name이 key 값이 됨.
post 보낼때 key 값이 맞아야됨.
jira ticket 담당자 지정시 automation 실행
첫번째 람다(automation to lambda) 마지막에 bitbucket tag push 성공 noti
두번째 람다(circleci to lamda) 마지막에 ci (build) complete noti