forj 0.0.30 → 0.0.31
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +10 -1
- data/bin/forj +4 -2
- data/lib/boot.rb +48 -20
- data/lib/build_tmpl/build-env.py +293 -0
- data/lib/catalog.yaml +11 -2
- data/lib/compute.rb +38 -0
- data/lib/down.rb +16 -15
- data/lib/network.rb +32 -1
- data/lib/repositories.rb +18 -0
- data/lib/security.rb +28 -2
- data/lib/setup.rb +52 -1
- data/lib/ssh.rb +5 -0
- data/lib/ssh.sh +3 -2
- data/spec/spec_helper.rb +3 -3
- metadata +15 -13
data/README.md
CHANGED
@@ -4,6 +4,15 @@ Forj cli
|
|
4
4
|
|
5
5
|
Installation
|
6
6
|
=====================
|
7
|
+
For ruby 2.0
|
8
|
+
Fedora/CentOS/Redhat/rpm like system :
|
9
|
+
sudo yum install ruby-dev build-essential libopenssl-ruby libssl-dev zlib1g-dev -y
|
10
|
+
sudo gem install forj
|
11
|
+
|
12
|
+
Ubuntu/Debian/debian like system (not tested)
|
13
|
+
sudo apt-get install ruby-dev build-essential libopenssl-ruby1.9.1 libssl-dev zlib1g-dev -y
|
14
|
+
sudo gem install forj
|
15
|
+
|
7
16
|
For ruby 1.9
|
8
17
|
|
9
18
|
sudo apt-get install ruby-dev build-essential libopenssl-ruby1.9.1 libssl-dev zlib1g-dev -y
|
@@ -59,4 +68,4 @@ your development today!
|
|
59
68
|
|
60
69
|
License
|
61
70
|
=====================
|
62
|
-
Forj Cli is licensed under the Apache License, Version 2.0. See LICENSE for full license text.
|
71
|
+
Forj Cli is licensed under the Apache License, Version 2.0. See LICENSE for full license text.
|
data/bin/forj
CHANGED
@@ -78,12 +78,13 @@ class Forj < Thor
|
|
78
78
|
method_option :build_config_dir, :aliases => '-d', :desc => 'Defines the build configuration directory to load the build configuration file. You can set FORJ_BLD_CONF_DIR. By default, it will look in your current directory.'
|
79
79
|
method_option :build_config, :aliases => '-c', :desc => 'The build config file to load <confdir>/<BoxName>.<Config>.env. By default, uses "master" as Config.'
|
80
80
|
method_option :branch, :aliases => '-b', :desc => 'The build will extract from git branch name. It sets the configuration build <config> to the branch name <branch>.'
|
81
|
-
method_option :test_box, :aliases => '-t', :desc => 'Create test-box meta from the repository path provided.'
|
81
|
+
method_option :test_box, :aliases => '-t', :desc => 'Create test.rb-box meta from the repository path provided.'
|
82
82
|
method_option :git_repo, :aliases => '-r', :desc => 'The box built will use a different git repository sent out to <user_data>. This repository needs to be read only. No keys are sent.'
|
83
83
|
method_option :boothook, :aliases => '-h', :desc => 'By default, boothook file used is build/bin/build-tools/boothook.sh. Use this option to set another one.'
|
84
84
|
method_option :box_name, :aliases => '-x', :desc => 'Defines the name of the box or box image to build.'
|
85
85
|
method_option :key_name, :aliases => '-k', :desc => 'Import a key pair.'
|
86
86
|
method_option :key_path, :aliases => '-p', :desc => 'Public key data'
|
87
|
+
method_option :catalog, :aliases => '-y', :desc => 'A path for the yaml file data about the blueprint'
|
87
88
|
|
88
89
|
def boot(blueprint, on, cloud_provider, as, name, test = false)
|
89
90
|
Boot.boot(blueprint, cloud_provider, name,
|
@@ -91,7 +92,8 @@ class Forj < Thor
|
|
91
92
|
options[:build_config], options[:branch],
|
92
93
|
options[:git_repo], options[:boothook],
|
93
94
|
options[:box_name], options[:key_name],
|
94
|
-
options[:key_path],
|
95
|
+
options[:key_path],options[:region],
|
96
|
+
options[:catalog], test)
|
95
97
|
end
|
96
98
|
|
97
99
|
desc 'down', 'delete the Maestro box and all systems installed by the blueprint'
|
data/lib/boot.rb
CHANGED
@@ -38,7 +38,7 @@ module Boot
|
|
38
38
|
def boot(blueprint, cloud_provider, name,
|
39
39
|
build, build_config_dir, build_config,
|
40
40
|
branch, git_repo, boothook, box_name,
|
41
|
-
key_name, key_path,
|
41
|
+
key_name, key_path, region, catalog,
|
42
42
|
test = false)
|
43
43
|
begin
|
44
44
|
initial_msg = 'booting %s on %s' % [blueprint , cloud_provider]
|
@@ -48,33 +48,58 @@ module Boot
|
|
48
48
|
|
49
49
|
forj_dir = File.expand_path(File.dirname(__FILE__))
|
50
50
|
Dir.chdir(forj_dir)
|
51
|
-
|
51
|
+
|
52
|
+
if catalog
|
53
|
+
definitions = YamlParse.get_values(catalog)
|
54
|
+
else
|
55
|
+
definitions = YamlParse.get_values('catalog.yaml')
|
56
|
+
end
|
52
57
|
|
53
58
|
maestro_url = definitions['default']['maestro']
|
54
59
|
|
55
60
|
Repositories.clone_repo(maestro_url)
|
61
|
+
infra_exists = nil
|
62
|
+
|
63
|
+
begin
|
64
|
+
if File.directory?(definitions[blueprint]['infra'])
|
65
|
+
infra_exists = true
|
66
|
+
else
|
67
|
+
Repositories.create_infra
|
68
|
+
end
|
69
|
+
rescue => e
|
70
|
+
infra_exists = false
|
71
|
+
puts e.message
|
72
|
+
end
|
73
|
+
|
56
74
|
|
57
|
-
network = Network.
|
58
|
-
|
59
|
-
|
60
|
-
|
75
|
+
network = Network.get_or_create_network(definitions[blueprint]['network'])
|
76
|
+
begin
|
77
|
+
subnet = Network.get_or_create_subnet(network.id, name)
|
78
|
+
router = Network.get_router(definitions[blueprint]['router'])
|
79
|
+
Network.create_router_interface(subnet.id, router)
|
80
|
+
rescue => e
|
81
|
+
puts e.message
|
82
|
+
end
|
61
83
|
|
62
|
-
security_group = SecurityGroup.
|
84
|
+
security_group = SecurityGroup.get_or_create_security_group(definitions[blueprint]['security_group'])
|
63
85
|
|
64
|
-
key_name = '
|
65
|
-
key_path = '
|
86
|
+
key_name = definitions[blueprint]['keypair_name'] unless key_name
|
87
|
+
key_path = definitions[blueprint]['keypair_path'] unless key_path
|
66
88
|
SecurityGroup.upload_existing_key(key_name, key_path)
|
67
89
|
|
68
|
-
ports = definitions[
|
90
|
+
ports = definitions[blueprint]['ports']
|
69
91
|
|
70
92
|
ports.each do|port|
|
71
|
-
Network.
|
93
|
+
Network.get_or_create_rule(security_group.id, 'tcp', port, port)
|
72
94
|
end
|
73
95
|
|
74
96
|
ENV['FORJ_HPC_NETID'] = network.id
|
75
97
|
ENV['FORJ_SECURITY_GROUP'] = security_group.name
|
76
98
|
ENV['FORJ_KEYPAIR'] = key_name
|
77
|
-
ENV['
|
99
|
+
ENV['FORJ_HPC_KEYPUB'] = key_path
|
100
|
+
if region
|
101
|
+
ENV['FORJ_REGION'] = region
|
102
|
+
end
|
78
103
|
|
79
104
|
# run build.sh to boot maestro
|
80
105
|
current_dir = Dir.pwd
|
@@ -84,20 +109,23 @@ module Boot
|
|
84
109
|
|
85
110
|
build = 'bin/build.sh' unless build
|
86
111
|
|
87
|
-
|
88
|
-
|
89
|
-
|
112
|
+
if infra_exists
|
113
|
+
build_config_dir = definitions[blueprint]['infra'] unless build_config_dir
|
114
|
+
else
|
115
|
+
build_config_dir = definitions[blueprint]['build_config_dir'] unless build_config_dir
|
116
|
+
end
|
90
117
|
|
91
|
-
|
118
|
+
build_config = definitions[blueprint]['build_config'] unless build_config
|
92
119
|
|
93
|
-
|
120
|
+
branch = definitions[blueprint]['branch'] unless branch
|
94
121
|
|
95
|
-
box_name = '
|
122
|
+
box_name = definitions[blueprint]['box_name'] unless box_name
|
96
123
|
|
97
|
-
|
124
|
+
meta = '--meta blueprint=%s --meta HPCLOUD_PRIV=~/.cache/forj/master.forj-13.5.g64' % [blueprint]
|
98
125
|
|
99
|
-
command = '%s --build_ID %s --box-name %s --build-conf-dir %s --build-config %s --gitBranch %s --
|
126
|
+
command = '%s --build_ID %s --box-name %s --build-conf-dir %s --build-config %s --gitBranch %s --debug-box %s' % [build, name, box_name, build_config_dir, build_config, branch, meta]
|
100
127
|
|
128
|
+
Logging.info('using build.sh for %s' % [name])
|
101
129
|
Kernel.system(command)
|
102
130
|
Dir.chdir(current_dir)
|
103
131
|
|
@@ -0,0 +1,293 @@
|
|
1
|
+
#!/bin/env python
|
2
|
+
#
|
3
|
+
# (c) Copyright 2014 Hewlett-Packard Development Company, L.P.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
# This script is used to create a env file used by build.sh.
|
18
|
+
# It is based on Maestro/templates/infra/maestro.box.GITBRANCH.env.tmpl
|
19
|
+
|
20
|
+
# This script detects few tags, and ask to enter the appropriate vallue,
|
21
|
+
# then save the result to a directory given by the user.
|
22
|
+
|
23
|
+
# As this script is very early in the boot process, we are running on end user worstation.
|
24
|
+
# We will avoid adding non default module like:
|
25
|
+
# - ptemplates, django for template interpreter. We use our own simple code to make it work with basics rules.
|
26
|
+
# It provides variable detection, with comments and default variables.
|
27
|
+
|
28
|
+
import sys
|
29
|
+
import getopt
|
30
|
+
#import urllib2
|
31
|
+
#from urlparse import urlparse,ParseResult
|
32
|
+
import re
|
33
|
+
import logging
|
34
|
+
import logging.handlers
|
35
|
+
import yaml
|
36
|
+
import os
|
37
|
+
#import subprocess
|
38
|
+
#import distutils.spawn
|
39
|
+
#import string
|
40
|
+
#from datetime import date,datetime
|
41
|
+
#import time
|
42
|
+
#import tempfile
|
43
|
+
import readline
|
44
|
+
|
45
|
+
# Defining defaults
|
46
|
+
|
47
|
+
GITBRANCH='master'
|
48
|
+
MAESTRO_RPATH_TMPL="templates/infra/"
|
49
|
+
MAESTRO_TMPL_FILE="maestro.box.GITBRANCH.env.tmpl"
|
50
|
+
MAESTRO_TMPL_PROVIDER='hpcloud'
|
51
|
+
|
52
|
+
#
|
53
|
+
#
|
54
|
+
|
55
|
+
##############################
|
56
|
+
def help(sMsg):
|
57
|
+
print 'build-env.py [-h|--help] --maestro-repo MaestroPath --env-path BuildEnvPath [--gitbranch BranchName] [-d|--debug] [-v|--verbose]'
|
58
|
+
|
59
|
+
# TODO Support for re-build it.
|
60
|
+
|
61
|
+
##############################
|
62
|
+
# TODO: split this function in 3, for code readibility
|
63
|
+
|
64
|
+
def BuildEnv(EnvPath, MaestroFile, MaestroPath, GitBranch):
|
65
|
+
"""Build env file if doesn't exist."""
|
66
|
+
|
67
|
+
global MAESTRO_TMPL_FILE
|
68
|
+
oLogging=logging.getLogger('build-env')
|
69
|
+
|
70
|
+
# -----------------------------------------------
|
71
|
+
# Check step ------------------------------------
|
72
|
+
|
73
|
+
if not os.path.exists(EnvPath):
|
74
|
+
oLogging.error("'%s' do not exist. This directory is required. (--env-path)", EnvPath)
|
75
|
+
sys.exit(1)
|
76
|
+
|
77
|
+
if not os.path.isdir(EnvPath):
|
78
|
+
oLogging.error("'%s' is not a valid directory (--env_path)", EnvPath)
|
79
|
+
sys.exit(1)
|
80
|
+
|
81
|
+
if not os.path.isdir(MaestroPath):
|
82
|
+
oLogging.error("'%s' is not a valid directory. Should refer to Maestro repository root dir.(--maestro-path)", MaestroPath)
|
83
|
+
sys.exit(1)
|
84
|
+
|
85
|
+
|
86
|
+
sInputFile=os.path.join(MaestroPath, MaestroFile)
|
87
|
+
if not os.path.exists(sInputFile):
|
88
|
+
oLogging.error("Unable to find template '%s' from '%s'. Are you sure about Maestro Repository root path given? Check and retry.", MaestroPath, MaestroFile)
|
89
|
+
sys.exit(1)
|
90
|
+
|
91
|
+
# Replace GITBRANCH Name by real git branch value.
|
92
|
+
sOutputFile=os.path.join(EnvPath,re.sub('GITBRANCH',GITBRANCH,MAESTRO_TMPL_FILE))
|
93
|
+
sOutputFile=re.sub('.tmpl$','',sOutputFile)
|
94
|
+
|
95
|
+
if os.path.exists(sOutputFile):
|
96
|
+
# TODO Support for re-build it.
|
97
|
+
oLogging.info("'%s' exists. If you want to rewrite it, remove the file before.", sOutputFile)
|
98
|
+
sys.exit(0)
|
99
|
+
|
100
|
+
# Load template variables and build list of variable to query
|
101
|
+
oLogging.debug("Opening template '%s'",sInputFile)
|
102
|
+
try:
|
103
|
+
fTmpl=open(sInputFile)
|
104
|
+
except IOError as e:
|
105
|
+
oLogging.error("Unable to open '%s'. Errno: %s (%s)", sInputFile, e.errno, e.strerror)
|
106
|
+
sys.exit(2)
|
107
|
+
|
108
|
+
# -----------------------------------------------
|
109
|
+
# Reading template ------------------------------
|
110
|
+
|
111
|
+
print("Reading template '{0}'".format(sInputFile))
|
112
|
+
oComments={}
|
113
|
+
oVars={}
|
114
|
+
|
115
|
+
# Search for Variable. If the variable name ends with '!', this value could not be null.
|
116
|
+
reVar=re.compile('[^$]{([A-Z_-]+)(!)?(:.*?)?}')
|
117
|
+
# 1: Variable Name
|
118
|
+
# 2: Required if = '!'
|
119
|
+
# 3: Default
|
120
|
+
|
121
|
+
|
122
|
+
# Search for comment
|
123
|
+
reComment=re.compile('^ *# +([A-Z_-]+): (.*)$')
|
124
|
+
|
125
|
+
for line in fTmpl:
|
126
|
+
|
127
|
+
# Detecting comment
|
128
|
+
oComment=reComment.search(line)
|
129
|
+
if oComment <> None:
|
130
|
+
oLogging.debug('Found "%s" = "%s"', oComment.group(1), oComment.group(2))
|
131
|
+
if oVars.has_key(oComment.group(1)):
|
132
|
+
oComments[oComment.group(1)]=oVars[oVar.group(1)]['comments']+'\n# '+oComment.group(2)
|
133
|
+
elif oComments.has_key(oComment.group(1)):
|
134
|
+
oComments[oComment.group(1)]=oComments[oComment.group(1)]+'\n# '+ oComment.group(2)
|
135
|
+
else:
|
136
|
+
oComments[oComment.group(1)]='# '+oComment.group(2)
|
137
|
+
|
138
|
+
oVar=reVar.search(line)
|
139
|
+
if oVar <> None:
|
140
|
+
oLogging.debug('Found var "%s" from "%s"',oVar.group(1),line)
|
141
|
+
|
142
|
+
sComment=''
|
143
|
+
if oComments.has_key(oVar.group(1)):
|
144
|
+
sComment=oComments[oVar.group(1)]
|
145
|
+
else:
|
146
|
+
oComments.remove(oVar.group(1))
|
147
|
+
|
148
|
+
sDefault=''
|
149
|
+
bRequired=False
|
150
|
+
if oVar.group(2) <> None :
|
151
|
+
bRequired=True
|
152
|
+
oLogging.debug("'%s' is required",oVar.group(1))
|
153
|
+
if oVar.group(3) <> None:
|
154
|
+
sDefault=oVar.group(3)[1:]
|
155
|
+
oLogging.debug("'%s' default value is '%s'",oVar.group(1),sDefault)
|
156
|
+
|
157
|
+
|
158
|
+
oVars[oVar.group(1)]={ 'comments': sComment,
|
159
|
+
'required': bRequired,
|
160
|
+
'default': sDefault }
|
161
|
+
|
162
|
+
oLogging.debug('template loaded.')
|
163
|
+
|
164
|
+
# -----------------------------------------------
|
165
|
+
# Ask section -----------------------------------
|
166
|
+
|
167
|
+
print "We need some information from you. Please ask the following question:\n"
|
168
|
+
# Time to ask information to the user.
|
169
|
+
|
170
|
+
for sElem in oVars:
|
171
|
+
print '{0}'.format(oVars[sElem]['comments'])
|
172
|
+
sPar="# "
|
173
|
+
if oVars[sElem]['default'] <> '':
|
174
|
+
sPar+='Default is "'+oVars[sElem]['default']+'".'
|
175
|
+
if oVars[sElem]['required'] :
|
176
|
+
if sPar <> "# " :
|
177
|
+
sPar+=' Required.'
|
178
|
+
else:
|
179
|
+
sPar='# Required.'
|
180
|
+
|
181
|
+
if sPar <> "":
|
182
|
+
print sPar
|
183
|
+
sValue=""
|
184
|
+
|
185
|
+
while sValue == "":
|
186
|
+
sValue=raw_input(sElem+'=')
|
187
|
+
|
188
|
+
if sValue == "":
|
189
|
+
sValue=oVars[sElem]['default']
|
190
|
+
|
191
|
+
if oVars[sElem]['required']:
|
192
|
+
if sValue == "":
|
193
|
+
print "[1mValue required[0m. Please enter a value.[2A[K"
|
194
|
+
else:
|
195
|
+
break
|
196
|
+
|
197
|
+
oVars[sElem]['value']=sValue
|
198
|
+
print '[A{0}="[1m{1}[0m"\n[K'.format(sElem,sValue)
|
199
|
+
|
200
|
+
|
201
|
+
fTmpl.seek(0) # Read the file again to replace data
|
202
|
+
try:
|
203
|
+
fOutputFile=open(sOutputFile,"w+")
|
204
|
+
except IOError as e:
|
205
|
+
oLogging.error("Unable to open '%s' for write. Errno: %s (%s)", sOutputFile, e.errno, e.strerror)
|
206
|
+
sys.exit(2)
|
207
|
+
|
208
|
+
print "--------------------------------\nThank you\nWriting '{0}'".format(sOutputFile)
|
209
|
+
|
210
|
+
# -----------------------------------------------
|
211
|
+
# Time to save the template. --------------------
|
212
|
+
|
213
|
+
reReplaced=re.compile('([^$])({([A-Z_-]+)(!)?(:.*?)?})')
|
214
|
+
for line in fTmpl:
|
215
|
+
oVar=reReplaced.search(line)
|
216
|
+
|
217
|
+
if oVar <> None:
|
218
|
+
|
219
|
+
if oVar.group(1) <> None:
|
220
|
+
sValue=oVar.group(1)+oVars[oVar.group(3)]['value']
|
221
|
+
else:
|
222
|
+
sValue=oVars[oVar.group(3)]['value']
|
223
|
+
sNewLine=reReplaced.sub(sValue,line)
|
224
|
+
else:
|
225
|
+
sNewLine=line
|
226
|
+
|
227
|
+
sys.stdout.flush()
|
228
|
+
fOutputFile.write(sNewLine)
|
229
|
+
|
230
|
+
fOutputFile.close()
|
231
|
+
fTmpl.close()
|
232
|
+
print 'Done'
|
233
|
+
|
234
|
+
|
235
|
+
|
236
|
+
##############################
|
237
|
+
def main(argv):
|
238
|
+
"""Main function"""
|
239
|
+
|
240
|
+
global GITBRANCH
|
241
|
+
global MAESTRO_TMPL_PROVIDER
|
242
|
+
global MAESTRO_RPATH_TMPL
|
243
|
+
|
244
|
+
oLogging=logging.getLogger('build-env')
|
245
|
+
oLogging.setLevel(20)
|
246
|
+
|
247
|
+
try:
|
248
|
+
opts,args = getopt.getopt(argv,"hp:vd:r:P:", ["help", "--for-provider=", "env-path=" , "maestro-path=" ,"debug" ,"verbose" ])
|
249
|
+
except getopt.GetoptError, e:
|
250
|
+
oLogging.error('Error: '+e.msg)
|
251
|
+
help()
|
252
|
+
sys.exit(2)
|
253
|
+
|
254
|
+
for opt, arg in opts:
|
255
|
+
if opt in ('-h', '--help'):
|
256
|
+
help()
|
257
|
+
sys.exit()
|
258
|
+
elif opt in ('-v','--verbose'):
|
259
|
+
if oLogging.level >20:
|
260
|
+
oLogging.setLevel(oLogging.level-10)
|
261
|
+
elif opt in ('--debug','-d'):
|
262
|
+
logging.getLogger().setLevel(logging.DEBUG)
|
263
|
+
logging.debug("Setting debug mode")
|
264
|
+
oLogging.setLevel(logging.DEBUG)
|
265
|
+
elif opt in ('-p', '--env-path'):
|
266
|
+
ENV_PATH=arg
|
267
|
+
elif opt in ('--gitbranch'):
|
268
|
+
GITBRANCH=arg
|
269
|
+
elif opt in ('-r','--maestro-path'):
|
270
|
+
MAESTRO_PATH=arg
|
271
|
+
elif opt in ('-P','--for-provider'):
|
272
|
+
TMPL_PROVIDER=arg
|
273
|
+
|
274
|
+
# Start Main tasks - Testing required variables.
|
275
|
+
if not 'ENV_PATH' in locals() or not 'MAESTRO_PATH' in locals():
|
276
|
+
oLogging.error("--env-path or --maestro-path values missing. Please check command flags.")
|
277
|
+
sys.exit(1)
|
278
|
+
|
279
|
+
|
280
|
+
global MAESTRO_TMPL_FILE # template file to use.
|
281
|
+
|
282
|
+
sMaestroFile=os.path.join(MAESTRO_RPATH_TMPL, MAESTRO_TMPL_PROVIDER+'-'+MAESTRO_TMPL_FILE)
|
283
|
+
BuildEnv(ENV_PATH, sMaestroFile, MAESTRO_PATH, GITBRANCH)
|
284
|
+
|
285
|
+
sys.exit(0)
|
286
|
+
|
287
|
+
#####################################
|
288
|
+
logging.basicConfig(format='%(asctime)s: %(levelname)s - %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
|
289
|
+
|
290
|
+
if __name__ == "__main__":
|
291
|
+
main(sys.argv[1:])
|
292
|
+
|
293
|
+
|
data/lib/catalog.yaml
CHANGED
@@ -16,9 +16,18 @@ redstone:
|
|
16
16
|
image: proto2b
|
17
17
|
flavor: standard.xsmall
|
18
18
|
ports: [22, 80, 443, 3131, 3000, 3132, 3133, 3134, 3135, 4505, 4506, 5000, 5666, 8000, 8080, 8081, 8083, 8125, 8139, 8140, 8773, 8774, 8776, 9292, 29418, 35357]
|
19
|
-
|
19
|
+
keypair_path: ~/.hpcloud/keypairs/nova
|
20
|
+
keypair_name: nova
|
20
21
|
router: private-ext
|
21
|
-
|
22
|
+
security_group: default
|
23
|
+
network: private
|
24
|
+
# at this point you have to clone the infra project manually
|
25
|
+
build_config_dir: ~/forj/infra/build/boxes/maestro
|
26
|
+
build_config: box-13.5
|
27
|
+
branch: master
|
28
|
+
box_name: maestro
|
29
|
+
infra: ~/.forj/infra
|
30
|
+
|
22
31
|
|
23
32
|
modus:
|
24
33
|
ports: []
|
data/lib/compute.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
# (c) Copyright 2014 Hewlett-Packard Development Company, L.P.
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
|
18
|
+
require 'rubygems'
|
19
|
+
require 'require_relative'
|
20
|
+
|
21
|
+
require_relative 'connection.rb'
|
22
|
+
include Connection
|
23
|
+
require_relative 'log.rb'
|
24
|
+
include Logging
|
25
|
+
|
26
|
+
#
|
27
|
+
# compute module
|
28
|
+
#
|
29
|
+
module Compute
|
30
|
+
def delete_forge(name)
|
31
|
+
instances = Connection.compute.servers.all(:name => name)
|
32
|
+
instances.each do|instance|
|
33
|
+
# make sure we don't delete another forge because fog filters
|
34
|
+
# the name in a "like syntax" way
|
35
|
+
Connection.compute.servers.get(instance.id).destroy
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/down.rb
CHANGED
@@ -24,6 +24,12 @@ require_relative 'yaml_parse.rb'
|
|
24
24
|
include YamlParse
|
25
25
|
require_relative 'security.rb'
|
26
26
|
include SecurityGroup
|
27
|
+
require_relative 'log.rb'
|
28
|
+
include Logging
|
29
|
+
require_relative 'ssh.rb'
|
30
|
+
include Ssh
|
31
|
+
require_relative 'compute.rb'
|
32
|
+
include Compute
|
27
33
|
|
28
34
|
#
|
29
35
|
# Down module
|
@@ -32,30 +38,25 @@ module Down
|
|
32
38
|
def down(name)
|
33
39
|
begin
|
34
40
|
|
35
|
-
|
41
|
+
initial_msg = 'deleting forge "%s"' % [name]
|
42
|
+
Logging.info(initial_msg)
|
43
|
+
puts (initial_msg)
|
36
44
|
|
37
|
-
|
45
|
+
Compute.delete_forge(name)
|
38
46
|
|
39
|
-
|
40
|
-
subnet = Network
|
41
|
-
|
42
|
-
# delete the router interface
|
43
|
-
router = Network::get_router(definitions['redstone']['router'])
|
47
|
+
router = Network.get_router('private-ext')
|
48
|
+
subnet = Network.get_subnet(name)
|
44
49
|
Network.delete_router_interface(subnet.id, router)
|
45
50
|
|
46
|
-
# delete subnet
|
47
51
|
Network.delete_subnet(subnet.id)
|
48
|
-
|
49
|
-
|
50
|
-
# Network.delete_security_group(security_group.id)
|
51
|
-
|
52
|
-
# delete network
|
53
|
-
Network.delete_network(name)
|
52
|
+
network = Network.get_network(name)
|
53
|
+
Network.delete_network(network.name)
|
54
54
|
|
55
55
|
rescue SystemExit, Interrupt
|
56
56
|
puts 'process interrupted by user'
|
57
|
+
Logging.error('process interrupted by user')
|
57
58
|
rescue Exception => e
|
58
|
-
|
59
|
+
Logging.error(e.message)
|
59
60
|
end
|
60
61
|
end
|
61
62
|
end
|
data/lib/network.rb
CHANGED
@@ -27,6 +27,25 @@ include Logging
|
|
27
27
|
# Network module
|
28
28
|
#
|
29
29
|
module Network
|
30
|
+
def get_or_create_network(name)
|
31
|
+
network = get_network(name)
|
32
|
+
if network == nil
|
33
|
+
network = create_network(name)
|
34
|
+
end
|
35
|
+
network
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_network(name)
|
39
|
+
begin
|
40
|
+
info = 'getting network %s' % [name]
|
41
|
+
Logging.info(info)
|
42
|
+
Connection.network.networks.all(:name => name)[0]
|
43
|
+
rescue => e
|
44
|
+
puts e.message
|
45
|
+
Logging.error(e.message)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
30
49
|
def create_network(name)
|
31
50
|
begin
|
32
51
|
info = 'creating network %s' % [name]
|
@@ -39,13 +58,25 @@ module Network
|
|
39
58
|
|
40
59
|
def delete_network(network_name)
|
41
60
|
begin
|
42
|
-
network =
|
61
|
+
network = get_network(network_name)
|
43
62
|
Connection.network.networks.get(network.id).destroy
|
44
63
|
rescue => e
|
45
64
|
Logging.error(e.message)
|
46
65
|
end
|
47
66
|
end
|
48
67
|
|
68
|
+
def get_or_create_subnet(network_id, name)
|
69
|
+
begin
|
70
|
+
subnet = get_subnet(name)
|
71
|
+
if subnet == nil
|
72
|
+
subnet = create_subnet(network_id, name)
|
73
|
+
end
|
74
|
+
subnet
|
75
|
+
rescue => e
|
76
|
+
Logging.error(e.message)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
49
80
|
def create_subnet(network_id, name)
|
50
81
|
begin
|
51
82
|
Connection.network.subnets.create(
|
data/lib/repositories.rb
CHANGED
@@ -49,5 +49,23 @@ module Repositories
|
|
49
49
|
Logging.error(e.message)
|
50
50
|
end
|
51
51
|
Dir.chdir(current_dir)
|
52
|
+
end
|
53
|
+
def create_infra
|
54
|
+
home = File.expand_path('~')
|
55
|
+
path = home + '/.forj/'
|
56
|
+
infra = path + 'infra/'
|
57
|
+
|
58
|
+
if File.directory?(infra)
|
59
|
+
FileUtils.rm_r(infra)
|
52
60
|
end
|
61
|
+
Dir.mkdir(infra)
|
62
|
+
|
63
|
+
command = 'cp -rp ~/.forj/maestro/templates/infra/cloud-init ~/.forj/infra/'
|
64
|
+
Kernel.system(command)
|
65
|
+
|
66
|
+
Dir.chdir('build_tmpl')
|
67
|
+
|
68
|
+
fill_template = 'python build-env.py -p ~/.forj/infra --maestro-path ~/.forj/maestro'
|
69
|
+
Kernel.system(fill_template)
|
70
|
+
end
|
53
71
|
end
|
data/lib/security.rb
CHANGED
@@ -28,6 +28,15 @@ include Logging
|
|
28
28
|
#
|
29
29
|
module SecurityGroup
|
30
30
|
|
31
|
+
def get_or_create_security_group(name)
|
32
|
+
Logging.info('getting or creating security group for %s' % [name])
|
33
|
+
security_group = get_security_group(name)
|
34
|
+
if security_group == nil
|
35
|
+
security_group = create_security_group(name)
|
36
|
+
end
|
37
|
+
security_group
|
38
|
+
end
|
39
|
+
|
31
40
|
def create_security_group(name)
|
32
41
|
sec_group = nil
|
33
42
|
begin
|
@@ -48,6 +57,14 @@ module SecurityGroup
|
|
48
57
|
sec_group
|
49
58
|
end
|
50
59
|
|
60
|
+
def get_security_group(name)
|
61
|
+
begin
|
62
|
+
Connection.network.security_groups.all({:name => name})[0]
|
63
|
+
rescue => e
|
64
|
+
Logging.error(e.message)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
51
68
|
def delete_security_group(security_group)
|
52
69
|
begin
|
53
70
|
sec_group = get_security_group(security_group)
|
@@ -82,14 +99,23 @@ module SecurityGroup
|
|
82
99
|
end
|
83
100
|
end
|
84
101
|
|
85
|
-
def
|
102
|
+
def get_security_group_rule(port)
|
86
103
|
begin
|
87
|
-
Connection.network.
|
104
|
+
Connection.network.security_group_rules.all({:port_range_min => port, :port_range_max => port})[0]
|
88
105
|
rescue => e
|
89
106
|
Logging.error(e.message)
|
90
107
|
end
|
91
108
|
end
|
92
109
|
|
110
|
+
def get_or_create_rule(security_group_id, protocol, port_min, port_max)
|
111
|
+
Logging.info('getting or creating rule %s' % [port_min])
|
112
|
+
rule = get_security_group_rule(port_min)
|
113
|
+
if rule == nil
|
114
|
+
rule = create_security_group_rule(security_group_id, protocol, port_min, port_max)
|
115
|
+
end
|
116
|
+
rule
|
117
|
+
end
|
118
|
+
|
93
119
|
def upload_existing_key(key_name, key_path)
|
94
120
|
command = 'hpcloud keypairs:import %s %s' % [key_name, key_path]
|
95
121
|
Kernel.system(command)
|
data/lib/setup.rb
CHANGED
@@ -15,6 +15,12 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
|
18
|
+
require 'rubygems'
|
19
|
+
require 'require_relative'
|
20
|
+
|
21
|
+
require_relative 'yaml_parse.rb'
|
22
|
+
include YamlParse
|
23
|
+
|
18
24
|
#
|
19
25
|
# Setup module call the hpcloud functions
|
20
26
|
#
|
@@ -22,6 +28,51 @@ module Setup
|
|
22
28
|
def setup
|
23
29
|
# delegate the initial configuration to hpcloud (unix_cli)
|
24
30
|
Kernel.system('hpcloud account:setup')
|
25
|
-
|
31
|
+
setup_credentials
|
32
|
+
save_cloud_fog
|
33
|
+
#Kernel.system('hpcloud keypairs:add nova')
|
26
34
|
end
|
27
35
|
end
|
36
|
+
|
37
|
+
def setup_credentials
|
38
|
+
puts 'Enter hpcloud username: '
|
39
|
+
hpcloud_os_user = $stdin.gets
|
40
|
+
puts 'Enter hpcloud password: '
|
41
|
+
hpcloud_os_key = $stdin.gets
|
42
|
+
|
43
|
+
home = File.expand_path('~')
|
44
|
+
creds = '%s/.cache/forj/creds' % [home]
|
45
|
+
File.open(creds, 'w') {|file|
|
46
|
+
file.write('HPCLOUD_OS_USER=%s' % [hpcloud_os_user])
|
47
|
+
file.write('HPCLOUD_OS_KEY=%s' % [hpcloud_os_key])
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def save_cloud_fog
|
53
|
+
home = File.expand_path('~')
|
54
|
+
|
55
|
+
cloud_fog = '%s/.cache/forj/master.forj-13.5' % [home]
|
56
|
+
local_creds = '%s/.cache/forj/creds' % [home]
|
57
|
+
|
58
|
+
creds = '%s/.hpcloud/accounts/hp' % [home]
|
59
|
+
template = YAML.load_file(creds)
|
60
|
+
local_template = YAML.load_file(local_creds)
|
61
|
+
|
62
|
+
|
63
|
+
access_key = template[:credentials][:account_id]
|
64
|
+
secret_key = template[:credentials][:secret_key]
|
65
|
+
|
66
|
+
os_user = local_template['HPCLOUD_OS_USER']
|
67
|
+
os_key = local_template['HPCLOUD_OS_KEY']
|
68
|
+
|
69
|
+
File.open(cloud_fog, 'w') {|file|
|
70
|
+
file.write('HPCLOUD_OS_USER=%s' % [os_user])
|
71
|
+
file.write('HPCLOUD_OS_KEY=%s' % [os_key])
|
72
|
+
file.write('DNS_KEY=%s' % [access_key])
|
73
|
+
file.write('DNS_SECRET=%s' % [secret_key])
|
74
|
+
}
|
75
|
+
|
76
|
+
command = 'cat %s | gzip -c | base64 -w0 > %s.g64' % [cloud_fog, cloud_fog]
|
77
|
+
Kernel.system(command)
|
78
|
+
end
|
data/lib/ssh.rb
CHANGED
@@ -18,11 +18,16 @@
|
|
18
18
|
require 'rubygems'
|
19
19
|
require 'require_relative'
|
20
20
|
|
21
|
+
require_relative 'log.rb'
|
22
|
+
include Logging
|
23
|
+
|
21
24
|
#
|
22
25
|
# ssh module
|
23
26
|
#
|
24
27
|
module Ssh
|
25
28
|
def connect(name, server)
|
29
|
+
msg = 'logging into %s : %s' % [name, server]
|
30
|
+
Logging.info(msg)
|
26
31
|
current_dir = Dir.pwd
|
27
32
|
Dir.chdir(current_dir + '/lib')
|
28
33
|
|
data/lib/ssh.sh
CHANGED
@@ -22,9 +22,10 @@ GRE="\e[92m"
|
|
22
22
|
OW=`date +%Y-%m-%d.%H%M%S`
|
23
23
|
logpath=~/.ssh/
|
24
24
|
DB=~/hosts
|
25
|
-
key_path=~/.ssh/
|
25
|
+
#key_path=~/.ssh/
|
26
|
+
#key_path=~/.hpcloud/keypairs/
|
26
27
|
init_config=~/ssh_init
|
27
|
-
key="nova"
|
28
|
+
key="nova.pem"
|
28
29
|
|
29
30
|
if [ ! -f $DB ]; then
|
30
31
|
cat > $DB <<'EOF'
|
data/spec/spec_helper.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
#
|
6
6
|
# Given that it is always loaded, you are encouraged to keep this file as
|
7
7
|
# light-weight as possible. Requiring heavyweight dependencies from this file
|
8
|
-
# will add to the boot time of your test suite on EVERY test run, even for an
|
8
|
+
# will add to the boot time of your test.rb suite on EVERY test.rb run, even for an
|
9
9
|
# individual file that may not need all of that loaded. Instead, make a
|
10
10
|
# separate helper file that requires this one and then use it only in the specs
|
11
11
|
# that actually need it.
|
@@ -48,7 +48,7 @@ RSpec.configure do |config|
|
|
48
48
|
|
49
49
|
# Seed global randomization in this process using the `--seed` CLI option.
|
50
50
|
# Setting this allows you to use `--seed` to deterministically reproduce
|
51
|
-
# test failures related to randomization by passing the same `--seed` value
|
51
|
+
# test.rb failures related to randomization by passing the same `--seed` value
|
52
52
|
# as the one that triggered the failure.
|
53
53
|
Kernel.srand config.seed
|
54
54
|
|
@@ -62,7 +62,7 @@ RSpec.configure do |config|
|
|
62
62
|
expectations.syntax = :expect
|
63
63
|
end
|
64
64
|
|
65
|
-
# rspec-mocks config goes here. You can use an alternate test double
|
65
|
+
# rspec-mocks config goes here. You can use an alternate test.rb double
|
66
66
|
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
67
67
|
config.mock_with :rspec do |mocks|
|
68
68
|
# Enable only the newer, non-monkey-patching expect syntax.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: forj
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.31
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2014-06-12 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|
16
|
-
requirement: &
|
16
|
+
requirement: &4634460 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 0.16.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *4634460
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: nokogiri
|
27
|
-
requirement: &
|
27
|
+
requirement: &4633120 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 1.5.11
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *4633120
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: fog
|
38
|
-
requirement: &
|
38
|
+
requirement: &4629760 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 1.19.0
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *4629760
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: hpcloud
|
49
|
-
requirement: &
|
49
|
+
requirement: &4629180 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 2.0.8
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *4629180
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: git
|
60
|
-
requirement: &
|
60
|
+
requirement: &4628460 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 1.2.7
|
66
66
|
type: :runtime
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *4628460
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rbx-require-relative
|
71
|
-
requirement: &
|
71
|
+
requirement: &4627320 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ~>
|
@@ -76,7 +76,7 @@ dependencies:
|
|
76
76
|
version: 0.0.7
|
77
77
|
type: :runtime
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *4627320
|
80
80
|
description: forj command line
|
81
81
|
email:
|
82
82
|
- forj@forj.io
|
@@ -86,6 +86,7 @@ extensions: []
|
|
86
86
|
extra_rdoc_files: []
|
87
87
|
files:
|
88
88
|
- bin/forj
|
89
|
+
- lib/compute.rb
|
89
90
|
- lib/connection.rb
|
90
91
|
- lib/network.rb
|
91
92
|
- lib/security.rb
|
@@ -99,6 +100,7 @@ files:
|
|
99
100
|
- lib/ssh.sh
|
100
101
|
- lib/log.rb
|
101
102
|
- lib/helpers.rb
|
103
|
+
- lib/build_tmpl/build-env.py
|
102
104
|
- spec/boot_spec.rb
|
103
105
|
- spec/connection_spec.rb
|
104
106
|
- spec/down_spec.rb
|