K2Bridge/azure-pipelines.yml

488 строки
18 KiB
YAML

# CI/CD Azure DevOps deployment pipeline.
# The following variables can be optionally set for each pipeline run:
# - RUN_FLAG_TERRAFORM: Set to 1 to have `terraform apply`. By default
# `terraform apply` only runs on the main branch.
# - RUN_FLAG_PROMOTE: Set to 1 to promote the Docker image to `<supported_kibana_version>_latest` tag if
# tests are successful. By default this is only done on the main branch.
# - RUN_SET_NAMESPACE: Set to a string to deploy to the given AKS namespace,
# and not delete the namespace after the build. By default the build deploys to
# the `main` AKS namespace if run on the main branch, and otherwise to a
# temporary AKS namespace that is deleted at the end of the build.
# - RUN_CREATE_DEMO_KUSTO: Set to 1 to cause the ADX db creation. this is for
# just for the demo ADX db and should be set only once after a deletion of
# that DB (which shouldn't normally happen)
stages:
- stage: build
displayName: Build
jobs:
- job: security_analysis
displayName: Security Analysis
condition: not(variables['IS_DEMO_PIPELINE'])
pool:
# CredScan only runs on Windows
vmImage: 'windows-latest'
steps:
- task: CredScan@2
displayName: 'Find credentials in source code'
inputs:
toolMajorVersion: 'V2'
- task: SdtReport@1
displayName: 'Security analysis report'
inputs:
AllTools: false
APIScan: false
BinSkim: false
CodesignValidation: false
CredScan: true
FortifySCA: false
FxCop: false
ModernCop: false
MSRD: false
PoliCheck: false
RoslynAnalyzers: false
SDLNativeRules: false
Semmle: false
TSLint: false
ToolLogsNotFoundAction: 'Standard'
- task: PublishSecurityAnalysisLogs@2
displayName: 'Publish security analysis logs'
inputs:
ArtifactName: 'CodeAnalysisLogs'
ArtifactType: 'Container'
AllTools: false
AntiMalware: false
APIScan: false
BinSkim: false
CodesignValidation: false
CredScan: true
FortifySCA: false
FxCop: false
ModernCop: false
MSRD: false
PoliCheck: false
RoslynAnalyzers: false
SDLNativeRules: false
Semmle: false
TSLint: false
WebScout: false
ToolLogsNotFoundAction: 'Standard'
- task: PostAnalysis@1
displayName: 'Post security analysis'
inputs:
AllTools: false
APIScan: false
BinSkim: false
CodesignValidation: false
CredScan: true
FortifySCA: false
FxCop: false
ModernCop: false
PoliCheck: false
RoslynAnalyzers: false
SDLNativeRules: false
Semmle: false
TSLint: false
VstsConsole: false
ToolLogsNotFoundAction: 'Standard'
- task: ComponentGovernanceComponentDetection@0
inputs:
scanType: 'Register'
verbosity: 'Verbose'
alertWarningLevel: 'Medium'
failOnAlert: true
- job: build_and_unittest
displayName: Build with UnitTests
condition: not(variables['IS_DEMO_PIPELINE'])
steps:
- bash: |
set -eux # fail on error
# Only build first stage of Dockerfile (build and unit test)
docker build --target build --build-arg VersionPrefix="$(SEMANTIC_VERSION)" -t k2bridge-build .
# Temporarily create container in order to extract test results file
id=$(docker create k2bridge-build)
docker cp $id:/app/TestResult.xml .
docker cp $id:/app/K2Bridge.Tests.UnitTests/coverage.cobertura.xml .
docker rm $id
displayName: Docker build & test
- task: PublishTestResults@2
displayName: Publish test results
condition: succeededOrFailed()
inputs:
testRunner: VSTest
testResultsFiles: 'TestResult.xml'
failTaskOnFailedTests: true
testRunTitle: 'Unit Tests'
# Publish the code coverage result (summary and web site)
# The summary allows to view the coverage percentage in the summary tab
# The web site allows to view which lines are covered directly in Azure Pipeline
- task: PublishCodeCoverageResults@1
displayName: 'Publish code coverage'
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: 'coverage.cobertura.xml'
pathToSources: '$(Build.SourcesDirectory)/K2Bridge/'
failIfCoverageEmpty: true
- task: AzureCLI@1
displayName: Login to ACR
inputs:
azureSubscription: $(ACR_PUSH_SERVICE_CONNECTION)
scriptLocation: inlineScript
inlineScript: |
set -eux # fail on error
az configure --defaults acr="$ACR_NAME"
az acr login
- bash: |
set -eux # fail on error
# Build runtime Docker image
# Reuses the cached build stage from the previous docker build task
docker build --build-arg VersionPrefix="$SEMANTIC_VERSION" \
-t "$ACR_NAME.azurecr.io/k2bridge:$SEMANTIC_VERSION" \
.
displayName: Build Runtime Image
- bash: |
set -eux # fail on error
# Build e2e-test Docker image
docker build --target end2endtest \
--build-arg VersionPrefix="$SEMANTIC_VERSION" \
-t "$ACR_NAME.azurecr.io/k2bridge-test:$SEMANTIC_VERSION" \
.
displayName: Build E2E-Test Image
- task: AzureCLI@1
displayName: Push Images
inputs:
azureSubscription: $(ACR_PUSH_SERVICE_CONNECTION)
scriptLocation: inlineScript
inlineScript: |
set -eux # fail on error
# Push Docker image
docker push "$ACR_NAME.azurecr.io/k2bridge:$SEMANTIC_VERSION"
docker push "$ACR_NAME.azurecr.io/k2bridge-test:$SEMANTIC_VERSION"
- job: push_helm_charts
displayName: Prepare and push helm charts
steps:
- task: AzureCLI@1
displayName: Login to ACR
inputs:
azureSubscription: $(ACR_PUSH_SERVICE_CONNECTION)
scriptLocation: inlineScript
inlineScript: |
set -eux # fail on error
az configure --defaults acr="$ACR_NAME"
az acr login
- task: HelmInstaller@1
displayName: Helm installer
inputs:
helmVersionToInstall: $(HELM_VERSION)
- task: AzureCLI@1
displayName: Push Helm Charts to ACR
inputs:
azureSubscription: $(ACR_PUSH_SERVICE_CONNECTION)
scriptLocation: inlineScript
inlineScript: |
# Push Helm chart
helm repo add elastic https://helm.elastic.co
helm repo update
helm dependency update charts/k2bridge
empty_dir="$(Build.StagingDirectory)/charts_out"
echo $SEMANTIC_VERSION
helm package --version "$SEMANTIC_VERSION" charts/k2bridge -d $empty_dir
az acr helm push --force "$(ls $empty_dir/*)"
- stage: terraform
displayName: Prepare Test Env
dependsOn: []
jobs:
- job: Terraform
steps:
# - task: AzureCLI@1
# inputs:
# azureSubscription: $(ACR_PULL_SERVICE_CONNECTION)
# scriptLocation: inlineScript
# inlineScript: |
# set -eux
# az login --service-principal --username $(AKS_SP_CLIENT_ID) --password "$password" --tenant $(TENANT_ID)
# az aks get-versions --location westeurope
# echo "1 - " $(AKS_SP_CLIENT_ID)
# echo $(az ad sp show --id $(AKS_SP_CLIENT_ID) --query objectId -o tsv 2>&1)
# echo "2 - " $(az ad sp show --id $(AKS_SP_CLIENT_ID) --query objectId 2>&1)
# oid=$(az ad sp show --id $(AKS_SP_CLIENT_ID) --query objectId -o tsv)
# echo "3 - " $(az ad sp show --id 87c08231-4e69-495c-bd3f-02999f44281e 2>&1)
# echo oid=$oid
# echo "4"
# echo "##vso[task.setvariable variable=AKS_SP_OBJECT_ID]$oid"
# displayName: Get AKS SP object ID
# env:
# password: $(AKS_SP_CLIENT_SECRET)
- template: infrastructure/terraform-tasks-template.yml
parameters:
TerraformArguments: >-
-var resource_group=$(RESOURCE_GROUP)
-var vnet_name=$(VNET_NAME)
-var aks_name=$(AKS_NAME)
-var aks_version=$(AKS_VERSION)
-var aks_sp_client_id=$(AKS_SP_CLIENT_ID)
-var aks_sp_object_id=$(AKS_SP_OBJECT_ID)
-var kusto_name=$(KUSTO_NAME)
-var kusto_admin_sp_object_id=$(AKS_SP_OBJECT_ID)
# For additional security, pass secret through environment instead of command line.
# Terraform recognizes TF_VAR prefixed environment variables.
TerraformEnvVariables:
TF_VAR_aks_sp_client_secret: $(AKS_SP_CLIENT_SECRET)
- stage: deploy_solution
displayName: Deploy Solution
dependsOn:
- build
- terraform
condition: or(succeeded(), and(succeeded('terraform'), variables['IS_DEMO_PIPELINE']))
jobs:
- job: Deploy_K2Bridge
displayName: Deploy K2Bridge
variables:
${{ if ne(variables['Build.SourceBranchName'], 'main') }}:
NOT_MAIN_ES_CONFIG: "--set elasticsearch.replicas=2 --set elasticsearch.minimumMasterNodes=2"
steps:
- template: infrastructure/setup-k8s-clients-template.yml
- task: AzureCLI@1
displayName: Login to ACR
inputs:
azureSubscription: $(ACR_PUSH_SERVICE_CONNECTION)
scriptLocation: inlineScript
inlineScript: |
set -eux # fail on error
az configure --defaults acr="$ACR_NAME"
az acr login
- task: AzureCLI@1
displayName: Add Helm repo to ACR
inputs:
azureSubscription: $(ACR_PULL_SERVICE_CONNECTION)
scriptLocation: inlineScript
inlineScript: |
set -eu # fail on error
az acr helm repo add -n "$(ACR_NAME)"
KUSTO_URI=$(az kusto cluster show -g $(RESOURCE_GROUP) -n $(KUSTO_NAME) --query uri -o tsv)
echo "##vso[task.setvariable variable=KUSTO_URI]$KUSTO_URI"
- bash: |
set -eux # fail on error
helm show chart $(ACR_NAME)/k2bridge
# List charts before deploying (for job log, useful if rerunning job)
helm list
# Deploy chart
helm upgrade --install k2bridge $(ACR_NAME)/k2bridge \
--version "$SEMANTIC_VERSION" \
--wait --timeout 30m \
--set image.repository=$(ACR_NAME).azurecr.io/k2bridge \
--set image.tag=$IMAGE_TAG \
--set replicaCount=2 \
--set settings.adxClusterUrl="$KUSTO_URI" \
--set settings.adxDefaultDatabaseName="$(KUSTO_DB)" \
--set settings.aadClientId="$(AKS_SP_CLIENT_ID)" \
--set settings.aadClientSecret="$secret" \
--set settings.aadTenantId="$TENANT_ID" \
--set settings.collectTelemetry="$COLLECT_TELEMETRY" \
--set settings.instrumentationKey="$TELEMETRY_KEY" \
--set settings.enableQueryLogging=true \
${NOT_MAIN_ES_CONFIG:-} #defaults to an empty string
# List charts after deploying (for job log)
helm list
displayName: Install Helm chart
env:
secret: $(AKS_SP_CLIENT_SECRET)
- bash: |
set -eux
kubectl run --attach --rm --restart=Never --image=curlimages/curl smoke-test-$RANDOM -- \
--max-time 5 --retry 99999 --retry-max-time 1200 http://k2bridge:8080
displayName: Smoke test
- stage: integration_tests
displayName: Integration Tests
condition: and(succeeded(), not(variables['IS_DEMO_PIPELINE']))
dependsOn:
- build
- terraform
- deploy_solution
# Deploy a new Kusto db
# Either if the db name is not set to 'demo' which means it is the Dev CI process
# Or it is the Demo CI process AND the RUN_CREATE_DEMO_KUSTO was set
jobs:
- job: Deploy_Kusto
displayName: Provision a new Kusto database
condition: or(and(succeeded(), not(eq(variables['KUSTO_DB'], 'demo'))), and(succeeded(), eq(variables['KUSTO_DB'], 'demo'), variables['RUN_CREATE_DEMO_KUSTO']))
steps:
- task: AzureCLI@1
displayName: Provision Kusto database
inputs:
azureSubscription: $(ACR_PULL_SERVICE_CONNECTION)
scriptLocation: inlineScript
inlineScript: |
set -eux # fail on error
az kusto database create -g "$(RESOURCE_GROUP)" --cluster-name "$(KUSTO_NAME)" -n "$(KUSTO_DB)"
- job: Deploy_Elasticsearch
displayName: Deploy Elasticsearch
steps:
- template: infrastructure/setup-k8s-clients-template.yml
- bash: |
set -eux # fail on error
helm list
helm repo add elastic https://helm.elastic.co
helm upgrade --install elasticsearchqa elastic/elasticsearch \
--wait --timeout 45m \
--set image=docker.elastic.co/elasticsearch/elasticsearch \
--set imageTag=7.16.2 \
--set clusterName=elasticsearchqa \
--set replicas=1 \
--set persistence.enabled=false \
--set minimumMasterNodes=1 \
--set antiAffinity="soft" \
--set esJavaOpts="-Xmx512m -Xms512m" \
--set resources.requests.cpu="100m" \
--set resources.requests.memory="1024M" \
--set limits.cpu="1000m" \
--set limits.memory="2048M" \
--version 7.5.2
displayName: Deploy Elasticsearch
- job: Test
displayName: Run Tests
dependsOn:
- Deploy_Kusto
- Deploy_Elasticsearch
steps:
- template: infrastructure/setup-k8s-clients-template.yml
- task: AzureCLI@1
displayName: End-to-end test
inputs:
azureSubscription: $(ACR_PULL_SERVICE_CONNECTION)
scriptLocation: inlineScript
inlineScript: |
set -eu # fail on error
podName="e2e-test-$RANDOM"
KUSTO_URI=$(az kusto cluster show -g $(RESOURCE_GROUP) -n $(KUSTO_NAME) --query uri -o tsv)
AAD_TOKEN=$(az account get-access-token --resource "$KUSTO_URI" --query accessToken -o tsv)
kubectl run --restart=Never \
--image=$(ACR_NAME).azurecr.io/k2bridge-test:$SEMANTIC_VERSION \
--env=K2BRIDGE_URL=http://k2bridge:8080 \
--env=ELASTICSEARCH_URL=http://elasticsearchqa-master:9200 \
--env=KUSTO_URI=$KUSTO_URI \
--env=KUSTO_DB=$(KUSTO_DB) \
--env=AAD_TOKEN=$AAD_TOKEN \
"$podName"
set -x # enable verbose mode, without exposing $AAD_TOKEN
kubectl wait --timeout 45m --for=condition=ContainersReady pod "$podName"
# Read test output from FIFO within container
kubectl exec "$podName" cat /test-result-pipe > TestResult.xml
kubectl delete pod "$podName"
- task: PublishTestResults@2
displayName: Publish test results
condition: succeededOrFailed()
inputs:
testRunner: VSTest
testResultsFiles: 'TestResult.xml'
failTaskOnFailedTests: true
testRunTitle: 'E2E Tests'
- stage: cleanup_integration_tests
displayName: Cleanup Tests
dependsOn: integration_tests
# Do not delete AKS namespace:
# - if pipeline was canceled or failed before a Kubernetes namespace was generated
# - if deploying on main branch
# - if namespace was manually set with RUN_SET_NAMESPACE
condition: and(not(variables['RUN_SET_NAMESPACE']), not(variables['IS_DEMO_PIPELINE']))
jobs:
- job: Cleanup
steps:
- template: infrastructure/setup-k8s-clients-template.yml
- task: AzureCLI@1
displayName: Delete AKS namespace and Kusto database
condition: and(variables['KUBERNETES_NAMESPACE'], not(eq(variables['KUBERNETES_NAMESPACE'], 'main')))
inputs:
azureSubscription: $(ACR_PULL_SERVICE_CONNECTION)
scriptLocation: inlineScript
inlineScript: |
set -eux # fail on error
kubectl delete namespace "$KUBERNETES_NAMESPACE"
az kusto database delete -g "$(RESOURCE_GROUP)" --cluster-name "$(KUSTO_NAME)" -n "$(KUSTO_DB)" -y
- stage: release
displayName: Release Artifacts
condition: not(variables['IS_DEMO_PIPELINE'])
dependsOn: integration_tests
jobs:
- job: Promote
displayName: Promote Latest Image
condition: and(succeeded(), or(eq(variables['Build.SourceBranch'], 'refs/heads/main'), eq(variables['Build.SourceBranch'], 'refs/heads/kibana6.8'), variables['RUN_FLAG_PROMOTE']))
steps:
- task: AzureCLI@1
displayName: Tag Docker image as $(K2_TAG)
inputs:
azureSubscription: $(ACR_PUSH_SERVICE_CONNECTION)
scriptLocation: inlineScript
inlineScript: |
set -eux # fail on error
az configure --defaults acr="$ACR_NAME"
az acr login
docker pull "$ACR_NAME.azurecr.io/k2bridge:$SEMANTIC_VERSION"
docker tag \
"$ACR_NAME.azurecr.io/k2bridge:$SEMANTIC_VERSION" \
"$ACR_NAME.azurecr.io/k2bridge:$K2_TAG"
docker push "$ACR_NAME.azurecr.io/k2bridge:$K2_TAG"
- task: AzureCLI@1
displayName: Tag Docker image as $(K2_TAG) for MCR
inputs:
azureSubscription: $(ACR_PUSH_SERVICE_CONNECTION)
scriptLocation: inlineScript
inlineScript: |
set -eux # fail on error
az configure --defaults acr="$ACR_MCR_NAME"
az acr login
docker pull "$ACR_NAME.azurecr.io/k2bridge:$SEMANTIC_VERSION"
docker tag \
"$ACR_NAME.azurecr.io/k2bridge:$SEMANTIC_VERSION" \
"$ACR_MCR_NAME.azurecr.io/public/azuredataexplorer/k2bridge:$K2_TAG"
docker push "$ACR_MCR_NAME.azurecr.io/public/azuredataexplorer/k2bridge:$K2_TAG"
docker tag \
"$ACR_NAME.azurecr.io/k2bridge:$SEMANTIC_VERSION" \
"$ACR_MCR_NAME.azurecr.io/public/azuredataexplorer/k2bridge:$SEMANTIC_VERSION"
docker push "$ACR_MCR_NAME.azurecr.io/public/azuredataexplorer/k2bridge:$SEMANTIC_VERSION"