dister 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ pkg
2
+ .idea
3
+ .dister
4
+ rdoc
5
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source :gemcutter
2
+
3
+ # Specify your gem's dependencies in dister.gemspec
4
+ gemspec
5
+
6
+ # Explicitly requiring test/unit here so that testing works on Ruby 1.9
7
+ group :test do
8
+ gem 'test-unit', '1.2.3', :require => 'test/unit'
9
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2011 Flavio Castelli
2
+ Copyright (c) 2011 Dominik Mayer
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,86 @@
1
+ = Dister: an Heroku like solution for SUSE Studio
2
+
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.
7
+
8
+ You can save some time cloning {this}[http://susegallery.com/a/CZ0T0D/rails-in-a-box]
9
+ appliance shared on {SUSE Gallery}[http://susegallery.com/], but some efforts
10
+ are still required.
11
+
12
+ Currenlty the easiest solution to deploy a rails application in the cloud is
13
+ {Heroku}[http://heroku.com/].
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,
17
+ download it and run into your private or public cloud.
18
+
19
+ SUSE Studio currenlty supports the following appliance formats:
20
+ - oem: it can be run inside KVM or it can be installed to a hard disk/usb pen.
21
+ - iso and preload iso: you can create a live dvd or an installation dvd.:
22
+ - vmx and ovf: use these formats if you want to run your appliance inside of
23
+ VMware, VirtualBox or KVM.
24
+ - XEN guest: use this format if you want to run your appliance using Xen
25
+ hypervisor.
26
+ - ec2: jumping into Amazon's cloud has never been so easy.
27
+
28
+ More formats are coming. Checkout {SUSE Studio}[http://susestudio.com] for
29
+ more details.
30
+
31
+ == Common workflow
32
+
33
+ This section will show the common workflow required to create a SUSE Studio
34
+ appliance running a standard Rails application.
35
+
36
+ All the following commands must be executed inside of the root directory of
37
+ your rails application.
38
+
39
+ === Create
40
+ You can create your SUSE Studio appliance using the following command:
41
+ 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
44
+ {JeOS}[http://en.wikipedia.org/wiki/Just_enough_operating_system] template.
45
+
46
+ You can change the default behaviour using the following command line options:
47
+ - <tt>--basesystem</tt>: to use something different from openSUSE.
48
+ - <tt>--template</tt>: to use something different from the JeOS template.
49
+ - <tt>--arch</tt>: to build a 64bit appliance.
50
+
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
+
55
+ 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.
61
+
62
+ The create task takes care of uploading some custom build and boot scripts.
63
+ These scripts take care of initializing your appliance. You can inspect them
64
+ using SUSE Studio web interface.
65
+
66
+ === Upload your code
67
+ In order to add your rails application to your SUSE Studio appliance just execute:
68
+ dister push
69
+
70
+ This will automatically create (or update) your local bundle and upload it to
71
+ SUSE Studio.
72
+ Your bundle contains:
73
+ - all the gems needed by your rails appliance (this is done using bundler).
74
+ - all your code
75
+ - apache configuration
76
+
77
+ === Build
78
+ Building can be triggered using the following command:
79
+ dister build
80
+ A nice progress bar will be shown.
81
+
82
+ TODO: write more documentation.
83
+
84
+ == License
85
+
86
+ Dister is released under the MIT license.
@@ -0,0 +1,26 @@
1
+ require 'bundler'
2
+ require 'rake'
3
+ require 'rake/rdoctask'
4
+ require 'rake/testtask'
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ task :default => "test"
8
+
9
+ require 'rake/testtask'
10
+ Rake::TestTask.new(:test) do |test|
11
+ test.libs << 'lib' << 'test'
12
+ test.pattern = 'test/**/*_test.rb'
13
+ test.verbose = true
14
+ end
15
+
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')
23
+ end
24
+
25
+ desc "Clean files generated by rake tasks"
26
+ task :clobber => [:clobber_rdoc]
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.expand_path('../../lib/dister',__FILE__)
3
+ Dister::Cli.start(ARGV)
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/dister/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "dister"
6
+ s.version = Dister::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ['Flavio Castelli', 'Dominik Mayer']
9
+ s.email = ['flavio@castelli.name','dmayer@novell.com']
10
+ s.homepage = "https://features.opensuse.org/311133"
11
+ s.summary = "Heroku like solution for SUSE Studio"
12
+ s.description = "Turn your rails app into a SUSE Studio appliance in a few steps."
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+ s.rubyforge_project = "dister"
16
+
17
+ s.add_dependency "curb"
18
+ s.add_dependency "progressbar"
19
+ s.add_dependency "studio_api", "~>3.1.0"
20
+ s.add_dependency "thor", "~>0.14.0"
21
+
22
+ s.add_development_dependency "bundler", "~>1.0.0"
23
+ s.add_development_dependency "fakefs"
24
+ s.add_development_dependency "mocha"
25
+ s.add_development_dependency "test-unit", "1.2.3"
26
+ s.add_development_dependency "shoulda"
27
+ s.files = `git ls-files`.split("\n")
28
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
29
+ s.require_path = 'lib'
30
+ end
@@ -0,0 +1,13 @@
1
+ packages:
2
+ - mysql-community-server
3
+ - ruby-mysql
4
+ daemon_name: mysql
5
+ cmdline_tool: mysql
6
+ create_user_cmd: |
7
+ CREATE USER '<%= @user %>'@'localhost'
8
+ IDENTIFIED BY '<%= @password %>';
9
+ GRANT ALL PRIVILEGES ON *.* TO '<%= @user %>'@'localhost'
10
+ WITH GRANT OPTION;
11
+ restore_dump_cmd: |
12
+ mysql --user=<%= @user %> --password=<%= @password %>
13
+ <%= @dbname %> < <%= @dump %>
@@ -0,0 +1,13 @@
1
+ packages:
2
+ - mysql-community-server
3
+ - rubygem-mysql2
4
+ daemon_name: mysql
5
+ cmdline_tool: mysql
6
+ create_user_cmd: |
7
+ CREATE USER '<%= @user %>'@'localhost'
8
+ IDENTIFIED BY '<%= @password %>';
9
+ GRANT ALL PRIVILEGES ON *.* TO '<%= @user %>'@'localhost'
10
+ WITH GRANT OPTION;
11
+ restore_dump_cmd: |
12
+ mysql --user=<%= @user %> --password=<%= @password %>
13
+ <%= @dbname %> < <%= @dump %>
@@ -0,0 +1,12 @@
1
+ packages:
2
+ - postgresql-server
3
+ - rubygem-pg
4
+ daemon_name: postgresql
5
+ cmdline_tool: psql
6
+ create_user_cmd: |
7
+ CREATE USER '<%= @user %>'
8
+ WITH PASSWORD '<%= @password %>';
9
+ CREATEDB;
10
+ restore_dump_cmd: |
11
+ psql -U <%= @user %> -h localhost -p <%= @password %>
12
+ <%= @dbname %> < <%= @dump %>
@@ -0,0 +1,7 @@
1
+ packages:
2
+ - sqlite3
3
+ - rubygem-sqlite3
4
+ cmdline_tool: sqlite3
5
+ daemon_name:
6
+ create_user_cmd:
7
+ restore_dump_cmd: sqlite db/<%= dbname %>.sqlite3 < <%= dump %>
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'thor'
3
+ require 'studio_api'
4
+ require 'yaml'
5
+ require 'progressbar'
6
+ require 'bundler'
7
+ require 'curb'
8
+
9
+ require File.expand_path('../dister/cli', __FILE__)
10
+ require File.expand_path('../dister/core', __FILE__)
11
+ require File.expand_path('../dister/options', __FILE__)
12
+ require File.expand_path('../dister/utils', __FILE__)
13
+ require File.expand_path('../dister/downloader', __FILE__)
14
+ require File.expand_path('../dister/db_adapter', __FILE__)
15
+ require File.expand_path('../studio_api/build', __FILE__)
16
+
17
+ module Dister
18
+
19
+ autoload :Version, File.expand_path('../dister/version', __FILE__)
20
+
21
+ end
@@ -0,0 +1,198 @@
1
+ module Dister
2
+
3
+ class Cli < Thor
4
+
5
+ VALID_TEMPLATES = %w(JeOS Server X Gnome KDE)
6
+ VALID_FOMATS = %w(oem vmx iso xen) #TODO: add other formats
7
+ VALID_ARCHS = %w(i686 x86_64)
8
+
9
+ include Thor::Actions
10
+
11
+ # Returns Dister's root directory.
12
+ # NOTE: Some of Thor's actions require this method to be defined.
13
+ def self.source_root
14
+ File.expand_path('../../',__FILE__)
15
+ end
16
+
17
+ desc "config OPTION VALUE", "set OPTION value to VALUE"
18
+ method_option :local,
19
+ :type => :boolean, :default => false, :required => false
20
+ def config option, value
21
+ dister_options = Dister::Options.new(!options[:local].nil?)
22
+ dister_options.send("#{option}=", value)
23
+ end
24
+
25
+ desc "create APPLIANCE_NAME", "create a new appliance named APPLIANCE_NAME."
26
+ method_option :basesystem, :type => :string, :default => nil, :required => false
27
+ method_option :template, :type => :string, :default => 'JeOS', :required => false
28
+ method_option :arch, :type => :string, :default => 'i686', :required => false
29
+ def create(appliance_name)
30
+ # Check parameters.
31
+ access_core
32
+ ensure_valid_option options[:arch], VALID_ARCHS, "arch"
33
+ ensure_valid_option options[:template], VALID_TEMPLATES, "template"
34
+ basesystems = @core.basesystems
35
+ basesystem = options[:basesystem] || basesystems.find_all{|a| a =~ /\d+\.\d+/}.sort.last
36
+ ensure_valid_option basesystem, basesystems, "base system"
37
+ # Create appliance and add patterns required to build native gems.
38
+ @core.create_appliance(appliance_name, options[:template], basesystem, options[:arch])
39
+ end
40
+
41
+ desc "build", "Build the appliance."
42
+ def build
43
+ access_core
44
+ ensure_appliance_exists
45
+ if @core.build
46
+ puts "Appliance successfully built."
47
+ else
48
+ puts "Build failed."
49
+ end
50
+ end
51
+
52
+ desc "download", "Download the appliance."
53
+ def download
54
+ access_core
55
+ ensure_appliance_exists
56
+ ensure_build_exists
57
+ @core.download(@builds)
58
+ end
59
+
60
+ desc "testdrive", "Testdrive the appliance."
61
+ def testdrive
62
+ access_core
63
+ ensure_appliance_exists
64
+ ensure_build_exists
65
+ @core.testdrive(@builds)
66
+ end
67
+
68
+ desc "format list|add|rm FORMAT", "Enables building of FORMAT"
69
+ method_option :all, :type => :boolean, :default => false, :required => false
70
+ def format(operation,format = nil)
71
+ access_core
72
+ ensure_valid_option operation, %w(add rm list), "operation"
73
+ if operation == 'list' and options[:all]
74
+ puts "Available formats:"
75
+ puts VALID_FOMATS
76
+ else
77
+ existing_types = @core.options.build_types || []
78
+ chosen_types = case operation
79
+ when "add"
80
+ ensure_valid_option format, VALID_FOMATS, "format"
81
+ @core.options.build_types = (existing_types + [format]).uniq
82
+ when "rm"
83
+ @core.options.build_types = (existing_types - [format])
84
+ else
85
+ existing_types
86
+ end
87
+ puts "Chosen formats:"
88
+ puts chosen_types
89
+ end
90
+ end
91
+
92
+ desc "templates", "List all the templates available on SUSE Studio."
93
+ def templates
94
+ puts VALID_TEMPLATES.sort
95
+ end
96
+
97
+ desc "basesystems", "List all the base systems available on SUSE Studio."
98
+ def basesystems
99
+ access_core
100
+ puts @core.basesystems.sort
101
+ end
102
+
103
+ desc "bundle", "Bundles the application and all required gems."
104
+ def bundle
105
+ access_core
106
+ @core.package_gems
107
+ @core.package_config_files
108
+ # Package app last, since it will tarball the application including all gems.
109
+ @core.package_app
110
+ end
111
+
112
+ desc 'push', 'Pushes all required gems and the application tarball to SUSE Studio.'
113
+ def push
114
+ access_core
115
+ # Always call 'bundle' to ensure we got the latest version bundled.
116
+ invoke :bundle
117
+ ensure_appliance_exists
118
+ @core.upload_bundled_files
119
+ end
120
+
121
+ desc "package add|rm PACKAGE [PACKAGE, ...]", "Add/remove PACKAGE to the appliance"
122
+ def package operation, *package
123
+ access_core
124
+ valid_operations = %w(add rm)
125
+ ensure_valid_option operation, valid_operations, "operation"
126
+ case operation
127
+ when "add"
128
+ package.each do |p|
129
+ @core.add_package p
130
+ end
131
+ when "rm"
132
+ package.each do |p|
133
+ @core.rm_package p
134
+ end
135
+ end
136
+ @core.verify_status
137
+ puts "Done."
138
+ end
139
+
140
+ desc "info", "Show some useful information about the appliance"
141
+ def info
142
+ access_core
143
+ app = Utils::execute_printing_progress "Contacting SUSE Studio" do
144
+ @core.appliance
145
+ end
146
+ puts "Name: #{app.name}"
147
+ puts "Based on: #{app.parent.name}"
148
+ if app.builds.empty?
149
+ puts "No builds yet."
150
+ else
151
+ puts "Builds:"
152
+ app.builds.each do |b|
153
+ puts " - #{b.image_type}, version #{b.version}"
154
+ end
155
+ end
156
+ puts "Edit url: #{app.edit_url}"
157
+ end
158
+
159
+ private
160
+
161
+ # Convenience method to reduce duplicity and improve readability.
162
+ # Sets @core
163
+ def access_core
164
+ @core ||= Core.new
165
+ end
166
+
167
+ # Checks whether an appliance already exists (invokes :create if not).
168
+ def ensure_appliance_exists
169
+ if @core.appliance.nil?
170
+ appliance_id = @core.shell.ask('Please provide a name for your appliance:')
171
+ invoke :create, [appliance_id]
172
+ @core.options.reload
173
+ end
174
+ end
175
+
176
+ # Checks whether there is at least one existing build (invokes :build if not).
177
+ def ensure_build_exists
178
+ @builds = @core.builds
179
+ if @builds.empty?
180
+ invoke :build
181
+ @builds = @core.builds
182
+ @core.options.reload
183
+ end
184
+ end
185
+
186
+ # Ensures actual_value is allowed. If not prints an error message to
187
+ # stderr and exits
188
+ def ensure_valid_option actual_value, allowed_values, option_name
189
+ if allowed_values.find{|v| v.downcase == actual_value.downcase}.nil?
190
+ STDERR.puts "#{actual_value} is not a valid value for #{option_name}"
191
+ STDERR.puts "Valid values are: #{allowed_values.join(" ")}"
192
+ exit 1
193
+ end
194
+ end
195
+
196
+ end
197
+
198
+ end