dister 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,3 +3,5 @@ pkg
3
3
  .dister
4
4
  rdoc
5
5
  Gemfile.lock
6
+ doc
7
+ .yardoc
@@ -0,0 +1,7 @@
1
+ --markup rdoc
2
+ --default-return ''
3
+ --no-private
4
+ --title "Documentation for dister"
5
+ --exclude studio_api
6
+ lib/**/*.rb
7
+ - LICENSE
data/Changelog CHANGED
@@ -1,3 +1,8 @@
1
+ Thu Apr 12 10:32:24 CEST 2012 Flavio Castelli <flavio@castelli.name>
2
+
3
+ * release version 0.1.3:
4
+ - add support for SLE11 SP2 base systems.
5
+
1
6
  Fri May 06 16:55:23 CEST 2011 Flavio Castelli <flavio@castelli.name>
2
7
 
3
8
  * Fixed a method missing error.
@@ -6,8 +11,8 @@ Fri May 06 16:46:10 CEST 2011 Flavio Castelli <flavio@castelli.name>
6
11
 
7
12
  * appliance building: ask the user what to do when there's already an
8
13
  image with the same version.
9
- * release version 1.0.1
14
+ * release version 0.1.1
10
15
 
11
16
  Wed May 04 16:00:00 CEST 2011 Flavio Castelli <flavio@castelli.name>
12
17
 
13
- * released first version 1.0.0
18
+ * released first version 0.0.1
@@ -1,22 +1,22 @@
1
1
  = Dister: an Heroku like solution for SUSE Studio
2
2
 
