hawser 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MzNhN2RlMTZiMDFhYzE2YjI5ZDdlMDRiZTg4NDdlN2FlY2E0NDdkNQ==
5
+ data.tar.gz: !binary |-
6
+ ZWExZDdlZTAyOTMzZGUxNTIwNTY0ZmMyNDliZjAzMmI2YmQ0ZmM3MA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ ZTc2YWJlYzllZGMxMjJjMmE1ZTcxZjg5YmI4MGY1ODljNmRkYTk0Y2Q3Yjkz
10
+ ZTFlOWVjZTJiMjZhMzE0MTVmNDhkMWJkODUwZWRmZjU3ODM2ODBjZjNmMDgz
11
+ MjJmYWFhMTFjZTcxNDQ4MWQ4YWJhNDRhMjJmNjk1MTBjZmM0Yjg=
12
+ data.tar.gz: !binary |-
13
+ NTNlNmUzOTg1YWY4MGNjNjk1MWRiNDBjZWRkY2E5OGFhNmMwODQ5MWRmY2Mw
14
+ NzI4ZjA0ZDkxMmIzZTMyZGY4OThkOTZiYTRjZDM3ZGIyMzdhNjg1MGE5ODhl
15
+ NGVhOTk2MmM1NmJkMGIyODYwMDBjZGVjOTUzOWIyMmYwMDJkZGY=
data/lib/hawser.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'hawser/credentialing'
2
+ require 'hawser/baking'
3
+ require 'hawser/servers'
4
+ require 'hawser/cluster'
@@ -0,0 +1,85 @@
1
+ require 'mattock'
2
+
3
+ module Hawser
4
+ class BakingCommand < Mattock::Rake::RemoteCommandTask
5
+ setting :arch, "x86_64"
6
+ setting :ec2_version_pattern, "1.4.0.5 20071010"
7
+
8
+ setting :absolute_path, "/"
9
+
10
+ setting :region, "us-west-1"
11
+
12
+ dir(:ephemeral_dir, "mnt",
13
+ dir(:keyfile_dir, "keys",
14
+ path(:private_key, "pk.pem"),
15
+ path(:certificate_file, "cert.pem")))
16
+
17
+ runtime_setting :image_name
18
+ runtime_setting :prefix
19
+
20
+ runtime_setting :bucket
21
+ runtime_setting :access_key
22
+ runtime_setting :secret_key
23
+ runtime_setting :aws_account_id
24
+
25
+ runtime_setting :manifest_name
26
+ runtime_setting :manifest_path
27
+
28
+ def resolve_configuration
29
+ super
30
+
31
+ resolve_paths
32
+ end
33
+
34
+ def resolve_runtime_configuration
35
+ if field_unset?(:prefix)
36
+ self.prefix = image_name
37
+ end
38
+
39
+ if field_unset?(:manifest_name)
40
+ self.manifest_name = "#{prefix}.manifest.xml"
41
+ end
42
+
43
+ if field_unset?(:manifest_path)
44
+ self.manifest_path = File::join(ephemeral_dir.abspath, manifest_name)
45
+ end
46
+
47
+ super
48
+ end
49
+
50
+ def command
51
+ cmd("(ec2-bundle-vol --version | grep \"#{ec2_version_pattern}\")") &
52
+ (cmd("rm") {|rm|
53
+ rm.options = ["-f", File::join(ephemeral_dir.abspath, prefix || "no-such-file") ]
54
+ }) &
55
+ (cmd("ec2-bundle-vol") { |bundle|
56
+ bundle.options += ["-k", private_key.abspath ]
57
+ bundle.options += ["-c", certificate_file.abspath ]
58
+ bundle.options += ["--user", aws_account_id ]
59
+
60
+ bundle.options += ["--destination", ephemeral_dir.abspath ]
61
+ bundle.options += ["--prefix", prefix ]
62
+ bundle.options += ["--arch", arch ]
63
+ bundle.options += %w{-i /etc/ec2/amitools/cert-ec2.pem}
64
+ bundle.options += ["-i", '$(ls /etc/ssl/certs/*.pem | tr \\\\n ,)']
65
+ bundle.options += %w{--ec2cert /etc/ec2/amitools/cert-ec2.pem}
66
+ bundle.options += ["-e", keyfile_dir.abspath]
67
+ }) &
68
+ (cmd("ec2-upload-bundle") {|upload|
69
+ upload.options += ["-b", bucket ]
70
+ upload.options += ["-m", manifest_path]
71
+ upload.options += ["-a", access_key ]
72
+ upload.options += ["-s", secret_key ]
73
+ upload.options += ["--location", region ]
74
+ upload.options += ["--retry"]
75
+ }) &
76
+ (cmd("ec2-register") {|register|
77
+ register.options << "#{bucket}/#{manifest_name}"
78
+ register.options += ["-n", image_name]
79
+ register.options += ["--region", region]
80
+ register.options += ["--aws-access-key", access_key]
81
+ register.options += ["--aws-secret-key", secret_key]
82
+ })
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,85 @@
1
+ require 'mattock'
2
+ require 'hawser/baking-command'
3
+
4
+ module Hawser
5
+ class Baking < Mattock::Tasklib
6
+ include Caliph::CommandLineDSL
7
+
8
+ default_namespace :baking
9
+
10
+ setting :location, "us-west-1"
11
+
12
+ setting :arch, "x86_64"
13
+ setting :ec2_version_pattern, "1.4.0.5 20071010"
14
+
15
+ dir(:ephemeral_dir, "/mnt",
16
+ dir(:keyfile_dir, "keys",
17
+ path(:private_key, "pk.pem"),
18
+ path(:certificate_file, "cert.pem")))
19
+
20
+ path(:signing_cert, "cert.pem")
21
+ path(:signing_key, "key.pem")
22
+
23
+ setting(:image_name).isnt(:required)
24
+ setting(:bucket)
25
+
26
+ setting :access_key
27
+ setting :secret_key
28
+ setting :aws_account_id
29
+
30
+ setting(:remote_server, nested{
31
+ setting(:address).isnt(:required)
32
+ setting :port, 22
33
+ setting :user, nil
34
+ })
35
+
36
+ def resolve_configuration
37
+ ephemeral_dir.absolute_path = ephemeral_dir.relative_path
38
+
39
+ super
40
+ resolve_paths
41
+ end
42
+
43
+ def define
44
+ in_namespace do
45
+ Mattock::Rake::RemoteCommandTask.define_task(:create_dirs => :collect) do |task|
46
+ task.remote_server = proxy_value.remote_server
47
+ task.command = cmd("mkdir") do |mkdir|
48
+ mkdir.options << "-p" #ok
49
+ mkdir.options << keyfile_dir.abspath
50
+ end
51
+ end
52
+
53
+ Mattock::Rake::CommandTask.define_task(:copy_cert => [:collect, :create_dirs]) do |task|
54
+ task.runtime_definition do |task|
55
+ task.command = cmd("scp") do |scp|
56
+ scp.options << signing_cert.abspath
57
+ scp.options << "#{remote_server.address}:#{certificate_file.abspath}"
58
+ end
59
+ end
60
+ end
61
+
62
+ Mattock::Rake::CommandTask.define_task(:copy_key => [:collect, :create_dirs]) do |task|
63
+ task.runtime_definition do |task|
64
+ task.command = cmd("scp") do |scp|
65
+ scp.options << signing_key.abspath
66
+ scp.options << "#{remote_server.address}:#{private_key.abspath}"
67
+ end
68
+ end
69
+ end
70
+
71
+ task :collect, [:target, :name] do |task, args|
72
+ self.remote_server.address = args[:target]
73
+ self.image_name = args[:name]
74
+ end
75
+
76
+ BakingCommand.define_task(:bake, [:target, :name] => [:collect, :copy_cert, :copy_key]) do |task|
77
+ self.copy_settings_to(task)
78
+ proxied = self.proxy_settings
79
+ proxied.field_names = [:remote_server, :image_name, :bucket]
80
+ proxied.to(task)
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,63 @@
1
+ require 'mattock'
2
+ require 'hawser/credentialing'
3
+ require 'hawser/baking'
4
+ require 'hawser/servers'
5
+
6
+ module Hawser
7
+ # Represents a cluster of servers on AWS
8
+ #
9
+ # @example in Rakefile
10
+ # Hawser::Cluster.new do |bollard|
11
+ # bollard.name = "bollard"
12
+ # bollard.user = "ahab"
13
+ # end
14
+ #
15
+ # @example at console
16
+ # > rake bollard:servers:list
17
+ # > rake bollard:bake[app1.bollard.com,app1-number1]
18
+ #
19
+ class Cluster < Mattock::Tasklib
20
+ setting :name
21
+ setting :user
22
+ setting :bucket
23
+
24
+ def resolve_configuration
25
+ @namespace ||= name.downcase
26
+ self.bucket ||= "#{name.downcase}-amis"
27
+ super
28
+ end
29
+
30
+ def define
31
+ in_namespace do
32
+ creds = Hawser::Credentialing.new do |creds|
33
+ copy_settings_to(creds)
34
+ creds.cluster_name = name
35
+ end
36
+
37
+ Hawser::Baking.new do |bake|
38
+ copy_settings_to(bake)
39
+ creds.copy_settings_to(bake)
40
+ creds.credentials.proxy_settings_to(bake)
41
+ end
42
+
43
+ Hawser::Servers.new do |servers|
44
+ copy_settings_to(servers)
45
+ servers.cluster_name = name
46
+ creds.copy_settings_to(servers)
47
+ creds.credentials.proxy_settings_to(servers)
48
+ end
49
+
50
+ namespace :baking do
51
+ task :bake => "credentials:establish"
52
+ end
53
+
54
+ namespace :servers do
55
+ task :list => "credentials:establish"
56
+ end
57
+
58
+ desc "Make an AMI copy of the running instance at :target. Name the AMI :name and store it in :bucket"
59
+ task :bake, [:target, :name] => "baking:bake"
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,208 @@
1
+ require 'mattock'
2
+ require 'aws-sdk'
3
+
4
+ module Hawser
5
+ class Credentialing < Mattock::TaskLib
6
+ default_namespace :credentials
7
+
8
+ setting :user
9
+ setting :cluster_name
10
+
11
+ setting :credentials, nested{
12
+ nil_fields :access_key, :secret_key, :certificate_id, :password, :aws_account_id
13
+ setting :mfa, true
14
+ }
15
+
16
+ setting :key_size, 4096
17
+
18
+ setting :cert, nested{
19
+ setting :lifetime, nested{
20
+ setting :years, 1
21
+ setting :days, 0
22
+ setting :hours, 0
23
+ setting :minutes, 0
24
+ setting :seconds, 0
25
+ setting :total_seconds
26
+ }
27
+ }
28
+
29
+ setting :iam, nil
30
+ setting :iam_user, nil
31
+
32
+ dir(:creds_root, "credentials",
33
+ dir(:cluster_dir,
34
+ dir(:user_dir,
35
+ path(:creds_csv, "creds.csv"),
36
+ path(:config_yaml, "config.yaml"),
37
+ path(:signing_cert, "cert.pem"),
38
+ path(:signing_key, "key.pem"))))
39
+
40
+ def resolve_configuration
41
+ cluster_dir.relative_path = cluster_name
42
+ user_dir.relative_path = user
43
+
44
+ cert.lifetime.total_seconds =
45
+ cert.lifetime.seconds + 60 * (
46
+ cert.lifetime.minutes + 60 * (
47
+ cert.lifetime.hours + 24 * (
48
+ cert.lifetime.days + 365 * cert.lifetime.years)))
49
+
50
+ super
51
+
52
+ resolve_paths
53
+ end
54
+
55
+ def signing_key_content
56
+ require 'openssl'
57
+ if key_size <= 1024
58
+ raise "Refusing to create an insecure RSA key"
59
+ end
60
+
61
+ #XXX Consider using a passphrase here - although #managment for baking
62
+ #etc...
63
+ key = OpenSSL::PKey::RSA.generate(key_size)
64
+
65
+ key.to_pem
66
+ end
67
+
68
+ def signing_cert_content(key_string)
69
+ require 'openssl'
70
+
71
+ key = OpenSSL::PKey.read(key_string)
72
+
73
+ cert = OpenSSL::X509::Certificate.new
74
+ cert.version = 2
75
+ cert.serial = 2
76
+ cert.public_key = key.public_key
77
+ cert.not_before = Time.now
78
+ cert.not_after = cert.not_before + self.cert.lifetime.total_seconds
79
+
80
+ File.open(task.name, "w") do |pem_file|
81
+ pem_file.write(cert.to_pem)
82
+ end
83
+ end
84
+
85
+ def load_from_yaml(string)
86
+ require 'yaml'
87
+ config = YAML::load(string)
88
+
89
+ credentials.aws_account_id = config["aws_account_id"] if config.has_key? "aws_account_id"
90
+ credentials.access_key = config["access_key"] if config.has_key? "access_key"
91
+ credentials.secret_key = config["secret_key"] if config.has_key? "secret_key"
92
+ credentials.password = config["password"] if config.has_key? "password"
93
+ credentials.certificate_id = config["certificate_id"] if config.has_key? "certificate_id"
94
+ credentials.mfa = config["mfa"] if config.has_key? "mfa"
95
+ end
96
+
97
+ def load_from_csv(string)
98
+ require 'csv'
99
+
100
+ rows = CSV.new(string).to_a
101
+
102
+ rows.shift #headers
103
+ row = rows.find do |name, key, secret|
104
+ name =~ /^#{user}$/i
105
+ end
106
+ if row.nil?
107
+ fail "Couldn't find Access credentials line for #{user.inspect} in #{rows.map{|name, _,_| name}.inspect}"
108
+ end
109
+
110
+ _, key, secret = *row
111
+
112
+ credentials.access_key = key
113
+ credentials.secret_key = secret
114
+ end
115
+
116
+ def find_cert_id(cert_body)
117
+ remote_cert = iam_user.signing_certificates.find do |certificate|
118
+ certificate.contents == cert_body
119
+ end
120
+
121
+ unless remote_cert.nil?
122
+ credentials.certificate_id = remote_cert.id
123
+ end
124
+ end
125
+
126
+ def define
127
+ in_namespace do
128
+ directory user_dir.abspath
129
+
130
+ file signing_cert.abspath => signing_key.abspath do |task|
131
+ key = File::read(signing_key.abspath)
132
+ File.write(task.name, signing_cert_content(key))
133
+ end
134
+
135
+ file signing_key.abspath do |task|
136
+ File.write(task.name, signing_key_content)
137
+ end
138
+
139
+ task :load do
140
+ if File::exists?(config_yaml.abspath)
141
+ load_from_yaml(File::read(config_yaml.abspath))
142
+ end
143
+ end
144
+
145
+ task :store do
146
+ require 'yaml'
147
+
148
+ File::open(config_yaml.abspath, "w") do |config|
149
+ config.write YAML.dump(Hash[credentials.to_hash.map do |key,value|
150
+ [key.to_s, value]
151
+ end])
152
+ end
153
+ end
154
+
155
+ task :iam => "get:access" do
156
+ self.iam = AWS::IAM.new(:access_key_id => credentials.access_key, :secret_access_key => credentials.secret_key)
157
+ end
158
+
159
+ task :iam_user => :iam do
160
+ self.iam_user = iam.users[user]
161
+ end
162
+
163
+ namespace :get do
164
+ task :access => :load do
165
+ if credentials.access_key.nil? or not credentials.secret_key.nil?
166
+ load_from_csv(File.read(creds_csv.abspath))
167
+ end
168
+ end
169
+
170
+ task :aws_account_id => :iam do
171
+ credentials.aws_account_id = iam.users.first.arn.split(":")[4]
172
+ end
173
+
174
+ task :certificate_id => [:iam_user, signing_cert.abspath] do
175
+ if credentials.certificate_id.nil?
176
+ find_cert_id(File::read(signing_cert.abspath))
177
+ end
178
+ end
179
+ end
180
+ task :get => %w{get:access get:certificate_id get:aws_account_id}
181
+
182
+ namespace :set do
183
+ task :password => :iam_user do
184
+ unless credentials.password.nil?
185
+ iam_user.login_policy.password = credentials.password
186
+ end
187
+ end
188
+
189
+ task :certificate => [:iam_user, signing_cert.abspath, "get:certificate_id"] do
190
+ if !credentials.certificate_id.nil?
191
+ begin
192
+ iam_user.signing_certificates[credentials.certificate_id].contents
193
+ next
194
+ rescue AWS::Core::Resource::NotFound
195
+ end
196
+ end
197
+
198
+ cert = iam_user.signing_certificates.upload(File::read(signing_cert.abspath))
199
+ credentials.certificate_id = cert.id
200
+ end
201
+ end
202
+
203
+ desc "Set up credentials for #{user} on cluster_name #{cluster_name}"
204
+ task :establish => %w{get set:certificate store}
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,36 @@
1
+ require "mattock"
2
+ require 'aws-sdk'
3
+
4
+ module Hawser
5
+ class Servers < Mattock::Tasklib
6
+ default_namespace :servers
7
+
8
+ setting :cluster_name
9
+ setting :access_key
10
+ setting :secret_key
11
+ setting :region, "us-west-1"
12
+
13
+ def define
14
+ in_namespace do
15
+ task :list do
16
+ require 'yaml'
17
+ ec2 = AWS::EC2.new(:region => region, :access_key_id => access_key, :secret_access_key => secret_key)
18
+ puts(YAML::dump( ec2.instances.map do |instance|
19
+ { "cluster_name" => cluster_name,
20
+ "platform" => "aws",
21
+ "id_from_platform" => instance.instance_id,
22
+ "private_dns_name" => instance.public_dns_name,
23
+ "public_dns_name" => instance.public_dns_name,
24
+ "private_ip_address" => instance.private_ip_address,
25
+ "public_ip_address" => instance.public_ip_address,
26
+ "architecture" => instance.architecture.to_s,
27
+ "availability_zone" => instance.placement[:availability_zone],
28
+ "launch_time" => instance.launch_time,
29
+ "image_id" => instance.image_id,
30
+ "key_name" => instance.key_name }
31
+ end))
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,10 @@
1
+ require 'hawser'
2
+
3
+ describe Hawser do
4
+ it "should create some rake tasks" do
5
+ Hawser::Cluster.new do |cluster|
6
+ cluster.name = "test"
7
+ cluster.user = "testy-mctester"
8
+ end
9
+ end
10
+ end
metadata CHANGED
@@ -1,32 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hawser
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.0.1
4
+ version: 0.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Judson Lester
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-10-22 00:00:00.000000000 Z
11
+ date: 2014-08-22 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- version_requirements: !ruby/object:Gem::Requirement
14
+ name: aws-sdk
15
+ requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ! '>'
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
- none: false
21
- name: aws-sdk
22
20
  type: :runtime
