ssc 0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +14 -0
- data/Gemfile.lock +39 -0
- data/README.rdoc +22 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/bin/ssc +4 -231
- data/lib/directory_manager.rb +194 -0
- data/lib/handlers/all.rb +33 -0
- data/lib/handlers/appliance.rb +74 -0
- data/lib/handlers/file.rb +102 -0
- data/lib/handlers/helper.rb +79 -0
- data/lib/handlers/package.rb +153 -0
- data/lib/handlers/repository.rb +109 -0
- data/lib/handlers/template.rb +25 -0
- data/lib/ssc.rb +18 -0
- data/test/handlers/test_appliance.rb +20 -0
- data/test/handlers/test_helper.rb +45 -0
- data/test/handlers/test_repository.rb +20 -0
- data/test/handlers/test_template.rb +27 -0
- data/test/helper.rb +19 -0
- data/test/test_argument_parser.rb +55 -0
- data/test/test_directory_manager.rb +40 -0
- data/test/test_ssc.rb +39 -0
- metadata +147 -31
- data/README +0 -1
- data/lib/appliancehandler.rb +0 -69
- data/lib/buildhandler.rb +0 -125
- data/lib/checkouthandler.rb +0 -322
- data/lib/commandhandler.rb +0 -38
- data/lib/request.rb +0 -29
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem "studio_api", ">= 3.1.2"
|
4
|
+
gem "thor", ">=0.14.6"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
gem "shoulda", ">= 0"
|
10
|
+
gem "mocha", ">= 0"
|
11
|
+
gem "bundler", "~> 1.0.0"
|
12
|
+
gem "jeweler", "~> 1.6.0"
|
13
|
+
gem "rcov", ">= 0"
|
14
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activemodel (3.0.0)
|
5
|
+
activesupport (= 3.0.0)
|
6
|
+
builder (~> 2.1.2)
|
7
|
+
i18n (~> 0.4.1)
|
8
|
+
activeresource (3.0.0)
|
9
|
+
activemodel (= 3.0.0)
|
10
|
+
activesupport (= 3.0.0)
|
11
|
+
activesupport (3.0.0)
|
12
|
+
builder (2.1.2)
|
13
|
+
git (1.2.5)
|
14
|
+
i18n (0.4.1)
|
15
|
+
jeweler (1.6.0)
|
16
|
+
bundler (~> 1.0.0)
|
17
|
+
git (>= 1.2.5)
|
18
|
+
rake
|
19
|
+
mocha (0.9.12)
|
20
|
+
rake (0.8.7)
|
21
|
+
rcov (0.9.9)
|
22
|
+
shoulda (2.11.3)
|
23
|
+
studio_api (3.1.2)
|
24
|
+
activeresource (>= 2.3.8)
|
25
|
+
xml-simple (>= 1.0.0)
|
26
|
+
thor (0.14.6)
|
27
|
+
xml-simple (1.1.0)
|
28
|
+
|
29
|
+
PLATFORMS
|
30
|
+
ruby
|
31
|
+
|
32
|
+
DEPENDENCIES
|
33
|
+
bundler (~> 1.0.0)
|
34
|
+
jeweler (~> 1.6.0)
|
35
|
+
mocha
|
36
|
+
rcov
|
37
|
+
shoulda
|
38
|
+
studio_api (>= 3.1.2)
|
39
|
+
thor (>= 0.14.6)
|
data/README.rdoc
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
= ssc
|
2
|
+
|
3
|
+
This is the new version of the the Suse Studio command line client. Built as a part of GSOC 2011.
|
4
|
+
|
5
|
+
== Installing ssc
|
6
|
+
|
7
|
+
=== Straight from the code
|
8
|
+
* Checkout the code
|
9
|
+
* In the checked out directory do `rake install`
|
10
|
+
|
11
|
+
=== Ocassionally released packaged gems
|
12
|
+
* Download
|
13
|
+
|
14
|
+
== Contributing to ssc
|
15
|
+
|
16
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
17
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
18
|
+
* Fork the project
|
19
|
+
* Start a feature/bugfix branch
|
20
|
+
* Commit and push until you are happy with your contribution
|
21
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
22
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "ssc"
|
18
|
+
gem.homepage = "http://github.com/rjsvaljean/ssc"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Command-line client for Suse Studio}
|
21
|
+
gem.description = %Q{Command-line client for Suse Studio}
|
22
|
+
gem.email = "rjsvaljean@gmail.com"
|
23
|
+
gem.authors = ["Ratan Sebastian"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'rcov/rcovtask'
|
36
|
+
Rcov::RcovTask.new do |test|
|
37
|
+
test.libs << 'test'
|
38
|
+
test.pattern = 'test/**/test_*.rb'
|
39
|
+
test.verbose = true
|
40
|
+
test.rcov_opts << '--exclude "gems/*"'
|
41
|
+
end
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "ssc #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/bin/ssc
CHANGED
@@ -1,233 +1,6 @@
|
|
1
|
-
#!/usr/bin/ruby
|
1
|
+
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
require 'net/http'
|
5
|
-
require 'net/netrc'
|
6
|
-
require 'xml/smart'
|
7
|
-
require 'cgi'
|
8
|
-
require 'fileutils'
|
9
|
-
require 'optparse'
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
10
4
|
|
11
|
-
|
12
|
-
|
13
|
-
require 'buildhandler.rb'
|
14
|
-
require 'checkouthandler.rb'
|
15
|
-
|
16
|
-
|
17
|
-
$server_name="susestudio.com"
|
18
|
-
$api_prefix="api/v1/user"
|
19
|
-
$username=""
|
20
|
-
$password=""
|
21
|
-
force = false
|
22
|
-
follow = false
|
23
|
-
images = false
|
24
|
-
version="0.1"
|
25
|
-
|
26
|
-
def get_appliance_from_args_or_config args
|
27
|
-
if args
|
28
|
-
appliance = args[1]
|
29
|
-
end
|
30
|
-
unless appliance
|
31
|
-
if File.exists?(".ssc/appliance.config")
|
32
|
-
appliance_config = XML::Smart.open(".ssc/appliance.config")
|
33
|
-
appliance = appliance_config.find("/checkout/appliance_id").first.to_s if appliance_config.find("/checkout/appliance_id").length > 0
|
34
|
-
end
|
35
|
-
end
|
36
|
-
if appliance.nil? || appliance.empty?
|
37
|
-
STDERR.puts "You need to specify an appliance."
|
38
|
-
exit 1
|
39
|
-
end
|
40
|
-
appliance
|
41
|
-
end
|
42
|
-
|
43
|
-
def base_url
|
44
|
-
"http://#{$server_name}/#{$api_prefix}"
|
45
|
-
end
|
46
|
-
|
47
|
-
opt = OptionParser.new
|
48
|
-
opt.separator("Options")
|
49
|
-
opt.on( "-s", "--server", "=HOST",
|
50
|
-
"The Studio hostname" ) do |v|
|
51
|
-
$server_name = v
|
52
|
-
end
|
53
|
-
opt.on( "-u", "--username", "=USER_NAME", "User name") do |v|
|
54
|
-
$username = v
|
55
|
-
end
|
56
|
-
opt.on( "-p", "--password", "=PASSWORD", "Password") do |v|
|
57
|
-
$password = v
|
58
|
-
end
|
59
|
-
opt.on( "-h", "--help", "Print this message" ) do
|
60
|
-
puts opt
|
61
|
-
exit
|
62
|
-
end
|
63
|
-
opt.on( "-v", "--version", "Print the version") do |v|
|
64
|
-
puts "ssc #{version} - A command line interface to SUSE Studio"
|
65
|
-
exit 0
|
66
|
-
end
|
67
|
-
|
68
|
-
# Try to get credentials from .netrc
|
69
|
-
rc = Net::Netrc.locate($server_name)
|
70
|
-
if $username.empty? and $password.empty? and rc
|
71
|
-
$username = rc.login
|
72
|
-
$password = rc.password
|
73
|
-
end
|
74
|
-
|
75
|
-
|
76
|
-
if ARGV.include?("ba") || ARGV.include?("buildappliance")
|
77
|
-
opt.banner = "Usage: ssc [options] buildappliance APPLIANCE [command-options]"
|
78
|
-
opt.separator("Trigger a build of an appliance.")
|
79
|
-
opt.separator("\n")
|
80
|
-
opt.separator("Command options:")
|
81
|
-
opt.on( "-f", "--force","Force building the appliance even if it overwrites a build") do |v|
|
82
|
-
force = v
|
83
|
-
end
|
84
|
-
elsif ARGV.include?("la") || ARGV.include?("listappliances")
|
85
|
-
opt.banner = "Usage: ssc [options] listappliances"
|
86
|
-
opt.separator("Show a list of your appliances.")
|
87
|
-
elsif ARGV.include?("ca") || ARGV.include?("cloneappliance")
|
88
|
-
opt.banner = "Usage: ssc [options] cloneappliance APPLIANCE"
|
89
|
-
opt.separator("Create a new appliance by cloning a template.")
|
90
|
-
elsif ARGV.include?("da") || ARGV.include?("deleteappliance")
|
91
|
-
opt.banner = "Usage: ssc [options] deleteappliance APPLIANCE"
|
92
|
-
opt.separator("Delete an appliance.")
|
93
|
-
elsif ARGV.include?("lt") || ARGV.include?("listtemplates")
|
94
|
-
opt.banner = "Usage: ssc [options] listtemplates"
|
95
|
-
opt.separator("Get a list of available templates.")
|
96
|
-
elsif ARGV.include?("lrb") || ARGV.include?("listrunningbuilds")
|
97
|
-
opt.banner = "Usage: ssc [options] listrunningbuilds APPLIANCE"
|
98
|
-
opt.separator("List all running builds of an appliance.")
|
99
|
-
elsif ARGV.include?("srb") || ARGV.include?("showrunningbuild")
|
100
|
-
opt.banner = "Usage: ssc [options] showrunningbuild ID"
|
101
|
-
opt.separator("Show the status of a running build.")
|
102
|
-
opt.on( "-f", "--follow","Follow the progress of the build") do |f|
|
103
|
-
follow = f
|
104
|
-
end
|
105
|
-
elsif ARGV.include?("lb") || ARGV.include?("listbuilds")
|
106
|
-
opt.banner = "Usage: ssc [options] listbuilds APPLIANCE"
|
107
|
-
opt.separator("List builds of an appliance.")
|
108
|
-
elsif ARGV.include?("sb") || ARGV.include?("showbuild")
|
109
|
-
opt.banner = "Usage: ssc [options] showbuild ID"
|
110
|
-
opt.separator("Show information on a build.")
|
111
|
-
elsif ARGV.include?("cb") || ARGV.include?("cancelbuild")
|
112
|
-
opt.banner = "Usage: ssc [options] cancelbuild ID"
|
113
|
-
opt.separator("Cancel a running build.")
|
114
|
-
elsif ARGV.include?("db") || ARGV.include?("deletebuild")
|
115
|
-
opt.banner = "Usage: ssc [options] deletebuild ID"
|
116
|
-
opt.separator("Delete a finished build.")
|
117
|
-
elsif ARGV.include?("co") || ARGV.include?("checkout")
|
118
|
-
opt.banner = "Usage: ssc [options] checkout APPLIANCE"
|
119
|
-
opt.separator("Checkout an appliance.")
|
120
|
-
opt.on( "-i", "--download-images","Download images of the appliance") do |i|
|
121
|
-
images = i
|
122
|
-
end
|
123
|
-
elsif ARGV.include?("st") || ARGV.include?("status")
|
124
|
-
opt.banner = "Usage: ssc [options] status"
|
125
|
-
opt.separator("Show the status of the checkout.")
|
126
|
-
elsif ARGV.include?("ci") || ARGV.include?("commit")
|
127
|
-
opt.banner = "Usage: ssc [options] commit"
|
128
|
-
opt.separator("Commit changes to the appliance.")
|
129
|
-
elsif ARGV.include?("add")
|
130
|
-
opt.banner = "Usage: ssc [options] add FILE"
|
131
|
-
opt.separator("Add a file to the checkout.")
|
132
|
-
elsif ARGV.include?("rm") || ARGV.include?("remove")
|
133
|
-
opt.banner = "Usage: ssc [options] remove FILE"
|
134
|
-
opt.separator("Remove a file from the checkout.")
|
135
|
-
else
|
136
|
-
opt.banner = "Usage: ssc [options] COMMAND [command-options]"
|
137
|
-
opt.separator("SUSE Studio command line client.")
|
138
|
-
opt.separator("Type 'ssc COMMAND --help' for help on a specific command.")
|
139
|
-
|
140
|
-
opt.separator("\n")
|
141
|
-
opt.separator("Commands:")
|
142
|
-
opt.separator(" Managing your appliances:")
|
143
|
-
opt.separator(" listappliances,la\t\t Get a list of your appliances")
|
144
|
-
opt.separator(" cloneappliance,ca\t\t Create a new appliance by cloning a template")
|
145
|
-
opt.separator(" deleteappliance,da\t Delete an appliance")
|
146
|
-
opt.separator(" listtemplates,lt\t\t Get a list of available templates")
|
147
|
-
opt.separator("\n")
|
148
|
-
opt.separator(" Managing builds:")
|
149
|
-
opt.separator(" buildappliance,ba\t\t Trigger a build of an appliance")
|
150
|
-
opt.separator(" listrunningbuilds,lrb\t List all running builds of an appliance")
|
151
|
-
opt.separator(" showrunningbuild,srb\t Show the status of a running build")
|
152
|
-
opt.separator(" listbuilds,lb\t\t List builds of an appliance")
|
153
|
-
opt.separator(" showbuild,sb\t\t Show information on a build")
|
154
|
-
opt.separator(" cancelbuild,cb\t\t Cancel a running build")
|
155
|
-
opt.separator(" deletebuild,db\t\t Delete a finished build")
|
156
|
-
opt.separator("\n")
|
157
|
-
opt.separator(" Managing checkouts:")
|
158
|
-
opt.separator(" checkout,co\t\t Checkout an appliance")
|
159
|
-
opt.separator(" status,st\t\t\t Show the status of the checkout")
|
160
|
-
opt.separator(" commit,ci\t\t\t Commit changes to the appliance")
|
161
|
-
opt.separator(" add\t\t\t Add a file to the checkout")
|
162
|
-
opt.separator(" rm\t\t\t Remove a file from the checkout")
|
163
|
-
opt.separator("\n")
|
164
|
-
end
|
165
|
-
|
166
|
-
begin
|
167
|
-
opt.parse!( ARGV )
|
168
|
-
rescue OptionParser::InvalidOption
|
169
|
-
STDERR.puts $!
|
170
|
-
STDERR.puts opt
|
171
|
-
exit 1
|
172
|
-
end
|
173
|
-
|
174
|
-
if ARGV.size == 0
|
175
|
-
STDERR.puts opt
|
176
|
-
exit 1
|
177
|
-
end
|
178
|
-
|
179
|
-
cmd = ARGV[0]
|
180
|
-
|
181
|
-
if cmd == "listappliances" or cmd =="la"
|
182
|
-
ApplianceHandler.list_appliances
|
183
|
-
|
184
|
-
elsif cmd == "cloneappliance" or cmd =="ca"
|
185
|
-
ApplianceHandler.clone_appliance ARGV
|
186
|
-
|
187
|
-
elsif cmd == "deleteappliance" or cmd == "da"
|
188
|
-
ApplianceHandler.delete_appliance ARGV
|
189
|
-
|
190
|
-
elsif cmd == "listtemplates" or cmd =="lt"
|
191
|
-
ApplianceHandler.template_sets
|
192
|
-
|
193
|
-
elsif cmd == "buildappliance" or cmd =="ba"
|
194
|
-
BuildHandler.build_appliance ARGV, force
|
195
|
-
|
196
|
-
elsif cmd == "listrunningbuilds" or cmd =="lrb"
|
197
|
-
BuildHandler.list_running_builds ARGV
|
198
|
-
|
199
|
-
elsif cmd == "showrunningbuild" or cmd =="srb"
|
200
|
-
BuildHandler.show_running_build ARGV, follow
|
201
|
-
|
202
|
-
elsif cmd == "listbuilds" or cmd =="lb"
|
203
|
-
BuildHandler.list_builds ARGV
|
204
|
-
|
205
|
-
elsif cmd == "showbuild" or cmd =="sb"
|
206
|
-
BuildHandler.show_build ARGV
|
207
|
-
|
208
|
-
elsif cmd == "cancelbuild" or cmd =="cb"
|
209
|
-
BuildHandler.cancel_build ARGV
|
210
|
-
|
211
|
-
elsif cmd == "deletebuild" or cmd =="db"
|
212
|
-
BuildHandler.delete_build ARGV
|
213
|
-
|
214
|
-
elsif cmd == "checkout" or cmd =="co"
|
215
|
-
CheckoutHandler.checkout ARGV, images
|
216
|
-
|
217
|
-
elsif cmd == "status" or cmd =="st"
|
218
|
-
CheckoutHandler.status
|
219
|
-
|
220
|
-
elsif cmd == "commit" or cmd =="ci"
|
221
|
-
CheckoutHandler.commit
|
222
|
-
|
223
|
-
elsif cmd == "add"
|
224
|
-
CheckoutHandler.add ARGV
|
225
|
-
|
226
|
-
elsif cmd == "remove" or cmd == "rm"
|
227
|
-
CheckoutHandler.remove ARGV
|
228
|
-
|
229
|
-
else
|
230
|
-
STDERR.puts "Unknown command: #{cmd}\n"
|
231
|
-
STDERR.puts opt
|
232
|
-
exit 1
|
233
|
-
end
|
5
|
+
require 'ssc'
|
6
|
+
SSC::Base.start
|
@@ -0,0 +1,194 @@
|
|
1
|
+
module SSC
|
2
|
+
module DirectoryManager
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
base.send :include, InstanceMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def create_appliance_directory(appliance_dir, username, password, appliance_id)
|
11
|
+
FileUtils.mkdir_p(appliance_dir)
|
12
|
+
FileUtils.mkdir_p(File.join(appliance_dir, 'files'))
|
13
|
+
FileUtils.touch(File.join(appliance_dir, 'repositories'))
|
14
|
+
FileUtils.touch(File.join(appliance_dir, 'software'))
|
15
|
+
FileUtils.touch(File.join(appliance_dir, 'files/.file_list'))
|
16
|
+
File.open(File.join(appliance_dir, '.sscrc'), 'w') do |file|
|
17
|
+
file.write("username: #{username}\n"+
|
18
|
+
"password: #{password}\n"+
|
19
|
+
"appliance_id: #{appliance_id}")
|
20
|
+
end
|
21
|
+
File.join(Dir.pwd, appliance_dir)
|
22
|
+
end
|
23
|
+
|
24
|
+
def manage(local_source)
|
25
|
+
self.class.class_variable_set('@@appliance_directory', Dir.pwd)
|
26
|
+
if appliance_directory_valid?(Dir.pwd)
|
27
|
+
file= File.join(Dir.pwd, local_source)
|
28
|
+
self.class.class_variable_set('@@local_source', file) if File.exist?(file)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def appliance_directory_valid?(dir)
|
35
|
+
config_file= File.join(dir, '.sscrc')
|
36
|
+
File.exist?(config_file) && File.read(config_file).match(/appliance_id:\ *\d+/)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
module InstanceMethods
|
42
|
+
include Thor::Actions
|
43
|
+
|
44
|
+
# Save data to local storage file
|
45
|
+
# @param [String] section The section of the document that is to be saved
|
46
|
+
# @param [Array] list The data in Array format which will be merged with existing data
|
47
|
+
def save(section, list)
|
48
|
+
safe_get_source_file do |source|
|
49
|
+
parsed_file= YAML::load(File.read(source))
|
50
|
+
# YAML::load returns false if file is empty
|
51
|
+
parsed_file= {} unless parsed_file
|
52
|
+
final_list= list
|
53
|
+
if parsed_file[section]
|
54
|
+
current_list= parsed_file[section]
|
55
|
+
final_list= current_list | final_list
|
56
|
+
end
|
57
|
+
parsed_file[section]= final_list
|
58
|
+
File.open(source, 'w') {|f| f.write parsed_file.to_yaml}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Reads data from the local storage file
|
63
|
+
# @param [String] section (optional) This is the top-level section
|
64
|
+
# of the storage file that is to be read. It can be left blank to
|
65
|
+
# return all sections of the file
|
66
|
+
# @return [String] Either the whole file of the specified section
|
67
|
+
def read(section = nil)
|
68
|
+
safe_get_source_file do |source|
|
69
|
+
if section
|
70
|
+
parsed_file= YAML::load(File.read(source))
|
71
|
+
parsed_file[section]
|
72
|
+
else
|
73
|
+
File.read(source)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
|
81
|
+
# Wrapper to check existence of source file and other sanity checks
|
82
|
+
# It takes a block with one argument - the path of the source file
|
83
|
+
def safe_get_source_file
|
84
|
+
source= self.class.class_variable_get('@@local_source')
|
85
|
+
source= File.join(Dir.pwd, source)
|
86
|
+
if File.exist?(source)
|
87
|
+
yield source
|
88
|
+
else
|
89
|
+
raise "Couldn't find the local source file" unless options.remote?
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def find_file_id(file_name)
|
94
|
+
file_list= File.join(self.class.class_variable_get('@@local_source'), '.file_list')
|
95
|
+
parsed_file= YAML::load(File.read(file_list))
|
96
|
+
if parsed_file["list"]
|
97
|
+
files= parsed_file["list"].select{|i| i.keys[0] == file_name}
|
98
|
+
if files.length < 1
|
99
|
+
raise ArgumentError, "file not found"
|
100
|
+
else
|
101
|
+
files[0][file_name]["id"]
|
102
|
+
end
|
103
|
+
else
|
104
|
+
raise ArgumentError, "file not found"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def full_local_file_path(file)
|
109
|
+
full_path= File.join(self.class.class_variable_get('@@local_source'), file)
|
110
|
+
end
|
111
|
+
|
112
|
+
def show_file(file)
|
113
|
+
full_path= full_local_file_path(file)
|
114
|
+
if File.exist?(full_path)
|
115
|
+
File.read(full_path)
|
116
|
+
else
|
117
|
+
raise ArgumentError, "file not found"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def find_diff(remote, local)
|
122
|
+
`diff #{remote} #{local}`
|
123
|
+
end
|
124
|
+
|
125
|
+
def initiate_file(file_dir, file_name, id)
|
126
|
+
source_file= File.join(file_dir, file_name)
|
127
|
+
destination_file= full_local_file_path(file_name)
|
128
|
+
file_list= File.join(self.class.class_variable_get('@@local_source'), '.file_list')
|
129
|
+
if File.exist?(source_file)
|
130
|
+
FileUtils.cp(source_file, destination_file)
|
131
|
+
parsed_file= YAML::load(File.read(file_list)) || {}
|
132
|
+
File.open(file_list, 'w') do |f|
|
133
|
+
if id # if the file has been uploaded
|
134
|
+
parsed_file['list']= [] unless parsed_file['list']
|
135
|
+
parsed_file['list'] |= [{file_name => {
|
136
|
+
"id" => id,
|
137
|
+
"path" => file_dir}}]
|
138
|
+
else
|
139
|
+
parsed_file['add']= [] unless parsed_file['add']
|
140
|
+
|
141
|
+
parsed_file['add'] |= [{file_name => {
|
142
|
+
"path" => file_dir}}]
|
143
|
+
end
|
144
|
+
f.write(parsed_file.to_yaml)
|
145
|
+
end
|
146
|
+
destination_file
|
147
|
+
else
|
148
|
+
raise ArgumentError, "File does not exist"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def list_local_files
|
153
|
+
source= self.class.class_variable_get('@@local_source')
|
154
|
+
parsed_file= YAML::load File.read(File.join(source, '.file_list'))
|
155
|
+
parsed_file = {} unless parsed_file
|
156
|
+
parsed_file["list"]
|
157
|
+
end
|
158
|
+
|
159
|
+
def parse_file_list
|
160
|
+
source= self.class.class_variable_get('@@local_source')
|
161
|
+
file_list= File.read(File.join(source, '.file_list'))
|
162
|
+
YAML::load(file_list)
|
163
|
+
end
|
164
|
+
|
165
|
+
def write_to_file(file, data)
|
166
|
+
written= []
|
167
|
+
existing_lines= file.readlines.collect{|i| i.strip}
|
168
|
+
file.write("\n") if existing_lines.last != '' and existing_lines != []
|
169
|
+
data.each do |line|
|
170
|
+
unless existing_lines.include?( line )
|
171
|
+
file.write(line+"\n")
|
172
|
+
written << line
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def file_list_empty?
|
178
|
+
safe_get_source_file do |source|
|
179
|
+
!YAML::load File.read(File.join(source, '.file_list'))
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Checks if the local source file has a list
|
184
|
+
# @return [Boolean] true if there is no list
|
185
|
+
def no_local_list?
|
186
|
+
safe_get_source_file do |source|
|
187
|
+
list= YAML::load(File.read source)
|
188
|
+
!list || list == nil || list == {} || list == []
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
data/lib/handlers/all.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'studio_api'
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
4
|
+
|
5
|
+
module SSC::Handler; end
|
6
|
+
|
7
|
+
require 'helper'
|
8
|
+
|
9
|
+
module SSC
|
10
|
+
module Handler
|
11
|
+
class Base < Thor
|
12
|
+
|
13
|
+
include Helper
|
14
|
+
include DirectoryManager
|
15
|
+
|
16
|
+
API_URL= 'https://susestudio.com/api/v1/user'
|
17
|
+
|
18
|
+
def initialize(*args)
|
19
|
+
super
|
20
|
+
|
21
|
+
optional_connection_options= filter_options(options, [:timeout, :proxy])
|
22
|
+
connect(options.username, options.password, optional_connection_options)
|
23
|
+
@not_local= true if options.remote?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
require 'appliance'
|
30
|
+
require 'repository'
|
31
|
+
require 'package'
|
32
|
+
require 'template'
|
33
|
+
require 'file'
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module SSC
|
2
|
+
module Handler
|
3
|
+
class Appliance < Base
|
4
|
+
|
5
|
+
desc "appliance create APPLIANCE_NAME", "Create an appliance"
|
6
|
+
require_authorization
|
7
|
+
method_option :source_id, :type => :numeric, :required => true
|
8
|
+
method_option :arch, :type => :string
|
9
|
+
def create(appliance_name)
|
10
|
+
appliance_dir= File.join('.', appliance_name)
|
11
|
+
params= {:name => appliance_name}
|
12
|
+
params.merge!(:arch => options.arch) if options.arch
|
13
|
+
appliance= StudioApi::Appliance.clone(options.source_id, params)
|
14
|
+
appliance_dir= self.class.create_appliance_directory(appliance_dir, options.username, options.password, appliance.id)
|
15
|
+
say_array ["Created: ", appliance_dir,
|
16
|
+
File.join(appliance_dir, 'files'),
|
17
|
+
File.join(appliance_dir, 'repositories'),
|
18
|
+
File.join(appliance_dir, 'software') ]
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "appliance list", "list all appliances"
|
22
|
+
require_authorization
|
23
|
+
def list
|
24
|
+
appliances= StudioApi::Appliance.find(:all)
|
25
|
+
print_table appliances.collect{|i| [i.id, i.name]}
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "appliance info", "show details of a specific appliance"
|
29
|
+
require_appliance_id
|
30
|
+
def info
|
31
|
+
appliance= StudioApi::Appliance.find(options.appliance_id)
|
32
|
+
say_array ["#{appliance.id}: #{appliance.name}",
|
33
|
+
"Parent: ( #{appliance.parent.id} ) #{appliance.parent.name}",
|
34
|
+
"Download Url: #{download_url(appliance)}"]
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "appliance destroy", "destroy the current appliance (within appliance directory only)"
|
38
|
+
require_appliance_id
|
39
|
+
def destroy
|
40
|
+
if appliance.destroy.code_type == Net::HTTPOK
|
41
|
+
say 'Appliance Successfully Destroyed', :red
|
42
|
+
else
|
43
|
+
say_array ['There was a problem with destroying the appliance.',
|
44
|
+
'Make sure that you\'re in the appliance directory OR',
|
45
|
+
'Have provided the --appliance_id option']
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "appliance status", "gives status of the appliance"
|
50
|
+
require_appliance_id
|
51
|
+
def status
|
52
|
+
require_appliance do |appliance|
|
53
|
+
response= appliance.status
|
54
|
+
case response.state
|
55
|
+
when 'error'
|
56
|
+
say "Error: #{response.issues.issue.text}"
|
57
|
+
when 'ok'
|
58
|
+
say "Appliance Ok"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def download_url(appliance)
|
66
|
+
if appliance.builds.empty?
|
67
|
+
"No Build Yet"
|
68
|
+
else
|
69
|
+
appliance.builds.last.download_url
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|