ssc 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|