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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/auto-bump.yml +11 -0
  3. data/.github/workflows/ci.yml +7 -0
  4. data/.github/workflows/release.yml +22 -0
  5. data/.gitignore +6 -0
  6. data/.rspec +3 -0
  7. data/AGENTS.md +3 -0
  8. data/CLAUDE.md +370 -0
  9. data/Gemfile +9 -0
  10. data/Gemfile.lock +128 -0
  11. data/README.md +42 -0
  12. data/Rakefile +8 -0
  13. data/flake.lock +2144 -0
  14. data/flake.nix +30 -0
  15. data/gemset.nix +312 -0
  16. data/lib/pangea/kubernetes/architecture.rb +383 -0
  17. data/lib/pangea/kubernetes/backend_registry.rb +117 -0
  18. data/lib/pangea/kubernetes/backends/aws_eks.rb +203 -0
  19. data/lib/pangea/kubernetes/backends/aws_nixos.rb +1347 -0
  20. data/lib/pangea/kubernetes/backends/azure_aks.rb +145 -0
  21. data/lib/pangea/kubernetes/backends/azure_nixos.rb +275 -0
  22. data/lib/pangea/kubernetes/backends/base.rb +116 -0
  23. data/lib/pangea/kubernetes/backends/gcp_gke.rb +176 -0
  24. data/lib/pangea/kubernetes/backends/gcp_nixos.rb +240 -0
  25. data/lib/pangea/kubernetes/backends/hcloud_k3s.rb +181 -0
  26. data/lib/pangea/kubernetes/backends/nixos_base.rb +235 -0
  27. data/lib/pangea/kubernetes/bare_metal/cloud_init.rb +196 -0
  28. data/lib/pangea/kubernetes/bare_metal/cluster_reference.rb +72 -0
  29. data/lib/pangea/kubernetes/load_balancer.rb +157 -0
  30. data/lib/pangea/kubernetes/network_backend_registry.rb +54 -0
  31. data/lib/pangea/kubernetes/network_backends/base.rb +78 -0
  32. data/lib/pangea/kubernetes/network_backends/cilium.rb +105 -0
  33. data/lib/pangea/kubernetes/network_backends/vpc_cni.rb +36 -0
  34. data/lib/pangea/kubernetes/types/argocd_config.rb +55 -0
  35. data/lib/pangea/kubernetes/types/control_plane_config.rb +65 -0
  36. data/lib/pangea/kubernetes/types/etcd_config.rb +64 -0
  37. data/lib/pangea/kubernetes/types/firewall_config.rb +39 -0
  38. data/lib/pangea/kubernetes/types/k3s_config.rb +112 -0
  39. data/lib/pangea/kubernetes/types/kernel_config.rb +31 -0
  40. data/lib/pangea/kubernetes/types/kubernetes_config.rb +129 -0
  41. data/lib/pangea/kubernetes/types/persistent_state_config.rb +100 -0
  42. data/lib/pangea/kubernetes/types/pki_config.rb +48 -0
  43. data/lib/pangea/kubernetes/types/secrets_config.rb +41 -0
  44. data/lib/pangea/kubernetes/types/vpn_config.rb +188 -0
  45. data/lib/pangea/kubernetes/types/wait_for_dns_config.rb +35 -0
  46. data/lib/pangea/kubernetes/types.rb +521 -0
  47. data/lib/pangea-kubernetes/version.rb +5 -0
  48. data/lib/pangea-kubernetes.rb +43 -0
  49. data/pangea-kubernetes.gemspec +33 -0
  50. 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