stackfu 0.1.1 → 0.1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/Gemfile +4 -0
  2. data/Manifest +36 -4
  3. data/Rakefile +1 -1
  4. data/autotest/discover.rb +1 -0
  5. data/firewall/config/01-controls.yml +5 -0
  6. data/firewall/config/02-requirements.yml +4 -0
  7. data/firewall/config/03-executions.yml +6 -0
  8. data/firewall/config/03-scripts.yml +6 -0
  9. data/firewall/config/04-validations.yml +4 -0
  10. data/firewall/executables/configure_ufw.sh.erb +9 -0
  11. data/firewall/executables/install_ufw.sh.erb +7 -0
  12. data/firewall/script/configure_ufw.sh.erb +9 -0
  13. data/firewall/script/install_ufw.sh.erb +7 -0
  14. data/firewall/script.yml +8 -0
  15. data/lib/stackfu/api_hooks.rb +2 -2
  16. data/lib/stackfu/app.rb +5 -5
  17. data/lib/stackfu/commands/command.rb +7 -3
  18. data/lib/stackfu/commands/config_command.rb +1 -1
  19. data/lib/stackfu/commands/deploy_command.rb +53 -61
  20. data/lib/stackfu/commands/dump_command.rb +46 -40
  21. data/lib/stackfu/commands/generate_command.rb +10 -12
  22. data/lib/stackfu/commands/help_command.rb +1 -1
  23. data/lib/stackfu/commands/list_command.rb +76 -22
  24. data/lib/stackfu/commands/publish_command.rb +35 -18
  25. data/lib/stackfu/commands/server_command.rb +124 -124
  26. data/lib/stackfu/date_helper.rb +111 -0
  27. data/lib/stackfu.rb +4 -2
  28. data/spec/fixtures/scripts/all.json +11 -0
  29. data/spec/fixtures/scripts/create.json +10 -0
  30. data/spec/fixtures/scripts/delete.json +10 -0
  31. data/spec/fixtures/scripts/firewall.json +12 -0
  32. data/spec/fixtures/scripts/none.json +12 -0
  33. data/spec/fixtures/scripts/not_found.json +11 -0
  34. data/spec/fixtures/scripts/script_not_found.json +11 -0
  35. data/spec/fixtures/servers/all.json +12 -0
  36. data/spec/fixtures/servers/cannot_deploy.json +0 -0
  37. data/spec/fixtures/servers/deploy.json +290 -0
  38. data/spec/fixtures/servers/none.json +12 -0
  39. data/spec/fixtures/servers/not_found.json +11 -0
  40. data/spec/fixtures/servers/webbynode/deploy.json +12 -0
  41. data/spec/fixtures/servers/webbynode.json +12 -0
  42. data/spec/spec_helper.rb +191 -0
  43. data/spec/stackfu/api_hooks_spec.rb +7 -0
  44. data/spec/stackfu/commands/deploy_command_spec.rb +65 -0
  45. data/spec/stackfu/commands/dump_command_spec.rb +47 -0
  46. data/spec/stackfu/commands/generate_command_spec.rb +90 -0
  47. data/spec/stackfu/commands/list_command_spec.rb +53 -0
  48. data/spec/stackfu/commands/publish_command_spec.rb +67 -0
  49. data/stackfu.gemspec +4 -4
  50. data/templates/02-requirements.yml.erb +8 -10
  51. data/templates/{03-scripts.yml.erb → 03-executions.yml.erb} +3 -3
  52. data/templates/04-validations.yml.erb +1 -2
  53. data/templates/stack.yml.erb +4 -13
  54. data/test/support/fixtures.rb +1 -1
  55. data/test/unit/commands/test_server_command.rb +259 -259
  56. metadata +42 -7
  57. data/test/stack.yml +0 -26
  58. /data/stackfu-installer/config/{03-scripts.yml → 03-executions.yml} +0 -0
@@ -1,31 +1,85 @@
1
- module StackFu
1
+ module StackFu::Commands
2
2
  class ListCommand < Command
3
- include ApiHooks
3
+ include StackFu::ApiHooks
4
+
5
+ class << self
6
+ include DateHelper
7
+ end
4
8
 
