harpoon 0.0.3 → 0.0.4

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 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