docker-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4daf7ed396ab33b4baccadbe1a1f888af9a3271d
4
+ data.tar.gz: 7a38191133060c00417437db6ba84e93d3f936a5
5
+ SHA512:
6
+ metadata.gz: 8d8ffca0992dbbbd4edfd75b36de681f018933d497f1b9cd37d05edb0cd59471c06fd15136de6e031867f928904732c8570f45e219c69b714a5e24f94cd2cdd3
7
+ data.tar.gz: 49cea1c9d37dcb37d519e3db6d0345185f6fc6cb6dac30c0a6f1d17fa708a7de2bf7df8cbd397f5daea0e0555046dc1a3f1a86d8b9df5562e888e148c4f863e7
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ docker-rails
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.2.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in docker-rails.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Kevin Ross
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # Docker::Rails
2
+
3
+ A simplified pattern to execute rails applications within Docker (with a CI build emphasis).
4
+
5
+ Features:
6
+ - cached global bundler data volume (automatic) based on ruby version
7
+ - interpolates `docker-compose.yml` making CI builds much easier
8
+ - starts `db` container first, and continues with `web` once `db` is ready
9
+ - cleans up `db` and `web` containers once completed
10
+
11
+
12
+ **Very much a work in progress - contributions welcome**
13
+ Open to pull requests, while this starts off as one-person's environment, it can be expanded to suit many different configurations.
14
+
15
+ Needs:
16
+ - remove hardcoded ip for db ip resolution
17
+ - remove or default hardcoded BUILD_NAME
18
+ - expand to different db status detection as needed
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ gem 'docker-rails'
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install docker-rails
33
+
34
+ ## Usage
35
+
36
+ ### 1. Add a Dockerfile
37
+
38
+ ```bash
39
+ FROM atlashealth/ruby:2.2.2
40
+
41
+ ENV DEBIAN_FRONTEND noninteractive
42
+
43
+ # For building, nokogiri support, capybara-webkit, mysql client
44
+ # Clean up APT when done.
45
+ RUN apt-get update -qq && \
46
+ apt-get install -qy build-essential libxml2-dev libxslt1-dev \
47
+ g++ qt5-default libqt5webkit5-dev xvfb libmysqlclient-dev && \
48
+
49
+ # cleanup
50
+ apt-get clean && \
51
+ cd /var/lib/apt/lists && rm -fr *Release* *Sources* *Packages* && \
52
+ truncate -s 0 /var/log/*log
53
+
54
+ # https://github.com/docker/docker/issues/4032
55
+ ENV DEBIAN_FRONTEND newt
56
+
57
+ ADD . /project
58
+ WORKDIR /project
59
+ RUN ["chmod", "+x", "/project/scripts/*"]
60
+ ```
61
+
62
+ ### 2. Add a docker-compose.yml
63
+
64
+ Environment variables will be interpolated, so feel free to use them.
65
+
66
+ ```yaml
67
+ web:
68
+ build: .
69
+ working_dir: /project/spec/dummy
70
+ command: /project/script/start
71
+ ports:
72
+ - "3000:3000"
73
+ links:
74
+ - db
75
+ volumes:
76
+ - .:/project
77
+ links:
78
+ - db
79
+ volumes_from:
80
+ # Mount the gems data volume container for cached bundler gem files
81
+ - #{GEMS_VOLUME_NAME}
82
+ environment:
83
+ # Tell bundler where to get the files
84
+ - GEM_HOME=#{GEMS_VOLUME_PATH}
85
+
86
+ db:
87
+ image: library/mysql:5.7.6
88
+ ports:
89
+ - "3306:3306"
90
+ environment:
91
+ - MYSQL_ALLOW_EMPTY_PASSWORD=true
92
+ ```
93
+
94
+ ### 3. Add a startup script
95
+
96
+ TODO: verify a good sample
97
+
98
+ ```bash
99
+ #!/usr/bin/env bash
100
+
101
+ echo "Bundling gems"
102
+ bundle install --jobs 4 --retry 3
103
+
104
+ echo "Generating Spring binstubs"
105
+ bundle exec spring binstub --all
106
+
107
+ echo "Clearing logs"
108
+ bin/rake log:clear
109
+
110
+ echo "Setting up new db if one doesn't exist"
111
+ bin/rake db:version || { bundle exec rake db:setup; }
112
+
113
+ echo "Removing contents of tmp dirs"
114
+ bin/rake tmp:clear
115
+
116
+ echo "Starting app server"
117
+ bundle exec rails s -p 3000
118
+
119
+ # or use foreman
120
+ # gem install foreman
121
+ # foreman start
122
+ ```
123
+
124
+ ### 4. Run it
125
+
126
+ `bundle exec docker-rails`
127
+
128
+ ### 5. Submit pull requests!
129
+
130
+ This is starting off simple, but again, we welcome pulls to make this and the process of using docker for rails much easier.
131
+
132
+
133
+ ## Contributing
134
+
135
+ 1. Fork it ( https://github.com/[my-github-username]/docker-rails/fork )
136
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
137
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
138
+ 4. Push to the branch (`git push origin my-new-feature`)
139
+ 5. Create a new Pull Request
140
+
141
+ ## License and Attributions
142
+ MIT license, inspired by many but certainly a [useful blog post by AtlasHealth](http://www.atlashealth.com/blog/2014/09/persistent-ruby-gems-docker-container).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/docker-rails ADDED
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ # compose cli
5
+ # https://docs.docker.com/v1.6/compose/cli/#environment-variables
6
+
7
+ # compose yml
8
+ # https://docs.docker.com/v1.6/compose/yml/
9
+
10
+ # remote api
11
+ # https://docs.docker.com/reference/api/docker_remote_api_v1.20
12
+
13
+
14
+ # enable local usage from cloned repo
15
+ root = File.expand_path('../..', __FILE__)
16
+ $LOAD_PATH << "#{root}/lib" if File.exist?("#{root}/Gemfile")
17
+
18
+ require 'docker/rails'
19
+
20
+ BUILD_NAME = '113' # temp should be passed in
21
+ ENV['BUILD_NAME'] = BUILD_NAME
22
+
23
+ # Discover ruby version from the Dockerfile image
24
+ IO.read('Dockerfile') =~ /^FROM \w+\/ruby:(\d+.\d+(?:.\d+))/
25
+ BUILD_RUBY_VERSION = $1
26
+
27
+ # Set as variable for interpolation
28
+ GEMS_VOLUME_PATH = "/gems/#{BUILD_RUBY_VERSION}"
29
+ GEMS_VOLUME_NAME = "gems-#{BUILD_RUBY_VERSION}"
30
+ ENV['GEMS_VOLUME_PATH'] = GEMS_VOLUME_PATH
31
+ ENV['GEMS_VOLUME_NAME'] = GEMS_VOLUME_NAME
32
+
33
+ # -----------
34
+ # Create global gems data volume to cache gems for this version of ruby
35
+ #
36
+ # Docker::Container.create('name' => 'foo-gems-2.2.2', 'Image' => 'busybox', 'Mounts' => [ { 'Destination' => '/gems/2.2.2' } ])
37
+ #
38
+ require 'docker'
39
+ begin
40
+ gems_container = Docker::Container.get(GEMS_VOLUME_NAME)
41
+ puts "Gem data volume container #{GEMS_VOLUME_NAME} already exists."
42
+ rescue Docker::Error::NotFoundError => e
43
+ gems_container = Docker::Container.create('name' => GEMS_VOLUME_NAME, 'Image' => 'busybox', 'Mounts' => [{'Destination' => GEMS_VOLUME_PATH}])
44
+ puts "Gem data volume container #{GEMS_VOLUME_NAME} created."
45
+ end
46
+ gems_container.streaming_logs(stdout: true) { |stream, chunk| puts "#{GEMS_VOLUME_NAME}: #{chunk}" }
47
+
48
+ # Read docker-compose.yml and rewrite with interpolated variables and BUILD_NAME
49
+ COMPOSE_FILENAME = "docker-compose-build-#{BUILD_NAME}.yml"
50
+ compose_config = Docker::Rails::ComposeConfig.interpolate_file(COMPOSE_FILENAME)
51
+
52
+ # convenience to execute docker-compose with file and project params
53
+ def xc(cmd)
54
+ cmd = "docker-compose -f #{COMPOSE_FILENAME} -p #{BUILD_NAME} #{cmd}"
55
+ puts "Running `#{cmd}`"
56
+ output =`#{cmd}`
57
+ result=$?.success?
58
+ output
59
+ end
60
+
61
+ # service_name i.e. 'db' or 'web'
62
+ def get_container_name(service_name)
63
+ output = xc "ps #{service_name}"
64
+ output =~ /^(\w+)/ # grab the name, only thing that is at the start of the line
65
+ $1
66
+ end
67
+
68
+ # http://blog.oddbit.com/2014/08/11/four-ways-to-connect-a-docker/
69
+ def get_ip_address(container_name)
70
+ cmd = "docker inspect --format '{{ .NetworkSettings.IPAddress }}' #{container_name}"
71
+ puts "Running `#{cmd}`"
72
+ output = `#{cmd}`
73
+ result=$?.success?
74
+ output
75
+ end
76
+
77
+ def up_container(service_name, options = '')
78
+ xc "up #{options} #{service_name}"
79
+ container_name = get_container_name(service_name)
80
+ puts "#{service_name}: container_name #{container_name}"
81
+
82
+ container = Docker::Container.get(container_name)
83
+ container.streaming_logs(stdout: true) { |stream, chunk| puts "#{service_name}: #{chunk}" }
84
+ # puts container
85
+
86
+ ip_address = get_ip_address(container_name)
87
+ [container, container_name, ip_address]
88
+ end
89
+
90
+ def destroy_container(container, container_name)
91
+ puts "Stopping and deleting #{container_name}..."
92
+ container.stop
93
+ container.delete(force: true)
94
+ end
95
+
96
+ # Start the db container
97
+ db_container, db_container_name, db_ip_address = *up_container('db', '-d')
98
+
99
+ # ping db to see if it is ready before continuing
100
+ require 'rubygems'
101
+ require 'active_record'
102
+
103
+ LOOP_LIMIT=60
104
+ puts "=> Waiting for confirmation of #{db_container_name} db service startup at #{db_ip_address}..."
105
+ LOOP_LIMIT.times do |i|
106
+ if i == LOOP_LIMIT - 1
107
+ puts 'Time out waiting for db to be up.'
108
+ #docker logs --follow=false $DB_CONTAINER
109
+ break
110
+ end
111
+
112
+ ActiveRecord::Base.establish_connection ({
113
+ adapter: 'mysql2',
114
+ # host: db_ip_address,
115
+ host: '192.168.99.100',
116
+ port: 3306,
117
+ username: 'root'})
118
+ # connected = ActiveRecord::Base.connection_pool.with_connection { |con| con.active? } rescue false
119
+ connected =
120
+ begin
121
+ ActiveRecord::Base.connection_pool.with_connection { |con| con.active? }
122
+ rescue => e
123
+ # puts "#{e.class.name}: #{e.message}"
124
+ false
125
+ end
126
+ printf '.'
127
+ if connected
128
+ printf 'connected.'
129
+ break
130
+ end
131
+ sleep 1
132
+ end
133
+ puts ''
134
+
135
+
136
+ # Start the web containers
137
+ web_container, web_container_name, web_ip_address = *up_container('web')
138
+
139
+
140
+ puts 'Cleaning up containers...'
141
+ destroy_container(db_container, db_container_name)
142
+ destroy_container(web_container, web_container_name)
143
+ puts "Done.\n\n\n"
144
+
145
+ # cleanup build interpolated docker-compose.yml
146
+ # File.delete COMPOSE_FILENAME if File.exists? COMPOSE_FILENAME
147
+
148
+ system 'docker ps -a'
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'docker/rails/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'docker-rails'
8
+ s.version = Docker::Rails::VERSION
9
+ s.authors = ['Kevin Ross']
10
+ s.email = ['kevin.ross@alienfast.com']
11
+ s.summary = %q{A simplified pattern to execute rails applications within Docker (with a CI build emphasis)}
12
+ s.description = %q{}
13
+ s.homepage = ''
14
+ s.license = 'MIT'
15
+
16
+ s.files = `git ls-files -z`.split("\x0")
17
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
19
+ s.require_paths = ['lib']
20
+
21
+ s.add_development_dependency 'bundler', '~> 1.6'
22
+ s.add_development_dependency 'rake'
23
+
24
+ s.add_dependency 'docker-api'
25
+ # s.add_dependency 'parallel_tests'
26
+ s.add_dependency 'dry-config', '>= 1.1.6'
27
+ end
@@ -0,0 +1,8 @@
1
+ require 'docker/rails/version'
2
+
3
+ module Docker
4
+ module Rails
5
+ end
6
+ end
7
+
8
+ require 'docker/rails/compose_config'
@@ -0,0 +1,14 @@
1
+ module Docker
2
+ module Rails
3
+ require 'dry/config'
4
+ class ComposeConfig < Dry::Config::Base
5
+ class << self
6
+ def interpolate_file(output_filename, input_filename = 'docker-compose.yml')
7
+ compose = ComposeConfig.new(symbolize: false)
8
+ compose.load!(nil, input_filename)
9
+ compose.write_yaml_file(output_filename)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ module Docker
2
+ module Rails
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: docker-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Ross
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: docker-api
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: dry-config
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 1.1.6
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 1.1.6
69
+ description: ''
70
+ email:
71
+ - kevin.ross@alienfast.com
72
+ executables:
73
+ - docker-rails
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".ruby-gemset"
79
+ - ".ruby-version"
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - bin/docker-rails
85
+ - docker-rails.gemspec
86
+ - lib/docker/rails.rb
87
+ - lib/docker/rails/compose_config.rb
88
+ - lib/docker/rails/version.rb
89
+ homepage: ''
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.4.8
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: A simplified pattern to execute rails applications within Docker (with a
113
+ CI build emphasis)
114
+ test_files: []