conjure 0.2.10 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/History.md +8 -0
  2. data/README.md +35 -76
  3. data/lib/conjure.rb +1 -12
  4. data/lib/conjure/delayed_job.rb +39 -0
  5. data/lib/conjure/digital_ocean/droplet.rb +5 -2
  6. data/lib/conjure/docker/host.rb +75 -0
  7. data/lib/conjure/docker/template.rb +71 -0
  8. data/lib/conjure/instance.rb +31 -76
  9. data/lib/conjure/passenger.rb +123 -0
  10. data/lib/conjure/postgres.rb +67 -0
  11. data/lib/conjure/rails_application.rb +32 -0
  12. data/lib/conjure/server.rb +41 -0
  13. data/lib/conjure/swap.rb +28 -0
  14. data/lib/conjure/{provision/templates → templates}/application-no-ssl.conf.erb +2 -1
  15. data/lib/conjure/{provision/templates → templates}/application-ssl.conf.erb +2 -1
  16. data/lib/conjure/version.rb +1 -1
  17. metadata +12 -41
  18. data/lib/conjure/application.rb +0 -35
  19. data/lib/conjure/command.rb +0 -74
  20. data/lib/conjure/command_target.rb +0 -25
  21. data/lib/conjure/config.rb +0 -44
  22. data/lib/conjure/data_set.rb +0 -7
  23. data/lib/conjure/identity.rb +0 -25
  24. data/lib/conjure/log.rb +0 -26
  25. data/lib/conjure/provider.rb +0 -26
  26. data/lib/conjure/provision.rb +0 -1
  27. data/lib/conjure/provision/docker/host.rb +0 -32
  28. data/lib/conjure/provision/docker/image.rb +0 -55
  29. data/lib/conjure/provision/docker/template.rb +0 -55
  30. data/lib/conjure/provision/instance.rb +0 -52
  31. data/lib/conjure/provision/local_docker.rb +0 -16
  32. data/lib/conjure/provision/passenger.rb +0 -111
  33. data/lib/conjure/provision/postgres.rb +0 -70
  34. data/lib/conjure/provision/server.rb +0 -78
  35. data/lib/conjure/service/cloud_server.rb +0 -112
  36. data/lib/conjure/service/database.rb +0 -25
  37. data/lib/conjure/service/database/mysql.rb +0 -69
  38. data/lib/conjure/service/database/postgres.rb +0 -77
  39. data/lib/conjure/service/digital_ocean_account.rb +0 -31
  40. data/lib/conjure/service/docker_host.rb +0 -259
  41. data/lib/conjure/service/docker_shell.rb +0 -46
  42. data/lib/conjure/service/forwarded_shell.rb +0 -25
  43. data/lib/conjure/service/rails_codebase.rb +0 -67
  44. data/lib/conjure/service/rails_console.rb +0 -10
  45. data/lib/conjure/service/rails_log_view.rb +0 -14
  46. data/lib/conjure/service/rails_server.rb +0 -91
  47. data/lib/conjure/service/rake_task.rb +0 -11
  48. data/lib/conjure/service/remote_file_set.rb +0 -24
  49. data/lib/conjure/service/remote_shell.rb +0 -73
  50. data/lib/conjure/service/repository_link.rb +0 -52
  51. data/lib/conjure/service/volume.rb +0 -28
  52. data/lib/conjure/target.rb +0 -19
  53. data/lib/conjure/view/application_view.rb +0 -42
  54. data/lib/conjure/view/table_view.rb +0 -38
