Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions .github/azure-live-tests/azure-pipelines.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

# Keep the fixture alive while Azure Pipelines uses its read-only identity.
set -euo pipefail
run_id=""
completed=false

# Called by the EXIT handler.
# shellcheck disable=SC2329
cancel_unfinished_run() {
access_token=$(az account get-access-token \
--resource https://app.vssps.visualstudio.com/ \
--query accessToken \
--output tsv) || return
state=$(curl --fail-with-body --silent --show-error \
--header "Authorization: Bearer $access_token" \
"https://dev.azure.com/${AZURE_DEVOPS_ORGANIZATION}/${AZURE_DEVOPS_PROJECT}/_apis/pipelines/${AZURE_DEVOPS_PIPELINE_ID}/runs/${run_id}?api-version=7.1" |
jq --raw-output '.state') || return
if [[ "$state" != "completed" ]]; then
curl --fail-with-body --silent --show-error \
--request PATCH \
--header "Authorization: Bearer $access_token" \
--header "Content-Type: application/json" \
--data '{"status":"cancelling"}' \
"https://dev.azure.com/${AZURE_DEVOPS_ORGANIZATION}/${AZURE_DEVOPS_PROJECT}/_apis/build/builds/${run_id}?api-version=7.1" \
>/dev/null
fi
}
# shellcheck disable=SC2329
cleanup() {
local result=$?
trap - EXIT
if [[ -n "$run_id" && "$completed" != true ]]; then
if ! cancel_unfinished_run; then
echo "Failed to cancel the unfinished Azure Pipelines run" >&2
if (( result == 0 )); then result=1; fi
fi
fi
exit "$result"
}
trap 'cleanup' EXIT
trap 'exit 130' INT
trap 'exit 143' TERM

access_token=$(az account get-access-token \
--resource https://app.vssps.visualstudio.com/ \
--query accessToken \
--output tsv)
test -n "$access_token"
if [[ "${GITHUB_ACTIONS:-}" == true ]]; then
echo "::add-mask::$access_token"
fi

# Pass the probe as a run variable so the bootstrap YAML needs no new parameter.
request_body=$(jq --null-input \
--arg github_sha "$GITHUB_SHA" \
--arg github_ref "$GITHUB_REF" \
--arg probe_url "$REQSIGN_AZURE_STORAGE_PROBE_URL" \
'{templateParameters: {githubSha: $github_sha, githubRef: $github_ref},
variables: {REQSIGN_AZURE_STORAGE_PROBE_URL: {value: $probe_url}}}')
response_file="$RUNNER_TEMP/azure-pipelines-queue-response.json"
if ! http_status=$(curl --silent --show-error \
--output "$response_file" \
--write-out '%{http_code}' \
--request POST \
--header "Authorization: Bearer $access_token" \
--header "Content-Type: application/json" \
--data "$request_body" \
"https://dev.azure.com/${AZURE_DEVOPS_ORGANIZATION}/${AZURE_DEVOPS_PROJECT}/_apis/pipelines/${AZURE_DEVOPS_PIPELINE_ID}/runs?api-version=7.1"); then
echo "Failed to send the Azure Pipelines queue request" >&2
exit 1
fi
if (( http_status < 200 || http_status >= 300 )); then
message=$(jq --raw-output '.message // .error.message // "No error message returned"' \
"$response_file" 2>/dev/null || echo "Non-JSON error response")
message=$(tr '\r\n' ' ' <<< "$message" | cut -c1-1000)
echo "Azure Pipelines queue request failed with HTTP $http_status: $message" >&2
exit 1
fi
response=$(<"$response_file")

run_id=$(jq --raw-output '.id // empty' <<< "$response")
run_url=$(jq --raw-output '._links.web.href // empty' <<< "$response")
test -n "$run_id"
test -n "$run_url"
echo "Queued Azure Pipelines run: $run_url"

