Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up agents #11

Closed
wants to merge 10 commits into from
Closed
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
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,11 @@ WORKDIR /go/src/${MODULE_NAME}
ENV GOPATH /go
ENV PATH /usr/local/go/bin:$GOPATH/bin:$PATH
RUN /bin/bash -c "curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh"
RUN curl -sL https://aka.ms/InstallAzureCLIDeb | bash

# Set work directory.
RUN mkdir -p /go/src/${MODULE_NAME}
WORKDIR /go/src/${MODULE_NAME}
COPY . /go/src/${MODULE_NAME}

RUN ["bundle", "install", "--gemfile", "./Gemfile"]
11 changes: 6 additions & 5 deletions docker/linux/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
FROM ubuntu:16.04
FROM ubuntu:18.04

# To make it easier for build and release pipelines to run apt-get,
# configure apt to not require confirmation (assume the -y argument by default)
ENV DEBIAN_FRONTEND=noninteractive
RUN echo "APT::Get::Assume-Yes \"true\";" > /etc/apt/apt.conf.d/90assumeyes

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
&& apt-get install -y --no-install-recommends \
ca-certificates \
curl \
jq \
git \
iputils-ping \
libcurl3 \
libicu55 \
libcurl4 \
libicu60 \
libunwind8 \
netcat
netcat \
libssl1.0

WORKDIR /azp

Expand Down
19 changes: 10 additions & 9 deletions docker/linux/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,6 @@ curl -LsS $AZP_AGENTPACKAGE_URL | tar -xz & wait $!

source ./env.sh

trap 'cleanup; exit 130' INT
trap 'cleanup; exit 143' TERM

print_header "3. Configuring Azure Pipelines agent..."

./config.sh --unattended \
Expand All @@ -88,11 +85,15 @@ print_header "3. Configuring Azure Pipelines agent..."
--replace \
--acceptTeeEula & wait $!

# remove the administrative token before accepting work
rm $AZP_TOKEN_FILE

print_header "4. Running Azure Pipelines agent..."

# `exec` the node runtime so it's aware of TERM and INT signals
# AgentService.js understands how to handle agent self-update and restart
exec ./externals/node/bin/node ./bin/AgentService.js interactive
trap 'cleanup; exit 130' SIGINT
trap 'cleanup; exit 143' SIGTERM
trap 'cleanup; exit 144' SIGQUIT

# To be aware of TERM and INT signals call run.sh
# Running it with the --once flag at the end will shut down the agent after the build is executed
./run.sh $* &
wait $!

