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 +2 -0
- data/.yardopts +7 -0
- data/Changelog +7 -2
- data/README.rdoc +40 -21
- data/Rakefile +5 -11
- data/dister.gemspec +4 -2
- data/lib/dister.rb +2 -1
- data/lib/dister/cli.rb +17 -9
- data/lib/dister/core.rb +81 -51
- data/lib/dister/db_adapter.rb +18 -7
- data/lib/dister/downloader.rb +8 -2
- data/lib/dister/options.rb +11 -1
- data/lib/dister/utils.rb +8 -1
- data/lib/dister/version.rb +1 -1
- data/test/cli_test.rb +2 -3
- data/test/core_test.rb +16 -0
- data/test/test_helper.rb +1 -0
- data/test/utils_test.rb +12 -0
- metadata +69 -41
data/.gitignore
CHANGED
data/.yardopts
ADDED
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
|
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
|
18
|
+
* released first version 0.0.1
|
data/README.rdoc
CHANGED
@@ -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
|
5
|
-
there are extra repositories to add, gem dependencies to satisfy, a database to
|
6
|
-
setup,
|
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
|
-
|
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
|
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
|
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
|
-
|
43
|
-
|
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
|
52
|
-
repository. This repository contains all
|
53
|
-
It's actively maintained by the openSUSE community
|
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
|
57
|
-
- rubygem-bundler
|
58
|
-
- rubygem-passenger-apache2
|
59
|
-
|
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
|
77
|
+
- all the gems needed by your Rails appliance (this is done using bundler).
|
74
78
|
- all your code
|
75
|
-
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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]
|
data/dister.gemspec
CHANGED
@@ -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.
|
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"
|
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'
|
data/lib/dister.rb
CHANGED
@@ -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__)
|
data/lib/dister/cli.rb
CHANGED
@@ -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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
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]
|
data/lib/dister/core.rb
CHANGED
@@ -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
|
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.
|
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
|
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
|
-
#
|
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
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
-
|
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
|
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
|
-
#
|
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
|
-
#
|
192
|
-
#
|
193
|
-
#
|
194
|
-
#
|
195
|
-
#
|
196
|
-
#
|
197
|
-
#
|
198
|
-
#
|
199
|
-
#
|
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.
|
313
|
+
keep_trying = @shell.yes?('Would you like to search for this package '\
|
292
314
|
'inside other repositories? (y/n)')
|
293
|
-
if keep_trying
|
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
|
-
#
|
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
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
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
|
-
|
468
|
-
|
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
|
data/lib/dister/db_adapter.rb
CHANGED
@@ -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
|
data/lib/dister/downloader.rb
CHANGED
@@ -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
|
data/lib/dister/options.rb
CHANGED
@@ -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
|
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
|
data/lib/dister/utils.rb
CHANGED
@@ -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
|
-
#
|
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"
|
data/lib/dister/version.rb
CHANGED
data/test/cli_test.rb
CHANGED
@@ -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
|
-
|
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()
|
data/test/core_test.rb
CHANGED
@@ -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)
|
data/test/test_helper.rb
CHANGED
data/test/utils_test.rb
ADDED
@@ -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:
|
4
|
+
hash: 800522045387967993
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
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:
|
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:
|
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:
|
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:
|
59
|
+
hash: 2002549777813010636
|
45
60
|
segments:
|
46
61
|
- 0
|
47
62
|
version: "0"
|
48
63
|
type: :runtime
|
49
|
-
version_requirements: *
|
64
|
+
version_requirements: *id003
|
50
65
|
- !ruby/object:Gem::Dependency
|
51
66
|
name: studio_api
|
52
67
|
prerelease: false
|
53
|
-
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:
|
73
|
+
hash: 1144350831328566222
|
59
74
|
segments:
|
60
75
|
- 3
|
61
|
-
-
|
62
|
-
|
63
|
-
version: 3.1.1
|
76
|
+
- 2
|
77
|
+
version: "3.2"
|
64
78
|
type: :runtime
|
65
|
-
version_requirements: *
|
79
|
+
version_requirements: *id004
|
66
80
|
- !ruby/object:Gem::Dependency
|
67
81
|
name: thor
|
68
82
|
prerelease: false
|
69
|
-
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:
|
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: *
|
95
|
+
version_requirements: *id005
|
82
96
|
- !ruby/object:Gem::Dependency
|
83
97
|
name: bundler
|
84
98
|
prerelease: false
|
85
|
-
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:
|
104
|
+
hash: 2002549777813010636
|
91
105
|
segments:
|
92
|
-
- 1
|
93
106
|
- 0
|
94
|
-
|
95
|
-
version: 1.0.0
|
107
|
+
version: "0"
|
96
108
|
type: :development
|
97
|
-
version_requirements: *
|
109
|
+
version_requirements: *id006
|
98
110
|
- !ruby/object:Gem::Dependency
|
99
111
|
name: fakefs
|
100
112
|
prerelease: false
|
101
|
-
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:
|
118
|
+
hash: 2002549777813010636
|
107
119
|
segments:
|
108
120
|
- 0
|
109
121
|
version: "0"
|
110
122
|
type: :development
|
111
|
-
version_requirements: *
|
123
|
+
version_requirements: *id007
|
112
124
|
- !ruby/object:Gem::Dependency
|
113
125
|
name: mocha
|
114
126
|
prerelease: false
|
115
|
-
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:
|
132
|
+
hash: 2002549777813010636
|
121
133
|
segments:
|
122
134
|
- 0
|
123
135
|
version: "0"
|
124
136
|
type: :development
|
125
|
-
version_requirements: *
|
137
|
+
version_requirements: *id008
|
126
138
|
- !ruby/object:Gem::Dependency
|
127
139
|
name: test-unit
|
128
140
|
prerelease: false
|
129
|
-
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:
|
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: *
|
153
|
+
version_requirements: *id009
|
142
154
|
- !ruby/object:Gem::Dependency
|
143
155
|
name: shoulda
|
144
156
|
prerelease: false
|
145
|
-
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:
|
162
|
+
hash: 2002549777813010636
|
151
163
|
segments:
|
152
164
|
- 0
|
153
165
|
version: "0"
|
154
166
|
type: :development
|
155
|
-
version_requirements: *
|
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
|
-
|
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:
|
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:
|
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.
|
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:
|