dister 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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