ssc 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,56 @@
1
+ module SSC
2
+ module Handler
3
+ class Build < Base
4
+
5
+ default_task :build
6
+
7
+ desc "build", "build an appliance"
8
+ require_appliance_id
9
+ method_option :image_type, :type => :string
10
+ def build
11
+ require_appliance_dir do |appliance, files|
12
+ if appliance.status.state != "ok"
13
+ raise Thor::Error, "Appliance is not OK. Please fix before building.\n#{appliance.status.issues.join("\n")}\n"
14
+ else
15
+ build = StudioApi::RunningBuild.new(:appliance_id => appliance.id, :image_type => options.image_type)
16
+ build.save
17
+ config_file= File.join(Dir.pwd, '.sscrc')
18
+ if File.exists?(config_file)
19
+ config= YAML::load(File.read(config_file))
20
+ config.merge!('latest_build_id' => build.id)
21
+ File.open(config_file, 'w') do |file|
22
+ file.write(config.to_yaml)
23
+ end
24
+ end
25
+ say "Build Started. Build id: #{build.id}"
26
+ end
27
+ end
28
+ end
29
+
30
+ desc "status", "find the build status of an appliance"
31
+ require_authorization
32
+ require_build_id
33
+ def status
34
+ build = StudioApi::Build.find options.build_id
35
+ additional_info=(build.state == 'finished' ? "" : " - #{build.percent}")
36
+ say "Build Status: #{build.state}" + additional_info
37
+ end
38
+
39
+ desc "list", "list builds (running or completed)"
40
+ require_appliance_id
41
+ method_option :running, :type => :boolean, :default => false
42
+ def list
43
+ builds= if options.running?
44
+ StudioApi::RunningBuild.find(:all, :params => {:appliance_id => options.appliance_id})
45
+ else
46
+ StudioApi::Build.find(:all, :params => {:appliance_id => options.appliance_id})
47
+ end
48
+ say "Build List:\n"
49
+ print_table([["id", "version", "state"]]+
50
+ builds.collect{|i| [i.id, "v#{i.version}", i.state]})
51
+ end
52
+
53
+
54
+ end
55
+ end
56
+ end
data/lib/handlers/file.rb CHANGED
@@ -4,39 +4,34 @@ module SSC
4
4
  module Handler
5
5
  class OverlayFile < Base
6
6
 
7
+ include DirectoryManager
7
8
 
8
9
  no_tasks do
9
10
  cattr_reader :local_source
10
11
  @@local_source= 'files/'
11
12
  end
12
13
 
13
- # must be run in appliance directory
14
- # takes the following argument:
15
- # file_path => (relative positions("." and "..") allowed and ~ for home directory allowed)
16
- # takes the following options:
17
- # --path="/path/to/file_directory/" => optional (by default it is the path of the file on the local system)
18
- # --name="file_name" => optional (by default it is the name of the file on the local system)
19
- # --permissions="0766" => optional (default: 0755)
20
- # --owner="user" => optional (default: root)
21
- desc 'file create PATH', 'create a new overlay file'
14
+ desc 'file add PATH', 'create a new overlay file'
22
15
  require_appliance_id
23
16
  allow_remote_option
24
- method_option :path, :type => :string, :default => ''
25
- method_option :name, :type => :string, :default => ''
17
+ method_option :path, :type => :string, :default => ''
18
+ method_option :name, :type => :string, :default => ''
26
19
  method_option :permissions, :type => :string, :default => '0755'
27
- method_option :owner, :type => :string, :default => 'root'
28
- def create(path)
20
+ method_option :owner, :type => :string, :default => 'root'
21
+ method_option :group, :type => :string, :default => 'root'
22
+ def add(path)
29
23
  absolute_path= File.expand_path(path)
30
24
  optional_file_params= {:permissions => options.permissions,
31
- :owner => options.owner}
25
+ :group => options.group,
26
+ :owner => options.owner}
32
27
  file_dir, file_name= File.split(absolute_path)
