trinidad_sandbox_extension 0.4.2 → 1.0.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/Gemfile +4 -0
- data/Gemfile.lock +56 -0
- data/History.txt +6 -0
- data/README +27 -0
- data/git-hooks/post-commit +7 -0
- data/lib/trinidad_sandbox_extension.rb +24 -3
- data/lib/trinidad_sandbox_extension/app/helpers/{sandbox.rb → auth.rb} +20 -19
- data/lib/trinidad_sandbox_extension/app/helpers/context.rb +72 -0
- data/lib/trinidad_sandbox_extension/app/helpers/deploy.rb +106 -0
- data/lib/trinidad_sandbox_extension/app/helpers/view.rb +39 -0
- data/lib/trinidad_sandbox_extension/app/model/application_context.rb +48 -4
- data/lib/trinidad_sandbox_extension/app/public/css/sandbox.css +187 -0
- data/lib/trinidad_sandbox_extension/app/sandbox.rb +56 -39
- data/lib/trinidad_sandbox_extension/app/views/actions.html.haml +2 -2
- data/lib/trinidad_sandbox_extension/app/views/applications.html.haml +17 -0
- data/lib/trinidad_sandbox_extension/app/views/applications.xml.haml +13 -0
- data/lib/trinidad_sandbox_extension/app/views/deploy.html.haml +16 -0
- data/lib/trinidad_sandbox_extension/app/views/layout.html.haml +10 -6
- data/lib/trinidad_sandbox_extension/app/views/navigation.html.haml +10 -0
- data/spec/trinidad_sandbox_extension_spec.rb +12 -2
- data/trinidad-libs/trinidad-sandbox-extension.jar +0 -0
- data/trinidad_sandbox_extension.gemspec +16 -9
- metadata +52 -41
- data/lib/trinidad_sandbox_extension/app/public/css/main.css +0 -137
- data/lib/trinidad_sandbox_extension/app/views/app.html.haml +0 -8
- data/lib/trinidad_sandbox_extension/app/views/app.xml.haml +0 -11
- data/lib/trinidad_sandbox_extension/app/views/index.html.haml +0 -6
- data/lib/trinidad_sandbox_extension/app/views/index.xml.haml +0 -3
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
trinidad_sandbox_extension (0.4.2)
|
5
|
+
grit
|
6
|
+
haml
|
7
|
+
json
|
8
|
+
sinatra
|
9
|
+
sinatra-authorization
|
10
|
+
sinatra-flash
|
11
|
+
sinatra-respond_to
|
12
|
+
trinidad (>= 1.0.1)
|
13
|
+
|
14
|
+
GEM
|
15
|
+
remote: http://rubygems.org/
|
16
|
+
specs:
|
17
|
+
diff-lcs (1.1.2)
|
18
|
+
grit (2.4.1)
|
19
|
+
diff-lcs (~> 1.1)
|
20
|
+
mime-types (~> 1.15)
|
21
|
+
haml (3.0.25)
|
22
|
+
jruby-rack (1.0.7)
|
23
|
+
json (1.5.1-java)
|
24
|
+
mime-types (1.16)
|
25
|
+
mocha (0.9.12)
|
26
|
+
rack (1.2.1)
|
27
|
+
rspec (2.5.0)
|
28
|
+
rspec-core (~> 2.5.0)
|
29
|
+
rspec-expectations (~> 2.5.0)
|
30
|
+
rspec-mocks (~> 2.5.0)
|
31
|
+
rspec-core (2.5.1)
|
32
|
+
rspec-expectations (2.5.0)
|
33
|
+
diff-lcs (~> 1.1.2)
|
34
|
+
rspec-mocks (2.5.0)
|
35
|
+
sinatra (1.2.0)
|
36
|
+
rack (~> 1.1)
|
37
|
+
tilt (>= 1.2.2, < 2.0)
|
38
|
+
sinatra-authorization (1.0.0)
|
39
|
+
sinatra (>= 0.9.1.1)
|
40
|
+
sinatra-flash (0.3.0)
|
41
|
+
sinatra (>= 1.0.0)
|
42
|
+
sinatra-respond_to (0.6.0)
|
43
|
+
sinatra (~> 1.1)
|
44
|
+
tilt (1.2.2)
|
45
|
+
trinidad (1.0.5)
|
46
|
+
jruby-rack (>= 1.0.2)
|
47
|
+
trinidad_jars (>= 0.3.0)
|
48
|
+
trinidad_jars (1.0.0)
|
49
|
+
|
50
|
+
PLATFORMS
|
51
|
+
java
|
52
|
+
|
53
|
+
DEPENDENCIES
|
54
|
+
mocha
|
55
|
+
rspec (>= 2.2)
|
56
|
+
trinidad_sandbox_extension!
|
data/History.txt
CHANGED
data/README
CHANGED
@@ -36,11 +36,38 @@ It also supports basic authentication, we'll have to specify the username and pa
|
|
36
36
|
username: manager
|
37
37
|
password: XXXXXXX
|
38
38
|
|
39
|
+
We can also use the console in readonly mode, so users can see the applications deployed but they cannot deploy new ones or modify them:
|
40
|
+
|
41
|
+
---
|
42
|
+
extensions:
|
43
|
+
sandbox:
|
44
|
+
readonly: true
|
45
|
+
|
39
46
|
# FEATURES
|
40
47
|
|
41
48
|
The console as well as the REST api allow to list all the applications managed by that Trinidad's instance and start/stop them.
|
42
49
|
By security reasons the sandbox application is not listed nor can be stopped.
|
43
50
|
|
51
|
+
# GIT DEPLOYMENT
|
52
|
+
|
53
|
+
The sandbox console also allows to deploy new applications into Trinidad via Git. By default it uses ssh keys to access to the repository with the user `git`.
|
54
|
+
|
55
|
+
We can also use git hooks to deploy applications via a POST-Receive callback.
|
56
|
+
In this case we use a token for authentication that must be set in the configuration, then we'll use an url like we show bellow in our hook:
|
57
|
+
|
58
|
+
---
|
59
|
+
extensions:
|
60
|
+
sandbox:
|
61
|
+
deploy_token: ULTRA_SECRET_TOKEN
|
62
|
+
|
63
|
+
POST-Receive url: http://host/sandbox/deploy?deploy_token=ULTRA_SECRET_TOKEN
|
64
|
+
|
65
|
+
If we want to let users to deploy applications in public git repositories we can also disable the ssh authentication with the option `git_ssh`:
|
66
|
+
|
67
|
+
---
|
68
|
+
extensions:
|
69
|
+
sandbox:
|
70
|
+
git_ssh: false
|
44
71
|
|
45
72
|
# TODO
|
46
73
|
|
@@ -6,7 +6,7 @@ require File.expand_path('../../trinidad-libs/trinidad-sandbox-extension', __FIL
|
|
6
6
|
module Trinidad
|
7
7
|
module Extensions
|
8
8
|
class SandboxServerExtension < ServerExtension
|
9
|
-
VERSION = '0.
|
9
|
+
VERSION = '1.0.0'
|
10
10
|
|
11
11
|
def configure(tomcat)
|
12
12
|
opts = prepare_options
|
@@ -42,12 +42,33 @@ module Trinidad
|
|
42
42
|
app_ctx.privileged = true
|
43
43
|
|
44
44
|
if opts[:username] && opts[:password]
|
45
|
-
app_ctx.servlet_context.
|
46
|
-
app_ctx.servlet_context.
|
45
|
+
app_ctx.servlet_context.set_attribute("sandbox_username", opts[:username].to_s);
|
46
|
+
app_ctx.servlet_context.set_attribute("sandbox_password", opts[:password].to_s);
|
47
47
|
end
|
48
48
|
|
49
|
+
app_ctx.servlet_context.set_attribute('deploy_token', opts[:deploy_token]) if opts[:deploy_token]
|
50
|
+
app_ctx.servlet_context.set_attribute('host_name', opts[:host_name]) if opts[:host_name]
|
51
|
+
|
52
|
+
app_ctx.servlet_context.set_attribute('enable_default', boolean_option(opts[:enable_default]))
|
53
|
+
|
54
|
+
app_ctx.servlet_context.set_attribute('git_ssh', boolean_option(opts[:git_ssh]))
|
55
|
+
|
56
|
+
app_ctx.servlet_context.set_attribute('readonly', boolean_option(opts[:readonly], false))
|
57
|
+
|
49
58
|
app_ctx
|
50
59
|
end
|
60
|
+
|
61
|
+
private
|
62
|
+
def boolean_option(option, default = true)
|
63
|
+
option.nil? ? default : option
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class SandboxOptionsExtension < OptionsExtension
|
68
|
+
def configure(parser, default_options)
|
69
|
+
default_options[:extensions] ||= {}
|
70
|
+
default_options[:extensions][:sandbox] = {}
|
71
|
+
end
|
51
72
|
end
|
52
73
|
end
|
53
74
|
end
|
@@ -1,8 +1,6 @@
|
|
1
|
-
|
2
1
|
module Trinidad
|
3
2
|
module Sandbox
|
4
3
|
module Helpers
|
5
|
-
|
6
4
|
module Auth
|
7
5
|
require 'sinatra/authorization'
|
8
6
|
include Sinatra::Authorization
|
@@ -17,6 +15,24 @@ module Trinidad
|
|
17
15
|
|
18
16
|
def authorization_realm; "Trinidad's sandbox"; end
|
19
17
|
|
18
|
+
def basic_auth_required?(request)
|
19
|
+
!token_required?(request)
|
20
|
+
end
|
21
|
+
|
22
|
+
def token_required?(request)
|
23
|
+
request.path == '/deploy'
|
24
|
+
end
|
25
|
+
|
26
|
+
def token_required(params, realm = authorization_realm)
|
27
|
+
return if authorized_by_token?(params)
|
28
|
+
response["WWW-Authenticate"] = %(Basic realm="#{realm}")
|
29
|
+
throw :halt, [401, "Deploy Token Required"]
|
30
|
+
end
|
31
|
+
|
32
|
+
def authorized_by_token?(params)
|
33
|
+
deploy_token.nil? || params[:deploy_token] == deploy_token
|
34
|
+
end
|
35
|
+
|
20
36
|
private
|
21
37
|
def sandbox_username
|
22
38
|
@sandbox_username ||= $servlet_context.getAttribute('sandbox_username')
|
@@ -25,24 +41,9 @@ module Trinidad
|
|
25
41
|
def sandbox_password
|
26
42
|
@sandbox_password ||= $servlet_context.getAttribute('sandbox_password')
|
27
43
|
end
|
28
|
-
end
|
29
|
-
|
30
|
-
module Context
|
31
|
-
def sandbox_context
|
32
|
-
@sandbox_context ||= $servlet_context.getAttribute('sandbox_context')
|
33
|
-
end
|
34
|
-
|
35
|
-
def context_not_found(name)
|
36
|
-
flash[:warning] = "application not found: #{name}"
|
37
|
-
$servlet_context.log "application not found: #{name}"
|
38
|
-
respond_to do |wants|
|
39
|
-
wants.html { redirect sandbox_context.path }
|
40
|
-
wants.xml { status 404 }
|
41
|
-
end
|
42
|
-
end
|
43
44
|
|
44
|
-
def
|
45
|
-
$servlet_context.
|
45
|
+
def deploy_token
|
46
|
+
@deploy_token ||= $servlet_context.get_attribute('deploy_token')
|
46
47
|
end
|
47
48
|
end
|
48
49
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Trinidad
|
2
|
+
module Sandbox
|
3
|
+
module Helpers
|
4
|
+
module Context
|
5
|
+
def sandbox_context
|
6
|
+
@sandbox_context ||= $servlet_context.getAttribute('sandbox_context')
|
7
|
+
end
|
8
|
+
|
9
|
+
def enable_default?
|
10
|
+
!!$servlet_context.getAttribute('enable_default')
|
11
|
+
end
|
12
|
+
|
13
|
+
def git_ssh?
|
14
|
+
!!$servlet_context.getAttribute('git_ssh')
|
15
|
+
end
|
16
|
+
|
17
|
+
def readonly?
|
18
|
+
!!$servlet_context.get_attribute('readonly')
|
19
|
+
end
|
20
|
+
|
21
|
+
def render_readonly
|
22
|
+
warning "The console has been started as READONLY, you can access to that resource"
|
23
|
+
redirect_to_home 401
|
24
|
+
end
|
25
|
+
|
26
|
+
def context_not_found(name)
|
27
|
+
warning "It seems the application #{name} is not running on Trinidad"
|
28
|
+
redirect_to_home 404
|
29
|
+
end
|
30
|
+
|
31
|
+
def repo_not_found
|
32
|
+
warning "The repository url is required to clone the application", :now
|
33
|
+
respond_to_invalid_deploy
|
34
|
+
end
|
35
|
+
|
36
|
+
def invalid_app_path(path)
|
37
|
+
warning "The path #{path} is not valid, please remove the slashes", :now
|
38
|
+
respond_to_invalid_deploy
|
39
|
+
end
|
40
|
+
|
41
|
+
def host
|
42
|
+
$servlet_context.getAttribute('tomcat_host')
|
43
|
+
end
|
44
|
+
|
45
|
+
def redirect_to_home(status_code)
|
46
|
+
respond_to do |wants|
|
47
|
+
wants.html { redirect sandbox_context.path }
|
48
|
+
wants.xml { status status_code }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def respond_to_invalid_deploy
|
53
|
+
@page_id = 'deploy'
|
54
|
+
respond_to do |wants|
|
55
|
+
wants.html { haml :deploy }
|
56
|
+
wants.xml { status 400 }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def warning(message, req = :next)
|
61
|
+
flash.send(req)[:warning] = message
|
62
|
+
$servlet_context.log message
|
63
|
+
end
|
64
|
+
|
65
|
+
def available_context?(context)
|
66
|
+
context.name != sandbox_context.name || enable_default? ||
|
67
|
+
(!enable_default? && context.name == 'default')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Trinidad
|
2
|
+
module Sandbox
|
3
|
+
module Helpers
|
4
|
+
module Deploy
|
5
|
+
require 'grit'
|
6
|
+
require 'json'
|
7
|
+
require 'uri'
|
8
|
+
require 'fileutils'
|
9
|
+
|
10
|
+
def deploy_from_form(params)
|
11
|
+
repo_url = params["repo"]
|
12
|
+
if repo_url.empty?
|
13
|
+
repo_not_found
|
14
|
+
else
|
15
|
+
branch = params["branch"]
|
16
|
+
branch = 'master' if branch.empty?
|
17
|
+
|
18
|
+
ssh = normalize_uri repo_url
|
19
|
+
path = params["path"]
|
20
|
+
path = path_from_repo(ssh) if path.empty? && !enable_default?
|
21
|
+
|
22
|
+
unless valid_path? path
|
23
|
+
invalid_app_path(path)
|
24
|
+
else
|
25
|
+
status = find_and_deploy(ssh, branch, path)
|
26
|
+
redirect_to_home status
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def deploy_from_web_hook(params)
|
32
|
+
payload = JSON.parse(params['payload'])
|
33
|
+
url = payload['repository']['url']
|
34
|
+
branch = File.basename payload['ref']
|
35
|
+
|
36
|
+
ssh = normalize_uri url
|
37
|
+
path = path_from_repo(ssh)
|
38
|
+
|
39
|
+
status = find_and_deploy(ssh, branch, path)
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_and_deploy(repo, branch, path)
|
43
|
+
dest = File.join(host.app_base, path)
|
44
|
+
|
45
|
+
deployed_app = ApplicationContext.find_by_doc_base(dest)
|
46
|
+
status = if deployed_app
|
47
|
+
redeploy_application(deployed_app, repo, branch, dest)
|
48
|
+
204
|
49
|
+
else
|
50
|
+
deploy_new_application(path, repo, branch, dest)
|
51
|
+
201
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def deploy_new_application(path, repo, branch, dest)
|
56
|
+
clone(repo, branch, dest)
|
57
|
+
bundle(dest)
|
58
|
+
ApplicationContext.create(path, dest)
|
59
|
+
end
|
60
|
+
|
61
|
+
def redeploy_application(context, repo, branch, dest)
|
62
|
+
context.send(:setPaused, true)
|
63
|
+
FileUtils.rm_rf File.expand_path(dest)
|
64
|
+
|
65
|
+
clone(repo, branch, dest)
|
66
|
+
|
67
|
+
context.reload
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def clone(repo, branch, dest)
|
72
|
+
Grit.debug = true
|
73
|
+
Grit::Git.with_timeout(1000) do
|
74
|
+
Grit::Git.new(dest).clone({:branch => branch}, repo, dest)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def bundle(dest)
|
79
|
+
Dir.chdir(dest) do
|
80
|
+
`jruby -S bundle install` if File.exist? 'Gemfile'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def normalize_uri(url)
|
85
|
+
normalized = if git_ssh?
|
86
|
+
return url if url =~ /^git@/
|
87
|
+
uri = URI.parse(url)
|
88
|
+
|
89
|
+
"git@#{uri.host}#{uri.path.sub('/', ':')}"
|
90
|
+
end || url
|
91
|
+
|
92
|
+
normalized << '.git' unless normalized =~ /\.git$/
|
93
|
+
normalized
|
94
|
+
end
|
95
|
+
|
96
|
+
def path_from_repo(repo)
|
97
|
+
repo.split('/').last.sub('.git', '')
|
98
|
+
end
|
99
|
+
|
100
|
+
def valid_path?(path)
|
101
|
+
path.split('/').length == 1
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Trinidad
|
2
|
+
module Sandbox
|
3
|
+
module Helpers
|
4
|
+
module View
|
5
|
+
def link_to_deploy
|
6
|
+
%q{<a href="deploy">deploy</a>}
|
7
|
+
end
|
8
|
+
|
9
|
+
def render_parameters(parameters)
|
10
|
+
render = ''
|
11
|
+
|
12
|
+
parameters.keys.sort.each_with_index do |key, index|
|
13
|
+
column = find_column(parameters, index)
|
14
|
+
|
15
|
+
klass = "column#{column}"
|
16
|
+
klass << " reset" if column == 2 && find_column(parameters, index - 1) == 1
|
17
|
+
|
18
|
+
render << %Q{<li class="#{klass}">#{key} => #{parameters[key]}</li>}
|
19
|
+
end
|
20
|
+
|
21
|
+
render
|
22
|
+
end
|
23
|
+
|
24
|
+
def render_host_name
|
25
|
+
$servlet_context.get_attribute('host_name') || 'HOST_NAME'
|
26
|
+
end
|
27
|
+
|
28
|
+
def render_deploy_token
|
29
|
+
$servlet_context.get_attribute('deploy_token') || 'SECRET_DEPLOY_TOKEN'
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def find_column(parameters, index)
|
34
|
+
(parameters.length / 2) > index ? 1 : 2
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|