kitsune-kit 0.4.0 → 0.5.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 +117 -2
- data/CONTRIBUTING.md +83 -0
- data/README.md +108 -136
- 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/provider-adapters.md +49 -0
- data/docs/architecture.md +89 -0
- data/docs/commands.md +210 -0
- data/docs/configuration.md +210 -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/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 +802 -64
- data/lib/kitsune/kit/clock.rb +11 -0
- data/lib/kitsune/kit/configuration.rb +493 -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 +285 -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 +127 -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 +137 -0
- data/lib/kitsune/kit/scripts/firewall.sh +205 -0
- data/lib/kitsune/kit/scripts/metrics.sh +54 -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 +81 -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 +72 -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/environment_selection.rb +70 -0
- data/lib/kitsune/kit/workflows/import_server.rb +100 -0
- data/lib/kitsune/kit/workflows/initialize_project.rb +121 -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 +41 -2
- metadata +122 -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
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "adapters/digitalocean_provider"
|
|
4
|
+
require_relative "adapters/transport_factory"
|
|
5
|
+
require_relative "configuration"
|
|
6
|
+
require_relative "events"
|
|
7
|
+
require_relative "operations/ensure_server"
|
|
8
|
+
require_relative "operations/ensure_dns_records"
|
|
9
|
+
require_relative "operations/ensure_service"
|
|
10
|
+
require_relative "state_store"
|
|
11
|
+
|
|
12
|
+
module Kitsune
|
|
13
|
+
module Kit
|
|
14
|
+
class Application
|
|
15
|
+
attr_reader :config, :event_bus, :provider, :state_store, :transport_factory, :secret_store
|
|
16
|
+
|
|
17
|
+
def self.build(root:, environment:, config_path: nil, overrides: {}, env: ENV, event_bus: Events::Bus.new,
|
|
18
|
+
host_key_confirmation: nil, maximum_timeout: nil)
|
|
19
|
+
config = Configuration::Loader.new(root: root, env: env, config_path: config_path)
|
|
20
|
+
.load(environment: environment, overrides: overrides)
|
|
21
|
+
token = env.fetch(config.provider.token_env, "")
|
|
22
|
+
provider = Adapters::DigitalOceanProvider.new(token: token, maximum_timeout: maximum_timeout)
|
|
23
|
+
state_store = StateStore.new(root: root)
|
|
24
|
+
transport_factory = Adapters::TransportFactory.new(
|
|
25
|
+
config: config,
|
|
26
|
+
provider: provider,
|
|
27
|
+
root: root,
|
|
28
|
+
host_key_confirmation: host_key_confirmation,
|
|
29
|
+
maximum_timeout: maximum_timeout
|
|
30
|
+
)
|
|
31
|
+
new(
|
|
32
|
+
config: config,
|
|
33
|
+
provider: provider,
|
|
34
|
+
state_store: state_store,
|
|
35
|
+
transport_factory: transport_factory,
|
|
36
|
+
secret_store: SecretStores::Environment.new(env: env),
|
|
37
|
+
event_bus: event_bus,
|
|
38
|
+
maximum_timeout: maximum_timeout
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def initialize(config:, provider:, state_store:, transport_factory: nil, event_bus: Events::Bus.new,
|
|
43
|
+
secret_store: SecretStores::Environment.new, maximum_timeout: nil)
|
|
44
|
+
@config = config
|
|
45
|
+
@provider = provider
|
|
46
|
+
@state_store = state_store
|
|
47
|
+
@transport_factory = transport_factory
|
|
48
|
+
@secret_store = secret_store
|
|
49
|
+
@event_bus = event_bus
|
|
50
|
+
@maximum_timeout = maximum_timeout
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def operations
|
|
54
|
+
values = [Operations::EnsureServer.new(
|
|
55
|
+
config: config, provider: provider, state_store: state_store, maximum_timeout: @maximum_timeout
|
|
56
|
+
)]
|
|
57
|
+
values.concat(remote_operations) if @transport_factory
|
|
58
|
+
values.concat(service_operations) if @transport_factory
|
|
59
|
+
if config.dns.domains.any?
|
|
60
|
+
values << Operations::EnsureDnsRecords.new(config: config, provider: provider, state_store: state_store)
|
|
61
|
+
end
|
|
62
|
+
values
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def service_operation(type)
|
|
66
|
+
Operations::EnsureService.new(
|
|
67
|
+
config: config,
|
|
68
|
+
type: type,
|
|
69
|
+
transport_factory: @transport_factory,
|
|
70
|
+
state_store: state_store,
|
|
71
|
+
secret_store: secret_store
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def remote_operations
|
|
78
|
+
values = [user_operation, ssh_operation, firewall_operation]
|
|
79
|
+
if config.system.unattended_upgrades
|
|
80
|
+
values << remote(resource: "unattended_upgrades",
|
|
81
|
+
script: script("unattended.sh"))
|
|
82
|
+
end
|
|
83
|
+
values << swap_operation
|
|
84
|
+
values << docker_operation
|
|
85
|
+
if config.system.metrics
|
|
86
|
+
values << remote(resource: "metrics", script: script("metrics.sh"),
|
|
87
|
+
arguments: [config.system.metrics_installer_sha256])
|
|
88
|
+
end
|
|
89
|
+
values
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def user_operation
|
|
93
|
+
remote(
|
|
94
|
+
resource: "user.#{config.ssh.user}",
|
|
95
|
+
script: script("user.sh"),
|
|
96
|
+
arguments: [config.ssh.user],
|
|
97
|
+
role: :wait_for_bootstrap,
|
|
98
|
+
rollback_role: :root,
|
|
99
|
+
post_verify: method(:verify_deploy_connection)
|
|
100
|
+
)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def ssh_operation
|
|
104
|
+
remote(
|
|
105
|
+
resource: "ssh.policy", script: script("ssh.sh"), arguments: [config.ssh.user],
|
|
106
|
+
post_verify: method(:verify_deploy_connection), finalize_action: "finalize", rollback_on_failure: true
|
|
107
|
+
)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def firewall_operation
|
|
111
|
+
remote(
|
|
112
|
+
resource: "firewall",
|
|
113
|
+
script: script("firewall.sh"),
|
|
114
|
+
arguments: [config.ssh.port, *config.ssh.allowed_cidrs],
|
|
115
|
+
post_verify: method(:verify_deploy_connection),
|
|
116
|
+
finalize_action: "finalize",
|
|
117
|
+
rollback_on_failure: true
|
|
118
|
+
)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def swap_operation
|
|
122
|
+
remote(
|
|
123
|
+
resource: "swap",
|
|
124
|
+
script: script("swap.sh"),
|
|
125
|
+
arguments: [config.system.swap_size_gb, config.system.swap_swappiness]
|
|
126
|
+
)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def docker_operation
|
|
130
|
+
remote(resource: "docker", script: script("docker.sh"), arguments: [config.ssh.user])
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def verify_deploy_connection
|
|
134
|
+
return if @transport_factory.deploy.reachable?
|
|
135
|
+
|
|
136
|
+
raise Errors::VerificationError.new(
|
|
137
|
+
"the deploy user cannot open a second SSH connection",
|
|
138
|
+
hint: "Root access remains enabled; inspect authorized_keys and retry."
|
|
139
|
+
)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def script(name) = File.expand_path("scripts/#{name}", __dir__)
|
|
143
|
+
|
|
144
|
+
def remote(resource:, script:, arguments: [], role: :deploy, rollback_role: nil, post_verify: nil,
|
|
145
|
+
finalize_action: nil, rollback_on_failure: false)
|
|
146
|
+
Operations::RemoteScript.new(
|
|
147
|
+
config: config,
|
|
148
|
+
transport_factory: @transport_factory,
|
|
149
|
+
state_store: state_store,
|
|
150
|
+
resource: resource,
|
|
151
|
+
script_path: script,
|
|
152
|
+
arguments: arguments,
|
|
153
|
+
transport_roles: { apply: role, rollback: rollback_role || role },
|
|
154
|
+
verification: {
|
|
155
|
+
callback: post_verify, finalize_action: finalize_action, rollback_on_failure: rollback_on_failure
|
|
156
|
+
}
|
|
157
|
+
)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def service_operations
|
|
161
|
+
%w[postgres redis].filter_map do |type|
|
|
162
|
+
service = config.services.public_send(type)
|
|
163
|
+
next unless service.enabled && service.mode == "managed"
|
|
164
|
+
|
|
165
|
+
service_operation(type)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kitsune
|
|
4
|
+
module Kit
|
|
5
|
+
class Cancellation
|
|
6
|
+
class Cancelled < StandardError; end
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@cancelled = false
|
|
10
|
+
@mutex = Mutex.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def cancel! = @mutex.synchronize { @cancelled = true }
|
|
14
|
+
def cancelled? = @mutex.synchronize { @cancelled }
|
|
15
|
+
|
|
16
|
+
def check!
|
|
17
|
+
raise Cancelled, "operation cancelled" if cancelled?
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|