33
28
  file_dir = options.path == '' ? file_dir : options.path
34
29
  file_name = options.name == '' ? file_name : options.name
30
+ file_params= ({:path => file_dir, :filename => file_name})
31
+ file_params.merge!(optional_file_params)
35
32
  id= nil
36
33
  if options.remote?
37
34
  require_appliance do |appliance|
38
- file_params= ({:path => file_dir, :filename => file_name})
39
- file_params.merge!(optional_file_params)
40
35
  File.open(absolute_path) do |file|
41
36
  file= StudioApi::File.upload(file, appliance.id, file_params)
42
37
  id= file.id.to_i
@@ -44,8 +39,32 @@ module SSC
44
39
  say "Overlay file saved. Id: #{id}"
45
40
  end
46
41
  end
47
- local_copy= initiate_file(file_dir, file_name, id)
48
- say "Created #{local_copy}"
42
+ if ApplianceDirectory.new.valid?
43
+ local_copy= FileListFile.new.initiate_file(absolute_path, file_params)
44
+ say "Created #{local_copy}"
45
+ end
46
+ end
47
+
48
+ desc 'file remove FILE_NAME', 'removes existing overlay file'
49
+ require_appliance_id
50
+ allow_remote_option
51
+ def remove(file_name)
52
+ @file_list= FileListFile.new
53
+ file_id= @file_list.is_uploaded?(file_name)
54
+ if options.remote? && file_id
55
+ begin
56
+ StudioApi::File.find(file_id).destroy
57
+ say "File '#{file_name}' removed"
58
+ rescue
59
+ raise Thor::Error, "Couldn't remove file #{file_name} (id: #{file_id}"
60
+ end
61
+ elsif options.remote? && !file_id
62
+ raise Thor::Error, "File '#{file_name}' not found"
63
+ else
64
+ @file_list.push('remove', {file_name => nil})
65
+ @file_list.save
66
+ say "File '#{file_name}' marked for removal"
67
+ end
49
68
  end
50
69
 
51
70
  desc 'file show FILE_NAME', 'show the contents of the file'
@@ -89,7 +108,7 @@ module SSC
89
108
  out= if options.remote? || file_list_empty?
90
109
  response= StudioApi::File.find(:all, :params => {:appliance_id => appliance.id})
91
110
  response.collect do |file|
92
- {file.filename => {"id" => id, "path" => file.path}}
111
+ {file.filename => {"id" => file.id, "path" => file.path}}
93
112
  end
94
113
  else
95
114
  list_local_files
@@ -30,6 +30,12 @@ module SSC
30
30
  :default => config["appliance_id"]
31
31
  end
32
32
 
33
+ def require_build_id
34
+ config= get_config
35
+ method_option :build_id, :type => :numeric, :required => true,
36
+ :default => config["latest_build_id"]
37
+ end
38
+
33
39
  def allow_remote_option
34
40
  method_option :remote, :type => :boolean, :default => false
35
41
  end
@@ -45,6 +51,9 @@ module SSC
45
51
 
46
52
  module InstanceMethods
47
53
 
54
+ include DirectoryManager
55
+
56
+
48
57
  # Establish connection to Suse Studio with username, password
49
58
  def connect(user, pass, connection_options)
50
59
  @connection= StudioApi::Connection.new(user, pass, self.class::API_URL, connection_options)
@@ -62,7 +71,13 @@ module SSC
62
71
  # Included for those methods that still return arrays for printing
63
72
  # Can be removed eventually
64
73
  # Still seems to be a nice way to format the text output of methods
65
- say array.join("\n"), color
74
+ if array.is_a?(Array)
75
+ array= array.collect {|i| yield(i)} if block_given?
76
+ say array.join("\n"), color
77
+ else
78
+ say "\n"
79
+ end
80
+ array
66
81
  end