9
+ # error_messages :missing_subcommand => "You have to tell what you want to deploy and to which server."
10
+
11
+ subcommand :servers, :required_parameters => []
12
+ subcommand :scripts, :required_parameters => []
13
+
14
+ TableAttributes = {
15
+ Server => [
16
+ [:name, :key, :ip, :validated, :last_seen],
17
+ lambda do |item|
18
+ last_seen = item.last_checked_in if item.respond_to?(:last_checked_in)
19
+
20
+ if last_seen
21
+ last_seen = distance_of_time_in_words(Time.now, Time.parse(last_seen))
22
+ last_seen = "#{last_seen} ago"
23
+ else
24
+ last_seen = "- never -"
25
+ end
26
+
27
+ validated = item.validated? ? "yes" : ""
28
+
29
+ [item.name, item._id, item.ip, validated, last_seen]
30
+ end,
31
+ "You have no servers yet. You can add new servers to your account in http://stackfu.com."
32
+ ],
33
+ Script => [
34
+ [:name, :description],
35
+ lambda { |item| [item.name, (item.description || "").truncate_words(10)] },
36
+ "You have nothing to list yet. To generate a new script, use the 'stackfu generate' command."
37
+ ]
38
+ }
39
+
40
+ def servers(parameters, options)
41
+ list Server
42
+ end
43
+
44
+ def scripts(parameters, options)
45
+ list Script
46
+ end
47
+
5
48
  def default(parameters, options)
6
- # user = User.find(:all).first
7
- items = spinner {
8
- [
9
- # Stack.find(:all, :conditions => { "user.id" => user.id }),
10
- Plugin.find(:all)
11
- ].flatten
12
- }
49
+ list [Script, Server]
50
+ end
51
+
52
+ private
53
+
54
+ def list(things_to_list)
55
+ things_to_list = [things_to_list] unless things_to_list.is_a?(Enumerable)
56
+
57
+ things_to_list.each_with_index do |kls, i|
58
+ attributes = TableAttributes[kls]
59
+
60
+ display = attributes[0]
61
+ fields = attributes[1]
62
+ error = attributes[2]
63
+
64
+ items = spinner {
65
+ [
66
+ kls.find(:all)
67
+ ].flatten
68
+ }
13
69
 
14
- params = {
15
- # :class => [Stack, Plugin],
16
- :class => [Plugin],
17
- :collection => items,
18
- :display => [:name, :type, :description],
19
- :main_column => :name,
20
- :empty => "You have nothing to list yet. To generate stacks or plugins, try the 'stackfu generate' command.",
21
- :ansi => options[:plain].nil?
22
- }
70
+ params = {
71
+ :class => [kls],
72
+ :collection => items,
73
+ :display => display,
74
+ :main_column => :name,
75
+ :empty => error,
76
+ :ansi => options[:plain].nil?
77
+ }
23
78
 
24
- puts table(params) { |item|
25
- description = item.respond_to?(:description) ? item.description : ""
79
+ puts table(params) { |item| fields.call item }
26
80
 
27
- [item.name, "Plugin", description.truncate_words(10)]
28
- }
81
+ puts "" if i < things_to_list.size-1
82
+ end
29
83
  end
30
84
  end
31
85
  end
@@ -1,6 +1,6 @@
1
- module StackFu
1
+ module StackFu::Commands
2
2
  class PublishCommand < Command
3
- include ApiHooks
3
+ include StackFu::ApiHooks
4
4
  aliases :pub
5
5
 
6
6
  def stack?
@@ -12,8 +12,7 @@ module StackFu
12
12
  end
13
13
 
14
14
  def default(parameters, options)
15
- what = :stack if stack?
16
- what = :plugin if plugin?
15
+ what = :script
17
16
 
18
17
  unless what
19
18
  error "Couldn't find an item to publish on current folder.",
@@ -23,41 +22,58 @@ module StackFu
23
22
  begin
24
23
  stack_spec = YAML.load(read("#{what}.yml"))
25
24
 
26
- %w[controls requirements scripts validations].each_with_index do |item, i|
25
+ %w[controls requirements executions validations].each_with_index do |item, i|
27
26
  if (yaml = read("config/0#{i+1}-#{item}.yml"))
28
- yaml.gsub!("type:", "_type:") if item == "controls"
27
+ yaml.gsub!("type:", "_type:")
29
28
  if (from_yaml = YAML.load(yaml))
29
+ if item == 'requirements' or item == 'validations'
30
+ buffer = []
31
+ from_yaml[item].each do |itm|
32
+ itm["params"] = { "data" => itm.delete("data") }
33
+ buffer << itm
34
+ end
35
+ from_yaml[item] = buffer
36
+ end
37
+
30
38
  stack_spec[item == "scripts" ? "executions" : item] = from_yaml[item]
31
39
  end
