vps 0.2.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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +20 -0
- data/README.md +107 -0
- data/Rakefile +10 -0
- data/VERSION +1 -0
- data/bin/vps +10 -0
- data/config/services.yml +63 -0
- data/lib/vps.rb +88 -0
- data/lib/vps/cli.rb +62 -0
- data/lib/vps/cli/domain.rb +59 -0
- data/lib/vps/cli/playbook.rb +110 -0
- data/lib/vps/cli/playbook/state.rb +170 -0
- data/lib/vps/cli/playbook/tasks.rb +262 -0
- data/lib/vps/cli/service.rb +96 -0
- data/lib/vps/cli/upstream.rb +95 -0
- data/lib/vps/core_ext/ostruct.rb +17 -0
- data/lib/vps/core_ext/string.rb +33 -0
- data/lib/vps/version.rb +7 -0
- data/playbooks/deploy.yml +43 -0
- data/playbooks/deploy/docker.yml +96 -0
- data/playbooks/init.yml +12 -0
- data/playbooks/init/ubuntu-18.04.yml +110 -0
- data/playbooks/install.yml +18 -0
- data/playbooks/install/docker/ubuntu-18.04.yml +35 -0
- data/script/console +7 -0
- data/templates/docker/data/nginx/app.conf.erb +69 -0
- data/templates/docker/docker-compose.yml.erb +58 -0
- data/templates/docker/upstream/Dockerfile.phoenix.erb +13 -0
- data/templates/docker/upstream/Dockerfile.plug.erb +13 -0
- data/templates/docker/upstream/Dockerfile.rack.erb +16 -0
- data/templates/docker/upstream/Dockerfile.rails.erb +18 -0
- data/templates/docker/upstream/init-letsencrypt.sh.erb +76 -0
- data/test/test_helper.rb +12 -0
- data/test/test_helper/coverage.rb +8 -0
- data/test/unit/test_version.rb +15 -0
- data/vps.gemspec +29 -0
- metadata +214 -0
@@ -0,0 +1,96 @@
|
|
1
|
+
module VPS
|
2
|
+
class CLI < Thor
|
3
|
+
class Service < Thor
|
4
|
+
|
5
|
+
SERVICES = "#{VPS::ROOT}/config/services.yml"
|
6
|
+
|
7
|
+
desc "add HOST [SERVICE]", "Add service to host configuration"
|
8
|
+
def add(host, service = nil)
|
9
|
+
config = VPS.read_config(host)
|
10
|
+
|
11
|
+
unless service
|
12
|
+
list = services.keys.sort - config[:services].keys
|
13
|
+
service = list[Ask.list("Which service to you want to add?", list)]
|
14
|
+
end
|
15
|
+
|
16
|
+
if !config[:services].include?(service) && (yml = services[service])
|
17
|
+
list = tags(yml[:image] || "library/#{service}")
|
18
|
+
tag = list[Ask.list("Choose which tag to use", list)]
|
19
|
+
image = "#{yml[:image] || service}:#{tag}"
|
20
|
+
|
21
|
+
yml, volumes = finalize_config(yml)
|
22
|
+
config[:services][service] = {:image => image}.merge(yml)
|
23
|
+
config[:volumes].concat(volumes).uniq!
|
24
|
+
|
25
|
+
VPS.write_config(host, config)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "remove HOST [SERVICE]", "Remove service from host configuration"
|
30
|
+
def remove(host, service = nil)
|
31
|
+
config = VPS.read_config(host)
|
32
|
+
|
33
|
+
unless service
|
34
|
+
list = config[:services].keys.sort
|
35
|
+
service = list[Ask.list("Which service do you want to remove?", list)]
|
36
|
+
end
|
37
|
+
|
38
|
+
if config[:services].delete(service)
|
39
|
+
VPS.write_config(host, config)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "list HOST", "List services of host configuration"
|
44
|
+
def list(host)
|
45
|
+
config = VPS.read_config(host)
|
46
|
+
|
47
|
+
services = config[:services].collect do |service, yml|
|
48
|
+
"* #{yml[:image]}"
|
49
|
+
end.sort
|
50
|
+
|
51
|
+
puts services
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def services
|
57
|
+
@services ||= with_indifferent_access(YAML.load_file(SERVICES))
|
58
|
+
end
|
59
|
+
|
60
|
+
def tags(image)
|
61
|
+
uri = URI.parse("https://registry.hub.docker.com/v2/repositories/#{image}/tags/")
|
62
|
+
tags = JSON.parse(Net::HTTP.get(uri))["results"]
|
63
|
+
tags.collect{|tag| tag["name"]}.sort{|a, b| (a == "latest") ? -1 : (b <=> a)}
|
64
|
+
end
|
65
|
+
|
66
|
+
def with_indifferent_access(object)
|
67
|
+
case object
|
68
|
+
when Hash
|
69
|
+
object.inject({}.with_indifferent_access) do |hash, (key, value)|
|
70
|
+
hash[key] = with_indifferent_access(value)
|
71
|
+
hash
|
72
|
+
end
|
73
|
+
when Array
|
74
|
+
object.collect{|item| with_indifferent_access(item)}
|
75
|
+
else
|
76
|
+
object
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def finalize_config(config)
|
81
|
+
config.delete(:image)
|
82
|
+
if config[:environment]
|
83
|
+
config[:environment] = config[:environment].inject({}) do |env, variable|
|
84
|
+
env[variable] = Ask.input(variable)
|
85
|
+
env
|
86
|
+
end
|
87
|
+
end
|
88
|
+
volumes = (config[:volumes] || []).collect do |volume|
|
89
|
+
volume.split(":")[0]
|
90
|
+
end
|
91
|
+
[config, volumes]
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module VPS
|
2
|
+
class CLI < Thor
|
3
|
+
class Upstream < Thor
|
4
|
+
|
5
|
+
desc "add HOST[:UPSTREAM] PATH", "Add upstream to host configuration"
|
6
|
+
def add(host_and_optional_upstream, path)
|
7
|
+
host, name = host_and_optional_upstream.split(":")
|
8
|
+
config = VPS.read_config(host)
|
9
|
+
path = File.expand_path(path)
|
10
|
+
|
11
|
+
unless config[:upstreams].any?{|upstream| upstream[:name] == name}
|
12
|
+
spec = derive_upstream(path)
|
13
|
+
spec[:nginx] ||= {
|
14
|
+
:http => nil,
|
15
|
+
:https => nil
|
16
|
+
}
|
17
|
+
config[:upstreams].push(spec.merge({
|
18
|
+
:name => name || File.basename(path),
|
19
|
+
:path => path,
|
20
|
+
:domains => [],
|
21
|
+
:email => nil,
|
22
|
+
:compose => nil
|
23
|
+
}))
|
24
|
+
VPS.write_config(host, config)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "remove HOST[:UPSTREAM]", "Remove upstream from host configuration"
|
29
|
+
def remove(host_and_optional_upstream)
|
30
|
+
host, name = host_and_optional_upstream.split(":")
|
31
|
+
config = VPS.read_config(host)
|
32
|
+
|
33
|
+
unless name
|
34
|
+
list = config[:upstreams].collect{|upstream| upstream[:name]}.sort
|
35
|
+
name = list[Ask.list("Which upstream do you want to remove?", list)]
|
36
|
+
end
|
37
|
+
|
38
|
+
if config[:upstreams].reject!{|upstream| upstream[:name] == name}
|
39
|
+
VPS.write_config(host, config)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "list HOST", "List upstreams of host configuration"
|
44
|
+
def list(host)
|
45
|
+
config = VPS.read_config(host)
|
46
|
+
|
47
|
+
upstreams = config[:upstreams].collect do |upstream|
|
48
|
+
"* #{upstream[:name]} (#{upstream[:path].gsub(Dir.home, "~")})"
|
49
|
+
end.sort
|
50
|
+
|
51
|
+
puts upstreams
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def derive_upstream(path)
|
57
|
+
if Dir["#{path}/mix.exs"].any?
|
58
|
+
elixir = <<-ELIXIR
|
59
|
+
Application.started_applications()
|
60
|
+
|> Enum.reduce([], fn {name, _desc, _version}, acc ->
|
61
|
+
if(name in [:phoenix, :plug], do: [name | acc], else: acc)
|
62
|
+
end)
|
63
|
+
|> Enum.sort()
|
64
|
+
|> Enum.at(0)
|
65
|
+
|> IO.puts()
|
66
|
+
ELIXIR
|
67
|
+
type = `cd #{path} && mix run -e "#{elixir.strip.gsub(/\n\s+/, " ")}" | tail -n 1`.strip
|
68
|
+
{
|
69
|
+
type: type,
|
70
|
+
elixir_version: `cd #{path} && mix run -e "System.version() |> IO.puts()" | tail -n 1`.strip,
|
71
|
+
port: (type == "phoenix") ? 4000 : `cd #{path} && mix run -e ":ranch.info |> hd() |> elem(0) |> :ranch.get_port() |> IO.puts()" | tail -n 1`.strip.to_i
|
72
|
+
}
|
73
|
+
elsif Dir["#{path}/Gemfile"].any?
|
74
|
+
lines = `cd #{path} && BUNDLE_GEMFILE=#{path}/Gemfile bundle list`.split("\n")
|
75
|
+
type = %w(rails rack).detect{|gem| lines.any?{|line| line.include?("* #{gem} (")}}
|
76
|
+
{
|
77
|
+
type: type,
|
78
|
+
ruby_version: `$SHELL -l -c 'cd #{path} && ruby -e "puts RUBY_VERSION"'`.strip,
|
79
|
+
bundler_version: `$SHELL -l -c 'cd #{path} && bundle -v'`.split.last,
|
80
|
+
port: (type == "rails" ? 3000 : 9292) # :'(
|
81
|
+
}.tap do |spec|
|
82
|
+
if type == "rails"
|
83
|
+
spec[:nginx] = {
|
84
|
+
root: "/opt/app/public",
|
85
|
+
try_files: true,
|
86
|
+
proxy_redirect: "off"
|
87
|
+
}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class OpenStruct
|
2
|
+
def self.to_hash(object, hash = {})
|
3
|
+
case object
|
4
|
+
when OpenStruct then
|
5
|
+
object.each_pair do |key, value|
|
6
|
+
hash[key.to_s] = to_hash(value)
|
7
|
+
end
|
8
|
+
hash
|
9
|
+
when Array then
|
10
|
+
object.collect do |value|
|
11
|
+
to_hash(value)
|
12
|
+
end
|
13
|
+
else
|
14
|
+
object
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class String
|
2
|
+
|
3
|
+
COLORS = {
|
4
|
+
:red => 31,
|
5
|
+
:green => 32,
|
6
|
+
:yellow => 33,
|
7
|
+
:blue => 34,
|
8
|
+
:magenta => 35,
|
9
|
+
:cyan => 36,
|
10
|
+
:white => 39,
|
11
|
+
:gray => 90
|
12
|
+
}
|
13
|
+
|
14
|
+
COLORS.keys.each do |color|
|
15
|
+
define_method color do
|
16
|
+
colorize color
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def indent(n, string = " ")
|
21
|
+
split("\n").collect do |line|
|
22
|
+
line.empty? ? line : "#{string * n}#{line}"
|
23
|
+
end.join("\n")
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def colorize(color)
|
29
|
+
color = COLORS[color]
|
30
|
+
"\033[0;#{color}m#{self}\033[0m"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/lib/vps/version.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
---
|
2
|
+
description:
|
3
|
+
Deploy web application to the server
|
4
|
+
usage:
|
5
|
+
deploy HOST [TOOL]
|
6
|
+
arguments:
|
7
|
+
- host
|
8
|
+
- tool
|
9
|
+
tasks:
|
10
|
+
- task: ensure
|
11
|
+
argument: user
|
12
|
+
fallbacks:
|
13
|
+
- task: read_config
|
14
|
+
key: user
|
15
|
+
- description: Obtaining remote user
|
16
|
+
task: remote_execute
|
17
|
+
command: whoami
|
18
|
+
- task: ensure
|
19
|
+
argument: tool
|
20
|
+
fallbacks:
|
21
|
+
- task: read_config
|
22
|
+
key: tool
|
23
|
+
- description: Choose which deploy tool to use
|
24
|
+
task: select
|
25
|
+
question: Which deployment tool do you want to use?
|
26
|
+
options: << playbooks >>
|
27
|
+
- task: ensure
|
28
|
+
argument: release_path
|
29
|
+
fallbacks:
|
30
|
+
- task: read_config
|
31
|
+
key: release_path
|
32
|
+
- description: Specify which directory to deploy to
|
33
|
+
task: input
|
34
|
+
question: Enter release path on the server
|
35
|
+
default: ~/app
|
36
|
+
- task: write_config
|
37
|
+
config:
|
38
|
+
user: << user >>
|
39
|
+
tool: << tool >>
|
40
|
+
release_path: << release_path >>
|
41
|
+
- task: obtain_config
|
42
|
+
- task: playbook
|
43
|
+
playbook: deploy/{{ tool }}
|
@@ -0,0 +1,96 @@
|
|
1
|
+
---
|
2
|
+
source:
|
3
|
+
- http://dimafeng.com/2015/10/17/docker-distribution/
|
4
|
+
constants:
|
5
|
+
config_path: ~/.vps/{{ host }}
|
6
|
+
docker_compose_file: "{{ config_path }}/docker-compose.yml"
|
7
|
+
nginx_conf_file: "{{ config_path }}/data/nginx/app.conf"
|
8
|
+
tasks:
|
9
|
+
- task: loop
|
10
|
+
through: << upstreams >>
|
11
|
+
as: upstream
|
12
|
+
run:
|
13
|
+
- task: generate_file
|
14
|
+
template: docker/upstream/Dockerfile.{{ upstream.type }}.erb
|
15
|
+
target: "{{ config_path }}/{{ upstream.name }}/Dockerfile"
|
16
|
+
- description: Building '{{ upstream.name }}' image
|
17
|
+
task: execute
|
18
|
+
command: docker build --no-cache -f {{ config_path }}/{{ upstream.name }}/Dockerfile -t {{ upstream.name }} {{ upstream.path }}
|
19
|
+
- description: Saving docker images as .gzip file
|
20
|
+
task: loop
|
21
|
+
through: << upstreams >>
|
22
|
+
as: upstream
|
23
|
+
run:
|
24
|
+
- task: execute
|
25
|
+
command: docker save {{ upstream.name }} | gzip > /tmp/{{ upstream.name }}.gzip
|
26
|
+
- description: Uploading images
|
27
|
+
task: loop
|
28
|
+
through: << upstreams >>
|
29
|
+
as: upstream
|
30
|
+
run:
|
31
|
+
- task: upload
|
32
|
+
file: /tmp/{{ upstream.name }}.gzip
|
33
|
+
- description: Stopping current containers
|
34
|
+
task: remote_execute
|
35
|
+
command: cd {{ release_path }} && docker-compose stop
|
36
|
+
- description: Removing current containers
|
37
|
+
task: loop
|
38
|
+
through: << upstreams >>
|
39
|
+
as: upstream
|
40
|
+
run:
|
41
|
+
- task: remote_execute
|
42
|
+
command: docker container rm $(docker container ls -f ancestor={{ upstream.name }} -aq)
|
43
|
+
- description: Removing current images
|
44
|
+
task: loop
|
45
|
+
through: << upstreams >>
|
46
|
+
as: upstream
|
47
|
+
run:
|
48
|
+
- task: remote_execute
|
49
|
+
command: docker image rm {{ upstream.name }}
|
50
|
+
- task: run_tasks
|
51
|
+
tasks: << preload >>
|
52
|
+
- description: Unzipping and loading docker images
|
53
|
+
task: loop
|
54
|
+
through: << upstreams >>
|
55
|
+
as: upstream
|
56
|
+
run:
|
57
|
+
- task: remote_execute
|
58
|
+
command: gunzip < /tmp/{{ upstream.name }}.gzip | docker load
|
59
|
+
- task: generate_file
|
60
|
+
template: docker/docker-compose.yml.erb
|
61
|
+
target: "{{ docker_compose_file }}"
|
62
|
+
- description: Uploading docker-compose.yml
|
63
|
+
task: upload
|
64
|
+
file: "{{ docker_compose_file }}"
|
65
|
+
remote_path: "{{ release_path }}/docker-compose.yml"
|
66
|
+
- task: generate_file
|
67
|
+
template: docker/data/nginx/app.conf.erb
|
68
|
+
target: "{{ nginx_conf_file }}"
|
69
|
+
- description: Uploading Nginx config
|
70
|
+
task: upload
|
71
|
+
file: "{{ nginx_conf_file }}"
|
72
|
+
remote_path: "{{ release_path }}/data/nginx/app.conf"
|
73
|
+
- task: loop
|
74
|
+
through: << upstreams >>
|
75
|
+
as: upstream
|
76
|
+
run:
|
77
|
+
- task: generate_file
|
78
|
+
template: docker/upstream/init-letsencrypt.sh.erb
|
79
|
+
target: "{{ config_path }}/{{ upstream.name }}/init-letsencrypt.sh"
|
80
|
+
- description: Uploading Let’s Encrypt script
|
81
|
+
task: upload
|
82
|
+
file: "{{ config_path }}/{{ upstream.name }}/init-letsencrypt.sh"
|
83
|
+
remote_path: "{{ release_path }}/init-letsencrypt/{{ upstream.name }}.sh"
|
84
|
+
- description: Install SSL certificates (if required)
|
85
|
+
task: remote_execute
|
86
|
+
command:
|
87
|
+
- chmod +x {{ release_path }}/init-letsencrypt/{{ upstream.name }}.sh
|
88
|
+
- cd {{ release_path }} && if [ ! -d "data/certbot/conf/live/{{ domain:upstream }}" ]; then yes Y | sudo ./init-letsencrypt/{{ upstream.name }}.sh; fi
|
89
|
+
- description: Starting containers
|
90
|
+
task: remote_execute
|
91
|
+
command: cd {{ release_path }} && docker-compose up {{ up }} -d
|
92
|
+
- description: Checking running docker images
|
93
|
+
task: remote_execute
|
94
|
+
command: docker ps
|
95
|
+
- task: run_tasks
|
96
|
+
tasks: << postload >>
|
data/playbooks/init.yml
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
---
|
2
|
+
source:
|
3
|
+
- https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04
|
4
|
+
- https://github.com/jasonheecs/ubuntu-server-setup/blob/master/setupLibrary.sh
|
5
|
+
- https://www.linode.com/stackscripts/view/1
|
6
|
+
- https://gist.github.com/parente/0227cfbbd8de1ce8ad05
|
7
|
+
tasks:
|
8
|
+
- description: Updating server with latest packages
|
9
|
+
task: remote_execute
|
10
|
+
command: apt-get update && apt-get upgrade -y
|
11
|
+
- description: Choose which tasks to execute
|
12
|
+
task: multiselect
|
13
|
+
question: Which tasks do you want to execute?
|
14
|
+
options:
|
15
|
+
set_server_hostname: Set server hostname
|
16
|
+
add_sudo_user: Add new user with sudo access
|
17
|
+
disable_password_authentication: Disable password authentication to the server
|
18
|
+
setup_uncomplicated_firewall: Setup Uncomplicated FireWall
|
19
|
+
setup_timezone: Setup server timezone
|
20
|
+
install_network_time_protocol: Install Network Time Protocol
|
21
|
+
deny_root_login: Deny root login to the server
|
22
|
+
- description: Setting the server hostname
|
23
|
+
task: when
|
24
|
+
boolean: set_server_hostname
|
25
|
+
run:
|
26
|
+
- task: input
|
27
|
+
question: Enter hostname for the server
|
28
|
+
as: hostname
|
29
|
+
- task: remote_execute
|
30
|
+
command:
|
31
|
+
- echo '{{ hostname }}' > /etc/hostname
|
32
|
+
- hostname -F /etc/hostname
|
33
|
+
- description: Creating new sudo user
|
34
|
+
task: when
|
35
|
+
boolean: add_sudo_user
|
36
|
+
run:
|
37
|
+
- task: input
|
38
|
+
question: Enter username for the new sudo user
|
39
|
+
as: username
|
40
|
+
- task: input
|
41
|
+
question: Enter location of your public SSH key
|
42
|
+
default: ~/.ssh/id_rsa.pub
|
43
|
+
as: public_key
|
44
|
+
- task: remote_execute
|
45
|
+
command: adduser --disabled-password --gecos '' {{ username }}
|
46
|
+
- description: Granting administrative privileges
|
47
|
+
task: remote_execute
|
48
|
+
command: usermod -aG sudo {{ username }}
|
49
|
+
- description: Copying public SSH key
|
50
|
+
task: execute
|
51
|
+
command: cat {{ public_key }}
|
52
|
+
as: public_key
|
53
|
+
- description: Installing public SSH key on server
|
54
|
+
task: remote_execute
|
55
|
+
user: "{{ username }}"
|
56
|
+
command:
|
57
|
+
- mkdir -p ~/.ssh
|
58
|
+
- touch ~/.ssh/authorized_keys
|
59
|
+
- echo {{{ public_key }}} >> ~/.ssh/authorized_keys
|
60
|
+
- chmod 700 ~/.ssh
|
61
|
+
- chmod 600 ~/.ssh/authorized_keys
|
62
|
+
- description: Disabling sudo password for new user
|
63
|
+
task: remote_execute
|
64
|
+
command:
|
65
|
+
- cp /etc/sudoers /etc/sudoers.bak
|
66
|
+
- sh -c 'echo "{{ username }} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers'
|
67
|
+
- description: Disabling password authentication
|
68
|
+
task: when
|
69
|
+
boolean: disable_password_authentication
|
70
|
+
run:
|
71
|
+
- task: remote_execute
|
72
|
+
command:
|
73
|
+
- sed -i 's/#*ChallengeResponseAuthentication.*/ChallengeResponseAuthentication no/' /etc/ssh/sshd_config
|
74
|
+
- sed -i 's/#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
|
75
|
+
- service ssh restart
|
76
|
+
- description: Denying root login to the server
|
77
|
+
task: when
|
78
|
+
boolean: deny_root_login
|
79
|
+
run:
|
80
|
+
- task: remote_execute
|
81
|
+
command:
|
82
|
+
- sed -i 's/#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
|
83
|
+
- service ssh restart
|
84
|
+
- description: Setting up Uncomplicated FireWall
|
85
|
+
task: when
|
86
|
+
boolean: setup_uncomplicated_firewall
|
87
|
+
run:
|
88
|
+
- task: remote_execute
|
89
|
+
command:
|
90
|
+
- ufw allow OpenSSH
|
91
|
+
- ufw allow http
|
92
|
+
- ufw allow https
|
93
|
+
- yes y | ufw enable
|
94
|
+
- description: Setting up the server timezone
|
95
|
+
task: when
|
96
|
+
boolean: setup_timezone
|
97
|
+
run:
|
98
|
+
- task: remote_execute
|
99
|
+
command:
|
100
|
+
export timezone=`wget -qO - http://geoip.ubuntu.com/lookup | sed -n -e 's/.*<TimeZone>\(.*\)<\/TimeZone>.*/\1/p'` &&
|
101
|
+
timedatectl set-timezone $timezone &&
|
102
|
+
echo $timezone
|
103
|
+
- description: Installing Network Time Protocol
|
104
|
+
task: when
|
105
|
+
boolean: install_network_time_protocol
|
106
|
+
run:
|
107
|
+
- task: remote_execute
|
108
|
+
command:
|
109
|
+
- apt-get update
|
110
|
+
- apt-get --assume-yes install ntp
|