67
82
 
68
83
  def require_appliance
@@ -72,6 +87,22 @@ module SSC
72
87
  raise "Unable to find the appliance"
73
88
  end
74
89
  end
90
+
91
+ class ApplianceDirectoryError < StandardError; end
92
+
93
+ def require_appliance_directory
94
+ if File.exist?('./.sscrc')
95
+ require_appliance do |appliance|
96
+ files= {
97
+ :package => PackageFile.new,
98
+ :repository => RepositoryFile.new,
99
+ :file_list => FileListFile.new }
100
+ yield(appliance, files)
101
+ end
102
+ else
103
+ raise ApplianceDirectoryError, 'Appliance directory not found'
104
+ end
105
+ end
75
106
  end
76
107
 
77
108
  end
@@ -47,7 +47,7 @@ module SSC
47
47
  require_appliance_id
48
48
  method_option :all_repos, :type => :boolean, :default => true
49
49
  def search(search_string)
50
- require_appliance_id(@options) do |appliance|
50
+ require_appliance do |appliance|
51
51
  params= {:all_repos => options.all_repos} if options.all_repos
52
52
  software= appliance.search_software(search_string, params)
53
53
  say_array software.collect do |software|
@@ -61,20 +61,20 @@ module SSC
61
61
  allow_remote_option
62
62
  method_option :build_id, :type => :numeric
63
63
  def list(type)
64
- say("installed | selected package only", :red) unless ['installed', 'selected'].include?(type)
65
- out= if options.remote? || no_local_list?
64
+ package_file= PackageFile.new
65
+ raise Thor::Error, "installed | selected package only" unless ['installed', 'selected'].include?(type)
66
+ out= if options.remote? || package_file.empty_list?
66
67
  require_appliance do |appliance|
67
68
  params= {:build_id => options.build_id} if options.build_id
68
69
  software= appliance.send("#{type}_software")
69
70
  formatted_list= software.collect do |package|
70
71
  version= package.version ? { "version" => package.version } : nil
71
- {package.name => version}
72
+ package_file.push('list', {package.name => version})
72
73
  end
73
- save(type, formatted_list)
74
- formatted_list
74
+ package_file.save
75
75
  end
76
76
  else
77
- read(type)
77
+ package_file.read
78
78
  end
79
79
  say out.to_yaml
80
80
  end
@@ -99,7 +99,9 @@ module SSC
99
99
  end
100
100
  end
101
101
  else
102
- save("add", [ name ])
102
+ package_file= PackageFile.new
103
+ package_file.push('add', name)
104
+ package_file.save
103
105
  say "#{name} marked for addition"
104
106
  end
105
107
  end
@@ -114,7 +116,9 @@ module SSC
114
116
  say "State: #{response['state']}"
115
117
  end
116
118
  else
117
- save("remove", [ name ])
119
+ package_file= PackageFile.new
120
+ package_file.push('remove', name)
121
+ package_file.save
118
122
  say "#{name} marked for removal"
119
123
  end
120
124
  end
@@ -129,7 +133,9 @@ module SSC
129
133
  response.collect{|key, val| "#{key}: #{val}"}
130
134
  end
131
135
  else
132
- save("ban", [ name ])
136
+ package_file= PackageFile.new
137
+ package_file.push('ban', name)
138
+ package_file.save
133
139
  say "#{name} marked to be banned"
134
140
  end
135
141
  end
@@ -144,7 +150,9 @@ module SSC
144
150
  response.collect{|key, val| "#{key}: #{val}"}
145
151
  end
146
152
  else
147
- save("unban", [ name ])
153
+ package_file= PackageFile.new
154
+ package_file.push('unban', name)
155
+ package_file.save
148
156
  say "#{name} marked to be unbanned"
149
157
  end
150
158
  end
@@ -48,18 +48,19 @@ module SSC
48
48
  require_appliance_id
49
49
  allow_remote_option