@@ -0,0 +1,123 @@
1
+ require "conjure/docker/template"
2
+ require "erubis"
3
+ require "securerandom"
4
+
5
+ module Conjure
6
+ class Passenger
7
+ def initialize(container_host, options)
8
+ @container_host = container_host
9
+ @database = options[:database]
10
+ @rails_env = options[:rails_env] || "staging"
11
+ @max_upload_mb = options[:max_upload_mb] || 20
12
+ @system_packages = options[:system_packages] || []
13
+ @ruby_version = options[:ruby_version] || "2.2"
14
+ @rubygems_version = options[:rubygems_version]
15
+ @use_ssl = !!options[:ssl_hostname]
16
+ @ssl_hostname = options[:ssl_hostname] || "unknown"
17
+ @services = options[:services] || []
18
+ @system_packages += ["libsqlite3-dev", "libpq-dev"]
19
+ @system_packages += ["libruby#{@ruby_version}", "ruby#{@ruby_version}"]
20
+ @system_packages += @services.flat_map(&:system_packages)
21
+ end
22
+
23
+ def install
24
+ server_template.start(@container_host, "/sbin/my_init", start_options)
25
+ end
26
+
27
+ def pending_files
28
+ return [] unless @use_ssl
29
+ [
30
+ "/etc/ssl/certs/application.crt",
31
+ "/etc/ssl/certs/root_and_intermediates.crt",
32
+ "/etc/ssl/private/application.key",
33
+ "/etc/ssl/dhparam.pem",
34
+ ]
35
+ end
36
+
37
+ private
38
+
39
+ def start_options
40
+ {
41
+ :linked_containers => @database.container_link,
42
+ :name => "passenger",
43
+ :ports => {80 => 80, 443 => 443, 2222 => 22},
44
+ :volumes => {"passenger_data" => "/home/app/application"},
45
+ }
46
+ end
47
+
48
+ def base_docker_image
49
+ {
50
+ "2.2" => "phusion/passenger-ruby22:0.9.18",
51
+ "2.1" => "phusion/passenger-ruby21:0.9.18",
52
+ "2.0" => "phusion/passenger-ruby20:0.9.18",
53
+ "1.9" => "phusion/passenger-ruby19:0.9.18",
54
+ }[@ruby_version] || raise("Unsupported ruby version #{@ruby_version.inspect}")
55
+ end
56
+
57
+ def server_template
58
+ public_key = File.expand_path("~/.ssh/id_rsa.pub")
59
+ raise "Error: ~/.ssh/id_rsa.pub must exist." unless File.exist?(public_key)
60
+ file = Docker::Template.new(base_docker_image)
61
+ file.environment HOME: "/root"
62
+ file.run "rm -f /etc/service/nginx/down /etc/nginx/sites-enabled/default"
63
+ file.run "mkdir -p /home/app/application/shared/bundle/ruby/1.9.0/bin"
64
+ file.run "chown -R app /home/app/application && chmod -R 755 /home/app/application"
65
+ file.run "ln -s /usr/bin/node /home/app/application/shared/bundle/ruby/1.9.0/bin/node"
66
+ file.run apt_command if apt_command
67
+ file.run rubygems_command if rubygems_command
68
+ file.run "passwd -u app"
69
+ file.run "rm -f /etc/service/sshd/down"
70
+ file.add_file public_key, "/root/.ssh/authorized_keys"
71
+ file.add_file public_key, "/home/app/.ssh/authorized_keys"
72
+ file.run "chown app.app /home/app/.ssh/authorized_keys"
73
+ file.run "chown root.root /root/.ssh/authorized_keys"
74
+ file.add_file_data nginx_conf, "/etc/nginx/sites-available/application-no-ssl.conf"
75
+ file.add_file_data nginx_ssl_conf, "/etc/nginx/sites-available/application-ssl.conf"
76
+ which_config = @use_ssl ? "application-ssl" : "application-no-ssl"
77
+ file.run "ln -s /etc/nginx/sites-available/#{which_config}.conf /etc/nginx/sites-enabled/application.conf"
78
+ file.add_file_data database_yml, "/home/app/application/shared/config/database.yml"
79
+ file.add_file_data secrets_yml, "/home/app/application/shared/config/secrets.yml"
80
+ @services.each { |service| service.apply(file) }
81
+ file
82
+ end
83
+
84
+ def apt_command
85
+ if @system_packages.any?
86
+ "apt-get update && apt-get install -y #{@system_packages.join ' '}"
87
+ end
88
+ end
89
+
90
+ def rubygems_command
91
+ if @rubygems_version
92
+ "gem update --system #{@rubygems_version}"
93
+ end
94
+ end
95
+
96
+ def database_yml
97
+ {@rails_env.to_s => @database.rails_config}.to_yaml
98
+ end
99
+
100
+ def secrets_yml
101
+ {@rails_env.to_s => {"secret_key_base" => SecureRandom.hex(64)}}.to_yaml
102
+ end
103
+
104
+ def nginx_conf
105
+ render_template "application-no-ssl.conf"
106
+ end
107
+
108
+ def nginx_ssl_conf
109
+ render_template "application-ssl.conf"
110
+ end
111
+
112
+ def render_template(name)
113
+ template_path = File.join File.dirname(__FILE__), "templates", "#{name}.erb"
114
+ template_data = File.read template_path
115
+ Erubis::Eruby.new(template_data).result(
116
+ :rails_env => @rails_env,
117
+ :ruby_version => @ruby_version,
118
+ :ssl_hostname => @ssl_hostname,
119
+ :max_upload_mb => @max_upload_mb,
120
+ )
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,67 @@
1
+ require "conjure/docker/template"
2
+ require "securerandom"
3
+
4
+ module Conjure
5
+ class Postgres
6
+ def initialize(container_host)
7
+ @container_host = container_host
8
+ @name = "conjure_db_#{SecureRandom.hex 8}"
9
+ @password = new_password
10
+ end
11
+
12
+ def install
13
+ server_template.start(@container_host, "/sbin/my_init", start_options)
14
+ end
15
+
16
+ def rails_config
17
+ {
18
+ "adapter" => "postgresql",
19
+ "database" => @name,
20
+ "host" => container_name,
21
+ "username" => "db",
22
+ "password" => @password,
23
+ "template" => "template0",
24
+ }
25
+ end
26
+
27
+ def container_link
28
+ {container_name => container_name}
29
+ end
30
+
31
+ def pending_files
32
+ []
33
+ end
34
+
35
+ private
36
+
37
+ def start_options
38
+ {
39
+ :name => container_name,
40
+ :volumes => {"postgres_data" => "/var/lib/postgresql/9.3/main"}
41
+ }
42
+ end
43
+
44
+ def container_name
45
+ "postgres"
46
+ end
47
+
48
+ def server_template
49
+ file = Docker::Template.new("atbaker/sd-postgres")
50
+ file.run "useradd db"
51
+ file.run "/sbin/my_init -- /sbin/setuser postgres sh -c \"sleep 1; psql -h localhost -c 'CREATE USER db CREATEDB'\""
52
+ file.run "/sbin/my_init -- /sbin/setuser db sh -c \"sleep 1; createdb db\""
53
+ file.run "echo 'local all all ident' >/usr/local/pgsql/data/pg_hba.conf"
54
+ file.run "echo 'host all all 0.0.0.0/0 md5' >>/usr/local/pgsql/data/pg_hba.conf"
55
+ file.run "echo 'host all all ::1/128 md5' >>/usr/local/pgsql/data/pg_hba.conf"
56
+ file.run "echo \"ALTER USER db PASSWORD '#{@password}'\" >/tmp/setpass"
57
+ file.run "/sbin/my_init -- /sbin/setuser postgres sh -c \"sleep 1; psql -f /tmp/setpass\""
58
+ file.run "rm /tmp/setpass"
59
+ file.run "/sbin/my_init -- /sbin/setuser db sh -c \"sleep 1; /usr/bin/createdb #{@name}\""
60
+ file
61
+ end
62
+
63
+ def new_password
64
+ SecureRandom.urlsafe_base64 20
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,32 @@
1
+ require "conjure/delayed_job"
2
+ require "conjure/postgres"
3
+ require "conjure/passenger"
4
+
5
+ module Conjure
6
+ class RailsApplication
7
+ def initialize(container_host, options)
8
+ @container_host = container_host
9
+ @options = options
10
+ end
11
+
12
+ def install
13
+ components.each(&:install)
14
+ end
15
+
16
+ def pending_files
17
+ components.flat_map(&:pending_files)
18
+ end
19
+
20
+ private
21
+
22
+ def components
23
+ [
24
+ database = Postgres.new(@container_host),
25
+ Passenger.new(@container_host, @options.merge(
26
+ database: database,
27
+ services: [DelayedJob.new(@options)],
28
+ )),
29
+ ]
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,41 @@
1
+ require "conjure/digital_ocean/droplet"
2
+
3
+ module Conjure
4
+ class Server < Struct.new(:ip_address)
5
+ def run(command)
6
+ `ssh #{self.class.ssh_options} root@#{ip_address} #{quote_command command}`
7
+ end
8
+
9
+ def send_file(local_name, remote_name)
10
+ `scp #{self.class.ssh_options} #{local_name} root@#{ip_address}:#{remote_name}`
11
+ end
12
+
13
+ def self.ssh_options
14
+ "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR"
15
+ end
16
+
17
+ def quote_command(command)
18
+ "'" + command.gsub("'", "'\"'\"'") + "'"
19
+ end
20
+
21
+ def self.create(name_prefix, options = {})
22
+ new DigitalOcean::Droplet.new(droplet_options(name_prefix, options)).ip_address
23
+ end
24
+
25
+ def self.droplet_options(name_prefix, options = {})
26
+ {
27
+ image: "docker",
28
+ key_data: key_data,
29
+ name_prefix: name_prefix,
30
+ region: "nyc3",
31
+ size: (options[:instance_size] || "512mb"),
32
+ }
33
+ end
34
+
35
+ def self.key_data
36
+ ssh_dir = File.expand_path "~/.ssh"
37
+ raise "Error: ~/.ssh/id_rsa.pub must exist." unless File.exist?(ssh_dir) && File.exist?("#{ssh_dir}/id_rsa.pub")
38
+ File.read "#{ssh_dir}/id_rsa.pub"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,28 @@
1
+ module Conjure
2
+ class Swap
3
+ def initialize(server)
4
+ @server = server
5
+ end
6
+
7
+ def install
8
+ if exists?
9
+ puts "Swap space detected."
10
+ else
11
+ puts "Swap space not detected, installing..."
12
+ @server.run "dd if=/dev/zero of=/root/swapfile bs=4096 count=524288"
13
+ @server.run "mkswap /root/swapfile; swapon /root/swapfile"
14
+ puts "Swap space installed."
15
+ end
16
+ end
17
+
18
+ def pending_files
19
+ []
20
+ end
21
+
22
+ private
23
+
24
+ def exists?
25
+ @server.run("swapon -s | wc -l").to_i > 1
26
+ end
27
+ end
28
+ end
@@ -3,6 +3,7 @@ server {
3
3
  root /home/app/application/current/public;
4
4
  passenger_enabled on;
5
5
  passenger_user app;
6
- passenger_ruby /usr/bin/ruby2.2;
6
+ passenger_ruby /usr/bin/ruby<%= ruby_version %>;
7
7
  passenger_app_env <%= rails_env %>;
8
+ client_max_body_size <%= max_upload_mb %>M;
8
9
  }