23
21
  prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>'
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mattock
24
29
  requirement: !ruby/object:Gem::Requirement
25
30
  requirements:
26
31
  - - ! '>'
27
32
  - !ruby/object:Gem::Version
28
33
  version: '0'
29
- none: false
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>'
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
30
41
  description: ! ' A toolbelt of utilities for doing stuff with AWS
31
42
 
32
43
  '
@@ -36,17 +47,25 @@ executables: []
36
47
  extensions: []
37
48
  extra_rdoc_files: []
38
49
  files:
50
+ - lib/hawser.rb
51
+ - lib/hawser/cluster.rb
52
+ - lib/hawser/credentialing.rb
53
+ - lib/hawser/servers.rb
54
+ - lib/hawser/baking-command.rb
55
+ - lib/hawser/baking.rb
56
+ - spec/hawser_spec.rb
39
57
  - spec_help/gem_test_suite.rb
40
58
  homepage: http://nyarly.github.com/hawser
41
59
  licenses:
42
60
  - MIT
61
+ metadata: {}
43
62
  post_install_message:
44
63
  rdoc_options:
45
64
  - --inline-source
46
65
  - --main
47
66
  - doc/README
48
67
  - --title
49
- - hawser-0.0.1 Documentation
68
+ - hawser-0.1.0 Documentation
50
69
  require_paths:
51
70
  - lib/
52
71
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -54,19 +73,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
54
73
  - - ! '>='
55
74
  - !ruby/object:Gem::Version
56
75
  version: '0'
57
- none: false
58
76
  required_rubygems_version: !ruby/object:Gem::Requirement
59
77
  requirements:
60
78
  - - ! '>='
61
79
  - !ruby/object:Gem::Version
62
80
  version: '0'
63
- none: false
64
81
  requirements: []
65
82
  rubyforge_project: hawser
66
- rubygems_version: 1.8.24
83
+ rubygems_version: 2.0.14
67
84
  signing_key:
68
- specification_version: 3
85
+ specification_version: 4
69
86
  summary: AWS tools for towing your servers around
70
87
  test_files:
71
88
  - spec_help/gem_test_suite.rb
72
- has_rdoc: true