kensa 1.2.0rc7 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,64 +1,17 @@
1
1
  require 'restclient'
2
2
  require 'term/ansicolor'
3
3
  require 'launchy'
4
+ require 'optparse'
4
5
 
5
6
  module Heroku
6
- class Git
7
- class << self
8
- def verify_create(app_name, template)
9
- raise CommandInvalid.new("template #{clone_url(template)} does not exist") unless template_exists?(template)
10
- raise CommandInvalid.new("Need git to clone repository") unless git_installed?
11
- end
12
-
13
- def template_exists?(template)
14
- true
15
- end
16
-
17
- def git_installed?
18
- `git` rescue false
19
- end
20
-
21
- def clone(app_name, template)
22
- verify_create(app_name, template)
23
- cmd = "git clone #{clone_url(template)} #{app_name}"
24
- puts cmd
25
- `#{cmd}`
26
- raise Exception.new("couldn't clone the repository from #{clone_url(template)}") unless File.directory?("#{app_name}")
27
- puts "Created #{app_name} from #{template} template"
28
- end
29
-
30
- def clone_url(name)
31
- prefix = ENV['REPO_PREFIX'] || "heroku"
32
- if name.include? "://" #its a full url
33
- return name
34
- elsif name.include? "/" #its a non-heroku repo
35
- name = "#{prefix}/#{name}"
36
- else #its one of ours
37
- name = "#{prefix}/kensa-create-#{name}"
38
- end
39
-
40
- "git://github.com/#{name}"
41
- end
42
- end
43
- end
44
-
45
7
  module Kensa
46
- class UserError < RuntimeError; end
47
-
48
8
  class Client
49
- def initialize(args, options)
9
+ def initialize(args, options = {})
50
10
  @args = args
51
- @options = options
11
+ @options = OptParser.parse(args).merge(options)
52
12
  end
53
13
 
54
- def filename
55
- @options[:filename]
56
- end
57
-
58
- #you fucked up
59
14
  class CommandInvalid < Exception; end
60
- #we fucked up
61
- class CommandFailed < Exception; end
62
15
 
63
16
  def run!
64
17
  command = @args.shift || @options[:command]
@@ -67,63 +20,44 @@ module Heroku
67
20
  end
68
21
 
69
22
  def init
70
- Manifest.new(filename, @options).write
71
- Screen.new.message "Initialized new addon manifest in #{filename}\n"
23
+ Manifest.new(@options).write
24
+ screen.message "Initialized new addon manifest in #{filename}\n"
25
+ if @options[:foreman]
26
+ screen.message "Initialized new .env file for foreman\n"
27
+ end
72
28
  end
73
29
 
74
30
  def create
75
31
  app_name = @args.shift
76
32
  template = @options[:template]
77
- raise CommandInvalid.new("Need git to supply a template") unless template
78
- raise CommandInvalid.new("Need git to supply an application name") unless app_name
79
-
33
+ raise CommandInvalid.new("Need to supply an application name") unless app_name
34
+ raise CommandInvalid.new("Need to supply a template") unless template
80
35
  begin
81
- Git.clone(app_name, template)
36
+ Git.clone(app_name, template) and screen.message "Created #{app_name} from #{template} template\n"
37
+ Dir.chdir(app_name)
38
+ @options[:foreman] = true
39
+ init
82
40
  rescue Exception => e
83
- raise CommandFailed.new("error cloning #{Git.clone_url(template)} into #{app_name}")
41
+ raise CommandInvalid.new("error cloning #{Git.clone_url(template)} into #{app_name}")
84
42
  end
85
-
86
- Dir.chdir("./#{app_name}")
87
- `./after_clone #{app_name}` if File.exist?('./after_clone')
88
43
  end
89
44
 
90
-
91
45
  def test
92
46
  case check = @args.shift
93
47
  when "manifest"
94
- require "#{File.dirname(__FILE__)}/../../../test/manifest_test"
95
- require 'test/unit/ui/console/testrunner'
96
- $manifest = Yajl::Parser.parse(resolve_manifest)
97
- Test::Unit.run = true
98
- Test::Unit::UI::Console::TestRunner.new(ManifestTest.suite).start
48
+ run_check ManifestCheck
99
49
  when "provision"
100
- require "#{File.dirname(__FILE__)}/../../../test/provision_test"
101
- require 'test/unit/ui/console/testrunner'
102
- $manifest = Yajl::Parser.parse(resolve_manifest)
103
- Test::Unit.run = true
104
- Test::Unit::UI::Console::TestRunner.new(ProvisionTest.suite).start
50
+ run_check ManifestCheck, ProvisionCheck
105
51
  when "deprovision"
