conjure 0.1.3 → 0.1.4

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,8 @@
1
+ ### Version 0.1.4
2
+ 2013-11-19
3
+
4
+ * Use SSH agent forwarding to deploy with the user's personal keys
5
+
1
6
  ### Version 0.1.3
2
7
  2013-11-12
3
8
 
data/README.md CHANGED
@@ -43,7 +43,7 @@ and then running `bundle`, OR by installing it directly:
43
43
 
44
44
  Then add a file to your Rails project called
45
45
  `config/conjure.yml`. This should be a YAML file with the following
46
- fields (all fields are required):
46
+ fields:
47
47
 
48
48
  * `digitalocean_client_id` and `digitalocean_api_key`: These
49
49
  credentials are available after logging in to your Digital Ocean
@@ -52,20 +52,17 @@ fields (all fields are required):
52
52
  * `digitalocean_region`: The geographic region for deploying new
53
53
  cloud servers. If unsure, use "New York 1".
54
54
 
55
- * `private_key_file` and `public_key_file`: Pathnames to local files
56
- (relative to your project's `config` directory) that contain the
57
- private and public SSH keys to use for deployment. It's
58
- recommended to generate a new keypair rather than using your
59
- personal SSH keys, since Conjure currently copies the specified
60
- private key to the server during deployment.
55
+ * `private_key_file` and `public_key_file` (optional): Pathnames to
56
+ local files (absolute paths, or relative to your project's
57
+ `config` directory) that contain the private and public SSH keys
58
+ to use for deployment. If these aren't specified, Conjure will try
59
+ to find identity files in `~/.ssh`.
61
60
 
62
61
  Here's an example conjure.yml file:
63
62
 
64
63
  digitalocean_client_id: XXXXXXXX
65
64
  digitalocean_api_key: XXXXXXXX
66
65
  digitalocean_region: New York 1
67
- private_key_file: conjure_key
68
- public_key_file: conjure_key.pub
69
66
 
70
67
  Finally, tell Conjure to deploy your app:
71
68
 
@@ -10,6 +10,7 @@ module Conjure
10
10
 
11
11
  def initialize(options)
12
12
  @options = options
13
+ find_default_keys unless @options["private_key"]
13
14
  end
14
15
 
15
16
  def method_missing(name)
@@ -19,7 +20,22 @@ module Conjure
19
20
 
20
21
  def file_contents(name)
21
22
  name = @options[name.to_s] if name.is_a? Symbol
22
- File.open File.join(@options["config_path"], name), "rb", &:read
23
+ name = File.join(@options["config_path"], name) unless name[0] == "/"
24
+ File.open name, "rb", &:read
25
+ end
26
+
27
+ private
28
+
29
+ def find_default_keys
30
+ private_key_paths = ["~/.ssh/id_rsa", "~/.ssh/id_dsa", "~/.ssh/identity"]
31
+ private_key_paths.each do |path|
32
+ path = File.expand_path(path)
33
+ if File.exists?(path) and File.exists?("#{path}.pub")
34
+ @options["private_key_file"] = path
35
+ @options["public_key_file"] = "#{path}.pub"
36
+ return
37
+ end
38
+ end
23
39
  end
24
40
  end
25
41
  end
@@ -109,7 +109,6 @@ module Conjure
109
109
  @remote_shell ||= RemoteShell.new(
110
110
  :ip_address => server.public_ip_address,
111
111
  :username => "root",
112
- :private_key_path => private_key_file,
113
112
  )
114
113
  end
115
114
  end
@@ -9,9 +9,8 @@ module Conjure
9
9
  end
10
10
 
11
11
  def base_image
12
- @base_image ||= @host.images.create(
12
+ @base_image ||= @host.shell.prepare(
13
13
  label: "mysql",
14
- base_image: "ubuntu",
15
14
  setup_commands: [
16
15
  "apt-get install -y mysql-server mysql-client"
17
16
  ],
@@ -19,9 +18,8 @@ module Conjure
19
18
  end
20
19
 
21
20
  def server_image
22
- @server_image ||= @host.images.create(
21
+ @server_image ||= base_image.prepare(
23
22
  label: "mysqlserver",
24
- base_image: base_image,
25
23
  setup_commands: [
26
24
  "/usr/sbin/mysqld & sleep 5; echo \"GRANT ALL ON *.* TO root@'%' IDENTIFIED BY '' WITH GRANT OPTION\" | /usr/bin/mysql",
27
25
  ],
@@ -8,9 +8,8 @@ module Conjure
8
8
  end
9
9
 
10
10
  def base_image
11
- @base_image ||= @host.images.create(
11
+ @base_image ||= @host.shell.prepare(
12
12
  label: "postgres",
13
- base_image: "ubuntu",
14
13
  setup_commands: [
15
14
  "apt-get install -y python-software-properties software-properties-common",
16
15
  "add-apt-repository -y ppa:pitti/postgresql",
@@ -21,9 +20,8 @@ module Conjure
21
20
  end
22
21
 
23
22
  def server_image
24
- @server_image ||= @host.images.create(
23
+ @server_image ||= base_image.prepare(
25
24
  label: "pgserver",
26
- base_image: base_image,
27
25
  setup_commands: [
28
26
  "service postgresql start; su postgres -c 'createuser -d -r -s root; createdb -O root root'; service postgresql stop",
29
27
  "echo 'host all all 0.0.0.0/0 trust' >>/etc/postgresql/9.2/main/pg_hba.conf",
@@ -66,6 +66,10 @@ module Conjure
66
66
  def containers
67
67
  ContainerSet.new :host => self
68
68
  end
69
+
70
+ def shell
71
+ DockerShell.new :docker_host => self
72
+ end
69
73
  end
70
74
 
71
75
  class ImageSet
@@ -79,6 +83,8 @@ module Conjure
79
83
  end
80
84
 
81
85
  class Image
86
+ attr_reader :host_volumes
87
+
82
88
  def initialize(host, options)
83
89
  @host = host
84
90
  @label = options[:label]
@@ -0,0 +1,46 @@
1
+ module Conjure
2
+ module Service
3
+ class DockerShell
4
+ attr_reader :docker_host
5
+
6
+ def initialize(options)
7
+ @docker_host = options[:docker_host]
8
+ @image = options[:image]
9
+ end
10
+
11
+ def prepare(options)
12
+ self.class.new(
13
+ :docker_host => @docker_host,
14
+ :image => @docker_host.images.create(image_options.merge options),
15
+ )
16
+ end
17
+
18
+ def command(*args)
19
+ (@image || default_image).command *args
20
+ end
21
+
22
+ def run(*args)
23
+ (@image || default_image).run *args
24
+ end
25
+
26
+ def stop(*args)
27
+ (@image || default_image).stop *args
28
+ end
29
+
30
+ def image_options
31
+ {
32
+ :base_image => (@image || default_image_name),
33
+ :host_volumes => (@image.host_volumes if @image),
34
+ }
35
+ end
36
+
37
+ def default_image
38
+ @default_image ||= @docker_host.images.create(image_options)
39
+ end
40
+
41
+ def default_image_name
42
+ "ubuntu"
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,25 @@
1
+ module Conjure
2
+ module Service
3
+ class ForwardedShell
4
+ def initialize(options)
5
+ @shell = options[:shell].prepare(
6
+ :label => "forwarded",
7
+ :setup_commands => [
8
+ "apt-get install -y openssh-server",
9
+ "mkdir -p /var/run/sshd",
10
+ "mkdir -p /root/.ssh; echo '#{options[:public_key]}' > /root/.ssh/authorized_keys",
11
+ "chmod 600 /root/.ssh/authorized_keys"
12
+ ],
13
+ )
14
+ end
15
+
16
+ def command(c)
17
+ container = @shell.run "/usr/sbin/sshd -D -e"
18
+ escaped_command = @shell.docker_host.shell_escape c
19
+ ssh_command = "ssh -A -o StrictHostKeyChecking=no #{container.ip_address} '#{escaped_command}'"
20
+ result = @shell.docker_host.server.run ssh_command
21
+ result.stdout
22
+ end
23
+ end
24
+ end
25
+ end
@@ -7,20 +7,6 @@ module Conjure
7
7
  @app_name = app_name
8
8
  @rails_environment = rails_environment
9
9
  @host = host
10
- github_private_key = Conjure.config.file_contents(:private_key_file).gsub("\n", "\\n")
11
- github_public_key = Conjure.config.file_contents(:public_key_file).gsub("\n", "\\n")
12
- @image = @host.images.create(
13
- label: "codebase",
14
- base_image: "ubuntu",
15
- setup_commands: [
16
- "apt-get install -y git",
17
- "mkdir -p /root/.ssh; echo '#{github_private_key}' > /root/.ssh/id_rsa",
18
- "mkdir -p /root/.ssh; echo '#{github_public_key}' > /root/.ssh/id_rsa.pub",
19
- "chmod -R go-rwx /root/.ssh",
20
- "echo 'Host github.com\\n\\tStrictHostKeyChecking no\\n' >> /root/.ssh/config",
21
- ],
22
- host_volumes: {"/rails_app" => "/#{app_name}"},
23
- )
24
10
  end
25
11
 
26
12
  def database_yml
@@ -37,34 +23,33 @@ module Conjure
37
23
  end
38
24
 
39
25
  def install
40
- code_checked_out ? fetch_code_updates : checkout_code
26
+ repository_link.update
41
27
  configure_database
42
28
  configure_logs
43
29
  end
44
30
 
45
- def code_checked_out
46
- @image.command("[ -d #{@app_name}/.git ] && echo yes; true").strip == "yes"
47
- end
48
-
49
- def checkout_code
50
- Conjure.log "[ repo] Checking out code from git"
51
- @image.command "git clone -b #{@branch} #{@github_url}"
31
+ def repository_link
32
+ @repository_link ||= RepositoryLink.new(
33
+ :volume => volume,
34
+ :branch => @branch,
35
+ :origin_url => @github_url,
36
+ :public_key => Conjure.config.file_contents(:public_key_file).gsub("\n", "\\n"),
37
+ )
52
38
  end
53
39
 
54
- def fetch_code_updates
55
- Conjure.log "[ repo] Fetching code updates from git"
56
- @image.command "cd #{@app_name}; git reset --hard; git checkout #{@branch}; git pull"
40
+ def volume
41
+ @volume ||= Volume.new(:docker_host => @host, :host_path => "/rails_app", :container_path => "/#{@app_name}")
57
42
  end
58
43
 
59
44
  def configure_database
60
45
  Conjure.log "[ repo] Generating database.yml"
61
- @image.command "echo '#{database_yml}' >/#{@app_name}/config/database.yml"
46
+ volume.write "config/database.yml", database_yml
62
47
  end
63
48
 
64
49
  def configure_logs
65
50
  Conjure.log "[ repo] Configuring application logger"
66
51
  setup = 'Rails.logger = Logger.new "#{Rails.root}/log/#{Rails.env}.log"'
67
- @image.command "echo '#{setup}' >/#{@app_name}/config/initializers/z_conjure_logger.rb"
52
+ volume.write "config/initializers/z_conjure_logger.rb", setup
68
53
  end
69
54
 
70
55
  def database_name
@@ -72,8 +57,7 @@ module Conjure
72
57
  end
73
58
 
74
59
  def gem_names
75
- gemfile = @image.command "cat #{@app_name}/Gemfile"
76
- gemfile.scan(/gem ['"]([^'"]+)['"]/).flatten
60
+ volume.read("Gemfile").scan(/gem ['"]([^'"]+)['"]/).flatten
77
61
  end
78
62
 
79
63
  def database
@@ -8,9 +8,8 @@ module Conjure
8
8
  end
9
9
 
10
10
  def base_image
11
- @base_image ||= @host.images.create(
11
+ @base_image ||= @host.shell.prepare(
12
12
  label: "rails_base",
13
- base_image: "ubuntu",
14
13
  setup_commands: [
15
14
  "apt-get install -y curl git",
16
15
  "curl -L https://get.rvm.io | bash -s stable",
@@ -35,14 +34,12 @@ module Conjure
35
34
  end
36
35
 
37
36
  def server_image
38
- @server_image ||= @host.images.create(
37
+ @server_image ||= base_image.prepare(
39
38
  label: "rails_server",
40
- base_image: base_image,
41
39
  ports: [80],
42
40
  environment: {
43
41
  PATH:"/usr/local/rvm/gems/ruby-1.9.3-p448@global/bin:/usr/local/rvm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
44
42
  },
45
- host_volumes: {"/rails_app" => "/#{@app_name}"},
46
43
  )
47
44
  end
48
45
 
@@ -42,17 +42,12 @@ module Conjure
42
42
  def session
43
43
  session_options = {
44
44
  :auth_methods => ["publickey"],
45
- :key_data => key_data,
46
- :keys_only => true,
47
45
  :paranoid => false,
46
+ :forward_agent => true,
48
47
  }
49
48
  @session ||= self.class.ssh_service.start @options[:ip_address], @options[:username], session_options
50
49
  end
51
50
 
52
- def key_data
53
- File.read @options[:private_key_path] if @options[:private_key_path]
54
- end
55
-
56
51
  def poll_stream(stream, &block)
57
52
  yield stream.sysread(1) if IO.select([stream], nil, nil, 0.01)
58
53
  end
@@ -0,0 +1,47 @@
1
+ module Conjure
2
+ module Service
3
+ class RepositoryLink
4
+ def initialize(options)
5
+ @volume = options[:volume]
6
+ @branch = options[:branch]
7
+ @origin_url = options[:origin_url]
8
+ @public_key = options[:public_key]
9
+ end
10
+
11
+ def update
12
+ code_checked_out ? fetch_code_updates : checkout_code
13
+ end
14
+
15
+ private
16
+
17
+ def code_checked_out
18
+ git_shell.command("[ -d #{code_path}/.git ] && echo yes; true").strip == "yes"
19
+ end
20
+
21
+ def checkout_code
22
+ Conjure.log "[ repo] Checking out code from git"
23
+ git_shell.command "git clone -b #{@branch} #{@origin_url} #{code_path}"
24
+ end
25
+
26
+ def fetch_code_updates
27
+ Conjure.log "[ repo] Fetching code updates from git"
28
+ git_shell.command "cd #{code_path}; git reset --hard; git checkout #{@branch}; git pull"
29
+ end
30
+
31
+ def code_path
32
+ @volume.container_path
33
+ end
34
+
35
+ def git_shell
36
+ @git_shell ||= ForwardedShell.new(:shell => @volume.shell.prepare({
37
+ label: "git",
38
+ setup_commands: [
39
+ "apt-get install -y git",
40
+ "mkdir -p /root/.ssh",
41
+ "echo 'Host github.com\\n\\tStrictHostKeyChecking no\\n' >> /root/.ssh/config",
42
+ ],
43
+ }), :public_key => @public_key)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,28 @@
1
+ module Conjure
2
+ module Service
3
+ class Volume
4
+ attr_reader :docker_host, :container_path
5
+
6
+ def initialize(options)
7
+ @docker_host = options[:docker_host]
8
+ @host_path = options[:host_path]
9
+ @container_path = options[:container_path]
10
+ end
11
+
12
+ def read(filename)
13
+ shell.command "cat #{@container_path}/#{filename}"
14
+ end
15
+
16
+ def write(filename, data)
17
+ shell.command "echo '#{data}' >#{@container_path}/#{filename}"
18
+ end
19
+
20
+ def shell
21
+ @shell ||= @docker_host.shell.prepare(
22
+ :label => "volume",
23
+ :host_volumes => {@host_path => @container_path},
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module Conjure
2
- VERSION = "0.1.3" unless defined?(VERSION)
2
+ VERSION = "0.1.4" 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.1.3
4
+ version: 0.1.4
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: 2013-11-12 00:00:00.000000000 Z
12
+ date: 2013-11-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fog
@@ -104,10 +104,14 @@ files:
104
104
  - lib/conjure/service/rails_server.rb
105
105
  - lib/conjure/service/rails_codebase.rb
106
106
  - lib/conjure/service/rails_application.rb
107
+ - lib/conjure/service/forwarded_shell.rb
108
+ - lib/conjure/service/repository_link.rb
109
+ - lib/conjure/service/volume.rb
107
110
  - lib/conjure/service/database/postgres.rb
108
111
  - lib/conjure/service/database/mysql.rb
109
112
  - lib/conjure/service/remote_shell.rb
110
113
  - lib/conjure/service/docker_host.rb
114
+ - lib/conjure/service/docker_shell.rb
111
115
  - lib/conjure/service/database.rb
112
116
  - lib/conjure/config.rb
113
117
  - lib/conjure/version.rb