50
50
  def list
51
- list= if options.remote? || no_local_list?
51
+ repo_file= RepositoryFile.new
52
+ list= if options.remote? || repo_file.empty_list?
52
53
  require_appliance do |appliance|
53
54
  appliance.repositories.collect do |repo|
54
- { repo.id => { 'name' => repo.name,
55
- 'type' => repo.type,
56
- 'base_system' => repo.base_system}}
55
+ repo_file.push('list', { repo.id => { 'name' => repo.name,
56
+ 'type' => repo.type,
57
+ 'base_system' => repo.base_system}})
57
58
  end
59
+ repo_file.save
58
60
  end
59
61
  else
60
- read('list')
62
+ repo_file['list']
61
63
  end
62
- save('list', list) unless options.remote?
63
64
  say list.to_yaml
64
65
  end
65
66
 
@@ -73,7 +74,9 @@ module SSC
73
74
  say "Added"+( response.collect{|repos| repos.name} ).join(", ")
74
75
  end
75
76
  else
76
- save('add', repo_ids)
77
+ repo_file= RepositoryFile.new
78
+ repo_ids.each {|id| repo_file.push('add', id)}
79
+ repo_file.save
77
80
  say "Marked the following for addition #{repo_ids.join(", ")}"
78
81
  end
79
82
  end
@@ -88,19 +91,24 @@ module SSC
88
91
  say "Removed #{repo_ids.join(", ")}"
89
92
  end
90
93
  else
91
- save('remove', repo_ids)
94
+ repo_file= RepositoryFile.new
95
+ repo_ids.each {|id| repo_file.push('remove', id)}
96
+ repo_file.save
92
97
  say "Marked the following for removal #{repo_ids.join(", ")}"
93
98
  end
94
99
  end
95
100
 
96
101
  desc 'repository import URL NAME', 'import a 3rd party repository into appliance'
102
+ require_authorization
97
103
  allow_remote_option
98
104
  def import(url, name)
99
105
  if options.remote?
100
106
  repository= StudioApi::Repository.import(url, name)
101
107
  say "Added #{repository.name} at #{url}"
102
108
  else
103
- save("import", [{"name" => name, "url" => url}])
109
+ repo_file= RepositoryFile.new
110
+ repo_file.push('import', {"name" => name, "url" => url})
111
+ repo_file.save
104
112
  say "Marked #{name} for import"
105
113
  end
106
114
  end
data/lib/ssc.rb CHANGED
@@ -8,11 +8,134 @@ require 'handlers/all'
8
8
  require 'yaml'
9
9
 
10
10
  module SSC
11
- class Base < Thor
11
+ class Client < Handler::Base
12
+
13
+ include DirectoryManager
14
+
12
15
  register Handler::Appliance, :appliance, "appliance", "manage appliances"
13
16
  register Handler::Repository, :repository, "repository","manage repositories"
14
17
  register Handler::Package, :package, "package", "manage packages"
15
18
  register Handler::Template, :template, "template", "manage templates"
16
19
  register Handler::OverlayFile, :file, "file", "manage files"
