Skip to content

Commit 2b4ad6f

Browse files
adferrandjsha
authored andcommitted
Configure AppVeyor to deliver native Windows containers of Pebble and Challtestsrv (#213)
Fixes #208. As a prerequisite, #212 must be merged before. This PR configure an AppVeyor pipeline to build and publish native Windows containers of Pebble and Challtestsrv on Docker Hub. This pipeline execute the same logic that his Linux counterpart on Travis CI: go build, go test using chisel2 and `acme` module from Certbot, then docker build + docker push. The CI part (build + test) is executed for every commit on every branch (including pull requests). The CD part (docker deploy) is executed only for tags pushed on Pebble repository. When triggered, the CD will publish two native Windows dockers on the `letsencrypt/pebble` and `letsencrypt/pebble-challtestsrv` Docker Hub projects: * the pinned release tag `X.X.X-nanoserver-sac2016`, that corresponds to the tag released on the GIT repository (Linux counterpart is `X.X.X`) * the rolling release tag `nanoserver-sac2016` that is the latest pinned release (Linux counterpart is `latest`) Aside AppVeyor configuration, I also modified chisel2 to be runnable on both Python 2.x and Python 3.x, because Certbot needs to run on Python 3.x on Windows. I also made some lights corrections on the Travis CI pipeline to make it live nicely with AppVeyor. Once this PR have been merged, one thing will need to be done from the Pebble maintainers that I cannot do myself. Indeed, two secured environment variables are required by this pipeline: `DOCKER_USER` and `DOCKER_PASS` that are the Docker Hub credentials used to push the image triggered by a GIT tag push, to trigger a release. These credentials need to be set with the login/password of a user authorized to push to `letsencrypt/pebble` and `letsencrypt/pebble-challtestsrv` Docker Hub projects. To do so, go to the AppVeyor Pebble project > Settings > Environment, and add the two environment variables (name + value) in the "Environment variables" section. **Be sure to check the lock "Toggle variable encryption" for each environment variable.** This can be done later after this PR have been merged, because as I said, the credentials are required only when a tag, and so a new release of Pebble, is pushed.
1 parent 1f851bf commit 2b4ad6f

File tree

9 files changed

+133
-32
lines changed

9 files changed

+133
-32
lines changed
Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
#!/usr/bin/env bash
22
set -e
33

4-
if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then
5-
echo "Publishing..."
6-
else
7-
echo "Skipping publishing"
8-
exit 0
9-
fi
10-
114
docker login -u "$DOCKER_USER" -p "$DOCKER_PASS"
125

136
BASE_NAMES=(pebble pebble-challtestsrv)
@@ -17,15 +10,15 @@ for BASE_NAME in "${BASE_NAMES[@]}"; do
1710
echo "Updating docker ${IMAGE_NAME} image..."
1811

1912
# create docker image
20-
docker build -t "${IMAGE_NAME}:temp" -f "docker/${BASE_NAME}/Dockerfile" .
13+
docker build -t "${IMAGE_NAME}:temp" -f "docker/${BASE_NAME}/linux.Dockerfile" .
2114

2215
# push images
2316
if [ -n "${TRAVIS_TAG}" ]; then
2417
echo "Try to publish image: ${IMAGE_NAME}:${TRAVIS_TAG}"
2518
docker tag "${IMAGE_NAME}:temp" "${IMAGE_NAME}:${TRAVIS_TAG}"
2619
docker push "${IMAGE_NAME}:${TRAVIS_TAG}"
2720

28-
echo "Try to publish image: ${IMAGE_NAME}:latest"
21+
echo "Try to publish rolling image: ${IMAGE_NAME}:latest"
2922
docker tag "${IMAGE_NAME}:${TRAVIS_TAG}" "${IMAGE_NAME}:latest"
3023
docker push "${IMAGE_NAME}:latest"
3124
fi

.ci/publish_windows.ps1

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
$ErrorActionPreference = 'Stop'
2+
if ($env:APPVEYOR_REPO_TAG -ne "true") {
3+
"Skipping publishing because this is not a tagged commit"
4+
} else {
5+
"Publishing the tagged commit..."
6+
7+
$ErrorActionPreference = 'SilentlyContinue'
8+
docker login -u="$env:DOCKER_USER" -p="$env:DOCKER_PASS"
9+
$ErrorActionPreference = 'Stop'
10+
11+
$basenames = @("pebble", "pebble-challtestsrv")
12+
foreach ($basename in $basenames) {
13+
$image_name = "letsencrypt/$basename"
14+
$tag = "$env:APPVEYOR_REPO_TAG_NAME-nanoserver-sac2016"
15+
16+
"Updating docker $basename image ..."
17+
18+
docker build -t="$image_name`:temp" -f="docker/$basename/windows.Dockerfile" .
19+
20+
"Try to publish image: $image_name`:$tag"
21+
docker tag "$image_name`:temp" "$image_name`:$tag"
22+
docker push "$image_name`:$tag"
23+
24+
"Try to publish rolling image: $image_name`:nanoserver-sac2016"
25+
docker tag "$image_name`:temp" "$image_name`:nanoserver-sac2016"
26+
docker push "$image_name`:nanoserver-sac2016"
27+
}
28+
}

.travis.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
language: go
22

33
sudo: true
4+
dist: xenial
45
addons:
56
hosts:
67
- example.letsencrypt.org
@@ -37,8 +38,6 @@ install:
3738

3839
before_script:
3940
- pebble &
40-
# Wait for pebble to come up
41-
- until </dev/tcp/localhost/14000 ; do sleep 0.1 ; done
4241

4342
script:
4443
- go mod download
@@ -54,7 +53,7 @@ script:
5453

5554
deploy:
5655
- provider: script
57-
script: bash .travis/publish.sh
56+
script: bash .ci/publish_linux.sh
5857
skip_cleanup: true
5958
on:
6059
repo: letsencrypt/pebble

appveyor.yml

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,38 @@
1-
build: off
1+
image: Visual Studio 2017
2+
3+
hosts:
4+
example.letsencrypt.org: 127.0.0.1
5+
elpmaxe.letsencrypt.org: 127.0.0.1
6+
7+
environment:
8+
GO111MODULE: on
9+
PATH: C:\Python37;C:\msys64\mingw64\bin;%USERPROFILE%\go\bin;%PATH%
10+
11+
install:
12+
- git clone --single-branch --depth=1 -b master https://github.com/certbot/certbot
13+
- cd certbot
14+
- python tools\venv3.py
15+
- venv3\Scripts\activate.bat
16+
- cd ..
17+
18+
before_build:
19+
# Install `golangci-lint` using go get (installer not available for Windows)
20+
- go get github.com/golangci/golangci-lint/cmd/[email protected]
21+
22+
build_script:
23+
- go install -v -mod=vendor ./...
24+
25+
after_build:
26+
- ps: $PebbleProcess = Start-Process pebble -PassThru
227

328
test_script:
4-
- ps: Write-Host "Hello, world!"
29+
- go mod download
30+
# Vet Go source code using the linter config (see .golang-ci.yml)
31+
- golangci-lint run
32+
# Run project unit tests (with the race detector enabled)
33+
- go test -mod=vendor -v -race ./...
34+
# Perform a test issuance with chisel2.py
35+
- cmd /c "set REQUESTS_CA_BUNDLE=./test/certs/pebble.minica.pem && python .\test\chisel2.py example.letsencrypt.org elpmaxe.letsencrypt.org"
36+
37+
deploy_script:
38+
- ps: .ci\publish_windows.ps1
File renamed without changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
FROM golang:1.11-nanoserver-sac2016 as builder
2+
3+
ENV CGO_ENABLED=0 GOFLAGS=-mod=vendor
4+
5+
WORKDIR /pebble-src
6+
COPY . .
7+
8+
RUN go install -v ./cmd/pebble-challtestsrv/...
9+
10+
## main
11+
FROM mcr.microsoft.com/windows/nanoserver:sac2016
12+
13+
COPY --from=builder /gopath/bin/pebble-challtestsrv.exe /gopath/bin/pebble-challtestsrv.exe
14+
15+
RUN powershell.exe -Command $path = $env:path + ';c:\gopath\bin'; Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\' -Name Path -Value $path
16+
17+
CMD [ "/pebble-challtestsrv" ]

docker/pebble/windows.Dockerfile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
FROM golang:1.11-nanoserver-sac2016 as builder
2+
3+
ENV CGO_ENABLED=0 GOFLAGS=-mod=vendor
4+
5+
WORKDIR /pebble-src
6+
COPY . .
7+
8+
RUN go install -v ./cmd/pebble/...
9+
10+
## main
11+
FROM mcr.microsoft.com/windows/nanoserver:sac2016
12+
13+
COPY --from=builder /gopath/bin/pebble.exe /gopath/bin/pebble.exe
14+
COPY --from=builder /pebble-src/test/ /test/
15+
16+
RUN powershell.exe -Command $path = $env:path + ';c:\gopath\bin'; Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\' -Name Path -Value $path
17+
18+
CMD [ "/pebble" ]

test/chisel2.py

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88
$ pip install -r requirements.txt
99
$ python chisel.py foo.com bar.com
1010
"""
11-
import json
11+
from __future__ import print_function
1212
import logging
1313
import os
14+
import ssl
1415
import sys
1516
import signal
1617
import threading
1718
import time
18-
import urllib2
19+
20+
import requests
1921

2022
from cryptography.hazmat.backends import default_backend
2123
from cryptography.hazmat.primitives.asymmetric import rsa
@@ -44,6 +46,16 @@
4446
SET_TXT = "http://localhost:8055/set-txt"
4547
CLEAR_TXT = "http://localhost:8055/clear-txt"
4648

49+
def wait_for_acme_server():
50+
"""Wait for directory URL set in the DIRECTORY env variable to respond"""
51+
while True:
52+
try:
53+
if requests.get(DIRECTORY).status_code == 200:
54+
return
55+
except requests.exceptions.ConnectionError:
56+
pass
57+
time.sleep(0.1)
58+
4759
def make_client(email=None):
4860
"""Build an acme.Client and register a new account with a random key."""
4961
key = josepy.JWKRSA(key=rsa.generate_private_key(65537, 2048, default_backend()))
@@ -120,18 +132,16 @@ def do_dns_challenges(client, authzs):
120132
name, value = (c.validation_domain_name(a.body.identifier.value),
121133
c.validation(client.net.key))
122134
cleanup_hosts.append(name)
123-
urllib2.urlopen(SET_TXT,
124-
data=json.dumps({
125-
"host": name + ".",
126-
"value": value,
127-
})).read()
135+
requests.post(SET_TXT, json={
136+
"host": name + ".",
137+
"value": value
138+
}).raise_for_status()
128139
client.answer_challenge(c, c.response(client.net.key))
129140
def cleanup():
130141
for host in cleanup_hosts:
131-
urllib2.urlopen(CLEAR_TXT,
132-
data=json.dumps({
133-
"host": host + ".",
134-
})).read()
142+
requests.post(CLEAR_TXT, json={
143+
"host": host + "."
144+
}).raise_for_status()
135145
return cleanup
136146

137147
def do_http_challenges(client, authzs):
@@ -153,10 +163,11 @@ def cleanup():
153163
# Loop until the HTTP01Server is ready.
154164
while True:
155165
try:
156-
urllib2.urlopen("http://localhost:%d" % port)
157-
break
158-
except urllib2.URLError:
159-
time.sleep(0.1)
166+
if requests.get("http://localhost:{0}".format(port)).status_code == 200:
167+
break
168+
except requests.exceptions.ConnectionError:
169+
pass
170+
time.sleep(0.1)
160171

161172
for chall_body in challs:
162173
client.answer_challenge(chall_body, chall_body.response(client.net.key))
@@ -191,10 +202,11 @@ def expect_problem(problem_type, func):
191202
signal.signal(signal.SIGINT, signal.SIG_DFL)
192203
domains = sys.argv[1:]
193204
if len(domains) == 0:
194-
print __doc__
205+
print(__doc__)
195206
sys.exit(0)
196207
try:
208+
wait_for_acme_server()
197209
auth_and_issue(domains)
198-
except messages.Error, e:
199-
print e
210+
except messages.Error as e:
211+
print(e)
200212
sys.exit(1)

0 commit comments

Comments
 (0)