deployku 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +7 -0
- data/README.md +234 -0
- data/Rakefile +14 -0
- data/bin/deployku +45 -0
- data/deployku.gemspec +23 -0
- data/lib/deployku/config.rb +92 -0
- data/lib/deployku/configurable.rb +75 -0
- data/lib/deployku/containerable.rb +82 -0
- data/lib/deployku/engine.rb +55 -0
- data/lib/deployku/helpers.rb +7 -0
- data/lib/deployku/plugins/access.rb +161 -0
- data/lib/deployku/plugins/app.rb +211 -0
- data/lib/deployku/plugins/docker.rb +77 -0
- data/lib/deployku/plugins/git.rb +56 -0
- data/lib/deployku/plugins/lxc.rb +5 -0
- data/lib/deployku/plugins/nginx.rb +140 -0
- data/lib/deployku/plugins/postgres.rb +139 -0
- data/lib/deployku/plugins/rails.rb +100 -0
- data/lib/deployku/plugins.rb +111 -0
- data/lib/deployku.rb +19 -0
- metadata +63 -0
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'yaml'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'open3'
|
5
|
+
|
6
|
+
module Deployku
|
7
|
+
class PostgresPlugin < Deployku::Plugin
|
8
|
+
include Deployku::Configurable
|
9
|
+
include Deployku::Containerable
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@config = {
|
13
|
+
'from' => 'postgres',
|
14
|
+
'env' => {}
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
describe :create, '<NAME>', 'creates new PostgreSQL instance', acl_sys: :admin
|
19
|
+
def create(name)
|
20
|
+
app_dir = dir(name)
|
21
|
+
unless Dir.exists?(app_dir)
|
22
|
+
FileUtils.mkdir_p(app_dir)
|
23
|
+
end
|
24
|
+
app_dir
|
25
|
+
end
|
26
|
+
|
27
|
+
describe :delete, '<NAME>', 'deletes an existing PostgreSQL instance', acl_sys: :admin
|
28
|
+
def delete(name)
|
29
|
+
app_dir = dir(name)
|
30
|
+
if Dir.exists?(app_dir)
|
31
|
+
FileUtils.rm_rf(app_dir)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe :list, '', 'lists available PostgreSQL instances', acl_sys: :admin
|
36
|
+
def list
|
37
|
+
Dir.glob(File.join(Deployku::Config.home, '.postgres', '*')) do |path|
|
38
|
+
puts File.basename(path) if File.directory?(path)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'db:create', '<NAME> <DB_NAME>', 'create a database in postgres instance', acl_sys: :admin
|
43
|
+
def db_create(name, db_name)
|
44
|
+
config_load(name)
|
45
|
+
cid = get_container_id(name)
|
46
|
+
unless Deployku::Engine.running?(cid)
|
47
|
+
puts "Database instance '#{name}' is not running."
|
48
|
+
exit 1
|
49
|
+
end
|
50
|
+
ip = Deployku::Engine.ip(cid)
|
51
|
+
db = Deployku.sanitize_app_name(db_name)
|
52
|
+
system "echo \"#{@config['env']['POSTGRES_PASSWORD']}\n\" 'CREATE DATABASE #{db};' | psql -h #{ip} -U postgres"
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'db:drop', '<NAME> <DB_NAME>', 'destroy a database in postgres instance', acl_sys: :admin
|
56
|
+
def db_drop(name, db_name)
|
57
|
+
config_load(name)
|
58
|
+
cid = get_container_id(name)
|
59
|
+
unless Deployku::Engine.running?(cid)
|
60
|
+
puts "Database instance '#{name}' is not running."
|
61
|
+
exit 1
|
62
|
+
end
|
63
|
+
ip = Deployku::Engine.ip(cid)
|
64
|
+
db = Deployku.sanitize_app_name(db_name)
|
65
|
+
system "echo \"#{@config['env']['POSTGRES_PASSWORD']}\n\" 'DROP DATABASE #{db};' | psql -h #{ip} -U postgres"
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'db:link', '<NAME> <DB_NAME> <APP>', 'connect appliaction with database', acl_sys: :admin
|
69
|
+
def db_link(name, db_name, app_name)
|
70
|
+
config_load(name)
|
71
|
+
db_id = get_container_id(name)
|
72
|
+
ip = Deployku::Engine.ip(db_id)
|
73
|
+
db = Deployku.sanitize_app_name(db_name)
|
74
|
+
user_name = 'user_' + SecureRandom.uuid.gsub('-','')
|
75
|
+
user_passwd = SecureRandom.uuid
|
76
|
+
database_url = "postgres://#{user_name}:#{user_passwd}@#{container_name(name)}/#{db}"
|
77
|
+
system "echo \"#{@config['env']['POSTGRES_PASSWORD']}\n\" \"CREATE USER #{user_name} WITH PASSWORD '#{user_passwd}';\" | psql -h #{ip} -U postgres"
|
78
|
+
system "echo \"#{@config['env']['POSTGRES_PASSWORD']}\n\" 'GRANT ALL ON DATABASE #{db} TO #{user_name};' | psql -h #{ip} -U postgres"
|
79
|
+
if $?.exitstatus == 0
|
80
|
+
Deployku::AppPlugin.run('config:set', [app_name, 'DATABASE_URL', database_url])
|
81
|
+
Deployku::AppPlugin.run(:link, [app_name, container_name(name)])
|
82
|
+
else
|
83
|
+
puts "Unable to create user in database."
|
84
|
+
exit 1
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe 'db:connect', '<NAME> <DB_NAME>', 'connect to database and enter prompt', acl_sys: :admin
|
89
|
+
def db_connect(name, db_name)
|
90
|
+
config_load(name)
|
91
|
+
db_id = get_container_id(name)
|
92
|
+
dbname = Deployku.sanitize_app_name(db_name)
|
93
|
+
ip = Deployku::Engine.ip(db_id)
|
94
|
+
system "PGPASSWORD=\"#{@config['env']['POSTGRES_PASSWORD']}\" psql -h #{ip} -U postgres #{dbname}"
|
95
|
+
end
|
96
|
+
|
97
|
+
# methods from containerable
|
98
|
+
describe :start, '<NAME>', 'starts container', acl_sys: :admin
|
99
|
+
def start(app_name)
|
100
|
+
config_load(app_name)
|
101
|
+
unless @config['volumes']
|
102
|
+
@config['volumes'] = {
|
103
|
+
File.join(dir(app_name), 'data') => '/postgresql/'
|
104
|
+
}
|
105
|
+
end
|
106
|
+
@config['env']['PGDATA'] = '/postgresql/' unless @config['env']['PGDATA']
|
107
|
+
@config['env']['POSTGRES_PASSWORD'] = SecureRandom.uuid unless @config['env']['POSTGRES_PASSWORD']
|
108
|
+
config_save(app_name)
|
109
|
+
|
110
|
+
Deployku::Config.merge!(@config)
|
111
|
+
app_hash = Deployku::Engine.start(@config['from'], dir(app_name), container_name(app_name))
|
112
|
+
exit 1 if $?.nil? || $?.exitstatus != 0
|
113
|
+
set_container_id(app_name, container_name(app_name))
|
114
|
+
puts "Container #{app_hash} started."
|
115
|
+
end
|
116
|
+
|
117
|
+
describe :status, '<NAME>', 'show container status', acl_sys: :admin
|
118
|
+
describe :stop, '<NAME>', 'stops running container', acl_sys: :admin
|
119
|
+
describe :restart, '<NAME>', 'restarts container', acl_sys: :admin
|
120
|
+
describe :logs, '<NAME>', 'show app logs', acl_sys: :admin
|
121
|
+
|
122
|
+
# methods from configurable
|
123
|
+
describe 'config:show', '<NAME>', 'shows instance configuration', acl_sys: :admin
|
124
|
+
describe 'config:set', '<NAME> <ENV_VARIABLE> <VALUE>', 'sets environment variable', acl_sys: :admin
|
125
|
+
describe 'config:unset', '<NAME> <ENV_VARIABLE>', 'unsets environment variable', acl_sys: :admin
|
126
|
+
describe 'config:set_from', '<NAME> <VALUE>', 'sets base image name for container', acl_sys: :admin
|
127
|
+
describe 'config:unset_from', '<NAME>', 'sets base image to default', acl_sys: :admin
|
128
|
+
describe 'config:set_engine', '<NAME> <ENGINE>', 'sets container engine (docker, lxc)', acl_sys: :admin
|
129
|
+
describe 'config:unset_engine', '<NAME>', 'sets engine to default', acl_sys: :admin
|
130
|
+
|
131
|
+
def dir(name)
|
132
|
+
File.join(Deployku::Config.home, '.postgres', Deployku.sanitize_app_name(name))
|
133
|
+
end
|
134
|
+
|
135
|
+
def container_name(app_name)
|
136
|
+
"deployku-postgres-#{app_name}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Deployku
|
4
|
+
class RailsPlugin < Deployku::Plugin
|
5
|
+
PACKAGES = ['nodejs']
|
6
|
+
|
7
|
+
def volumes
|
8
|
+
['/app/public/']
|
9
|
+
end
|
10
|
+
|
11
|
+
def port
|
12
|
+
3000
|
13
|
+
end
|
14
|
+
|
15
|
+
def build_start_script(path)
|
16
|
+
app_path = File.join(path, 'app')
|
17
|
+
start_path = File.join(path, 'start')
|
18
|
+
custom_start_path = File.join(app_path, 'start')
|
19
|
+
if File.exists?(custom_start_path)
|
20
|
+
FileUtils.cp(custom_start_path, start_path)
|
21
|
+
else
|
22
|
+
File.open(start_path, 'w') do |f|
|
23
|
+
f << <<EOF
|
24
|
+
#!/usr/bin/env bash
|
25
|
+
source /usr/local/rvm/scripts/rvm
|
26
|
+
cd app
|
27
|
+
export RAILS_ENV=production
|
28
|
+
bundle exec rake db:migrate RAILS_ENV=production
|
29
|
+
bundle exec rake assets:precompile RAILS_ENV=production
|
30
|
+
|
31
|
+
bundle exec rails s -p #{Deployku::Config.port} -b 0.0.0.0 -e production
|
32
|
+
EOF
|
33
|
+
end
|
34
|
+
end
|
35
|
+
File.chmod(0755, start_path)
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_dockerfile(path)
|
39
|
+
app_path = File.join(path, 'app')
|
40
|
+
ruby_version = detect_ruby_version(app_path)
|
41
|
+
dockerfile_path = File.join(path, 'Dockerfile')
|
42
|
+
# TODO: process Dockerfile with erubis
|
43
|
+
custom_docker_file = File.join(app_path, 'Dockerfile')
|
44
|
+
if File.exists?(custom_docker_file)
|
45
|
+
FileUtils.cp(custom_docker_file, dockerfile_path)
|
46
|
+
else
|
47
|
+
File.open(dockerfile_path, 'w') do |f|
|
48
|
+
f << <<EOF
|
49
|
+
FROM #{Deployku::Config.from}
|
50
|
+
|
51
|
+
ENV DEBIAN_FRONTEND noninteractive
|
52
|
+
|
53
|
+
RUN apt-get update
|
54
|
+
|
55
|
+
RUN /bin/bash -l -c 'rvm install #{ruby_version} && rvm use #{ruby_version} --default'
|
56
|
+
RUN /bin/bash -l -c 'rvm rubygems current'
|
57
|
+
RUN /bin/bash -l -c 'gem install bundler'
|
58
|
+
|
59
|
+
RUN /bin/bash -l -c 'rvm cleanup all'
|
60
|
+
|
61
|
+
RUN apt-get install -y #{packages.join(' ')}
|
62
|
+
|
63
|
+
RUN apt-get -y autoclean
|
64
|
+
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
65
|
+
|
66
|
+
EXPOSE #{Deployku::Config.port}
|
67
|
+
CMD []
|
68
|
+
ENTRYPOINT ["/start"]
|
69
|
+
|
70
|
+
ADD start /start
|
71
|
+
|
72
|
+
ADD app /app
|
73
|
+
RUN /bin/bash -l -c 'cd app && RAILS_ENV=production bundle install --without development test'
|
74
|
+
EOF
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def detect(path)
|
80
|
+
gem_file_path = File.join(path, 'Gemfile')
|
81
|
+
if File.exists?(gem_file_path)
|
82
|
+
if File.read(gem_file_path) =~ %r{^\s*gem\s+['"](rails)['"]}m
|
83
|
+
return true
|
84
|
+
end
|
85
|
+
end
|
86
|
+
false
|
87
|
+
end
|
88
|
+
|
89
|
+
def detect_ruby_version(path)
|
90
|
+
gem_file_path = File.join(path, 'Gemfile')
|
91
|
+
if File.exists?(gem_file_path)
|
92
|
+
ruby_version = File.read(gem_file_path).gsub(%r{.*ruby\s+['"]([^'"]+)['"].*}m, '\1')
|
93
|
+
end
|
94
|
+
if not ruby_version || ruby_version == ''
|
95
|
+
ruby_version = '2.1.7'
|
96
|
+
end
|
97
|
+
ruby_version
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Deployku
|
2
|
+
|
3
|
+
class Plugin
|
4
|
+
@plugins = []
|
5
|
+
|
6
|
+
class NoMethod < NoMethodError
|
7
|
+
end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_reader :plugins
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.<<(plugin)
|
14
|
+
@plugins << plugin
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.find_plugin(name)
|
18
|
+
plugin_name = "Deployku::#{name.capitalize}Plugin"
|
19
|
+
@plugins.each do |plugin|
|
20
|
+
return plugin if plugin.name == plugin_name
|
21
|
+
end
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.filter_plugins(fn)
|
26
|
+
plugins = []
|
27
|
+
@plugins.each do |plugin|
|
28
|
+
plug = plugin.instance
|
29
|
+
plugins << plug if plug.respond_to?(fn)
|
30
|
+
end
|
31
|
+
plugins
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.inherited(plugin)
|
35
|
+
Plugin << plugin
|
36
|
+
plugin.instance_eval do
|
37
|
+
@help_register = []
|
38
|
+
class << self
|
39
|
+
attr_reader :help_register
|
40
|
+
end
|
41
|
+
def describe(fn_name, args, description, acl={})
|
42
|
+
@help_register << { name: fn_name, arg_list: args, desc: description, acl_app: acl[:acl_app], acl_sys: acl[:acl_sys] }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# collects info from 'describe' and returns it as a string
|
48
|
+
def self.help
|
49
|
+
str = ''
|
50
|
+
help_register = []
|
51
|
+
max_name_length = 0
|
52
|
+
max_arg_length = 0
|
53
|
+
max_desc_length = 0
|
54
|
+
@plugins.each do |plugin|
|
55
|
+
plugin.name =~ /Deployku::(.*?)Plugin/
|
56
|
+
next unless $1
|
57
|
+
name = $1.downcase
|
58
|
+
plugin.help_register.each do |reg|
|
59
|
+
new_reg = { name: "#{name}:#{reg[:name]}", args: reg[:arg_list], desc: reg[:desc] }
|
60
|
+
max_name_length = new_reg[:name].length if new_reg[:name].length > max_name_length
|
61
|
+
max_arg_length = new_reg[:args].length if new_reg[:args].length > max_arg_length
|
62
|
+
max_desc_length = new_reg[:desc].length if new_reg[:desc].length > max_desc_length
|
63
|
+
help_register << new_reg
|
64
|
+
end
|
65
|
+
end
|
66
|
+
help_register.each do |reg|
|
67
|
+
str << " %-#{max_name_length}s %-#{max_arg_length}s %s\n" % [reg[:name], reg[:args], reg[:desc]]
|
68
|
+
end
|
69
|
+
str
|
70
|
+
end
|
71
|
+
|
72
|
+
# run command in a plugin
|
73
|
+
def self.run(fn, args=[])
|
74
|
+
fn_name = fn.to_s.gsub(':', '_')
|
75
|
+
fn_desc = command_description(fn)
|
76
|
+
unless fn_desc
|
77
|
+
puts "Unknown command '#{fn}'."
|
78
|
+
exit 1
|
79
|
+
end
|
80
|
+
if fn_desc[:acl_app]
|
81
|
+
fn_desc[:acl_app].each do |idx, right|
|
82
|
+
Deployku::AccessPlugin.instance.check_app_rights(args[idx], right, true)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
if fn_desc[:acl_sys]
|
86
|
+
Deployku::AccessPlugin.instance.check_system_rights(fn_desc[:acl_sys], true)
|
87
|
+
end
|
88
|
+
plug = instance
|
89
|
+
raise Deployku::Plugin::NoMethod.new("no method '#{plug.class.name}.#{fn}'") unless plug.respond_to?(fn_name)
|
90
|
+
plug.send(fn_name, *args)
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.instance
|
94
|
+
@instance ||= self.new
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.command_description(fn)
|
98
|
+
self.help_register.each do |reg|
|
99
|
+
return reg if reg[:name].to_s == fn.to_s
|
100
|
+
end
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
|
104
|
+
# concatenate system wide packages and packages required by plugin
|
105
|
+
def packages
|
106
|
+
Deployku::Config.packages + self.class::PACKAGES
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
data/lib/deployku.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
dir = File.expand_path(File.dirname(__FILE__))
|
2
|
+
$:.unshift(dir) unless $:.include?(dir)
|
3
|
+
|
4
|
+
require 'deployku/config'
|
5
|
+
require 'deployku/helpers'
|
6
|
+
require 'deployku/configurable'
|
7
|
+
require 'deployku/containerable'
|
8
|
+
require 'deployku/plugins'
|
9
|
+
require 'deployku/engine'
|
10
|
+
|
11
|
+
# load core plugins
|
12
|
+
require 'deployku/plugins/access'
|
13
|
+
require 'deployku/plugins/docker'
|
14
|
+
require 'deployku/plugins/lxc'
|
15
|
+
require 'deployku/plugins/app'
|
16
|
+
require 'deployku/plugins/git'
|
17
|
+
require 'deployku/plugins/postgres'
|
18
|
+
require 'deployku/plugins/rails'
|
19
|
+
require 'deployku/plugins/nginx'
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: deployku
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Petr Kovář
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-09 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email: pejuko@gmail.com
|
15
|
+
executables:
|
16
|
+
- deployku
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- LICENSE
|
21
|
+
- README.md
|
22
|
+
- Rakefile
|
23
|
+
- bin/deployku
|
24
|
+
- deployku.gemspec
|
25
|
+
- lib/deployku.rb
|
26
|
+
- lib/deployku/config.rb
|
27
|
+
- lib/deployku/configurable.rb
|
28
|
+
- lib/deployku/containerable.rb
|
29
|
+
- lib/deployku/engine.rb
|
30
|
+
- lib/deployku/helpers.rb
|
31
|
+
- lib/deployku/plugins.rb
|
32
|
+
- lib/deployku/plugins/access.rb
|
33
|
+
- lib/deployku/plugins/app.rb
|
34
|
+
- lib/deployku/plugins/docker.rb
|
35
|
+
- lib/deployku/plugins/git.rb
|
36
|
+
- lib/deployku/plugins/lxc.rb
|
37
|
+
- lib/deployku/plugins/nginx.rb
|
38
|
+
- lib/deployku/plugins/postgres.rb
|
39
|
+
- lib/deployku/plugins/rails.rb
|
40
|
+
homepage: http://github.com/deployku/deployku
|
41
|
+
licenses: []
|
42
|
+
metadata: {}
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 2.4.8
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: Deploy applications using git with zero down time
|
63
|
+
test_files: []
|