conjure 0.1.8 → 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.
data/History.md CHANGED
@@ -1,3 +1,9 @@
1
+ ### Version 0.2.0
2
+ 2014-3-18
3
+
4
+ * Add source files for published Docker images
5
+ * Experimental API for programmatic provisioning
6
+
1
7
  ### Version 0.1.8
2
8
  2014-1-24
3
9
 
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
  [![Gem Version](https://badge.fury.io/rb/conjure.png)](http://badge.fury.io/rb/conjure)
3
3
  [![Build Status](https://travis-ci.org/brianauton/conjure.png?branch=master)](https://travis-ci.org/brianauton/conjure)
4
4
  [![Code Climate](https://codeclimate.com/github/brianauton/conjure.png)](https://codeclimate.com/github/brianauton/conjure)
5
+ [![Dependency Status](https://gemnasium.com/brianauton/conjure.png)](https://gemnasium.com/brianauton/conjure)
6
+
5
7
 
6
8
  Magically powerful deployment for Rails applications.
7
9
 
@@ -7,11 +7,11 @@ module Conjure
7
7
  end
8
8
 
9
9
  def instances
10
- Instance.where(:application => self)
10
+ Instance.where(:origin => @origin)
11
11
  end
12
12
 
13
13
  def data_sets
14
- DataSet.find(:application => self)
14
+ DataSet.find(:origin => @origin)
15
15
  end
16
16
 
17
17
  def name
@@ -8,29 +8,37 @@ module Conjure
8
8
  Log.level = :debug if options[:verbose]
9
9
  end
10
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
+
11
19
  desc "deploy", "Deploy the app"
12
- method_option :branch, :aliases => "-b", :type => :string, :desc => "Specify branch to deploy"
13
- method_option :test, :type => :boolean, :desc => "Describe the deploy settings but don't deploy"
20
+ method_option :branch, :aliases => "-b", :type => :string, :desc => "Specify branch to deploy, default 'master'"
14
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'"
15
23
  def deploy
16
- deployment.deploy
24
+ (target.existing_instance || target.new_instance).deploy
17
25
  end
18
26
 
19
27
  desc "import FILE", "Import the production database from a postgres SQL dump"
20
28
  def import(file)
21
- deployment.database.import file
29
+ target.existing_instance.database.import file
22
30
  end
23
31
 
24
32
  desc "export FILE", "Export the production database to a postgres SQL dump"
25
33
  def export(file)
26
- deployment.database.export file
34
+ target.existing_instance.database.export file
27
35
  end
28
36
 
29
37
  desc "log", "Display the Rails log from the deployed application"
30
38
  method_option :num, :aliases => "-n", :type => :numeric, :default => 10, :desc => "Show N lines of output"
31
39
  method_option :tail, :aliases => "-t", :type => :boolean, :desc => "Continue streaming new log entries"
32
40
  def log
33
- Service::RailsLogView.new(:shell => deployment.shell, :lines => options[:num], :tail => options[:tail]) do |stdout|
41
+ Service::RailsLogView.new(:shell => target.existing_instance.shell, :rails_env => target.existing_instance.rails_env, :lines => options[:num], :tail => options[:tail]) do |stdout|
34
42
  print stdout
35
43
  end
36
44
  end
@@ -38,42 +46,29 @@ module Conjure
38
46
  desc "rake [ARGUMENTS...]", "Run the specified rake task on the deployed application"
39
47
  def rake(*arguments)
40
48
  task = arguments.join(" ")
41
- Service::RakeTask.new(:task => task, :shell => deployment.shell) do |stdout|
49
+ Service::RakeTask.new(:task => task, :shell => target.existing_instance.shell) do |stdout|
42
50
  print stdout
43
51
  end
44
52
  end
45
53
 
46
54
  desc "console", "Start a console on the deployed application"
47
55
  def console
48
- Service::RailsConsole.new(:shell => deployment.shell) do |stdout|
56
+ Service::RailsConsole.new(:shell => target.existing_instance.shell) do |stdout|
49
57
  print stdout
50
58
  end
51
59
  end
52
60
 
53
61
  desc "show", "Show info on deployed instances"
54
62
  def show
55
- puts View::ApplicationView.new(application).render
63
+ puts View::ApplicationView.new(target.application).render
56
64
  end
57
65
 
58
66
  default_task :help
59
67
 
60
68
  private
61
69
 
62
- def application
63
- @application ||= Application.find(:path => Dir.pwd, :origin => options[:origin])
64
- end
65
-
66
- def deployment
67
- @deployment ||= Service::RailsDeployment.new({
68
- :branch => options[:branch],
69
- :origin => application.origin,
70
- :target => target,
71
- :test => options[:test],
72
- })
73
- end
74
-
75
70
  def target
76
- Target.new(:machine_name => "#{application.name}-production")
71
+ CommandTarget.new(options)
77
72
  end
78
73
  end
79
74
  end
@@ -0,0 +1,25 @@
1
+ module Conjure
2
+ class CommandTarget
3
+ def initialize(options = {})
4
+ @origin = options[:origin]
5
+ @branch = options[:branch] || "master"
6
+ @rails_env = options[:rails_env] || "production"
7
+ end
8
+
9
+ def application
10
+ @application ||= Application.find(:path => Dir.pwd, :origin => @origin)
11
+ end
12
+
13
+ def existing_instance
14
+ @existing_instance ||= application.instances.first
15
+ end
16
+
17
+ def new_instance
18
+ @new_instance ||= Instance.new(
19
+ :origin => application.origin,
20
+ :branch => @branch,
21
+ :rails_environment => @rails_env,
22
+ )
23
+ end
24
+ end
25
+ end
@@ -1,39 +1,100 @@
1
1
  module Conjure
2
2
  class Instance
3
- attr_reader :application, :ip_address, :rails_environment
4
-
5
3
  def initialize(options)
6
- @application = options[:application]
7
- @ip_address = options[:ip_address]
4
+ @origin = options[:origin]
5
+ @branch = options[:branch]
8
6
  @rails_environment = options[:rails_environment]
7
+ @server = options[:server]
9
8
  end
10
9
 
11
10
  def self.where(options = {})
12
11
  Collection.new(options)
13
12
  end
14
13
 
14
+ def origin
15
+ @origin ||= @server.name.split("-")[0]
16
+ end
17
+
18
+ def rails_environment
19
+ @rails_environment ||= @server.name.split("-")[1]
20
+ end
21
+
22
+ def ip_address
23
+ @server.ip_address
24
+ end
25
+
26
+ def shell
27
+ rails_server.base_image
28
+ end
29
+
30
+ def branch
31
+ @branch ||= codebase.branch
32
+ end
33
+
34
+ def database
35
+ codebase.database
36
+ end
37
+
38
+ def create
39
+ @server_name = Service::CloudServer.ensure_unique_name(server_name)
40
+ deploy
41
+ end
42
+
43
+ def deploy
44
+ Log.info "[deploy] Deploying #{branch} to #{rails_environment}"
45
+ codebase.install
46
+ rails_server.run
47
+ Log.info "[deploy] Application deployed to #{ip_address}"
48
+ end
49
+
50
+ def codebase
51
+ @codebase ||= Service::RailsCodebase.new target, origin, @branch, rails_environment
52
+ end
53
+
54
+ def rails_server
55
+ @rails_server ||= Service::RailsServer.new target, rails_environment
56
+ end
57
+
58
+ def server_name
59
+ @server_name ||= "#{application_name}-#{rails_environment}"
60
+ end
61
+
62
+ def server
63
+ @server ||= Service::CloudServer.new(server_name)
64
+ end
65
+
66
+ def target
67
+ @target ||= Target.new(:machine_name => server.name)
68
+ end
69
+
70
+ def application_name
71
+ Application.new(:origin => @origin).name
72
+ end
73
+
15
74
  def status
16
75
  "running"
17
76
  end
18
77
 
78
+ def name
79
+ server.name
80
+ end
81
+
19
82
  class Collection
20
83
  include Enumerable
21
84
 
22
85
  def initialize(options)
23
- @application = options[:application]
86
+ @origin = options[:origin]
24
87
  end
25
88
 
26
- def server
27
- @server ||= Service::CloudServer.new("#{@application.name}-production") if @application
89
+ def application_name
90
+ Application.new(:origin => @origin).name
28
91
  end
29
92
 
30
93
  def each(&block)
31
- if server and server.existing_server
32
- yield Instance.new(
33
- :application => @application,
34
- :rails_environment => "production",
35
- :ip_address => server.ip_address
36
- )
94
+ return unless @origin
95
+ Service::CloudServer.each_with_name_prefix("#{application_name}-") do |server|
96
+ match = server.name.match(/^#{application_name}-([^-]+)(-[0-9]+)?$/)
97
+ yield Instance.new(:server => server) if match
37
98
  end
38
99
  end
39
100
  end
@@ -1,9 +1,61 @@
1
+ ==== New hook-based architecture:
2
+ 1. Proxy app, binds to machine's ports and proxies traffic to other containers
3
+ 2. Deployer app, builds and starts revisions of the app in other containers
4
+ - Recieves github push notification, builds a new container with the new app revision
5
+ - Uses a custom-built Dockerfile for the app, designed to rebuild with minimal i/o
6
+ - After new revision is verified up and running, notifies the proxy to redirect
7
+ - Could run a small capybara smoke test against the production app
8
+ - Stops old container. Destroys it? (Rebuilding it should be fast if needed)
9
+ 3. The application itself
10
+ ====
11
+
12
+ 1. Add area in conjure repo to build and publish docker images
13
+ - publish "conjure/passenger-ruby21"
14
+ - publish "conjure/postgres"
15
+ 2. Add isolated conjure commands to work with custom docker images:
16
+ - "provision [rails-env]"
17
+ - Creates DO server using DIGITAL_OCEAN_* env vars
18
+ - Starts conjure/postgres
19
+ - Installs shared database.yml
20
+ - Adds 'passenger_app_env ENV' to http{} section of nginx.conf
21
+ - Starts conjure/passenger-ruby21 linked to postgres
22
+ - Displays needed Cap info: ip, ssh port, deploy user, app path, rails_env
23
+ - "addkey [ip] [sshport]"
24
+ 3. Add conjure to client app & run "provision staging"
25
+ 4. Add Cap to client app and configure staging settings
26
+ 5. "cap staging deploy" from client app
27
+
28
+ ==== Steps to deploy w/ Cap:
29
+
30
+ Dockerfile (webserver):
31
+ Install passenger/nginx
32
+ Install likely gem dependencies incl. node
33
+ Install required ruby version
34
+ Dockerfile (postgres):
35
+
36
+ Conjure:
37
+ Provision DigitalOcean Docker instance
38
+
39
+ Postgres:
40
+ Start postgres container
41
+
42
+ Passenger:
43
+ Start webserver container linked to postgres container
44
+ Install public key in webserver container
45
+ Install nginx config for app
46
+ Install shared database.yml
47
+
48
+ Script in app:
49
+ Capistrano:
50
+ Deploy current app version
51
+
52
+
1
53
  TASKS:
2
54
  Remove "label" from prepared shell creation
3
55
  Alternate way to show per-service progress output
4
56
  Log creation and status from each service
5
57
  Log name and IP from docker container creation in verbose mode only
6
- Separate resources vs services
58
+ Separate platforms vs services
7
59
  Deployment.deploy should become Deployment.create
8
60
 
9
61
  Instead of passing DockerHost objects around, pass something that
@@ -51,10 +103,10 @@ But where do the database, logger, cron jobs, and other required
51
103
  services get identified and instantiated?
52
104
 
53
105
  Conjure's basic job is: deploy [something] [somewhere] using
54
- [resources]. "Something" is a Service, of which Instance and DataSet
106
+ [platforms]. "Something" is a Service, of which Instance and DataSet
55
107
  are both types. "Somewhere" is the desired interface over which the
56
- new Service will be accessible. "Resource" is the cloud service, local
57
- VMs, and other combined resources that will be used to do the
108
+ new Service will be accessible. "Platform" is the cloud service, local
109
+ VMs, and other combined platforms that will be used to do the
58
110
  deployment.
59
111
 
60
112
  Need a concept for something that has both stdin and stdout (and
@@ -93,7 +145,6 @@ supported), or rails_env. Need "deploy" to be a shorthand for either
93
145
  already a staging instance. Maybe "deploy" should be deprecated?
94
146
 
95
147
  Needs:
96
- Route all current commands through instances
97
148
  Separate commands to "create" and "update" instances
98
149
  Support specific env/branch when creating
99
150
  Preserve existing env/branch when updating
@@ -104,4 +155,47 @@ Needs:
104
155
  Deploy to local docker
105
156
  Deploy to vagrant (?)
106
157
  Progress display
158
+ Allow specifying SHA for deployment (pull out into object with repo+branch?)
107
159
  Used stored docker images for faster deployment
160
+
161
+
162
+ New Refactor (POODR)
163
+
164
+ Application-related models:
165
+ Application - All revisions of the app in abstract (a repo URL for now)
166
+ ApplicationVersion - Refers to a specific repo/branch/revision
167
+ ApplicationSource - Unpacked into a file system for reading/writing
168
+
169
+ UserPreferredPlatform - used by Command. Takes all user
170
+ configuration (command line switches, config file, etc) and produces
171
+ a Platform used to provision services.
172
+
173
+ InstanceFactory - takes a Platform and an ApplicationRevision to deploy on it.
174
+ queries [something] to ask which services the ApplicationRevision needs.
175
+ queries Instance to create it with the services
176
+ Instance - associates an ApplicationVersion with multiple Services that support it.
177
+ traverses the services to find certain kinds (web service, database)
178
+
179
+
180
+ Acceptance specs that drive Command and watch for specific output
181
+ Unit specs for Command that expect messages to collaborators
182
+
183
+
184
+ [binary]
185
+ constructs Command with a CommandResultView
186
+
187
+ Command
188
+ takes status_view (via constant)
189
+ sends output to status_view
190
+
191
+ StatusView
192
+ takes various *View classes for rendering different types
193
+ takes output_stream
194
+ sends output_stream and object for each object rendered
195
+ sends text output directly to output_stream
196
+
197
+ ApplicationView
198
+ takes output_stream and application
199
+
200
+ Cloud server infrastructure tips:
201
+ http://wblinks.com/notes/aws-tips-i-wish-id-known-before-i-started/
@@ -0,0 +1 @@
1
+ require "conjure/provision/instance"
@@ -0,0 +1,20 @@
1
+ module Conjure
2
+ module Provision
3
+ class DockerImage
4
+ attr_reader :image_name
5
+
6
+ def initialize(server, base_image_name)
7
+ @server = server
8
+ @image_name = base_image_name
9
+ end
10
+
11
+ def start(shell_command, options = {})
12
+ container_id = @server.run("docker run -d #{options[:run_options].to_s} #{image_name} #{shell_command}").strip
13
+ sleep 2
14
+ ip_address = @server.run("docker inspect -format '{{ .NetworkSettings.IPAddress }}' #{container_id}").strip
15
+ raise "Container failed to start" unless ip_address.present?
16
+ ip_address
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,62 @@
1
+ require "conjure/provision/docker_image"
2
+
3
+ module Conjure
4
+ module Provision
5
+ class Dockerfile
6
+ def initialize(base_image_name)
7
+ @commands = ["FROM #{base_image_name}"]
8
+ @file_data = {}
9
+ end
10
+
11
+ def add_file(filename, remote_name)
12
+ add_file_data File.read(filename), remote_name
13
+ end
14
+
15
+ def add_file_data(data, remote_name)
16
+ local_name = "file#{@file_data.length+1}"
17
+ @file_data[local_name] = data
18
+ @commands << "ADD #{local_name} #{remote_name}"
19
+ end
20
+
21
+ def run(command)
22
+ @commands << "RUN #{command}"
23
+ end
24
+
25
+ def source
26
+ @commands.join "\n"
27
+ end
28
+
29
+ def prepare_build_directory(&block)
30
+ Dir.mktmpdir do |dir|
31
+ @file_data.merge("Dockerfile" => source).each do |filename, data|
32
+ File.write "#{dir}/#{filename}", data
33
+ end
34
+ yield dir
35
+ end
36
+ end
37
+
38
+ def upload_build_directory(server, &block)
39
+ archive = "/tmp/dockerfile.tar.gz"
40
+ build_dir = "/tmp/docker_build"
41
+ prepare_build_directory do |dir|
42
+ `cd #{dir}; tar czf #{archive} *`
43
+ server.send_file archive, "dockerfile.tar.gz"
44
+ server.run "mkdir #{build_dir}; cd #{build_dir}; tar xzf ~/dockerfile.tar.gz"
45
+ result = yield "/tmp/docker_build"
46
+ server.run "rm -Rf #{build_dir} ~/dockerfile.tar.gz"
47
+ `rm #{archive}`
48
+ result
49
+ end
50
+ end
51
+
52
+ def build(server)
53
+ result = upload_build_directory(server) { |dir| server.run "docker build #{dir}" }
54
+ if match = result.match(/Successfully built ([0-9a-z]+)/)
55
+ DockerImage.new server, match[1]
56
+ else
57
+ raise "Failed to build Docker image, output was #{result}"
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,72 @@
1
+ require "conjure/provision/dockerfile"
2
+
3
+ module Conjure
4
+ module Provision
5
+ class Instance
6
+ def initialize(app_name, rails_env)
7
+ @app_name = app_name
8
+ @rails_env = rails_env
9
+ end
10
+
11
+ def provision
12
+ server = Server.create "#{@app_name}-#{@rails_env}"
13
+
14
+ db_password = new_db_password
15
+ postgres_image = postgres_dockerfile(db_password).build(server)
16
+ db_ip_address = postgres_image.start("/sbin/my_init")
17
+
18
+ passenger_image = passenger_dockerfile(db_ip_address, db_password).build(server)
19
+ passenger_image.start("/sbin/my_init", :run_options => "-p 80:80 -p 2222:22")
20
+
21
+ host = "root@#{server.ip_address} -p 2222"
22
+ remote_command host, "/etc/init.d/nginx restart"
23
+ {
24
+ :ip_address => server.ip_address,
25
+ :port => "2222",
26
+ :user => "app",
27
+ :rails_env => @rails_env
28
+ }
29
+ end
30
+
31
+ def postgres_dockerfile(db_password)
32
+ file = Dockerfile.new("conjure/postgres93:1.0.0")
33
+ file.run "echo \"ALTER USER db PASSWORD '#{db_password}'\" >/tmp/setpass"
34
+ file.run "/sbin/my_init -- /sbin/setuser postgres sh -c \"sleep 1; psql -f /tmp/setpass\""
35
+ file.run "rm /tmp/setpass"
36
+ file
37
+ end
38
+
39
+ def passenger_dockerfile(db_ip_address, db_password)
40
+ public_key = File.expand_path("~/.ssh/id_rsa.pub")
41
+ raise "Error: ~/.ssh/id_rsa.pub must exist." unless File.exist?(public_key)
42
+ file = Dockerfile.new("conjure/passenger-ruby21:1.0.0")
43
+ file.add_file public_key, "/root/.ssh/authorized_keys"
44
+ file.add_file public_key, "/home/app/.ssh/authorized_keys"
45
+ file.run "chown app.app /home/app/.ssh/authorized_keys"
46
+ file.run "chown root.root /root/.ssh/authorized_keys"
47
+ file.add_file_data application_conf, "/etc/nginx/sites-enabled/application.conf"
48
+ file.add_file_data database_yml(db_ip_address, db_password), "/home/app/application/shared/config/database.yml"
49
+ file
50
+ end
51
+
52
+ def new_db_password
53
+ require "securerandom"
54
+ SecureRandom.urlsafe_base64 20
55
+ end
56
+
57
+ def database_yml(db_ip_address, db_password)
58
+ require "yaml"
59
+ {@rails_env => {"adapter" => "sqlite3", "database" => "db/#{@rails_env}.sqlite3", "host" => db_ip_address, "username" => "db", "password" => db_password}}.to_yaml
60
+ end
61
+
62
+ def application_conf
63
+ options = {listen: "80", root: "/home/app/application/current/public", passenger_enabled: "on", passenger_user: "app", passenger_ruby: "/usr/bin/ruby2.1", passenger_app_env: @rails_env}
64
+ "server {" + options.map{|k, v| " #{k} #{v};"}.join("\n") + "}\n"
65
+ end
66
+
67
+ def remote_command(host, command)
68
+ `ssh -o StrictHostKeyChecking=no #{host} #{command}`
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,64 @@
1
+ module Conjure
2
+ module Provision
3
+ class Server
4
+ def initialize(server)
5
+ @server = server
6
+ puts "Configuring droplet..."
7
+ install_swap
8
+ end
9
+
10
+ def ip_address
11
+ @server.public_ip_address
12
+ end
13
+
14
+ def run(command)
15
+ `ssh #{ssh_options} root@#{ip_address} '#{shell_escape_single_quotes command}'`
16
+ end
17
+
18
+ def send_file(local_name, remote_name)
19
+ `scp #{ssh_options} #{local_name} root@#{ip_address}:#{remote_name}`
20
+ end
21
+
22
+ def ssh_options
23
+ "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
24
+ end
25
+
26
+ def shell_escape_single_quotes(command)
27
+ command.gsub("'", "'\"'\"'")
28
+ end
29
+
30
+ def install_swap
31
+ run "dd if=/dev/zero of=/root/swapfile bs=1024 count=524288"
32
+ run "mkswap /root/swapfile; swapon /root/swapfile"
33
+ end
34
+
35
+ def self.create(name)
36
+ puts "Creating DigitalOcean droplet..."
37
+ connection = Fog::Compute.new compute_options
38
+ new connection.servers.bootstrap(bootstrap_options name)
39
+ end
40
+
41
+ def self.compute_options
42
+ raise "Error: DIGITALOCEAN_API_KEY and DIGITALOCEAN_CLIENT_ID env vars must both be set." unless ENV["DIGITALOCEAN_API_KEY"] && ENV["DIGITALOCEAN_CLIENT_ID"]
43
+ {
44
+ :provider => :digitalocean,
45
+ :digitalocean_api_key => ENV["DIGITALOCEAN_API_KEY"],
46
+ :digitalocean_client_id => ENV["DIGITALOCEAN_CLIENT_ID"],
47
+ }
48
+ end
49
+
50
+ def self.bootstrap_options(name)
51
+ ssh_dir = File.expand_path("~/.ssh")
52
+ raise "Error: ~/.ssh/id_rsa and ~/.ssh/id_rsa.pub must exist." unless File.exist?(ssh_dir) && File.exist?("#{ssh_dir}/id_rsa") && File.exist?("#{ssh_dir}/id_rsa.pub")
53
+ {
54
+ :name => name,
55
+ :flavor_id => "66",
56
+ :region_id => "4",
57
+ :image_id => "2158507",
58
+ :private_key_path => "#{ssh_dir}/id_rsa",
59
+ :public_key_path => "#{ssh_dir}/id_rsa.pub",
60
+ }
61
+ end
62
+ end
63
+ end
64
+ end
@@ -3,6 +3,7 @@ module Conjure
3
3
  class CloudServer
4
4
  require "fog"
5
5
  require "pathname"
6
+ attr_reader :name
6
7
 
7
8
  def initialize(name)
8
9
  @name = name
@@ -32,6 +33,30 @@ module Conjure
32
33
  @existing_server ||= connection.servers.find{|s| s.name == @name } if connection
33
34
  end
34
35
 
36
+ def self.connection
37
+ new("").connection
38
+ end
39
+
40
+ def self.each_with_name_prefix(prefix, &block)
41
+ return unless connection
42
+ connection.servers.all.select{|s| s.name.match(/^#{prefix}/)}.each do |server|
43
+ block.call new(server.name)
44
+ end
45
+ end
46
+
47
+ def self.ensure_unique_name(name)
48
+ return name unless connection
49
+ existing_names = connection.servers.all.map{ |s| s.name }
50
+ name = increment_numeric_suffix(name) while existing_names.include? name
51
+ name
52
+ end
53
+
54
+ def self.increment_numeric_suffix(name)
55
+ parts = name.split("-")
56
+ parts[2] = parts[2] ? ((parts[2].to_i)+1).to_s : "2"
57
+ parts.join("-")
58
+ end
59
+
35
60
  def new_server
36
61
  Log.info " [cloud] Launching new server #{@name}"
37
62
  bootstrap_options = account.bootstrap_options.merge(:name => @name)
@@ -21,6 +21,10 @@ module Conjure
21
21
  }.to_yaml
22
22
  end
23
23
 
24
+ def branch
25
+ @branch ||= repository_link.branch
26
+ end
27
+
24
28
  def install
25
29
  repository_link.update
26
30
  configure_database
@@ -5,7 +5,7 @@ module Conjure
5
5
  arguments = []
6
6
  arguments << "-n #{options[:lines]}" if options[:lines]
7
7
  arguments << "-f" if options[:tail]
8
- arguments << "application_root/log/production.log"
8
+ arguments << "application_root/log/#{options[:rails_env]}.log"
9
9
  options[:shell].command("tail #{arguments.join ' '}", &block)
10
10
  rescue Interrupt
11
11
  end
@@ -12,6 +12,10 @@ module Conjure
12
12
  code_checked_out ? fetch_code_updates : checkout_code
13
13
  end
14
14
 
15
+ def branch
16
+ @branch ||= git_shell.command("cd #{code_path}; git rev-parse --abbrev-ref HEAD").strip
17
+ end
18
+
15
19
  private
16
20
 
17
21
  def code_checked_out
@@ -1,3 +1,3 @@
1
1
  module Conjure
2
- VERSION = "0.1.8" unless defined?(VERSION)
2
+ VERSION = "0.2.0" unless defined?(VERSION)
3
3
  end
@@ -30,9 +30,9 @@ module Conjure
30
30
  def instances_table
31
31
  data = @instances.map do |instance|
32
32
  {
33
- "Address" => instance.ip_address,
34
- "Environment" => instance.rails_environment,
33
+ "Name" => instance.name,
35
34
  "Status" => instance.status,
35
+ "Address" => instance.ip_address,
36
36
  }
37
37
  end
38
38
  TableView.new(data).render
metadata CHANGED
@@ -1,18 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: conjure
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.2.0
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Brian Auton
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2014-01-24 00:00:00.000000000 Z
12
+ date: 2014-03-19 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: fog
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
19
  - - ">="
18
20
  - !ruby/object:Gem::Version
@@ -20,6 +22,7 @@ dependencies:
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
@@ -27,6 +30,7 @@ dependencies:
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: thor
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
35
  - - ">="
32
36
  - !ruby/object:Gem::Version
@@ -34,6 +38,7 @@ dependencies:
34
38
  type: :runtime
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
43
  - - ">="
39
44
  - !ruby/object:Gem::Version
@@ -41,6 +46,7 @@ dependencies:
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: unf
43
48
  requirement: !ruby/object:Gem::Requirement
49
+ none: false
44
50
  requirements:
45
51
  - - ">="
46
52
  - !ruby/object:Gem::Version
@@ -48,6 +54,7 @@ dependencies:
48
54
  type: :runtime
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
51
58
  requirements:
52
59
  - - ">="
53
60
  - !ruby/object:Gem::Version
@@ -55,6 +62,7 @@ dependencies:
55
62
  - !ruby/object:Gem::Dependency
56
63
  name: guard-rspec
57
64
  requirement: !ruby/object:Gem::Requirement
65
+ none: false
58
66
  requirements:
59
67
  - - ">="
60
68
  - !ruby/object:Gem::Version
@@ -62,6 +70,7 @@ dependencies:
62
70
  type: :development
63
71
  prerelease: false
64
72
  version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
65
74
  requirements:
66
75
  - - ">="
67
76
  - !ruby/object:Gem::Version
@@ -69,20 +78,23 @@ dependencies:
69
78
  - !ruby/object:Gem::Dependency
70
79
  name: rspec
71
80
  requirement: !ruby/object:Gem::Requirement
81
+ none: false
72
82
  requirements:
73
83
  - - ">="
74
84
  - !ruby/object:Gem::Version
75
- version: 3.0.0.beta1
85
+ version: 3.0.0.beta2
76
86
  type: :development
77
87
  prerelease: false
78
88
  version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
79
90
  requirements:
80
91
  - - ">="
81
92
  - !ruby/object:Gem::Version
82
- version: 3.0.0.beta1
93
+ version: 3.0.0.beta2
83
94
  - !ruby/object:Gem::Dependency
84
95
  name: rake
85
96
  requirement: !ruby/object:Gem::Requirement
97
+ none: false
86
98
  requirements:
87
99
  - - ">="
88
100
  - !ruby/object:Gem::Version
@@ -90,6 +102,7 @@ dependencies:
90
102
  type: :development
91
103
  prerelease: false
92
104
  version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
93
106
  requirements:
94
107
  - - ">="
95
108
  - !ruby/object:Gem::Version
@@ -102,64 +115,70 @@ executables:
102
115
  extensions: []
103
116
  extra_rdoc_files: []
104
117
  files:
105
- - History.md
106
- - License.txt
107
- - README.md
108
- - bin/conjure
109
118
  - lib/conjure.rb
119
+ - lib/conjure/instance.rb
120
+ - lib/conjure/target.rb
121
+ - lib/conjure/provider.rb
110
122
  - lib/conjure/application.rb
111
- - lib/conjure/command.rb
123
+ - lib/conjure/command_target.rb
124
+ - lib/conjure/log.rb
125
+ - lib/conjure/version.rb
112
126
  - lib/conjure/config.rb
113
- - lib/conjure/data_set.rb
114
127
  - lib/conjure/identity.rb
115
- - lib/conjure/instance.rb
116
- - lib/conjure/log.rb
117
- - lib/conjure/notes.txt
118
- - lib/conjure/provider.rb
119
- - lib/conjure/service/cloud_server.rb
120
- - lib/conjure/service/database.rb
121
- - lib/conjure/service/database/mysql.rb
122
- - lib/conjure/service/database/postgres.rb
128
+ - lib/conjure/provision/server.rb
129
+ - lib/conjure/provision/instance.rb
130
+ - lib/conjure/provision/docker_image.rb
131
+ - lib/conjure/provision/dockerfile.rb
132
+ - lib/conjure/command.rb
133
+ - lib/conjure/data_set.rb
123
134
  - lib/conjure/service/digital_ocean_account.rb
124
- - lib/conjure/service/docker_host.rb
125
- - lib/conjure/service/docker_shell.rb
126
- - lib/conjure/service/forwarded_shell.rb
127
135
  - lib/conjure/service/rails_codebase.rb
128
- - lib/conjure/service/rails_console.rb
129
- - lib/conjure/service/rails_deployment.rb
136
+ - lib/conjure/service/database/postgres.rb
137
+ - lib/conjure/service/database/mysql.rb
130
138
  - lib/conjure/service/rails_log_view.rb
139
+ - lib/conjure/service/docker_host.rb
140
+ - lib/conjure/service/database.rb
141
+ - lib/conjure/service/cloud_server.rb
142
+ - lib/conjure/service/forwarded_shell.rb
131
143
  - lib/conjure/service/rails_server.rb
132
- - lib/conjure/service/rake_task.rb
133
144
  - lib/conjure/service/remote_file_set.rb
134
145
  - lib/conjure/service/remote_shell.rb
146
+ - lib/conjure/service/rails_console.rb
147
+ - lib/conjure/service/rake_task.rb
148
+ - lib/conjure/service/docker_shell.rb
135
149
  - lib/conjure/service/repository_link.rb
136
150
  - lib/conjure/service/volume.rb
137
- - lib/conjure/target.rb
138
- - lib/conjure/version.rb
139
- - lib/conjure/view/application_view.rb
151
+ - lib/conjure/provision.rb
152
+ - lib/conjure/notes.txt
140
153
  - lib/conjure/view/table_view.rb
154
+ - lib/conjure/view/application_view.rb
155
+ - README.md
156
+ - History.md
157
+ - License.txt
158
+ - bin/conjure
141
159
  homepage: http://github.com/brianauton/conjure
142
160
  licenses:
143
161
  - MIT
144
- metadata: {}
145
162
  post_install_message:
146
163
  rdoc_options: []
147
164
  require_paths:
148
165
  - lib
149
166
  required_ruby_version: !ruby/object:Gem::Requirement
167
+ none: false
150
168
  requirements:
151
169
  - - ">="
152
170
  - !ruby/object:Gem::Version
153
171
  version: '0'
154
172
  required_rubygems_version: !ruby/object:Gem::Requirement
173
+ none: false
155
174
  requirements:
156
175
  - - ">="
157
176
  - !ruby/object:Gem::Version
158
177
  version: 1.3.6
159
178
  requirements: []
160
179
  rubyforge_project:
161
- rubygems_version: 2.2.1
180
+ rubygems_version: 1.8.28
162
181
  signing_key:
163
- specification_version: 4
182
+ specification_version: 3
164
183
  summary: Magically powerful deployment for Rails applications
165
184
  test_files: []
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: f83195bdf15bdf55eaa8d3ce3bb10ac1dae0caad
4
- data.tar.gz: 37098f28d23149e7aebb192adf286884e81b77e7
5
- SHA512:
6
- metadata.gz: 5200a75af33b8dc94bb5046cbfbd5bcdcb37b3d528006df08728561a2970bc405289b403df10431c71621223a71e5ad1f8bf5bbed4c298416384bf91b7b0e5ab
7
- data.tar.gz: 792f7d9702ffb3215e078126b5992bacc01799b455e491fa17ce47ba7a31fc9fd7d5ca18ea3ba87a819cd0b448ae6a44d53f18a1b8237582fef0d69b99f8d311
@@ -1,39 +0,0 @@
1
- module Conjure
2
- module Service
3
- class RailsDeployment
4
- def initialize(options)
5
- @origin = options[:origin]
6
- @branch = options[:branch] || "master"
7
- @environment = "production"
8
- @test = options[:test]
9
- @target = options[:target]
10
- end
11
-
12
- def deploy
13
- Log.info "[deploy] Deploying #{@branch} to #{@environment}"
14
- unless @test
15
- codebase.install
16
- rails.run
17
- Log.info "[deploy] Application deployed to #{@target.ip_address}"
18
- end
19
- end
20
-
21
- def database
22
- codebase.database
23
- end
24
-
25
- def codebase
26
- @codebase ||= Service::RailsCodebase.new @target, @origin, @branch, @environment
27
- end
28
-
29
- def rails
30
- @rails ||= Service::RailsServer.new @target, @environment
31
- end
32
-
33
- def shell
34
- rails.base_image
35
- end
36
- end
37
- end
38
- end
39
-