kitsune-kit 0.4.1 โ 0.6.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 +4 -4
- data/CHANGELOG.md +143 -2
- data/CONTRIBUTING.md +83 -0
- data/README.md +112 -135
- data/SECURITY.md +25 -0
- data/bin/kit +4 -1
- data/docs/architecture/decisions/0001-product-boundary.md +23 -0
- data/docs/architecture/decisions/0002-core-and-interfaces.md +23 -0
- data/docs/architecture/decisions/0003-configuration-state-and-secrets.md +23 -0
- data/docs/architecture/decisions/0004-supported-platforms.md +19 -0
- data/docs/architecture/decisions/0005-operation-semantics.md +19 -0
- data/docs/architecture/decisions/0006-tui-backend.md +21 -0
- data/docs/architecture/decisions/0007-compose-customization.md +30 -0
- data/docs/architecture/provider-adapters.md +49 -0
- data/docs/architecture.md +93 -0
- data/docs/commands.md +224 -0
- data/docs/configuration.md +226 -0
- data/docs/getting-started.md +133 -0
- data/docs/providers/digitalocean.md +52 -0
- data/docs/releasing.md +43 -0
- data/docs/roadmap.md +46 -0
- data/docs/security-audit.md +130 -0
- data/docs/security.md +116 -0
- data/docs/services/compose.md +164 -0
- data/docs/services/postgres.md +78 -0
- data/docs/services/redis.md +53 -0
- data/docs/testing.md +200 -0
- data/docs/troubleshooting.md +123 -0
- data/docs/tui.md +57 -0
- data/lib/kitsune/kit/adapters/confirming_host_key_verifier.rb +42 -0
- data/lib/kitsune/kit/adapters/digitalocean_provider.rb +217 -0
- data/lib/kitsune/kit/adapters/fake_clock.rb +34 -0
- data/lib/kitsune/kit/adapters/fake_provider.rb +107 -0
- data/lib/kitsune/kit/adapters/fake_reporter.rb +34 -0
- data/lib/kitsune/kit/adapters/fake_secret_store.rb +35 -0
- data/lib/kitsune/kit/adapters/fake_state_store.rb +91 -0
- data/lib/kitsune/kit/adapters/fake_transport.rb +56 -0
- data/lib/kitsune/kit/adapters/net_ssh_transport.rb +155 -0
- data/lib/kitsune/kit/adapters/provider.rb +34 -0
- data/lib/kitsune/kit/adapters/transport.rb +22 -0
- data/lib/kitsune/kit/adapters/transport_factory.rb +116 -0
- data/lib/kitsune/kit/application.rb +170 -0
- data/lib/kitsune/kit/cancellation.rb +21 -0
- data/lib/kitsune/kit/cli.rb +879 -64
- data/lib/kitsune/kit/clock.rb +11 -0
- data/lib/kitsune/kit/configuration.rb +577 -0
- data/lib/kitsune/kit/errors.rb +98 -0
- data/lib/kitsune/kit/events.rb +55 -0
- data/lib/kitsune/kit/operations/ensure_dns_records.rb +165 -0
- data/lib/kitsune/kit/operations/ensure_server.rb +135 -0
- data/lib/kitsune/kit/operations/ensure_service.rb +302 -0
- data/lib/kitsune/kit/operations/remote_script.rb +214 -0
- data/lib/kitsune/kit/operations/service_backup.rb +71 -0
- data/lib/kitsune/kit/operations/service_files.rb +131 -0
- data/lib/kitsune/kit/operations/service_firewall.rb +190 -0
- data/lib/kitsune/kit/operations/service_state.rb +59 -0
- data/lib/kitsune/kit/plan.rb +72 -0
- data/lib/kitsune/kit/reporters/human.rb +89 -0
- data/lib/kitsune/kit/reporters/json.rb +65 -0
- data/lib/kitsune/kit/reporters/reporter.rb +11 -0
- data/lib/kitsune/kit/result.rb +28 -0
- data/lib/kitsune/kit/run_journal.rb +102 -0
- data/lib/kitsune/kit/run_logger.rb +37 -0
- data/lib/kitsune/kit/scripts/docker.sh +142 -0
- data/lib/kitsune/kit/scripts/firewall.sh +210 -0
- data/lib/kitsune/kit/scripts/metrics.sh +59 -0
- data/lib/kitsune/kit/scripts/ssh.sh +95 -0
- data/lib/kitsune/kit/scripts/swap.sh +86 -0
- data/lib/kitsune/kit/scripts/unattended.sh +86 -0
- data/lib/kitsune/kit/scripts/user.sh +114 -0
- data/lib/kitsune/kit/secret_filter.rb +54 -0
- data/lib/kitsune/kit/secret_store.rb +32 -0
- data/lib/kitsune/kit/secret_stores/store.rb +12 -0
- data/lib/kitsune/kit/service_compose.rb +272 -0
- data/lib/kitsune/kit/state_store.rb +158 -0
- data/lib/kitsune/kit/state_stores/store.rb +15 -0
- data/lib/kitsune/kit/tui/actions.rb +72 -0
- data/lib/kitsune/kit/tui/application.rb +35 -0
- data/lib/kitsune/kit/tui/controller.rb +162 -0
- data/lib/kitsune/kit/tui/renderer.rb +134 -0
- data/lib/kitsune/kit/tui/state.rb +18 -0
- data/lib/kitsune/kit/tui/store.rb +88 -0
- data/lib/kitsune/kit/tui/terminal.rb +95 -0
- data/lib/kitsune/kit/version.rb +1 -1
- data/lib/kitsune/kit/workflows/apply_plan.rb +145 -0
- data/lib/kitsune/kit/workflows/base.rb +31 -0
- data/lib/kitsune/kit/workflows/build_plan.rb +33 -0
- data/lib/kitsune/kit/workflows/destroy_server.rb +84 -0
- data/lib/kitsune/kit/workflows/doctor.rb +225 -0
- data/lib/kitsune/kit/workflows/eject_compose.rb +82 -0
- data/lib/kitsune/kit/workflows/environment_selection.rb +70 -0
- data/lib/kitsune/kit/workflows/import_server.rb +100 -0
- data/lib/kitsune/kit/workflows/initialize_project.rb +129 -0
- data/lib/kitsune/kit/workflows/inspect_environment.rb +44 -0
- data/lib/kitsune/kit/workflows/rollback.rb +54 -0
- data/lib/kitsune/kit/workflows/support_bundle.rb +82 -0
- data/lib/kitsune/kit.rb +42 -2
- metadata +125 -79
- data/.rspec +0 -3
- data/Rakefile +0 -8
- data/kitsune-kit-logo.jpg +0 -0
- data/lib/kitsune/blueprints/.env.template +0 -31
- data/lib/kitsune/blueprints/docker/postgres.yml +0 -27
- data/lib/kitsune/blueprints/docker/redis.yml +0 -23
- data/lib/kitsune/blueprints/kit.env.template +0 -1
- data/lib/kitsune/kit/ansi_color.rb +0 -78
- data/lib/kitsune/kit/commands/bootstrap.rb +0 -134
- data/lib/kitsune/kit/commands/bootstrap_docker.rb +0 -66
- data/lib/kitsune/kit/commands/dns.rb +0 -112
- data/lib/kitsune/kit/commands/init.rb +0 -148
- data/lib/kitsune/kit/commands/install_docker_engine.rb +0 -146
- data/lib/kitsune/kit/commands/postinstall_docker.rb +0 -142
- data/lib/kitsune/kit/commands/provision.rb +0 -43
- data/lib/kitsune/kit/commands/setup_do_metrics.rb +0 -123
- data/lib/kitsune/kit/commands/setup_docker_prereqs.rb +0 -151
- data/lib/kitsune/kit/commands/setup_firewall.rb +0 -132
- data/lib/kitsune/kit/commands/setup_postgres_docker.rb +0 -246
- data/lib/kitsune/kit/commands/setup_redis_docker.rb +0 -241
- data/lib/kitsune/kit/commands/setup_swap.rb +0 -151
- data/lib/kitsune/kit/commands/setup_unattended.rb +0 -132
- data/lib/kitsune/kit/commands/setup_user.rb +0 -189
- data/lib/kitsune/kit/commands/ssh.rb +0 -46
- data/lib/kitsune/kit/commands/switch_env.rb +0 -42
- data/lib/kitsune/kit/defaults.rb +0 -91
- data/lib/kitsune/kit/env_loader.rb +0 -41
- data/lib/kitsune/kit/options_builder.rb +0 -26
- data/lib/kitsune/kit/provisioner.rb +0 -107
- data/sig/kitsune/kit.rbs +0 -6
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
require "thor"
|
|
2
|
-
require "net/ssh"
|
|
3
|
-
require_relative "../defaults"
|
|
4
|
-
require_relative "../options_builder"
|
|
5
|
-
|
|
6
|
-
module Kitsune
|
|
7
|
-
module Kit
|
|
8
|
-
module Commands
|
|
9
|
-
class SetupDoMetrics < Thor
|
|
10
|
-
namespace "setup_do_metrics"
|
|
11
|
-
|
|
12
|
-
class_option :server_ip, aliases: "-s", required: true, desc: "Server IP address"
|
|
13
|
-
class_option :ssh_port, aliases: "-p", desc: "SSH port"
|
|
14
|
-
class_option :ssh_key_path, aliases: "-k", desc: "SSH private key path"
|
|
15
|
-
|
|
16
|
-
desc "create", "Install and enable the DigitalOcean Metrics Agent"
|
|
17
|
-
def create
|
|
18
|
-
unless Kitsune::Kit::Defaults.metrics[:enable_do_metrics]
|
|
19
|
-
say "โ ๏ธ DigitalOcean Metrics Agent setup is disabled via ENABLE_DO_METRICS=false", :yellow
|
|
20
|
-
return
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
filled_options = Kitsune::Kit::OptionsBuilder.build(
|
|
24
|
-
options,
|
|
25
|
-
required: [:server_ip],
|
|
26
|
-
defaults: Kitsune::Kit::Defaults.ssh
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
with_ssh_connection(filled_options) do |ssh|
|
|
30
|
-
install_agent(ssh)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
desc "rollback", "Uninstall the DigitalOcean Metrics Agent"
|
|
35
|
-
def rollback
|
|
36
|
-
filled_options = Kitsune::Kit::OptionsBuilder.build(
|
|
37
|
-
options,
|
|
38
|
-
required: [:server_ip],
|
|
39
|
-
defaults: Kitsune::Kit::Defaults.ssh
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
with_ssh_connection(filled_options) do |ssh|
|
|
43
|
-
uninstall_agent(ssh)
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
no_commands do
|
|
48
|
-
def with_ssh_connection(filled_options)
|
|
49
|
-
Net::SSH.start(
|
|
50
|
-
filled_options[:server_ip],
|
|
51
|
-
"deploy",
|
|
52
|
-
port: filled_options[:ssh_port],
|
|
53
|
-
keys: [File.expand_path(filled_options[:ssh_key_path])],
|
|
54
|
-
non_interactive: true,
|
|
55
|
-
timeout: 5
|
|
56
|
-
) do |ssh|
|
|
57
|
-
yield ssh
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def install_agent(ssh)
|
|
62
|
-
marker = "/usr/local/backups/setup_do_metrics.after"
|
|
63
|
-
|
|
64
|
-
script = <<~BASH
|
|
65
|
-
set -e
|
|
66
|
-
sudo mkdir -p /usr/local/backups
|
|
67
|
-
|
|
68
|
-
if [ -f "#{marker}" ]; then
|
|
69
|
-
echo "๐ Metrics Agent already installed, skipping."
|
|
70
|
-
exit 0
|
|
71
|
-
fi
|
|
72
|
-
|
|
73
|
-
echo "โ๐ป Installing DigitalOcean Metrics Agent..."
|
|
74
|
-
curl -sSL https://repos.insights.digitalocean.com/install.sh | sudo bash
|
|
75
|
-
|
|
76
|
-
sudo touch "#{marker}"
|
|
77
|
-
echo "โ
Metrics Agent installed"
|
|
78
|
-
|
|
79
|
-
ps aux | grep do-agent | grep -v grep || echo "โ ๏ธ do-agent process not found"
|
|
80
|
-
BASH
|
|
81
|
-
|
|
82
|
-
say ssh.exec!(script)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def uninstall_agent(ssh)
|
|
86
|
-
marker = "/usr/local/backups/setup_do_metrics.after"
|
|
87
|
-
|
|
88
|
-
script = <<~BASH
|
|
89
|
-
set -e
|
|
90
|
-
|
|
91
|
-
if [ ! -f "#{marker}" ]; then
|
|
92
|
-
echo "๐ก No marker for metrics agent found. Skipping rollback."
|
|
93
|
-
exit 0
|
|
94
|
-
fi
|
|
95
|
-
|
|
96
|
-
echo "๐งน Uninstalling DigitalOcean Metrics Agent..."
|
|
97
|
-
|
|
98
|
-
if dpkg -l | grep -q do-agent; then
|
|
99
|
-
echo "๐ฆ Removing do-agent package..."
|
|
100
|
-
sudo apt-get remove --purge -y do-agent
|
|
101
|
-
else
|
|
102
|
-
echo "๐ก do-agent is not installed, skipping package removal."
|
|
103
|
-
fi
|
|
104
|
-
|
|
105
|
-
if systemctl list-unit-files | grep -q do-agent.service; then
|
|
106
|
-
echo "โ๐ป Stopping do-agent service..."
|
|
107
|
-
sudo systemctl stop do-agent || true
|
|
108
|
-
sudo systemctl disable do-agent || true
|
|
109
|
-
else
|
|
110
|
-
echo "๐ก do-agent.service not found in systemd, skipping stop/disable"
|
|
111
|
-
fi
|
|
112
|
-
|
|
113
|
-
sudo rm -f "#{marker}"
|
|
114
|
-
echo "โ
Metrics Agent removed"
|
|
115
|
-
BASH
|
|
116
|
-
|
|
117
|
-
say ssh.exec!(script)
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
end
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
require "thor"
|
|
2
|
-
require "net/ssh"
|
|
3
|
-
require_relative "../options_builder"
|
|
4
|
-
require_relative "../defaults"
|
|
5
|
-
|
|
6
|
-
module Kitsune
|
|
7
|
-
module Kit
|
|
8
|
-
module Commands
|
|
9
|
-
class SetupDockerPrereqs < Thor
|
|
10
|
-
namespace "setup_docker_prereqs"
|
|
11
|
-
|
|
12
|
-
class_option :server_ip, aliases: "-s", required: true, desc: "Server IP address or hostname"
|
|
13
|
-
class_option :ssh_port, aliases: "-p", desc: "SSH port"
|
|
14
|
-
class_option :ssh_key_path, aliases: "-k", desc: "Path to your private SSH key"
|
|
15
|
-
|
|
16
|
-
desc "create", "Install Docker prerequisites on the remote server"
|
|
17
|
-
def create
|
|
18
|
-
filled_options = Kitsune::Kit::OptionsBuilder.build(
|
|
19
|
-
options,
|
|
20
|
-
required: [:server_ip],
|
|
21
|
-
defaults: Kitsune::Kit::Defaults.ssh
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
with_ssh_connection(filled_options) do |ssh|
|
|
25
|
-
perform_setup(ssh)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
desc "rollback", "Remove installed Docker prerequisites from the remote server"
|
|
30
|
-
def rollback
|
|
31
|
-
filled_options = Kitsune::Kit::OptionsBuilder.build(
|
|
32
|
-
options,
|
|
33
|
-
required: [:server_ip],
|
|
34
|
-
defaults: Kitsune::Kit::Defaults.ssh
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
with_ssh_connection(filled_options) do |ssh|
|
|
38
|
-
perform_rollback(ssh)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
no_commands do
|
|
43
|
-
def with_ssh_connection(filled_options)
|
|
44
|
-
server = filled_options[:server_ip]
|
|
45
|
-
port = filled_options[:ssh_port]
|
|
46
|
-
key = File.expand_path(filled_options[:ssh_key_path])
|
|
47
|
-
|
|
48
|
-
say "๐ Connecting as deploy@#{server}:#{port}", :green
|
|
49
|
-
Net::SSH.start(server, "deploy", port: port, keys: [key], non_interactive: true, timeout: 5) do |ssh|
|
|
50
|
-
yield ssh
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def perform_setup(ssh)
|
|
55
|
-
output = ssh.exec! <<~EOH
|
|
56
|
-
set -e
|
|
57
|
-
|
|
58
|
-
sudo mkdir -p /usr/local/backups
|
|
59
|
-
sudo chown deploy:deploy /usr/local/backups
|
|
60
|
-
|
|
61
|
-
BACKUP_DIR="/usr/local/backups"
|
|
62
|
-
SCRIPT_ID="setup_docker_prereqs"
|
|
63
|
-
BEFORE_FILE="${BACKUP_DIR}/${SCRIPT_ID}.before"
|
|
64
|
-
AFTER_FILE="${BACKUP_DIR}/${SCRIPT_ID}.after"
|
|
65
|
-
|
|
66
|
-
TARGET_PKGS=(
|
|
67
|
-
apt-transport-https
|
|
68
|
-
ca-certificates
|
|
69
|
-
curl
|
|
70
|
-
gnupg
|
|
71
|
-
lsb-release
|
|
72
|
-
software-properties-common
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
echo "โ๐ป TARGET_PKGS=(\${TARGET_PKGS[*]})"
|
|
76
|
-
|
|
77
|
-
if [ ! -f "$AFTER_FILE" ]; then
|
|
78
|
-
for pkg in "\${TARGET_PKGS[@]}"; do
|
|
79
|
-
if dpkg -l "\$pkg" &>/dev/null; then
|
|
80
|
-
echo "\$pkg" >> "$BEFORE_FILE"
|
|
81
|
-
fi
|
|
82
|
-
done
|
|
83
|
-
|
|
84
|
-
echo "โ๐ป Installing prerequisites..."
|
|
85
|
-
sudo apt-get update -y
|
|
86
|
-
sudo apt-get install -y "\${TARGET_PKGS[@]}"
|
|
87
|
-
sudo touch "$AFTER_FILE" && echo " - marker created at $AFTER_FILE"
|
|
88
|
-
else
|
|
89
|
-
echo "๐ Already set up, ensuring latest"
|
|
90
|
-
sudo apt-get update -y
|
|
91
|
-
sudo apt-get install -y "\${TARGET_PKGS[@]}"
|
|
92
|
-
echo "โ
Prerequisites are current"
|
|
93
|
-
fi
|
|
94
|
-
EOH
|
|
95
|
-
say output
|
|
96
|
-
say "โ
Docker prerequisites setup completed", :green
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def perform_rollback(ssh)
|
|
100
|
-
output = ssh.exec! <<~EOH
|
|
101
|
-
set -e
|
|
102
|
-
|
|
103
|
-
sudo mkdir -p /usr/local/backups
|
|
104
|
-
sudo chown deploy:deploy /usr/local/backups
|
|
105
|
-
|
|
106
|
-
BACKUP_DIR="/usr/local/backups"
|
|
107
|
-
SCRIPT_ID="setup_docker_prereqs"
|
|
108
|
-
BEFORE_FILE="${BACKUP_DIR}/${SCRIPT_ID}.before"
|
|
109
|
-
AFTER_FILE="${BACKUP_DIR}/${SCRIPT_ID}.after"
|
|
110
|
-
|
|
111
|
-
TARGET_PKGS=(
|
|
112
|
-
apt-transport-https
|
|
113
|
-
ca-certificates
|
|
114
|
-
curl
|
|
115
|
-
gnupg
|
|
116
|
-
lsb-release
|
|
117
|
-
software-properties-common
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
echo "โ๐ป TARGET_PKGS=(\${TARGET_PKGS[*]})"
|
|
121
|
-
|
|
122
|
-
if [ -f "$AFTER_FILE" ]; then
|
|
123
|
-
to_remove=()
|
|
124
|
-
for pkg in "\${TARGET_PKGS[@]}"; do
|
|
125
|
-
if dpkg -l "\$pkg" &>/dev/null; then
|
|
126
|
-
if ! grep -Fxq "\$pkg" "$BEFORE_FILE"; then
|
|
127
|
-
to_remove+=("\$pkg")
|
|
128
|
-
fi
|
|
129
|
-
fi
|
|
130
|
-
done
|
|
131
|
-
|
|
132
|
-
if [ \${#to_remove[@]} -gt 0 ]; then
|
|
133
|
-
echo "๐ Removing packages..."
|
|
134
|
-
sudo apt-get remove -y "\${to_remove[@]}" && echo " - removed: \${to_remove[*]}"
|
|
135
|
-
else
|
|
136
|
-
echo " - no new packages to remove"
|
|
137
|
-
fi
|
|
138
|
-
|
|
139
|
-
sudo rm -f "$BEFORE_FILE" "$AFTER_FILE" && echo " - backups removed"
|
|
140
|
-
else
|
|
141
|
-
echo " - no marker for $SCRIPT_ID, skipping removal"
|
|
142
|
-
fi
|
|
143
|
-
EOH
|
|
144
|
-
say output
|
|
145
|
-
say "โ
Docker prerequisites rollback completed", :green
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
end
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
require "thor"
|
|
2
|
-
require "net/ssh"
|
|
3
|
-
require_relative "../defaults"
|
|
4
|
-
require_relative "../options_builder"
|
|
5
|
-
|
|
6
|
-
module Kitsune
|
|
7
|
-
module Kit
|
|
8
|
-
module Commands
|
|
9
|
-
class SetupFirewall < Thor
|
|
10
|
-
namespace "setup_firewall"
|
|
11
|
-
|
|
12
|
-
class_option :server_ip, aliases: "-s", required: true, desc: "Server IP address or hostname"
|
|
13
|
-
class_option :ssh_port, aliases: "-p", desc: "SSH port"
|
|
14
|
-
class_option :ssh_key_path, aliases: "-k", desc: "Path to your private SSH key"
|
|
15
|
-
|
|
16
|
-
desc "create", "Setup UFW firewall rules on the remote server"
|
|
17
|
-
def create
|
|
18
|
-
filled_options = Kitsune::Kit::OptionsBuilder.build(
|
|
19
|
-
options,
|
|
20
|
-
required: [:server_ip],
|
|
21
|
-
defaults: Kitsune::Kit::Defaults.ssh
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
with_ssh_connection(filled_options) do |ssh|
|
|
25
|
-
perform_setup(ssh, filled_options)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
desc "rollback", "Remove UFW firewall rules and disable UFW on the remote server"
|
|
30
|
-
def rollback
|
|
31
|
-
filled_options = Kitsune::Kit::OptionsBuilder.build(
|
|
32
|
-
options,
|
|
33
|
-
required: [:server_ip],
|
|
34
|
-
defaults: Kitsune::Kit::Defaults.ssh
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
with_ssh_connection(filled_options) do |ssh|
|
|
38
|
-
perform_rollback(ssh, filled_options)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
no_commands do
|
|
43
|
-
def with_ssh_connection(filled_options)
|
|
44
|
-
server = filled_options[:server_ip]
|
|
45
|
-
port = filled_options[:ssh_port]
|
|
46
|
-
key = File.expand_path(filled_options[:ssh_key_path])
|
|
47
|
-
|
|
48
|
-
say "๐ Connecting as deploy@#{server}:#{port}", :green
|
|
49
|
-
Net::SSH.start(server, "deploy", port: port, keys: [key], non_interactive: true, timeout: 5) do |ssh|
|
|
50
|
-
yield ssh
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def perform_setup(ssh, filled_options)
|
|
55
|
-
ssh_port = filled_options[:ssh_port]
|
|
56
|
-
|
|
57
|
-
output = ssh.exec! <<~EOH
|
|
58
|
-
set -e
|
|
59
|
-
|
|
60
|
-
echo "โ๐ป Updating repositories and ensuring UFW is installedโฆ"
|
|
61
|
-
if ! dpkg -l | grep -q ufw; then
|
|
62
|
-
sudo apt-get update -y
|
|
63
|
-
sudo apt-get install -y ufw && echo " - ufw installed"
|
|
64
|
-
else
|
|
65
|
-
echo " - ufw is already installed"
|
|
66
|
-
fi
|
|
67
|
-
|
|
68
|
-
echo "โ๐ป Configuring UFW rulesโฆ"
|
|
69
|
-
add_rule() {
|
|
70
|
-
local rule="$1"
|
|
71
|
-
if ! sudo ufw status | grep -q "$rule"; then
|
|
72
|
-
sudo ufw allow "$rule" >/dev/null 2>&1 && echo " - rule '$rule' added"
|
|
73
|
-
else
|
|
74
|
-
echo " - rule '$rule' already exists"
|
|
75
|
-
fi
|
|
76
|
-
}
|
|
77
|
-
add_rule "#{ssh_port}/tcp"
|
|
78
|
-
add_rule "80/tcp"
|
|
79
|
-
add_rule "443/tcp"
|
|
80
|
-
|
|
81
|
-
echo "โ๐ป Enabling UFW loggingโฆ"
|
|
82
|
-
if ! sudo ufw status verbose | grep -q "Logging: on"; then
|
|
83
|
-
sudo ufw logging on >/dev/null 2>&1 && echo " - logging enabled"
|
|
84
|
-
else
|
|
85
|
-
echo " - logging was already enabled"
|
|
86
|
-
fi
|
|
87
|
-
|
|
88
|
-
echo "โ๐ป Enabling UFWโฆ"
|
|
89
|
-
if sudo ufw status | grep -q "Status: inactive"; then
|
|
90
|
-
sudo ufw --force enable >/dev/null 2>&1 && echo " - UFW enabled"
|
|
91
|
-
else
|
|
92
|
-
echo " - UFW is already enabled"
|
|
93
|
-
fi
|
|
94
|
-
EOH
|
|
95
|
-
say output
|
|
96
|
-
say "โ
Firewall setup completed", :green
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def perform_rollback(ssh, filled_options)
|
|
100
|
-
ssh_port = filled_options[:ssh_port]
|
|
101
|
-
|
|
102
|
-
output = ssh.exec! <<~EOH
|
|
103
|
-
set -e
|
|
104
|
-
|
|
105
|
-
echo "๐ Removing UFW rulesโฆ"
|
|
106
|
-
delete_rule() {
|
|
107
|
-
local rule="$1"
|
|
108
|
-
if sudo ufw status | grep -q "$rule"; then
|
|
109
|
-
sudo ufw delete allow "$rule" >/dev/null 2>&1 && echo " - rule '$rule' removed"
|
|
110
|
-
else
|
|
111
|
-
echo " - rule '$rule' does not exist"
|
|
112
|
-
fi
|
|
113
|
-
}
|
|
114
|
-
delete_rule "#{ssh_port}/tcp"
|
|
115
|
-
delete_rule "80/tcp"
|
|
116
|
-
delete_rule "443/tcp"
|
|
117
|
-
|
|
118
|
-
echo "โ๐ป Disabling UFW if activeโฆ"
|
|
119
|
-
if sudo ufw status | grep -q "Status: inactive"; then
|
|
120
|
-
echo " - UFW is already inactive"
|
|
121
|
-
else
|
|
122
|
-
sudo ufw --force disable >/dev/null 2>&1 && echo " - UFW disabled"
|
|
123
|
-
fi
|
|
124
|
-
EOH
|
|
125
|
-
say output
|
|
126
|
-
say "โ
Firewall rollback completed", :green
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
end
|
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
require "thor"
|
|
2
|
-
require "net/ssh"
|
|
3
|
-
require "tempfile"
|
|
4
|
-
require "fileutils"
|
|
5
|
-
require "shellwords"
|
|
6
|
-
require_relative "../defaults"
|
|
7
|
-
require_relative "../options_builder"
|
|
8
|
-
|
|
9
|
-
module Kitsune
|
|
10
|
-
module Kit
|
|
11
|
-
module Commands
|
|
12
|
-
class SetupPostgresDocker < Thor
|
|
13
|
-
namespace "setup_postgres_docker"
|
|
14
|
-
|
|
15
|
-
class_option :server_ip, aliases: "-s", required: true, desc: "Server IP address or hostname"
|
|
16
|
-
class_option :ssh_port, aliases: "-p", desc: "SSH port"
|
|
17
|
-
class_option :ssh_key_path, aliases: "-k", desc: "Path to SSH private key"
|
|
18
|
-
|
|
19
|
-
desc "create", "Setup PostgreSQL using Docker Compose on remote server"
|
|
20
|
-
def create
|
|
21
|
-
postgres_defaults = Kitsune::Kit::Defaults.postgres
|
|
22
|
-
|
|
23
|
-
if postgres_defaults[:postgres_password] == "secret"
|
|
24
|
-
say "โ ๏ธ Warning: You are using the default PostgreSQL password ('secret').", :yellow
|
|
25
|
-
if ENV.fetch("KIT_ENV", "development") == "production"
|
|
26
|
-
abort "โ Production environment requires a secure PostgreSQL password!"
|
|
27
|
-
else
|
|
28
|
-
say "๐ Please change POSTGRES_PASSWORD in your .env if needed.", :yellow
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
filled_options = Kitsune::Kit::OptionsBuilder.build(
|
|
33
|
-
options,
|
|
34
|
-
required: [:server_ip],
|
|
35
|
-
defaults: Kitsune::Kit::Defaults.ssh
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
with_ssh_connection(filled_options) do |ssh|
|
|
39
|
-
perform_setup(ssh, postgres_defaults, filled_options)
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
desc "rollback", "Remove PostgreSQL Docker setup from remote server"
|
|
44
|
-
def rollback
|
|
45
|
-
postgres_defaults = Kitsune::Kit::Defaults.postgres
|
|
46
|
-
|
|
47
|
-
filled_options = Kitsune::Kit::OptionsBuilder.build(
|
|
48
|
-
options,
|
|
49
|
-
required: [:server_ip],
|
|
50
|
-
defaults: Kitsune::Kit::Defaults.ssh
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
with_ssh_connection(filled_options) do |ssh|
|
|
54
|
-
perform_rollback(ssh, postgres_defaults)
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
no_commands do
|
|
59
|
-
def with_ssh_connection(filled_options)
|
|
60
|
-
server = filled_options[:server_ip]
|
|
61
|
-
port = filled_options[:ssh_port]
|
|
62
|
-
key = File.expand_path(filled_options[:ssh_key_path])
|
|
63
|
-
|
|
64
|
-
say "๐ Connecting as deploy@#{server}:#{port}", :green
|
|
65
|
-
Net::SSH.start(server, "deploy", port: port, keys: [key], non_interactive: true, timeout: 5) do |ssh|
|
|
66
|
-
yield ssh
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def perform_setup(ssh, postgres_defaults, filled_options)
|
|
71
|
-
local_compose = ".kitsune/docker/postgres.yml"
|
|
72
|
-
remote_dir = "$HOME/docker/postgres"
|
|
73
|
-
compose_remote = "#{remote_dir}/docker-compose.yml"
|
|
74
|
-
env_remote = "#{remote_dir}/.env"
|
|
75
|
-
marker = "/usr/local/backups/setup_postgres_docker.after"
|
|
76
|
-
|
|
77
|
-
abort "โ Missing #{local_compose}" unless File.exist?(local_compose)
|
|
78
|
-
|
|
79
|
-
# 1. Create base directory securely
|
|
80
|
-
ssh.exec!("mkdir -p #{remote_dir} && chmod 700 #{remote_dir}")
|
|
81
|
-
|
|
82
|
-
# 2. Upload docker-compose.yml
|
|
83
|
-
say "๐ฆ Uploading docker-compose.yml to #{remote_dir}", :cyan
|
|
84
|
-
upload_file(ssh, File.read(local_compose), compose_remote)
|
|
85
|
-
|
|
86
|
-
# 3. Create .env file for docker-compose based on postgres_defaults
|
|
87
|
-
say "๐ฆ Creating .env file for Docker Compose...", :cyan
|
|
88
|
-
env_content = <<~ENVFILE
|
|
89
|
-
POSTGRES_DB=#{postgres_defaults[:postgres_db]}
|
|
90
|
-
POSTGRES_USER=#{postgres_defaults[:postgres_user]}
|
|
91
|
-
POSTGRES_PASSWORD=#{postgres_defaults[:postgres_password]}
|
|
92
|
-
POSTGRES_PORT=#{postgres_defaults[:postgres_port]}
|
|
93
|
-
POSTGRES_IMAGE=#{postgres_defaults[:postgres_image]}
|
|
94
|
-
ENVFILE
|
|
95
|
-
upload_file(ssh, env_content, env_remote)
|
|
96
|
-
|
|
97
|
-
# 4. Secure file permissions
|
|
98
|
-
ssh.exec!("chmod 600 #{compose_remote} #{env_remote}")
|
|
99
|
-
|
|
100
|
-
# 5. Create backup marker
|
|
101
|
-
ssh.exec!("sudo mkdir -p /usr/local/backups && sudo touch #{marker}")
|
|
102
|
-
|
|
103
|
-
# 6. Validate docker-compose.yml
|
|
104
|
-
say "๐ Validating docker-compose.yml...", :cyan
|
|
105
|
-
validation_output = ssh.exec!("cd #{remote_dir} && docker compose config")
|
|
106
|
-
say validation_output, :cyan
|
|
107
|
-
|
|
108
|
-
# 7. Check if container is running
|
|
109
|
-
container_status = ssh.exec!("docker ps --filter 'name=postgres' --format '{{.Status}}'").strip
|
|
110
|
-
|
|
111
|
-
if container_status.empty?
|
|
112
|
-
say "โถ๏ธ No running container. Running docker compose up...", :cyan
|
|
113
|
-
ssh.exec!("cd #{remote_dir} && docker compose up -d")
|
|
114
|
-
else
|
|
115
|
-
say "โ ๏ธ PostgreSQL container is already running.", :yellow
|
|
116
|
-
if yes?("๐ Recreate the container with updated configuration? [y/N]", :yellow)
|
|
117
|
-
say "๐ Recreating container...", :cyan
|
|
118
|
-
ssh.exec!("cd #{remote_dir} && docker compose down -v && docker compose up -d")
|
|
119
|
-
else
|
|
120
|
-
say "โฉ Keeping existing container.", :cyan
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# 8. Check container status
|
|
125
|
-
say "๐ Final container status (docker compose ps):", :cyan
|
|
126
|
-
docker_ps_output = ssh.exec!("cd #{remote_dir} && docker compose ps --format json")
|
|
127
|
-
|
|
128
|
-
if docker_ps_output.nil? || docker_ps_output.strip.empty? || docker_ps_output.include?("no configuration file")
|
|
129
|
-
say "โ ๏ธ docker compose ps returned no valid output.", :yellow
|
|
130
|
-
else
|
|
131
|
-
begin
|
|
132
|
-
services = JSON.parse(docker_ps_output)
|
|
133
|
-
services = [services] if services.is_a?(Hash)
|
|
134
|
-
|
|
135
|
-
postgres = services.find { |svc| svc["Service"] == "postgres" }
|
|
136
|
-
status = postgres && postgres["State"]
|
|
137
|
-
health = postgres && postgres["Health"]
|
|
138
|
-
|
|
139
|
-
if (status == "running" && health == "healthy") || (health == "healthy")
|
|
140
|
-
say "โ
PostgreSQL container is running and healthy.", :green
|
|
141
|
-
else
|
|
142
|
-
say "โ ๏ธ PostgreSQL container is not healthy yet.", :yellow
|
|
143
|
-
end
|
|
144
|
-
rescue JSON::ParserError => e
|
|
145
|
-
say "๐จ Failed to parse docker compose ps output as JSON: #{e.message}", :red
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
# 9. Check PostgreSQL readiness with retries
|
|
150
|
-
say "๐ Checking PostgreSQL health with retries...", :cyan
|
|
151
|
-
|
|
152
|
-
max_attempts = 10
|
|
153
|
-
attempt = 0
|
|
154
|
-
success = false
|
|
155
|
-
|
|
156
|
-
while attempt < max_attempts
|
|
157
|
-
attempt += 1
|
|
158
|
-
healthcheck = ssh.exec!("docker exec $(docker ps -qf name=postgres) pg_isready -U #{postgres_defaults[:postgres_user]} -d #{postgres_defaults[:postgres_db]} -h localhost")
|
|
159
|
-
|
|
160
|
-
if healthcheck.include?("accepting connections")
|
|
161
|
-
say "โ
PostgreSQL is up and accepting connections! (attempt #{attempt})", :green
|
|
162
|
-
success = true
|
|
163
|
-
|
|
164
|
-
database_url = build_database_url(filled_options, postgres_defaults)
|
|
165
|
-
say "๐ Your DATABASE_URL is:\t", :cyan
|
|
166
|
-
say database_url, :green
|
|
167
|
-
break
|
|
168
|
-
else
|
|
169
|
-
say "โณ PostgreSQL not ready yet, retrying in 5 seconds... (#{attempt}/#{max_attempts})", :yellow
|
|
170
|
-
sleep 5
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
unless success
|
|
175
|
-
say "โ PostgreSQL did not become ready after #{max_attempts} attempts.", :red
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
# 10. Allow PostgreSQL port through firewall (ufw)
|
|
179
|
-
say "๐ก๏ธ Configuring firewall to allow PostgreSQL (port #{postgres_defaults[:postgres_port]})...", :cyan
|
|
180
|
-
output = ssh.exec! <<~EOH
|
|
181
|
-
if command -v ufw >/dev/null; then
|
|
182
|
-
if ! sudo ufw status | grep -q "#{postgres_defaults[:postgres_port]}"; then
|
|
183
|
-
sudo ufw allow #{postgres_defaults[:postgres_port]}
|
|
184
|
-
else
|
|
185
|
-
echo "๐ก Port #{postgres_defaults[:postgres_port]} is already allowed in ufw."
|
|
186
|
-
fi
|
|
187
|
-
else
|
|
188
|
-
echo "โ ๏ธ ufw not found. Skipping firewall configuration."
|
|
189
|
-
fi
|
|
190
|
-
EOH
|
|
191
|
-
say output
|
|
192
|
-
|
|
193
|
-
say "โ
PostgreSQL setup completed successfully!", :green
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
def perform_rollback(ssh, postgres_defaults)
|
|
197
|
-
output = ssh.exec! <<~EOH
|
|
198
|
-
set -e
|
|
199
|
-
|
|
200
|
-
BASE_DIR="$HOME/docker/postgres"
|
|
201
|
-
BACKUP_DIR="/usr/local/backups"
|
|
202
|
-
SCRIPT_ID="setup_postgres_docker"
|
|
203
|
-
AFTER_FILE="${BACKUP_DIR}/${SCRIPT_ID}.after"
|
|
204
|
-
|
|
205
|
-
if [ -f "$AFTER_FILE" ]; then
|
|
206
|
-
echo "๐ Stopping and removing docker containers..."
|
|
207
|
-
cd "$BASE_DIR"
|
|
208
|
-
docker compose down -v || true
|
|
209
|
-
|
|
210
|
-
echo "๐งน Cleaning up files..."
|
|
211
|
-
rm -rf "$BASE_DIR"
|
|
212
|
-
sudo rm -f "$AFTER_FILE"
|
|
213
|
-
|
|
214
|
-
if command -v ufw >/dev/null; then
|
|
215
|
-
echo "๐ก๏ธ Removing PostgreSQL port from firewall..."
|
|
216
|
-
sudo ufw delete allow #{postgres_defaults[:postgres_port]} || true
|
|
217
|
-
fi
|
|
218
|
-
else
|
|
219
|
-
echo "๐ก Nothing to rollback"
|
|
220
|
-
fi
|
|
221
|
-
|
|
222
|
-
echo "โ
Rollback completed"
|
|
223
|
-
EOH
|
|
224
|
-
say output
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
def upload_file(ssh, content, remote_path)
|
|
228
|
-
escaped = Shellwords.escape(content)
|
|
229
|
-
ssh.exec!("mkdir -p #{File.dirname(remote_path)}")
|
|
230
|
-
ssh.exec!("echo #{escaped} > #{remote_path}")
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
def build_database_url(filled_options, postgres_defaults)
|
|
234
|
-
user = postgres_defaults[:postgres_user]
|
|
235
|
-
password = postgres_defaults[:postgres_password]
|
|
236
|
-
host = filled_options[:server_ip]
|
|
237
|
-
port = postgres_defaults[:postgres_port]
|
|
238
|
-
db = postgres_defaults[:postgres_db]
|
|
239
|
-
|
|
240
|
-
"postgres://#{user}:#{password}@#{host}:#{port}/#{db}"
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
end
|
|
244
|
-
end
|
|
245
|
-
end
|
|
246
|
-
end
|