32
40
  end
33
41
  end
34
-
42
+
35
43
  unless stack_spec["executions"].present?
36
- error "To publish a #{what} you have to define at least one script.",
37
- "Take a look at the scripts descriptor file config/03-script.yml for more information.\nYou can also use 'stackfu generate stack_name script_name:script' command to auto-generate a sample script."
44
+ error "To publish a #{what} you have to define at least one execution.",
45
+ "Take a look at the executions descriptor file config/03-executions.yml for more information.\nYou can also use 'stackfu generate stack_name script_name:script' command to auto-generate a sample execution."
38
46
  return false
39
47
  end
40
48
 
41
49
  return unless stack_spec["executions"].each do |script|
42
- template = "script/#{script["file"]}.sh.erb"
50
+ template = "executables/#{script["file"]}.sh.erb"
43
51
 
44
52
  begin
45
- script["script"] = read(template)
53
+ script["body"] = read(template)
46
54
  rescue Errno::ENOENT
47
- error "The template file for the script '#{script["description"]}' was not found.", "This stack has a script called '#{script["description"]}', and the template for it should be in a file called script/#{script["file"]}.sh.erb."
55
+ error "The template file for the script '#{script["description"]}' was not found.", "This script has an executable called '#{script["description"]}', and the template for it should be in a file called executables/#{script["file"]}.sh.erb."
48
56
  break false
49
57
  end
50
58
 
51
59
  true
52
60
  end
53
61
 
54
- item_class = ApiHooks.const_get("#{what.to_s.classify}")
62
+ item_class = StackFu::ApiHooks.const_get("#{what.to_s.classify}")
55
63
  item_class.format = :json
56
64
 
57
- stacks = item_class.find(:all) || []
58
- stacks = stacks.select { |s| s.name == stack_spec["name"] }
59
-
60
- if stacks.any?
65
+ begin
66
+ stack = item_class.find(stack_spec["name"])
67
+ rescue ActiveResource::ResourceNotFound
68
+ rescue NoMethodError
69
+ if $!.message =~ /closed\?/
70
+ raise Errno::ECONNREFUSED
71
+ else
72
+ raise
73
+ end
74
+ end
75
+
76
+ if stack
61
77
  unless options[:update]
62
78
  if agree("You already have a #{what} named #{stack_spec["name"]}. Do you want to update it?")
63
79
  puts ""
@@ -69,7 +85,6 @@ module StackFu
69
85
  end
70
86
  end
71
87
 
72
- stack = stacks.first
73
88
  begin
74
89
  item_class.delete(stack.name)
75
90
  rescue ActiveResource::ResourceNotFound
@@ -81,6 +96,7 @@ module StackFu
81
96
  puts "Publishing #{what} #{stack_spec["name"]}..."
82
97
 
83
98
  stack = item_class.new(stack_spec)
99
+
84
100
  if publish(stack)
85
101
  done "#{what.to_s.titleize} #{stack.name} published."
86
102
  else
@@ -90,6 +106,7 @@ module StackFu
90
106
  error "#{$!.message}"
91
107
  rescue Errno::ENOENT
92
108
  error "There was an error opening your file descriptor"
109
+ raise
93
110
  end
94
111
  end
95
112
 