20
+ register Handler::Build, :build, "build", "manage builds"
21
+
22
+ desc "status", "show status of the appliance"
23
+ require_appliance_id
24
+ def status
25
+ require_appliance_directory do |appliance, files|
26
+ # Show appliance status
27
+ say "Appliance: id: #{appliance.id} | name: #{appliance.name}"
28
+ say "Status: #{appliance.status.state}"
29
+ say appliance.status.issues
30
+
31
+ # Show additions
32
+ say "\nAdditions : \n"
33
+ say "\nPackages : \n"
34
+ say_array files[:package]["add"]
35
+ say "\nRepositories : \n"
36
+ say_array files[:repository]["add"]
37
+ say "\nOverlay Files : \n"
38
+ say_array(files[:file_list]["add"]) {|i| i.keys[0]}
39
+
40
+ # Show removals
41
+ say "\nRemovals : \n"
42
+ say "\nPackages : \n"
43
+ say_array files[:package]["remove"]
44
+ say "\nRepositories : \n"
45
+ say_array files[:repository]["remove"]
46
+ say "\nOverlay Files :\n "
47
+ say_array(files[:file_list]["remove"]) {|i| i.keys[0]}
48
+
49
+ # Show banned
50
+ say "\nBanned Packages : \n"
51
+ say_array files[:package]["ban"]
52
+
53
+ # Show unbanned
54
+ say "\nUnBanned Packages : \n"
55
+ say_array files[:package]["unban"]
56
+ end
57
+ end
58
+
59
+ desc "checkout", "checkout the latest changes to an appliance"
60
+ require_appliance_id
61
+ def checkout
62
+ params= {:appliance_id => options.appliance_id,
63
+ :username => options.username,
64
+ :password => options.password}
65
+ require_appliance_directory do |appliance, files|
66
+ options= params.merge(:remote => true)
67
+ invoke "s_s_c:handler:package:list", ["installed"], options
68
+ invoke "s_s_c:handler:repository:list", [], options
69
+ invoke "s_s_c:handler:overlay_file:list", [], options
70
+ end
71
+ rescue ApplianceDirectoryError
72
+ require_appliance do |appliance|
73
+ ApplianceDirectory.new(appliance.name, params).create
74
+ Dir.chdir(appliance.name)
75
+ options= params.merge(:remote => true)
76
+ invoke "s_s_c:handler:package:list", ["installed"], options
77
+ invoke "s_s_c:handler:repository:list", [], options
78
+ invoke "s_s_c:handler:overlay_file:list", [], options
79
+ end
80
+ end
81
+
82
+ desc "commit", "commit changes to studio"
83
+ require_appliance_id
84
+ def commit
85
+ params= {:remote => true,
86
+ :appliance_id => options.appliance_id,
87
+ :username => options.username,
88
+ :password => options.password}
89
+ # Add, Remove, Ban and Unban Packages
90
+ package_file= PackageFile.new
91
+ ["add", "remove", "ban", "unban"].each do |action|
92
+ while package= package_file.pop(action)
93
+ invoke "s_s_c:handler:package:#{action}", [package], params
94
+ end
95
+ end
96
+ package_file.save
97
+
98
+ # Add or Remove Repositories
99
+ repository_file= RepositoryFile.new
100
+ ["add", "remove"].each do |action|
101
+ while repository= repository_file.pop(action)
102
+ invoke "s_s_c:handler:repository:#{action}", [repository], params
103
+ end
104
+ end
105
+ repository_file.save
106
+
107
+ # Add Overlay Files
108
+ file_list = FileListFile.new
109
+ while file= file_list.pop("add")
110
+ params= params.merge(file[:params])
111
+ invoke "s_s_c:handler:overlay_file:add", [file[:full_path]], params
112
+ end
113
+ # Remove Overlay Files
114
+ while file= file_list.pop("remove")
115
+ invoke "s_s_c:handler:overlay_file:remove", [file[:name]], params
116
+ end
117
+ file_list.save
118
+ end
119
+
120
+ class << self
121
+ def help(*args)
122
+ message= <<HELP_MESSAGE
123
+ Tasks:
124
+ ssc checkout # checkout the latest changes to an appliance
125
+ ssc commit # commit changes to studio
126
+ ssc status # show status of the appliance
127
+
128
+ ssc appliance # manage appliances
129
+ ssc build # manage builds
130
+ ssc file # manage files
131
+ ssc package # manage packages
132
+ ssc repository # manage repositories
133
+ ssc template # manage templates
134
+
135
+ ssc help [TASK] # Describe available tasks or one specific task
136
+ HELP_MESSAGE
137
+ puts message
138
+ end
139
+ end
17
140
  end
18
141
  end