@@ -9,8 +9,9 @@ server {
9
9
  root /home/app/application/current/public;
10
10
  passenger_enabled on;
11
11
  passenger_user app;
12
- passenger_ruby /usr/bin/ruby2.2;
12
+ passenger_ruby /usr/bin/ruby<%= ruby_version %>;
13
13
  passenger_app_env <%= rails_env %>;
14
+ client_max_body_size <%= max_upload_mb %>M;
14
15
 
15
16
  ssl_certificate /etc/ssl/certs/application.crt;
16
17
  ssl_certificate_key /etc/ssl/private/application.key;
@@ -1,3 +1,3 @@
1
1
  module Conjure
2
- VERSION = "0.2.10" unless defined?(VERSION)
2
+ VERSION = "0.3.0" unless defined?(VERSION)
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: conjure
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.10
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-06-24 00:00:00.000000000 Z
12
+ date: 2016-01-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: net-scp
@@ -163,52 +163,23 @@ executables:
163
163
  extensions: []
164
164
  extra_rdoc_files: []
165
165
  files:
166
- - lib/conjure/application.rb
167
- - lib/conjure/command.rb
168
- - lib/conjure/command_target.rb
169
- - lib/conjure/config.rb
170
- - lib/conjure/data_set.rb
166
+ - lib/conjure/delayed_job.rb
171
167
  - lib/conjure/digital_ocean/account.rb
172
168
  - lib/conjure/digital_ocean/droplet.rb
173
169
  - lib/conjure/digital_ocean/key.rb
174
170
  - lib/conjure/digital_ocean/key_set.rb
171
+ - lib/conjure/docker/host.rb
172
+ - lib/conjure/docker/template.rb
175
173
  - lib/conjure/http_request.rb
176
- - lib/conjure/identity.rb
177
174
  - lib/conjure/instance.rb
178
- - lib/conjure/log.rb
179
- - lib/conjure/provider.rb
180
- - lib/conjure/provision/docker/host.rb
181
- - lib/conjure/provision/docker/image.rb
182
- - lib/conjure/provision/docker/template.rb
183
- - lib/conjure/provision/instance.rb
184
- - lib/conjure/provision/local_docker.rb
185
- - lib/conjure/provision/passenger.rb
186
- - lib/conjure/provision/postgres.rb
187
- - lib/conjure/provision/server.rb
188
- - lib/conjure/provision/templates/application-no-ssl.conf.erb
189
- - lib/conjure/provision/templates/application-ssl.conf.erb
190
- - lib/conjure/provision.rb
191
- - lib/conjure/service/cloud_server.rb
192
- - lib/conjure/service/database/mysql.rb
193
- - lib/conjure/service/database/postgres.rb
194
- - lib/conjure/service/database.rb
195
- - lib/conjure/service/digital_ocean_account.rb
196
- - lib/conjure/service/docker_host.rb
197
- - lib/conjure/service/docker_shell.rb
198
- - lib/conjure/service/forwarded_shell.rb
199
- - lib/conjure/service/rails_codebase.rb
200
- - lib/conjure/service/rails_console.rb
201
- - lib/conjure/service/rails_log_view.rb
202
- - lib/conjure/service/rails_server.rb
203
- - lib/conjure/service/rake_task.rb
204
- - lib/conjure/service/remote_file_set.rb
205
- - lib/conjure/service/remote_shell.rb
206
- - lib/conjure/service/repository_link.rb
207
- - lib/conjure/service/volume.rb
208
- - lib/conjure/target.rb
175
+ - lib/conjure/passenger.rb
176
+ - lib/conjure/postgres.rb
177
+ - lib/conjure/rails_application.rb
178
+ - lib/conjure/server.rb
179
+ - lib/conjure/swap.rb
180
+ - lib/conjure/templates/application-no-ssl.conf.erb
181
+ - lib/conjure/templates/application-ssl.conf.erb
209
182
  - lib/conjure/version.rb
210
- - lib/conjure/view/application_view.rb
211
- - lib/conjure/view/table_view.rb
212
183
  - lib/conjure.rb
213
184
  - README.md
214
185
  - History.md
@@ -1,35 +0,0 @@
1
- module Conjure
2
- class Application
3
- attr_reader :origin
4
-
5
- def self.find(options = {})
6
- new(options)
7
- end
8
-
9
- def instances
10
- Instance.where(:origin => @origin)
11
- end
12
-
13
- def data_sets
14
- DataSet.find(:origin => @origin)
15
- end
16
-
17
- def name
18
- match = @origin.match(/\/([^.]+)\.git$/) if @origin
19
- match[1] if match
20
- end
21
-
22
- private
23
-
24
- def initialize(options = {})
25
- @origin = options[:origin] || origin_from_path(options[:path])
26
- end
27
-
28
- def origin_from_path(path)
29
- return unless path
30
- remote_info = `cd #{path}; git remote -v |grep origin`
31
- match = remote_info.match(/(git@github.com[^ ]+)/)
32
- match[1] if match
33
- end
34
- end
35
- end
@@ -1,74 +0,0 @@
1
- require "thor"
2
-
3
- module Conjure
4
- class Command < Thor
5
- class_option :verbose, :aliases => "-v", :type => :boolean, :desc => "Show details of low-level operations for debugging"
6
- def initialize(*args)
7
- super
8
- Log.level = :debug if options[:verbose]
9
- end
10
-
11
- desc "create", "Create and deploy a new instance of the application"
12
- method_option :branch, :aliases => "-b", :type => :string, :desc => "Specify branch to deploy, default 'master'"
13
- method_option :origin, :type => :string, :desc => "Specify git URL to deploy from"
14
- method_option :rails_env, :type => :string, :desc => "Specify the Rails environment, default 'production'"
15
- def create
16
- target.new_instance.create
17
- end
18
-
19
- desc "deploy", "Deploy the app"
20
- method_option :branch, :aliases => "-b", :type => :string, :desc => "Specify branch to deploy, default 'master'"
21
- method_option :origin, :type => :string, :desc => "Specify git URL to deploy from"
22
- method_option :rails_env, :type => :string, :desc => "Specify the Rails environment, default 'production'"
23
- def deploy
24
- (target.existing_instance || target.new_instance).deploy
25
- end
26
-
27
- desc "import FILE", "Import the production database from a postgres SQL dump"
28
- def import(file)
29
- target.existing_instance.database.import file
30
- end
31
-
32
- desc "export FILE", "Export the production database to a postgres SQL dump"
33
- def export(file)
34
- target.existing_instance.database.export file
35
- end
36
-
37
- desc "log", "Display the Rails log from the deployed application"
38
- method_option :num, :aliases => "-n", :type => :numeric, :default => 10, :desc => "Show N lines of output"
39
- method_option :tail, :aliases => "-t", :type => :boolean, :desc => "Continue streaming new log entries"
40
- def log
41
- Service::RailsLogView.new(:shell => target.existing_instance.shell, :rails_env => target.existing_instance.rails_env, :lines => options[:num], :tail => options[:tail]) do |stdout|
42
- print stdout
43
- end
44
- end
45
-
46
- desc "rake [ARGUMENTS...]", "Run the specified rake task on the deployed application"
47
- def rake(*arguments)
48
- task = arguments.join(" ")
49
- Service::RakeTask.new(:task => task, :shell => target.existing_instance.shell) do |stdout|
50
- print stdout
51
- end
52
- end
53
-
54
- desc "console", "Start a console on the deployed application"
55
- def console
56
- Service::RailsConsole.new(:shell => target.existing_instance.shell) do |stdout|
57
- print stdout
58
- end
59
- end
60
-
61
- desc "show", "Show info on deployed instances"
62
- def show
63
- puts View::ApplicationView.new(target.application).render
64
- end
65
-
66
- default_task :help
67
-
68
- private
69
-
70
- def target
71
- CommandTarget.new(options)
72
- end
73
- end
74
- end