@@ -1,124 +1,124 @@
1
- module StackFu
2
- class ServerCommand < Command
3
- include StackFu::ApiHooks
4
- include StackFu::ProvidersCredentials
5
-
6
- aliases :servers
7
-
8
- alias_subcommand :list => :default
9
- # subcommand :add, :required_parameters => [:provider, :server_name]
10
- subcommand :delete, :required_parameters => [:server_name]
11
-
12
- def default(parameters, options)
13
- servers = spinner {
14
- Server.find(:all)
15
- }
16
-
17
- params = {
18
- :class => Server,
19
- :collection => servers,
20
- :display => [:hostname, :provider_class, :ip, :status],
21
- :labels => { :hostname => "Name", :provider_class => "Provider", :ip => "IP", :status => "Status" },
22
- :main_column => :hostname,
23
- :empty => "You have no servers under your account. Try adding some with 'server add' command.",
24
- :ansi => options[:plain].nil?
25
- }
26
-
27
- puts table(params) { |item| [item.hostname, item.provider_class, item.ip, item.status ? item.status[0] : ""] }
28
- end
29
-
30
- def delete(parameters, options)
31
- # TODO more than one server with the same hostname
32
- spinner {
33
- server = Server.find(:all).select { |s| s.hostname == parameters[0] }.first
34
- server.destroy
35
- }
36
-
37
- puts "Server #{parameters[0]} deleted successfully"
38
- end
39
-
40
- def add(parameters, options)
41
- if params?
42
- if parameters.size < 2
43
- puts "The command #{"server add".to_s.foreground(:yellow)} requires 2 parameters.\nUsage: stackfu server add PROVIDER SERVER_NAME"
44
- return false
45
- end
46
- provider = parameters[0]
47
- server_name = parameters[1]
48
-
49
- user = User.find(:all).first
50
- return false unless send("check_#{provider.downcase}", user)
51
- else
52
- server_add_header
53
- end
54
-
55
- unless params?
56
- provider, server_name = *server_menu
57
- end
58
-
59
- server = Server.new(:provider_class => provider, :hostname => server_name)
60
- result = spinner {
61
- server.save
62
- }
63
-
64
- puts " "
65
- if result
66
- puts "Server #{server_name} added successfully."
67
- else
68
- puts " "
69
- puts "Server #{server_name} couldn't be added. Here's the error we've got:\n#{server.errors.full_messages.to_s}"
70
- end
71
- end
72
-
73
- private
74
-
75
- def check_slicehost(user)
76
- unless user.settings.respond_to?(:slicehost_token)
77
- return false unless add_slicehost_credentials(user)
78
- end
79
-
80
- return true
81
- end
82
-
83
- def check_webbynode(user)
84
- unless user.settings.respond_to?(:webbynode_login) and user.settings.respond_to?(:webbynode_token)
85
- return false unless add_webbynode_credentials(user)
86
- end
87
-
88
- return true
89
- end
90
-
91
- def server_add_header
92
- puts "=== Add Server ===".foreground(:green).bright
93
- end
94
-
95
- def server_menu
96
- user = User.find(:all).first
97
-
98
- providers = spinner { Provider.find(:all) }
99
- provider_id = menu_for("provider", providers, true)
100
-
101
- provider = providers.select { |p| p.id == provider_id }.first
102
-
103
- puts ""
104
- puts "Provider: #{provider_id.foreground(:blue)}"
105
- puts ""
106
-
107
- return false unless send("check_#{provider_id.downcase}", user)
108
-
109
- servers = spinner { provider.get(:servers).to_structs }
110
- server = menu_for("server", servers)
111
-
112
- puts ""
113
- puts "Adding server #{server.foreground(:blue)}..."
114
-
115
- [provider_id.titleize, server]
116
- end
117
-
118
- def slicehost
119
- spinner {
120
- UserAccount.find(:conditions => { :provider => "Slicehost" }).servers
121
- }
122
- end
123
- end
124
- end
1
+ # module StackFu
2
+ # class ServerCommand < Command
3
+ # include StackFu::ApiHooks
4
+ # include StackFu::ProvidersCredentials
5
+ #
6
+ # aliases :servers
7
+ #
8
+ # alias_subcommand :list => :default
9
+ # # subcommand :add, :required_parameters => [:provider, :server_name]
10
+ # subcommand :delete, :required_parameters => [:server_name]
11
+ #
12
+ # def default(parameters, options)
13
+ # servers = spinner {
14
+ # Server.find(:all)
15
+ # }
16
+ #
17
+ # params = {
18
+ # :class => Server,
19
+ # :collection => servers,
20
+ # :display => [:hostname, :provider_class, :ip, :status],
21
+ # :labels => { :hostname => "Name", :provider_class => "Provider", :ip => "IP", :status => "Status" },
22
+ # :main_column => :hostname,
23
+ # :empty => "You have no servers under your account. Try adding some with 'server add' command.",
24
+ # :ansi => options[:plain].nil?
25
+ # }
26
+ #
27
+ # puts table(params) { |item| [item.hostname, item.provider_class, item.ip, item.status ? item.status[0] : ""] }
28
+ # end
29
+ #
30
+ # def delete(parameters, options)
31
+ # # TODO more than one server with the same hostname
32
+ # spinner {
33
+ # server = Server.find(:all).select { |s| s.hostname == parameters[0] }.first
34
+ # server.destroy
35
+ # }
36
+ #
37
+ # puts "Server #{parameters[0]} deleted successfully"
38
+ # end
39
+ #
40
+ # def add(parameters, options)
41
+ # if params?
42
+ # if parameters.size < 2
43
+ # puts "The command #{"server add".to_s.foreground(:yellow)} requires 2 parameters.\nUsage: stackfu server add PROVIDER SERVER_NAME"
44
+ # return false
45
+ # end
46
+ # provider = parameters[0]
47
+ # server_name = parameters[1]
48
+ #
49
+ # user = User.find(:all).first
50
+ # return false unless send("check_#{provider.downcase}", user)
51
+ # else
52
+ # server_add_header
53
+ # end
54
+ #
55
+ # unless params?
56
+ # provider, server_name = *server_menu
57
+ # end
58
+ #
59
+ # server = Server.new(:provider_class => provider, :hostname => server_name)
60
+ # result = spinner {
61
+ # server.save
62
+ # }
63
+ #
64
+ # puts " "
65
+ # if result
66
+ # puts "Server #{server_name} added successfully."
67
+ # else
68
+ # puts " "
69
+ # puts "Server #{server_name} couldn't be added. Here's the error we've got:\n#{server.errors.full_messages.to_s}"
70
+ # end
71
+ # end
72
+ #
73
+ # private
74
+ #
75
+ # def check_slicehost(user)
76
+ # unless user.settings.respond_to?(:slicehost_token)
77
+ # return false unless add_slicehost_credentials(user)
78
+ # end
79
+ #
80
+ # return true
81
+ # end
82
+ #
83
+ # def check_webbynode(user)
84
+ # unless user.settings.respond_to?(:webbynode_login) and user.settings.respond_to?(:webbynode_token)
85
+ # return false unless add_webbynode_credentials(user)
86
+ # end
87
+ #
88
+ # return true
89
+ # end
90
+ #
91
+ # def server_add_header
92
+ # puts "=== Add Server ===".foreground(:green).bright
93
+ # end
94
+ #
95
+ # def server_menu
96
+ # user = User.find(:all).first
97
+ #
98
+ # providers = spinner { Provider.find(:all) }
99
+ # provider_id = menu_for("provider", providers, true)
100
+ #
101
+ # provider = providers.select { |p| p.id == provider_id }.first
102
+ #
103
+ # puts ""
104
+ # puts "Provider: #{provider_id.foreground(:blue)}"
105
+ # puts ""
106
+ #
107
+ # return false unless send("check_#{provider_id.downcase}", user)
108
+ #
109
+ # servers = spinner { provider.get(:servers).to_structs }
110
+ # server = menu_for("server", servers)
111
+ #
112
+ # puts ""
113
+ # puts "Adding server #{server.foreground(:blue)}..."
114
+ #
115
+ # [provider_id.titleize, server]
116
+ # end
117
+ #
118
+ # def slicehost
119
+ # spinner {
120
+ # UserAccount.find(:conditions => { :provider => "Slicehost" }).servers
121
+ # }
122
+ # end
123
+ # end
124
+ # end
@@ -0,0 +1,111 @@
1
+ module DateHelper
2
+ def distance_of_time_in_words_hash(from_time, to_time, options={})
3
+ from_time = from_time.to_time if from_time.respond_to?(:to_time)
4
+ to_time = to_time.to_time if to_time.respond_to?(:to_time)
5
+
6
+ distance = (from_time - to_time).abs
7
+ distance_of_time_hash(distance, from_time, to_time, options)
8
+ end
9
+
10
+ def distance_of_time_hash(distance, from_time = nil, to_time = nil, options={})
11
+ output = HashWithIndifferentAccess.new
12
+ from_time ||= Time.now
13
+ to_time ||= from_time + distance.seconds
14
+ while distance > 0
15
+ if distance < 1.minute
16
+ output["seconds"] = distance.to_i
17
+ distance = 0
18
+ elsif distance < 1.hour
19
+ output["minutes"], distance = distance.divmod(1.minute)
20
+ elsif distance < 1.day
21
+ output["hours"], distance = distance.divmod(1.hour)
22
+ elsif distance < 28.days
23
+ output["days"], distance = distance.divmod(1.day)
24
+ # Time has to be greater than a month
25
+ else
26
+ smallest, largest = from_time < to_time ? [from_time, to_time] : [to_time, from_time]
27
+
28
+ months = (largest.year - smallest.year) * 12 + (largest.month - smallest.month)
29
+ years, months = months.divmod(12)
30
+
31
+ days = largest.day - smallest.day
32
+
33
+ # Will otherwise incorrectly say one more day if our range goes over a day.
34
+ days -= 1 if largest.hour < smallest.hour
35
+
36
+ if days < 0
37
+ # Convert the last month to days and add to total
38
+ months -= 1
39
+ last_month = largest.advance(:months => -1)
40
+ days += Time.days_in_month(last_month.month, last_month.year)
41
+ end
42
+
43
+ if months < 0
44
+ # Convert a year to months
45
+ years -= 1
46
+ months += 12
47
+ end
48
+
49
+ output["years"] = years
50
+ output["months"] = months
51
+ output["days"] = days
52
+
53
+ total_days, distance = distance.divmod(1.day)
54
+ end
55
+ end
56
+ output
57
+ end
58
+
59
+ def distance_of_time(seconds, options = {})
60
+ display_time_in_words(distance_of_time_hash(seconds), options)
61
+ end
62
+
63
+ def display_time_in_words(hash, include_seconds = false, options = {})
64
+ options.symbolize_keys!
65
+ hash.delete(:seconds) if !include_seconds && hash[:minutes]
66
+ # Remove all the values that are nil.
67
+ time_measurements = ["years",
68
+ "months",
69
+ "weeks",
70
+ "days",
71
+ "hours",
72
+ "minutes",
73
+ "seconds" ].delete_if do |key|
74
+ hash[key].nil? || hash[key].zero? ||
75
+ # Remove the keys that we don't want.
76
+ (!options[:except].nil? && options[:except].include?(key)) ||
77
+ # keep the keys we only want.
78
+ (options[:only] && !options[:only].include?(key))
79
+ end
80
+
81
+ options.delete(:except)
82
+ options.delete(:only)
83
+ output = []
84
+ time_measurements.each do |key|
85
+ name = hash[key] > 1 ? key : key.singularize
86
+ output += ["#{hash[key]} #{name}"]
87
+ end
88
+
89
+ # maybe only grab the first few values
90
+ if options[:precision]
91
+ output = output[0...options[:precision]]
92
+ options.delete(:precision)
93
+ end
94
+
95
+
96
+ output.to_sentence(options)
97
+ end
98
+
99
+ def distance_of_time_in_words(from_time, to_time, include_seconds = false, options = {})
100
+ return old_distance_of_time_in_words(from_time, to_time, include_seconds, options) if options.delete(:vague)
101
+ hash = distance_of_time_in_words_hash(from_time, to_time, options)
102
+ display_time_in_words(hash, include_seconds, options)
103
+ end
104
+
105
+ def distance_of_time_in_percent(from_time, current_time, to_time, options = {})
106
+ options[:precision] ||= 0
107
+ distance = to_time - from_time
108
+ number_with_precision(((current_time - from_time) / distance) * 100, options).to_s + "%"
109
+ end
110
+ end
111
+
data/lib/stackfu.rb CHANGED
@@ -37,6 +37,7 @@ require "#{dir}/helpers/providers_credentials"
37
37
  require "#{dir}/operating_systems"
