awsom 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 +7 -0
- data/.gitignore +8 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +2 -0
- data/awsom.gemspec +27 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/dev_exe/awsom +4 -0
- data/exe/awsom +71 -0
- data/lib/awsom.rb +6 -0
- data/lib/awsom/application.rb +51 -0
- data/lib/awsom/cli/resources.rb +59 -0
- data/lib/awsom/config.rb +59 -0
- data/lib/awsom/custom_logger.rb +34 -0
- data/lib/awsom/ec2.rb +4 -0
- data/lib/awsom/error.rb +4 -0
- data/lib/awsom/ext/string.rb +33 -0
- data/lib/awsom/image.rb +21 -0
- data/lib/awsom/instance.rb +53 -0
- data/lib/awsom/instance_dsl.rb +139 -0
- data/lib/awsom/lock.rb +19 -0
- data/lib/awsom/logger.rb +27 -0
- data/lib/awsom/salt_bootstrap.rb +88 -0
- data/lib/awsom/security_group.rb +159 -0
- data/lib/awsom/subnet.rb +92 -0
- data/lib/awsom/version.rb +3 -0
- data/lib/awsom/vpc.rb +85 -0
- metadata +99 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a4b8a319b849ca7accad77d9e127a61d36601faa39185ea2b8d0afdb38a5789b
|
|
4
|
+
data.tar.gz: 521125b13c35ca550c9e2f73567b78a0b93a95bbbc51a5efd5d62bda6085b203
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a69c757a2bdc7b9ec6d62683020ba05e418bfd54a6a8ba4cbb26e4d4c6f2cf79f0bb7ec4ee244154ca705dd910678608e78688e7c0c89ea0c89bf3fc5b3cb3a4
|
|
7
|
+
data.tar.gz: 79eba4391587b74cf9acae2fcbf295968229974ad93cd63656de7b70581bf05ccb9eddab84f59146819c9b0a5219340d5ce8b3a0dfe2b2c0317c956ea3f3758f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 Neeraj Bhunwal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Awsom
|
|
2
|
+
|
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/awsom`. To experiment with that code, run `bin/console` for an interactive prompt.
|
|
4
|
+
|
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add this line to your application's Gemfile:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem 'awsom'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
And then execute:
|
|
16
|
+
|
|
17
|
+
$ bundle
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
|
|
21
|
+
$ gem install awsom
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
TODO: Write usage instructions here
|
|
26
|
+
|
|
27
|
+
## Development
|
|
28
|
+
|
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
30
|
+
|
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
32
|
+
|
|
33
|
+
## Contributing
|
|
34
|
+
|
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/awsom.
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/awsom.gemspec
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require "awsom/version"
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "awsom"
|
|
8
|
+
spec.version = Awsom::VERSION
|
|
9
|
+
spec.authors = ["Neeraj"]
|
|
10
|
+
# spec.email = ["neeraj.bhunwal@gmail.com"]
|
|
11
|
+
|
|
12
|
+
spec.summary = %q{Aws IaC dsl}
|
|
13
|
+
# spec.description = %q{TODO: Write a longer description or delete this line.}
|
|
14
|
+
# spec.homepage = "TODO: Put your gem's website or public repo URL here."
|
|
15
|
+
spec.license = "MIT"
|
|
16
|
+
|
|
17
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
18
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
|
19
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
20
|
+
end
|
|
21
|
+
spec.bindir = "exe"
|
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
23
|
+
spec.require_paths = ["lib"]
|
|
24
|
+
|
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.17"
|
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
27
|
+
end
|
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "awsom"
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require "irb"
|
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/dev_exe/awsom
ADDED
data/exe/awsom
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
Signal.trap("INT") { exit 1 }
|
|
3
|
+
|
|
4
|
+
$stdout.sync = true
|
|
5
|
+
|
|
6
|
+
require 'optparse'
|
|
7
|
+
require 'awsom/version'
|
|
8
|
+
require 'awsom/error'
|
|
9
|
+
require 'awsom/logger'
|
|
10
|
+
|
|
11
|
+
logger = ::Awsom::Logger.logger
|
|
12
|
+
|
|
13
|
+
options = {}
|
|
14
|
+
opts_parser = OptionParser.new do |opts|
|
|
15
|
+
|
|
16
|
+
banner = []
|
|
17
|
+
banner << "Usage: awsom [global options] command [options] args"
|
|
18
|
+
banner << "Commands: resources"
|
|
19
|
+
|
|
20
|
+
banner << "Options: "
|
|
21
|
+
opts.banner = banner.join("\n")
|
|
22
|
+
|
|
23
|
+
opts.on("-v", "--version", "Show version") do |v|
|
|
24
|
+
require 'awsom/version'
|
|
25
|
+
puts ::Awsom::VERSION
|
|
26
|
+
exit
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
opts.on("--debug", "Show debug messages") do
|
|
30
|
+
options[:debug] = true
|
|
31
|
+
logger.level = ::Logger::DEBUG
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
opts.on("--trace", "Show debug messages and exception stack trace") do
|
|
35
|
+
options[:debug] = true
|
|
36
|
+
options[:trace] = true
|
|
37
|
+
logger.level = ::Logger::DEBUG
|
|
38
|
+
# logger.trace_mode = true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
|
42
|
+
puts opts
|
|
43
|
+
exit
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
begin
|
|
48
|
+
opts_parser.order!(ARGV)
|
|
49
|
+
command = (ARGV.shift || '')
|
|
50
|
+
case command
|
|
51
|
+
when "resources", "r"
|
|
52
|
+
require 'awsom/cli/resources'
|
|
53
|
+
cli = ::Awsom::Cli::Resources.new(ARGV)
|
|
54
|
+
cli.run
|
|
55
|
+
when ''
|
|
56
|
+
puts opts_parser
|
|
57
|
+
else
|
|
58
|
+
raise ::Awsom::Error, "no such command #{command}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument, ::Awsom::Error => e
|
|
62
|
+
cause = e.cause
|
|
63
|
+
if options[:trace]
|
|
64
|
+
puts cause
|
|
65
|
+
cause ? (raise cause) : (raise e)
|
|
66
|
+
else
|
|
67
|
+
logger.debug "#{cause.message}" if cause
|
|
68
|
+
logger.error "#{e.message}"
|
|
69
|
+
abort
|
|
70
|
+
end
|
|
71
|
+
end
|
data/lib/awsom.rb
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require_relative 'security_group'
|
|
2
|
+
require_relative 'subnet'
|
|
3
|
+
require_relative 'instance'
|
|
4
|
+
require_relative 'instance_dsl'
|
|
5
|
+
|
|
6
|
+
module Awsom
|
|
7
|
+
class Application
|
|
8
|
+
|
|
9
|
+
def initialize(vpc_id:, region:)
|
|
10
|
+
@vpc_id = vpc_id
|
|
11
|
+
@region = region
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def run(files)
|
|
15
|
+
files.each do |f|
|
|
16
|
+
apply f
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def security_group(name, &block)
|
|
23
|
+
sg = SecurityGroup.new(name, vpc_id: @vpc_id)
|
|
24
|
+
sg.created(&block)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def subnet(name, az:, cidr:, &block)
|
|
28
|
+
az = "#{@region}#{az}"
|
|
29
|
+
subnet = Subnet.new(name, cidr: cidr, vpc_id: @vpc_id, az: az)
|
|
30
|
+
id = subnet.created
|
|
31
|
+
puts "subnet #{name} (#{cidr}) #{id}"
|
|
32
|
+
@subnet_id = id
|
|
33
|
+
instance_eval(&block) if block
|
|
34
|
+
ensure
|
|
35
|
+
@subnet_id = nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def instance(name, &block)
|
|
39
|
+
instance = Instance.new(name, vpc_id: @vpc_id)
|
|
40
|
+
dsl = InstanceDSL.new(name, vpc_id: @vpc_id, subnet_id: @subnet_id)
|
|
41
|
+
dsl.read(&block)
|
|
42
|
+
setup_proc = dsl.setup_proc
|
|
43
|
+
instance.created(setup_proc) { dsl.run_params }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def apply(file)
|
|
47
|
+
instance_eval(File.read(file), file)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'awsom/lock'
|
|
2
|
+
require 'awsom/config'
|
|
3
|
+
|
|
4
|
+
module Awsom
|
|
5
|
+
module Cli
|
|
6
|
+
class Resources
|
|
7
|
+
|
|
8
|
+
def initialize(argv)
|
|
9
|
+
@argv = argv
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def run
|
|
13
|
+
lock.acquire
|
|
14
|
+
opts.parse!(@argv)
|
|
15
|
+
config = Config.new("config.rb").config
|
|
16
|
+
|
|
17
|
+
init_aws(config)
|
|
18
|
+
|
|
19
|
+
vpc_id = config.fetch :vpc_id
|
|
20
|
+
region = config.fetch :region
|
|
21
|
+
|
|
22
|
+
require 'awsom/application'
|
|
23
|
+
app = Application.new(vpc_id: vpc_id, region: region)
|
|
24
|
+
app.run(@argv)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def init_aws(config)
|
|
28
|
+
require 'aws-sdk-core'
|
|
29
|
+
if config.key?(:aws_key) && config.key?(:aws_secret)
|
|
30
|
+
credentials = Aws::Credentials.new(config.fetch(:aws_key), config.fetch(:aws_secret))
|
|
31
|
+
Aws.config[:credentials] = credentials
|
|
32
|
+
end
|
|
33
|
+
Aws.config[:region] = config.fetch(:region)
|
|
34
|
+
require_relative '../ec2'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def lock
|
|
38
|
+
@lock ||= ::Awsom::Lock.new
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def opts
|
|
42
|
+
OptionParser.new do |opts|
|
|
43
|
+
|
|
44
|
+
opts.banner = "Usage: awsom create [options] [target]"
|
|
45
|
+
|
|
46
|
+
opts.on("-f", "--file FILE", "Specify hosts file") do |f|
|
|
47
|
+
salt.hosts_file = f
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
opts.on("-h", "--help", "Help") do
|
|
51
|
+
puts opts
|
|
52
|
+
exit
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
data/lib/awsom/config.rb
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Awsom
|
|
2
|
+
class Config
|
|
3
|
+
|
|
4
|
+
def initialize(config_path)
|
|
5
|
+
@config_path = config_path
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def config
|
|
9
|
+
@config = {
|
|
10
|
+
minion_config: {}
|
|
11
|
+
}
|
|
12
|
+
read_config
|
|
13
|
+
@config
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def region(region)
|
|
19
|
+
set :region, region
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def vpc_id(vpc)
|
|
23
|
+
set :vpc_id, vpc
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def minion_config(config)
|
|
27
|
+
set :minion_config, config
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def aws_key(aws_key)
|
|
31
|
+
set :aws_key, aws_key
|
|
32
|
+
set :use_iam, false
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def aws_secret(aws_secret)
|
|
36
|
+
set :aws_secret, aws_secret
|
|
37
|
+
set :use_iam, false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def set(key, value)
|
|
41
|
+
@config.store key, value
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def read_config
|
|
45
|
+
read(@config_path, required: false)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def read(file, required: true)
|
|
49
|
+
if not File.readable? file
|
|
50
|
+
raise Error, "config: #{file} not readable" if required
|
|
51
|
+
return
|
|
52
|
+
end
|
|
53
|
+
instance_eval(File.read(file), file)
|
|
54
|
+
rescue NoMethodError => e
|
|
55
|
+
raise Error, "invalid option used in config: #{e.name}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require_relative 'ext/string'
|
|
2
|
+
module Awsom
|
|
3
|
+
class CustomLogger < ::Logger
|
|
4
|
+
|
|
5
|
+
attr_writer :trace
|
|
6
|
+
|
|
7
|
+
def initialize(file)
|
|
8
|
+
super(file)
|
|
9
|
+
@level = ::Logger::INFO
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def format_message(severity, timestamp, progname, msg)
|
|
13
|
+
case severity
|
|
14
|
+
when "INFO"
|
|
15
|
+
"#{msg}\n"
|
|
16
|
+
when "ERROR"
|
|
17
|
+
"#{severity.bold.red} #{msg}\n"
|
|
18
|
+
when "WARN"
|
|
19
|
+
"#{severity.downcase.bold.yellow} #{msg}\n"
|
|
20
|
+
else
|
|
21
|
+
"#{severity[0].bold.blue} #{msg}\n"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def bullet(msg)
|
|
26
|
+
puts "#{"\u2219".bold.blue} #{msg}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def trace?
|
|
30
|
+
@trace
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
end
|
data/lib/awsom/ec2.rb
ADDED
data/lib/awsom/error.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
class String
|
|
2
|
+
def colorize(color_code)
|
|
3
|
+
"\e[#{color_code}m#{self}\e[0m"
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def bold
|
|
7
|
+
"\e[1m#{self}\e[0m"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def white
|
|
11
|
+
colorize(37)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def green
|
|
15
|
+
colorize(32)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def yellow
|
|
19
|
+
colorize(33)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def red
|
|
23
|
+
colorize(31)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def blue
|
|
27
|
+
colorize(34)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def grey
|
|
31
|
+
colorize("90")
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/awsom/image.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require_relative 'ec2'
|
|
2
|
+
module Awsom
|
|
3
|
+
module Image
|
|
4
|
+
|
|
5
|
+
@cache = {}
|
|
6
|
+
|
|
7
|
+
def self.block_device_mappings(image_id)
|
|
8
|
+
image = describe(image_id)
|
|
9
|
+
image.block_device_mappings
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.describe(image_id)
|
|
13
|
+
if image = @cache[image_id]
|
|
14
|
+
return image
|
|
15
|
+
else
|
|
16
|
+
@cache[image_id] = Ec2.describe_images(image_ids: [image_id]).images[0]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'pty'
|
|
2
|
+
module Awsom
|
|
3
|
+
class Instance
|
|
4
|
+
|
|
5
|
+
include Logger
|
|
6
|
+
|
|
7
|
+
def initialize(name, vpc_id:, setup_proc: nil)
|
|
8
|
+
@name = name
|
|
9
|
+
@vpc_id = vpc_id
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def created(setup_proc = nil, &block)
|
|
13
|
+
id = find_id
|
|
14
|
+
return id if id
|
|
15
|
+
run_params = yield
|
|
16
|
+
id = create(run_params)
|
|
17
|
+
instance = Aws::EC2::Instance.new(id)
|
|
18
|
+
puts "(#{@name}) waiting for instance to start"
|
|
19
|
+
instance.wait_until_running
|
|
20
|
+
setup_proc.call(instance) if setup_proc
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def find_id
|
|
26
|
+
filters = [
|
|
27
|
+
{ name: "vpc-id", values: [@vpc_id] },
|
|
28
|
+
{ name: "tag:Name", values: [@name] }
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
result = Ec2.describe_instances(filters: filters)
|
|
32
|
+
instances = []
|
|
33
|
+
result.reservations.each do |r|
|
|
34
|
+
r.instances.each { |i| instances << i }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
num_results = instances.size
|
|
38
|
+
raise Error, "mulitple instances found with name #{@name}" if num_results > 1
|
|
39
|
+
if num_results == 1
|
|
40
|
+
instances.first.instance_id
|
|
41
|
+
else
|
|
42
|
+
false
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def create(run_params)
|
|
47
|
+
result = Ec2.run_instances(run_params)
|
|
48
|
+
result.instances[0].instance_id
|
|
49
|
+
rescue Aws::Errors::ServiceError, ArgumentError
|
|
50
|
+
raise Error, "while creating instance #{@name}"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
require_relative 'image'
|
|
2
|
+
module Awsom
|
|
3
|
+
class InstanceDSL
|
|
4
|
+
|
|
5
|
+
attr_reader :setup_proc
|
|
6
|
+
|
|
7
|
+
def initialize(name, subnet_id:, vpc_id:)
|
|
8
|
+
@name = name
|
|
9
|
+
@subnet_id = subnet_id
|
|
10
|
+
@vpc_id = vpc_id
|
|
11
|
+
@volumes = {}
|
|
12
|
+
@security_groups = []
|
|
13
|
+
@attrs = {
|
|
14
|
+
image_id: nil,
|
|
15
|
+
key_name: nil,
|
|
16
|
+
size: nil,
|
|
17
|
+
associate_public_ip_address: true,
|
|
18
|
+
credits: "unlimited",
|
|
19
|
+
term_protect: false,
|
|
20
|
+
}
|
|
21
|
+
@setup_proc = nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def read(&block)
|
|
25
|
+
instance_eval(&block)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def run_params
|
|
29
|
+
params
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def include_config(pr)
|
|
33
|
+
instance_eval(&pr) if pr
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def set(key, val)
|
|
39
|
+
raise Error, "unknown attr #{key}" if not @attrs.key?(key)
|
|
40
|
+
@attrs[key] = val
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def get(key)
|
|
44
|
+
val = @attrs[key]
|
|
45
|
+
raise Error, "key #{key} not found" if val.nil?
|
|
46
|
+
val
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def setup(&block)
|
|
50
|
+
@setup_proc = block
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def setup_salt(template, &block)
|
|
54
|
+
require_relative 'salt_bootstrap'
|
|
55
|
+
@setup_proc = Proc.new do |instance|
|
|
56
|
+
ip = instance.private_ip_address
|
|
57
|
+
bootstrap = SaltBootstrap.new(@name, ip: ip, template: template)
|
|
58
|
+
bootstrap.run
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def image_id(val)
|
|
63
|
+
set :image_id, val
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def associate_public_ip_address(val)
|
|
67
|
+
set :associate_public_ip_address, val
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def key_name(val)
|
|
71
|
+
set :key_name, val
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def size(val)
|
|
75
|
+
set :size, val
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def volume(name, size:, **opts)
|
|
79
|
+
ebs = { delete_on_termination: false, volume_type: "gp2", iops: nil }
|
|
80
|
+
ebs[:volume_size] = size
|
|
81
|
+
ebs.merge!(opts)
|
|
82
|
+
@volumes[name] = { device_name: name, ebs: ebs }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def security_groups(val)
|
|
86
|
+
@security_groups = Set.new(val)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def load_ami_volumes
|
|
90
|
+
Image.block_device_mappings(get :image_id).each do |m|
|
|
91
|
+
name = m.device_name
|
|
92
|
+
ebs = m.ebs.to_h
|
|
93
|
+
if @volumes.key?(name)
|
|
94
|
+
@volumes[name][:ebs].merge!(ebs) { |k, old, new| old }
|
|
95
|
+
else
|
|
96
|
+
@volumes[name] = { device_name: name, ebs: ebs }
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def network_interfaces
|
|
102
|
+
groups = @security_groups.map do |i|
|
|
103
|
+
SecurityGroup.id(i, @vpc_id)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
netif = {
|
|
107
|
+
associate_public_ip_address: get(:associate_public_ip_address),
|
|
108
|
+
delete_on_termination: true,
|
|
109
|
+
device_index: 0,
|
|
110
|
+
groups: groups,
|
|
111
|
+
subnet_id: @subnet_id
|
|
112
|
+
}
|
|
113
|
+
[netif]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def params
|
|
117
|
+
load_ami_volumes
|
|
118
|
+
tag_specifications = [
|
|
119
|
+
{ resource_type: "instance", tags: [ { key: "Name", value: @name } ] },
|
|
120
|
+
{ resource_type: "volume", tags: [ { key: "Name", value: @name } ] }
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
params = {
|
|
124
|
+
image_id: get(:image_id),
|
|
125
|
+
instance_type: get(:size),
|
|
126
|
+
key_name: get(:key_name),
|
|
127
|
+
disable_api_termination: get(:term_protect),
|
|
128
|
+
block_device_mappings: @volumes.values,
|
|
129
|
+
network_interfaces: network_interfaces,
|
|
130
|
+
tag_specifications: tag_specifications,
|
|
131
|
+
min_count: 1,
|
|
132
|
+
max_count: 1
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
params[:credit_specification] = { cpu_credits: get(:credits) } if get(:size) =~ /\At[23]/
|
|
136
|
+
params
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
data/lib/awsom/lock.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require_relative 'logger'
|
|
2
|
+
module Awsom
|
|
3
|
+
class Lock
|
|
4
|
+
|
|
5
|
+
include Logger
|
|
6
|
+
|
|
7
|
+
def acquire
|
|
8
|
+
logger.debug "acquiring lock"
|
|
9
|
+
lock_acquired = lock_file.flock(File::LOCK_NB | File::LOCK_EX)
|
|
10
|
+
raise "exclusive lock not available" if not lock_acquired
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def lock_file
|
|
14
|
+
@lock_file ||= File.open(".awsom.lock", "a+")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
data/lib/awsom/logger.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
require_relative 'custom_logger'
|
|
3
|
+
module Awsom
|
|
4
|
+
module Logger
|
|
5
|
+
|
|
6
|
+
def self.logger
|
|
7
|
+
@logger ||= CustomLogger.new(STDOUT)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.stderr
|
|
11
|
+
@stderr ||= ::Logger.new(STDERR)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def logger
|
|
15
|
+
::Awsom::Logger.logger
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def stderr
|
|
19
|
+
::Awsom::Logger.stderr
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def debug?
|
|
23
|
+
logger.level == ::Logger::DEBUG
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require 'tmpdir'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
require 'pty'
|
|
4
|
+
require 'erb'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
|
|
7
|
+
module Awsom
|
|
8
|
+
class SaltBootstrap
|
|
9
|
+
|
|
10
|
+
def initialize(name, ip:, template:)
|
|
11
|
+
@name = name
|
|
12
|
+
@ip = ip
|
|
13
|
+
@template = template
|
|
14
|
+
validate!
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run
|
|
18
|
+
copy_bootstrap_data
|
|
19
|
+
puts "running bootstrap.sh"
|
|
20
|
+
run_bootstrap_sh
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def copy_bootstrap_data
|
|
26
|
+
Dir.mktmpdir("./") do |dir|
|
|
27
|
+
puts "generating salt key"
|
|
28
|
+
system "salt-key", "--gen-keys-dir=#{dir}", "--gen-keys=minion"
|
|
29
|
+
|
|
30
|
+
puts "generating bootstrap script"
|
|
31
|
+
erb = ERB.new(File.read @template)
|
|
32
|
+
script = erb.result_with_hash({name: @name})
|
|
33
|
+
File.write("#{dir}/bootstrap.sh", script)
|
|
34
|
+
|
|
35
|
+
scp "#{dir}/minion.pem", "#{dir}/minion.pub", "#{dir}/bootstrap.sh", "root@#{@ip}:/root/"
|
|
36
|
+
FileUtils.mv "#{dir}/minion.pub", "/etc/salt/pki/master/minions/#{@name}"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def run_bootstrap_sh
|
|
41
|
+
FileUtils.mkdir_p "log"
|
|
42
|
+
log_file = "log/#{@name}.log"
|
|
43
|
+
script = 'bash /root/bootstrap.sh; rm /root/bootstrap.sh'
|
|
44
|
+
r, w, pid = PTY.spawn "ssh", "-tt", *ssh_opts, "root@#{@ip}", script
|
|
45
|
+
File.open log_file, "w" do |f|
|
|
46
|
+
pty_read(r, f)
|
|
47
|
+
end
|
|
48
|
+
pid, status = Process.waitpid2(pid)
|
|
49
|
+
p status
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def pty_read(io, out)
|
|
53
|
+
io.each { |line| out.print line }
|
|
54
|
+
rescue EOFError,Errno::ECONNRESET, Errno::EPIPE, Errno::EIO => e
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def scp(*args, tries: 5)
|
|
58
|
+
begin
|
|
59
|
+
tries = tries - 1
|
|
60
|
+
system "timeout", "60", "scp", *ssh_opts, *args, exception: true
|
|
61
|
+
rescue RuntimeError
|
|
62
|
+
sleep 5
|
|
63
|
+
retry if tries > 0
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def ssh_opts(attempts = 65)
|
|
68
|
+
[
|
|
69
|
+
"-oConnectTimeout=1",
|
|
70
|
+
"-oConnectionAttempts=#{attempts}",
|
|
71
|
+
"-oStrictHostKeyChecking=no",
|
|
72
|
+
"-oUserKnownHostsFile=/dev/null",
|
|
73
|
+
"-oBatchMode=yes"
|
|
74
|
+
]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def validate!
|
|
78
|
+
if @name !~ /\A[a-zA-Z0-9\-\. ]+\z/
|
|
79
|
+
raise "invalid name #{@name}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
if @ip !~ /\A[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\z/
|
|
83
|
+
raise "invalid ip #{@ip}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
require_relative 'logger'
|
|
2
|
+
require 'set'
|
|
3
|
+
|
|
4
|
+
module Awsom
|
|
5
|
+
class SecurityGroup
|
|
6
|
+
|
|
7
|
+
include Logger
|
|
8
|
+
|
|
9
|
+
DiffStruct = Struct.new(:to_add, :to_remove, keyword_init: true)
|
|
10
|
+
|
|
11
|
+
def self.id(name, vpc_id)
|
|
12
|
+
sg = new(name, vpc_id: vpc_id)
|
|
13
|
+
sg.id
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def initialize(name, vpc_id:)
|
|
17
|
+
@name = name
|
|
18
|
+
@vpc_id = vpc_id
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def id
|
|
22
|
+
id = find_id
|
|
23
|
+
raise Error, "specified security_group #{@name} doesn't exist in vpc #{@vpc_id}" if not id
|
|
24
|
+
id
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def description(description)
|
|
28
|
+
@description = description
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def created(&block)
|
|
32
|
+
@desired_rules = Set.new
|
|
33
|
+
instance_eval &block
|
|
34
|
+
@id = find_id || create
|
|
35
|
+
sync
|
|
36
|
+
@id
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def sg
|
|
42
|
+
@sg ||= Aws::EC2::SecurityGroup.new(id)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def find_id
|
|
46
|
+
filters = [
|
|
47
|
+
{ name: "group-name", values: [@name] },
|
|
48
|
+
{ name: "vpc-id", values: [@vpc_id] }
|
|
49
|
+
]
|
|
50
|
+
result = Ec2.describe_security_groups(filters: filters)
|
|
51
|
+
num_results = result.security_groups.size
|
|
52
|
+
if num_results == 1
|
|
53
|
+
result.security_groups.first.group_id
|
|
54
|
+
else
|
|
55
|
+
false
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def create
|
|
60
|
+
resp = Ec2.create_security_group(
|
|
61
|
+
group_name: @name,
|
|
62
|
+
description: @description,
|
|
63
|
+
vpc_id: @vpc_id
|
|
64
|
+
)
|
|
65
|
+
logger.info "(#{@name}) created security group"
|
|
66
|
+
resp.group_id
|
|
67
|
+
rescue Aws::Errors::ServiceError, ArgumentError
|
|
68
|
+
error "while creating security_group #{@name}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def parse_ports(port)
|
|
72
|
+
case port
|
|
73
|
+
when Integer
|
|
74
|
+
[port, port]
|
|
75
|
+
when Range
|
|
76
|
+
[port.first, port.last]
|
|
77
|
+
when /\A([0-9]+)\z/
|
|
78
|
+
[$1.to_i, $1.to_i]
|
|
79
|
+
when /\A([0-9]+)\-([0-9]+)\z/
|
|
80
|
+
[$1.to_i, $2.to_i]
|
|
81
|
+
else
|
|
82
|
+
raise Error, "invalid port specified #{port}"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def tcp(port, cidr_ip:)
|
|
87
|
+
(from_port, to_port) = parse_ports(port)
|
|
88
|
+
rule(ip_protocol: "tcp", from_port: from_port, to_port: to_port, cidr_ip: cidr_ip)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def udp(port, cidr_ip:)
|
|
92
|
+
(from_port, to_port) = parse_ports(port)
|
|
93
|
+
rule(ip_protocol: "udp", from_port: from_port, to_port: to_port, cidr_ip: cidr_ip)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def icmp(cidr_ip)
|
|
97
|
+
rule(ip_protocol: "icmp", from_port: -1, to_port: -1, cidr_ip: cidr_ip)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def rule(ip_protocol:, from_port:, to_port:, cidr_ip:)
|
|
101
|
+
rule = {
|
|
102
|
+
ip_protocol: ip_protocol,
|
|
103
|
+
from_port: from_port,
|
|
104
|
+
to_port: to_port,
|
|
105
|
+
cidr_ip: cidr_ip
|
|
106
|
+
}
|
|
107
|
+
@desired_rules << rule
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def current_rules
|
|
111
|
+
current_rules = Set.new
|
|
112
|
+
sg.ip_permissions.each do |permission|
|
|
113
|
+
rules = permission_to_rules(permission)
|
|
114
|
+
rules.each { |r| current_rules << r }
|
|
115
|
+
end
|
|
116
|
+
current_rules
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def diff(current, desired)
|
|
120
|
+
to_add = desired - current
|
|
121
|
+
to_remove = current - desired
|
|
122
|
+
DiffStruct.new(to_add: to_add, to_remove: to_remove)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def sync
|
|
126
|
+
current = current_rules
|
|
127
|
+
ingress_diff = diff(current, @desired_rules)
|
|
128
|
+
ingress_diff.to_add.each { |r| authorize_ingress r }
|
|
129
|
+
ingress_diff.to_remove.each { |r| revoke_ingress r }
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def authorize_ingress(rule)
|
|
133
|
+
sg.authorize_ingress rule if not @dry_run
|
|
134
|
+
logger.info "(#{@name}) #{"+".green.bold} allow #{rule[:cidr_ip]} #{rule[:from_port]}-#{rule[:to_port]}"
|
|
135
|
+
rescue Aws::Errors::ServiceError
|
|
136
|
+
error "error authorizing rule #{rule}"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def revoke_ingress(rule)
|
|
140
|
+
sg.revoke_ingress rule if not @dry_run
|
|
141
|
+
logger.info "(#{@name}) #{"-".red.bold} allow #{rule[:cidr_ip]} #{rule[:from_port]}-#{rule[:to_port]}"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def permission_to_rules(permission)
|
|
145
|
+
rules = Set.new
|
|
146
|
+
permission.ip_ranges.each do |r|
|
|
147
|
+
rule = {
|
|
148
|
+
ip_protocol: permission.ip_protocol,
|
|
149
|
+
from_port: permission.from_port,
|
|
150
|
+
to_port: permission.to_port,
|
|
151
|
+
cidr_ip: r.cidr_ip
|
|
152
|
+
}
|
|
153
|
+
rules << rule
|
|
154
|
+
end
|
|
155
|
+
rules
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
end
|
|
159
|
+
end
|
data/lib/awsom/subnet.rb
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require_relative 'logger'
|
|
2
|
+
|
|
3
|
+
module Awsom
|
|
4
|
+
class Subnet
|
|
5
|
+
|
|
6
|
+
include Logger
|
|
7
|
+
|
|
8
|
+
def initialize(name, cidr:, az:, vpc_id:)
|
|
9
|
+
@vpc_id = vpc_id
|
|
10
|
+
@name = name
|
|
11
|
+
@cidr = cidr
|
|
12
|
+
@az = az
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def id
|
|
16
|
+
id = find_id
|
|
17
|
+
error "specified subnet #{@name} doesn't exist in vpc #{@vpc_id}" if not id
|
|
18
|
+
id
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def created
|
|
22
|
+
id = find_id
|
|
23
|
+
if id
|
|
24
|
+
subnet = subnet(id)
|
|
25
|
+
tag(subnet) if not tagged?(subnet)
|
|
26
|
+
return id
|
|
27
|
+
end
|
|
28
|
+
create
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def create
|
|
34
|
+
resp = Ec2.create_subnet(
|
|
35
|
+
vpc_id: @vpc_id,
|
|
36
|
+
cidr_block: @cidr,
|
|
37
|
+
availability_zone: @availability_zone
|
|
38
|
+
)
|
|
39
|
+
logger.info "(#{@name}) created subnet: #{@cidr}"
|
|
40
|
+
id = resp.subnet.subnet_id
|
|
41
|
+
subnet = subnet(id)
|
|
42
|
+
tag(subnet)
|
|
43
|
+
id
|
|
44
|
+
rescue Aws::Errors::ServiceError, ArgumentError
|
|
45
|
+
raise Error, "while creating subnet #{@name}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def subnet(id)
|
|
49
|
+
Aws::EC2::Subnet.new(id)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def tag(subnet)
|
|
53
|
+
return if tagged?(subnet)
|
|
54
|
+
subnet.create_tags(
|
|
55
|
+
tags: [ { key: "Name", value: @name } ]
|
|
56
|
+
)
|
|
57
|
+
subnet.load
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def tagged?(subnet)
|
|
61
|
+
subnet.tags.any? { |tag| tag.key == "Name"}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def verify(subnet)
|
|
65
|
+
error "availability zone mismatch for subnet #{@name}" if subnet.availability_zone != @az
|
|
66
|
+
name_tag = subnet.tags.find { |tag| tag.key == "Name" }
|
|
67
|
+
return if not name_tag
|
|
68
|
+
if name_tag.value != @name
|
|
69
|
+
raise Error, "subnet #{@name} already tagged with another name #{name_tag}"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def find_id
|
|
74
|
+
filters = [
|
|
75
|
+
{ name: "vpc-id", values: [@vpc_id.to_s] },
|
|
76
|
+
{ name: "cidr", values: [@cidr.to_s] }
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
result = Ec2.describe_subnets(filters: filters)
|
|
80
|
+
num_results = result.subnets.size
|
|
81
|
+
raise Error, "mulitple subnets found with name #{@name}" if num_results > 1
|
|
82
|
+
if num_results == 1
|
|
83
|
+
subnet = result.subnets.first
|
|
84
|
+
verify(subnet)
|
|
85
|
+
result.subnets.first.subnet_id
|
|
86
|
+
else
|
|
87
|
+
false
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
end
|
|
92
|
+
end
|
data/lib/awsom/vpc.rb
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require_relative 'logger'
|
|
2
|
+
|
|
3
|
+
module Awsom
|
|
4
|
+
class Vpc
|
|
5
|
+
|
|
6
|
+
include Logger
|
|
7
|
+
|
|
8
|
+
def initialize(name, cidr:)
|
|
9
|
+
@name = name
|
|
10
|
+
@cidr = cidr
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def id
|
|
14
|
+
id = find_id
|
|
15
|
+
error "specified vpc #{@name} doesn't exist" if not id
|
|
16
|
+
id
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def created
|
|
20
|
+
id = find_id
|
|
21
|
+
if id
|
|
22
|
+
vpc = vpc(id)
|
|
23
|
+
tag(vpc) if not tagged?(vpc)
|
|
24
|
+
id
|
|
25
|
+
else
|
|
26
|
+
create
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def create
|
|
33
|
+
resp = Ec2.create_vpc(cidr_block: @cidr)
|
|
34
|
+
logger.info "(#{@name}) created vpc: #{@cidr}"
|
|
35
|
+
id = resp.vpc.vpc_id
|
|
36
|
+
vpc = vpc(id)
|
|
37
|
+
tag(vpc)
|
|
38
|
+
id
|
|
39
|
+
rescue Aws::Errors::ServiceError, ArgumentError
|
|
40
|
+
raise Error, "while creating vpc #{@name}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def vpc(id)
|
|
44
|
+
Aws::EC2::Vpc.new(id)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def tag(vpc)
|
|
48
|
+
return if tagged?(vpc)
|
|
49
|
+
vpc.create_tags(
|
|
50
|
+
tags: [ { key: "Name", value: @name } ]
|
|
51
|
+
)
|
|
52
|
+
vpc.load
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def tagged?(vpc)
|
|
56
|
+
vpc.tags.any? { |tag| tag.key == "Name"}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def verify(vpc)
|
|
60
|
+
name_tag = vpc.tags.find { |tag| tag.key == "Name" }
|
|
61
|
+
return if not name_tag
|
|
62
|
+
if name_tag.value != @name
|
|
63
|
+
raise Error, "vpc #{@name} already tagged with another name #{name_tag}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def find_id
|
|
68
|
+
filters = [
|
|
69
|
+
{ name: "cidr", values: [@cidr.to_s] }
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
result = Ec2.describe_vpcs(filters: filters)
|
|
73
|
+
num_results = result.vpcs.size
|
|
74
|
+
raise Error, "mulitple vpcs found for name #{@name}" if num_results > 1
|
|
75
|
+
if num_results == 1
|
|
76
|
+
vpc = result.vpcs.first
|
|
77
|
+
verify(vpc)
|
|
78
|
+
result.vpc.first.vpc_id
|
|
79
|
+
else
|
|
80
|
+
false
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: awsom
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Neeraj
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2019-05-27 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.17'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.17'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '10.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '10.0'
|
|
41
|
+
description:
|
|
42
|
+
email:
|
|
43
|
+
executables:
|
|
44
|
+
- awsom
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- ".gitignore"
|
|
49
|
+
- Gemfile
|
|
50
|
+
- LICENSE.txt
|
|
51
|
+
- README.md
|
|
52
|
+
- Rakefile
|
|
53
|
+
- awsom.gemspec
|
|
54
|
+
- bin/console
|
|
55
|
+
- bin/setup
|
|
56
|
+
- dev_exe/awsom
|
|
57
|
+
- exe/awsom
|
|
58
|
+
- lib/awsom.rb
|
|
59
|
+
- lib/awsom/application.rb
|
|
60
|
+
- lib/awsom/cli/resources.rb
|
|
61
|
+
- lib/awsom/config.rb
|
|
62
|
+
- lib/awsom/custom_logger.rb
|
|
63
|
+
- lib/awsom/ec2.rb
|
|
64
|
+
- lib/awsom/error.rb
|
|
65
|
+
- lib/awsom/ext/string.rb
|
|
66
|
+
- lib/awsom/image.rb
|
|
67
|
+
- lib/awsom/instance.rb
|
|
68
|
+
- lib/awsom/instance_dsl.rb
|
|
69
|
+
- lib/awsom/lock.rb
|
|
70
|
+
- lib/awsom/logger.rb
|
|
71
|
+
- lib/awsom/salt_bootstrap.rb
|
|
72
|
+
- lib/awsom/security_group.rb
|
|
73
|
+
- lib/awsom/subnet.rb
|
|
74
|
+
- lib/awsom/version.rb
|
|
75
|
+
- lib/awsom/vpc.rb
|
|
76
|
+
homepage:
|
|
77
|
+
licenses:
|
|
78
|
+
- MIT
|
|
79
|
+
metadata: {}
|
|
80
|
+
post_install_message:
|
|
81
|
+
rdoc_options: []
|
|
82
|
+
require_paths:
|
|
83
|
+
- lib
|
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0'
|
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
|
+
requirements:
|
|
91
|
+
- - ">="
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
version: '0'
|
|
94
|
+
requirements: []
|
|
95
|
+
rubygems_version: 3.0.3
|
|
96
|
+
signing_key:
|
|
97
|
+
specification_version: 4
|
|
98
|
+
summary: Aws IaC dsl
|
|
99
|
+
test_files: []
|