Skip to content

Commit 770c58d

Browse files
cameronmeissnerCameron Meissner
andauthored
feat(client): add official build + publishing pipeline definition (#153)
Co-authored-by: Cameron Meissner <cameissner@microsoft.com>
1 parent fa845ff commit 770c58d

10 files changed

Lines changed: 396 additions & 21 deletions

File tree

.pipelines/client/e2e.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ pr:
1111
- client/go.sum
1212
- client/**/*.go
1313
- .pipelines/client/e2e.yaml
14-
- .pipelines/client/scripts/common.sh
1514
- .pipelines/client/scripts/run-pipeline.sh
1615

1716
pool:

.pipelines/client/publish.yaml

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
parameters:
2+
- name: VERSION
3+
displayName: Version
4+
type: string
5+
6+
stages:
7+
- stage: build
8+
jobs:
9+
- job: build_linux_amd64
10+
displayName: Build linux-amd64
11+
pool:
12+
name: $(AMD64_POOL_NAME)
13+
dependsOn: []
14+
steps:
15+
- template: templates/build-template.yaml
16+
parameters:
17+
VERSION: ${{ parameters.VERSION }}
18+
OS: linux
19+
ARCH: amd64
20+
21+
- job: build_linux_arm64
22+
displayName: Build linux-arm64
23+
pool:
24+
name: $(ARM64_POOL_NAME)
25+
dependsOn: []
26+
steps:
27+
- template: templates/build-template.yaml
28+
parameters:
29+
VERSION: ${{ parameters.VERSION }}
30+
OS: linux
31+
ARCH: arm64
32+
33+
- job: build_windows_amd64
34+
displayName: Build windows-amd64
35+
pool:
36+
name: $(AMD64_POOL_NAME)
37+
dependsOn: []
38+
steps:
39+
- template: templates/build-template.yaml
40+
parameters:
41+
VERSION: ${{ parameters.VERSION }}
42+
OS: windows
43+
ARCH: amd64
44+
EXTENSION: .exe
45+
46+
- stage: publish
47+
pool:
48+
name: $(AMD64_POOL_NAME)
49+
dependsOn: build
50+
jobs:
51+
- job: publish
52+
steps:
53+
- checkout: self
54+
55+
- task: DownloadPipelineArtifact@2
56+
displayName: Download linux-amd64
57+
inputs:
58+
buildType: current
59+
artifactName: linux-amd64
60+
path: $(Build.ArtifactStagingDirectory)
61+
62+
- task: DownloadPipelineArtifact@2
63+
displayName: Download linux-arm64
64+
inputs:
65+
buildType: current
66+
artifactName: linux-arm64
67+
path: $(Build.ArtifactStagingDirectory)
68+
69+
- task: DownloadPipelineArtifact@2
70+
displayName: Download windows-amd64
71+
inputs:
72+
buildType: current
73+
artifactName: windows-amd64
74+
path: $(Build.ArtifactStagingDirectory)
75+
76+
- bash: /bin/bash .pipelines/client/scripts/generate-github-token.sh
77+
env:
78+
PRIVATE_KEY: $(aks-node-sig-release-assistant-private-key)
79+
CLIENT_ID: $(aks-node-sig-release-assistant-client-id)
80+
INSTALLATION_ID: $(aks-node-sig-release-assistant-installation-id)
81+
displayName: Generate GitHub token
82+
83+
- bash: /bin/bash .pipelines/client/scripts/github-publish.sh
84+
env:
85+
ARTIFACT_DIRECTORY: "$(Build.ArtifactStagingDirectory)"
86+
VERSION: ${{ parameters.Version }}
87+
GITHUB_TOKEN: $(GITHUB_TOKEN)
88+
displayName: Publish new GitHub release

.pipelines/client/scripts/build.sh

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/bin/bash
2+
set -euxo pipefail
3+
4+
[ -z "${VERSION:-}" ] && echo "VERSION must be specified" && exit 1
5+
[ -z "${OS:-}" ] && echo "OS must be specified" && exit 1
6+
[ -z "${ARCH:-}" ] && echo "ARCH must be specified" && exit 1
7+
[ -z "${STAGING_DIRECTORY:-}" ] && echo "STAGING_DIRECTORY must be specified" && exit 1
8+
9+
EXTENSION="${EXTENSION:-}"
10+
11+
# checkout the particular tag we'd like to build
12+
git checkout client/${VERSION}
13+
14+
# run unit tests
15+
cd client && make test
16+
17+
# build the binary for the target OS/arch
18+
LDFLAGS="-X github.com/Azure/aks-secure-tls-bootstrap/client/internal/build.version=${VERSION}"
19+
make build-prod OS="${OS}" ARCH="${ARCH}" EXTENSION="${EXTENSION}" LDFLAGS="${LDFLAGS}"
20+
21+
BIN_PATH="bin/aks-secure-tls-bootstrap-client-${ARCH}${EXTENSION}"
22+
if [ ! -f "${BIN_PATH}" ]; then
23+
echo "binary ${BIN_PATH} is missing after building with make"
24+
exit 1
25+
fi
26+
27+
# we can only test linux binaries since we're using Ubuntu-based build agents
28+
if [ "${OS,,}" = "linux" ]; then
29+
set +e
30+
sudo chmod +x "${BIN_PATH}"
31+
HELP_OUTPUT=$(sudo ./${BIN_PATH} -h 2>&1)
32+
EXIT_CODE=$?
33+
set -e
34+
35+
if [ $EXIT_CODE -ne 0 ]; then
36+
echo "failed to run help command on newly-built binary, exit code: ${EXIT_CODE}"
37+
exit 1
38+
fi
39+
if [[ "${HELP_OUTPUT}" != *"${VERSION}"* ]]; then
40+
echo "help command output did not contain expected version string: \"${VERSION}\""
41+
exit 1
42+
fi
43+
fi
44+
45+
# move the newly-built binary to the staging directory
46+
mv "${BIN_PATH}" "${STAGING_DIRECTORY}/aks-secure-tls-bootstrap-client-${ARCH}${EXTENSION}"

.pipelines/client/scripts/common.sh

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
[ -z "${PRIVATE_KEY:-}" ] && echo "PRIVATE_KEY must be set" && exit 1
5+
[ -z "${CLIENT_ID:-}" ] && echo "CLIENT_ID must be set" && exit 1
6+
[ -z "${INSTALLATION_ID:-}" ] && echo "INSTALLATION_ID must be set" && exit 1
7+
8+
REPO_NAME="aks-secure-tls-bootstrap"
9+
10+
now=$(date +%s)
11+
iat=$((${now} - 60)) # issues 60 seconds in the past
12+
exp=$((${now} + 600)) # expires 10 minutes in the future
13+
14+
b64enc() { openssl base64 | tr -d '=' | tr '/+' '_-' | tr -d '\n'; }
15+
16+
header_json='{
17+
"typ":"JWT",
18+
"alg":"RS256"
19+
}'
20+
# encode the JWT header
21+
header=$(echo -n "${header_json}" | b64enc)
22+
23+
payload_json="{
24+
\"iat\":${iat},
25+
\"exp\":${exp},
26+
\"iss\":\"${CLIENT_ID}\"
27+
}"
28+
# encode the JWT payload
29+
payload=$(echo -n "${payload_json}" | b64enc)
30+
31+
# create the JWT signature
32+
header_payload="${header}"."${payload}"
33+
signature=$(
34+
openssl dgst -sha256 -sign <(echo -n "${PRIVATE_KEY}") \
35+
<(echo -n "${header_payload}") | b64enc
36+
)
37+
38+
# create the JWT
39+
jwt="${header_payload}"."${signature}"
40+
41+
# get the installation token
42+
response=$(curl -X POST -L \
43+
-H "Accept: application/vnd.github+json" \
44+
-H "Authorization: Bearer $jwt" \
45+
-H "X-GitHub-Api-Version: 2022-11-28" \
46+
-d "{\"repositories\":[\"$REPO_NAME\"]}" \
47+
"https://api.github.com/app/installations/${INSTALLATION_ID}/access_tokens")
48+
49+
token="$(echo $response | jq -r '.token')"
50+
if [ -z "$token" ] || [ "$token" == "null" ]; then
51+
echo "unable to generate installation access token for repository: $REPO_NAME"
52+
echo "unable to extract token from GitHub resposne"
53+
exit 1
54+
fi
55+
56+
echo "##vso[task.setvariable variable=GITHUB_TOKEN;issecret=true]$token"
57+
echo "generated installation access token for repository: $REPO_NAME"
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#!/bin/bash
2+
set -euxo pipefail
3+
4+
[ -z "${VERSION:-}" ] && echo "VERSION must be specified" && exit 1
5+
[ -z "${ARTIFACT_DIRECTORY:-}" ] && echo "ARTIFACT_DIRECTORY must be specified" && exit 1
6+
7+
LINUX_AMD64_PATH="${ARTIFACT_DIRECTORY}/aks-secure-tls-bootstrap-client-amd64"
8+
LINUX_ARM64_PATH="${ARTIFACT_DIRECTORY}/aks-secure-tls-bootstrap-client-arm64"
9+
WINDOWS_AMD64_PATH="${ARTIFACT_DIRECTORY}/aks-secure-tls-bootstrap-client-amd64.exe"
10+
11+
REPO_PATH="Azure/aks-secure-tls-bootstrap"
12+
13+
verify_artifacts() {
14+
if [ ! -f "${LINUX_AMD64_PATH}" ]; then
15+
echo "could not find linux-amd64 client binary artifact at: ${LINUX_AMD64_PATH}"
16+
exit 1
17+
fi
18+
if [ ! -f "${LINUX_ARM64_PATH}" ]; then
19+
echo "could not find linux-arm64 client binary artifact at: ${LINUX_ARM64_PATH}"
20+
exit 1
21+
fi
22+
if [ ! -f "${WINDOWS_AMD64_PATH}" ]; then
23+
echo "could not find windows-amd64 client binary artifact at: ${WINDOWS_AMD64_PATH}"
24+
exit 1
25+
fi
26+
}
27+
28+
create_linux_tarballs() {
29+
CLIENT_PATH="aks-secure-tls-bootstrap-client"
30+
31+
# create linux-amd64 tarball
32+
mv "${LINUX_AMD64_PATH}" "${CLIENT_PATH}"
33+
LINUX_AMD64_TAR="linux-amd64.tar.gz"
34+
tar -czvf "${LINUX_AMD64_TAR}" "${CLIENT_PATH}"
35+
36+
# cleanup
37+
rm -f "${CLIENT_PATH}"
38+
39+
# create linux-arm64 tarball
40+
mv "${LINUX_ARM64_PATH}" "${CLIENT_PATH}"
41+
LINUX_ARM64_TAR="linux-arm64.tar.gz"
42+
tar -czvf "${LINUX_ARM64_TAR}" "${CLIENT_PATH}"
43+
44+
# cleanup
45+
rm -f "${CLIENT_PATH}"
46+
}
47+
48+
create_windows_zip_archive() {
49+
CLIENT_PATH="aks-secure-tls-bootstrap-client.exe"
50+
51+
#create windows-amd64 zip archive
52+
mv "${WINDOWS_AMD64_PATH}" "${CLIENT_PATH}"
53+
WINDOWS_AMD64_ZIP="windows-amd64.zip"
54+
zip -r "${WINDOWS_AMD64_ZIP}" "${CLIENT_PATH}"
55+
56+
# cleanup
57+
rm -f "${CLIENT_PATH}"
58+
}
59+
60+
create_github_release() {
61+
TAG_NAME="client/${VERSION}"
62+
echo "Creating GitHub release for tag: ${TAG_NAME}"
63+
64+
# create the release
65+
CREATE_RELEASE_RESPONSE=$(curl -s -X POST \
66+
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
67+
-H "Accept: application/vnd.github.v3+json" \
68+
-H "Content-Type: application/json" \
69+
"https://api.github.com/repos/${REPO_PATH}/releases" \
70+
-d "{
71+
\"tag_name\": \"${TAG_NAME}\",
72+
\"name\": \"${TAG_NAME}\",
73+
\"draft\": false,
74+
\"prerelease\": false
75+
}")
76+
RELEASE_ID=$(echo "${CREATE_RELEASE_RESPONSE}" | jq -r '.id')
77+
UPLOAD_URL=$(echo "${CREATE_RELEASE_RESPONSE}" | jq -r '.upload_url')
78+
UPLOAD_URL="${UPLOAD_URL%\{*}"
79+
if [ -z "${RELEASE_ID}" ] || [ "${RELEASE_ID}" = "null" ] || [ -z "${UPLOAD_URL}" ] || [ "${UPLOAD_URL}" = "null" ]; then
80+
echo "Failed to create GitHub release. Response:"
81+
echo "${CREATE_RELEASE_RESPONSE}"
82+
exit 1
83+
fi
84+
85+
echo "Created GitHub release with ID: ${RELEASE_ID}"
86+
87+
# upload artifacts
88+
upload_asset "${UPLOAD_URL}" "${LINUX_AMD64_TAR}" "application/gzip"
89+
upload_asset "${UPLOAD_URL}" "${LINUX_ARM64_TAR}" "application/gzip"
90+
upload_asset "${UPLOAD_URL}" "${WINDOWS_AMD64_ZIP}" "application/zip"
91+
92+
echo "Successfully created GitHub release for tag: ${TAG_NAME}"
93+
echo "Release URL: https://github.com/${REPO_PATH}/releases/tag/${TAG_NAME}"
94+
}
95+
96+
upload_asset() {
97+
local upload_url="$1"
98+
local file_path="$2"
99+
local content_type="$3"
100+
local file_name=$(basename "${file_path}")
101+
102+
echo "Uploading asset: ${file_name}"
103+
104+
UPLOAD_RESPONSE=$(curl -s -X POST \
105+
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
106+
-H "Accept: application/vnd.github.v3+json" \
107+
-H "Content-Type: ${content_type}" \
108+
"${upload_url}?name=${file_name}" \
109+
--data-binary "@${file_path}")
110+
111+
# Check if upload was successful
112+
if [ "$(echo "${UPLOAD_RESPONSE}" | jq -r '.state')" = "uploaded" ]; then
113+
echo "Successfully uploaded: ${file_name}"
114+
else
115+
echo "Failed to upload asset: ${file_name}. Response:"
116+
echo "${UPLOAD_RESPONSE}"
117+
exit 1
118+
fi
119+
}
120+
121+
verify_artifacts
122+
create_linux_tarballs
123+
create_windows_zip_archive
124+
create_github_release

.pipelines/client/scripts/run-pipeline.sh

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
set -euo pipefail
33
set +x
44

5-
source .pipelines/client/scripts/common.sh
6-
75
TEST_BRANCH="${TEST_BRANCH:-master}"
86
ADDITIONAL_VARS="${ADDITIONAL_VARS:-}"
97

@@ -15,6 +13,22 @@ ADDITIONAL_VARS="${ADDITIONAL_VARS:-}"
1513

1614
WAIT_SECONDS=300 # 5 minutes
1715

16+
retrycmd_if_failure() {
17+
retries=$1; wait_sleep=$2; shift && shift
18+
for i in $(seq 1 $retries); do
19+
"${@}" && break || \
20+
echo "Failed to execute command \"$@\""
21+
if [ $i -eq $retries ]; then
22+
echo "ERROR: Exhausted all retries (${i}/${retries})"
23+
return 1
24+
else
25+
echo "$(($retries - $i)) retries remaining"
26+
sleep $wait_sleep
27+
fi
28+
done
29+
echo Executed \"$@\" $i times;
30+
}
31+
1832
main() {
1933
# retry 3 times, waiting 1 minute between attempts
2034
retrycmd_if_failure 3 60 runSuite || exit $?

0 commit comments

Comments
 (0)