diff --git a/.github/workflows/building-comfort.yml b/.github/workflows/building-comfort.yml new file mode 100644 index 0000000..eb0a488 --- /dev/null +++ b/.github/workflows/building-comfort.yml @@ -0,0 +1,178 @@ +# Copyright 2025 The Drasi Authors. +# +# Licensed 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. + +name: Building Comfort E2E Test + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + repository_dispatch: + types: [building-comfort-trigger] + +permissions: + contents: read + id-token: write + +env: + CLUSTER_NAME_REDIS: "building-comfort-aks-${{ github.run_number }}-redis" + CLUSTER_NAME_ROCKS: "building-comfort-aks-${{ github.run_number }}-rocks" + CLUSTER_NAME_MEMORY: "building-comfort-aks-${{ github.run_number }}-memory" + RESOURCE_GROUP_NAME: "project-drasi" + +jobs: + infra: + runs-on: ubuntu-latest + strategy: + matrix: + cluster_type: [redis, rocks, memory] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Deploy AKS cluster + run: | + az deployment group create \ + --resource-group ${{ env.RESOURCE_GROUP_NAME }} \ + --template-file infra/aks/aks-cluster.bicep \ + --parameters clusterName="building-comfort-aks-${{ github.run_number }}-${{ matrix.cluster_type }}" \ + --name "aks-cluster-${{ github.run_number }}-${{ matrix.cluster_type }}" + + deploy-tests: + runs-on: ubuntu-latest + needs: infra + strategy: + matrix: + cluster_type: [redis, rocks, memory] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Get AKS credentials + run: | + az aks get-credentials \ + --resource-group ${{ env.RESOURCE_GROUP_NAME }} \ + --name "building-comfort-aks-${{ github.run_number }}-${{ matrix.cluster_type }}" \ + --overwrite-existing + + + - name: Install Drasi CLI + run: | + curl -fsSL https://raw.githubusercontent.com/drasi-project/drasi-platform/main/cli/installers/install-drasi-cli.sh | /bin/bash + + + - name: Deploy building comfort tests + run: | + cd e2e-test-framework + ./examples/building_comfort/drasi/run_test_with_${{ matrix.cluster_type }}.sh + + sleep 10 + + # Wait for tests to complete by polling the query status + echo "Waiting for tests to complete..." + while true; do + + # Check query status + RESPONSE=$(curl -s "http://localhost:63123/test_run_host/queries/github_dev_repo.building_comfort.test_run_001.room-comfort-level") + echo "API Response: $RESPONSE" + + STATUS=$(echo "$RESPONSE" | jq -r '.query_observer.status // "Running"') + echo "Current status: $STATUS" + if [ "$STATUS" = "Stopped" ]; then + echo "Tests completed successfully!" + break + fi + + echo "Tests still running, waiting 10 seconds..." + sleep 10 + done + + - name: Export test results + run: | + cd e2e-test-framework + ./examples/building_comfort/drasi/get_test_run_results.sh "building-comfort-aks-${{ github.run_number }}-${{ matrix.cluster_type }}" + + - name: Re-authenticate with Azure (refresh token) + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Upload test results to storage account + run: | + cd e2e-test-framework + CLUSTER_NAME="building-comfort-aks-${{ github.run_number }}-${{ matrix.cluster_type }}" + CONTAINER_NAME="test-results" + STORAGE_ACCOUNT="drasidev" + + # Create container if it doesn't exist + az storage container create \ + --name $CONTAINER_NAME \ + --account-name $STORAGE_ACCOUNT \ + --auth-mode login \ + --only-show-errors + + # Upload all files from the results directory with Cold tier + az storage blob upload-batch \ + --destination $CONTAINER_NAME \ + --source "./$CLUSTER_NAME" \ + --destination-path "building-comfort/$CLUSTER_NAME" \ + --account-name $STORAGE_ACCOUNT \ + --auth-mode login \ + --tier Cold + + + cleanup: + runs-on: ubuntu-latest + needs: deploy-tests + if: always() + strategy: + matrix: + cluster_type: [redis, rocks, memory] + + steps: + - name: Azure Login + uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Delete AKS cluster + run: | + az aks delete \ + --resource-group ${{ env.RESOURCE_GROUP_NAME }} \ + --name "building-comfort-aks-${{ github.run_number }}-${{ matrix.cluster_type }}" \ + --yes --no-wait \ No newline at end of file diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index 62500a4..7e697a9 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -23,7 +23,9 @@ on: image_prefix: description: 'Image Prefix' required: false - default: 'ghcr.io/drasi-project' + default: 'ghcr.io/drasi-project' + repository_dispatch: + types: [draft-release-trigger] permissions: id-token: write # Required for requesting the JWT @@ -32,6 +34,8 @@ permissions: env: RELEASE_PATH: ./release + TAG: ${{ github.event.client_payload.tag || inputs.tag }} + IMAGE_PREFIX: ${{ github.event.client_payload.image_prefix || inputs.image_prefix }} TEST_INFRA_COMPONENTS: '[ {"label": "E2E proxy", "path": "e2e-test-framework/proxy", "name": "e2e-proxy", "platforms": "linux/amd64,linux/arm64"}, @@ -94,8 +98,8 @@ jobs: if: contains(matrix.component.platforms, steps.platform.outputs.platform) run: | cd ${{ matrix.component.path }} - DOCKER_TAG_VERSION=${{ inputs.tag }}${{ steps.platform.outputs.suffix }} \ - IMAGE_PREFIX=${{ inputs.image_prefix }} \ + DOCKER_TAG_VERSION=${{ env.TAG }}${{ steps.platform.outputs.suffix }} \ + IMAGE_PREFIX=${{ env.IMAGE_PREFIX }} \ DOCKERX_OPTS="--push --platform ${{ steps.platform.outputs.platform }}" \ make @@ -117,32 +121,32 @@ jobs: expected_platforms=$(echo '${{ env.TEST_INFRA_COMPONENTS }}' | jq -r ".[] | select(.name == \"$component\") | .platforms") if echo "$expected_platforms" | grep -q "linux/amd64"; then - if docker manifest inspect ghcr.io/drasi-project/$component:${{ inputs.tag }}-amd64 > /dev/null 2>&1; then - echo "Manifest for $component:${{ inputs.tag }}-amd64:" - docker manifest inspect ghcr.io/drasi-project/$component:${{ inputs.tag }}-amd64 - manifests+=("ghcr.io/drasi-project/$component:${{ inputs.tag }}-amd64") + if docker manifest inspect ${{ env.IMAGE_PREFIX }}/$component:${{ env.TAG }}-amd64 > /dev/null 2>&1; then + echo "Manifest for $component:${{ env.TAG }}-amd64:" + docker manifest inspect ${{ env.IMAGE_PREFIX }}/$component:${{ env.TAG }}-amd64 + manifests+=("${{ env.IMAGE_PREFIX }}/$component:${{ env.TAG }}-amd64") else - echo "Error: Expected amd64 manifest not found for $component:${{ inputs.tag }}-amd64" >&2 + echo "Error: Expected amd64 manifest not found for $component:${{ env.TAG }}-amd64" >&2 exit 1 fi fi if echo "$expected_platforms" | grep -q "linux/arm64"; then - if docker manifest inspect ghcr.io/drasi-project/$component:${{ inputs.tag }}-arm64 > /dev/null 2>&1; then - echo "Manifest for $component:${{ inputs.tag }}-arm64:" - docker manifest inspect ghcr.io/drasi-project/$component:${{ inputs.tag }}-arm64 - manifests+=("ghcr.io/drasi-project/$component:${{ inputs.tag }}-arm64") + if docker manifest inspect ${{ env.IMAGE_PREFIX }}/$component:${{ env.TAG }}-arm64 > /dev/null 2>&1; then + echo "Manifest for $component:${{ env.TAG }}-arm64:" + docker manifest inspect ${{ env.IMAGE_PREFIX }}/$component:${{ env.TAG }}-arm64 + manifests+=("${{ env.IMAGE_PREFIX }}/$component:${{ env.TAG }}-arm64") else - echo "Error: Expected arm64 manifest not found for $component:${{ inputs.tag }}-arm64" >&2 + echo "Error: Expected arm64 manifest not found for $component:${{ env.TAG }}-arm64" >&2 exit 1 fi fi if [ ${#manifests[@]} -gt 0 ]; then - docker buildx imagetools create -t ghcr.io/drasi-project/$component:${{ inputs.tag }} ${manifests[@]} + docker buildx imagetools create -t ${{ env.IMAGE_PREFIX }}/$component:${{ env.TAG }} ${manifests[@]} else echo "No manifests found for $component, skipping manifest creation." fi diff --git a/e2e-test-framework/examples/building_comfort/drasi/get_test_run_results.sh b/e2e-test-framework/examples/building_comfort/drasi/get_test_run_results.sh index c6c7a9c..66cb11d 100755 --- a/e2e-test-framework/examples/building_comfort/drasi/get_test_run_results.sh +++ b/e2e-test-framework/examples/building_comfort/drasi/get_test_run_results.sh @@ -39,20 +39,20 @@ if [ -z "$POD_NAME" ]; then fi # Source -kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/az_dev_repo.building_comfort.test_run_001/sources/facilities-db/test_run_summary.json" "./${TEST_RUN_ID}/source_summary.json" +kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/github_dev_repo.building_comfort.test_run_001/sources/facilities-db/test_run_summary.json" "./${TEST_RUN_ID}/source_summary.json" # Query Observer -kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/az_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/test_run_summary.json" "./${TEST_RUN_ID}/query_summary.json" +kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/github_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/test_run_summary.json" "./${TEST_RUN_ID}/query_summary.json" # Query Result Profiler -kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/az_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/summary.json" "./${TEST_RUN_ID}/query_profiler_summary.json" +kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/github_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/summary.json" "./${TEST_RUN_ID}/query_profiler_summary.json" -kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/az_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/change_all_abs.png" "./${TEST_RUN_ID}/query_profiler_viz_all_abs.png" -kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/az_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/change_all_rel.png" "./${TEST_RUN_ID}/query_profiler_viz_all_rel.png" -kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/az_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/change_drasi_only_abs.png" "./${TEST_RUN_ID}/query_profiler_viz_drasi_only_abs.png" -kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/az_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/change_drasi_only_rel.png" "./${TEST_RUN_ID}/query_profiler_viz_drasi_only_rel.png" +kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/github_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/change_all_abs.png" "./${TEST_RUN_ID}/query_profiler_viz_all_abs.png" +kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/github_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/change_all_rel.png" "./${TEST_RUN_ID}/query_profiler_viz_all_rel.png" +kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/github_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/change_drasi_only_abs.png" "./${TEST_RUN_ID}/query_profiler_viz_drasi_only_abs.png" +kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/github_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/change_drasi_only_rel.png" "./${TEST_RUN_ID}/query_profiler_viz_drasi_only_rel.png" -kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/az_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/change_rates.csv" "./${TEST_RUN_ID}/query_profiler_change_rates.csv" -kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/az_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/change_distributions.csv" "./${TEST_RUN_ID}/query_profiler_change_distributions.csv" +kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/github_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/change_rates.csv" "./${TEST_RUN_ID}/query_profiler_change_rates.csv" +kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/github_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/change_distributions.csv" "./${TEST_RUN_ID}/query_profiler_change_distributions.csv" -# kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/az_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/change_00000.jsonl" "./${TEST_RUN_ID}/query_profiler_change_log_sample.jsonl" +# kubectl cp -c "$CONTAINER" "$NAMESPACE/$POD_NAME:/drasi_data_store/test_runs/github_dev_repo.building_comfort.test_run_001/queries/room-comfort-level/result_stream_log/profiler/change_00000.jsonl" "./${TEST_RUN_ID}/query_profiler_change_log_sample.jsonl" diff --git a/infra/aks/aks-cluster.bicep b/infra/aks/aks-cluster.bicep new file mode 100644 index 0000000..681b7f0 --- /dev/null +++ b/infra/aks/aks-cluster.bicep @@ -0,0 +1,42 @@ +@description('The name of the AKS cluster') +param clusterName string + +@description('The location of the AKS cluster') +param location string = 'westus3' + +@description('Optional DNS prefix to use with hosted Kubernetes API server FQDN') +param dnsPrefix string = 'drasi-aks-${uniqueString(resourceGroup().id)}' + +@description('The number of nodes for the system pool') +param systemNodeCount int = 2 + +@description('The size of the Virtual Machine') +param vmSize string = 'Standard_D8ds_v5' + +resource aks 'Microsoft.ContainerService/managedClusters@2024-02-01' = { + name: clusterName + location: location + identity: { + type: 'SystemAssigned' + } + properties: { + dnsPrefix: dnsPrefix + autoUpgradeProfile: { + nodeOSUpgradeChannel: 'NodeImage' + upgradeChannel: 'stable' + } + agentPoolProfiles: [ + { + name: 'agentpool' + count: systemNodeCount + vmSize: vmSize + osType: 'Linux' + osSKU: 'AzureLinux' + mode: 'System' + type: 'VirtualMachineScaleSets' + } + ] + } +} + +output controlPlaneFQDN string = aks.properties.fqdn