ssc 0.2.0 → 0.3.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.
- data/Gemfile.lock +1 -1
- data/README.rdoc +83 -3
- data/VERSION +1 -1
- data/bin/ssc +1 -1
- data/lib/directory_manager.rb +107 -155
- data/lib/handlers/all.rb +8 -1
- data/lib/handlers/appliance.rb +16 -12
- data/lib/handlers/build.rb +56 -0
- data/lib/handlers/file.rb +38 -19
- data/lib/handlers/helper.rb +32 -1
- data/lib/handlers/package.rb +19 -11
- data/lib/handlers/repository.rb +17 -9
- data/lib/ssc.rb +124 -1
- data/ssc.gemspec +82 -0
- data/test/helper.rb +12 -1
- data/test/integration/test_appliance.rb +82 -0
- data/test/integration/test_file.rb +86 -0
- data/test/integration/test_package.rb +75 -0
- data/test/integration/test_repository.rb +78 -0
- data/test/unit/test_directory_manager.rb +100 -0
- metadata +12 -22
- data/test/handlers/test_appliance.rb +0 -20
- data/test/handlers/test_helper.rb +0 -45
- data/test/handlers/test_repository.rb +0 -20
- data/test/handlers/test_template.rb +0 -27
- data/test/test_argument_parser.rb +0 -55
- data/test/test_directory_manager.rb +0 -40
- data/test/test_ssc.rb +0 -39
@@ -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
|
-
|
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,
|
25
|
-
method_option :name,
|
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,
|
28
|
-
|
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
|
-
:
|
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
|
-
|
48
|
-
|
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
|
data/lib/handlers/helper.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/handlers/package.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
65
|
-
|
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
|
74
|
-
formatted_list
|
74
|
+
package_file.save
|
75
75
|
end
|
76
76
|
else
|
77
|
-
read
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/handlers/repository.rb
CHANGED
@@ -48,18 +48,19 @@ module SSC
|
|
48
48
|
require_appliance_id
|
49
49
|
allow_remote_option
|
50
50
|
def list
|
51
|
-
|
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'
|
55
|
-
|
56
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|