harpoon 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/harpoon.gemspec +2 -1
- data/lib/harpoon.rb +4 -2
- data/lib/harpoon/config.rb +48 -5
- data/lib/harpoon/logger.rb +50 -0
- data/lib/harpoon/runner.rb +14 -21
- data/lib/harpoon/service.rb +58 -0
- data/lib/harpoon/services/s3.rb +117 -97
- data/lib/harpoon/services/test.rb +3 -2
- data/lib/harpoon/templates/harpoon.json +2 -4
- data/test/test_config.rb +49 -0
- data/test/test_directory/nested_files/harpoon.json +3 -5
- data/test/test_directory/test_client/harpoon.json +3 -5
- data/test/test_directory/unnested_files/harpoon.json +3 -5
- data/test/test_hosting.rb +2 -2
- data/test/test_runner.rb +4 -8
- data/test/test_service_base.rb +58 -0
- data/test/test_services.rb +1 -0
- metadata +32 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67816218225a067d3f8b66c9673338f17204fd3d
|
4
|
+
data.tar.gz: 75ba5ab88e915ad54987f0518a620ba4bbeb19b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f61d684a67f40527bc41396e63d3a5115ae2d02ca4677b315890b33cb6202f90ea50bfab68022ab3bd6895ed51ab86fd3574a8a52c3d4fff8005b3c2158e6f3
|
7
|
+
data.tar.gz: d1af82d8c71e92dcdabf854a91cdbc451413dc169f2e4498dfb59f0418aeb768e30fde4bcc560b3ec25f082060b1daac800d4e92f393219afea5e9c65be5a302
|
data/README.md
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# Harpoon
|
2
|
-
A static site deployer
|
2
|
+
A static site deployer, initially targeting Amazon s3
|
3
3
|
|
4
|
-
This is a work in progress,
|
4
|
+
This project is a work in progress, but it's already pretty functional and serves my purposes well. Feedback/issues/encouragement/complaints are all awesome and appreciated!
|
5
|
+
|
6
|
+
Learn more at [www.getharpoon.com](http://www.getharpoon.com).
|
data/harpoon.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'harpoon'
|
3
|
-
s.version = '0.0.
|
3
|
+
s.version = '0.0.4'
|
4
4
|
s.date = '2014-08-20'
|
5
5
|
s.summary = "A single page app deployer for amazon s3"
|
6
6
|
s.description = "Deploy small server-less webapps to amazon s3, including buckets, dns and permissions"
|
@@ -14,5 +14,6 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.add_dependency "aws-sdk", "~> 1.51.0"
|
15
15
|
s.add_dependency "public_suffix", "~> 1.4.5"
|
16
16
|
s.add_dependency 'colorize', '~> 0.7.3'
|
17
|
+
s.add_dependency 'activesupport', "~> 4.1.5"
|
17
18
|
s.homepage = 'http://www.getharpoon.com'
|
18
19
|
end
|
data/lib/harpoon.rb
CHANGED
@@ -4,8 +4,10 @@ module Harpoon
|
|
4
4
|
require_relative "harpoon/errors"
|
5
5
|
require_relative "harpoon/config"
|
6
6
|
require_relative "harpoon/runner"
|
7
|
+
require_relative "harpoon/logger"
|
7
8
|
|
8
9
|
# Services
|
9
|
-
require_relative "harpoon/
|
10
|
-
require_relative "harpoon/services/
|
10
|
+
require_relative "harpoon/service"
|
11
|
+
require_relative "harpoon/services/test"
|
12
|
+
require_relative "harpoon/services/s3"
|
11
13
|
end
|
data/lib/harpoon/config.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "json"
|
2
2
|
require "fileutils"
|
3
|
+
require "active_support/core_ext/hash"
|
3
4
|
|
4
5
|
module Harpoon
|
5
6
|
class Config
|
@@ -43,18 +44,60 @@ module Harpoon
|
|
43
44
|
end
|
44
45
|
end
|
45
46
|
|
46
|
-
attr_reader :files
|
47
|
-
|
48
47
|
# Initialize a new config object with the data loaded
|
49
48
|
def initialize(data = {}, logger = nil)
|
50
|
-
@
|
49
|
+
@data = data.symbolize_keys
|
51
50
|
@logger = logger
|
52
|
-
@
|
51
|
+
@required_values = []
|
52
|
+
end
|
53
|
+
|
54
|
+
def files
|
55
|
+
Dir.glob(File.join(@data[:directory], "**", "*")).select {|f| !File.directory?(f) && File.basename(f) != "harpoon.json"} if @data[:directory]
|
56
|
+
end
|
57
|
+
|
58
|
+
def deep_merge!(h1)
|
59
|
+
@data.deep_merge! h1.symbolize_keys
|
60
|
+
end
|
61
|
+
|
62
|
+
def requires(required_value)
|
63
|
+
@required_values.push(required_value.to_sym)
|
64
|
+
end
|
65
|
+
|
66
|
+
def validate!
|
67
|
+
@required_values.each do |r|
|
68
|
+
raise Harpoon::Errors::InvalidConfiguration, "Missing #{r} configuration" if @data[r] == nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def validate
|
73
|
+
begin
|
74
|
+
validate!
|
75
|
+
rescue Harpoon::Errors::InvalidConfiguration
|
76
|
+
return false
|
77
|
+
else
|
78
|
+
return true
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def data
|
83
|
+
@data.dup
|
84
|
+
end
|
85
|
+
|
86
|
+
def dup
|
87
|
+
c = Harpoon::Config.new(self.data)
|
88
|
+
req = @required_values.dup
|
89
|
+
c.instance_eval {@required_values = req}
|
90
|
+
c
|
53
91
|
end
|
54
92
|
|
55
93
|
# Check for the configuration item
|
56
94
|
def method_missing(method, *args)
|
57
|
-
|
95
|
+
method = method.to_s
|
96
|
+
if method.end_with? "="
|
97
|
+
@data[method.gsub(/=$/, "").to_sym] = args.first
|
98
|
+
else
|
99
|
+
@data[method.to_sym]
|
100
|
+
end
|
58
101
|
end
|
59
102
|
end
|
60
103
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "logger"
|
2
|
+
require "colorize"
|
3
|
+
|
4
|
+
module Harpoon
|
5
|
+
class Logger < Logger
|
6
|
+
SEVS = %w(DEBUG INFO WARN ERROR FATAL SUGGEST PASS FAIL)
|
7
|
+
CHECKMARK = "\u2713"
|
8
|
+
ARROW = "\u279C"
|
9
|
+
|
10
|
+
def format_severity(severity)
|
11
|
+
SEVS[severity] || 'ANY'
|
12
|
+
end
|
13
|
+
|
14
|
+
def suggest(message, progname = nil, &block)
|
15
|
+
add(5, message, progname, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def pass(message, progname = nil, &block)
|
19
|
+
add(6, message, progname, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def fail(message, progname = nil, &block)
|
23
|
+
add(7, message, progname, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def format_message(severity, datetime, progname, msg)
|
27
|
+
case severity.to_s.downcase
|
28
|
+
when "debug"
|
29
|
+
"DEBUG: #{msg}\n"
|
30
|
+
when "info"
|
31
|
+
"#{ARROW} #{msg}\n".colorize(:gray)
|
32
|
+
when "warn"
|
33
|
+
"#{msg}\n".colorize(:yellow)
|
34
|
+
when "error"
|
35
|
+
"#{msg}\n".colorize(:orange)
|
36
|
+
when "fatal"
|
37
|
+
"#{msg}\n".colorize(:red)
|
38
|
+
when "pass"
|
39
|
+
"#{CHECKMARK} - #{msg}\n".colorize(:green)
|
40
|
+
when "fail"
|
41
|
+
"X - #{msg}\n".colorize(:red)
|
42
|
+
when "suggest"
|
43
|
+
"! - #{msg}\n".colorize(:blue)
|
44
|
+
else
|
45
|
+
"#{severity} - #{msg}\n"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
data/lib/harpoon/runner.rb
CHANGED
@@ -7,11 +7,23 @@ module Harpoon
|
|
7
7
|
@logger = load_logger(options[:log_level])
|
8
8
|
@config = Harpoon::Config.read(options[:config] || Dir.pwd, @logger)
|
9
9
|
@auth = load_auth
|
10
|
-
|
10
|
+
end
|
11
|
+
|
12
|
+
def doctor
|
13
|
+
#we specify doctor specifically to test the config
|
14
|
+
begin
|
15
|
+
@service = load_host
|
16
|
+
rescue Harpoon::Errors::InvalidConfiguration => e
|
17
|
+
@logger.fail("Invalid configuration - #{e.message}")
|
18
|
+
else
|
19
|
+
@logger.pass("Valid configuration")
|
20
|
+
@service.doctor
|
21
|
+
end
|
11
22
|
end
|
12
23
|
|
13
24
|
def method_missing(method, *args)
|
14
25
|
#don't know about this here, must be for the service
|
26
|
+
@service = load_host
|
15
27
|
@service.send(method, *args)
|
16
28
|
end
|
17
29
|
|
@@ -39,7 +51,7 @@ module Harpoon
|
|
39
51
|
|
40
52
|
def load_logger(log_level = "info")
|
41
53
|
log_level ||= "info"
|
42
|
-
logger = Logger.new(STDOUT)
|
54
|
+
logger = Harpoon::Logger.new(STDOUT)
|
43
55
|
|
44
56
|
#set log level
|
45
57
|
case log_level.downcase
|
@@ -54,25 +66,6 @@ module Harpoon
|
|
54
66
|
when "fatal"
|
55
67
|
logger.level = Logger::FATAL
|
56
68
|
end
|
57
|
-
|
58
|
-
#set log formatter
|
59
|
-
logger.formatter = proc do |severity, datetime, progname, msg|
|
60
|
-
case severity.to_s.downcase
|
61
|
-
when "debug"
|
62
|
-
"DEBUG: #{msg}\n".colorize(:light_blue)
|
63
|
-
when "info"
|
64
|
-
"#{msg}\n".colorize(:gray)
|
65
|
-
when "warn"
|
66
|
-
"#{msg}\n".colorize(:yellow)
|
67
|
-
when "error"
|
68
|
-
"#{msg}\n".colorize(:orange)
|
69
|
-
when "fatal"
|
70
|
-
"#{msg}\n".colorize(:red)
|
71
|
-
else
|
72
|
-
"#{severity} - #{msg}\n"
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
69
|
logger
|
77
70
|
end
|
78
71
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Harpoon
|
2
|
+
# All services should include this module to work with Harpoon
|
3
|
+
module Service
|
4
|
+
|
5
|
+
#include all the class methods we need
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def auth(key, help = nil)
|
13
|
+
required_auth.push({identifier: key, help: help})
|
14
|
+
end
|
15
|
+
|
16
|
+
def option(key, *args)
|
17
|
+
args = args.last.is_a?(Hash) ? args.pop : {}
|
18
|
+
|
19
|
+
default_options.send("#{key}=", args[:default]) if args[:default]
|
20
|
+
default_options.requires(key) if args[:required]
|
21
|
+
end
|
22
|
+
|
23
|
+
# Defaults setup by the service
|
24
|
+
# These are overridden by the hosting_options in the configuration
|
25
|
+
# file if they exist
|
26
|
+
def default_options
|
27
|
+
@default_options ||= Harpoon::Config.new
|
28
|
+
end
|
29
|
+
|
30
|
+
# An array of required authentication params
|
31
|
+
# These are provided to services as @auth[:key] values
|
32
|
+
def required_auth
|
33
|
+
@required_auth ||= []
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#instance methods
|
38
|
+
def initialize(config, auth, logger)
|
39
|
+
@logger = logger
|
40
|
+
@auth = {}
|
41
|
+
@options = self.class.default_options.dup
|
42
|
+
|
43
|
+
@options.deep_merge!(config.hosting_options || {})
|
44
|
+
#copy over the directory as well
|
45
|
+
@options.directory = config.directory
|
46
|
+
|
47
|
+
@options.validate!
|
48
|
+
|
49
|
+
if self.class.required_auth.length > 0
|
50
|
+
req = self.class.required_auth.map {|h| h[:help]}
|
51
|
+
auth_results = auth.__send__(:get_or_ask, config.hosting, *req)
|
52
|
+
self.class.required_auth.each_with_index do |r, index|
|
53
|
+
@auth[r[:identifier]] = auth_results[index]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/harpoon/services/s3.rb
CHANGED
@@ -6,96 +6,78 @@ require "pathname"
|
|
6
6
|
module Harpoon
|
7
7
|
module Services
|
8
8
|
class S3
|
9
|
-
|
10
|
-
# Store what we're given for later
|
11
|
-
@config = config
|
12
|
-
@auth = auth
|
13
|
-
@logger = logger
|
9
|
+
include Harpoon::Service
|
14
10
|
|
15
|
-
|
16
|
-
|
11
|
+
auth :key, "Key"
|
12
|
+
auth :secret, "Secret"
|
17
13
|
|
18
|
-
|
19
|
-
region = config.hosting_options["region"]
|
20
|
-
else
|
21
|
-
region = "us-west-2"
|
22
|
-
end
|
23
|
-
|
24
|
-
AWS.config(access_key_id: @credentials[0], secret_access_key: @credentials[1], region: region)
|
25
|
-
|
26
|
-
# setup amazon interfaces
|
27
|
-
@s3 = AWS::S3.new
|
28
|
-
@r53 = AWS::Route53.new
|
29
|
-
end
|
14
|
+
option :region, default: "us-west-2"
|
30
15
|
|
31
16
|
def setup
|
32
|
-
if @
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
bucket.website_configuration = cw
|
70
|
-
setup_dns_alias(f, bucket)
|
71
|
-
end
|
17
|
+
if @options.primary_domain
|
18
|
+
#primary domain detected, let's make sure it exists
|
19
|
+
bucket = setup_bucket(@options.primary_domain)
|
20
|
+
#setup bucket to server webpages
|
21
|
+
@logger.info "Setting primary domain as website"
|
22
|
+
bucket.configure_website
|
23
|
+
#setup ACL
|
24
|
+
@logger.info "Setting bucket policy"
|
25
|
+
policy = AWS::S3::Policy.new
|
26
|
+
policy.allow(
|
27
|
+
actions: ['s3:GetObject'],
|
28
|
+
resources: [bucket.objects],
|
29
|
+
principals: :any
|
30
|
+
)
|
31
|
+
|
32
|
+
@logger.debug policy.to_json
|
33
|
+
|
34
|
+
bucket.policy = policy
|
35
|
+
|
36
|
+
|
37
|
+
history = setup_bucket(rollback_bucket(@options.primary_domain))
|
38
|
+
@logger.info "Created rollback bucket"
|
39
|
+
|
40
|
+
setup_dns_alias(@options.primary_domain, bucket)
|
41
|
+
|
42
|
+
|
43
|
+
if @options.forwarded_domain
|
44
|
+
#we also want to forward some domains
|
45
|
+
#make sure we have an array
|
46
|
+
#TODO : Move all of this nonsense to the config object, it should be validating this stuff
|
47
|
+
forwarded = @options.forwarded_domain.is_a?(Array) ? @options.forwarded_domain : [@options.forwarded_domain]
|
48
|
+
forwarded.each do |f|
|
49
|
+
bucket = setup_bucket(f)
|
50
|
+
@logger.info "Seting up redirect to primary"
|
51
|
+
cw = AWS::S3::WebsiteConfiguration.new({redirect_all_requests_to: {host_name: @options.primary_domain}})
|
52
|
+
bucket.website_configuration = cw
|
53
|
+
setup_dns_alias(f, bucket)
|
72
54
|
end
|
73
|
-
|
74
|
-
# print out DNS settings
|
75
|
-
print_dns_settings(@config.domain["primary"])
|
76
55
|
end
|
56
|
+
|
57
|
+
# print out DNS settings
|
58
|
+
print_dns_settings(@options.primary_domain)
|
77
59
|
end
|
78
60
|
end
|
79
61
|
|
80
62
|
def deploy
|
81
|
-
raise Harpoon::Errors::InvalidConfiguration, "Missing list of files" unless @
|
63
|
+
raise Harpoon::Errors::InvalidConfiguration, "Missing list of files" unless @options.files && @options.directory && @options.primary_domain
|
82
64
|
move_existing_to_history!
|
83
|
-
current_bucket =
|
84
|
-
raise Harpoon::Errors::MissingSetup, "Required s3 buckets are not created
|
85
|
-
@logger.info "
|
86
|
-
@
|
65
|
+
current_bucket = s3.buckets[@options.primary_domain]
|
66
|
+
raise Harpoon::Errors::MissingSetup, "Required s3 buckets are not created. Run harpoon doctor to test." unless current_bucket.exists?
|
67
|
+
@logger.info "Copying new release to s3"
|
68
|
+
@options.files.each do |f|
|
87
69
|
@logger.debug "Path: #{f}"
|
88
|
-
relative_path = Pathname.new(f).relative_path_from(Pathname.new(@
|
70
|
+
relative_path = Pathname.new(f).relative_path_from(Pathname.new(@options.directory)).to_s
|
89
71
|
@logger.debug "s3 key: #{relative_path}"
|
90
72
|
current_bucket.objects[relative_path].write(Pathname.new(f))
|
91
73
|
end
|
92
|
-
@logger.info "
|
74
|
+
@logger.info "...done"
|
93
75
|
end
|
94
76
|
|
95
77
|
def list
|
96
78
|
@logger.info "The following rollbacks are available:"
|
97
|
-
if @
|
98
|
-
tree =
|
79
|
+
if @options.primary_domain
|
80
|
+
tree = s3.buckets[rollback_bucket(@options.primary_domain)].as_tree
|
99
81
|
rollbacks = tree.children.collect {|i| i.prefix.gsub(/\/$/, "").to_i }
|
100
82
|
rollbacks.sort!.reverse!
|
101
83
|
rollbacks.each_with_index do |r, index|
|
@@ -105,39 +87,77 @@ module Harpoon
|
|
105
87
|
end
|
106
88
|
|
107
89
|
def doctor
|
90
|
+
@logger.pass "Region: #{@options.region}"
|
108
91
|
# check configuration
|
109
|
-
if @
|
110
|
-
|
111
|
-
@logger.info "Primary Domain: #{@config.domain["primary"]}"
|
112
|
-
else
|
113
|
-
@logger.fatal "Missing Primary Domain"
|
114
|
-
exit
|
115
|
-
end
|
92
|
+
if @options.primary_domain
|
93
|
+
@logger.pass "Primary Domain: #{@options.primary_domain}"
|
116
94
|
else
|
117
|
-
@logger.
|
95
|
+
@logger.fail "Missing Primary Domain"
|
118
96
|
exit
|
119
97
|
end
|
98
|
+
|
99
|
+
if @options.forwarded_domain
|
100
|
+
@logger.pass "Forwarded Domains: #{@options.forwarded_domain}"
|
101
|
+
else
|
102
|
+
@logger.pass "No forwarded domains"
|
103
|
+
end
|
104
|
+
|
120
105
|
# check IAM permissions
|
121
|
-
|
122
|
-
|
106
|
+
|
107
|
+
# check buckets exist and permissions
|
108
|
+
primary_bucket = s3.buckets[@options.primary_domain]
|
123
109
|
if primary_bucket.exists?
|
124
|
-
@logger.
|
110
|
+
@logger.pass "Primary bucket exists"
|
125
111
|
else
|
126
|
-
@logger.
|
112
|
+
@logger.fail "Missing Primary domain bucket"
|
127
113
|
end
|
114
|
+
|
115
|
+
if @options.forwarded_domain
|
116
|
+
forwarded = @options.forwarded_domain.is_a?(Array) ? @options.forwarded_domain : [@options.forwarded_domain]
|
117
|
+
forwarded.each do |f|
|
118
|
+
bucket = s3.buckets[f]
|
119
|
+
if bucket.exists?
|
120
|
+
@logger.pass "Forwarded bucket exists: #{f}"
|
121
|
+
#check forwarding
|
122
|
+
wc = bucket.website_configuration.to_hash
|
123
|
+
@logger.debug "Website Configuration: #{wc}"
|
124
|
+
if wc && wc[:redirect_all_requests_to] && wc[:redirect_all_requests_to][:host_name] == @options.primary_domain
|
125
|
+
@logger.pass "Forwarding setup: #{f}"
|
126
|
+
else
|
127
|
+
@logger.fail "Forwarding needs to be setup: #{f}"
|
128
|
+
@logger.suggest "Run `harpoon setup`"
|
129
|
+
end
|
130
|
+
else
|
131
|
+
@logger.fail "Forwarded bucket doesn't exist: #{f}"
|
132
|
+
@logger.suggest "Run `harpoon setup`"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
128
137
|
# check domain setup
|
138
|
+
|
139
|
+
|
129
140
|
# print DNS settings
|
130
|
-
print_dns_settings(@
|
141
|
+
print_dns_settings(@options.primary_domain)
|
142
|
+
@logger.info "...done"
|
131
143
|
end
|
132
144
|
|
133
145
|
def rollback
|
134
146
|
@logger.info "Not yet implemented!"
|
135
|
-
@logger.info "
|
147
|
+
@logger.info "Your rollbacks ARE safe on s3 #{rollback_bucket(@options.primary_domain)}"
|
136
148
|
self.list
|
137
149
|
end
|
138
150
|
|
139
151
|
private
|
140
152
|
|
153
|
+
def s3
|
154
|
+
@s3 ||= AWS::S3.new({access_key_id: @auth[:key], secret_access_key: @auth[:secret], region: @options.region})
|
155
|
+
end
|
156
|
+
|
157
|
+
def r53
|
158
|
+
@r53 ||= AWS::Route53.new({access_key_id: @auth[:key], secret_access_key: @auth[:secret], region: @options.region})
|
159
|
+
end
|
160
|
+
|
141
161
|
def setup_dns_alias(domain, bucket)
|
142
162
|
@logger.debug "Setup Domain Alias for #{domain}"
|
143
163
|
#extract root domain
|
@@ -151,8 +171,8 @@ module Harpoon
|
|
151
171
|
|
152
172
|
|
153
173
|
#ensure we have a hosted zone
|
154
|
-
hosted_zone =
|
155
|
-
hosted_zone =
|
174
|
+
hosted_zone = r53.hosted_zones.find {|h| h.name == rdomain}
|
175
|
+
hosted_zone = r53.hosted_zones.create(rdomain, {comment: "Created By Harpoon"}) if !hosted_zone
|
156
176
|
|
157
177
|
record = hosted_zone.rrsets[domain, "A"]
|
158
178
|
|
@@ -165,9 +185,9 @@ module Harpoon
|
|
165
185
|
|
166
186
|
def setup_bucket(bucket_name)
|
167
187
|
@logger.info "Creating bucket: #{bucket_name}"
|
168
|
-
bucket =
|
188
|
+
bucket = s3.buckets[bucket_name]
|
169
189
|
unless bucket.exists?
|
170
|
-
bucket =
|
190
|
+
bucket = s3.buckets.create(bucket_name)
|
171
191
|
end
|
172
192
|
bucket
|
173
193
|
end
|
@@ -206,13 +226,13 @@ module Harpoon
|
|
206
226
|
@logger.debug "Print DNS Settings for: #{domain}"
|
207
227
|
rdomain = "#{root_domain(domain)}."
|
208
228
|
@logger.debug "Root Domain: #{rdomain}"
|
209
|
-
@logger.
|
210
|
-
@logger.
|
211
|
-
hosted_zone =
|
229
|
+
@logger.suggest "=============================="
|
230
|
+
@logger.suggest "Please transfer domain to Amazon Nameservers:"
|
231
|
+
hosted_zone = r53.hosted_zones.find {|h| h.name == rdomain}
|
212
232
|
hosted_zone.rrsets[rdomain, "NS"].resource_records.each do |r|
|
213
|
-
@logger.
|
233
|
+
@logger.suggest r[:value]
|
214
234
|
end
|
215
|
-
@logger.
|
235
|
+
@logger.suggest "=============================="
|
216
236
|
end
|
217
237
|
|
218
238
|
def rollback_bucket(domain)
|
@@ -220,10 +240,10 @@ module Harpoon
|
|
220
240
|
end
|
221
241
|
|
222
242
|
def move_existing_to_history!
|
223
|
-
raise Harpoon::Errors::InvalidConfiguration, "Must have a primary domain defined" unless @
|
243
|
+
raise Harpoon::Errors::InvalidConfiguration, "Must have a primary domain defined" unless @options.primary_domain
|
224
244
|
@logger.info "Moving existing deploy to history"
|
225
|
-
current =
|
226
|
-
history =
|
245
|
+
current = s3.buckets[@options.primary_domain]
|
246
|
+
history = s3.buckets[rollback_bucket(@options.primary_domain)]
|
227
247
|
raise Harpoon::Errors::MissingSetup, "The expected buckets are not yet created, please try running harpoon setup" unless current.exists? && history.exists?
|
228
248
|
|
229
249
|
current_date = Time.now.to_i
|
@@ -1,16 +1,17 @@
|
|
1
1
|
module Harpoon
|
2
2
|
module Services
|
3
3
|
class Test
|
4
|
+
include Harpoon::Service
|
4
5
|
attr_accessor :config, :requests
|
5
6
|
|
6
7
|
def initialize(config = nil, auth = nil, logger = nil)
|
7
|
-
@auth = auth
|
8
8
|
@config = config
|
9
|
-
@
|
9
|
+
@auth = auth
|
10
10
|
@logger = logger
|
11
11
|
end
|
12
12
|
|
13
13
|
def method_missing(method, *args)
|
14
|
+
@requests ||= []
|
14
15
|
@requests.push [method, args]
|
15
16
|
end
|
16
17
|
end
|
@@ -3,11 +3,9 @@
|
|
3
3
|
"hosting" : "s3",
|
4
4
|
"auth_namespace" : "main",
|
5
5
|
"hosting_options" : {
|
6
|
+
"primary_domain": "www.domain.com",
|
7
|
+
"forwarded_domain" : ["domain.com"],
|
6
8
|
"region" : "us-west-2"
|
7
9
|
},
|
8
|
-
"domain" : {
|
9
|
-
"primary" : "www.domain.com",
|
10
|
-
"forwarded" : ["domain.com"]
|
11
|
-
},
|
12
10
|
"directory" : ""
|
13
11
|
}
|
data/test/test_config.rb
CHANGED
@@ -57,4 +57,53 @@ describe "Config File" do
|
|
57
57
|
assert_equal 2, unnested.files.length, "Should have found 2 files"
|
58
58
|
assert_equal [File.join(Dir.pwd, "test", "test_directory", "unnested_files", "nested", "test2.txt"), File.join(Dir.pwd, "test", "test_directory", "unnested_files", "test.txt")], unnested.files, "Should have found the right files"
|
59
59
|
end
|
60
|
+
|
61
|
+
it "Should provide some deep merge functionality" do
|
62
|
+
h1 = { x: { y: [4,5,6] }, z: [7,8,9] }
|
63
|
+
h2 = { x: { y: [7,8,9] }, z: "xyz" }
|
64
|
+
|
65
|
+
c1 = Harpoon::Config.new(h1.dup)
|
66
|
+
c2 = Harpoon::Config.new(h2.dup)
|
67
|
+
|
68
|
+
assert_equal ({ x: { y: [7, 8, 9] }, z: "xyz" }), c1.deep_merge!(h2)
|
69
|
+
assert_equal ({ x: { y: [4, 5, 6] }, z: [7, 8, 9] }), c2.deep_merge!(h1)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "Should let you get to all the data" do
|
73
|
+
d = {key: "value", key2: "value2"}
|
74
|
+
c1 = Harpoon::Config.new(d)
|
75
|
+
assert_equal d, c1.data, "Should have returned data"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "Should let you require data and then check to confirm you have it" do
|
79
|
+
c1 = Harpoon::Config.new
|
80
|
+
c1.requires :required_1
|
81
|
+
assert_raises Harpoon::Errors::InvalidConfiguration do
|
82
|
+
c1.validate!
|
83
|
+
end
|
84
|
+
|
85
|
+
assert_equal false, c1.validate, "Should have a silent validation as well"
|
86
|
+
end
|
87
|
+
|
88
|
+
it "Should let you set values" do
|
89
|
+
c1 = Harpoon::Config.new
|
90
|
+
c1.test_value = true
|
91
|
+
assert_equal true, c1.test_value, "Should have set the value"
|
92
|
+
end
|
93
|
+
|
94
|
+
it "shouldn't care about strings and symbols" do
|
95
|
+
c1 = Harpoon::Config.new({hosting_options: true})
|
96
|
+
c1.requires :hosting_options
|
97
|
+
assert_equal true, c1.validate, "Should have caught it"
|
98
|
+
|
99
|
+
assert_equal true, c1.hosting_options, "Should have found it!"
|
100
|
+
end
|
101
|
+
|
102
|
+
it "Should have a working dup method" do
|
103
|
+
c1 = Harpoon::Config.new({hosting: true})
|
104
|
+
c2 = c1.dup
|
105
|
+
c1.new_value = true
|
106
|
+
assert !c2.new_value, "Shouldn't have a new value in c2"
|
107
|
+
end
|
108
|
+
|
60
109
|
end
|
@@ -3,11 +3,9 @@
|
|
3
3
|
"hosting" : "s3",
|
4
4
|
"auth_namespace" : "main",
|
5
5
|
"hosting_options" : {
|
6
|
-
"region" : "US Standard"
|
7
|
-
|
8
|
-
|
9
|
-
"primary" : "www.domain.com",
|
10
|
-
"forwarded" : ["domain.com"]
|
6
|
+
"region" : "US Standard",
|
7
|
+
"primary_domain" : "www.domain.com",
|
8
|
+
"forwarded_domain" : ["domain.com"]
|
11
9
|
},
|
12
10
|
"directory" : "nested/directory"
|
13
11
|
}
|
@@ -3,11 +3,9 @@
|
|
3
3
|
"hosting" : "test",
|
4
4
|
"auth_namespace" : "test-namespace",
|
5
5
|
"hosting_options" : {
|
6
|
-
"option" : "value"
|
7
|
-
|
8
|
-
|
9
|
-
"primary" : "www.domain.com",
|
10
|
-
"forwarded" : ["domain.com"]
|
6
|
+
"option" : "value",
|
7
|
+
"primary_domain" : "www.domain.com",
|
8
|
+
"forwarded_domain" : ["domain.com"]
|
11
9
|
},
|
12
10
|
"directory" : ""
|
13
11
|
}
|
@@ -3,10 +3,8 @@
|
|
3
3
|
"hosting" : "s3",
|
4
4
|
"auth_namespace" : "main",
|
5
5
|
"hosting_options" : {
|
6
|
-
"region" : "
|
7
|
-
|
8
|
-
|
9
|
-
"primary" : "www.domain.com",
|
10
|
-
"forwarded" : ["domain.com"]
|
6
|
+
"region" : "us-west-2",
|
7
|
+
"primary_domain" : "www.domain.com",
|
8
|
+
"forwarded_domain" : ["domain.com"]
|
11
9
|
}
|
12
10
|
}
|
data/test/test_hosting.rb
CHANGED
@@ -2,7 +2,7 @@ require "helper"
|
|
2
2
|
|
3
3
|
describe "Test Hosting Module" do
|
4
4
|
before do
|
5
|
-
@hosting = Harpoon::Services::Test.new
|
5
|
+
@hosting = Harpoon::Services::Test.new(Object.new, Object.new, Object.new)
|
6
6
|
end
|
7
7
|
|
8
8
|
it "Should let me make requests" do
|
@@ -10,5 +10,5 @@ describe "Test Hosting Module" do
|
|
10
10
|
assert_equal :deploy, @hosting.requests[0][0]
|
11
11
|
assert_equal ["options", "go", "here"], @hosting.requests[0][1]
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
end
|
data/test/test_runner.rb
CHANGED
@@ -2,6 +2,7 @@ require "helper"
|
|
2
2
|
describe "Runner" do
|
3
3
|
before do
|
4
4
|
@runner = Harpoon::Runner.new({config: "test/test_directory/test_client"})
|
5
|
+
@runner.instance_eval {@service = load_host}
|
5
6
|
end
|
6
7
|
|
7
8
|
it "Should load the config from the config option" do
|
@@ -16,19 +17,14 @@ describe "Runner" do
|
|
16
17
|
assert_equal "test-namespace", @runner.instance_eval {@auth.namespace}, "Should have set the namespace correctly"
|
17
18
|
end
|
18
19
|
|
19
|
-
it "Should be passing in the configuration and auth to the service" do
|
20
|
-
assert_equal @runner.instance_eval {@auth}, @runner.instance_eval {@service.instance_eval {@auth}}, "Should have gotten the right auth"
|
21
|
-
assert_equal @runner.instance_eval {@config}, @runner.instance_eval {@service.instance_eval {@config}}, "Should have gotten the right config"
|
22
|
-
end
|
23
|
-
|
24
20
|
it "Should pass any unknown commands to the service" do
|
25
21
|
@runner.deploy
|
26
22
|
assert_equal :deploy, @runner.instance_eval {@service.requests[0][0]}, "Should have run deploy on the service"
|
27
23
|
end
|
28
24
|
|
29
25
|
it "Should pass a default logger to everyone" do
|
30
|
-
assert_equal Logger, @runner.instance_eval {@service.instance_eval {@logger.class}}, "Should have passed a logger to the service"
|
31
|
-
assert_equal Logger, @runner.instance_eval {@auth.instance_eval {@logger.class}}, "Should have passed a logger to the auth"
|
32
|
-
assert_equal Logger, @runner.instance_eval {@config.instance_eval {@logger.class}}, "Should have passed a logger to the config"
|
26
|
+
assert_equal Harpoon::Logger, @runner.instance_eval {@service.instance_eval {@logger.class}}, "Should have passed a logger to the service"
|
27
|
+
assert_equal Harpoon::Logger, @runner.instance_eval {@auth.instance_eval {@logger.class}}, "Should have passed a logger to the auth"
|
28
|
+
assert_equal Harpoon::Logger, @runner.instance_eval {@config.instance_eval {@logger.class}}, "Should have passed a logger to the config"
|
33
29
|
end
|
34
30
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
#overwrite test service for this test
|
4
|
+
module Harpoon
|
5
|
+
module Services
|
6
|
+
class TestBase
|
7
|
+
include Harpoon::Service
|
8
|
+
attr_accessor :config, :requests
|
9
|
+
|
10
|
+
auth :auth1, "Auth1"
|
11
|
+
auth :auth2, "Auth2"
|
12
|
+
|
13
|
+
option :default_option, default: true
|
14
|
+
option :default_option_2, default: true
|
15
|
+
option :default_option_required, required: true
|
16
|
+
|
17
|
+
def method_missing(method, *args)
|
18
|
+
@requests ||= []
|
19
|
+
@requests.push [method, args]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "Service Base" do
|
26
|
+
before do
|
27
|
+
@mock_auth = MiniTest::Mock.new
|
28
|
+
@mock_logger = MiniTest::Mock.new
|
29
|
+
d = {
|
30
|
+
hosting: :test,
|
31
|
+
hosting_options: {default_option: "one", new_option: true, default_option_required: true}
|
32
|
+
}
|
33
|
+
@mock_options = Harpoon::Config.new(d)
|
34
|
+
|
35
|
+
@mock_auth.expect :get_or_ask, ["asdf", "asdf2"], [Symbol, String, String]
|
36
|
+
end
|
37
|
+
|
38
|
+
it "Should request and verify authentication" do
|
39
|
+
@service = Harpoon::Services::TestBase.new(@mock_options, @mock_auth, @mock_logger)
|
40
|
+
@mock_auth.verify
|
41
|
+
assert_equal "asdf", @service.instance_eval {@auth[:auth1]}, "Should have stored auth1 correctly"
|
42
|
+
assert_equal "asdf2", @service.instance_eval {@auth[:auth2]}, "Should have stored auth2 correctly"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "Should have merged in the hosting options with default values" do
|
46
|
+
@service = Harpoon::Services::TestBase.new(@mock_options, @mock_auth, @mock_logger)
|
47
|
+
assert_equal "one", @service.instance_eval {@options.default_option}
|
48
|
+
assert_equal true, @service.instance_eval {@options.default_option_2}
|
49
|
+
assert_equal true, @service.instance_eval {@options.new_option}
|
50
|
+
end
|
51
|
+
|
52
|
+
it "Should raise an error if required options aren't included" do
|
53
|
+
@mock_options = Harpoon::Config.new({hosting: :test})
|
54
|
+
assert_raises Harpoon::Errors::InvalidConfiguration do
|
55
|
+
@service = Harpoon::Services::TestBase.new(@mock_options, @mock_auth, @mock_logger)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/test/test_services.rb
CHANGED
@@ -7,6 +7,7 @@ describe "Test all services" do
|
|
7
7
|
# functions
|
8
8
|
Harpoon::Services.constants.each do |c|
|
9
9
|
next if c.to_s == "Test" #skip the test
|
10
|
+
next if c.to_s == "TestBase" #skip base test
|
10
11
|
m = Kernel.const_get("Harpoon").const_get("Services").const_get(c).instance_methods
|
11
12
|
assert_includes m, :deploy, "Should include a deploy method"
|
12
13
|
assert_includes m, :doctor, "Should include a doctor method"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: harpoon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Quinn
|
@@ -14,72 +14,86 @@ dependencies:
|
|
14
14
|
name: thor
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 0.19.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.19.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: netrc
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 0.7.7
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 0.7.7
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: aws-sdk
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - ~>
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: 1.51.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - ~>
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 1.51.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: public_suffix
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - ~>
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: 1.4.5
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - ~>
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 1.4.5
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: colorize
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - ~>
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: 0.7.3
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - ~>
|
80
|
+
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 0.7.3
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: activesupport
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 4.1.5
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 4.1.5
|
83
97
|
description: Deploy small server-less webapps to amazon s3, including buckets, dns
|
84
98
|
and permissions
|
85
99
|
email: ryan@mazondo.com
|
@@ -88,7 +102,7 @@ executables:
|
|
88
102
|
extensions: []
|
89
103
|
extra_rdoc_files: []
|
90
104
|
files:
|
91
|
-
- .gitignore
|
105
|
+
- ".gitignore"
|
92
106
|
- LICENSE.md
|
93
107
|
- README.md
|
94
108
|
- Rakefile
|
@@ -100,7 +114,9 @@ files:
|
|
100
114
|
- lib/harpoon/client.rb
|
101
115
|
- lib/harpoon/config.rb
|
102
116
|
- lib/harpoon/errors.rb
|
117
|
+
- lib/harpoon/logger.rb
|
103
118
|
- lib/harpoon/runner.rb
|
119
|
+
- lib/harpoon/service.rb
|
104
120
|
- lib/harpoon/services/s3.rb
|
105
121
|
- lib/harpoon/services/test.rb
|
106
122
|
- lib/harpoon/templates/harpoon.json
|
@@ -118,6 +134,7 @@ files:
|
|
118
134
|
- test/test_directory/unnested_files/test.txt
|
119
135
|
- test/test_hosting.rb
|
120
136
|
- test/test_runner.rb
|
137
|
+
- test/test_service_base.rb
|
121
138
|
- test/test_services.rb
|
122
139
|
homepage: http://www.getharpoon.com
|
123
140
|
licenses:
|
@@ -129,17 +146,17 @@ require_paths:
|
|
129
146
|
- lib
|
130
147
|
required_ruby_version: !ruby/object:Gem::Requirement
|
131
148
|
requirements:
|
132
|
-
- -
|
149
|
+
- - ">="
|
133
150
|
- !ruby/object:Gem::Version
|
134
151
|
version: '0'
|
135
152
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
153
|
requirements:
|
137
|
-
- -
|
154
|
+
- - ">="
|
138
155
|
- !ruby/object:Gem::Version
|
139
156
|
version: '0'
|
140
157
|
requirements: []
|
141
158
|
rubyforge_project:
|
142
|
-
rubygems_version: 2.
|
159
|
+
rubygems_version: 2.2.2
|
143
160
|
signing_key:
|
144
161
|
specification_version: 4
|
145
162
|
summary: A single page app deployer for amazon s3
|