106
52
  id = @args.shift || abort("! no id specified; see usage")
107
- require "#{File.dirname(__FILE__)}/../../../test/deprovision_test"
108
- require 'test/unit/ui/console/testrunner'
109
- $manifest = Yajl::Parser.parse(resolve_manifest).merge("user_id" => id)
110
- Test::Unit.run = true
111
- Test::Unit::UI::Console::TestRunner.new(DeprovisionTest.suite).start
53
+ run_check ManifestCheck, DeprovisionCheck, :id => id
112
54
  when "planchange"
113
55
  id = @args.shift || abort("! no id specified; see usage")
114
56
  plan = @args.shift || abort("! no plan specified; see usage")
115
- require "#{File.dirname(__FILE__)}/../../../test/plan_change_test"
116
- require 'test/unit/ui/console/testrunner'
117
- $manifest = Yajl::Parser.parse(resolve_manifest).merge("user_id" => id)
118
- Test::Unit.run = true
119
- Test::Unit::UI::Console::TestRunner.new(PlanChangeTest.suite).start
57
+ run_check ManifestCheck, PlanChangeCheck, :id => id, :plan => plan
120
58
  when "sso"
121
59
  id = @args.shift || abort("! no id specified; see usage")
122
- require "#{File.dirname(__FILE__)}/../../../test/sso_test"
123
- require 'test/unit/ui/console/testrunner'
124
- $manifest = Yajl::Parser.parse(resolve_manifest).merge("user_id" => id)
125
- Test::Unit.run = true
126
- Test::Unit::UI::Console::TestRunner.new(SsoTest.suite).start
60
+ run_check ManifestCheck, SsoCheck, :id => id
127
61
  else
128
62
  abort "! Unknown test '#{check}'; see usage"
129
63
  end
@@ -177,12 +111,20 @@ module Heroku
177
111
  end
178
112
 
179
113
  def version
180
- puts "Kensa #{Kensa::VERSION}"
114
+ puts "Kensa #{VERSION}"
181
115
  end
182
116
 
183
117
  private
118
+ def filename
119
+ @options[:filename]
120
+ end
121
+
122
+ def screen
123
+ @screen ||= @options[:silent] ? NilScreen.new : Screen.new
124
+ end
125
+
184
126
  def headers
185
- { :accept => :json, "X-Kensa-Version" => "1", "User-Agent" => "kensa/#{Kensa::VERSION}" }
127
+ { :accept => :json, "X-Kensa-Version" => "1", "User-Agent" => "kensa/#{VERSION}" }
186
128
  end
187
129
 
188
130
  def heroku_host
@@ -206,7 +148,6 @@ module Heroku
206
148
  options = args.pop if args.last.is_a?(Hash)
207
149
 
208
150
  args.each do |klass|
209
- screen = Screen.new
210
151
  data = Yajl::Parser.parse(resolve_manifest)
211
152
  check = klass.new(data.merge(@options.merge(options)), screen)
212
153
  result = check.call
@@ -298,6 +239,43 @@ module Heroku
298
239
  $stdout.puts "done."
299
240
  end
300
241
  end
242
+
243
+
244
+ class OptParser
245
+ def self.defaults
246
+ {
247
+ :filename => 'addon-manifest.json',
248
+ :env => "test",
249
+ :async => false,
250
+ }
251
+ end
252
+
253
+ def self.parse(args)
254
+ self.defaults.tap do |options|
255
+ OptionParser.new do |o|
256
+ o.on("-f file", "--file") { |filename| options[:filename] = filename }
257
+ o.on("--async") { options[:async] = true }
258
+ o.on("--production") { options[:env] = "production" }
259
+ o.on("--without-sso") { options[:sso] = false }
260
+ o.on("-h", "--help") { command = "help" }
261
+ o.on("-p plan", "--plan") { |plan| options[:plan] = plan }
262
+ o.on("-v", "--version") { options[:command] = "version" }
263
+ o.on("-s sso", "--sso") { |method| options[:method] = method }
264
+ o.on("--foreman") { options[:foreman] = true }
265
+ o.on("-t name", "--template") do |template|
266
+ options[:template] = template
267
+ end
268
+
269
+ begin
270
+ o.parse!(args)
271
+ rescue OptionParser::InvalidOption
272
+ #skip over invalid options
273
+ retry
274
+ end
275
+ end
276
+ end
277
+ end
278
+ end
301
279
  end
