gosen 0.1.7 → 0.2.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/README.md +21 -2
- data/VERSION +1 -1
- data/bin/trebuchet +123 -0
- data/lib/gosen.rb +1 -1
- data/lib/gosen/deployment.rb +4 -0
- data/test/gosen/test_deployment.rb +17 -0
- data/test/gosen/test_deployment_run.rb +5 -0
- metadata +8 -7
data/README.md
CHANGED
@@ -5,13 +5,14 @@ It relies on the [Restfully library](http://github.com/crohr/restfully) for inte
|
|
5
5
|
|
6
6
|
## Features
|
7
7
|
|
8
|
-
Currently,
|
8
|
+
Currently, this library allows to submit deployments that retry automatically when too many nodes fail.
|
9
|
+
A clone of the [Katapult](http://www.loria.fr/~lnussbau/katapult.html) script, called Trebuchet, is also included and can be used on the command line.
|
9
10
|
|
10
11
|
## Installation
|
11
12
|
|
12
13
|
$ gem install gosen
|
13
14
|
|
14
|
-
## Usage
|
15
|
+
## Library Usage
|
15
16
|
|
16
17
|
The following example deploys the latest version of the Lenny-x64-big environment on the paramount-1 and paramount-2 nodes.
|
17
18
|
If both nodes are not successfully deployed, Gosen retries again (in this case, at most 5 deployment are submitted).
|
@@ -43,6 +44,24 @@ The logger allows to print information about the deployment, in a style similar
|
|
43
44
|
I, [2010-04-21T11:37:11.817323 #21673] INFO -- : Nodes deployed: paramount-1.rennes.grid5000.fr paramount-2.rennes.grid5000.fr
|
44
45
|
I, [2010-04-21T11:37:11.817440 #21673] INFO -- : Had to run 1 kadeploy runs, deployed 2 nodes
|
45
46
|
|
47
|
+
The Gosen specific options accepted by Gosen::Deployment.new() are:
|
48
|
+
|
49
|
+
* :logger, a Ruby Logger object,
|
50
|
+
* :min_deployed_nodes, the minimal number of successfully deployed nodes (defaults to 1),
|
51
|
+
* :max_deploy_runs, the maximal number of deploy runs to perform (defaults to 1),
|
52
|
+
* :continue_if_error, a boolean allowing to retry if a deployment returned with an error (defaults to false),
|
53
|
+
* :ssh_public_key, an SSH public key to be installed in the deployed environment.
|
54
|
+
|
55
|
+
It is also possible to pass options accepted by the [Deployments API](https://api.grid5000.fr/sid/deployments/help/index.html), such as version, block_device, partition_number, etc.
|
56
|
+
|
57
|
+
## Script usage
|
58
|
+
|
59
|
+
Trebuchet was designed to be used like Katapult:
|
60
|
+
|
61
|
+
$ trebuchet -e lenny-x64-base --env-version 3 --min-deployed-nodes 4 --max-deploy-runs 2 -c
|
62
|
+
|
63
|
+
Run `trebuchet --help` to get usage information.
|
64
|
+
|
46
65
|
## Note on Patches/Pull Requests
|
47
66
|
|
48
67
|
* Fork the project.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/bin/trebuchet
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'gosen'
|
4
|
+
require 'logger'
|
5
|
+
require 'optparse'
|
6
|
+
require 'ostruct'
|
7
|
+
|
8
|
+
trap('INT') {
|
9
|
+
deployment_resource = @deployment.deployment_resource rescue nil
|
10
|
+
if deployment_resource && deployment_resource['status'] == 'processing'
|
11
|
+
deployment_resource.delete
|
12
|
+
puts "Cancelled deployment #{deployment_resource['uid']}"
|
13
|
+
end
|
14
|
+
exit
|
15
|
+
}
|
16
|
+
|
17
|
+
config = OpenStruct.new
|
18
|
+
deployment_options = {}
|
19
|
+
config.nodes = []
|
20
|
+
|
21
|
+
logger = Logger.new(STDOUT)
|
22
|
+
logger.level = Logger::INFO
|
23
|
+
deployment_options[:logger] = logger
|
24
|
+
|
25
|
+
options = OptionParser.new do |opts|
|
26
|
+
opts.banner = 'Usage: trebuchet [options]'
|
27
|
+
opts.separator ''
|
28
|
+
opts.separator 'Specific options:'
|
29
|
+
opts.on('-b', '--block-device BLOCKDEVICE', 'Specify the block device to use') do |p|
|
30
|
+
deployment_options[:block_device] = p
|
31
|
+
end
|
32
|
+
opts.on('-c', '--copy-ssh-key', 'Copy SSH keys to nodes') do
|
33
|
+
config.copy_ssh_key = true
|
34
|
+
end
|
35
|
+
opts.on('--disable_bootloader_install', 'Disable the automatic installation of a bootloader for a Linux based environnment') do
|
36
|
+
deployment_options[:disable_bootloader_install] = true
|
37
|
+
end
|
38
|
+
opts.on('--disable_disk_partitioning', 'Disable the disk partitioning') do
|
39
|
+
deployment_options[:disable_disk_partitioning] = true
|
40
|
+
end
|
41
|
+
opts.on('-e', '--deploy-env ENV', 'Environment to deploy') do |env|
|
42
|
+
config.deploy_env = env
|
43
|
+
end
|
44
|
+
opts.on('--env-version NUMBER', 'Number of version of the environment to deploy') do |v|
|
45
|
+
deployment_options[:version] = v
|
46
|
+
end
|
47
|
+
opts.on('-f', '--file MACHINELIST', 'Files containing the list of nodes') do |f|
|
48
|
+
config.nodefile = f
|
49
|
+
end
|
50
|
+
opts.on('-i', '--ssh-key-file FILE', "File containing keys to copy (defaults to #{config.ssh_keyfile})") do |f|
|
51
|
+
config.ssh_keyfile = f
|
52
|
+
end
|
53
|
+
opts.on('--ignore-nodes-deploying', 'Allow to deploy even on the nodes tagged as "currently deploying" (use this only if you know what you do)') do
|
54
|
+
deployment_options[:ignore_nodes_deploying] = true
|
55
|
+
end
|
56
|
+
opts.on('-l', '--deploy-user USER', 'User owning the deployment environment') do |u|
|
57
|
+
config.deploy_user = u
|
58
|
+
end
|
59
|
+
opts.on('-m', '--machine MACHINE', 'Node to run on') do |n|
|
60
|
+
config.nodes << n
|
61
|
+
end
|
62
|
+
opts.on('--max-deploy-runs NB', 'Maximum number of deployment runs before we admit we cannot get enough nodes deployed') do |n|
|
63
|
+
deployment_options[:max_deploy_runs] = n.to_i
|
64
|
+
end
|
65
|
+
opts.on('--min-deployed-nodes NB', 'Minimum number of nodes that must be correctly deployed before continuing') do |n|
|
66
|
+
deployment_options[:min_deployed_nodes] = n.to_i
|
67
|
+
end
|
68
|
+
opts.on('-p', '--partition-number NUMBER', 'Specify the partition number to use') do |p|
|
69
|
+
deployment_options[:partition_number] = p
|
70
|
+
end
|
71
|
+
opts.on('-r', '--reformat-tmp FSTYPE', 'Reformat the /tmp partition') do |fs|
|
72
|
+
fstypes = [ "ext2", "ext3", "ext4" ]
|
73
|
+
abort "FSTYPE must be one of #{fstypes.join(', ')}" unless fstypes.include?(fs)
|
74
|
+
deployment_options[:reformat_tmp] = fs
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
begin
|
79
|
+
options.parse!(ARGV)
|
80
|
+
rescue OptionParser::ParseError => e
|
81
|
+
$stderr.puts e
|
82
|
+
exit 1
|
83
|
+
end
|
84
|
+
|
85
|
+
if config.nodes.empty?
|
86
|
+
config.nodefile ||= ENV['OAR_NODEFILE']
|
87
|
+
config.nodes = File.open(File.expand_path(config.nodefile)).readlines.collect { |l| l.chomp }.sort.uniq
|
88
|
+
end
|
89
|
+
|
90
|
+
if config.nodes.empty?
|
91
|
+
abort "No nodes specified, and no OAR_NODEFILE variable, exiting.\nRun trebuchet --help if you need help."
|
92
|
+
end
|
93
|
+
|
94
|
+
if config.deploy_env.nil?
|
95
|
+
abort "Error: an environment to deploy is required.\nRun trebuchet --help if you need help."
|
96
|
+
end
|
97
|
+
|
98
|
+
if config.deploy_user
|
99
|
+
config.deploy_env += "@#{config.deploy_user}"
|
100
|
+
end
|
101
|
+
|
102
|
+
if config.ssh_keyfile
|
103
|
+
abort 'Error: the --ssh-keyfile option only makes sense with --copy-ssh-key.\nRun trebuchet --help if you need help.' if config.copy_ssh_key.nil?
|
104
|
+
end
|
105
|
+
|
106
|
+
if config.copy_ssh_key
|
107
|
+
config.ssh_keyfile ||= '~/.ssh/authorized_keys'
|
108
|
+
deployment_options[:ssh_public_key] = File.open(File.expand_path(config.ssh_keyfile)).read
|
109
|
+
end
|
110
|
+
|
111
|
+
if ENV['RESTFULLY_CONFIG'].nil?
|
112
|
+
abort "No RESTFULLY_CONFIG environment variable, exiting.\nRun trebuchet --help if you need help."
|
113
|
+
end
|
114
|
+
|
115
|
+
Restfully::Session.new({ :configuration_file => ENV['RESTFULLY_CONFIG'] }) do |grid, session|
|
116
|
+
indexed_nodes = Gosen::index_nodes_by_site(config.nodes)
|
117
|
+
if indexed_nodes.keys.length > 1
|
118
|
+
raise StandardError.new("The node list needs to be specific to a single site\nRun trebuchet --help if you need help.")
|
119
|
+
end
|
120
|
+
site = grid.sites[indexed_nodes.keys.first.to_sym].load
|
121
|
+
@deployment = Gosen::Deployment.new(site, config.deploy_env, config.nodes, deployment_options)
|
122
|
+
@deployment.join
|
123
|
+
end
|
data/lib/gosen.rb
CHANGED
data/lib/gosen/deployment.rb
CHANGED
@@ -80,6 +80,10 @@ module Gosen
|
|
80
80
|
raise Gosen::Error.new("Not enough nodes deployed after #{@max_deploy_runs} deployment(s): needed #{@min_deployed_nodes} nodes, got only #{@good_nodes.length}")
|
81
81
|
end
|
82
82
|
|
83
|
+
def deployment_resource
|
84
|
+
@deployment_run.deployment_resource rescue nil
|
85
|
+
end
|
86
|
+
|
83
87
|
def no_more_required?
|
84
88
|
@good_nodes.length >= @min_deployed_nodes
|
85
89
|
end
|
@@ -126,6 +126,23 @@ class TestDeployment < Test::Unit::TestCase
|
|
126
126
|
@deployment_resource.stubs(:[]).with('status').returns('processing', 'processing', 'terminated')
|
127
127
|
end
|
128
128
|
|
129
|
+
should 'have a reader on the deployment resource' do
|
130
|
+
@deployment_result = {
|
131
|
+
'paramount-1.rennes.grid5000.fr' => { 'state' => 'OK' },
|
132
|
+
'paramount-2.rennes.grid5000.fr' => { 'state' => 'OK' }
|
133
|
+
}
|
134
|
+
@deployment_resource.expects(:[]).with('result').returns(@deployment_result)
|
135
|
+
@site_deployments.expects(:submit).with({ :environment => @environment, :nodes => @nodes }).returns(@deployment_resource)
|
136
|
+
@min_deployed_nodes = 2
|
137
|
+
@logger.expects(:info).with("Kadeploy run 1 with #{@nodes.length} nodes (0 already deployed, need #{@min_deployed_nodes} more)")
|
138
|
+
@logger.expects(:info).with("Nodes deployed: paramount-1.rennes.grid5000.fr paramount-2.rennes.grid5000.fr")
|
139
|
+
@logger.expects(:info).with("Had to run 1 kadeploy runs, deployed #{@deployment_result.length} nodes")
|
140
|
+
|
141
|
+
@deployment = Gosen::Deployment.new(@site, @environment, @nodes, { :logger => @logger, :min_deployed_nodes => @min_deployed_nodes })
|
142
|
+
@deployment.join
|
143
|
+
assert_equal(@deployment_resource, @deployment.deployment_resource)
|
144
|
+
end
|
145
|
+
|
129
146
|
should 'submit a deployment run and wait for the result' do
|
130
147
|
@deployment_result = {
|
131
148
|
'paramount-1.rennes.grid5000.fr' => { 'state' => 'OK' },
|
@@ -81,6 +81,11 @@ class TestDeploymentRun < Test::Unit::TestCase
|
|
81
81
|
@deployment = Gosen::DeploymentRun.new(@site, @environment, @nodes, { :ssh_public_key => @ssh_public_key })
|
82
82
|
end
|
83
83
|
|
84
|
+
should 'have a reader on the deployment resource' do
|
85
|
+
@deployment.join
|
86
|
+
assert_equal(@resource, @deployment.deployment_resource)
|
87
|
+
end
|
88
|
+
|
84
89
|
should 'wait for deployment completion and give access to the results' do
|
85
90
|
assert_nothing_raised(Exception) {
|
86
91
|
@deployment.join
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Pierre Riteau
|
@@ -14,8 +14,8 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-10-
|
18
|
-
default_executable:
|
17
|
+
date: 2010-10-28 00:00:00 +02:00
|
18
|
+
default_executable: trebuchet
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: restfully
|
@@ -64,8 +64,8 @@ dependencies:
|
|
64
64
|
version_requirements: *id003
|
65
65
|
description: Gosen is a Ruby library providing high-level operations using the Grid'5000 RESTful API, such as Kadeploy deployments.
|
66
66
|
email: priteau@gmail.com
|
67
|
-
executables:
|
68
|
-
|
67
|
+
executables:
|
68
|
+
- trebuchet
|
69
69
|
extensions: []
|
70
70
|
|
71
71
|
extra_rdoc_files:
|
@@ -78,6 +78,7 @@ files:
|
|
78
78
|
- README.md
|
79
79
|
- Rakefile
|
80
80
|
- VERSION
|
81
|
+
- bin/trebuchet
|
81
82
|
- lib/gosen.rb
|
82
83
|
- lib/gosen/deployment.rb
|
83
84
|
- lib/gosen/deployment_run.rb
|