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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 937896b519cb54849436445b71d22a9e8d8ba4af
4
- data.tar.gz: 7ce38434f4b5d2c80aebe8a11fa1b76d44b8822f
3
+ metadata.gz: 67816218225a067d3f8b66c9673338f17204fd3d
4
+ data.tar.gz: 75ba5ab88e915ad54987f0518a620ba4bbeb19b8
5
5
  SHA512:
6
- metadata.gz: 395c2a001da8362adf10d10cb49d7bda934236f00fdcb733dd09631c9b15987347fa90e5d3323939af42a3d09bde1e515b45cdd0bb08a059aefe68cb45caea3e
7
- data.tar.gz: e58b5de1b132b71b25369087264838717fd52443c580ad109ddcfd1e9ddacc6ff7c8e82988a8350616f2e69c824da676c82d4a9f9677f469d423d8b263506f78
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, not ready for prime time just yet. Keep your eyes on it though, I'll be releasing it shortly!
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).
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'harpoon'
3
- s.version = '0.0.3'
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
@@ -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/services/test.rb"
10
- require_relative "harpoon/services/s3.rb"
10
+ require_relative "harpoon/service"
11
+ require_relative "harpoon/services/test"
12
+ require_relative "harpoon/services/s3"
11
13
  end
@@ -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
- @config = data
49
+ @data = data.symbolize_keys
51
50
  @logger = logger
52
- @files = Dir.glob(File.join(@config["directory"], "**", "*")).select {|f| !File.directory?(f) && File.basename(f) != "harpoon.json"} if @config["directory"]
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
- @config[method.to_s]
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
@@ -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
- @service = load_host
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
@@ -6,96 +6,78 @@ require "pathname"
6
6
  module Harpoon
7
7
  module Services
8
8
  class S3
9
- def initialize(config, auth, logger)
10
- # Store what we're given for later
11
- @config = config
12
- @auth = auth
13
- @logger = logger
9
+ include Harpoon::Service
14
10
 
15
- # Ask for the users credentials
16
- @credentials = @auth.get_or_ask("s3", "Key", "Secret")
11
+ auth :key, "Key"
12
+ auth :secret, "Secret"
17
13
 
18
- if config.hosting_options && config.hosting_options["region"]
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 @config.domain
33
- #we have domain info, so let's make sure it's setup for it
34
- if @config.domain["primary"]
35
- #primary domain detected, let's make sure it exists
36
- bucket = setup_bucket(@config.domain["primary"])
37
- #setup bucket to server webpages
38
- @logger.info "Setting primary domain as website"
39
- bucket.configure_website
40
- #setup ACL
41
- @logger.info "Setting bucket policy"
42
- policy = AWS::S3::Policy.new
43
- policy.allow(
44
- actions: ['s3:GetObject'],
45
- resources: [bucket.objects],
46
- principals: :any
47
- )
48
-
49
- @logger.debug policy.to_json
50
-
51
- bucket.policy = policy
52
-
53
-
54
- history = setup_bucket(rollback_bucket(@config.domain["primary"]))
55
- @logger.info "Created rollback bucket"
56
-
57
- setup_dns_alias(@config.domain["primary"], bucket)
58
-
59
-
60
- if @config.domain["forwarded"]
61
- #we also want to forward some domains
62
- #make sure we have an array
63
- #TODO : Move all of this nonsense to the config object, it should be validating this stuff
64
- forwarded = @config.domain["forwarded"].is_a?(Array) ? @config.domain["forwarded"] : [@config.domain["forwarded"]]
65
- forwarded.each do |f|
66
- bucket = setup_bucket(f)
67
- @logger.info "Seting up redirect to primary"
68
- cw = AWS::S3::WebsiteConfiguration.new({redirect_all_requests_to: {host_name: @config.domain["primary"]}})
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 @config.files && @config.directory && @config.domain["primary"]
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 = @s3.buckets[@config.domain["primary"]]
84
- raise Harpoon::Errors::MissingSetup, "Required s3 buckets are not created, consider running harpoon setup first" unless current_bucket.exists?
85
- @logger.info "Writing files to s3"
86
- @config.files.each do |f|
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(@config.directory)).to_s
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 "Deploy complete"
74
+ @logger.info "...done"
93
75
  end
94
76
 
95
77
  def list
96
78
  @logger.info "The following rollbacks are available:"
97
- if @config.domain && @config.domain["primary"]
98
- tree = @s3.buckets[rollback_bucket(@config.domain["primary"])].as_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 @config.domain
110
- if @config.domain["primary"]
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.fatal "Missing Domain Configuration"
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
- # check buckets exist
122
- primary_bucket = @s3.buckets[@config.domain["primary"]]
106
+
107
+ # check buckets exist and permissions
108
+ primary_bucket = s3.buckets[@options.primary_domain]
123
109
  if primary_bucket.exists?
124
- @logger.info "Primary bucket exists"
110
+ @logger.pass "Primary bucket exists"
125
111
  else
126
- @logger.fatal "Missing Primary domain bucket"
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(@config.domain["primary"])
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 "But don't worry, your rollbacks are safely stored in #{rollback_bucket(@config.domain["primary"])}"
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 = @r53.hosted_zones.find {|h| h.name == rdomain}
155
- hosted_zone = @r53.hosted_zones.create(rdomain, {comment: "Created By Harpoon"}) if !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 = @s3.buckets[bucket_name]
188
+ bucket = s3.buckets[bucket_name]
169
189
  unless bucket.exists?
170
- bucket = @s3.buckets.create(bucket_name)
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.warn "=============================="
210
- @logger.warn "Please forward to the following DNS:"
211
- hosted_zone = @r53.hosted_zones.find {|h| h.name == rdomain}
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.warn r[:value]
233
+ @logger.suggest r[:value]
214
234
  end
215
- @logger.warn "=============================="
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 @config.domain["primary"]
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 = @s3.buckets[@config.domain["primary"]]
226
- history = @s3.buckets[rollback_bucket(@config.domain["primary"])]
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
- @requests = []
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
  }
@@ -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
- "domain" : {
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
- "domain": {
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" : "US Standard"
7
- },
8
- "domain" : {
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
  }
@@ -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
@@ -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
@@ -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.3
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.1.11
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