Class: Dister::Core
- Inherits:
-
Object
- Object
- Dister::Core
- Defined in:
- lib/dister/core.rb
Overview
Core functionality
Constant Summary collapse
- APP_ROOT =
Absolute path to the root of the current application
File.('.')
Instance Attribute Summary collapse
-
#options
readonly
Returns the value of attribute options.
-
#shell
readonly
Returns the value of attribute shell.
Instance Method Summary collapse
-
#add_package(package)
Add a package to the appliance.
-
#add_packages(packages)
Add a list of packages at once.
-
#appliance ⇒ StudioApi::Appliance
Finds the appliance for the current app.
-
#basesystems ⇒ Array<String>
Find available base systems.
-
#build(build_options = {})
Builds the appliance.
-
#builds ⇒ Array<StudioApi::Build>
Finds all builds.
- #check_template_and_basesystem_availability(template, basesystem)
-
#create_appliance(name, template, basesystem, arch) ⇒ StudioApi::Appliance
Creates a new appliance.
- #download(build_set)
- #ensure_devel_languages_ruby_extensions_repo_is_added
-
#file_upload(filename, upload_options = {}) ⇒ Boolean
Uploads a file identified by filename to a SuSE Studio Appliance.
-
#import_repository(url, name) ⇒ StudioApi::Repository
Asks Studio to mirror a repository.
-
#initialize ⇒ Core
constructor
Connect to SUSE Studio and verify the user’s credentials.
-
#package_app
Creates a tarball that holds the application’s source-files.
-
#package_config_files
Creates all relevant config files (e.g. apache.conf) for the appliance.
-
#package_gems
Use bundler to download and package all required gems for the app.
-
#rm_package(package)
Remove a package from the appliance.
- #templates
- #testdrive(build_set)
-
#upload_bundled_files
Uploads the app tarball and the config file to the appliance.
-
#upload_configurations_scripts ⇒ true
Uploads our configuration scripts.
-
#verify_status
Make sure the appliance doesn’t have conflicts.
Constructor Details
#initialize ⇒ Core
Connect to SUSE Studio and verify the user’s credentials. Sets @options, @shell and @connection for further use.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/dister/core.rb', line 15 def initialize @options ||= Options.new @shell = Thor::Shell::Basic.new @connection = StudioApi::Connection.new( @options.username, @options.api_key, @options.api_path, :proxy => @options.proxy, # proxy can be nil :timeout => (@options.timeout || 60) # default to 60s ) # Try the connection once to determine whether credentials are correct. @connection.api_version StudioApi::Util.configure_studio_connection @connection # Ensure app_name is stored for further use. if @options.app_name.nil? @options.app_name = APP_ROOT.split(/(\/|\\)/).last end true rescue ActiveResource::UnauthorizedAccess puts 'A connection to SUSE Studio could not be established.' = @shell.yes?( 'Would you like to re-enter your credentials and try again? (y/n)' ) if update_credentials retry else abort('Exiting dister.') end end |
Instance Attribute Details
#options (readonly)
Returns the value of attribute options.
8 9 10 |
# File 'lib/dister/core.rb', line 8 def @options end |
#shell (readonly)
Returns the value of attribute shell.
8 9 10 |
# File 'lib/dister/core.rb', line 8 def shell @shell end |
Instance Method Details
#add_package(package)
Add a package to the appliance
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 |
# File 'lib/dister/core.rb', line 305 def add_package package appliance_basesystem = appliance.basesystem result = appliance.search_software(package)#.find{|s| s.name == package } #TODO: better handling #Blocked by bnc# if result.empty? #it is not found in available repos puts "'#{package}' has not been found in the repositories currently "\ "added to your appliance." = @shell.yes?('Would you like to search for this package '\ 'inside other repositories? (y/n)') if matches = appliance.search_software(package, :all_repos => true)\ .find_all { |s| s.name == package } repositories = matches.map do |r| StudioApi::Repository.find r.repository_id end.find_all{|r| r.base_system == appliance_basesystem} if repositories.empty? puts "Cannot find #{package}, please look at this page: " puts URI.encode "http://software.opensuse.org/search?p=1&"\ "baseproject=ALL&q=#{package}" else puts "Package #{package} can be installed from one of the "\ "following repositories:" repositories.each_with_index do |repo, index| puts "#{index+1} - #{repo.name} (#{repo.base_url})" end puts "#{repositories.size+1} - None of them." begin choice = @shell.ask("Which repo do you want to use? "\ "[1-#{repositories.size+1}]") end while (choice.to_i > (repositories.size+1) || choice.to_i < 1) if (choice.to_i == (repositories.size+1)) abort("Package not added.") else repo_id = repositories[choice.to_i-1].id end appliance.add_repository repo_id end else exit 0 end # add repo which contain samba #appliance.add_repository result.repository_id end Utils::execute_printing_progress "Adding #{package} package" do appliance.add_package(package) end end |
#add_packages(packages)
Add a list of packages at once
357 358 359 |
# File 'lib/dister/core.rb', line 357 def add_packages(packages) packages.each { |package| self.add_package(package) } end |
#appliance ⇒ StudioApi::Appliance
Finds the appliance for the current app
144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/dister/core.rb', line 144 def appliance if @appliance.nil? begin appliance_id = self..appliance_id return nil if appliance_id.nil? @appliance = StudioApi::Appliance.find(appliance_id.to_i) rescue ActiveResource::BadRequest self..appliance_id = nil nil end else @appliance end end |
#basesystems ⇒ Array<String>
Find available base systems
178 179 180 |
# File 'lib/dister/core.rb', line 178 def basesystems templates.collect(&:basesystem).uniq end |
#build(build_options = {})
Builds the appliance
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/dister/core.rb', line 86 def build = {} verify_status #TODO: # * build using another format force = [:force] version = nil begin params = { :appliance_id => @options.appliance_id, :image_type => "oem" } params[:force] = force if force params[:version] = version if version build = StudioApi::RunningBuild.create(params) rescue StudioApi::ImageAlreadyExists @shell.say 'An image with the same version already exists' if @shell.yes? 'Do you want to overwrite it? (y/n)' force = true retry else begin version = @shell.ask 'Enter new version number:' end until !version.blank? retry end end build.reload if build.state == "queued" puts "Your build is queued. It will be automatically processed by "\ "SUSE Studio. You can keep waiting or you can exit from dister." puts "Exiting from dister won't remove your build from the queue." if @shell.no?('Do you want to keep waiting (y/n)') exit 0 end Utils::execute_printing_progress "Build queued..." do while build.state == 'queued' do sleep 5 build.reload end end end # build is no longer queued = ProgressBar.new "Building", 100 while not ['finished', 'error', 'failed', 'cancelled'].include?(build.state) .set build.percent.to_i sleep 5 build.reload end .finish build.state == 'finished' end |
#builds ⇒ Array<StudioApi::Build>
Finds all builds
161 162 163 |
# File 'lib/dister/core.rb', line 161 def builds StudioApi::Build.find(:all, :params => {:appliance_id => @options.appliance_id}) end |
#check_template_and_basesystem_availability(template, basesystem)
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/dister/core.rb', line 182 def check_template_and_basesystem_availability template, basesystem available_templates = self.templates match = available_templates.find do |t| t.basesystem == basesystem && t.name.downcase.include?(template.downcase) end if match.nil? STDERR.puts "The #{basesystem} doesn't have the #{template} template." STDERR.puts "Available templates are:" available_templates.find_all do |t| t.basesystem.downcase == basesystem.downcase end.each do |t| STDERR.puts " - #{t.name}" end end match end |
#create_appliance(name, template, basesystem, arch) ⇒ StudioApi::Appliance
Creates a new appliance.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/dister/core.rb', line 56 def create_appliance(name, template, basesystem, arch) match = check_template_and_basesystem_availability(template, basesystem) exit 1 if match.nil? @db_adapter = get_db_adapter app = Utils::execute_printing_progress "Cloning appliance" do StudioApi::Appliance.clone(match.appliance_id, {:name => name, :arch => arch}) end @options.appliance_id = app.id ensure_devel_languages_ruby_extensions_repo_is_added default_packages = %w(devel_C_C++ devel_ruby rubygem-bundler rubygem-passenger-apache2) self.add_packages(default_packages) self.add_packages(@db_adapter.packages) unless @db_adapter.nil? Utils::execute_printing_progress "Uploading build scripts" do upload_configurations_scripts end puts "SUSE Studio appliance successfull created:" puts " #{app.edit_url}" app end |
#download(build_set)
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 |
# File 'lib/dister/core.rb', line 477 def download(build_set) # Choose the build(s) to download. to_download = [] if build_set.size == 1 to_download << build_set.first else to_download = choose do || .choices *build_set do |i| [i] end # wrap choice in an array .choice("All of them.") { build_set } .choice("None.") { exit 0 } .prompt = "Which appliance do you want to download?" end end # Download selected builds. to_download.each do |b| puts "Going to download #{b.to_s}" d = Downloader.new(b.download_url.sub("https:", "http:"),"Downloading") if File.exists? d.filename if @shell.no?("Do you want to overwrite file #{d.filename}? (y/n)") exit 0 end end begin d.start Utils::execute_printing_progress "Calculating md5sum" do digest = Digest::MD5.file d.filename raise "digest check not passed" if digest.to_s != b.checksum.md5 end rescue STDOUT.puts STDERR.puts STDERR.flush STDERR.puts $! exit 1 end end end |
#ensure_devel_languages_ruby_extensions_repo_is_added
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
# File 'lib/dister/core.rb', line 398 def ensure_devel_languages_ruby_extensions_repo_is_added name = "devel:language:ruby:extensions" url = "http://download.opensuse.org/repositories/devel:/languages:/ruby:/extensions/" case appliance.basesystem when "11.1" url += "openSUSE_11.1" name += " 11.1" when "11.2" url += "openSUSE_11.2" name += " 11.2" when "11.3" url += "openSUSE_11.3" name += " 11.3" when "11.4" url += "openSUSE_11.4" name += " 11.4" when "12.1" url += "openSUSE_12.1" name += " 12.1" when "SLED10_SP2", "SLED10_SP3", "SLES10_SP2", "SLES10_SP3" url += "SLE_10/" name += " SLE10" when "SLED11", "SLES11" url += "SLE_11" name += " SLE 11" when "SLED11_SP1", "SLES11_SP1", "SLES11_SP1_VMware" url += "SLE_11_SP1" name += " SLE11 SP1" when "SLES11_SP2", "SLES11_SP2" url += "SLE_11_SP2" name += " SLE11 SP2" else STDERR.puts "#{appliance.basesystem}: unknown base system" exit 1 end Utils::execute_printing_progress "Adding #{name} repository" do repos = StudioApi::Repository.find(:all, :params => {:filter => url.downcase}) if repos.size > 0 repo = repos.first else repo = import_repository url, name end appliance.add_repository repo.id end end |
#file_upload(filename, upload_options = {}) ⇒ Boolean
Uploads a file identified by filename to a SuSE Studio Appliance
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/dister/core.rb', line 216 def file_upload filename, ={} if File.exists? filename # Delete existing (obsolete) file. StudioApi::File.find(:all, :params => { :appliance_id => self..appliance_id }).select { |file| file.path == ([:path] || '/') and file.filename == File.basename(filename) }.each(&:destroy) # Upload new file. = "Uploading #{filename} " += "(#{Utils.readable_file_size(File.size(filename),2)})" Utils::execute_printing_progress do File.open(filename) do |file| StudioApi::File.upload file, @options.appliance_id, end end true else STDERR.puts "Cannot upload #{filename}, it doesn't exists." false end end |
#import_repository(url, name) ⇒ StudioApi::Repository
Asks Studio to mirror a repository.
394 395 396 |
# File 'lib/dister/core.rb', line 394 def import_repository url, name StudioApi::Repository.import url, name end |
#package_app
Creates a tarball that holds the application’s source-files. Previously packaged versions get overwritten.
260 261 262 263 264 265 266 |
# File 'lib/dister/core.rb', line 260 def package_app puts 'Packaging application...' package = ".dister/#{@options.app_name}_application.tar.gz" system "rm #{package}" if File.exists?(package) system "tar -czf .dister/#{@options.app_name}_application.tar.gz ../#{@options.app_name}/ --exclude=.dister &> /dev/null" puts "Done!" end |
#package_config_files
Creates all relevant config files (e.g. apache.conf) for the appliance.
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/dister/core.rb', line 269 def package_config_files filename = File.('../../templates/passenger.erb', __FILE__) erb = ERB.new(File.read(filename)) config_content = erb.result(binding) config_path = "#{APP_ROOT}/.dister/#{@options.app_name}_apache.conf" FileUtils.rm(config_path, :force => true) File.open(config_path, 'w') do |config_file| config_file.write(config_content) end @db_adapter = get_db_adapter unless @db_adapter.nil? create_db_user_file = "#{APP_ROOT}/.dister/create_db_user.sql" FileUtils.rm(create_db_user_file, :force => true) File.open(create_db_user_file, 'w') do |file| file.write(@db_adapter.create_user_cmd) end end end |
#package_gems
Use bundler to download and package all required gems for the app.
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/dister/core.rb', line 240 def package_gems if !File.exists?("#{APP_ROOT}/Gemfile") puts "Gemfile missing, cannot use bundler" puts 'Either create a Gemfile or use "dister package add" command' return end puts 'Packaging gems...' system "cd #{APP_ROOT}" system "rm -R vendor/cache" if File.exists?("#{APP_ROOT}/vendor/cache") success = system 'bundle package' unless success STDERR.puts "`bundle package` failed, exiting" exit 1 end puts "Done!" end |
#rm_package(package)
Remove a package from the appliance
363 364 365 366 367 |
# File 'lib/dister/core.rb', line 363 def rm_package package Utils::execute_printing_progress "Removing #{package} package" do appliance.remove_package(package) end end |
#templates
165 166 167 168 169 170 171 172 173 174 |
# File 'lib/dister/core.rb', line 165 def templates reply = StudioApi::TemplateSet.find(:first, :conditions => {:name => "default"}) if reply.nil? STDERR.puts "There is no default template set named 'default'" STDERR.puts "Please contact SUSE Studio admin" exit 1 else return reply.template end end |
#testdrive(build_set)
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 |
# File 'lib/dister/core.rb', line 459 def testdrive(build_set) build = build_set[0] # for now we just take the first available build testdrive = Utils::execute_printing_progress "Starting testdrive" do begin StudioApi::Testdrive.create(:build_id => build.id) rescue STDERR.puts $! exit 1 end end # NOTE can't get http to work, so lets just provide vnc info for now puts "Connect to your testdrive using VNC:" vnc = testdrive.server.vnc puts "Server: #{vnc.host}:#{vnc.port}" puts "Password: #{vnc.password}" end |
#upload_bundled_files
Uploads the app tarball and the config file to the appliance.
291 292 293 294 295 296 297 298 299 300 301 |
# File 'lib/dister/core.rb', line 291 def upload_bundled_files = {:path => "/srv/www", :owner => 'root', :group => 'root'} # Upload tarball. self.file_upload("#{APP_ROOT}/.dister/#{@options.app_name}_application.tar.gz", ) # Upload config files to separate location. [:path] = "/etc/apache2/vhosts.d" self.file_upload("#{APP_ROOT}/.dister/#{@options.app_name}_apache.conf", ) # Upload db related files to separate location. [:path] = "/root" self.file_upload("#{APP_ROOT}/.dister/create_db_user.sql", ) end |
#upload_configurations_scripts ⇒ true
Uploads our configuration scripts
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 |
# File 'lib/dister/core.rb', line 371 def upload_configurations_scripts rails_root = "/srv/www/#{@options.app_name}" filename = File.('../../templates/boot_script.erb', __FILE__) erb = ERB.new(File.read(filename)) boot_script = erb.result(binding) filename = File.('../../templates/build_script.erb', __FILE__) erb = ERB.new(File.read(filename)) build_script = erb.result(binding) conf = appliance.configuration conf.scripts.boot.script = boot_script conf.scripts.boot.enabled = true conf.scripts.build.script = build_script conf.scripts.build.enabled = true conf.save true end |
#verify_status
Make sure the appliance doesn’t have conflicts. In this case an error message is shown and the program halts.
448 449 450 451 452 453 454 455 456 |
# File 'lib/dister/core.rb', line 448 def verify_status Utils::execute_printing_progress "Verifying appliance status" do if appliance.status.state != "ok" = "Appliance is not OK - #{appliance.status.issues.inspect}" += "\nVisit #{appliance.edit_url} to manually fix the issue." raise end end end |