kube_cluster 0.2.0 → 0.3.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/.github/workflows/release.yml +43 -0
- data/.github/workflows/tag-gem-version-bump.yml +47 -0
- data/.gitignore +2 -0
- data/Gemfile.lock +48 -52
- data/bin/console +3 -0
- data/bin/dev +4 -0
- data/docker-compose.yml +26 -0
- data/examples/01-basic-redis-pod/manifest.rb +60 -0
- data/examples/02-manifest-with-middleware/manifest.rb +37 -0
- data/examples/02-manifest-with-middleware/middleware/labels.rb +4 -0
- data/examples/02-manifest-with-middleware/middleware/namespace.rb +4 -0
- data/examples/02-manifest-with-middleware/templates/config_map.rb +13 -0
- data/examples/02-manifest-with-middleware/templates/deployment.rb +59 -0
- data/examples/02-manifest-with-middleware/templates/horizontal_pod_autoscaler.rb +30 -0
- data/examples/02-manifest-with-middleware/templates/ingress.rb +38 -0
- data/examples/02-manifest-with-middleware/templates/service.rb +12 -0
- data/examples/03-app-with-database/demo.rb +87 -0
- data/examples/03-app-with-database/helpers.rb +18 -0
- data/examples/03-app-with-database/my_app.rb +45 -0
- data/examples/03-app-with-database/postgresql.rb +81 -0
- data/examples/03-app-with-database/ruby_on_rails.rb +31 -0
- data/flake.lock +3 -3
- data/flake.nix +6 -0
- data/kube_cluster.gemspec +3 -1
- data/lib/kube/cli/cluster.rb +41 -0
- data/lib/kube/cluster/connection.rb +18 -0
- data/lib/kube/cluster/instance.rb +21 -0
- data/lib/kube/cluster/manifest.rb +25 -0
- data/lib/kube/cluster/middleware/annotations.rb +32 -0
- data/lib/kube/cluster/middleware/hpa_for_deployment.rb +111 -0
- data/lib/kube/cluster/middleware/ingress_for_service.rb +91 -0
- data/lib/kube/cluster/middleware/labels.rb +59 -0
- data/lib/kube/cluster/middleware/namespace.rb +31 -0
- data/lib/kube/cluster/middleware/pod_anti_affinity.rb +61 -0
- data/lib/kube/cluster/middleware/resource_preset.rb +64 -0
- data/lib/kube/cluster/middleware/security_context.rb +84 -0
- data/lib/kube/cluster/middleware/service_for_deployment.rb +71 -0
- data/lib/kube/cluster/middleware/stack.rb +43 -0
- data/lib/kube/cluster/middleware.rb +69 -0
- data/lib/kube/cluster/resource/dirty_tracking.rb +113 -0
- data/lib/kube/cluster/resource/persistence.rb +67 -0
- data/lib/kube/cluster/resource.rb +99 -0
- data/lib/kube/cluster/version.rb +1 -1
- data/lib/kube/cluster.rb +34 -7
- data/lib/kube/errors.rb +57 -0
- metadata +69 -17
- data/Rakefile +0 -11
- data/TREE_PLAN.md +0 -513
- data/bin/generate-command-schema-v1 +0 -44
- data/data/kubectl-command-tree-v1-minimal.json +0 -125
- data/data/kubectl-command-tree-v1.json +0 -1469
- data/examples/quick-repl/docker-compose.yml +0 -52
- data/exe/kube_cluster +0 -6
- data/lib/kube/cluster/command_node.rb +0 -89
- data/lib/kube/cluster/ctl.rb +0 -33
- data/lib/kube/cluster/query_builder.rb +0 -35
- data/lib/kube/cluster/resource_selector.rb +0 -19
- data/lib/kube/cluster/tree_node.rb +0 -51
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f35a6fb5b6ece5ffc32651a5ad104c896bc8cb80810c80ecb560a22fe0a59eca
|
|
4
|
+
data.tar.gz: df13e8af081ff1bba21254570f12eef80f82fe2ab47804877e76c0f360c9212c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cbfd8728fd22e79cc2429dc503cbba4f931da5d2f36e99d210b189863bdaa9fcf21965b13b1af231029d973dfafc03f1d44dba48f0f51eb07ba3d6597d8dfea1
|
|
7
|
+
data.tar.gz: 4dd4cec8ee94d15cce4430e6ee1b6a80a5db1e5aa97995b4aa58c3d5180dc9ac7829dbb4d36e9ba2b55c17ec025de097d0622d808dc24ba9e87ee5fadf4a2478
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
build-and-push-gem:
|
|
13
|
+
uses: ./.github/workflows/build-and-push-gem.yml
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
id-token: write
|
|
17
|
+
secrets: inherit
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
github-release:
|
|
22
|
+
|
|
23
|
+
needs: [build-and-push-gem]
|
|
24
|
+
|
|
25
|
+
runs-on: ubuntu-24.04
|
|
26
|
+
permissions:
|
|
27
|
+
contents: write
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/download-artifact@v4
|
|
30
|
+
with:
|
|
31
|
+
path: release/
|
|
32
|
+
merge-multiple: true
|
|
33
|
+
|
|
34
|
+
- name: Consolidate checksums
|
|
35
|
+
run: |
|
|
36
|
+
cd release
|
|
37
|
+
cat SHA256SUMS-linux-*.txt > SHA256SUMS.txt
|
|
38
|
+
rm SHA256SUMS-linux-*.txt
|
|
39
|
+
|
|
40
|
+
- uses: softprops/action-gh-release@v2
|
|
41
|
+
with:
|
|
42
|
+
files: release/*
|
|
43
|
+
generate_release_notes: true
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Auto-tag on version bump
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
tag:
|
|
9
|
+
runs-on: ubuntu-24.04
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
with:
|
|
15
|
+
fetch-depth: 0
|
|
16
|
+
|
|
17
|
+
- uses: ruby/setup-ruby@v1
|
|
18
|
+
with:
|
|
19
|
+
ruby-version: '3.4'
|
|
20
|
+
|
|
21
|
+
- name: Read version from gemspec
|
|
22
|
+
id: read
|
|
23
|
+
run: |
|
|
24
|
+
version=$(ruby -e '
|
|
25
|
+
spec = Gem::Specification.load(Dir["*.gemspec"].first)
|
|
26
|
+
puts spec.version
|
|
27
|
+
')
|
|
28
|
+
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
29
|
+
|
|
30
|
+
- name: Check if tag exists
|
|
31
|
+
id: check
|
|
32
|
+
run: |
|
|
33
|
+
tag="v${{ steps.read.outputs.version }}"
|
|
34
|
+
if git rev-parse "$tag" >/dev/null 2>&1; then
|
|
35
|
+
echo "exists=true" >> "$GITHUB_OUTPUT"
|
|
36
|
+
else
|
|
37
|
+
echo "exists=false" >> "$GITHUB_OUTPUT"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
- name: Create and push tag
|
|
41
|
+
if: steps.check.outputs.exists == 'false'
|
|
42
|
+
run: |
|
|
43
|
+
tag="v${{ steps.read.outputs.version }}"
|
|
44
|
+
git config user.name "Nathan K"
|
|
45
|
+
git config user.email "nathankidd@hey.com"
|
|
46
|
+
git tag -a "$tag" -m "Release $tag"
|
|
47
|
+
git push origin "$tag"
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,52 +1,75 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
kube_cluster (0.
|
|
5
|
-
|
|
4
|
+
kube_cluster (0.2.1)
|
|
5
|
+
kube_kit (> 0)
|
|
6
|
+
kube_kubectl (~> 2.0.0)
|
|
7
|
+
kube_schema (~> 1.2.0)
|
|
6
8
|
|
|
7
9
|
GEM
|
|
8
10
|
remote: https://rubygems.org/
|
|
9
11
|
specs:
|
|
10
|
-
addressable (2.8.9)
|
|
11
|
-
public_suffix (>= 2.0.2, < 8.0)
|
|
12
12
|
ast (2.4.3)
|
|
13
|
-
bigdecimal (4.
|
|
13
|
+
bigdecimal (4.1.2)
|
|
14
14
|
black_hole_struct (0.1.3)
|
|
15
|
+
date (3.5.1)
|
|
16
|
+
debug (1.11.1)
|
|
17
|
+
irb (~> 1.10)
|
|
18
|
+
reline (>= 0.3.8)
|
|
19
|
+
erb (6.0.3)
|
|
15
20
|
hana (1.3.7)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
io-console (0.8.2)
|
|
22
|
+
irb (1.17.0)
|
|
23
|
+
pp (>= 0.6.0)
|
|
24
|
+
prism (>= 1.3.0)
|
|
25
|
+
rdoc (>= 4.0.0)
|
|
26
|
+
reline (>= 0.4.2)
|
|
27
|
+
json (2.19.4)
|
|
20
28
|
json_schemer (2.5.0)
|
|
21
29
|
bigdecimal
|
|
22
30
|
hana (~> 1.3)
|
|
23
31
|
regexp_parser (~> 2.0)
|
|
24
32
|
simpleidn (~> 0.2)
|
|
25
|
-
|
|
33
|
+
kube_kit (0.2.0)
|
|
34
|
+
kube_kubectl (2.0.2)
|
|
35
|
+
debug (~> 1.11)
|
|
36
|
+
json_schemer (~> 2.5)
|
|
37
|
+
rubyshell (~> 1.5)
|
|
38
|
+
shellwords (~> 0.2.2)
|
|
39
|
+
string_builder (~> 1.2.0)
|
|
40
|
+
kube_schema (1.2.3)
|
|
26
41
|
black_hole_struct (~> 0.1)
|
|
27
42
|
json_schemer (~> 2.5)
|
|
28
43
|
rubyshell (~> 1.5)
|
|
29
44
|
language_server-protocol (3.17.0.5)
|
|
30
45
|
lint_roller (1.1.0)
|
|
31
|
-
mcp (0.8.0)
|
|
32
|
-
json-schema (>= 4.1)
|
|
33
46
|
minitest (5.27.0)
|
|
34
|
-
parallel (
|
|
35
|
-
parser (3.3.
|
|
47
|
+
parallel (2.0.1)
|
|
48
|
+
parser (3.3.11.1)
|
|
36
49
|
ast (~> 2.4.1)
|
|
37
50
|
racc
|
|
51
|
+
pp (0.6.3)
|
|
52
|
+
prettyprint
|
|
53
|
+
prettyprint (0.2.0)
|
|
38
54
|
prism (1.9.0)
|
|
39
|
-
|
|
55
|
+
psych (5.3.1)
|
|
56
|
+
date
|
|
57
|
+
stringio
|
|
40
58
|
racc (1.8.1)
|
|
41
59
|
rainbow (3.1.1)
|
|
42
|
-
rake (13.
|
|
43
|
-
|
|
44
|
-
|
|
60
|
+
rake (13.4.2)
|
|
61
|
+
rdoc (7.2.0)
|
|
62
|
+
erb
|
|
63
|
+
psych (>= 4.0.0)
|
|
64
|
+
tsort
|
|
65
|
+
regexp_parser (2.12.0)
|
|
66
|
+
reline (0.6.3)
|
|
67
|
+
io-console (~> 0.5)
|
|
68
|
+
rubocop (1.86.1)
|
|
45
69
|
json (~> 2.3)
|
|
46
70
|
language_server-protocol (~> 3.17.0.2)
|
|
47
71
|
lint_roller (~> 1.1.0)
|
|
48
|
-
|
|
49
|
-
parallel (~> 1.10)
|
|
72
|
+
parallel (>= 1.10)
|
|
50
73
|
parser (>= 3.3.0.2)
|
|
51
74
|
rainbow (>= 2.2.2, < 4.0)
|
|
52
75
|
regexp_parser (>= 2.9.3, < 3.0)
|
|
@@ -58,7 +81,11 @@ GEM
|
|
|
58
81
|
prism (~> 1.7)
|
|
59
82
|
ruby-progressbar (1.13.0)
|
|
60
83
|
rubyshell (1.5.0)
|
|
84
|
+
shellwords (0.2.2)
|
|
61
85
|
simpleidn (0.2.3)
|
|
86
|
+
string_builder (1.2.0)
|
|
87
|
+
stringio (3.2.0)
|
|
88
|
+
tsort (0.2.0)
|
|
62
89
|
unicode-display_width (3.2.0)
|
|
63
90
|
unicode-emoji (~> 4.1)
|
|
64
91
|
unicode-emoji (4.2.0)
|
|
@@ -73,36 +100,5 @@ DEPENDENCIES
|
|
|
73
100
|
rake (~> 13.0)
|
|
74
101
|
rubocop (~> 1.21)
|
|
75
102
|
|
|
76
|
-
CHECKSUMS
|
|
77
|
-
addressable (2.8.9) sha256=cc154fcbe689711808a43601dee7b980238ce54368d23e127421753e46895485
|
|
78
|
-
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
|
79
|
-
bigdecimal (4.0.1) sha256=8b07d3d065a9f921c80ceaea7c9d4ae596697295b584c296fe599dd0ad01c4a7
|
|
80
|
-
black_hole_struct (0.1.3) sha256=b1cac7dbe7f36bb3ed8372de656dbe140ad20d786aaace552c5706f7aa46c4a3
|
|
81
|
-
hana (1.3.7) sha256=5425db42d651fea08859811c29d20446f16af196308162894db208cac5ce9b0d
|
|
82
|
-
json (2.19.2) sha256=e7e1bd318b2c37c4ceee2444841c86539bc462e81f40d134cf97826cb14e83cf
|
|
83
|
-
json-schema (6.2.0) sha256=e8bff46ed845a22c1ab2bd0d7eccf831c01fe23bb3920caa4c74db4306813666
|
|
84
|
-
json_schemer (2.5.0) sha256=2f01fb4cce721a4e08dd068fc2030cffd0702a7f333f1ea2be6e8991f00ae396
|
|
85
|
-
kube_cluster (0.1.1)
|
|
86
|
-
kube_schema (1.0.0) sha256=a83e584b316f21492fe551231f22cf3e7439fd6f1df6f3769c24d66ab040dc6e
|
|
87
|
-
language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
|
|
88
|
-
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
|
|
89
|
-
mcp (0.8.0) sha256=ae8bd146bb8e168852866fd26f805f52744f6326afb3211e073f78a95e0c34fb
|
|
90
|
-
minitest (5.27.0) sha256=2d3b17f8a36fe7801c1adcffdbc38233b938eb0b4966e97a6739055a45fa77d5
|
|
91
|
-
parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130
|
|
92
|
-
parser (3.3.10.2) sha256=6f60c84aa4bdcedb6d1a2434b738fe8a8136807b6adc8f7f53b97da9bc4e9357
|
|
93
|
-
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
|
|
94
|
-
public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
|
|
95
|
-
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
|
|
96
|
-
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
|
97
|
-
rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
|
|
98
|
-
regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4
|
|
99
|
-
rubocop (1.85.1) sha256=3dbcf9e961baa4c376eeeb2a03913dca5e3987033b04d38fa538aa1e7406cc77
|
|
100
|
-
rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035
|
|
101
|
-
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
|
|
102
|
-
rubyshell (1.5.0) sha256=ffd528415962e52b2f3ec155fc3bc2cac401981413c0db451ea2a20194916ab6
|
|
103
|
-
simpleidn (0.2.3) sha256=08ce96f03fa1605286be22651ba0fc9c0b2d6272c9b27a260bc88be05b0d2c29
|
|
104
|
-
unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
|
|
105
|
-
unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
|
|
106
|
-
|
|
107
103
|
BUNDLED WITH
|
|
108
|
-
|
|
104
|
+
2.6.9
|
data/bin/console
CHANGED
data/bin/dev
ADDED
data/docker-compose.yml
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# to run define K3S_TOKEN, K3S_VERSION is optional, eg:
|
|
2
|
+
# K3S_TOKEN=${RANDOM}${RANDOM}${RANDOM} docker-compose up
|
|
3
|
+
services:
|
|
4
|
+
server:
|
|
5
|
+
image: "rancher/k3s:latest"
|
|
6
|
+
command: server
|
|
7
|
+
tmpfs:
|
|
8
|
+
- /run
|
|
9
|
+
- /var/run
|
|
10
|
+
ulimits:
|
|
11
|
+
nproc: 65535
|
|
12
|
+
nofile:
|
|
13
|
+
soft: 65535
|
|
14
|
+
hard: 65535
|
|
15
|
+
privileged: true
|
|
16
|
+
restart: always
|
|
17
|
+
environment:
|
|
18
|
+
- K3S_TOKEN=change-me-or-face-the-consequences
|
|
19
|
+
- K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml
|
|
20
|
+
- K3S_KUBECONFIG_MODE=666
|
|
21
|
+
volumes:
|
|
22
|
+
- .:/output
|
|
23
|
+
ports:
|
|
24
|
+
- 6443:6443 # Kubernetes API Server
|
|
25
|
+
- 80:80 # Ingress controller port 80
|
|
26
|
+
- 443:443 # Ingress controller port 443
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require "bundler/setup"
|
|
2
|
+
require "kube/schema"
|
|
3
|
+
|
|
4
|
+
class RedisPod < Kube::Cluster['Pod']
|
|
5
|
+
def initialize(container_name: 'my-redis-container', **options, &block)
|
|
6
|
+
super {
|
|
7
|
+
spec.containers = [
|
|
8
|
+
{
|
|
9
|
+
name: container_name,
|
|
10
|
+
image: 'redis:8.0.2',
|
|
11
|
+
|
|
12
|
+
command: ["redis-server", "/redis-master/redis.conf"],
|
|
13
|
+
env: [{name: 'MASTER', value: "true"}],
|
|
14
|
+
ports: [{ containerPort: 6379 }],
|
|
15
|
+
|
|
16
|
+
resources: { limits: { cpu: "0.1" } },
|
|
17
|
+
volumeMounts: [
|
|
18
|
+
{ mountPath: '/redis-master-data', name: 'data' },
|
|
19
|
+
{ mountPath: '/redis-master', name: 'config' },
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
spec.volumes = [
|
|
25
|
+
{
|
|
26
|
+
name: 'data',
|
|
27
|
+
emptyDir: {}
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'config',
|
|
31
|
+
configMap: {
|
|
32
|
+
name: 'example-redis-config',
|
|
33
|
+
items: [ { key: 'redis-config', path: 'redis.conf' } ]
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
instance_exec(&block) if block_given?
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
puts RedisPod.new(
|
|
43
|
+
container_name: 'my-redis-container-1',
|
|
44
|
+
metadata: {
|
|
45
|
+
namespace: 'my-namespace'
|
|
46
|
+
}
|
|
47
|
+
).to_yaml
|
|
48
|
+
|
|
49
|
+
puts RedisPod.new(container_name: 'my-redis-container-1') {
|
|
50
|
+
metadata.namespace = 'my-namespace'
|
|
51
|
+
}.to_yaml
|
|
52
|
+
|
|
53
|
+
puts RedisPod.new {
|
|
54
|
+
metadata.namespace = "my-namespace"
|
|
55
|
+
}.to_yaml
|
|
56
|
+
|
|
57
|
+
puts RedisPod.new {
|
|
58
|
+
metadata.name = "my-redis-1"
|
|
59
|
+
metadata.namespace = "my-namespace"
|
|
60
|
+
}.to_yaml
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require_relative 'templates/config_map'
|
|
2
|
+
require_relative 'templates/deployment'
|
|
3
|
+
require_relative 'templates/ingress'
|
|
4
|
+
require_relative 'templates/service'
|
|
5
|
+
require_relative 'templates/horizontal_pod_autoscaler'
|
|
6
|
+
|
|
7
|
+
require_relative 'middlware/labels'
|
|
8
|
+
require_relative 'middlware/namespace'
|
|
9
|
+
|
|
10
|
+
class MyApp < Kube::Schema::Manifest
|
|
11
|
+
stack do
|
|
12
|
+
use Middleware::Namespace
|
|
13
|
+
use Middleware::Labels
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
puts MyApp.new(
|
|
18
|
+
Templates::ConfigMap.new {
|
|
19
|
+
# no overrides today
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
Templates::Deployment.new {
|
|
23
|
+
# no overrides today
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
Templates::Ingress.new {
|
|
27
|
+
# no overrides today
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
Templates::Service.new {
|
|
31
|
+
# no overrides today
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
Templates::HorizontalPodScaler.new {
|
|
35
|
+
# no overrides today
|
|
36
|
+
},
|
|
37
|
+
).to_yaml
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
class Deployment < Kube::Cluster["Deployment"]
|
|
2
|
+
def initialize(namespace:)
|
|
3
|
+
build {
|
|
4
|
+
metadata.name = namespace
|
|
5
|
+
|
|
6
|
+
spec.replicas = 3
|
|
7
|
+
spec.selector.matchLabels = MATCH_LABELS
|
|
8
|
+
|
|
9
|
+
spec.template.metadata.labels = STANDARD_LABELS
|
|
10
|
+
spec.template.metadata.annotations = {
|
|
11
|
+
# Checksum pattern from _utils.tpl -- triggers rolling restart on config change
|
|
12
|
+
"checksum/config": "{{ sha256sum of configmap data }}",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
spec.template.spec.containers = [
|
|
16
|
+
{
|
|
17
|
+
name: APP_NAME,
|
|
18
|
+
image: IMAGE,
|
|
19
|
+
ports: [{ name: "http", containerPort: 3000, protocol: "TCP" }],
|
|
20
|
+
resources: RESOURCES,
|
|
21
|
+
env: [
|
|
22
|
+
{ name: "PORT", value: "3000" },
|
|
23
|
+
],
|
|
24
|
+
envFrom: [
|
|
25
|
+
{ configMapRef: { name: "#{FULLNAME}-config" } },
|
|
26
|
+
],
|
|
27
|
+
livenessProbe: {
|
|
28
|
+
httpGet: { path: "/healthz", port: "http" },
|
|
29
|
+
initialDelaySeconds: 15,
|
|
30
|
+
periodSeconds: 10,
|
|
31
|
+
},
|
|
32
|
+
readinessProbe: {
|
|
33
|
+
httpGet: { path: "/readyz", port: "http" },
|
|
34
|
+
initialDelaySeconds: 5,
|
|
35
|
+
periodSeconds: 5,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
# Pod anti-affinity (from _affinities.tpl)
|
|
41
|
+
# Soft anti-affinity: prefer spreading pods across nodes but don't enforce it
|
|
42
|
+
spec.template.spec.affinity = {
|
|
43
|
+
podAntiAffinity: {
|
|
44
|
+
preferredDuringSchedulingIgnoredDuringExecution: [
|
|
45
|
+
{
|
|
46
|
+
weight: 1,
|
|
47
|
+
podAffinityTerm: {
|
|
48
|
+
labelSelector: {
|
|
49
|
+
matchLabels: MATCH_LABELS,
|
|
50
|
+
},
|
|
51
|
+
topologyKey: "kubernetes.io/hostname",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
class HorizontalPodAutoscaler < Kube::Cluster["HorizontalPodAutoscaler"]
|
|
2
|
+
def initialize(namespace:)
|
|
3
|
+
build {
|
|
4
|
+
metadata.name = namespace
|
|
5
|
+
|
|
6
|
+
spec.scaleTargetRef.apiVersion = "apps/v1"
|
|
7
|
+
spec.scaleTargetRef.kind = "Deployment"
|
|
8
|
+
spec.scaleTargetRef.name = namespace
|
|
9
|
+
|
|
10
|
+
spec.minReplicas = 3
|
|
11
|
+
spec.maxReplicas = 10
|
|
12
|
+
spec.metrics = [
|
|
13
|
+
{
|
|
14
|
+
type: "Resource",
|
|
15
|
+
resource: {
|
|
16
|
+
name: "cpu",
|
|
17
|
+
target: { type: "Utilization", averageUtilization: 75 },
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
type: "Resource",
|
|
22
|
+
resource: {
|
|
23
|
+
name: "memory",
|
|
24
|
+
target: { type: "Utilization", averageUtilization: 80 },
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
class Ingress < Kube::Cluster["Ingress"]
|
|
2
|
+
def initialize(namespace:)
|
|
3
|
+
build {
|
|
4
|
+
metadata.name = namespace
|
|
5
|
+
metadata.annotations = {
|
|
6
|
+
"cert-manager.io/cluster-issuer": "letsencrypt-prod",
|
|
7
|
+
"nginx.ingress.kubernetes.io/ssl-redirect": "true",
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
spec.ingressClassName = "nginx"
|
|
11
|
+
spec.tls = [
|
|
12
|
+
{
|
|
13
|
+
hosts: ["app.example.com"],
|
|
14
|
+
secretName: "#{namespace}-tls",
|
|
15
|
+
},
|
|
16
|
+
]
|
|
17
|
+
spec.rules = [
|
|
18
|
+
{
|
|
19
|
+
host: "app.example.com",
|
|
20
|
+
http: {
|
|
21
|
+
paths: [
|
|
22
|
+
{
|
|
23
|
+
path: "/",
|
|
24
|
+
pathType: "Prefix",
|
|
25
|
+
backend: {
|
|
26
|
+
service: {
|
|
27
|
+
name: FULLNAME,
|
|
28
|
+
port: { name: "http" },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Middleware-driven Manifest Example
|
|
5
|
+
#
|
|
6
|
+
# Demonstrates how middleware eliminates boilerplate. The middleware stack
|
|
7
|
+
# declared in MyApp automatically generates Services, Ingresses, HPAs,
|
|
8
|
+
# and injects resource limits, security contexts, and pod anti-affinity.
|
|
9
|
+
#
|
|
10
|
+
# The block below only declares the unique intent — the things only a
|
|
11
|
+
# human knows. Everything else is derived by middleware from labels.
|
|
12
|
+
#
|
|
13
|
+
# What middleware generates from each Deployment:
|
|
14
|
+
# - Service (from container ports + matchLabels)
|
|
15
|
+
# - Ingress (from app.kubernetes.io/expose label)
|
|
16
|
+
# - HPA (from app.kubernetes.io/autoscale label)
|
|
17
|
+
# - Resource limits (from app.kubernetes.io/size label)
|
|
18
|
+
# - Security contexts (restricted profile, all pod-bearing resources)
|
|
19
|
+
# - Pod anti-affinity (spread across nodes, all pod-bearing resources)
|
|
20
|
+
# - Standard labels (managed-by, merged into everything)
|
|
21
|
+
#
|
|
22
|
+
# Usage:
|
|
23
|
+
# ruby examples/version2/demo.rb
|
|
24
|
+
# ruby examples/version2/demo.rb > app.yaml
|
|
25
|
+
|
|
26
|
+
require "kube/cluster"
|
|
27
|
+
require "securerandom"
|
|
28
|
+
require_relative "my_app"
|
|
29
|
+
|
|
30
|
+
app = MyApp.new("example.com", size: :small) do |m|
|
|
31
|
+
name = "rails-app"
|
|
32
|
+
ns = "production"
|
|
33
|
+
db_name = "postgresql"
|
|
34
|
+
db_ns = "database"
|
|
35
|
+
|
|
36
|
+
labels = m.app_labels(name: name, instance: name)
|
|
37
|
+
db_labels = m.app_labels(name: db_name, instance: db_name, component: "primary")
|
|
38
|
+
db_match = m.match_labels(name: db_name, instance: db_name, component: "primary")
|
|
39
|
+
|
|
40
|
+
# ── Rails tier ──────────────────────────────────────────────────
|
|
41
|
+
#
|
|
42
|
+
# One Namespace, one ConfigMap, one Deployment.
|
|
43
|
+
# Middleware generates: Service, Ingress, HPA
|
|
44
|
+
# Middleware injects: resource limits, security context, anti-affinity, labels
|
|
45
|
+
|
|
46
|
+
[
|
|
47
|
+
Kube::Cluster["Namespace"].new {
|
|
48
|
+
metadata.name = ns
|
|
49
|
+
metadata.labels = labels
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
Kube::Cluster["ConfigMap"].new {
|
|
53
|
+
metadata.name = "#{name}-config"
|
|
54
|
+
metadata.namespace = ns
|
|
55
|
+
metadata.labels = labels
|
|
56
|
+
self.data = {
|
|
57
|
+
RAILS_ENV: "production",
|
|
58
|
+
DATABASE_URL: "postgres://#{db_name}-headless.#{db_ns}.svc.cluster.local:5432/app",
|
|
59
|
+
LOG_LEVEL: "info",
|
|
60
|
+
WORKERS: "4",
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
RubyOnRails.new {
|
|
65
|
+
metadata.name = name
|
|
66
|
+
metadata.namespace = ns
|
|
67
|
+
metadata.labels = labels.merge(
|
|
68
|
+
"app.kubernetes.io/expose": "app.example.com",
|
|
69
|
+
"app.kubernetes.io/autoscale": "1-5",
|
|
70
|
+
)
|
|
71
|
+
},
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
# ── Database tier ───────────────────────────────────────────────
|
|
75
|
+
#
|
|
76
|
+
# StatefulSet + headless Service + Secret + NetworkPolicy.
|
|
77
|
+
# Middleware generates: Service (from container ports)
|
|
78
|
+
# Middleware injects: resource limits, security context, anti-affinity, labels
|
|
79
|
+
|
|
80
|
+
pg_password = SecureRandom.alphanumeric(24)
|
|
81
|
+
|
|
82
|
+
Postgresql.new {
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
puts app.to_yaml
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module App
|
|
4
|
+
module Helpers
|
|
5
|
+
def match_labels(name:, instance:, component: nil)
|
|
6
|
+
labels = {
|
|
7
|
+
"app.kubernetes.io/name": name,
|
|
8
|
+
"app.kubernetes.io/instance": instance,
|
|
9
|
+
}
|
|
10
|
+
labels[:"app.kubernetes.io/component"] = component if component
|
|
11
|
+
labels
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def base64(str)
|
|
15
|
+
[str].pack("m0")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|