vps 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 19a1e8448e7c6d7549da881d3c4410dffd9eee99
4
+ data.tar.gz: 8ed547a6a851bfc83a7be9df20d8781d8bf39d6b
5
+ SHA512:
6
+ metadata.gz: 589a2204c45d5eb76a578258d6ac56b053f7efd99fec980ed81b8b9f58e2d86d25918ddd0eff90cb91be63ba1d5f0bae4b26e9bedb7636d264a1e6c377693391
7
+ data.tar.gz: 477b312e229c9cef09c4f75a9e7e8f0aff05be8d9882940b9c03721049563addc569ace2c57739cb73c4a32390c8fdd78f8743af161037183be871745652d791
@@ -0,0 +1,8 @@
1
+ .DS_Store
2
+ .bundle
3
+ .yardoc
4
+ .rvmrc
5
+ Gemfile.lock
6
+ doc
7
+ pkg
8
+ test/coverage
@@ -0,0 +1,15 @@
1
+ ## VPS CHANGELOG
2
+
3
+ ### Version 0.2.0 (December 31, 2019)
4
+
5
+ * Provide ability to add both custom HTTP and HTTPS Nginx configs
6
+
7
+ ### Version 0.1.2 (September 29, 2019)
8
+
9
+ * Run postload tasks at the end of deployment
10
+ * Fix generating Rails and Rack Dockerfile (installing the correct Bundler version)
11
+ * Include configured :services in the generated docker-compose.yml file
12
+
13
+ ### Version 0.1.1 (August 12, 2019)
14
+
15
+ * Initial release
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2019 Paul Engel
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,107 @@
1
+ # VPS
2
+
3
+ Zero-config deployments of Plug, Phoenix, Rack and Rails apps on a clean Ubuntu server using Docker and Let's Encrypt
4
+
5
+ **NOTE: This is an experimental project**
6
+
7
+ ## Introduction
8
+
9
+ Despite of having my fair share of experience in software development, hosting web applications has never really been my strongest skill set. So as a real programmer, I want to use a command line interface for easy deployments of my web applications (using HTTPS or not).
10
+
11
+ Important for me is not want to be locked onto a specific hosting provider and also, I want the server to be dispensable because "I know that my CLI has got my back".
12
+
13
+ So enter the Ruby gem `VPS` which makes it able to deploy my web application on a totally clean Ubuntu server by roughly just executing five simple commands :muscle:
14
+
15
+ ## Installation
16
+
17
+ Run the following command to install `VPS`:
18
+
19
+ $ gem install "vps"
20
+
21
+ ## Usage
22
+
23
+ VPS is a command-line-interface, you can print help instructions:
24
+
25
+ $ vps help
26
+ Commands:
27
+ vps -v, [--version] # Show VPS version number
28
+ vps deploy HOST [TOOL] # Deploy web application to the server
29
+ vps domain # Manage upstream domains
30
+ vps domain add HOST:UPSTREAM DOMAIN [EMAIL] # Add domain to host upstream (email recommended for https)
31
+ vps domain help [COMMAND] # Describe subcommands or one specific subcommand
32
+ vps domain list HOST[:UPSTREAM] # List domains of host
33
+ vps domain remove HOST[:UPSTREAM] [DOMAIN] # Remove domain from host upstream
34
+ vps edit [HOST] # Edit the VPS configuration(s)
35
+ vps help [COMMAND] # Describe available commands or one specific command
36
+ vps init HOST # Execute an initial server setup
37
+ vps install HOST [TOOL] # Install software on the server
38
+ vps service # Manage host services
39
+ vps service add HOST [SERVICE] # Add service to host configuration
40
+ vps service help [COMMAND] # Describe subcommands or one specific subcommand
41
+ vps service list HOST # List services of host configuration
42
+ vps service remove HOST [SERVICE] # Remove service from host configuration
43
+ vps upstream # Manage host upstreams
44
+ vps upstream add HOST[:UPSTREAM] PATH # Add upstream to host configuration
45
+ vps upstream help [COMMAND] # Describe subcommands or one specific subcommand
46
+ vps upstream list HOST # List upstreams of host configuration
47
+ vps upstream remove HOST[:UPSTREAM] # Remove upstream from host configuration
48
+
49
+ ### Deploying a Plug / Phoenix / Rack / Rails application to a totally clean installed Ubuntu server
50
+
51
+ Let's say the SSH host is called `silver_surfer` and that the application is located at `~/Sources/spider_web`.
52
+
53
+ Just execute the following commands:
54
+
55
+ $ vps init silver_surfer
56
+ $ vps install silver_surfer docker
57
+ $ vps upstream add silver_surfer ~/Sources/spider_web
58
+ $ vps domain add silver_surfer:spider_web http://spider.web
59
+ $ vps deploy silver_surfer
60
+
61
+ Et voilà. Your awesome website is online, powered by Docker and Nginx! :D
62
+
63
+ ### Want to use a HTTPS domain?
64
+
65
+ No problem, just specify so and make sure you also pass a valid email address (which is recommended). During deployment `certbot` will be added to the docker compose config and the SSL certificates will be created using [`init-letsencrypt.sh`](https://github.com/archan937/vps/blob/master/templates/docker/init-letsencrypt.sh.erb).
66
+
67
+ $ vps init silver_surfer
68
+ $ vps install silver_surfer docker
69
+ $ vps upstream add silver_surfer ~/Sources/spider_web
70
+ $ vps domain add silver_surfer:spider_web https://spider.web your.valid@email-address.com
71
+ $ vps deploy silver_surfer
72
+
73
+ Cool, huh? :D
74
+
75
+ ### Want to add a (commonly used) service?
76
+
77
+ Easy. Just run `vps service add <yourhost>` (e.g. `vps service add silver_surfer`) and follow the instructions.
78
+
79
+ ## Credits
80
+
81
+ Thanks Philipp Medien (@pentacent_hq) for writing about using Nginx and Let's Encrypt with Docker:
82
+
83
+ https://medium.com/@pentacent / [the-blog-post](https://medium.com/@pentacent/nginx-and-lets-encrypt-with-docker-in-less-than-5-minutes-b4b8a60d3a71)
84
+
85
+ Thanks Dmitry Fedosov (@dimafeng) for writing about scripting Docker based deployments:
86
+
87
+ http://dimafeng.com / [the-blog-post](http://dimafeng.com/2015/10/17/docker-distribution)
88
+
89
+ ## TODO
90
+
91
+ * Add documentation about adding docker-compose and Nginx related configs plus adding pre- and postload tasks
92
+
93
+ ## Contact me
94
+
95
+ For support, remarks and requests, please mail me at [pm_engel@icloud.com](mailto:pm_engel@icloud.com).
96
+
97
+ ## License
98
+
99
+ Copyright (c) 2019 Paul Engel, released under the MIT license
100
+
101
+ http://github.com/archan937 – http://twitter.com/archan937 – pm_engel@icloud.com
102
+
103
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
104
+
105
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
106
+
107
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ task :default => :test
7
+
8
+ Rake::TestTask.new do |test|
9
+ test.pattern = "test/**/test_*.rb"
10
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.2
data/bin/vps ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "vps/cli"
5
+
6
+ begin
7
+ VPS::CLI.start
8
+ rescue Interrupt
9
+ puts "Abort."
10
+ end
@@ -0,0 +1,63 @@
1
+ ---
2
+ memcached:
3
+ restart: on-failure
4
+ mysql:
5
+ restart: on-failure
6
+ ports:
7
+ - 3306:3306
8
+ environment:
9
+ - MYSQL_DATABASE
10
+ - MYSQL_USER
11
+ - MYSQL_PASSWORD
12
+ - MYSQL_ROOT_PASSWORD
13
+ volumes:
14
+ - mysql:/var/lib/mysql
15
+ mariadb:
16
+ restart: on-failure
17
+ ports:
18
+ - 3306:3306
19
+ environment:
20
+ - MYSQL_DATABASE
21
+ - MYSQL_USER
22
+ - MYSQL_PASSWORD
23
+ - MYSQL_ROOT_PASSWORD
24
+ volumes:
25
+ - mariadb:/var/lib/mysql
26
+ postgres:
27
+ restart: on-failure
28
+ ports:
29
+ - 5432:5432
30
+ environment:
31
+ - POSTGRES_DB
32
+ - POSTGRES_USER
33
+ - POSTGRES_PASSWORD
34
+ volumes:
35
+ - postgres:/var/lib/postgres
36
+ mongo:
37
+ restart: on-failure
38
+ environment:
39
+ - MONGO_INITDB_DATABASE
40
+ - MONGO_INITDB_ROOT_USERNAME
41
+ - MONGO_INITDB_ROOT_PASSWORD
42
+ volumes:
43
+ - mongo-db:/data/db
44
+ - mongo-config:/data/configdb
45
+ redis:
46
+ restart: on-failure
47
+ volumes:
48
+ - redis:/data
49
+ rabbitmq:
50
+ restart: on-failure
51
+ environment:
52
+ - RABBITMQ_ERLANG_COOKIE
53
+ - RABBITMQ_DEFAULT_USER
54
+ - RABBITMQ_DEFAULT_PASS
55
+ volumes:
56
+ - rabbitmq-etc:/etc/rabbitmq
57
+ - rabbitmq-lib:/var/lib/rabbitmq
58
+ - rabbitmq-logs:/var/log/rabbitmq
59
+ clickhouse:
60
+ image: yandex/clickhouse-server
61
+ restart: on-failure
62
+ volumes:
63
+ - clickhouse:/var/lib/clickhouse
@@ -0,0 +1,88 @@
1
+ require "vps/version"
2
+
3
+ module VPS
4
+ extend self
5
+
6
+ ROOT = File.expand_path("#{__FILE__}/../..")
7
+ PLAYBOOKS = "#{ROOT}/playbooks"
8
+ TEMPLATES = "#{ROOT}/templates"
9
+
10
+ def config_path(host, path = "config.yml")
11
+ File.expand_path("~/.vps/#{host}/#{path}")
12
+ end
13
+
14
+ def read_template(path)
15
+ File.read("#{TEMPLATES}/#{path}")
16
+ end
17
+
18
+ def read_config(host, key = nil)
19
+ config =
20
+ if File.exists?(path = config_path(host))
21
+ YAML.load_file(path)
22
+ elsif key.nil?
23
+ {
24
+ :user => nil,
25
+ :tool => nil,
26
+ :release_path => nil,
27
+ :services => nil,
28
+ :upstreams => nil,
29
+ :volumes => nil,
30
+ :preload => nil,
31
+ :postload => nil
32
+ }
33
+ end
34
+
35
+ if config
36
+ config = with_indifferent_access(config)
37
+ if key
38
+ with_indifferent_access(config[key])
39
+ else
40
+ config[:services] ||= {}
41
+ config[:upstreams] ||= []
42
+ config[:volumes] ||= []
43
+ config
44
+ end
45
+ end
46
+ end
47
+
48
+ def write_config(host, changes)
49
+ config = read_config(host) || {}
50
+ changed = false
51
+
52
+ %w(services upstreams volumes).each do |key|
53
+ value = changes[key]
54
+ changes[key] = nil if value && value.empty?
55
+ end
56
+
57
+ changes.each do |key, value|
58
+ if !config.include?(key) || (config[key] != value)
59
+ config[key] = value
60
+ changed = true
61
+ end
62
+ end
63
+
64
+ if changed
65
+ path = config_path(host)
66
+ config = JSON.parse(config.to_json)
67
+ FileUtils.mkdir_p(File.dirname(path))
68
+ File.write(path, config.to_yaml)
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def with_indifferent_access(object)
75
+ case object
76
+ when Hash
77
+ object.inject({}.with_indifferent_access) do |hash, (key, value)|
78
+ hash[key] = with_indifferent_access(value)
79
+ hash
80
+ end
81
+ when Array
82
+ object.collect{|item| with_indifferent_access(item)}
83
+ else
84
+ object
85
+ end
86
+ end
87
+
88
+ end
@@ -0,0 +1,62 @@
1
+ require "fileutils"
2
+ require "json"
3
+ require "yaml"
4
+ require "ostruct"
5
+ require "uri"
6
+ require "thor"
7
+ require "erubis"
8
+ require "inquirer"
9
+ require "net/http"
10
+ require "net/ssh"
11
+
12
+ require "active_support/dependencies/autoload"
13
+ require "active_support/core_ext/string/inflections"
14
+ require "active_support/core_ext/hash"
15
+ require "active_support/number_helper"
16
+
17
+ require "vps"
18
+ require "vps/core_ext/string"
19
+ require "vps/core_ext/ostruct"
20
+ require "vps/cli/service"
21
+ require "vps/cli/upstream"
22
+ require "vps/cli/domain"
23
+ require "vps/cli/playbook"
24
+
25
+ module VPS
26
+ class CLI < Thor
27
+
28
+ class Error < StandardError; end
29
+
30
+ Playbook.all.each do |playbook|
31
+ desc playbook.usage, playbook.description
32
+ method_options playbook.options if playbook.options
33
+ define_method playbook.command do |*args|
34
+ start = Time.now
35
+ playbook.run!(args, options)
36
+ puts "\nDone. ".cyan + "#{(Time.now - start).round(3)}s".gray
37
+ end
38
+ end
39
+
40
+ desc "edit [HOST]", "Edit the VPS configuration(s)"
41
+ def edit(host = nil)
42
+ `#{ENV["EDITOR"]} #{VPS.config_path(host, "")}`
43
+ end
44
+
45
+ register(Upstream, "upstream", "upstream", "Manage host upstreams")
46
+ register(Service, "service", "service", "Manage host services")
47
+ register(Domain, "domain", "domain", "Manage upstream domains")
48
+
49
+ desc "-v, [--version]", "Show VPS version number"
50
+ map %w(-v --version) => :version
51
+ def version
52
+ puts "vps #{VPS::VERSION}"
53
+ end
54
+
55
+ private
56
+
57
+ def method_missing(method, *_args)
58
+ raise Error, "Unrecognized command \"#{method}\". Please consult `vps help`."
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,59 @@
1
+ module VPS
2
+ class CLI < Thor
3
+ class Domain < Thor
4
+
5
+ desc "add HOST:UPSTREAM DOMAIN [EMAIL]", "Add domain to host upstream (email recommended for https)"
6
+ def add(host_and_upstream, domain, email = nil)
7
+ return unless domain.match(/^https?:\/\/([a-z0-9\-]{2,}\.)+[a-z]{2,}$/)
8
+
9
+ host, name = host_and_upstream.split(":")
10
+ config = VPS.read_config(host)
11
+
12
+ if (upstream = config[:upstreams].detect{|upstream| upstream[:name] == name})
13
+ upstream[:email] = email if email
14
+ upstream[:domains].push(domain).uniq!
15
+ VPS.write_config(host, config)
16
+ end
17
+ end
18
+
19
+ desc "remove HOST[:UPSTREAM] [DOMAIN]", "Remove domain from host upstream"
20
+ def remove(host_and_upstream, domain = nil)
21
+ host, name = host_and_upstream.split(":")
22
+ config = VPS.read_config(host)
23
+
24
+ unless name
25
+ list = config[:upstreams].collect{|upstream| upstream[:name]}.sort
26
+ name = list[Ask.list("From which upstream do you want to remove a domain?", list)]
27
+ end
28
+
29
+ if (upstream = config[:upstreams].detect{|upstream| upstream[:name] == name})
30
+ unless domain
31
+ list = upstream[:domains]
32
+ domain = list[Ask.list("Which domain do you want to remove?", list)]
33
+ end
34
+
35
+ if upstream[:domains].reject!{|x| x == domain}
36
+ VPS.write_config(host, config)
37
+ end
38
+ end
39
+ end
40
+
41
+ desc "list HOST[:UPSTREAM]", "List domains of host"
42
+ def list(host_and_optional_upstream)
43
+ host, name = host_and_optional_upstream.split(":")
44
+ config = VPS.read_config(host)
45
+
46
+ domains = config[:upstreams].collect do |upstream|
47
+ if name.nil? || upstream[:name] == name
48
+ upstream[:domains]
49
+ .collect{|domain| " ~> #{domain}"}
50
+ .unshift("* #{upstream[:name]}:")
51
+ end
52
+ end.flatten.compact
53
+
54
+ puts domains
55
+ end
56
+
57
+ end
58
+ end
59
+ end