3
3
  {SUSE Studio}[http://susestudio.com] is an online Linux image creation tool.
4
- Creating an appliance to run your rails appliacation can be quite annoying:
5
- there are extra repositories to add, gem dependencies to satisfy, a database to
6
- setup, apache and passenger to configure, overlay files to add and so on.
4
+ Creating an appliance to run your Rails application can be quite annoying:
5
+ there are extra repositories to add, gem dependencies to satisfy, a database to
6
+ setup, Apache and Passenger to configure, overlay files to add and so on.
7
7
 
8
8
  You can save some time cloning {this}[http://susegallery.com/a/CZ0T0D/rails-in-a-box]
9
9
  appliance shared on {SUSE Gallery}[http://susegallery.com/], but some efforts
10
10
  are still required.
11
11
 
12
- Currenlty the easiest solution to deploy a rails application in the cloud is
12
+ Currently the easiest solution to deploy a Rails application in the cloud is
13
13
  {Heroku}[http://heroku.com/].
14
14
 
15
- Dister is a command line tool similar to the one used by Heroku. Within a few
16
- steps you can create a SUSE Studio appliance running your rails application,
15
+ Dister is a command line tool similar to the one used by Heroku. Within a few
16
+ steps you can create a SUSE Studio appliance running your Rails application,
17
17
  download it and run into your private or public cloud.
18
18
 
19
- SUSE Studio currenlty supports the following appliance formats:
19
+ SUSE Studio currently supports the following appliance formats:
20
20
  - oem: it can be run inside KVM or it can be installed to a hard disk/usb pen.
21
21
  - iso and preload iso: you can create a live dvd or an installation dvd.:
22
22
  - vmx and ovf: use these formats if you want to run your appliance inside of
@@ -25,12 +25,15 @@ SUSE Studio currenlty supports the following appliance formats:
25
25
  hypervisor.
26
26
  - ec2: jumping into Amazon's cloud has never been so easy.
27
27
 
28
- More formats are coming. Checkout {SUSE Studio}[http://susestudio.com] for
28
+ More formats are coming. Checkout {SUSE Studio}[http://susestudio.com] for
29
29
  more details.
30
30
 
31
+ Currently only MRI Ruby 1.8 is supported, but support for 1.9 and REE is
32
+ forthcoming.
33
+
31
34
  == Common workflow
32
35
 
33
- This section will show the common workflow required to create a SUSE Studio
36
+ This section will show the common workflow required to create a SUSE Studio
34
37
  appliance running a standard Rails application.
35
38
 
36
39
  All the following commands must be executed inside of the root directory of
@@ -39,8 +42,8 @@ your rails application.
39
42
  === Create
40
43
  You can create your SUSE Studio appliance using the following command:
41
44
  dister create APPLIANCE_NAME
42
- The following code creates a 32bit appliance based on the latest version of
43
- openSUSE supported by SUSE Studio. The appliance will use the
45
+ This creates a 32bit appliance based on the latest version of openSUSE supported
46
+ by SUSE Studio. The appliance will use the
44
47
  {JeOS}[http://en.wikipedia.org/wiki/Just_enough_operating_system] template.
45
48
 
46
49
  You can change the default behaviour using the following command line options:
@@ -48,16 +51,17 @@ You can change the default behaviour using the following command line options:
48
51
  - <tt>--template</tt>: to use something different from the JeOS template.
49
52
  - <tt>--arch</tt>: to build a 64bit appliance.
50
53
 
51
- By default all the appliances created by dister have the <em>devel:language:ruby:extensions</em>
52
- repository. This repository contains all the ruby-related packages.
53
- It's actively maintained by the openSUSE community and by some Novell employee.
54
+ By default all the appliances created by dister have the
55
+ <em>devel:language:ruby:extensions</em> repository. This repository contains all
56
+ the Ruby-related packages. It's actively maintained by the openSUSE community
57
+ and by some Novell employee.
54
58
 
55
59
  The following packages are automatically added to all appliances:
56
- - devel_C_C++ and devel_Ruby: these are needed in order to build native gems.
57
- - rubygem-bundler: this package provides latest version of bundler.
58
- - rubygem-passenger-apache2: this package is required in order to deploy your
59
- rails application using Apache. All the Apache packages will be automatically
60
- installed by SUSE Studio because they are dependencies of rubygem-passenger-apache2.
60
+ - <tt>devel_C_C++</tt> and +devel_Ruby+: these are needed in order to build native gems.
61
+ - +rubygem-bundler+: this package provides latest version of bundler.
62
+ - +rubygem-passenger-apache2+: this package is required in order to deploy your
63
+ Rails application using Apache. All the Apache packages will be automatically
64
+ installed by SUSE Studio because they are dependencies of +rubygem-passenger-apache2+.
61
65
 
62
66
  The create task takes care of uploading some custom build and boot scripts.
63
67
  These scripts take care of initializing your appliance. You can inspect them
@@ -70,15 +74,30 @@ In order to add your rails application to your SUSE Studio appliance just execut
70
74
  This will automatically create (or update) your local bundle and upload it to
71
75
  SUSE Studio.
72
76
  Your bundle contains:
73
- - all the gems needed by your rails appliance (this is done using bundler).
77
+ - all the gems needed by your Rails appliance (this is done using bundler).
74
78
  - all your code
75
- - apache configuration
79
+ - Apache configuration
76
80
 
77
81
  === Build
78
82
  Building can be triggered using the following command:
79
83
  dister build
80
84
  A nice progress bar will be shown.
81
85
 
86
+ === Testdrive
87
+ After your build has completed, you can testdrive your appliance:
88
+ dister testdrive
89
+
90
+ Currently you have to access your testdrive using VNC, but support for the
91
+ web-based Flash interface is on the way.
92
+
93
+ === Download
94
+ If you're happy with what you saw during your testdrive, you can download the
95
+ appliance:
96
+ dister download
97
+
98
+ You can then deploy it. We want to support direct deployment to EC2 in future
99
+ versions.
100
+
82
101
  TODO: write more documentation.
83
102
 
84
103
  == License
data/Rakefile CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'bundler'
2
2
  require 'rake'
3
- require 'rake/rdoctask'
4
3
  require 'rake/testtask'
5
4
  Bundler::GemHelper.install_tasks
6
5
 
@@ -13,14 +12,9 @@ Rake::TestTask.new(:test) do |test|
13
12
  test.verbose = true
14
13
  end
15
14
 
16
- desc 'Generate documentation.'
17
- Rake::RDocTask.new(:rdoc) do |rdoc|
18
- rdoc.rdoc_dir = 'rdoc'
19
- rdoc.title = 'dister'
20
- rdoc.options << '--line-numbers' << "--main" << "README.rdoc"
21
- rdoc.rdoc_files.include('README.rdoc')
22
- rdoc.rdoc_files.include('lib/**/*.rb')
15
+ begin
16
+ require 'yard'
17
+ YARD::Rake::YardocTask.new
18
+ rescue LoadError
19
+ puts "Yard not available. To generate documentation install it with: gem install yard"
23
20
  end
24
-
25
- desc "Clean files generated by rake tasks"
26
- task :clobber => [:clobber_rdoc]
@@ -15,15 +15,17 @@ Gem::Specification.new do |s|
15
15
  s.rubyforge_project = "dister"
16
16
 
17
17
  s.add_dependency "curb"
18
+ s.add_dependency "highline", "~>1.6.1"
18
19
  s.add_dependency "progressbar"
19
- s.add_dependency "studio_api", "~>3.1.1"
20
+ s.add_dependency "studio_api", "~>3.2"
20
21
  s.add_dependency "thor", "~>0.14.0"
21
22
 
22
- s.add_development_dependency "bundler", "~>1.0.0"
23
+ s.add_development_dependency "bundler"
23
24
  s.add_development_dependency "fakefs"
24
25
  s.add_development_dependency "mocha"
25
26
  s.add_development_dependency "test-unit", "1.2.3"
26
27
  s.add_development_dependency "shoulda"
28
+ s.add_development_dependency "yard"
27
29
  s.files = `git ls-files`.split("\n")
28
30
  s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
29
31
  s.require_path = 'lib'
@@ -1,10 +1,11 @@
1
1
  require 'rubygems'
2
+ require 'erb'
2
3
  require 'thor'
3
4
  require 'studio_api'
4
5
  require 'yaml'
5
6
  require 'progressbar'
6
- require 'bundler'
7
7
  require 'curb'
8
+ require 'highline/import'
8
9
 
9
10
  require File.expand_path('../dister/cli', __FILE__)
10
11
  require File.expand_path('../dister/constants', __FILE__)
@@ -1,14 +1,23 @@
1
1
  module Dister
2
2
 
3
+ # This is the public facing command line interface which is available through
4
+ # the +dister+ command line tool. Use +dister --help+ for usage instructions.
3
5
  class Cli < Thor
6
+
4
7
  include Thor::Actions
5
8
 
6
- # Returns Dister's root directory.
7
9
  # NOTE: Some of Thor's actions require this method to be defined.
10
+ # @return [String] Dister's root directory.
8
11
  def self.source_root
9
12
  File.expand_path('../../',__FILE__)
10
13
  end
11
14
 
15
+ desc "version", "Show dister version"
16
+ def version
17
+ require "dister/version"
18
+ puts "dister version #{Dister::VERSION}"
19
+ end
20
+
12
21
  desc "config OPTION VALUE", "set OPTION value to VALUE"
13
22
  method_option :local,
14
23
  :type => :boolean, :default => false, :required => false
@@ -36,14 +45,13 @@ module Dister
36
45
  # attempt to find latest version of openSUSE
37
46
  basesystem = basesystems.find_all{|a| a =~ /\d+\.\d+/}.sort.last
38
47
  if basesystem.nil?
39
- # apparently this server doesn't offer openSUSE basesystem
40
- @shell.say "Available base systems:"
41
- basesystems.each_with_index { |b,i| @shell.say "#{i+1} - #{b}" }
42
- begin
43
- choice = @shell.ask("Which base system do you want to use?"\
44
- "[1-#{basesystems.size}]")
45
- end while (choice.to_i > basesystems.size || choice.to_i < 1)
46
- basesystem = basesystems[choice.to_i - 1]
48
+ # apparently this server doesn't offer openSUSE basesystem, so we
49
+ # present the user with a menu with available choices
50
+ basesystem = choose do |menu|
51
+ menu.header = "Available base systems"
52
+ menu.choices *basesystems
53
+ menu.prompt = "Which base system do you want to use?"
54
+ end
47
55
  end
48
56
  else
49
57
  basesystem = options[:basesystem]
@@ -1,16 +1,17 @@
1
1
  require 'digest/md5'
2
- require 'erb'
3
2
 
4
3
  module Dister
5
4
 
5
+ # Core functionality
6
6
  class Core
7
7
 
8
8
  attr_reader :options, :shell
9
9
 
10
+ # Absolute path to the root of the current application
10
11
  APP_ROOT = File.expand_path('.')
11
12
 
12
13
  # Connect to SUSE Studio and verify the user's credentials.
13
- # Sets @options, @shell and @connection for further use.
14
+ # Sets +@options+, +@shell+ and +@connection+ for further use.
14
15
  def initialize
15
16
  @options ||= Options.new
16
17
  @shell = Thor::Shell::Basic.new
@@ -33,10 +34,10 @@ module Dister
33
34
  true
34
35
  rescue ActiveResource::UnauthorizedAccess
35
36
  puts 'A connection to SUSE Studio could not be established.'
36
- keep_trying = @shell.ask(
37
+ keep_trying = @shell.yes?(
37
38
  'Would you like to re-enter your credentials and try again? (y/n)'
38
39
  )
39
- if keep_trying == 'y'
40
+ if keep_trying
40
41
  update_credentials
41
42
  retry
42
43
  else
@@ -45,7 +46,13 @@ module Dister
45
46
  end
46
47
 
47
48
  # Creates a new appliance.
48
- # Returns the new appliance.
49
+ #
50
+ # @param [String] name
51
+ # @param [String] template
52
+ # @param [String] basesystem
53
+ # @param [String] arch
54
+ #
55
+ # @return [StudioApi::Appliance] the new appliance
49
56
  def create_appliance(name, template, basesystem, arch)
50
57
  match = check_template_and_basesystem_availability(template, basesystem)
51
58
  exit 1 if match.nil?
@@ -57,15 +64,12 @@ module Dister
57
64
  end
58
65
  @options.appliance_id = app.id
59
66
  ensure_devel_languages_ruby_extensions_repo_is_added
60
- self.add_package "devel_C_C++"
61
- self.add_package "devel_ruby"
62
- self.add_package 'rubygem-bundler'
63
- self.add_package 'rubygem-passenger-apache2'
64
- unless @db_adapter.nil?
65
- @db_adapter.packages.each do |p|
66
- self.add_package p
67
- end
68
- end
67
+
68
+ default_packages = %w(devel_C_C++ devel_ruby
69
+ rubygem-bundler rubygem-passenger-apache2)
70
+
71
+ self.add_packages(default_packages)
72
+ self.add_packages(@db_adapter.packages) unless @db_adapter.nil?
69
73
 
70
74
  Utils::execute_printing_progress "Uploading build scripts" do
71
75
  upload_configurations_scripts
@@ -75,6 +79,10 @@ module Dister
75
79
  app
76
80
  end
77
81
 
82
+ # Builds the appliance
83
+ #
84
+ # @param [Hash] build_options
85
+ # @option build_options [Boolean] :force
78
86
  def build build_options = {}
79
87
  verify_status
80
88
  #TODO:
@@ -91,8 +99,7 @@ module Dister
91
99
  build = StudioApi::RunningBuild.create(params)
92
100
  rescue StudioApi::ImageAlreadyExists
93
101
  @shell.say 'An image with the same version already exists'
94
- overwrite = @shell.ask 'Do you want to overwrite it? (y/n)'
95
- if overwrite == 'y'
102
+ if @shell.yes? 'Do you want to overwrite it? (y/n)'
96
103
  force = true
97
104
  retry
98
105
  else
@@ -108,9 +115,7 @@ module Dister
108
115
  puts "Your build is queued. It will be automatically processed by "\
109
116
  "SUSE Studio. You can keep waiting or you can exit from dister."
110
117
  puts "Exiting from dister won't remove your build from the queue."
111
- shell = Thor::Shell::Basic.new
112
- keep_waiting = @shell.ask('Do you want to keep waiting (y/n)')
113
- if keep_waiting == 'n'
118
+ if @shell.no?('Do you want to keep waiting (y/n)')
114
119
  exit 0
115
120
  end
116
121
 
@@ -134,7 +139,8 @@ module Dister
134
139
  build.state == 'finished'
135
140
  end
136
141
 
137
- # Returns an app's appliance (or nil if none exist).
142
+ # Finds the appliance for the current app
143
+ # @return [StudioApi::Appliance] the app's appliance (or nil if none exist).
138
144
  def appliance
139
145
  if @appliance.nil?
140
146
  begin
@@ -150,6 +156,8 @@ module Dister
150
156
  end
151
157
  end
152
158
 
159
+ # Finds all builds
160
+ # @return [Array<StudioApi::Build>]
153
161
  def builds
154
162
  StudioApi::Build.find(:all, :params => {:appliance_id => @options.appliance_id})
155
163
  end
@@ -165,6 +173,8 @@ module Dister
165
173
  end
166
174
  end
167
175
 
176
+ # Find available base systems
177
+ # @return [Array<String>] a list of available base systems
168
178
  def basesystems
169
179
  templates.collect(&:basesystem).uniq
170
180
  end
@@ -188,15 +198,21 @@ module Dister
188
198
  end
189
199
 
190
200
  # Uploads a file identified by filename to a SuSE Studio Appliance
191
- # options is an hash. it can have the following keys:
192
- # - filename (optional) - The name of the file in the filesystem.
193
- # - path (optional) - The path where the file will be stored.
194
- # - owner (optional) - The owner of the file.
195
- # - group (optional) - The group of the file.
196
- # - permissions (optional) - The permissions of the file.
197
- # - enabled (optional) - Used to enable/disable this file for the builds.
198
- # - url (optional) - The url of the file to add from the internet (HTTP and FTP are supported) when using the web upload method
199
- # This method returns true if the file has been successfully uploaded
201
+ #
202
+ # @param [String] filename name of file to upload
203
+ # @param [Hash] upload_options upload options (all parameters are optional)
204
+ # @option upload_options [String] filename The name of the file in the
205
+ # filesystem
206
+ # @option upload_options [String] path The path where the file will be stored
207
+ # @option upload_options [String] owner The owner of the file
208
+ # @option upload_options [String] group The group of the file
209
+ # @option upload_options [String] permissions The permissions of the file
210
+ # @option upload_options [String] enabled Used to enable/disable this file
211
+ # for the builds
212
+ # @option upload_options [String] url The url of the file to add from the
213
+ # internet (HTTP and FTP are supported) when using the web upload method
214
+ #
215
+ # @return [Boolean] true if the file has been successfully uploaded
200
216
  def file_upload filename, upload_options={}
201
217
  if File.exists? filename
202
218
  # Delete existing (obsolete) file.
@@ -231,7 +247,11 @@ module Dister
231
247
  puts 'Packaging gems...'
232
248
  system "cd #{APP_ROOT}"
233
249
  system "rm -R vendor/cache" if File.exists?("#{APP_ROOT}/vendor/cache")
234
- system 'bundle package'
250
+ success = system 'bundle package'
251
+ unless success
252
+ STDERR.puts "`bundle package` failed, exiting"
253
+ exit 1
254
+ end
235
255
  puts "Done!"
236
256
  end
237
257
 
@@ -280,6 +300,8 @@ module Dister
280
300
  self.file_upload("#{APP_ROOT}/.dister/create_db_user.sql", upload_options)
281
301
  end
282
302
 
303
+ # Add a package to the appliance
304
+ # @param [String] package the name of the package
283
305
  def add_package package
284
306
  appliance_basesystem = appliance.basesystem
285
307
  result = appliance.search_software(package)#.find{|s| s.name == package }
@@ -288,9 +310,9 @@ module Dister
288
310
  if result.empty? #it is not found in available repos
289
311
  puts "'#{package}' has not been found in the repositories currently "\
290
312
  "added to your appliance."
291
- keep_trying = @shell.ask('Would you like to search for this package '\
313
+ keep_trying = @shell.yes?('Would you like to search for this package '\
292
314
  'inside other repositories? (y/n)')
293
- if keep_trying == 'y'
315
+ if keep_trying
294
316
  matches = appliance.search_software(package, :all_repos => true)\
295
317
  .find_all { |s| s.name == package }
296
318
  repositories = matches.map do |r|
@@ -330,6 +352,14 @@ module Dister
330
352
  end
331
353
  end
332
354
 
355
+ # Add a list of packages at once
356
+ # @param [Array<String>] packages
357
+ def add_packages(packages)
358
+ packages.each { |package| self.add_package(package) }
359
+ end
360
+
361
+ # Remove a package from the appliance
362
+ # @param [String] package the name of the package
333
363
  def rm_package package
334
364
  Utils::execute_printing_progress "Removing #{package} package" do
335
365
  appliance.remove_package(package)
@@ -337,6 +367,7 @@ module Dister
337
367
  end
338
368
 
339
369
  # Uploads our configuration scripts
370
+ # @return [true] if the scripts are successfully uploaded
340
371
  def upload_configurations_scripts
341
372
  rails_root = "/srv/www/#{@options.app_name}"
342
373
 
@@ -359,7 +390,7 @@ module Dister
359
390
  end
360
391
 
361
392
  # Asks Studio to mirror a repository.
362
- # Returns a StudioApi::Repository object
393
+ # @return [StudioApi::Repository]
363
394
  def import_repository url, name
364
395
  StudioApi::Repository.import url, name
365
396
  end
@@ -390,6 +421,9 @@ module Dister
390
421
  when "SLED11_SP1", "SLES11_SP1", "SLES11_SP1_VMware"
391
422
  url += "SLE_11_SP1"
392
423
  name += " SLE11 SP1"
424
+ when "SLES11_SP2", "SLES11_SP2"
425
+ url += "SLE_11_SP2"
426
+ name += " SLE11 SP2"
393
427
  else
394
428
  STDERR.puts "#{appliance.basesystem}: unknown base system"
395
429
  exit 1
@@ -418,6 +452,7 @@ module Dister
418
452
  end
419
453
  end
420
454
 
455
+ # @param [Array] build_set
421
456
  def testdrive(build_set)
422
457
  build = build_set[0] # for now we just take the first available build
423
458
  testdrive = Utils::execute_printing_progress "Starting testdrive" do
@@ -435,37 +470,29 @@ module Dister
435
470
  puts "Password: #{vnc.password}"
436
471
  end
437
472
 
473
+ # @param [Array] build_set
438
474
  def download(build_set)
439
475
  # Choose the build(s) to download.
440
476
  to_download = []
441
477
  if build_set.size == 1
442
478
  to_download << build_set.first
443
479
  else
444
- build_set.each_with_index do |build, index|
445
- puts "#{index+1}) #{build.to_s}"
446
- end
447
- puts "#{build_set.size+1}) All of them."
448
- puts "#{build_set.size+2}) None."
449
- begin
450
- choice = @shell.ask "Which appliance do you want to download? [1-#{build_set.size+1}]"
451
- end while (choice.to_i > (build_set.size+2))
452
- if choice.to_i == (build_set.size+2)
453
- # none selected
454
- exit 0
455
- elsif choice.to_i == (build_set.size+1)
456
- # all selected
457
- to_download = build_set
458
- else
459
- to_download << build_set[choice.to_i-1]
480
+ to_download = choose do |menu|
481
+ menu.choices *build_set do |i| [i] end # wrap choice in an array
482
+ menu.choice("All of them.") { build_set }
483
+ menu.choice("None.") { exit 0 }
484
+ menu.prompt = "Which appliance do you want to download?"
460
485
  end
461
486
  end
487
+
462
488
  # Download selected builds.
463
489
  to_download.each do |b|
464
490
  puts "Going to download #{b.to_s}"
465
491
  d = Downloader.new(b.download_url.sub("https:", "http:"),"Downloading")
466
492
  if File.exists? d.filename
467
- overwrite = @shell.ask("Do you want to overwrite file #{d.filename}? (y/n)")
468
- exit 0 if overwrite == 'n'
493
+ if @shell.no?("Do you want to overwrite file #{d.filename}? (y/n)")
494
+ exit 0
495
+ end
469
496
  end
470
497
  begin
471
498
  d.start
@@ -492,6 +519,7 @@ module Dister
492
519
  @options.api_key = @shell.ask("API key:\t")
493
520
  end
494
521
 
522
+ # @return [Dister::DbAdapter]
495
523
  def get_db_adapter
496
524
  db_config_file = "#{APP_ROOT}/config/database.yml"
497
525
  if !File.exists?(db_config_file)
@@ -504,5 +532,7 @@ module Dister
504
532
  Dister::DbAdapter.new db_config_file
505
533
  end
506
534
  end
535
+
507
536
  end
537
+
508
538
  end
@@ -1,31 +1,40 @@
1
- require 'erb'
2
-
3
1
  module Dister
2
+
3
+ # Wrapper class for the database adapter specified in the application's
4
+ # +database.yml+. Also handles database credentials, and dump/restore.
5
+ # Currently this only works with ActiveRecord.
4
6
  class DbAdapter
5
7
 
8
+ # Initialize a new DbAdapter. May raise exceptions if the input is erronous.
9
+ #
10
+ # @param [String] db_config_file path to the database configuration (.yml)
11
+ # @param [String] dump filename of dump
6
12
  def initialize db_config_file, dump=nil
7
13
  config = YAML.load_file(db_config_file)
8
14
  if !config.has_key?("production")
9
15
  STDERR.puts "There's no configuration for the production environment"
10
16
  end
11
-
17
+
12
18
  @adapter = config["production"]["adapter"]
13
19
  @user = config["production"]["username"]
14
- @password = config["production"]["password"]
15
- @dbname = config["production"]["adapter"]
20
+ @password = config["production"]["password"]
21
+ @dbname = config["production"]["adapter"]
16
22
  @dump = dump
17
-
23
+
18
24
  filename = File.expand_path("../../adapters/#{@adapter}.yml", __FILE__)
19
25
  raise "There's no adapter for #{@adapter}" if !File.exists?(filename)
20
26
 
21
27
  @adapter_config = YAML.load_file(filename)
22
28
  end
23
29
 
30
+ # Checks if there is a db dump
31
+ #
32
+ # @return [Boolean] true if there is a dump file, false otherwise
24
33
  def has_dump?
25
34
  return false if @dump.nil?
26
35
  return File.exists? @dump
27
36
  end
28
-
37
+
29
38
  def cmdline_tool
30
39
  @adapter_config["cmdline_tool"]
31
40
  end
@@ -53,5 +62,7 @@ module Dister
53
62
  erb = ERB.new cmd
54
63
  erb.result(binding)
55
64
  end
65
+
56
66
  end
67
+
57
68
  end
@@ -1,12 +1,15 @@
1
1
  module Dister
2
+
3
+ # Curl wrapper for downloading appliance builds.
2
4
  class Downloader
3
5
  attr_reader :filename
4
6
 
5
-
7
+ # @param [String] url URL of file to be downloaded
8
+ # @param [String] message Message to be displayed while downloading
6
9
  def initialize url, message
7
10
  @filename = File.basename(url)
8
11
  @message = message
9
-
12
+
10
13
  # setup curl
11
14
  @curl = Curl::Easy.new
12
15
  @curl.url = url
@@ -21,6 +24,7 @@ module Dister
21
24
  end
22
25
  end
23
26
 
27
+ # Starts the download
24
28
  def start
25
29
  @file = File.open(@filename, "wb")
26
30
  @pbar = ProgressBar.new(@message, 100)
@@ -53,5 +57,7 @@ module Dister
53
57
  @file.close
54
58
  end
55
59
  end
60
+
56
61
  end
62
+
57
63
  end
@@ -1,15 +1,22 @@
1
1
  require 'fileutils'
2
2
 
3
3
  module Dister
4
+
5
+ # Class for handling user- and application-specific settings
4
6
  class Options
5
7
 
8
+ # Default API path, used unless a custom path is specified
6
9
  SUSE_STUDIO_DOT_COM_API_PATH = 'https://susestudio.com/api/v2/user'
10
+ # Path to global (per-user) options file
7
11
  GLOBAL_PATH = "#{File.expand_path('~')}/.dister"
12
+ # Path to app-specific options file
8
13
  LOCAL_PATH = "#{Dister::Core::APP_ROOT}/.dister/options.yml"
9
14
 
10
15
  attr_reader :use_only_local
11
16
 
12
17
  # Read options from file.
18
+ #
19
+ # @param [Boolean] use_only_local Use only local options?
13
20
  def initialize use_only_local=false
14
21
  @use_only_local = use_only_local
15
22
  reload
@@ -27,7 +34,8 @@ module Dister
27
34
  end
28
35
  end
29
36
 
30
- # Read @global and @local option files.
37
+ # Read +@global+ and +@local+ option files. Run this method if their
38
+ # contents has changed.
31
39
  def reload
32
40
  if @use_only_local
33
41
  @global = {}
@@ -104,6 +112,8 @@ module Dister
104
112
  # Returns a hash consisting of both global and local options.
105
113
  # All options can be read through this method.
106
114
  # NOTE: Local options override global options.
115
+ #
116
+ # @return [Hash] a hash consisting of both global and local options
107
117
  def provide
108
118
  @global.merge(@local)
109
119
  end
@@ -1,6 +1,10 @@
1
1
  module Dister
2
+
3
+ # Shared utility methods
2
4
  module Utils
5
+
3
6
  module_function
7
+
4
8
  # Shows message and prints a dot per second until the block code
5
9
  # terminates its execution.
6
10
  # Exceptions raised by the block are displayed and program exists with
@@ -31,7 +35,10 @@ module Dister
31
35
  MEGA_SIZE = 1048576.0
32
36
  KILO_SIZE = 1024.0
33
37
 
34
- # Return the file size with a readable style.
38
+ # @param [Number] size Size to be converted
39
+ # @param [Number] precision Number of decimals desired
40
+ #
41
+ # @return [String] Return the file size with a readable style.
35
42
  def readable_file_size(size, precision)
36
43
  case
37
44
  when size == 1 then "1 Byte"
@@ -1,5 +1,5 @@
1
1
  module Dister
2
2
 
3
- VERSION = "0.1.2"
3
+ VERSION = "0.1.3"
4
4
 
5
5
  end
@@ -131,8 +131,7 @@ class CliTest < Test::Unit::TestCase
131
131
  end
132
132
 
133
133
  should "ask the user which base system to use" do
134
- Thor::Shell::Color.any_instance.stubs(:say)
135
- Thor::Shell::Color.any_instance.expects(:ask).returns(1)
134
+ HighLine.any_instance.stubs(:choose).returns("SLED10_SP2")
136
135
  fake_app = mock()
137
136
  fake_app.stubs(:edit_url).returns("http://susestudio.com")
138
137
  Dister::Core.any_instance.expects(:create_appliance).\
@@ -143,7 +142,7 @@ class CliTest < Test::Unit::TestCase
143
142
  Dister::Cli.start(['create', 'foo'])
144
143
  end
145
144
  end
146
-
145
+
147
146
  should "not ask the user which base system to use if there's a preference" do
148
147
  Thor::Shell::Color.any_instance.expects(:ask).never
149
148
  fake_app = mock()
@@ -39,6 +39,11 @@ class CoreTest < Test::Unit::TestCase
39
39
  @core.stubs(:puts)
40
40
  end
41
41
 
42
+ should 'skip packaging gems unless there is a Gemfile' do
43
+ File.expects(:exists?).returns(false)
44
+ assert_nil @core.package_gems
45
+ end
46
+
42
47
  should 'package all required gems' do
43
48
  File.expects(:exists?).returns(true)
44
49
  @core.expects(:system).with("cd #{Dister::Core::APP_ROOT}").once.returns(true)
@@ -48,6 +53,17 @@ class CoreTest < Test::Unit::TestCase
48
53
  @core.package_gems
49
54
  end
50
55
 
56
+ should 'exit if gem packaging fails' do
57
+ File.expects(:exists?).returns(true)
58
+ @core.expects(:system).with("cd #{Dister::Core::APP_ROOT}").once.returns(true)
59
+ File.expects(:exists?).returns(true)
60
+ @core.expects(:system).with("rm -R vendor/cache").once.returns(true)
61
+ @core.expects(:system).with("bundle package").once.returns(false)
62
+ assert_raise SystemExit do
63
+ @core.package_gems
64
+ end
65
+ end
66
+
51
67
  should "create a tarball of the application's source files" do
52
68
  File.stubs(:exists?).returns(true)
53
69
  @core.expects(:system).with("rm .dister/dister_application.tar.gz").once.returns(true)
@@ -1,4 +1,5 @@
1
1
  require File.expand_path('../../lib/dister',__FILE__)
2
+ require 'bundler/setup' # Use bundled environment for testing
2
3
  require 'test/unit'
3
4
  require 'mocha'
4
5
  require 'shoulda'
@@ -0,0 +1,12 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ class UtilsTest < Test::Unit::TestCase
4
+
5
+ include Dister::Utils
6
+
7
+ should "return readable file sizes" do
8
+ assert_equal readable_file_size(12233, 0), "12 KB"
9
+ assert_equal readable_file_size(12233, 2), "11.95 KB"
10
+ end
11
+
12
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dister
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 800522045387967993
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 2
10
- version: 0.1.2
9
+ - 3
10
+ version: 0.1.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Flavio Castelli
@@ -16,8 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-05-06 00:00:00 +02:00
20
- default_executable:
19
+ date: 2012-04-12 00:00:00 Z
21
20
  dependencies:
22
21
  - !ruby/object:Gem::Dependency
23
22
  name: curb
@@ -27,132 +26,159 @@ dependencies:
27
26
  requirements:
28
27
  - - ">="
29
28
  - !ruby/object:Gem::Version
30
- hash: 3
29
+ hash: 2002549777813010636
31
30
  segments:
32
31
  - 0
33
32
  version: "0"
34
33
  type: :runtime
35
34
  version_requirements: *id001
36
35
  - !ruby/object:Gem::Dependency
37
- name: progressbar
36
+ name: highline
38
37
  prerelease: false
39
38
  requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ hash: 914886426701899987
44
+ segments:
45
+ - 1
46
+ - 6
47
+ - 1
48
+ version: 1.6.1
49
+ type: :runtime
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: progressbar
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
40
55
  none: false
41
56
  requirements:
42
57
  - - ">="
43
58
  - !ruby/object:Gem::Version
44
- hash: 3
59
+ hash: 2002549777813010636
45
60
  segments:
46
61
  - 0
47
62
  version: "0"
48
63
  type: :runtime
49
- version_requirements: *id002
64
+ version_requirements: *id003
50
65
  - !ruby/object:Gem::Dependency
51
66
  name: studio_api
52
67
  prerelease: false
53
- requirement: &id003 !ruby/object:Gem::Requirement
68
+ requirement: &id004 !ruby/object:Gem::Requirement
54
69
  none: false
55
70
  requirements:
56
71
  - - ~>
57
72
  - !ruby/object:Gem::Version
58
- hash: 1
73
+ hash: 1144350831328566222
59
74
  segments:
60
75
  - 3
61
- - 1
62
- - 1
63
- version: 3.1.1
76
+ - 2
77
+ version: "3.2"
64
78
  type: :runtime
65
- version_requirements: *id003
79
+ version_requirements: *id004
66
80
  - !ruby/object:Gem::Dependency
67
81
  name: thor
68
82
  prerelease: false
69
- requirement: &id004 !ruby/object:Gem::Requirement
83
+ requirement: &id005 !ruby/object:Gem::Requirement
70
84
  none: false
71
85
  requirements:
72
86
  - - ~>
73
87
  - !ruby/object:Gem::Version
74
- hash: 39
88
+ hash: 4540094552861788330
75
89
  segments:
76
90
  - 0
77
91
  - 14
78
92
  - 0
79
93
  version: 0.14.0
80
94
  type: :runtime
81
- version_requirements: *id004
95
+ version_requirements: *id005
82
96
  - !ruby/object:Gem::Dependency
83
97
  name: bundler
84
98
  prerelease: false
85
- requirement: &id005 !ruby/object:Gem::Requirement
99
+ requirement: &id006 !ruby/object:Gem::Requirement
86
100
  none: false
87
101
  requirements:
88
- - - ~>
102
+ - - ">="
89
103
  - !ruby/object:Gem::Version
90
- hash: 23
104
+ hash: 2002549777813010636
91
105
  segments:
92
- - 1
93
106
  - 0
94
- - 0
95
- version: 1.0.0
107
+ version: "0"
96
108
  type: :development
97
- version_requirements: *id005
109
+ version_requirements: *id006
98
110
  - !ruby/object:Gem::Dependency
99
111
  name: fakefs
100
112
  prerelease: false
101
- requirement: &id006 !ruby/object:Gem::Requirement
113
+ requirement: &id007 !ruby/object:Gem::Requirement
102
114
  none: false
103
115
  requirements:
104
116
  - - ">="
105
117
  - !ruby/object:Gem::Version
106
- hash: 3
118
+ hash: 2002549777813010636
107
119
  segments:
108
120
  - 0
109
121
  version: "0"
110
122
  type: :development
111
- version_requirements: *id006
123
+ version_requirements: *id007
112
124
  - !ruby/object:Gem::Dependency
113
125
  name: mocha
114
126
  prerelease: false
115
- requirement: &id007 !ruby/object:Gem::Requirement
127
+ requirement: &id008 !ruby/object:Gem::Requirement
116
128
  none: false
117
129
  requirements:
118
130
  - - ">="
119
131
  - !ruby/object:Gem::Version
120
- hash: 3
132
+ hash: 2002549777813010636
121
133
  segments:
122
134
  - 0
123
135
  version: "0"
124
136
  type: :development
125
- version_requirements: *id007
137
+ version_requirements: *id008
126
138
  - !ruby/object:Gem::Dependency
127
139
  name: test-unit
128
140
  prerelease: false
129
- requirement: &id008 !ruby/object:Gem::Requirement
141
+ requirement: &id009 !ruby/object:Gem::Requirement
130
142
  none: false
131
143
  requirements:
132
144
  - - "="
133
145
  - !ruby/object:Gem::Version
134
- hash: 25
146
+ hash: 1882242982824780815
135
147
  segments:
136
148
  - 1
137
149
  - 2
138
150
  - 3
139
151
  version: 1.2.3
140
152
  type: :development
141
- version_requirements: *id008
153
+ version_requirements: *id009
142
154
  - !ruby/object:Gem::Dependency
143
155
  name: shoulda
144
156
  prerelease: false
145
- requirement: &id009 !ruby/object:Gem::Requirement
157
+ requirement: &id010 !ruby/object:Gem::Requirement
146
158
  none: false
147
159
  requirements:
148
160
  - - ">="
149
161
  - !ruby/object:Gem::Version
150
- hash: 3
162
+ hash: 2002549777813010636
151
163
  segments:
152
164
  - 0
153
165
  version: "0"
154
166
  type: :development
155
- version_requirements: *id009
167
+ version_requirements: *id010
168
+ - !ruby/object:Gem::Dependency
169
+ name: yard
170
+ prerelease: false
171
+ requirement: &id011 !ruby/object:Gem::Requirement
172
+ none: false
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ hash: 2002549777813010636
177
+ segments:
178
+ - 0
179
+ version: "0"
180
+ type: :development
181
+ version_requirements: *id011
156
182
  description: Turn your rails app into a SUSE Studio appliance in a few steps.
157
183
  email:
158
184
  - flavio@castelli.name
@@ -165,6 +191,7 @@ extra_rdoc_files: []
165
191
 
166
192
  files:
167
193
  - .gitignore
194
+ - .yardopts
168
195
  - Changelog
169
196
  - Gemfile
170
197
  - LICENSE
@@ -197,7 +224,7 @@ files:
197
224
  - test/fixtures/unsupported_database.yml
198
225
  - test/options_test.rb
199
226
  - test/test_helper.rb
200
- has_rdoc: true
227
+ - test/utils_test.rb
201
228
  homepage: https://github.com/flavio/dister
202
229
  licenses: []
203
230
 
@@ -211,7 +238,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
211
238
  requirements:
212
239
  - - ">="
213
240
  - !ruby/object:Gem::Version
214
- hash: 3
241
+ hash: 2002549777813010636
215
242
  segments:
216
243
  - 0
217
244
  version: "0"
@@ -220,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
220
247
  requirements:
221
248
  - - ">="
222
249
  - !ruby/object:Gem::Version
223
- hash: 23
250
+ hash: 2387630629434237851
224
251
  segments:
225
252
  - 1
226
253
  - 3
@@ -229,9 +256,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
229
256
  requirements: []
230
257
 
231
258
  rubyforge_project: dister
232
- rubygems_version: 1.6.2
259
+ rubygems_version: 1.8.12
233
260
  signing_key:
234
261
  specification_version: 3
235
262
  summary: Heroku like solution for SUSE Studio
236
263
  test_files: []
237
264
 
265
+ has_rdoc: