conjure 0.1.3 → 0.1.4

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