cleanup
3 changes: 0 additions & 3 deletions docker/windows/start.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,6 @@ if (-not (Test-Path Env:AZP_URL)) {
--work "$(if (Test-Path Env:AZP_WORK) { ${Env:AZP_WORK} } else { '_work' })" `
--replace

# remove the administrative token before accepting work
Remove-Item $Env:AZP_TOKEN_FILE

jcorioland marked this conversation as resolved.
Show resolved Hide resolved
Write-Host "4. Running Azure Pipelines agent..." -ForegroundColor Cyan

.\run.cmd
Expand Down
123 changes: 110 additions & 13 deletions test/azure_devops_agent_aci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,12 @@ func TestDeployAzureDevOpsLinuxAgentsInVirtualNetwork(t *testing.T) {
t.Fatalf("Cannot create Azure DevOps agent pool for the test: %v", err)
}

// At the end of the test, clean up any resources that were created
defer test_structure.RunTestStage(t, "teardown", func() {
terraformOptions := test_structure.LoadTerraformOptions(t, fixtureFolder)
terraform.Destroy(t, terraformOptions)
})

// Deploy the example
test_structure.RunTestStage(t, "setup", func() {
terraformOptions := configureTerraformOptions(t, fixtureFolder)
Expand Down Expand Up @@ -226,12 +232,6 @@ func TestDeployAzureDevOpsLinuxAgentsInVirtualNetwork(t *testing.T) {
t.Fatalf("Test failed. Expected number of agents is %d. Actual number of agents is %d", expectedAgentsCount, actualAgentsCount)
}
})

// At the end of the test, clean up any resources that were created
test_structure.RunTestStage(t, "teardown", func() {
terraformOptions := test_structure.LoadTerraformOptions(t, fixtureFolder)
terraform.Destroy(t, terraformOptions)
})
}

// This function tests the deployment of Azure DevOps Linux and Windows agents
Expand Down Expand Up @@ -272,6 +272,12 @@ func TestDeployAzureDevOpsLinuxAndWindowsAgents(t *testing.T) {
t.Fatalf("Cannot create Azure DevOps Linux agent pool for the test: %v", err)
}

// At the end of the test, clean up any resources that were created
defer test_structure.RunTestStage(t, "teardown", func() {
terraformOptions := test_structure.LoadTerraformOptions(t, fixtureFolder)
terraform.Destroy(t, terraformOptions)
})

// Deploy the example
test_structure.RunTestStage(t, "setup", func() {
terraformOptions := configureTerraformOptions(t, fixtureFolder)
Expand Down Expand Up @@ -312,12 +318,6 @@ func TestDeployAzureDevOpsLinuxAndWindowsAgents(t *testing.T) {
t.Fatalf("Test failed. Expected number of Windows agents is %d. Actual number of Windows agents is %d", expectedWindowsAgentsCount, actualWindowsAgentsCount)
}
})

// At the end of the test, clean up any resources that were created
test_structure.RunTestStage(t, "teardown", func() {
terraformOptions := test_structure.LoadTerraformOptions(t, fixtureFolder)
terraform.Destroy(t, terraformOptions)
})
}

// This function tests the deployment of Azure DevOps Linux agents into an existing resource group
Expand Down Expand Up @@ -347,6 +347,12 @@ func TestDeployAzureDevOpsLinuxAgentsIntoExistingResourceGroup(t *testing.T) {
t.Fatalf("Cannot create Azure DevOps agent pool for the test: %v", err)
}

// At the end of the test, clean up any resources that were created
defer test_structure.RunTestStage(t, "teardown", func() {
terraformOptions := test_structure.LoadTerraformOptions(t, fixtureFolder)
terraform.Destroy(t, terraformOptions)
})

// Deploy the example
test_structure.RunTestStage(t, "setup", func() {
terraformOptions := configureTerraformOptions(t, fixtureFolder)
Expand Down Expand Up @@ -375,11 +381,102 @@ func TestDeployAzureDevOpsLinuxAgentsIntoExistingResourceGroup(t *testing.T) {
t.Fatalf("Test failed. Expected number of agents is %d. Actual number of agents is %d", expectedAgentsCount, actualAgentsCount)
}
})
}

// This function tests the deployment of Azure DevOps Linux
func TestAgentCleanUp(t *testing.T) {
t.Parallel()

fixtureFolder := "./fixture/agent-pool-cleanup"

// generate a random suffix for the test
rand.Seed(time.Now().UnixNano())
randomInt := rand.Intn(9999)
randomSuffix := strconv.Itoa(randomInt)

// create random agent pool name
testPoolName := fmt.Sprintf("e2e-agents-%s", randomSuffix)
os.Setenv("TF_VAR_linux_azure_devops_pool_name", testPoolName)
os.Setenv("TF_VAR_windows_azure_devops_pool_name", testPoolName)

devopsOrganizationName := os.Getenv("TF_VAR_azure_devops_org_name")
devopsPersonalAccessToken := os.Getenv("TF_VAR_azure_devops_personal_access_token")
devopsOrganizationURL := fmt.Sprintf("https://dev.azure.com/%s", devopsOrganizationName)

// create the Linux agents pool
defer deleteAzureDevOpsAgentTestPool(testPoolName, devopsOrganizationURL, devopsPersonalAccessToken)
err := createAzureDevOpsAgentTestPool(testPoolName, devopsOrganizationURL, devopsPersonalAccessToken)
if err != nil {
t.Fatalf("Cannot create Azure DevOps Linux agent pool for the test: %v", err)
}

// At the end of the test, clean up any resources that were created
test_structure.RunTestStage(t, "teardown", func() {
defer test_structure.RunTestStage(t, "teardown", func() {
jcorioland marked this conversation as resolved.
Show resolved Hide resolved
terraformOptions := test_structure.LoadTerraformOptions(t, fixtureFolder)
terraform.Destroy(t, terraformOptions)
})

// Deploy the example
test_structure.RunTestStage(t, "setup", func() {
terraformOptions := configureTerraformOptions(t, fixtureFolder)

// Save the options so later test stages can use them
test_structure.SaveTerraformOptions(t, fixtureFolder, terraformOptions)

// This will init and apply the resources and fail the test if there are any errors
terraform.InitAndApply(t, terraformOptions)
})

// Check whether the length of output meets the requirement
test_structure.RunTestStage(t, "validate", func() {
// add wait time for ACI to get connectivity
time.Sleep(45 * time.Second)

// ensure deployment was successful for agents
expectedAgentsCount := 2
actualAgentsCount, err := getAgentsCount(testPoolName, devopsOrganizationURL, devopsPersonalAccessToken)

if err != nil {
t.Fatalf("Cannot retrieve the number of agents that were deployed: %v", err)
}

if expectedAgentsCount != actualAgentsCount {
t.Fatalf("Test failed. Expected number of agents is %d. Actual number of agents is %d", expectedAgentsCount, actualAgentsCount)
}

// Update agent count
terraformOptions := test_structure.LoadTerraformOptions(t, fixtureFolder)
terraformOptions.Vars["agents_count"] = "1"
terraform.Apply(t, terraformOptions)
// add wait time for ACI to update
time.Sleep(45 * time.Second)

expectedAgentsCount = 1
actualAgentsCount, err = getAgentsCount(testPoolName, devopsOrganizationURL, devopsPersonalAccessToken)

if err != nil {
t.Fatalf("Cannot retrieve the number of agents that were deployed: %v", err)
}

if expectedAgentsCount != actualAgentsCount {
t.Fatalf("Test failed. Expected number of agents is %d. Actual number of agents is %d", expectedAgentsCount, actualAgentsCount)
}

jcorioland marked this conversation as resolved.
Show resolved Hide resolved
// destroying infrastructure:
terraformOptions = test_structure.LoadTerraformOptions(t, fixtureFolder)
terraform.Destroy(t, terraformOptions)

// after clean up, new expected count = 0
expectedAgentsCount = 0
actualAgentsCount, err = getAgentsCount(testPoolName, devopsOrganizationURL, devopsPersonalAccessToken)

if err != nil {
t.Fatalf("Cannot retrieve the number of agents that were deployed: %v", err)
}

if expectedAgentsCount != actualAgentsCount {
t.Fatalf("Test failed. Expected number of agents is %d. Actual number of agents is %d", expectedAgentsCount, actualAgentsCount)
}
})
}

Expand Down
20 changes: 20 additions & 0 deletions test/fixture/agent-pool-cleanup/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash
set -e

cd ../../../docker

az login --service-principal --username "$ARM_CLIENT_ID" --password "$ARM_CLIENT_SECRET" --tenant "microsoft.onmicrosoft.com"

while true;
do
echo "checking for acr..."
sleep 1
created=$(az acr check-name -n $acr_name --query [nameAvailable] --output tsv)
[[ $created == 'false' ]] && break
done

# add wait time for role propagation
sleep 60

az configure --defaults acr=$acr_name
az acr build -t "aci-devops-agent:0.2-linux" linux >> acr.txt
73 changes: 73 additions & 0 deletions test/fixture/agent-pool-cleanup/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
data "azurerm_client_config" "current" {}

resource "random_string" "suffix" {
length = 4
special = false
upper = false
keepers = {
id = data.azurerm_client_config.current.object_id
}
}

resource "azurerm_resource_group" "rg" {
name = "rg-terraform-azure-devops-agents-e2e-tests-${random_string.suffix.result}"
location = var.location
}

resource "azurerm_container_registry" "acr" {
name = "acr${random_string.suffix.result}"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
sku = "Basic"
admin_enabled = true
}

resource "azurerm_role_assignment" "acr_push" {
scope = azurerm_container_registry.acr.id
role_definition_name = "AcrPush"
principal_id = data.azurerm_client_config.current.object_id
}

resource "null_resource" "build" {
provisioner "local-exec" {
command = "./build.sh"

environment = {
acr_name = azurerm_container_registry.acr.name
}
working_dir = "${path.module}/"
}

depends_on = [azurerm_role_assignment.acr_push]
}


module "aci-devops-agent" {
source = "../../../"
enable_vnet_integration = false
create_resource_group = false

linux_agents_configuration = {
agent_name_prefix = "linux-agent-${random_string.suffix.result}"
count = var.agents_count,
docker_image = "${azurerm_container_registry.acr.login_server}/${var.linux_agent_docker_image}"
docker_tag = var.linux_agent_docker_tag
agent_pool_name = var.linux_azure_devops_pool_name
cpu = 1
memory = 4
user_assigned_identity_ids = []
use_system_assigned_identity = false
}

image_registry_credential = {
username = azurerm_container_registry.acr.admin_username
password = azurerm_container_registry.acr.admin_password
server = azurerm_container_registry.acr.login_server
}

resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
azure_devops_org_name = var.azure_devops_org_name
azure_devops_personal_access_token = var.azure_devops_personal_access_token
depends_on = [null_resource.build]
}
12 changes: 12 additions & 0 deletions test/fixture/agent-pool-cleanup/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.0"
}
}
}

provider "azurerm" {
features {}
}
Loading