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