302
280
  end
303
281
  end
@@ -0,0 +1,39 @@
1
+ module Heroku
2
+ module Kensa
3
+ class Git
4
+ class << self
5
+ def verify_create(app_name, template)
6
+ raise CommandInvalid.new("Need git to clone repository") unless git_installed?
7
+ end
8
+
9
+ def git_installed?
10
+ `git` rescue false
11
+ end
12
+
13
+ def clone(app_name, template)
14
+ verify_create(app_name, template)
15
+ run("git clone #{clone_url(template)} #{app_name}")
16
+ end
17
+
18
+ def run(cmd)
19
+ puts cmd
20
+ system(cmd)
21
+ end
22
+
23
+ def heroku_prefix
24
+ ENV["REPO_PREFIX"] || "heroku/kensa-create-"
25
+ end
26
+
27
+ def clone_url(name)
28
+ if name.include? "://" #its a full url not on github
29
+ return name
30
+ elsif !name.include? "/" #its one of ours
31
+ name = heroku_prefix + name
32
+ end
33
+
34
+ "git://github.com/#{name}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -2,28 +2,59 @@ module Heroku
2
2
  module Kensa
3
3
  class Manifest
4
4
 
5
- def initialize(filename = 'addon-manifest.json', options = {})
6
- @filename, @options = filename, options
5
+ def initialize(options = {})
6
+ @method = options.fetch(:method, 'post').to_sym
7
+ @filename = options.fetch(:filename, 'addon-manifest.json')
8
+ @options = options
7
9
  end
8
10
 
9
11
  def skeleton_json
12
+ @password = generate_password(16)
13
+ @port = @options[:foreman] ? 5000 : 4567
14
+ (@method == :get) ? get_skeleton : post_skeleton
15
+ end
16
+
17
+ def get_skeleton
18
+ <<-JSON
19
+ {
20
+ "id": "myaddon",
21
+ "api": {
22
+ "config_vars": [ "MYADDON_URL" ],
23
+ "password": "#{@password}",#{ sso_key }
24
+ "production": "https://yourapp.com/",
25
+ "test": "http://localhost:#{@port}/"
26
+ }
27
+ }
28
+ JSON
29
+ end
30
+
31
+ def post_skeleton
10
32
  <<-JSON
11
33
  {
12
34
  "id": "myaddon",
13
35
  "api": {
14
- "config_vars": [ "MYADDON_USER", "MYADDON_URL" ],
15
- "password": "#{generate_password(16)}",#{ sso_key }
36
+ "config_vars": [ "MYADDON_URL" ],
37
+ "password": "#{@password}",#{ sso_key }
16
38
  "production": {
17
39
  "base_url": "https://yourapp.com/heroku/resources",
18
40
  "sso_url": "https://yourapp.com/sso/login"
19
41
  },
20
42
  "test": {
21
- "base_url": "http://localhost:4567/heroku/resources",
22
- "sso_url": "http://localhost:4567/sso/login"
43
+ "base_url": "http://localhost:#{@port}/heroku/resources",
44
+ "sso_url": "http://localhost:#{@port}/sso/login"
23
45
  }
24
46
  }
25
47
  }
26
48
  JSON
49
+
50
+ end
51
+
52
+ def foreman
53
+ <<-ENV
54
+ SSO_SALT=#{@sso_salt}
55
+ HEROKU_USERNAME=myaddon
56
+ HEROKU_PASSWORD=#{@password}
57
+ ENV
27
58
  end
28
59
 
29
60
  def skeleton
@@ -31,14 +62,16 @@ JSON
31
62
  end
32
63
 
33
64
  def write
34
- open(@filename, 'w') { |f| f << skeleton_json }
65
+ File.open(@filename, 'w') { |f| f << skeleton_json }
66
+ File.open('.env', 'w') { |f| f << foreman } if @options[:foreman]
35
67
  end
36
68
 
37
69
  private
38
70
 
39
71
  def sso_key
72
+ @sso_salt = generate_password(16)
40
73
  unless @options[:sso] === false
