pangea-kubernetes 0.1.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/.github/workflows/auto-bump.yml +11 -0
- data/.github/workflows/ci.yml +7 -0
- data/.github/workflows/release.yml +22 -0
- data/.gitignore +6 -0
- data/.rspec +3 -0
- data/AGENTS.md +3 -0
- data/CLAUDE.md +370 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +128 -0
- data/README.md +42 -0
- data/Rakefile +8 -0
- data/flake.lock +2144 -0
- data/flake.nix +30 -0
- data/gemset.nix +312 -0
- data/lib/pangea/kubernetes/architecture.rb +383 -0
- data/lib/pangea/kubernetes/backend_registry.rb +117 -0
- data/lib/pangea/kubernetes/backends/aws_eks.rb +203 -0
- data/lib/pangea/kubernetes/backends/aws_nixos.rb +1347 -0
- data/lib/pangea/kubernetes/backends/azure_aks.rb +145 -0
- data/lib/pangea/kubernetes/backends/azure_nixos.rb +275 -0
- data/lib/pangea/kubernetes/backends/base.rb +116 -0
- data/lib/pangea/kubernetes/backends/gcp_gke.rb +176 -0
- data/lib/pangea/kubernetes/backends/gcp_nixos.rb +240 -0
- data/lib/pangea/kubernetes/backends/hcloud_k3s.rb +181 -0
- data/lib/pangea/kubernetes/backends/nixos_base.rb +235 -0
- data/lib/pangea/kubernetes/bare_metal/cloud_init.rb +196 -0
- data/lib/pangea/kubernetes/bare_metal/cluster_reference.rb +72 -0
- data/lib/pangea/kubernetes/load_balancer.rb +157 -0
- data/lib/pangea/kubernetes/network_backend_registry.rb +54 -0
- data/lib/pangea/kubernetes/network_backends/base.rb +78 -0
- data/lib/pangea/kubernetes/network_backends/cilium.rb +105 -0
- data/lib/pangea/kubernetes/network_backends/vpc_cni.rb +36 -0
- data/lib/pangea/kubernetes/types/argocd_config.rb +55 -0
- data/lib/pangea/kubernetes/types/control_plane_config.rb +65 -0
- data/lib/pangea/kubernetes/types/etcd_config.rb +64 -0
- data/lib/pangea/kubernetes/types/firewall_config.rb +39 -0
- data/lib/pangea/kubernetes/types/k3s_config.rb +112 -0
- data/lib/pangea/kubernetes/types/kernel_config.rb +31 -0
- data/lib/pangea/kubernetes/types/kubernetes_config.rb +129 -0
- data/lib/pangea/kubernetes/types/persistent_state_config.rb +100 -0
- data/lib/pangea/kubernetes/types/pki_config.rb +48 -0
- data/lib/pangea/kubernetes/types/secrets_config.rb +41 -0
- data/lib/pangea/kubernetes/types/vpn_config.rb +188 -0
- data/lib/pangea/kubernetes/types/wait_for_dns_config.rb +35 -0
- data/lib/pangea/kubernetes/types.rb +521 -0
- data/lib/pangea-kubernetes/version.rb +5 -0
- data/lib/pangea-kubernetes.rb +43 -0
- data/pangea-kubernetes.gemspec +33 -0
- metadata +192 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pangea
|
|
4
|
+
module Kubernetes
|
|
5
|
+
# Lazy-loading registry for network backends.
|
|
6
|
+
# Mirrors BackendRegistry pattern for compute backends.
|
|
7
|
+
module NetworkBackendRegistry
|
|
8
|
+
NETWORK_MAP = {
|
|
9
|
+
vpc_cni: 'pangea/kubernetes/network_backends/vpc_cni',
|
|
10
|
+
cilium: 'pangea/kubernetes/network_backends/cilium',
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
ALIASES = {
|
|
14
|
+
aws_cni: :vpc_cni,
|
|
15
|
+
eni: :vpc_cni,
|
|
16
|
+
ebpf: :cilium,
|
|
17
|
+
}.freeze
|
|
18
|
+
|
|
19
|
+
CLASS_MAP = {
|
|
20
|
+
vpc_cni: 'Pangea::Kubernetes::NetworkBackends::VpcCni',
|
|
21
|
+
cilium: 'Pangea::Kubernetes::NetworkBackends::Cilium',
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
# Resolve a network backend by name.
|
|
25
|
+
#
|
|
26
|
+
# @param name [Symbol] Backend name or alias
|
|
27
|
+
# @return [Module] The network backend module
|
|
28
|
+
# @raise [ArgumentError] If the backend is unknown
|
|
29
|
+
def self.resolve(name)
|
|
30
|
+
name = ALIASES.fetch(name, name)
|
|
31
|
+
path = NETWORK_MAP.fetch(name) do
|
|
32
|
+
raise ArgumentError, "Unknown network backend: #{name}. Available: #{NETWORK_MAP.keys.join(', ')}"
|
|
33
|
+
end
|
|
34
|
+
require path
|
|
35
|
+
Object.const_get(CLASS_MAP.fetch(name))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Check if a network backend is compatible with a compute backend.
|
|
39
|
+
#
|
|
40
|
+
# @param network [Symbol] Network backend name
|
|
41
|
+
# @param compute [Symbol] Compute backend name
|
|
42
|
+
# @return [Boolean]
|
|
43
|
+
def self.compatible?(network, compute)
|
|
44
|
+
backend = resolve(network)
|
|
45
|
+
backend.compatible_backends.include?(compute)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @return [Array<Symbol>] All registered network backend names
|
|
49
|
+
def self.available
|
|
50
|
+
NETWORK_MAP.keys
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pangea
|
|
4
|
+
module Kubernetes
|
|
5
|
+
module NetworkBackends
|
|
6
|
+
# Contract interface for network backends. Each backend module
|
|
7
|
+
# provides CNI-specific infrastructure (IAM, config) and metadata.
|
|
8
|
+
#
|
|
9
|
+
# Network backends are orthogonal to compute backends — you can
|
|
10
|
+
# combine any network backend with any compatible compute backend:
|
|
11
|
+
#
|
|
12
|
+
# | Network Backend | EKS | K3s/NixOS | mTLS | L7 Obs | eBPF |
|
|
13
|
+
# |----------------|-----|-----------|------|--------|------|
|
|
14
|
+
# | vpc_cni | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
15
|
+
# | cilium_eni | ✅ | ❌ | ✅ | ✅ | ✅ |
|
|
16
|
+
# | cilium_overlay | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
17
|
+
# | calico | ✅ | ✅ | ❌ | ❌ | partial |
|
|
18
|
+
# | flannel | ❌ | ✅ | ❌ | ❌ | ❌ |
|
|
19
|
+
#
|
|
20
|
+
module Base
|
|
21
|
+
def self.included(base)
|
|
22
|
+
base.extend(ClassMethods)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
module ClassMethods
|
|
26
|
+
# @return [Symbol] Backend identifier (:vpc_cni, :cilium, :calico, :flannel)
|
|
27
|
+
def backend_name
|
|
28
|
+
raise NotImplementedError, "#{self} must implement .backend_name"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @return [Array<Symbol>] Compatible compute backends (:aws, :gcp, :azure, :hcloud, etc.)
|
|
32
|
+
def compatible_backends
|
|
33
|
+
raise NotImplementedError, "#{self} must implement .compatible_backends"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return [Boolean] Whether this backend provides service mesh capabilities
|
|
37
|
+
def mesh_capable?
|
|
38
|
+
false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [Boolean] Whether this backend provides L7 observability (e.g., Hubble)
|
|
42
|
+
def l7_observable?
|
|
43
|
+
false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Create cloud-level IAM resources for the CNI (e.g., IRSA for Cilium operator).
|
|
47
|
+
# Returns nil if no cloud IAM is needed.
|
|
48
|
+
#
|
|
49
|
+
# @param ctx [Object] Synthesizer context
|
|
50
|
+
# @param name [Symbol] Cluster name
|
|
51
|
+
# @param config [Hash] Network configuration
|
|
52
|
+
# @param tags [Hash] Resource tags
|
|
53
|
+
# @return [Hash, nil] IAM resources created
|
|
54
|
+
def create_network_iam(_ctx, _name, _config, _tags)
|
|
55
|
+
nil # Most network backends don't need cloud IAM
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Return the NixOS/blackmatter-kubernetes profile name for this backend.
|
|
59
|
+
# Used by NixOS backends in cloud-init to select the correct CNI config.
|
|
60
|
+
#
|
|
61
|
+
# @return [String, nil] Profile name (e.g., 'cilium-mesh', 'flannel-production')
|
|
62
|
+
def nixos_profile
|
|
63
|
+
nil # Managed backends (EKS/GKE/AKS) don't use NixOS profiles
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Return Helm values or configuration to be passed to the GitOps layer.
|
|
67
|
+
# This is what gets deployed via FluxCD HelmRelease.
|
|
68
|
+
#
|
|
69
|
+
# @param config [Hash] Network configuration
|
|
70
|
+
# @return [Hash] Configuration for the network backend's Helm chart
|
|
71
|
+
def helm_values(_config)
|
|
72
|
+
{}
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pangea
|
|
4
|
+
module Kubernetes
|
|
5
|
+
module NetworkBackends
|
|
6
|
+
# Cilium — eBPF-based CNI with service mesh and L7 observability.
|
|
7
|
+
# Supports ENI mode (AWS VPC IPs) and overlay mode (VXLAN/Geneve).
|
|
8
|
+
# Hubble provides per-request latency histograms without app instrumentation.
|
|
9
|
+
module Cilium
|
|
10
|
+
include Base
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def backend_name
|
|
14
|
+
:cilium
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def compatible_backends
|
|
18
|
+
%i[aws gcp azure hcloud aws_nixos gcp_nixos azure_nixos]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def mesh_capable?
|
|
22
|
+
true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def l7_observable?
|
|
26
|
+
true # Hubble
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# IRSA for Cilium operator on EKS.
|
|
30
|
+
def create_network_iam(ctx, name, config, tags)
|
|
31
|
+
return nil unless config[:compute_backend] == :aws
|
|
32
|
+
|
|
33
|
+
# Cilium operator needs permissions for ENI management
|
|
34
|
+
ctx.extend(Pangea::Resources::AWS) unless ctx.respond_to?(:aws_iam_role)
|
|
35
|
+
|
|
36
|
+
policy_doc = JSON.generate({
|
|
37
|
+
Version: '2012-10-17',
|
|
38
|
+
Statement: [{
|
|
39
|
+
Effect: 'Allow',
|
|
40
|
+
Action: [
|
|
41
|
+
'ec2:DescribeNetworkInterfaces',
|
|
42
|
+
'ec2:DescribeSubnets',
|
|
43
|
+
'ec2:DescribeVpcs',
|
|
44
|
+
'ec2:DescribeSecurityGroups',
|
|
45
|
+
'ec2:CreateNetworkInterface',
|
|
46
|
+
'ec2:AttachNetworkInterface',
|
|
47
|
+
'ec2:DeleteNetworkInterface',
|
|
48
|
+
'ec2:ModifyNetworkInterfaceAttribute',
|
|
49
|
+
'ec2:AssignPrivateIpAddresses',
|
|
50
|
+
'ec2:UnassignPrivateIpAddresses',
|
|
51
|
+
'ec2:CreateTags',
|
|
52
|
+
],
|
|
53
|
+
Resource: '*',
|
|
54
|
+
}],
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
policy = ctx.aws_iam_policy(:"#{name}-cilium-operator", {
|
|
58
|
+
name: "#{name}-cilium-operator",
|
|
59
|
+
policy: policy_doc,
|
|
60
|
+
tags: tags.merge(Component: 'cilium'),
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
{ policy: policy }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def nixos_profile
|
|
67
|
+
'cilium-mesh'
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def helm_values(config)
|
|
71
|
+
mode = config[:cilium_mode] || :eni
|
|
72
|
+
values = {
|
|
73
|
+
'ipam' => { 'mode' => mode.to_s },
|
|
74
|
+
'hubble' => {
|
|
75
|
+
'enabled' => true,
|
|
76
|
+
'relay' => { 'enabled' => true },
|
|
77
|
+
'ui' => { 'enabled' => false }, # No GUI, MCP-queryable via Grafana
|
|
78
|
+
'metrics' => {
|
|
79
|
+
'enabled' => [
|
|
80
|
+
'dns', 'drop', 'tcp', 'flow',
|
|
81
|
+
'icmp', 'http', 'port-distribution',
|
|
82
|
+
'httpV2:exemplars=true;labelsContext=source_ip,source_namespace,source_workload,destination_ip,destination_namespace,destination_workload',
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
'prometheus' => { 'enabled' => true },
|
|
87
|
+
'operator' => { 'prometheus' => { 'enabled' => true } },
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# ENI mode: use AWS VPC IPs (compatible with existing /20 pod subnets)
|
|
91
|
+
if mode == :eni
|
|
92
|
+
values['eni'] = {
|
|
93
|
+
'enabled' => true,
|
|
94
|
+
'awsEnablePrefixDelegation' => true,
|
|
95
|
+
}
|
|
96
|
+
values['tunnel'] = 'disabled'
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
values
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pangea
|
|
4
|
+
module Kubernetes
|
|
5
|
+
module NetworkBackends
|
|
6
|
+
# AWS VPC CNI — default EKS networking.
|
|
7
|
+
# Pods get VPC IP addresses via ENI attachment.
|
|
8
|
+
# No mesh, no mTLS, no L7 observability.
|
|
9
|
+
module VpcCni
|
|
10
|
+
include Base
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def backend_name
|
|
14
|
+
:vpc_cni
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def compatible_backends
|
|
18
|
+
[:aws]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def mesh_capable?
|
|
22
|
+
false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def l7_observable?
|
|
26
|
+
false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def nixos_profile
|
|
30
|
+
nil # VPC CNI is EKS-only, no NixOS support
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pangea
|
|
4
|
+
module Kubernetes
|
|
5
|
+
module Types
|
|
6
|
+
# ArgoCD GitOps bootstrap configuration.
|
|
7
|
+
#
|
|
8
|
+
# Parallel to FluxCDConfig — provides the same role but for ArgoCD.
|
|
9
|
+
# The bootstrap service writes ArgoCD manifests to the k3s auto-deploy
|
|
10
|
+
# directory at boot, then creates git auth credentials after API is ready.
|
|
11
|
+
class ArgocdConfig < Pangea::Resources::BaseAttributes
|
|
12
|
+
transform_keys(&:to_sym)
|
|
13
|
+
|
|
14
|
+
attribute :enabled, T::Bool.default(true)
|
|
15
|
+
attribute :repo_url, T::String
|
|
16
|
+
attribute :target_revision, T::String.default('HEAD')
|
|
17
|
+
attribute :path, T::String.default('./')
|
|
18
|
+
attribute :project, T::String.default('default')
|
|
19
|
+
attribute :sync_policy, T::String.constrained(included_in: %w[automated manual]).default('automated')
|
|
20
|
+
attribute :auto_prune, T::Bool.default(true)
|
|
21
|
+
attribute :self_heal, T::Bool.default(true)
|
|
22
|
+
|
|
23
|
+
# Auth
|
|
24
|
+
attribute :auth_type, T::String.constrained(included_in: %w[ssh token]).default('ssh')
|
|
25
|
+
attribute :ssh_key_file, T::String.optional.default(nil)
|
|
26
|
+
attribute :token_file, T::String.optional.default(nil)
|
|
27
|
+
attribute :token_username, T::String.default('git')
|
|
28
|
+
|
|
29
|
+
# SOPS
|
|
30
|
+
attribute :sops_enabled, T::Bool.default(false)
|
|
31
|
+
attribute :sops_age_key_file, T::String.optional.default(nil)
|
|
32
|
+
|
|
33
|
+
def to_h
|
|
34
|
+
hash = {
|
|
35
|
+
enabled: enabled,
|
|
36
|
+
repo_url: repo_url,
|
|
37
|
+
target_revision: target_revision,
|
|
38
|
+
path: path,
|
|
39
|
+
project: project,
|
|
40
|
+
sync_policy: sync_policy,
|
|
41
|
+
auto_prune: auto_prune,
|
|
42
|
+
self_heal: self_heal,
|
|
43
|
+
auth_type: auth_type,
|
|
44
|
+
token_username: token_username
|
|
45
|
+
}
|
|
46
|
+
hash[:ssh_key_file] = ssh_key_file if ssh_key_file
|
|
47
|
+
hash[:token_file] = token_file if token_file
|
|
48
|
+
hash[:sops_enabled] = sops_enabled if sops_enabled
|
|
49
|
+
hash[:sops_age_key_file] = sops_age_key_file if sops_age_key_file
|
|
50
|
+
hash
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'pangea/resources/types'
|
|
4
|
+
|
|
5
|
+
module Pangea
|
|
6
|
+
module Kubernetes
|
|
7
|
+
module Types
|
|
8
|
+
# Control plane configuration for blackmatter-kubernetes NixOS modules.
|
|
9
|
+
# Maps to `controlPlane.*` options in the NixOS module.
|
|
10
|
+
# Only relevant for vanilla Kubernetes (k3s manages its own control plane).
|
|
11
|
+
class ControlPlaneConfig < Pangea::Resources::BaseAttributes
|
|
12
|
+
transform_keys(&:to_sym)
|
|
13
|
+
|
|
14
|
+
# Extra args for kube-apiserver
|
|
15
|
+
attribute :api_server_extra_args, T::Hash.default({}.freeze)
|
|
16
|
+
|
|
17
|
+
# Extra SANs for the API server certificate
|
|
18
|
+
attribute :api_server_extra_sans, T::Array.of(T::String).default([].freeze)
|
|
19
|
+
|
|
20
|
+
# Extra args for kube-controller-manager
|
|
21
|
+
attribute :controller_manager_extra_args, T::Hash.default({}.freeze)
|
|
22
|
+
|
|
23
|
+
# Extra args for kube-scheduler
|
|
24
|
+
attribute :scheduler_extra_args, T::Hash.default({}.freeze)
|
|
25
|
+
|
|
26
|
+
# Disable kube-proxy (for CNIs that replace it, like Cilium)
|
|
27
|
+
attribute :disable_kube_proxy, T::Bool.default(false)
|
|
28
|
+
|
|
29
|
+
# Extra args for kube-proxy (when not disabled)
|
|
30
|
+
attribute :kube_proxy_extra_args, T::Hash.default({}.freeze)
|
|
31
|
+
|
|
32
|
+
# Use external etcd instead of co-located
|
|
33
|
+
attribute :etcd_external, T::Bool.default(false)
|
|
34
|
+
|
|
35
|
+
# External etcd endpoints
|
|
36
|
+
attribute :etcd_endpoints, T::Array.of(T::String).default([].freeze)
|
|
37
|
+
|
|
38
|
+
# External etcd CA file
|
|
39
|
+
attribute :etcd_ca_file, T::String.optional.default(nil)
|
|
40
|
+
|
|
41
|
+
# External etcd cert file
|
|
42
|
+
attribute :etcd_cert_file, T::String.optional.default(nil)
|
|
43
|
+
|
|
44
|
+
# External etcd key file
|
|
45
|
+
attribute :etcd_key_file, T::String.optional.default(nil)
|
|
46
|
+
|
|
47
|
+
def to_h
|
|
48
|
+
hash = {}
|
|
49
|
+
hash[:api_server_extra_args] = api_server_extra_args if api_server_extra_args.any?
|
|
50
|
+
hash[:api_server_extra_sans] = api_server_extra_sans if api_server_extra_sans.any?
|
|
51
|
+
hash[:controller_manager_extra_args] = controller_manager_extra_args if controller_manager_extra_args.any?
|
|
52
|
+
hash[:scheduler_extra_args] = scheduler_extra_args if scheduler_extra_args.any?
|
|
53
|
+
hash[:disable_kube_proxy] = disable_kube_proxy if disable_kube_proxy
|
|
54
|
+
hash[:kube_proxy_extra_args] = kube_proxy_extra_args if kube_proxy_extra_args.any?
|
|
55
|
+
hash[:etcd_external] = etcd_external if etcd_external
|
|
56
|
+
hash[:etcd_endpoints] = etcd_endpoints if etcd_endpoints.any?
|
|
57
|
+
hash[:etcd_ca_file] = etcd_ca_file if etcd_ca_file
|
|
58
|
+
hash[:etcd_cert_file] = etcd_cert_file if etcd_cert_file
|
|
59
|
+
hash[:etcd_key_file] = etcd_key_file if etcd_key_file
|
|
60
|
+
hash
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'pangea/resources/types'
|
|
4
|
+
|
|
5
|
+
module Pangea
|
|
6
|
+
module Kubernetes
|
|
7
|
+
module Types
|
|
8
|
+
# Etcd configuration for blackmatter-kubernetes NixOS modules.
|
|
9
|
+
# Maps to `etcd.*` options in the NixOS module.
|
|
10
|
+
# Only relevant for vanilla Kubernetes (k3s embeds etcd).
|
|
11
|
+
class EtcdConfig < Pangea::Resources::BaseAttributes
|
|
12
|
+
transform_keys(&:to_sym)
|
|
13
|
+
|
|
14
|
+
# Initial cluster state: 'new' or 'existing'
|
|
15
|
+
attribute :initial_cluster_state, T::String.constrained(
|
|
16
|
+
included_in: %w[new existing]
|
|
17
|
+
).default('new')
|
|
18
|
+
|
|
19
|
+
# Data directory for etcd
|
|
20
|
+
attribute :data_dir, T::String.default('/var/lib/etcd')
|
|
21
|
+
|
|
22
|
+
# External etcd endpoints (when not co-located with control plane)
|
|
23
|
+
attribute :external_endpoints, T::Array.of(T::String).default([].freeze)
|
|
24
|
+
|
|
25
|
+
# Snapshot count before compaction
|
|
26
|
+
attribute :snapshot_count, T::Coercible::Integer.optional.default(nil)
|
|
27
|
+
|
|
28
|
+
# Heartbeat interval in ms
|
|
29
|
+
attribute :heartbeat_interval, T::Coercible::Integer.optional.default(nil)
|
|
30
|
+
|
|
31
|
+
# Election timeout in ms
|
|
32
|
+
attribute :election_timeout, T::Coercible::Integer.optional.default(nil)
|
|
33
|
+
|
|
34
|
+
# CA certificate file path (for external etcd)
|
|
35
|
+
attribute :ca_file, T::String.optional.default(nil)
|
|
36
|
+
|
|
37
|
+
# Client certificate file path (for external etcd)
|
|
38
|
+
attribute :cert_file, T::String.optional.default(nil)
|
|
39
|
+
|
|
40
|
+
# Client key file path (for external etcd)
|
|
41
|
+
attribute :key_file, T::String.optional.default(nil)
|
|
42
|
+
|
|
43
|
+
def external?
|
|
44
|
+
external_endpoints.any?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_h
|
|
48
|
+
hash = {
|
|
49
|
+
initial_cluster_state: initial_cluster_state,
|
|
50
|
+
data_dir: data_dir
|
|
51
|
+
}
|
|
52
|
+
hash[:external_endpoints] = external_endpoints if external_endpoints.any?
|
|
53
|
+
hash[:snapshot_count] = snapshot_count if snapshot_count
|
|
54
|
+
hash[:heartbeat_interval] = heartbeat_interval if heartbeat_interval
|
|
55
|
+
hash[:election_timeout] = election_timeout if election_timeout
|
|
56
|
+
hash[:ca_file] = ca_file if ca_file
|
|
57
|
+
hash[:cert_file] = cert_file if cert_file
|
|
58
|
+
hash[:key_file] = key_file if key_file
|
|
59
|
+
hash
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'pangea/resources/types'
|
|
4
|
+
|
|
5
|
+
module Pangea
|
|
6
|
+
module Kubernetes
|
|
7
|
+
module Types
|
|
8
|
+
# Firewall configuration for blackmatter-kubernetes NixOS modules.
|
|
9
|
+
# Maps to `firewall.*` options in the NixOS module.
|
|
10
|
+
class FirewallConfig < Pangea::Resources::BaseAttributes
|
|
11
|
+
transform_keys(&:to_sym)
|
|
12
|
+
|
|
13
|
+
# Enable the firewall module
|
|
14
|
+
attribute :enabled, T::Bool.default(true)
|
|
15
|
+
|
|
16
|
+
# Additional TCP ports to open (beyond K8s defaults)
|
|
17
|
+
attribute :extra_tcp_ports, T::Array.of(T::Coercible::Integer).default([].freeze)
|
|
18
|
+
|
|
19
|
+
# Additional UDP ports to open (beyond K8s defaults)
|
|
20
|
+
attribute :extra_udp_ports, T::Array.of(T::Coercible::Integer).default([].freeze)
|
|
21
|
+
|
|
22
|
+
# Trusted source CIDRs for internal traffic
|
|
23
|
+
attribute :trusted_cidrs, T::Array.of(T::String).default([].freeze)
|
|
24
|
+
|
|
25
|
+
# Allow all intra-cluster traffic
|
|
26
|
+
attribute :allow_intra_cluster, T::Bool.default(true)
|
|
27
|
+
|
|
28
|
+
def to_h
|
|
29
|
+
hash = { enabled: enabled }
|
|
30
|
+
hash[:extra_tcp_ports] = extra_tcp_ports if extra_tcp_ports.any?
|
|
31
|
+
hash[:extra_udp_ports] = extra_udp_ports if extra_udp_ports.any?
|
|
32
|
+
hash[:trusted_cidrs] = trusted_cidrs if trusted_cidrs.any?
|
|
33
|
+
hash[:allow_intra_cluster] = allow_intra_cluster
|
|
34
|
+
hash
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'pangea/resources/types'
|
|
4
|
+
require 'pangea/kubernetes/types/firewall_config'
|
|
5
|
+
require 'pangea/kubernetes/types/kernel_config'
|
|
6
|
+
require 'pangea/kubernetes/types/wait_for_dns_config'
|
|
7
|
+
|
|
8
|
+
module Pangea
|
|
9
|
+
module Kubernetes
|
|
10
|
+
module Types
|
|
11
|
+
# K3s distribution configuration for blackmatter-kubernetes NixOS modules.
|
|
12
|
+
# Maps to `services.blackmatter.k3s.*` options.
|
|
13
|
+
# Covers all k3s-specific settings that NixOS modules expose.
|
|
14
|
+
class K3sConfig < Pangea::Resources::BaseAttributes
|
|
15
|
+
transform_keys(&:to_sym)
|
|
16
|
+
|
|
17
|
+
# Cluster CIDR for pod networking
|
|
18
|
+
attribute :cluster_cidr, T::String.optional.default(nil)
|
|
19
|
+
|
|
20
|
+
# Service CIDR for service networking
|
|
21
|
+
attribute :service_cidr, T::String.optional.default(nil)
|
|
22
|
+
|
|
23
|
+
# Cluster DNS address
|
|
24
|
+
attribute :cluster_dns, T::String.optional.default(nil)
|
|
25
|
+
|
|
26
|
+
# Node name override
|
|
27
|
+
attribute :node_name, T::String.optional.default(nil)
|
|
28
|
+
|
|
29
|
+
# Node labels (key => value)
|
|
30
|
+
attribute :node_labels, T::Hash.default({}.freeze)
|
|
31
|
+
|
|
32
|
+
# Node taints (array of taint strings, e.g., "key=value:NoSchedule")
|
|
33
|
+
attribute :node_taints, T::Array.of(T::String).default([].freeze)
|
|
34
|
+
|
|
35
|
+
# Node IP address override
|
|
36
|
+
attribute :node_ip, T::String.optional.default(nil)
|
|
37
|
+
|
|
38
|
+
# Extra flags passed to k3s server/agent
|
|
39
|
+
attribute :extra_flags, T::Array.of(T::String).default([].freeze)
|
|
40
|
+
|
|
41
|
+
# Data directory for k3s
|
|
42
|
+
attribute :data_dir, T::String.optional.default(nil)
|
|
43
|
+
|
|
44
|
+
# Config file path for k3s
|
|
45
|
+
attribute :config_path, T::String.optional.default(nil)
|
|
46
|
+
|
|
47
|
+
# Environment file for k3s systemd service
|
|
48
|
+
attribute :environment_file, T::String.optional.default(nil)
|
|
49
|
+
|
|
50
|
+
# Containerd config template path
|
|
51
|
+
attribute :containerd_config_template, T::String.optional.default(nil)
|
|
52
|
+
|
|
53
|
+
# K3s components to disable (e.g., ['traefik', 'servicelb'])
|
|
54
|
+
attribute :disable, T::Array.of(T::String).default([].freeze)
|
|
55
|
+
|
|
56
|
+
# Disable the agent on server nodes
|
|
57
|
+
attribute :disable_agent, T::Bool.default(false)
|
|
58
|
+
|
|
59
|
+
# Extra kubelet args (key => value)
|
|
60
|
+
attribute :extra_kubelet_config, T::Hash.default({}.freeze)
|
|
61
|
+
|
|
62
|
+
# Extra kube-proxy args (key => value)
|
|
63
|
+
attribute :extra_kube_proxy_config, T::Hash.default({}.freeze)
|
|
64
|
+
|
|
65
|
+
# Auto-deploying manifests directory content
|
|
66
|
+
attribute :manifests, T::Hash.default({}.freeze)
|
|
67
|
+
|
|
68
|
+
# Firewall configuration
|
|
69
|
+
attribute :firewall, FirewallConfig.optional.default(nil)
|
|
70
|
+
|
|
71
|
+
# Kernel configuration
|
|
72
|
+
attribute :kernel, KernelConfig.optional.default(nil)
|
|
73
|
+
|
|
74
|
+
# DNS wait configuration
|
|
75
|
+
attribute :wait_for_dns, WaitForDNSConfig.optional.default(nil)
|
|
76
|
+
|
|
77
|
+
# Enable NVIDIA GPU support
|
|
78
|
+
attribute :nvidia_enable, T::Bool.default(false)
|
|
79
|
+
|
|
80
|
+
# Enable graceful node shutdown
|
|
81
|
+
attribute :graceful_node_shutdown, T::Bool.default(true)
|
|
82
|
+
|
|
83
|
+
def to_h
|
|
84
|
+
hash = {}
|
|
85
|
+
hash[:cluster_cidr] = cluster_cidr if cluster_cidr
|
|
86
|
+
hash[:service_cidr] = service_cidr if service_cidr
|
|
87
|
+
hash[:cluster_dns] = cluster_dns if cluster_dns
|
|
88
|
+
hash[:node_name] = node_name if node_name
|
|
89
|
+
hash[:node_labels] = node_labels if node_labels.any?
|
|
90
|
+
hash[:node_taints] = node_taints if node_taints.any?
|
|
91
|
+
hash[:node_ip] = node_ip if node_ip
|
|
92
|
+
hash[:extra_flags] = extra_flags if extra_flags.any?
|
|
93
|
+
hash[:data_dir] = data_dir if data_dir
|
|
94
|
+
hash[:config_path] = config_path if config_path
|
|
95
|
+
hash[:environment_file] = environment_file if environment_file
|
|
96
|
+
hash[:containerd_config_template] = containerd_config_template if containerd_config_template
|
|
97
|
+
hash[:disable] = disable if disable.any?
|
|
98
|
+
hash[:disable_agent] = disable_agent if disable_agent
|
|
99
|
+
hash[:extra_kubelet_config] = extra_kubelet_config if extra_kubelet_config.any?
|
|
100
|
+
hash[:extra_kube_proxy_config] = extra_kube_proxy_config if extra_kube_proxy_config.any?
|
|
101
|
+
hash[:manifests] = manifests if manifests.any?
|
|
102
|
+
hash[:firewall] = firewall.to_h if firewall
|
|
103
|
+
hash[:kernel] = kernel.to_h if kernel
|
|
104
|
+
hash[:wait_for_dns] = wait_for_dns.to_h if wait_for_dns
|
|
105
|
+
hash[:nvidia_enable] = nvidia_enable if nvidia_enable
|
|
106
|
+
hash[:graceful_node_shutdown] = graceful_node_shutdown
|
|
107
|
+
hash
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'pangea/resources/types'
|
|
4
|
+
|
|
5
|
+
module Pangea
|
|
6
|
+
module Kubernetes
|
|
7
|
+
module Types
|
|
8
|
+
# Kernel configuration for blackmatter-kubernetes NixOS modules.
|
|
9
|
+
# Maps to `kernel.*` options in the NixOS module.
|
|
10
|
+
class KernelConfig < Pangea::Resources::BaseAttributes
|
|
11
|
+
transform_keys(&:to_sym)
|
|
12
|
+
|
|
13
|
+
# Extra kernel modules to load at boot
|
|
14
|
+
attribute :extra_modules, T::Array.of(T::String).default([].freeze)
|
|
15
|
+
|
|
16
|
+
# Kernel sysctl parameters (key => value)
|
|
17
|
+
attribute :sysctl, T::Hash.default({}.freeze)
|
|
18
|
+
|
|
19
|
+
# Enable kernel hardening (sysctl defaults for K8s)
|
|
20
|
+
attribute :hardening, T::Bool.default(true)
|
|
21
|
+
|
|
22
|
+
def to_h
|
|
23
|
+
hash = { hardening: hardening }
|
|
24
|
+
hash[:extra_modules] = extra_modules if extra_modules.any?
|
|
25
|
+
hash[:sysctl] = sysctl if sysctl.any?
|
|
26
|
+
hash
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|