38
38
  require "#{dir}/api_hooks"
39
39
  require "#{dir}/app"
40
+ require "#{dir}/date_helper"
40
41
 
41
42
  require "#{dir}/commands/command"
42
43
  require "#{dir}/commands/help_command"
@@ -50,12 +51,13 @@ require "#{dir}/commands/dump_command"
50
51
 
51
52
  module StackFu
52
53
  VERSION = '0.1.1'
53
- API = "http://stackfu.com"
54
+ # API = "http://beta.stackfu.com"
55
+ API = "http://localhost:3000"
54
56
  CONFIG_FILE = "#{ENV['HOME']}/.stackfu"
55
57
 
56
58
  include StackFu::OperatingSystems
57
59
 
58
- module Exceptions
60
+ module Commands::Exceptions
59
61
  class UnknownCommand < StandardError; end
60
62
  class InvalidCommand < StandardError; end
61
63
  class InvalidSubcommand < StandardError; end
@@ -0,0 +1,11 @@
1
+ HTTP/1.1 200 OK
2
+ Etag: "473e36b98defa0d6d071cbbcc0d4d637"
3
+ Connection: Keep-Alive
4
+ Content-Type: application/json; charset=utf-8
5
+ Date: Fri, 03 Sep 2010 18:55:17 GMT
6
+ Server: WEBrick/1.3.1 (Ruby/1.8.7/2009-12-24)
7
+ X-Runtime: 1.067382
8
+ Content-Length: 6099
9
+ Cache-Control: max-age=0, private, must-revalidate
10
+
11
+ [{"slug":"firewall","name":"firewall","controls":[{"name":"Ports","label":"Ports","_id":"4c7fd205d489e8d081000005","_type":"Textbox","type":"Textbox","hint":"separate by comma's"}],"created_at":"2010-09-02T13:34:13-03:00","updated_at":"2010-09-02T13:34:13-03:00","_id":"4c7fd205d489e8d081000008","validations":[{"body":"test -x ufw","_id":"4c7fd205d489e8d081000002","_type":"ExecutableExists","params":{"data":"ufw"},"description":"File ufw exists and is executable"}],"user_id":"4c7fd205d489e8d081000003","requirements":[{"body":"test -x apt-get","_id":"4c7fd205d489e8d081000001","_type":"ExecutableExists","params":{"data":"apt-get"},"description":"File apt-get exists and is executable"}],"executions":[{"body":" # \n # configure_ufw.sh\n # Tue Dec 01 15:06:48 -0200 2009\n #\n\n apt-get update\n apt-get install -y ufw\n","_id":"4c7fd205d489e8d081000006","description":"Install Ufw"},{"body":" # \n # install_ufw.sh\n # Tue Dec 01 15:06:48 -0200 2009\n #\n\n ufw default deny\n <% ports.split(\",\").each do |port| %>\n ufw allow <%= port %> \n <% end %>\n","_id":"4c7fd205d489e8d081000007","description":"Configure Ufw"}],"description":"Set up a firewall for your server to improve security.\n Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n Praesent eget erat libero, id malesuada tortor.\n Donec pharetra sapien et nulla ultricies ac pharetra neque vulputate."},{"slug":"mongodb","name":"mongodb","controls":[{"name":"Ports","label":"Ports","_id":"4c7fd205d489e8d081000009","_type":"Textbox","type":"Textbox","hint":"separate by comma's"}],"created_at":"2010-09-02T13:34:13-03:00","updated_at":"2010-09-02T13:34:13-03:00","_id":"4c7fd205d489e8d08100000c","validations":[{"body":"test -x ufw","_id":"4c7fd205d489e8d081000002","_type":"ExecutableExists","params":{"data":"ufw"},"description":"File ufw exists and is executable"}],"user_id":"4c7fd205d489e8d081000003","requirements":[{"body":"test -x apt-get","_id":"4c7fd205d489e8d081000001","_type":"ExecutableExists","params":{"data":"apt-get"},"description":"File apt-get exists and is executable"}],"executions":[{"body":" # \n # configure_ufw.sh\n # Tue Dec 01 15:06:48 -0200 2009\n #\n\n apt-get update\n apt-get install -y ufw\n","_id":"4c7fd205d489e8d08100000a","description":"Install Ufw"},{"body":" # \n # install_ufw.sh\n # Tue Dec 01 15:06:48 -0200 2009\n #\n\n ufw default deny\n <% ports.split(\",\").each do |port| %>\n ufw allow <%= port %> \n <% end %>\n","_id":"4c7fd205d489e8d08100000b","description":"Configure Ufw"}],"description":"Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n In et erat sem, at pellentesque augue. Aliquam tincidunt viverra mauris,\n non auctor nibh pretium ac. Phasellus dapibus interdum rutrum. Nunc eget\n felis sed nulla porta elementum. Proin nec tortor sit amet turpis scelerisque ullamcorper."},{"slug":"memcached","name":"memcached","controls":[{"name":"Ports","label":"Ports","_id":"4c7fd205d489e8d08100000d","_type":"Textbox","type":"Textbox","hint":"separate by comma's"}],"created_at":"2010-09-02T13:34:13-03:00","updated_at":"2010-09-02T13:34:13-03:00","_id":"4c7fd205d489e8d081000010","validations":[{"body":"test -x ufw","_id":"4c7fd205d489e8d081000002","_type":"ExecutableExists","params":{"data":"ufw"},"description":"File ufw exists and is executable"}],"user_id":"4c7fd205d489e8d081000003","requirements":[{"body":"test -x apt-get","_id":"4c7fd205d489e8d081000001","_type":"ExecutableExists","params":{"data":"apt-get"},"description":"File apt-get exists and is executable"}],"executions":[{"body":" # \n # configure_ufw.sh\n # Tue Dec 01 15:06:48 -0200 2009\n #\n\n apt-get update\n apt-get install -y ufw\n","_id":"4c7fd205d489e8d08100000e","description":"Install Ufw"},{"body":" # \n # install_ufw.sh\n # Tue Dec 01 15:06:48 -0200 2009\n #\n\n ufw default deny\n <% ports.split(\",\").each do |port| %>\n ufw allow <%= port %> \n <% end %>\n","_id":"4c7fd205d489e8d08100000f","description":"Configure Ufw"}],"description":"Etiam id odio lorem, vitae consectetur nunc. Nunc a libero vel\n tortor malesuada mattis. Integer euismod libero vel tellus tristique a dapibus\n neque consequat. Mauris metus tellus, pellentesque hendrerit aliquam vitae,\n tristique at quam. Cras posuere imperdiet nunc in semper."},{"slug":"mysql","name":"mysql","controls":[{"name":"Ports","label":"Ports","_id":"4c7fd205d489e8d081000011","_type":"Textbox","type":"Textbox","hint":"separate by comma's"}],"created_at":"2010-09-02T13:34:13-03:00","updated_at":"2010-09-02T13:34:13-03:00","_id":"4c7fd205d489e8d081000014","validations":[{"body":"test -x ufw","_id":"4c7fd205d489e8d081000002","_type":"ExecutableExists","params":{"data":"ufw"},"description":"File ufw exists and is executable"}],"user_id":"4c7fd205d489e8d081000004","requirements":[{"body":"test -x apt-get","_id":"4c7fd205d489e8d081000001","_type":"ExecutableExists","params":{"data":"apt-get"},"description":"File apt-get exists and is executable"}],"executions":[{"body":" # \n # configure_ufw.sh\n # Tue Dec 01 15:06:48 -0200 2009\n #\n\n apt-get update\n apt-get install -y ufw\n","_id":"4c7fd205d489e8d081000012","description":"Install Ufw"},{"body":" # \n # install_ufw.sh\n # Tue Dec 01 15:06:48 -0200 2009\n #\n\n ufw default deny\n <% ports.split(\",\").each do |port| %>\n ufw allow <%= port %> \n <% end %>\n","_id":"4c7fd205d489e8d081000013","description":"Configure Ufw"}],"description":"Duis nulla ipsum, malesuada nec facilisis vel, blandit ac ante.\n Nullam sollicitudin tortor sit amet enim tincidunt consequat accumsan ipsum vestibulum.\n Sed et congue enim. Morbi venenatis orci quis nisi vulputate lacinia. Morbi pharetra\n adipiscing velit nec cursus. Vivamus consequat nunc nec diam vulputate euismod. Proin\n sit amet nunc tortor. Nam non mi eu velit pulvinar fermentum aliquam vitae felis."}]
@@ -0,0 +1,10 @@
1
+ HTTP/1.1 200 OK
2
+ Connection: Keep-Alive
3
+ Content-Type: application/json; charset=utf-8
4
+ Date: Mon, 06 Sep 2010 16:03:21 GMT
5
+ Server: WEBrick/1.3.1 (Ruby/1.8.7/2009-12-24)
6
+ X-Runtime: 0.405559
7
+ Content-Length: 1
8
+ Cache-Control: no-cache
9
+ Set-Cookie: _stackfu_session=BAh7ByIPc2Vzc2lvbl9pZCIlNjkwZDkzNDkwYjBjYmE2NWU3NWNhOTI5NmYwYTM3NjQiGXdhcmRlbi51c2VyLnVzZXIua2V5WwciCVVzZXJvOhNCU09OOjpPYmplY3RJRAY6CkBkYXRhWxFpUWkBg2kB%2FmkkaQHUaQGJaQHoaQGTaQGPaQBpAGkJ--823a781c29cb181fa20038701dd325f47e6e457d; path=/; HttpOnly
10
+
@@ -0,0 +1,10 @@
1
+ HTTP/1.1 200 OK
2
+ Connection: Keep-Alive
3
+ Content-Type: application/json; charset=utf-8
4
+ Date: Mon, 06 Sep 2010 16:03:21 GMT
5
+ Server: WEBrick/1.3.1 (Ruby/1.8.7/2009-12-24)
6
+ X-Runtime: 0.405559
7
+ Content-Length: 1
8
+ Cache-Control: no-cache
9
+ Set-Cookie: _stackfu_session=BAh7ByIPc2Vzc2lvbl9pZCIlNjkwZDkzNDkwYjBjYmE2NWU3NWNhOTI5NmYwYTM3NjQiGXdhcmRlbi51c2VyLnVzZXIua2V5WwciCVVzZXJvOhNCU09OOjpPYmplY3RJRAY6CkBkYXRhWxFpUWkBg2kB%2FmkkaQHUaQGJaQHoaQGTaQGPaQBpAGkJ--823a781c29cb181fa20038701dd325f47e6e457d; path=/; HttpOnly
10
+