hawser 0.0.1 → 0.1.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.
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