Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
61ba4db
first pass at concourse CI
miguelsimon Jul 6, 2019
00c0a8e
bugfixes
miguelsimon Jul 6, 2019
d056382
getting there
miguelsimon Jul 6, 2019
184420a
saner approach
miguelsimon Jul 6, 2019
414fd78
add docs
miguelsimon Jul 6, 2019
9e87292
better docs
miguelsimon Jul 6, 2019
a60b52a
better docs
miguelsimon Jul 6, 2019
1b4007d
run on gce vm, https certificates via letsencrypt
miguelsimon Jul 7, 2019
c5cc58b
add remote login to Makefile, test PR
miguelsimon Jul 7, 2019
638ed33
add docs
miguelsimon Jul 7, 2019
69411cf
github-pr-resource doesn't pull git lfs by default, try to pull manually
miguelsimon Jul 8, 2019
8a701dd
more realistic pipeline
miguelsimon Jul 9, 2019
c596ad3
bugfixes
miguelsimon Jul 9, 2019
8ff08be
testing stuff
miguelsimon Jul 9, 2019
c0a6c5b
remove miniconda
miguelsimon Jul 9, 2019
cdf407b
bugfix
miguelsimon Jul 10, 2019
e58365f
fix bug in build pipeline, add periodic trigger, remove redundant inp…
Jul 20, 2019
3f1c4e8
add real credential ops, cluster-tests.sh starts doing stuff that loo…
Jul 24, 2019
e3303a4
vars in pipeline should be params, add explicit dependencies to Makefile
Jul 25, 2019
fe07311
specify tests and compile them to artifact using a simple python dsl
Jul 26, 2019
5065335
better docs, add todo link
Jul 26, 2019
9ba5dcc
create pbs job chain, initially serially dependent
Jul 26, 2019
74bb3dd
getting there
Jul 26, 2019
029d244
working end to end when submitting via fly
Jul 27, 2019
7d1e6e2
actually run the job on the cluster
Jul 27, 2019
7c17294
add all_ok job to collect completion status
Jul 27, 2019
812f6c5
clean up
Jul 27, 2019
3b2fabe
refactor, add Pmap comparison jobs
Jul 28, 2019
7da1243
output directories as comparisons, start on assembling outputs into a…
Aug 1, 2019
95a1380
generate bigger jobs, assemble reports
Aug 3, 2019
a16bff5
generate acceptable html
Aug 3, 2019
72a7a5c
install requirements
Aug 3, 2019
a3809b7
collect outputs, html report in preparation for rsyncing them somewhere
Aug 4, 2019
a880459
upload results to netlify until ific server is ready
Aug 31, 2019
67e5697
move generated html file after generating it to avoid treating it as …
Sep 1, 2019
b248733
explain role of .gitignored credentials folder
Sep 14, 2019
87af245
simpler, more straightforward approach
Sep 14, 2019
1aaead0
remove docs for code generation
Sep 15, 2019
47931e3
much better docs
Sep 15, 2019
eaf9951
Merge branch 'simplified-cluster-tests' into concourse-ci
Sep 15, 2019
fdcef31
pr-pipeline now calls simple-cluster-tests.yml
Sep 15, 2019
97cf36a
simplify
Sep 15, 2019
f8ac674
simplify city config management
Sep 15, 2019
9a47673
doc formatting
Sep 15, 2019
5ffd50d
update Makefile, readme to reflect move to https://gpu1next.ific.uv.es
Sep 23, 2019
2ac6ab7
rsync outputs to http server
Sep 23, 2019
b0902f9
Makefile bugfix
Oct 14, 2019
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
5 changes: 5 additions & 0 deletions concourse-ci/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
._*
.mypy_cache
credentials
env
env_ok
7 changes: 7 additions & 0 deletions concourse-ci/.isort.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