41
- %{\n "sso_salt": #{ generate_password(16).inspect },}
74
+ %{\n "sso_salt": "#{@sso_salt}",}
42
75
  end
43
76
  end
44
77
 
@@ -0,0 +1,37 @@
1
+ module Heroku
2
+ module Kensa
3
+
4
+ class NilScreen
5
+ def test(msg)
6
+ end
7
+
8
+ def check(msg)
9
+ end
10
+
11
+ def error(msg)
12
+ end
13
+
14
+ def result(status)
15
+ end
16
+
17
+ def message(msg)
18
+ end
19
+ end
20
+
21
+ class IOScreen
22
+ attr_accessor :output
23
+
24
+ def initialize(io)
25
+ @output = io
26
+ end
27
+
28
+ def to_s
29
+ @output.closed_read? ? '' : @output.tap{|o| o.rewind }.read
30
+ end
31
+
32
+ [:test, :check, :error, :result, :message].each do |method|
33
+ eval %{ def #{method}(*args)\n @output.puts *args\n end }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,43 +1,40 @@
1
1
  require 'restclient'
2
+ require 'uri'
2
3
 
3
4
  module Heroku
4
5
  module Kensa
5
6
  class Sso
6
- attr_accessor :id, :url, :proxy_port, :timestamp, :token, :sso_url
7
+ attr_accessor :id, :url, :proxy_port, :timestamp, :token
7
8
 
8
9
  def initialize(data)
9
10
  @id = data[:id]
10
11
  @salt = data['api']['sso_salt']
11
12
 
12
13
  env = data.fetch :env, 'test'
13
- if data["api"][env].is_a?(Hash)
14
- @url = data["api"][env]["base_url"].chomp('/')
15
- @sso_url = data["api"][env]["sso_url"].chomp('/')
16
- @use_post = true
14
+ if @url = data['api'][env]['sso_url']
15
+ @use_post = true
16
+ @proxy_port = find_available_port
17
17
  else
18
18
  @url = data["api"][env].chomp('/')
19
- @use_post = false
20
- end
21
- @proxy_port = find_available_port
19
+ end
22
20
  @timestamp = Time.now.to_i
23
21
  @token = make_token(@timestamp)
24
22
  end
25
23
 
26
24
  def path
27
- extra = self.post? ? '/sso' : ''
28
- if self.post?
29
- self.sso_url
25
+ if self.POST?
26
+ URI.parse(url).path
30
27
  else
31
28
  "/heroku/resources/#{id}"
32
29
  end
33
30
  end
34
31
 
35
- def post?
32
+ def POST?
36
33
  @use_post
37
34
  end
38
35
 
39
36
  def sso_url
40
- if self.post?
37
+ if self.POST?
41
38
  "http://localhost:#{@proxy_port}/"
42
39
  else
43
40
  full_url
@@ -50,13 +47,13 @@ module Heroku
50
47
  alias get_url full_url
51
48
 
52
49
  def post_url
53
- @sso_url
50
+ url
54
51
  end
55
52
 
56
53
  def timestamp=(other)
57
54
  @timestamp = other
58
55
  @token = make_token(@timestamp)
59
- end
56
+ end
60
57
 
61
58
  def make_token(t)
62
59
  Digest::SHA1.hexdigest([@id, @salt, t].join(':'))
@@ -64,7 +61,7 @@ module Heroku
64
61
 
65
62
  def querystring
66
63
  return '' unless @salt
67
- '?' + query_data
64
+ '?' + query_data
68
65
  end
69
66
 
70
67
  def query_data
@@ -72,10 +69,13 @@ module Heroku
72
69
  end
73
70
 
74
71
  def query_params
75
- { 'token' => @token,
72
+ { 'token' => @token,
76
73
  'timestamp' => @timestamp.to_s,
77
- 'nav-data' => sample_nav_data,
78
- 'user' => 'username@example.com' }
74
+ 'nav-data' => sample_nav_data,
75
+ 'email' => 'username@example.com'
76
+ }.tap do |params|
77
+ params.merge!('id' => @id) if self.POST?
78
+ end
79
79
  end
80
80
 
81
81
  def sample_nav_data
@@ -97,7 +97,7 @@ module Heroku
97
97
  end
98
98
 
99
99
  def message
100
- if self.post?
100
+ if self.POST?
101
101
  "POSTing #{query_data} to #{post_url} via proxy on port #{@proxy_port}"
102
102
  else
103
103
  "Opening #{full_url}"
@@ -117,13 +117,13 @@ module Heroku
117
117
  end
118
118
 
119
119
  def run_proxy
120
- return unless self.post?
120
+ return unless self.POST?
121
121
  server = PostProxy.new self
122
122
  @proxy = server
123
123
 
124
124
  trap("INT") { server.stop }
125
125
  pid = fork do
126
- server.start
126
+ server.start
127
127
  end
128
128
  at_exit { server.stop; Process.waitpid pid }
129
129
  end