-
Notifications
You must be signed in to change notification settings - Fork 282
/
Copy pathbk-install-elastic-stack.ps1
271 lines (214 loc) · 10.6 KB
/
bk-install-elastic-stack.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
## Installs the Buildkite Agent, run from the CloudFormation template
Set-PSDebug -Trace 2
# Stop script execution when a non-terminating error occurs
$ErrorActionPreference = "Stop"
function on_error {
$errorLine=$_.InvocationInfo.ScriptLineNumber
$errorMessage=$_.Exception
$Token = (Invoke-WebRequest -UseBasicParsing -Method Put -Headers @{'X-aws-ec2-metadata-token-ttl-seconds' = '60'} http://169.254.169.254/latest/api/token).content
$instance_id=(Invoke-WebRequest -UseBasicParsing -Headers @{'X-aws-ec2-metadata-token' = $Token} http://169.254.169.254/latest/meta-data/instance-id).content
aws autoscaling set-instance-health `
--instance-id "$instance_id" `
--health-status Unhealthy
cfn-signal `
--region "$Env:AWS_REGION" `
--stack "$Env:BUILDKITE_STACK_NAME" `
--reason "Error on line ${errorLine}: $errorMessage" `
--resource "AgentAutoScaleGroup" `
--exit-code 1
}
trap {on_error}
$Token = (Invoke-WebRequest -UseBasicParsing -Method Put -Headers @{'X-aws-ec2-metadata-token-ttl-seconds' = '60'} http://169.254.169.254/latest/api/token).content
$Env:INSTANCE_ID=(Invoke-WebRequest -UseBasicParsing -Headers @{'X-aws-ec2-metadata-token' = $Token} http://169.254.169.254/latest/meta-data/instance-id).content
$DOCKER_VERSION=(docker --version).split(" ")[2].Replace(",","")
$PLUGINS_ENABLED=@()
If ($Env:SECRETS_PLUGIN_ENABLED -eq "true") { $PLUGINS_ENABLED += "secrets" }
If ($Env:ECR_PLUGIN_ENABLED -eq "true") { $PLUGINS_ENABLED += "ecr" }
If ($Env:DOCKER_LOGIN_PLUGIN_ENABLED -eq "true") { $PLUGINS_ENABLED += "docker-login" }
# cfn-env is sourced by the environment hook in builds
# There's a confusing situation here, because this is PowerShell, writing out a script which will be
# evaluated in Bash. So take note of the mixed export / $Env:.. idioms. This code mirrors the same
# behaviour of the script in /packer/linux/conf/bin/bk-install-elastic-stack.sh.
Set-Content -Path C:\buildkite-agent\cfn-env -Value @'
# The Buildkite agent sets a number of variables such as AWS_DEFAULT_REGION to fixed values which
# are determined at AMI-build-time. However, sometimes a user might want to override such variables
# using an env: block in their pipeline.yml. This little helper is sets the environment variables
# buildkite-agent and plugins expect, except if a user want to override them, for example to do a
# deployment to a region other than where the Buildkite agent lives.
function set_unless_present() {
local target=$1
local value=$2
if [[ -v "${target}" ]]; then
echo "^^^ +++"
echo "⚠️ ${target} already set, NOT overriding! (current value \"${!target}\" set by Buildkite step env configuration, or inherited from the buildkite-agent process environment)"
else
echo "export ${target}=\"${value}\""
declare -gx "${target}=${value}"
fi
}
function set_always() {
local target=$1
local value=$2
echo "export ${target}=\"${value}\""
declare -gx "${target}=${value}"
}
'@
Add-Content -Path C:\buildkite-agent\cfn-env -Value @"
set_always "BUILDKITE_AGENTS_PER_INSTANCE" "$Env:BUILDKITE_AGENTS_PER_INSTANCE"
# also set via nssm
set_always "BUILDKITE_TERMINATE_INSTANCE_AFTER_JOB" "$Env:BUILDKITE_TERMINATE_INSTANCE_AFTER_JOB"
set_always "BUILDKITE_ECR_POLICY" "$Env:BUILDKITE_ECR_POLICY"
set_always "BUILDKITE_SECRETS_BUCKET" "$Env:BUILDKITE_SECRETS_BUCKET"
set_always "BUILDKITE_SECRETS_BUCKET_REGION" "$Env:BUILDKITE_SECRETS_BUCKET_REGION"
set_always "BUILDKITE_STACK_NAME" "$Env:BUILDKITE_STACK_NAME"
set_always "BUILDKITE_STACK_VERSION" "$Env:BUILDKITE_STACK_VERSION"
set_always "BUILDKITE_DOCKER_EXPERIMENTAL" "$DOCKER_EXPERIMENTAL"
set_always "DOCKER_VERSION" "$DOCKER_VERSION"
set_always "PLUGINS_ENABLED" "$PLUGINS_ENABLED"
set_unless_present "AWS_DEFAULT_REGION" "$Env:AWS_REGION"
set_unless_present "AWS_REGION" "$Env:AWS_REGION"
set_unless_present "BUILDKITE_AGENT_ENDPOINT" "https://agent.buildkite.com/v3"
"@
If ($Env:BUILDKITE_AGENT_RELEASE -eq "edge") {
Write-Output "Downloading buildkite-agent edge..."
Invoke-WebRequest -OutFile C:\buildkite-agent\bin\buildkite-agent-edge.exe -Uri "https://download.buildkite.com/agent/experimental/latest/buildkite-agent-windows-amd64.exe"
buildkite-agent-edge.exe --version
If ($lastexitcode -ne 0) { Exit $lastexitcode }
}
Copy-Item -Path C:\buildkite-agent\bin\buildkite-agent-${Env:BUILDKITE_AGENT_RELEASE}.exe -Destination C:\buildkite-agent\bin\buildkite-agent.exe
$agent_metadata=@(
"queue=${Env:BUILDKITE_QUEUE}"
"docker=${DOCKER_VERSION}"
"stack=${Env:BUILDKITE_STACK_NAME}"
"buildkite-aws-stack=${Env:BUILDKITE_STACK_VERSION}"
)
If (Test-Path Env:BUILDKITE_AGENT_TAGS) {
$agent_metadata += $Env:BUILDKITE_AGENT_TAGS.split(",")
}
# Enable git-mirrors
If ($Env:BUILDKITE_AGENT_ENABLE_GIT_MIRRORS -eq "true") {
$Env:BUILDKITE_AGENT_GIT_MIRRORS_PATH = "C:\buildkite-agent\git-mirrors"
}
# Either you can have timestamp-lines xor ansi-timestamps.
# There's no technical reason you can't have both, its a pragmatic decision to
# simplify the avaliable parameters on the stack
If ($Env:BUILDKITE_AGENT_TIMESTAMP_LINES -eq "true") {
$Env:BUILDKITE_AGENT_NO_ANSI_TIMESTAMPS = "true"
} Else {
$Env:BUILDKITE_AGENT_NO_ANSI_TIMESTAMPS = "false"
}
# Get token from ssm param (if we have a path)
If ($null -ne $Env:BUILDKITE_AGENT_TOKEN_PATH -and $Env:BUILDKITE_AGENT_TOKEN_PATH -ne "") {
$Env:BUILDKITE_AGENT_TOKEN = $(aws ssm get-parameter --name $Env:BUILDKITE_AGENT_TOKEN_PATH --with-decryption --output text --query Parameter.Value --region $Env:AWS_REGION)
}
$OFS=","
Set-Content -Path C:\buildkite-agent\buildkite-agent.cfg -Value @"
name="${Env:BUILDKITE_STACK_NAME}-${Env:INSTANCE_ID}-%spawn"
token="${Env:BUILDKITE_AGENT_TOKEN}"
endpoint="${Env:BUILDKITE_AGENT_ENDPOINT}"
tags=$agent_metadata
tags-from-ec2-meta-data=true
no-ansi-timestamps=${Env:BUILDKITE_AGENT_NO_ANSI_TIMESTAMPS}
timestamp-lines=${Env:BUILDKITE_AGENT_TIMESTAMP_LINES}
hooks-path="C:\buildkite-agent\hooks"
build-path="C:\buildkite-agent\builds"
plugins-path="C:\buildkite-agent\plugins"
git-mirrors-path="${Env:BUILDKITE_AGENT_GIT_MIRRORS_PATH}"
experiment="${Env:BUILDKITE_AGENT_EXPERIMENTS}"
priority=%n
spawn=${Env:BUILDKITE_AGENTS_PER_INSTANCE}
no-color=true
shell=powershell
disconnect-after-idle-timeout=${Env:BUILDKITE_SCALE_IN_IDLE_PERIOD}
disconnect-after-job=${Env:BUILDKITE_TERMINATE_INSTANCE_AFTER_JOB}
tracing-backend=${Env:BUILDKITE_AGENT_TRACING_BACKEND}
signing-aws-kms-key=${Env:BUILDKITE_AGENT_SIGNING_KMS_KEY}
verification-failure-behavior=${Env:BUILDKITE_AGENT_SIGNING_FAILURE_BEHAVIOR}
"@
$OFS=" "
nssm set lifecycled AppEnvironmentExtra +AWS_REGION=$Env:AWS_REGION
nssm set lifecycled AppEnvironmentExtra +LIFECYCLED_HANDLER="C:\buildkite-agent\bin\stop-agent-gracefully.ps1"
Restart-Service lifecycled
# wait for docker to start
$next_wait_time=0
do {
Write-Output "Sleeping $next_wait_time seconds"
Start-Sleep -Seconds ($next_wait_time++)
docker ps
} until ($? -OR ($next_wait_time -eq 5))
docker ps
if (! $?) {
Write-Output "Failed to contact docker"
exit 1
}
# prevent password from being revealed by debug tracing
Set-PSDebug -Trace 0
Write-Output "Creating buildkite-agent user account in Administrators group"
$lowerChars = [char[]](97..122) # a-z
$upperChars = [char[]](65..90) # A-Z
$numbers = [char[]](48..57) # 0-9
$specialChars = [char[]](40, 41, 33, 64, 36, 37, 45, 61, 46, 63, 42, 59, 38) # ()!@$%-=.?*;&
$minPasswordLength = 32
$randomChars = @()
Do {
$randomChars += Get-Random -Count 1 -InputObject $lowerChars
$randomChars += Get-Random -Count 1 -InputObject $upperChars
$randomChars += Get-Random -Count 1 -InputObject $numbers
$randomChars += Get-Random -Count 1 -InputObject $specialChars
# randomize the order of the random characters
$randomChars = Get-Random -Count $randomChars.Length -InputObject $randomChars
} While ($randomChars.Length -lt $minPasswordLength)
$Password = -join $randomChars
$UserName = "buildkite-agent"
New-LocalUser -Name $UserName -PasswordNeverExpires -Password ($Password | ConvertTo-SecureString -AsPlainText -Force) | out-null
If ($Env:BUILDKITE_WINDOWS_ADMINISTRATOR -eq "true") {
Add-LocalGroupMember -Group "Administrators" -Member $UserName | out-null
}
If (![string]::IsNullOrEmpty($Env:BUILDKITE_ELASTIC_BOOTSTRAP_SCRIPT)) {
Write-Output "Running the elastic bootstrap script"
C:\buildkite-agent\bin\bk-fetch.ps1 -From "$Env:BUILDKITE_ELASTIC_BOOTSTRAP_SCRIPT" -To C:\buildkite-agent\elastic_bootstrap.ps1
C:\buildkite-agent\elastic_bootstrap.ps1
Remove-Item -Path C:\buildkite-agent\elastic_bootstrap.ps1
}
If (![string]::IsNullOrEmpty($Env:BUILDKITE_ENV_FILE_URL)) {
C:\buildkite-agent\bin\bk-fetch.ps1 -From "$Env:BUILDKITE_ENV_FILE_URL" -To C:\buildkite-agent\env
}
Write-Output "Starting the Buildkite Agent"
nssm install buildkite-agent C:\buildkite-agent\bin\buildkite-agent.exe start
If ($lastexitcode -ne 0) { Exit $lastexitcode }
nssm set buildkite-agent ObjectName .\$UserName $Password
If ($lastexitcode -ne 0) { Exit $lastexitcode }
nssm set buildkite-agent AppStdout C:\buildkite-agent\buildkite-agent.log
If ($lastexitcode -ne 0) { Exit $lastexitcode }
nssm set buildkite-agent AppStderr C:\buildkite-agent\buildkite-agent.log
If ($lastexitcode -ne 0) { Exit $lastexitcode }
nssm set buildkite-agent AppEnvironmentExtra +HOME=C:\buildkite-agent
If ((![string]::IsNullOrEmpty($Env:BUILDKITE_ENV_FILE_URL)) -And (Test-Path -Path C:\buildkite-agent\env -PathType leaf)) {
foreach ($var in Get-Content C:\buildkite-agent\env) {
nssm set buildkite-agent AppEnvironmentExtra "+$var"
If ($lastexitcode -ne 0) { Exit $lastexitcode }
}
}
# also set in cfn so it's show in job logs
nssm set buildkite-agent AppEnvironmentExtra +BUILDKITE_TERMINATE_INSTANCE_AFTER_JOB=$Env:BUILDKITE_TERMINATE_INSTANCE_AFTER_JOB
If ($lastexitcode -ne 0) { Exit $lastexitcode }
nssm set buildkite-agent AppExit Default Restart
If ($lastexitcode -ne 0) { Exit $lastexitcode }
nssm set buildkite-agent AppRestartDelay 10000
If ($lastexitcode -ne 0) { Exit $lastexitcode }
nssm set buildkite-agent AppEvents Exit/Post "powershell C:\buildkite-agent\bin\terminate-instance.ps1"
If ($lastexitcode -ne 0) { Exit $lastexitcode }
Restart-Service buildkite-agent
# renable debug tracing
Set-PSDebug -Trace 2
# let the stack know that this host has been initialized successfully
cfn-signal `
--region "$Env:AWS_REGION" `
--stack "$Env:BUILDKITE_STACK_NAME" `
--resource "AgentAutoScaleGroup" `
--exit-code 0 ; if (-not $?) {
# This will fail if the stack has already completed, for instance if there is a min size
# of 1 and this is the 2nd instance. This is ok, so we just ignore the erro
Write-Output "Signal failed"
}
Set-PSDebug -Off