[settings]
multi_line_output=3
include_trailing_comma=True
force_grid_wrap=0
use_parentheses=True
line_length=88
75 changes: 75 additions & 0 deletions concourse-ci/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
py_dirs = assemble_jobs
py_files = $(wildcard assemble_jobs/*.py)


.PHONY: fmt
fmt: env_ok
env/bin/isort -sp .isort.cfg $(py_files)
env/bin/black $(py_files)


.PHONY: test
test: check
env/bin/python -m unittest discover $(py_dirs) -p "*.py" -v


.PHONY: check
check: env_ok
env/bin/python -m mypy \
--check-untyped-defs \
--ignore-missing-imports \
assemble_jobs
env/bin/python -m flake8 --select F $(py_dirs)
env/bin/isort -sp .isort.cfg --check $(py_files)
env/bin/black --check $(py_files)


env_ok: requirements.txt
rm -rf env env_ok
python3 -m venv env
env/bin/pip install -r requirements.txt
touch env_ok


.PHONY: clean
clean:
rm -rf env env_ok


.PHONY: fly_remote_login
fly_remote_login:
fly -t remote login \
-c https://gpu1next.ific.uv.es

.PHONY: fly_execute
fly_execute:
fly -t local execute \
--include-ignored \
--input IC=../ \
--config run-tests.yml

.PHONY: set_local_pr_pipeline
set_local_pr_pipeline: credentials/github_access_token credentials/key_concourse
fly -t local set-pipeline \
--pipeline IC-pull-request \
--config pr-pipeline.yml \
-v "SSH_PRIVATE_KEY=$$(cat credentials/key_concourse)" \
-v github_access_token=$$(cat credentials/github_access_token)

.PHONY: set_pr_pipeline
set_pr_pipeline: credentials/github_access_token credentials/key_concourse
fly -t remote set-pipeline \
--pipeline IC-pull-request \
--config pr-pipeline.yml \
-v "SSH_PRIVATE_KEY=$$(cat credentials/key_concourse)" \
-v github_access_token=$$(cat credentials/github_access_token)


.PHONY: launch_prod_concourse
launch_prod_concourse: credentials/CONCOURSE_TEST_PASSWORD
docker-compose -f prod-docker-compose.yml down -v --remove-orphans
docker-compose -f prod-docker-compose.yml run make_dummy_certs
CONCOURSE_TEST_PASSWORD=$$(credentials/CONCOURSE_TEST_PASSWORD) docker-compose -f prod-docker-compose.yml up -d nginx
docker-compose -f prod-docker-compose.yml run erase_certs
docker-compose -f prod-docker-compose.yml run create_certs
docker-compose -f prod-docker-compose.yml exec nginx nginx -s reload
128 changes: 128 additions & 0 deletions concourse-ci/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
* [Overview](#overview)
* [Usage](#usage)
* [Concourse server setup](#concourse-server-setup)
* [Online demo version](#online-demo-version)
* [Walkthrough for executing concourse locally](#walkthrough-for-executing-concourse-locally)
* [Running on a server](#running-on-a-server)
* [Developing cluster tests](#developing-cluster-tests)
* [running the test script via the command line](#running-the-test-script-via-the-command-line)
* [setting the concourse pipeline](#setting-the-concourse-pipeline)
* [running locally](#running-locally)
* [running in production](#running-in-production)
* [updating the concourse pipeline](#updating-the-concourse-pipeline)

# Overview

**currently moving to [https://gpu1next.ific.uv.es](https://gpu1next.ific.uv.es), still need to sync docker compose file that @jocarbur is working on into this repo**

Proof of concept is up at [https://gpu1next.ific.uv.es](https://gpu1next.ific.uv.es). It's password protected, ask @mmkekic for access if you want to poke around, authorization for the real version would be mediated via oauth by membership in the [nextic github organization](https://github.com/nextic).

This sets up an example CI pipeline for the invisible cities project. The idea is that, whenever a pull request to a [protected branch](https://help.github.com/en/articles/about-protected-branches) happens:

1. the CI runs the unit tests (currently what your travis CI runs) and errors out if they fail
2. if unit tests pass, the CI submits a job to the majorana cluster that performs sanity checks; a report is generated and put somewhere (an ific http server)
3. If the sanity checks also pass, the CI marks the PR as approved in github and the merge can now be performed.

I set this up using [concourse](https://concourse-ci.org/) because:
* as a side effect of dragging my current company's devs into the 19th century, I can now set up continuous integration environments based on concourse in my sleep
* concourse is awesome; it does have a learning curve (as do all CI tools) but it's very flexible and forces you to decouple stuff in a way that makes deploying in cloud environments easy.

# Usage

## Concourse server setup

### Online demo version

The concourse interface is up at [https://gpu1next.ific.uv.es/](https://gpu1next.ific.uv.es/).

The easiest way to understand what's going on is just to open a test PR to the master branch in the [https://github.com/miguelsimon/IC](https://github.com/miguelsimon/IC) repo, you should see:
* Merges are disallowed until the build passes
* At least 1 review is required.

### Walkthrough for executing concourse locally

#### Prerequisites

* [docker-compose](https://www.digitalocean.com/community/tutorials/how-to-install-docker-compose-on-ubuntu-18-04)
* make ie if you're on some unix flavor you're fine
* required credentials are text files that belong in a `credentials` directory which is .gitignored to avoid committing credentials; for a local deploy, these are:
* `credentials/github_access_token` a github access token to write PR status back to github
* `credentials/key_concourse` is an ssh private key which lets the `icdev` user access the majorana cluster

These steps must be executed from within this directory:

1. launch the local concourse instance:
`docker-compose up -d`
2. navigate to [http://localhost:8080](http://localhost:8080) and login with username: test password: test
3. download the [fly cli](https://concourse-ci.org/fly.html) from your local concourse installation
4. log in to concourse via the command line:
`fly -t local login -c http://localhost:8080`
5. push the pr pipeline (you'll need appropriate credentials)
`make set_local_pr_pipeline`

Voilà, you can now unpause the pipeline.

### Running on a server

I've set it up at [https://gpu1next.ific.uv.es/](https://gpu1next.ific.uv.es/).

Doing that involves quite a bit of onerous and annoying details, dealing with dns, certificates, hosting etc. These are just annoying if you know what you're doing but require loads of time to learn all that boring trivia if you don't. I can talk to you guys and help you set it up in your context.

The set up is mostly sane but the top priority was getting it working in < 3 hours so I'm mainly using stuff I'm comfortable with.

Birds-eye overview of the current setup:
* The deploy is specified in docker compose; I'm running postgres, concourse, an nginx frontend and [certbot](https://certbot.eff.org/) to set up free certificates via letsencrypt.
* I'm running it on a trial google compute engine VM (google cloud is a very sane cloud provider when compared to others *cough* amazon *cough*)
* access control is via username - password now, we'd delegate access control to github via oauth to avoid operational hassles

## Developing cluster tests

**Code in the [assemble_jobs](assemble_jobs) directory is meant for a later stage and should't be used for now**

An ssh script called [simple-cluster-tests.sh](simple-cluster-tests.sh) is responsible for:
1. copying the required context to the majorana cluster
* the PR code for the IC repo
* the master code for the IC repo
* the scripts that submit the jobs, found in the [jobs](jobs) directory
2. submitting the jobs on majorana and waiting for them to complete
3. fetching the job outputs from majorana via rsync
4. **Not yet implemented** Running the comparison functions on the outputs and generating a report.

This script is meant to be called from a concourse pipeline.

### running the test script via the command line

To test the `simple-cluster-tests.sh` script it's convenient to launch it using local content; you can do this via fly execute (if you've got the proper credentials); the following example uses an IC_master checkout to populate both the IC and IC_master inputs to the script, and leaves results in the `outputs` directory:

```
mkdir outputs

SSH_PRIVATE_KEY=$(cat credentials/key_concourse) \
fly -t remote execute \
--include-ignored \
--input IC=~/IC_master \
--input IC_master=~/IC_master \
--input IC_operations=../ \
--output outputs=./outputs \
--config simple-cluster-tests.yml
```

### setting the concourse pipeline

The [pr-pipeline.yml](pr-pipeline.yml) script contains the pipeline definition needed to tell the concourse server how and when to invoke the cluster tests script.

The [Makefile](Makefile) contains the commands and required files needed to configure the pipelines on the concourse server.

#### running locally

`make set_local_pr_pipeline` will upload the pipeline to the concourse server running on localhost.

#### running in production

`make set_pr_pipeline` will upload the pipeline to the production server.

### updating the concourse pipeline

If you make changes to the pipeline itself (eg. modifying the pr-pipeline.yml or cluster-tests.yml files) `make set_pr_pipeline` will apply those changes.

If you make changes to the code, committing and pushing your changes is enough; the concourse pipeline will detect the changes in the code and automatically rebuild the test image.
Empty file.
32 changes: 32 additions & 0 deletions concourse-ci/assemble_jobs/city_job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from typing import NamedTuple

CITY_JOB_TEMPLATE = """#!/bin/bash
source {bashrc}
source {conda_sh}
conda activate {conda_activate}
export LD_LIBRARY_PATH="{conda_lib}:$LD_LIBRARY_PATH"
export ICTDIR={ictdir}
export ICDIR="$ICTDIR/invisible_cities"
export PATH="$ICTDIR/bin:$PATH"
export PYTHONPATH="$ICTDIR:$PYTHONPATH"

city {city} \\
-i {input_path} \\
-o {output_path} \\
{conf_path}
"""


class CityJob(NamedTuple):
city: str
input_path: str
output_path: str
conf_path: str
ictdir: str
bashrc: str
conda_sh: str
conda_activate: str
conda_lib: str

def to_sh(self) -> str:
return CITY_JOB_TEMPLATE.format(**self._asdict())
35 changes: 35 additions & 0 deletions concourse-ci/assemble_jobs/compare_kdst.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from typing import NamedTuple

COMPARE_KDST_JOB_TEMPLATE = """#!/bin/bash
source {bashrc}
source {conda_sh}
conda activate {conda_activate}
export LD_LIBRARY_PATH="{conda_lib}:$LD_LIBRARY_PATH"
export ICTDIR={ictdir}
export ICDIR="$ICTDIR/invisible_cities"
export PATH="$ICTDIR/bin:$PATH"
export PYTHONPATH="$ICTDIR:$PYTHONPATH"

mkdir {output_path}

h5diff -c {master_path} {pr_path} > {output_path}/h5diff.txt
if [ $? -eq 0 ]; then
echo "ok" > {output_path}/status
else
echo "DIFFERENCES" > {output_path}/status
fi
"""


class CompareKdstJob(NamedTuple):
master_path: str
pr_path: str
output_path: str
ictdir: str
bashrc: str
conda_sh: str
conda_activate: str
conda_lib: str

def to_sh(self) -> str:
return COMPARE_KDST_JOB_TEMPLATE.format(**self._asdict())
35 changes: 35 additions & 0 deletions concourse-ci/assemble_jobs/compare_pmap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from typing import NamedTuple

COMPARE_PMAP_JOB_TEMPLATE = """#!/bin/bash
source {bashrc}
source {conda_sh}
conda activate {conda_activate}
export LD_LIBRARY_PATH="{conda_lib}:$LD_LIBRARY_PATH"
export ICTDIR={ictdir}
export ICDIR="$ICTDIR/invisible_cities"
export PATH="$ICTDIR/bin:$PATH"
export PYTHONPATH="$ICTDIR:$PYTHONPATH"

mkdir {output_path}

h5diff -c {master_path} {pr_path} > {output_path}/h5diff.txt
if [ $? -eq 0 ]; then
echo "ok" > {output_path}/status
else
echo "DIFFERENCES" > {output_path}/status
fi
"""


class ComparePmapJob(NamedTuple):
master_path: str
pr_path: str
output_path: str
ictdir: str
bashrc: str
conda_sh: str
conda_activate: str
conda_lib: str

def to_sh(self) -> str:
return COMPARE_PMAP_JOB_TEMPLATE.format(**self._asdict())
Loading