deadline=$((SECONDS + 3300))
while (( SECONDS < deadline )); do
access_token=$(az account get-access-token \
--resource https://app.vssps.visualstudio.com/ \
--query accessToken \
--output tsv)
response=$(curl --fail-with-body --silent --show-error \
--header "Authorization: Bearer $access_token" \
"https://dev.azure.com/${AZURE_DEVOPS_ORGANIZATION}/${AZURE_DEVOPS_PROJECT}/_apis/pipelines/${AZURE_DEVOPS_PIPELINE_ID}/runs/${run_id}?api-version=7.1")
state=$(jq --raw-output '.state' <<< "$response")
result=$(jq --raw-output '.result // empty' <<< "$response")
if [[ "$state" == "completed" ]]; then
completed=true
echo "Azure Pipelines run completed with result '$result': $run_url"
test "$result" = "succeeded"
exit
fi
sleep 15
done
echo "Azure Pipelines run did not complete before the timeout: $run_url" >&2
exit 1
97 changes: 97 additions & 0 deletions .github/azure-live-tests/imds-vm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

set -euo pipefail

: "${AZURE_RUNTIME_RESOURCE_GROUP:?}"
: "${RUNNER_TEMP:?}"
attempts_file="$RUNNER_TEMP/azure-imds-vm-attempts"

case "${1:-}" in
create)
: "${GITHUB_RUN_ID:?}"
: "${GITHUB_RUN_ATTEMPT:?}"
: "${GITHUB_OUTPUT:?}"
: "${AZURE_IMDS_IDENTITY_ID:?}"
ssh-keygen -q -t ed25519 -N '' -f "$RUNNER_TEMP/azure-imds-vm-key"
: > "$attempts_file"
attempt=0
while read -r location vnet size; do
attempt=$((attempt + 1))
vm_name="reqsign-imds-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${location}-${attempt}"
# Record before provisioning so cleanup also covers partial deployments.
echo "$vm_name" >> "$attempts_file"
if az vm create \
--resource-group "$AZURE_RUNTIME_RESOURCE_GROUP" \
--name "$vm_name" --location "$location" \
--image Ubuntu2404 --size "$size" \
--admin-username azureuser \
--ssh-key-values "$RUNNER_TEMP/azure-imds-vm-key.pub" \
--assign-identity "$AZURE_IMDS_IDENTITY_ID" \
--vnet-name "$vnet" --subnet reqsign-ci-subnet \
--nsg "" --public-ip-address "" --security-type Standard \
--os-disk-name "${vm_name}-os" \
--nic-delete-option Delete --os-disk-delete-option Delete \
--tags Project=reqsign Purpose=live-test GitHubRunId="$GITHUB_RUN_ID" \
--only-show-errors --output none; then
echo "name=$vm_name" >> "$GITHUB_OUTPUT"
exit 0
fi
done <<'CANDIDATES'
eastus reqsign-ci-vnet Standard_B1s
eastus reqsign-ci-vnet Standard_B2s
eastus reqsign-ci-vnet Standard_D2s_v6
eastus2 reqsign-ci-vnet-eastus2 Standard_B1s
eastus2 reqsign-ci-vnet-eastus2 Standard_B2s
eastus2 reqsign-ci-vnet-eastus2 Standard_D2s_v7
eastus2 reqsign-ci-vnet-eastus2 Standard_D2s_v6
CANDIDATES
echo "All configured Azure VM deployment attempts failed; see the errors above" >&2
exit 1
;;
cleanup)
[[ -f "$attempts_file" ]] || exit 0
result=0
while read -r vm_name; do
if ! vm_id=$(az vm list --resource-group "$AZURE_RUNTIME_RESOURCE_GROUP" \
--query "[?name=='${vm_name}'].id | [0]" --output tsv --only-show-errors); then
result=1
continue
fi
if [[ -n "$vm_id" ]]; then
if ! az vm delete --ids "$vm_id" --yes --force-deletion true --only-show-errors; then
result=1
continue
fi
fi
# A failed deployment may create a NIC or disk without creating a VM.
if ! resource_ids=$(az resource list --resource-group "$AZURE_RUNTIME_RESOURCE_GROUP" \
--query "[?name=='${vm_name}VMNic' || name=='${vm_name}-os'].id" \
--output tsv --only-show-errors); then
result=1
continue
fi
while IFS= read -r resource_id; do
[[ -n "$resource_id" ]] || continue
az resource delete --ids "$resource_id" --only-show-errors || result=1
done <<< "$resource_ids"
done < "$attempts_file"
exit "$result"
;;
*) echo "Usage: imds-vm.sh create|cleanup" >&2; exit 2 ;;
esac
Loading
Loading