conjure 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
-