shipitron 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.dockerignore +4 -0
- data/.gitignore +4 -0
- data/.rspec +3 -0
- data/Dockerfile +60 -0
- data/Dockerfile.release +49 -0
- data/README.md +44 -10
- data/build_dev.sh +39 -0
- data/exe/shipitron +5 -0
- data/lib/shipitron/cli.rb +132 -0
- data/lib/shipitron/client/bootstrap_application.rb +35 -0
- data/lib/shipitron/client/create_ecs_services.rb +81 -0
- data/lib/shipitron/client/deploy_application.rb +34 -0
- data/lib/shipitron/client/ensure_deploy_not_running.rb +54 -0
- data/lib/shipitron/client/load_application_config.rb +41 -0
- data/lib/shipitron/client/load_templates.rb +45 -0
- data/lib/shipitron/client/register_ecs_task_definitions.rb +75 -0
- data/lib/shipitron/client/run_ecs_tasks.rb +141 -0
- data/lib/shipitron/consul_keys.rb +34 -0
- data/lib/shipitron/consul_lock.rb +28 -0
- data/lib/shipitron/docker_image.rb +23 -0
- data/lib/shipitron/ecs_client.rb +22 -0
- data/lib/shipitron/ecs_task_def.rb +17 -0
- data/lib/shipitron/fetch_bucket.rb +26 -0
- data/lib/shipitron/logger.rb +33 -0
- data/lib/shipitron/mustache_yaml_parser.rb +22 -0
- data/lib/shipitron/parse_templates.rb +32 -0
- data/lib/shipitron/post_build.rb +32 -0
- data/lib/shipitron/server/deploy_application.rb +75 -0
- data/lib/shipitron/server/docker/build_image.rb +25 -0
- data/lib/shipitron/server/docker/configure.rb +35 -0
- data/lib/shipitron/server/docker/push_image.rb +37 -0
- data/lib/shipitron/server/docker/run_build_script.rb +53 -0
- data/lib/shipitron/server/download_build_cache.rb +44 -0
- data/lib/shipitron/server/ecs_task_defs/map_parsed_templates.rb +31 -0
- data/lib/shipitron/server/ecs_task_defs/update_from_params.rb +42 -0
- data/lib/shipitron/server/ecs_task_defs/update_in_place.rb +68 -0
- data/lib/shipitron/server/git/clone_local_copy.rb +46 -0
- data/lib/shipitron/server/git/configure.rb +40 -0
- data/lib/shipitron/server/git/download_cache.rb +56 -0
- data/lib/shipitron/server/git/pull_repo.rb +30 -0
- data/lib/shipitron/server/git/update_cache.rb +52 -0
- data/lib/shipitron/server/git/upload_cache.rb +61 -0
- data/lib/shipitron/server/run_post_build.rb +79 -0
- data/lib/shipitron/server/transform_cli_args.rb +85 -0
- data/lib/shipitron/server/update_ecs_services.rb +105 -0
- data/lib/shipitron/server/update_ecs_task_definitions.rb +47 -0
- data/lib/shipitron/server/upload_build_cache.rb +43 -0
- data/lib/shipitron/smash.rb +10 -0
- data/lib/shipitron/version.rb +1 -1
- data/lib/shipitron.rb +38 -0
- data/scripts/docker-entrypoint.sh +18 -0
- data/scripts/fetch-bundler-data.sh +16 -0
- data/shipitron.gemspec +14 -0
- metadata +236 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 26404f4e8acd7b859cc8dc2e74a399ccb08ff293
|
|
4
|
+
data.tar.gz: 5654cccd96d69d418e468f1a95217d89000f6edf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 24a5b2934c357f1204d315c32c7fcba122aad527e165feb4015c5b44f77335f7e7ed5226833216417da210711077204ce9d93f0c7d779ca454781be8713f40ff
|
|
7
|
+
data.tar.gz: b1978b9a83fae96d8c7d992253f46e0d4ebd87a3943080bdb0d697c36677100a6a27498376d48b415eb58ea690528abc38b6a5080b4eb5045f4ef6cafe97adea
|
data/.dockerignore
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/Dockerfile
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
FROM ruby:2.3.1-alpine
|
|
2
|
+
MAINTAINER Ryan Schlesinger <ryan@outstand.com>
|
|
3
|
+
|
|
4
|
+
RUN addgroup -S shipitron && \
|
|
5
|
+
adduser -S -G shipitron shipitron && \
|
|
6
|
+
addgroup -g 1101 docker && \
|
|
7
|
+
addgroup shipitron docker
|
|
8
|
+
|
|
9
|
+
ENV GOSU_VERSION 1.9
|
|
10
|
+
ENV DUMB_INIT_VERSION 1.1.3
|
|
11
|
+
|
|
12
|
+
RUN apk add --no-cache ca-certificates gnupg && \
|
|
13
|
+
mkdir -p /tmp/build && \
|
|
14
|
+
cd /tmp/build && \
|
|
15
|
+
gpg --keyserver pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 && \
|
|
16
|
+
curl -o gosu -L "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64" && \
|
|
17
|
+
curl -o gosu.asc -L "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64.asc" && \
|
|
18
|
+
gpg --verify gosu.asc && \
|
|
19
|
+
chmod +x gosu && \
|
|
20
|
+
cp gosu /bin/gosu && \
|
|
21
|
+
curl -O -L https://github.com/Yelp/dumb-init/releases/download/v${DUMB_INIT_VERSION}/dumb-init_${DUMB_INIT_VERSION}_amd64 && \
|
|
22
|
+
curl -O -L https://github.com/Yelp/dumb-init/releases/download/v${DUMB_INIT_VERSION}/sha256sums && \
|
|
23
|
+
grep dumb-init_${DUMB_INIT_VERSION}_amd64$ sha256sums | sha256sum -c && \
|
|
24
|
+
chmod +x dumb-init_${DUMB_INIT_VERSION}_amd64 && \
|
|
25
|
+
cp dumb-init_${DUMB_INIT_VERSION}_amd64 /bin/dumb-init && \
|
|
26
|
+
cd /tmp && \
|
|
27
|
+
rm -rf /tmp/build && \
|
|
28
|
+
apk del gnupg && \
|
|
29
|
+
rm -rf /root/.gnupg
|
|
30
|
+
|
|
31
|
+
RUN apk add --no-cache \
|
|
32
|
+
build-base \
|
|
33
|
+
git \
|
|
34
|
+
openssh \
|
|
35
|
+
perl \
|
|
36
|
+
bash
|
|
37
|
+
|
|
38
|
+
ENV USE_BUNDLE_EXEC true
|
|
39
|
+
ENV BUNDLE_GEMFILE /shipitron/Gemfile
|
|
40
|
+
|
|
41
|
+
WORKDIR /app
|
|
42
|
+
COPY Gemfile shipitron.gemspec /shipitron/
|
|
43
|
+
COPY lib/shipitron/version.rb /shipitron/lib/shipitron/
|
|
44
|
+
COPY scripts/fetch-bundler-data.sh /shipitron/scripts/fetch-bundler-data.sh
|
|
45
|
+
|
|
46
|
+
ARG bundler_data_host
|
|
47
|
+
RUN /shipitron/scripts/fetch-bundler-data.sh ${bundler_data_host} && \
|
|
48
|
+
(bundle check || bundle install) && \
|
|
49
|
+
git config --global push.default simple
|
|
50
|
+
COPY . /shipitron/
|
|
51
|
+
RUN ln -s /shipitron/exe/shipitron /usr/local/bin/shipitron && \
|
|
52
|
+
mkdir -p /home/shipitron/.ssh && \
|
|
53
|
+
chown shipitron:shipitron /home/shipitron/.ssh && \
|
|
54
|
+
chmod 700 /home/shipitron/.ssh
|
|
55
|
+
|
|
56
|
+
COPY scripts/docker-entrypoint.sh /docker-entrypoint.sh
|
|
57
|
+
|
|
58
|
+
ENV DUMB_INIT_SETSID 0
|
|
59
|
+
ENTRYPOINT ["/docker-entrypoint.sh"]
|
|
60
|
+
CMD ["help"]
|
data/Dockerfile.release
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
FROM ruby:2.3.1-alpine
|
|
2
|
+
MAINTAINER Ryan Schlesinger <ryan@outstand.com>
|
|
3
|
+
|
|
4
|
+
RUN addgroup -S shipitron && \
|
|
5
|
+
adduser -S -G shipitron shipitron && \
|
|
6
|
+
addgroup -g 1101 docker && \
|
|
7
|
+
addgroup shipitron docker
|
|
8
|
+
|
|
9
|
+
ENV GOSU_VERSION 1.9
|
|
10
|
+
ENV DUMB_INIT_VERSION 1.1.3
|
|
11
|
+
|
|
12
|
+
RUN apk add --no-cache ca-certificates gnupg && \
|
|
13
|
+
mkdir -p /tmp/build && \
|
|
14
|
+
cd /tmp/build && \
|
|
15
|
+
gpg --keyserver pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 && \
|
|
16
|
+
curl -o gosu -L "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64" && \
|
|
17
|
+
curl -o gosu.asc -L "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64.asc" && \
|
|
18
|
+
gpg --verify gosu.asc && \
|
|
19
|
+
chmod +x gosu && \
|
|
20
|
+
cp gosu /bin/gosu && \
|
|
21
|
+
curl -O -L https://github.com/Yelp/dumb-init/releases/download/v${DUMB_INIT_VERSION}/dumb-init_${DUMB_INIT_VERSION}_amd64 && \
|
|
22
|
+
curl -O -L https://github.com/Yelp/dumb-init/releases/download/v${DUMB_INIT_VERSION}/sha256sums && \
|
|
23
|
+
grep dumb-init_${DUMB_INIT_VERSION}_amd64$ sha256sums | sha256sum -c && \
|
|
24
|
+
chmod +x dumb-init_${DUMB_INIT_VERSION}_amd64 && \
|
|
25
|
+
cp dumb-init_${DUMB_INIT_VERSION}_amd64 /bin/dumb-init && \
|
|
26
|
+
cd /tmp && \
|
|
27
|
+
rm -rf /tmp/build && \
|
|
28
|
+
apk del gnupg && \
|
|
29
|
+
rm -rf /root/.gnupg
|
|
30
|
+
|
|
31
|
+
RUN apk add --no-cache \
|
|
32
|
+
git \
|
|
33
|
+
perl \
|
|
34
|
+
bash
|
|
35
|
+
|
|
36
|
+
WORKDIR /app
|
|
37
|
+
|
|
38
|
+
ENV SHIPITRON_VERSION=0.2.0
|
|
39
|
+
|
|
40
|
+
RUN gem install shipitron -v ${SHIPITRON_VERSION} && \
|
|
41
|
+
mkdir -p /home/shipitron/.ssh && \
|
|
42
|
+
chown shipitron:shipitron /home/shipitron/.ssh && \
|
|
43
|
+
chmod 700 /home/shipitron/.ssh
|
|
44
|
+
|
|
45
|
+
COPY scripts/docker-entrypoint.sh /docker-entrypoint.sh
|
|
46
|
+
|
|
47
|
+
ENV DUMB_INIT_SETSID 0
|
|
48
|
+
ENTRYPOINT ["/docker-entrypoint.sh"]
|
|
49
|
+
CMD ["help"]
|
data/README.md
CHANGED
|
@@ -1,13 +1,47 @@
|
|
|
1
1
|
# shipitron
|
|
2
2
|
A deployment tool for use with Docker and ECS
|
|
3
3
|
|
|
4
|
-
##
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
## Usage
|
|
5
|
+
|
|
6
|
+
Example config file:
|
|
7
|
+
```yaml
|
|
8
|
+
applications:
|
|
9
|
+
dummy-app:
|
|
10
|
+
repository: git@github.com:outstand/dummy-app
|
|
11
|
+
cache_bucket: bucket
|
|
12
|
+
image_name: outstand/dummy-app
|
|
13
|
+
build_script: shipitron/build.sh
|
|
14
|
+
post_builds:
|
|
15
|
+
- ecs_task: dummy-app
|
|
16
|
+
container_name: dummy-app
|
|
17
|
+
command: echo postbuild
|
|
18
|
+
ecs_clusters:
|
|
19
|
+
- name: us-east-1-prod-blue
|
|
20
|
+
region: us-east-1
|
|
21
|
+
- name: us-east-1-prod-green
|
|
22
|
+
region: us-east-1
|
|
23
|
+
shipitron_task: shipitron
|
|
24
|
+
ecs_task_defs:
|
|
25
|
+
- dummy-app
|
|
26
|
+
ecs_services:
|
|
27
|
+
- dummy-app
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- Create shipitron.yml file
|
|
31
|
+
- `docker run -it --rm -v shipitron.yml:/shipitron/config/shipitron.yml outstand/shipitron:<version> deploy <app>`
|
|
32
|
+
|
|
33
|
+
## Development
|
|
34
|
+
|
|
35
|
+
- `docker volume create --name shipitron_fog`
|
|
36
|
+
- `./build_dev.sh`
|
|
37
|
+
- `docker run -it --rm -v $(pwd):/shipitron -v shipitron_fog:/fog -e FOG_LOCAL=true outstand/shipitron:dev rspec spec` to run specs
|
|
38
|
+
- `docker run -it --rm -v $(pwd):/shipitron outstand/shipitron:dev deploy <app>` to run client side
|
|
39
|
+
- `docker run -it --rm -v $(pwd):/shipitron outstand/shipitron:dev deploy <app> --simulate` to get the arguments for `server_deploy` below
|
|
40
|
+
- `docker run -it --rm --dns 10.10.10.2 -v /path/to/shipitron:/shipitron -v $(pwd):/app -v /bin/docker:/bin/docker -v /var/run/docker.sock:/var/run/docker.sock -v shipitron_fog:/fog -e FOG_LOCAL=true -e CONSUL_HOST=consul outstand/shipitron:dev server_deploy --name dummy-app --repository git@github.com:outstand/dummy-app --bucket outstand-shipitron --image-name outstand/dummy-app --region us-east-1 --cluster-name us-east-1-prod-blue --ecs-task-defs dummy-app --ecs-services dummy-app --build-script shipitron/build.sh --post-builds 'ecs_task:dummy-app,container_name:dummy-app,command:echo postbuild' --ecs-task-def-templates LS0tCmZhbWlseTogZHVtbXktYXBwCmNvbnRhaW5lcl9kZWZpbml0aW9uczoKICAtIG5hbWU6IGR1bW15LWFwcAogICAgaW1hZ2U6IG91dHN0YW5kL2R1bW15LWFwcDp7e3RhZ319CiAgICBtZW1vcnk6IDEyOAogICAgZXNzZW50aWFsOiB0cnVlCiAgICBwb3J0X21hcHBpbmdzOgogICAgICAtIGNvbnRhaW5lcl9wb3J0OiA4MAogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gbmFtZTogU0VSVklDRV84MF9OQU1FCiAgICAgICAgdmFsdWU6IGR1bW15Cg== --ecs-service-templates LS0tCmNsdXN0ZXI6IHt7Y2x1c3Rlcn19CnNlcnZpY2VfbmFtZTogZHVtbXktYXBwCnRhc2tfZGVmaW5pdGlvbjogZHVtbXktYXBwe3tyZXZpc2lvbn19CmRlc2lyZWRfY291bnQ6IHt7Y291bnR9fQojcm9sZToge3tyb2xlfX0KZGVwbG95bWVudF9jb25maWd1cmF0aW9uOgogIG1heGltdW1fcGVyY2VudDogMjAwCiAgbWluaW11bV9oZWFsdGh5X3BlcmNlbnQ6IDUwCg== --debug` to run server side (dummy-app is an example)
|
|
41
|
+
|
|
42
|
+
To release a new version:
|
|
43
|
+
- Update the version number in `version.rb` and `Dockerfile.release` and commit the result.
|
|
44
|
+
- `./build_dev.sh`
|
|
45
|
+
- `docker run -it --rm -v ~/.gitconfig:/root/.gitconfig -v ~/.gitconfig.user:/root/.gitconfig.user -v ~/.ssh/id_rsa:/root/.ssh/id_rsa -v ~/.gem:/root/.gem -w /shipitron outstand/shipitron:dev rake release`
|
|
46
|
+
- `docker build -t outstand/shipitron:VERSION -f Dockerfile.release .`
|
|
47
|
+
- `docker push outstand/shipitron:VERSION`
|
data/build_dev.sh
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e -x
|
|
3
|
+
|
|
4
|
+
build_image=outstand/shipitron:dev
|
|
5
|
+
dockerfile=Dockerfile
|
|
6
|
+
bundler_data_dir=tmp
|
|
7
|
+
|
|
8
|
+
bundler_data_container=''
|
|
9
|
+
tar_container=''
|
|
10
|
+
|
|
11
|
+
function cleanup {
|
|
12
|
+
if [ -n "$bundler_data_container" ]; then
|
|
13
|
+
docker stop bundler-data
|
|
14
|
+
docker rm -fv bundler-data
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
if [ -n "$tar_container" ]; then
|
|
18
|
+
docker rm -fv ${tar_container}
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
rm -f ${bundler_data_dir}/cidfile
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
trap cleanup EXIT
|
|
25
|
+
|
|
26
|
+
build_args=''
|
|
27
|
+
mkdir -p ${bundler_data_dir}
|
|
28
|
+
|
|
29
|
+
if [ -f $(pwd)/${bundler_data_dir}/bundler-data.tar.gz ]; then
|
|
30
|
+
docker run --name bundler-data -v $(pwd)/${bundler_data_dir}/bundler-data.tar.gz:/usr/share/nginx/html/bundler-data.tar.gz:ro -d nginx:stable-alpine
|
|
31
|
+
bundler_data_container=bundler-data
|
|
32
|
+
build_args="--build-arg bundler_data_host=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' bundler-data)"
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
docker build -t ${build_image} -f ${dockerfile} ${build_args} .
|
|
36
|
+
|
|
37
|
+
docker run -t --cidfile=${bundler_data_dir}/cidfile -w /usr/local/bundle ${build_image} tar -zcf /tmp/bundler-data.tar.gz .
|
|
38
|
+
tar_container=$(cat ${bundler_data_dir}/cidfile)
|
|
39
|
+
docker cp ${tar_container}:/tmp/bundler-data.tar.gz ${bundler_data_dir}/bundler-data.tar.gz
|
data/exe/shipitron
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require 'shipitron'
|
|
3
|
+
|
|
4
|
+
module Shipitron
|
|
5
|
+
class CLI < Thor
|
|
6
|
+
desc 'version', 'Print out the version string'
|
|
7
|
+
def version
|
|
8
|
+
require 'shipitron/version'
|
|
9
|
+
say Shipitron::VERSION.to_s
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
desc 'deploy <app>', 'Deploys the app'
|
|
13
|
+
option :config_file, default: 'shipitron/config.yml'
|
|
14
|
+
option :secrets_file, default: 'shipitron/secrets.yml'
|
|
15
|
+
option :ember, type: :boolean, default: false
|
|
16
|
+
option :ember_only, type: :boolean, default: false
|
|
17
|
+
option :debug, type: :boolean, default: false
|
|
18
|
+
option :simulate, type: :boolean, default: false
|
|
19
|
+
def deploy(app)
|
|
20
|
+
setup(
|
|
21
|
+
config_file: options[:config_file],
|
|
22
|
+
secrets_file: options[:secrets_file]
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
require 'shipitron/client/deploy_application'
|
|
26
|
+
result = Client::DeployApplication.call(
|
|
27
|
+
application: app,
|
|
28
|
+
simulate: options[:simulate]
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
if result.failure?
|
|
32
|
+
result.errors.each do |error|
|
|
33
|
+
Logger.fatal error
|
|
34
|
+
end
|
|
35
|
+
Logger.fatal 'Deploy failed.'
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
desc 'server_deploy', 'Server-side component of deploy'
|
|
40
|
+
option :name, required: true
|
|
41
|
+
option :repository, required: true
|
|
42
|
+
option :branch, default: 'master'
|
|
43
|
+
option :bucket, required: true
|
|
44
|
+
option :image_name, required: true
|
|
45
|
+
option :region, required: true
|
|
46
|
+
option :cluster_name, required: true
|
|
47
|
+
option :ecs_task_defs, type: :array, required: true
|
|
48
|
+
option :ecs_task_def_templates, type: :array, default: []
|
|
49
|
+
option :ecs_services, type: :array, required: true
|
|
50
|
+
option :ecs_service_templates, type: :array, default: []
|
|
51
|
+
option :build_script, default: nil
|
|
52
|
+
option :post_builds, type: :array
|
|
53
|
+
option :secrets_file, default: 'shipitron/secrets.yml'
|
|
54
|
+
option :debug, type: :boolean, default: false
|
|
55
|
+
def server_deploy
|
|
56
|
+
setup(
|
|
57
|
+
secrets_file: options[:secrets_file]
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
require 'shipitron/server/transform_cli_args'
|
|
61
|
+
cli_args = Server::TransformCliArgs.call!(
|
|
62
|
+
application: options[:name],
|
|
63
|
+
repository_url: options[:repository],
|
|
64
|
+
repository_branch: options[:branch],
|
|
65
|
+
s3_cache_bucket: options[:bucket],
|
|
66
|
+
image_name: options[:image_name],
|
|
67
|
+
region: options[:region],
|
|
68
|
+
cluster_name: options[:cluster_name],
|
|
69
|
+
ecs_task_defs: options[:ecs_task_defs],
|
|
70
|
+
ecs_task_def_templates: options[:ecs_task_def_templates],
|
|
71
|
+
ecs_services: options[:ecs_services],
|
|
72
|
+
ecs_service_templates: options[:ecs_service_templates],
|
|
73
|
+
build_script: options[:build_script],
|
|
74
|
+
post_builds: options[:post_builds]
|
|
75
|
+
).cli_args
|
|
76
|
+
|
|
77
|
+
require 'shipitron/server/deploy_application'
|
|
78
|
+
result = Server::DeployApplication.call(
|
|
79
|
+
cli_args
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
if result.failure?
|
|
83
|
+
result.errors.each do |error|
|
|
84
|
+
Logger.fatal error
|
|
85
|
+
end
|
|
86
|
+
Logger.fatal 'Deploy failed.'
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
desc 'bootstrap <app>', 'Bootstrap ECS task definitions and services'
|
|
91
|
+
option :region, required: true
|
|
92
|
+
option :cluster_name, required: true
|
|
93
|
+
option :service_count, type: :numeric, default: 3
|
|
94
|
+
option :task_def_dir, default: 'shipitron/ecs_task_defs'
|
|
95
|
+
option :service_dir, default: 'shipitron/ecs_services'
|
|
96
|
+
option :secrets_file, default: 'shipitron/secrets.yml'
|
|
97
|
+
option :debug, type: :boolean, default: false
|
|
98
|
+
def bootstrap(app)
|
|
99
|
+
setup(
|
|
100
|
+
secrets_file: options[:secrets_file]
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
require 'shipitron/client/bootstrap_application'
|
|
104
|
+
result = Client::BootstrapApplication.call(
|
|
105
|
+
application: app,
|
|
106
|
+
region: options[:region],
|
|
107
|
+
cluster_name: options[:cluster_name],
|
|
108
|
+
service_count: options[:service_count],
|
|
109
|
+
task_def_directory: options[:task_def_dir],
|
|
110
|
+
service_directory: options[:service_dir]
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if result.failure?
|
|
114
|
+
result.errors.each do |error|
|
|
115
|
+
Logger.fatal error
|
|
116
|
+
end
|
|
117
|
+
Logger.fatal 'Bootstrap failed.'
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
private
|
|
122
|
+
def setup(config_file:nil, secrets_file:nil)
|
|
123
|
+
$stdout.sync = true
|
|
124
|
+
if options[:debug] == false
|
|
125
|
+
Logger.level = :info
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
Shipitron.config_file = config_file unless config_file.nil?
|
|
129
|
+
Shipitron.secrets_file = secrets_file unless secrets_file.nil?
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'shipitron'
|
|
2
|
+
require 'shipitron/client/register_ecs_task_definitions'
|
|
3
|
+
require 'shipitron/client/create_ecs_services'
|
|
4
|
+
|
|
5
|
+
module Shipitron
|
|
6
|
+
module Client
|
|
7
|
+
class BootstrapApplication
|
|
8
|
+
include Metaractor
|
|
9
|
+
include Interactor::Organizer
|
|
10
|
+
|
|
11
|
+
required :application
|
|
12
|
+
required :region
|
|
13
|
+
required :cluster_name
|
|
14
|
+
required :service_count
|
|
15
|
+
required :task_def_directory
|
|
16
|
+
required :service_directory
|
|
17
|
+
|
|
18
|
+
organize [
|
|
19
|
+
RegisterEcsTaskDefinitions,
|
|
20
|
+
CreateEcsServices
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
def call
|
|
24
|
+
Logger.info "==> Bootstrapping #{application}"
|
|
25
|
+
super
|
|
26
|
+
Logger.info "==> Done"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
def application
|
|
31
|
+
context.application
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require 'shipitron'
|
|
2
|
+
require 'shipitron/ecs_client'
|
|
3
|
+
require 'shipitron/mustache_yaml_parser'
|
|
4
|
+
|
|
5
|
+
module Shipitron
|
|
6
|
+
module Client
|
|
7
|
+
class CreateEcsServices
|
|
8
|
+
include Metaractor
|
|
9
|
+
include EcsClient
|
|
10
|
+
|
|
11
|
+
required :region
|
|
12
|
+
required :service_directory
|
|
13
|
+
required :cluster_name
|
|
14
|
+
required :service_count
|
|
15
|
+
|
|
16
|
+
def call
|
|
17
|
+
Logger.info 'Creating ECS services'
|
|
18
|
+
|
|
19
|
+
service_defs = Pathname.new(service_directory)
|
|
20
|
+
unless service_defs.directory?
|
|
21
|
+
fail_with_error!(
|
|
22
|
+
message: "service directory '#{service_directory}' does not exist"
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
service_defs.find do |path|
|
|
27
|
+
next if path.directory?
|
|
28
|
+
|
|
29
|
+
service_def = Smash.load(
|
|
30
|
+
path.to_s,
|
|
31
|
+
parser: MustacheYamlParser.new(
|
|
32
|
+
context: {
|
|
33
|
+
cluster: cluster_name,
|
|
34
|
+
revision: nil, # ECS will default to latest ACTIVE
|
|
35
|
+
count: service_count
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
).merge(
|
|
39
|
+
client_token: SecureRandom.uuid
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
Logger.info "Creating service '#{service_def.service_name}'"
|
|
43
|
+
Logger.debug "Service definition: #{service_def.to_h}"
|
|
44
|
+
begin
|
|
45
|
+
ecs_client(region: region).create_service(
|
|
46
|
+
service_def.to_h
|
|
47
|
+
)
|
|
48
|
+
rescue Aws::ECS::Errors::InvalidParameterException => e
|
|
49
|
+
raise if e.message != 'Creation of service was not idempotent.'
|
|
50
|
+
|
|
51
|
+
Logger.info "Service '#{service_def.service_name}' already exists."
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
Logger.info 'Done'
|
|
56
|
+
rescue Aws::ECS::Errors::ServiceError => e
|
|
57
|
+
fail_with_errors!(messages: [
|
|
58
|
+
"Error: #{e.message}",
|
|
59
|
+
e.backtrace.join("\n")
|
|
60
|
+
])
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
def region
|
|
65
|
+
context.region
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def service_directory
|
|
69
|
+
context.service_directory
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def cluster_name
|
|
73
|
+
context.cluster_name
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def service_count
|
|
77
|
+
context.service_count
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'shipitron'
|
|
2
|
+
require 'shipitron/client/load_application_config'
|
|
3
|
+
require 'shipitron/client/load_templates'
|
|
4
|
+
require 'shipitron/client/ensure_deploy_not_running'
|
|
5
|
+
require 'shipitron/client/run_ecs_tasks'
|
|
6
|
+
|
|
7
|
+
module Shipitron
|
|
8
|
+
module Client
|
|
9
|
+
class DeployApplication
|
|
10
|
+
include Metaractor
|
|
11
|
+
include Interactor::Organizer
|
|
12
|
+
|
|
13
|
+
required :application
|
|
14
|
+
|
|
15
|
+
organize [
|
|
16
|
+
LoadApplicationConfig,
|
|
17
|
+
LoadTemplates,
|
|
18
|
+
EnsureDeployNotRunning,
|
|
19
|
+
RunEcsTasks
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
def call
|
|
23
|
+
Logger.info "==> Deploying #{application}"
|
|
24
|
+
super
|
|
25
|
+
Logger.info "==> Done"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
def application
|
|
30
|
+
context.application
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'shipitron'
|
|
2
|
+
require 'shipitron/ecs_client'
|
|
3
|
+
|
|
4
|
+
# Note: This is a best effort client side check to make sure there
|
|
5
|
+
# isn't a deploy running. The server side check has more guarantees.
|
|
6
|
+
|
|
7
|
+
module Shipitron
|
|
8
|
+
module Client
|
|
9
|
+
class EnsureDeployNotRunning
|
|
10
|
+
include Metaractor
|
|
11
|
+
include EcsClient
|
|
12
|
+
|
|
13
|
+
required :clusters
|
|
14
|
+
optional :simulate
|
|
15
|
+
|
|
16
|
+
def call
|
|
17
|
+
return if simulate?
|
|
18
|
+
|
|
19
|
+
clusters.each do |cluster|
|
|
20
|
+
%w[PENDING RUNNING].each do |status|
|
|
21
|
+
begin
|
|
22
|
+
response = ecs_client(region: cluster.region).list_tasks(
|
|
23
|
+
cluster: cluster.name,
|
|
24
|
+
started_by: 'shipitron',
|
|
25
|
+
max_results: 1,
|
|
26
|
+
desired_status: status
|
|
27
|
+
)
|
|
28
|
+
if !response.task_arns.empty?
|
|
29
|
+
fail_with_errors!(messages: [
|
|
30
|
+
'Shipitron says "THERE CAN BE ONLY ONE"',
|
|
31
|
+
'Deploy is already running.'
|
|
32
|
+
])
|
|
33
|
+
end
|
|
34
|
+
rescue Aws::ECS::Errors::ClusterNotFoundException
|
|
35
|
+
fail_with_errors!(messages: [
|
|
36
|
+
'Shipitron says "PUNY HUMAN IS MISSING A CLUSTER"',
|
|
37
|
+
"Cluster '#{cluster.name}' not found in region #{cluster.region}."
|
|
38
|
+
])
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
def clusters
|
|
46
|
+
context.clusters
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def simulate?
|
|
50
|
+
context.simulate == true
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'shipitron'
|
|
2
|
+
require 'shipitron/post_build'
|
|
3
|
+
|
|
4
|
+
module Shipitron
|
|
5
|
+
module Client
|
|
6
|
+
class LoadApplicationConfig
|
|
7
|
+
include Metaractor
|
|
8
|
+
|
|
9
|
+
required :application
|
|
10
|
+
|
|
11
|
+
def call
|
|
12
|
+
context.repository_url = config.repository
|
|
13
|
+
context.s3_cache_bucket = config.cache_bucket
|
|
14
|
+
context.image_name = config.image_name
|
|
15
|
+
context.build_script = config.build_script
|
|
16
|
+
context.post_builds = begin
|
|
17
|
+
if config.post_builds.nil?
|
|
18
|
+
[]
|
|
19
|
+
else
|
|
20
|
+
config.post_builds.map {|pb| PostBuild.new(pb) }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
context.clusters = config.ecs_clusters
|
|
24
|
+
context.shipitron_task = config.shipitron_task
|
|
25
|
+
context.ecs_task_defs = config.ecs_task_defs
|
|
26
|
+
context.ecs_services = config.ecs_services
|
|
27
|
+
context.ecs_task_def_dir = config.ecs_task_def_dir
|
|
28
|
+
context.ecs_service_dir = config.ecs_service_dir
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
def application
|
|
33
|
+
context.application
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def config
|
|
37
|
+
@config ||= Shipitron.config.applications[application]
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'shipitron'
|
|
2
|
+
|
|
3
|
+
module Shipitron
|
|
4
|
+
module Client
|
|
5
|
+
class LoadTemplates
|
|
6
|
+
include Metaractor
|
|
7
|
+
|
|
8
|
+
required :ecs_task_def_dir
|
|
9
|
+
required :ecs_service_dir
|
|
10
|
+
|
|
11
|
+
def call
|
|
12
|
+
context.ecs_task_def_templates = load_templates(ecs_task_def_dir)
|
|
13
|
+
context.ecs_service_templates = load_templates(ecs_service_dir)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
def ecs_task_def_dir
|
|
18
|
+
context.ecs_task_def_dir
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def ecs_service_dir
|
|
22
|
+
context.ecs_service_dir
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def load_templates(dir)
|
|
26
|
+
search_path = Pathname.new(dir)
|
|
27
|
+
unless search_path.directory?
|
|
28
|
+
fail_with_error!(
|
|
29
|
+
message: "directory '#{dir}' does not exist"
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
templates = []
|
|
34
|
+
search_path.find do |path|
|
|
35
|
+
next if path.directory?
|
|
36
|
+
|
|
37
|
+
templates << path.read
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Logger.debug "Templates loaded: #{templates.inspect}"
|
|
41
|
+
templates
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|