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