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,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2025 The Pangea Authors
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
require 'pangea/kubernetes/backends/base'
|
|
18
|
+
|
|
19
|
+
module Pangea
|
|
20
|
+
module Kubernetes
|
|
21
|
+
module Backends
|
|
22
|
+
# Azure AKS backend — creates managed AKS clusters.
|
|
23
|
+
# AKS bundles default node pool with the cluster resource,
|
|
24
|
+
# so create_cluster handles both.
|
|
25
|
+
module AzureAks
|
|
26
|
+
include Base
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
def backend_name = :azure
|
|
30
|
+
def managed_kubernetes? = true
|
|
31
|
+
def required_gem = 'pangea-azure'
|
|
32
|
+
|
|
33
|
+
def load_provider!
|
|
34
|
+
require required_gem
|
|
35
|
+
rescue LoadError => e
|
|
36
|
+
raise LoadError,
|
|
37
|
+
"Backend :azure requires gem 'pangea-azure'. " \
|
|
38
|
+
"Add it to your Gemfile: gem 'pangea-azure'\n" \
|
|
39
|
+
"Original error: #{e.message}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Create Azure VNet + subnet
|
|
43
|
+
def create_network(ctx, name, config, tags)
|
|
44
|
+
network = Architecture::AzureNetworkResult.new
|
|
45
|
+
|
|
46
|
+
network.resource_group = ctx.azurerm_resource_group(
|
|
47
|
+
:"#{name}_rg",
|
|
48
|
+
name: "#{name}-rg",
|
|
49
|
+
location: config.region,
|
|
50
|
+
tags: tags
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
network.vnet = ctx.azurerm_virtual_network(
|
|
54
|
+
:"#{name}_vnet",
|
|
55
|
+
name: "#{name}-vnet",
|
|
56
|
+
resource_group_name: network.resource_group.ref(:name),
|
|
57
|
+
location: config.region,
|
|
58
|
+
address_space: [config.network&.vpc_cidr || '10.0.0.0/16'],
|
|
59
|
+
tags: tags
|
|
60
|
+
)
|
|
61
|
+
network.vpc = network.vnet
|
|
62
|
+
|
|
63
|
+
subnet = ctx.azurerm_subnet(
|
|
64
|
+
:"#{name}_subnet",
|
|
65
|
+
name: "#{name}-subnet",
|
|
66
|
+
resource_group_name: network.resource_group.ref(:name),
|
|
67
|
+
virtual_network_name: network.vnet.ref(:name),
|
|
68
|
+
address_prefixes: [config.network&.pod_cidr || '10.0.1.0/24']
|
|
69
|
+
)
|
|
70
|
+
network.add_subnet(:subnet, subnet)
|
|
71
|
+
|
|
72
|
+
network
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# AKS uses managed identity — no standalone IAM resources needed
|
|
76
|
+
def create_iam(_ctx, _name, _config, _tags)
|
|
77
|
+
Architecture::IamResult.new
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Create AKS cluster with default node pool
|
|
81
|
+
def create_cluster(ctx, name, config, result, tags)
|
|
82
|
+
system_pool = config.system_node_pool
|
|
83
|
+
rg_name = config.resource_group_name || result.network&.dig(:resource_group)&.name || "#{name}-rg"
|
|
84
|
+
dns_prefix = config.dns_prefix || name.to_s
|
|
85
|
+
|
|
86
|
+
cluster_attrs = {
|
|
87
|
+
name: "#{name}-cluster",
|
|
88
|
+
resource_group_name: rg_name,
|
|
89
|
+
location: config.region,
|
|
90
|
+
dns_prefix: dns_prefix,
|
|
91
|
+
kubernetes_version: config.kubernetes_version,
|
|
92
|
+
default_node_pool: {
|
|
93
|
+
name: system_pool.name.to_s[0..11], # AKS max 12 chars
|
|
94
|
+
vm_size: system_pool.instance_types.first,
|
|
95
|
+
node_count: system_pool.effective_desired_size,
|
|
96
|
+
min_count: system_pool.min_size,
|
|
97
|
+
max_count: system_pool.max_size,
|
|
98
|
+
enable_auto_scaling: true,
|
|
99
|
+
os_disk_size_gb: system_pool.disk_size_gb
|
|
100
|
+
},
|
|
101
|
+
identity: { type: 'SystemAssigned' },
|
|
102
|
+
tags: tags
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
cluster_attrs[:sku_tier] = 'Standard' if tags[:Environment] == 'production'
|
|
106
|
+
|
|
107
|
+
# Network profile
|
|
108
|
+
if result.network&.dig(:subnet)
|
|
109
|
+
cluster_attrs[:default_node_pool][:vnet_subnet_id] = result.network[:subnet].id
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
ctx.azurerm_kubernetes_cluster(:"#{name}_cluster", cluster_attrs)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Create additional AKS node pool
|
|
116
|
+
def create_node_pool(ctx, name, cluster_ref, pool_config, tags)
|
|
117
|
+
pool_name = :"#{name}_#{pool_config.name}"
|
|
118
|
+
|
|
119
|
+
node_pool_attrs = {
|
|
120
|
+
name: pool_config.name.to_s[0..11], # AKS max 12 chars
|
|
121
|
+
kubernetes_cluster_id: cluster_ref.id,
|
|
122
|
+
vm_size: pool_config.instance_types.first,
|
|
123
|
+
node_count: pool_config.effective_desired_size,
|
|
124
|
+
min_count: pool_config.min_size,
|
|
125
|
+
max_count: pool_config.max_size,
|
|
126
|
+
enable_auto_scaling: true,
|
|
127
|
+
os_disk_size_gb: pool_config.disk_size_gb,
|
|
128
|
+
tags: tags.merge(NodePool: pool_config.name.to_s)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
node_pool_attrs[:node_labels] = pool_config.labels if pool_config.labels.any?
|
|
132
|
+
|
|
133
|
+
if pool_config.taints.any?
|
|
134
|
+
node_pool_attrs[:node_taints] = pool_config.taints.map do |t|
|
|
135
|
+
"#{t[:key]}=#{t[:value]}:#{t[:effect]}"
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
ctx.azurerm_kubernetes_cluster_node_pool(pool_name, node_pool_attrs)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2025 The Pangea Authors
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
require 'pangea/kubernetes/backends/base'
|
|
18
|
+
require 'pangea/kubernetes/backends/nixos_base'
|
|
19
|
+
|
|
20
|
+
module Pangea
|
|
21
|
+
module Kubernetes
|
|
22
|
+
module Backends
|
|
23
|
+
# Azure NixOS backend — Azure VMs running NixOS with k3s/k8s
|
|
24
|
+
# via blackmatter-kubernetes modules.
|
|
25
|
+
#
|
|
26
|
+
# Uses:
|
|
27
|
+
# - Azure VMs for control plane (static)
|
|
28
|
+
# - VM Scale Sets (VMSS) for worker node pools
|
|
29
|
+
# - VNet + NSG for networking
|
|
30
|
+
#
|
|
31
|
+
# No managed K8s services (AKS) — all k3s/k8s managed by NixOS.
|
|
32
|
+
module AzureNixos
|
|
33
|
+
include Base
|
|
34
|
+
extend NixosBase
|
|
35
|
+
|
|
36
|
+
class << self
|
|
37
|
+
def backend_name = :azure_nixos
|
|
38
|
+
def managed_kubernetes? = false
|
|
39
|
+
def required_gem = 'pangea-azure'
|
|
40
|
+
|
|
41
|
+
def load_provider!
|
|
42
|
+
require required_gem
|
|
43
|
+
rescue LoadError => e
|
|
44
|
+
raise LoadError,
|
|
45
|
+
"Backend :azure_nixos requires gem 'pangea-azure'. " \
|
|
46
|
+
"Add it to your Gemfile: gem 'pangea-azure'\n" \
|
|
47
|
+
"Original error: #{e.message}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Create Resource Group + VNet + Subnet + NSG
|
|
51
|
+
def create_network(ctx, name, config, tags)
|
|
52
|
+
network = Architecture::AzureNetworkResult.new
|
|
53
|
+
|
|
54
|
+
network.resource_group = ctx.azurerm_resource_group(
|
|
55
|
+
:"#{name}_rg",
|
|
56
|
+
name: "#{name}-rg",
|
|
57
|
+
location: config.region,
|
|
58
|
+
tags: tags
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
network.vnet = ctx.azurerm_virtual_network(
|
|
62
|
+
:"#{name}_vnet",
|
|
63
|
+
name: "#{name}-vnet",
|
|
64
|
+
resource_group_name: network.resource_group.ref(:name),
|
|
65
|
+
location: config.region,
|
|
66
|
+
address_space: [config.network&.vpc_cidr || '10.0.0.0/16'],
|
|
67
|
+
tags: tags
|
|
68
|
+
)
|
|
69
|
+
network.vpc = network.vnet
|
|
70
|
+
|
|
71
|
+
subnet = ctx.azurerm_subnet(
|
|
72
|
+
:"#{name}_subnet",
|
|
73
|
+
name: "#{name}-subnet",
|
|
74
|
+
resource_group_name: network.resource_group.ref(:name),
|
|
75
|
+
virtual_network_name: network.vnet.ref(:name),
|
|
76
|
+
address_prefixes: [config.network&.pod_cidr || '10.0.1.0/24']
|
|
77
|
+
)
|
|
78
|
+
network.add_subnet(:subnet, subnet)
|
|
79
|
+
|
|
80
|
+
# Network Security Group
|
|
81
|
+
network.nsg = ctx.azurerm_network_security_group(
|
|
82
|
+
:"#{name}_nsg",
|
|
83
|
+
name: "#{name}-nsg",
|
|
84
|
+
resource_group_name: network.resource_group.ref(:name),
|
|
85
|
+
location: config.region,
|
|
86
|
+
security_rule: azure_nsg_rules(config.distribution),
|
|
87
|
+
tags: tags
|
|
88
|
+
)
|
|
89
|
+
network.sg = network.nsg
|
|
90
|
+
|
|
91
|
+
# Associate NSG with subnet
|
|
92
|
+
ctx.azurerm_subnet_network_security_group_association(
|
|
93
|
+
:"#{name}_nsg_assoc",
|
|
94
|
+
subnet_id: subnet.id,
|
|
95
|
+
network_security_group_id: network.nsg.id
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
network
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# No standalone IAM — Azure VMs use Managed Identity
|
|
102
|
+
def create_iam(_ctx, _name, _config, _tags)
|
|
103
|
+
Architecture::IamResult.new
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Create control plane Azure VMs (static)
|
|
107
|
+
def create_cluster(ctx, name, config, result, tags)
|
|
108
|
+
nixos_create_cluster(ctx, name, config, result, tags)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Create worker node pool via VMSS (VM Scale Set)
|
|
112
|
+
def create_node_pool(ctx, name, cluster_ref, pool_config, tags)
|
|
113
|
+
nixos_create_node_pool(ctx, name, cluster_ref, pool_config, tags)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# --- NixosBase template hooks ---
|
|
117
|
+
|
|
118
|
+
def create_compute_instance(ctx, name, config, result, cloud_init, index, tags)
|
|
119
|
+
system_pool = config.system_node_pool
|
|
120
|
+
vm_size = system_pool.instance_types.first
|
|
121
|
+
image_id = config.azure_image_id || config.nixos&.image_id
|
|
122
|
+
rg_name = config.resource_group_name || result.network&.dig(:resource_group)&.name || "#{name}-rg"
|
|
123
|
+
|
|
124
|
+
# Network interface
|
|
125
|
+
nic = ctx.azurerm_network_interface(
|
|
126
|
+
:"#{name}_cp_#{index}_nic",
|
|
127
|
+
name: "#{name}-cp-#{index}-nic",
|
|
128
|
+
resource_group_name: rg_name,
|
|
129
|
+
location: config.region,
|
|
130
|
+
ip_configuration: {
|
|
131
|
+
name: 'internal',
|
|
132
|
+
subnet_id: result.network&.dig(:subnet)&.id,
|
|
133
|
+
private_ip_address_allocation: 'Dynamic',
|
|
134
|
+
public_ip_address_id: nil
|
|
135
|
+
},
|
|
136
|
+
tags: tags
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
ctx.azurerm_linux_virtual_machine(
|
|
140
|
+
:"#{name}_cp_#{index}",
|
|
141
|
+
name: "#{name}-cp-#{index}",
|
|
142
|
+
resource_group_name: rg_name,
|
|
143
|
+
location: config.region,
|
|
144
|
+
size: vm_size,
|
|
145
|
+
network_interface_ids: [nic.id],
|
|
146
|
+
admin_username: 'nixos',
|
|
147
|
+
admin_ssh_key: {
|
|
148
|
+
username: 'nixos',
|
|
149
|
+
public_key: '${file("~/.ssh/id_ed25519.pub")}'
|
|
150
|
+
},
|
|
151
|
+
os_disk: {
|
|
152
|
+
caching: 'ReadWrite',
|
|
153
|
+
storage_account_type: 'Premium_LRS',
|
|
154
|
+
disk_size_gb: system_pool.disk_size_gb
|
|
155
|
+
},
|
|
156
|
+
source_image_id: image_id,
|
|
157
|
+
custom_data: cloud_init,
|
|
158
|
+
identity: { type: 'SystemAssigned' },
|
|
159
|
+
tags: tags.merge(
|
|
160
|
+
Role: 'control-plane',
|
|
161
|
+
NodeIndex: index.to_s,
|
|
162
|
+
Distribution: config.distribution.to_s
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def create_worker_pool(ctx, name, _cluster_ref, pool_config, cloud_init, tags)
|
|
168
|
+
pool_name = :"#{name}_#{pool_config.name}"
|
|
169
|
+
vm_size = pool_config.instance_types.first
|
|
170
|
+
|
|
171
|
+
vmss = ctx.azurerm_linux_virtual_machine_scale_set(
|
|
172
|
+
pool_name,
|
|
173
|
+
name: "#{name}-#{pool_config.name}-vmss",
|
|
174
|
+
resource_group_name: tags[:ResourceGroupName] || "#{name}-rg",
|
|
175
|
+
location: tags[:Region] || 'eastus',
|
|
176
|
+
sku: vm_size,
|
|
177
|
+
instances: pool_config.effective_desired_size,
|
|
178
|
+
admin_username: 'nixos',
|
|
179
|
+
admin_ssh_key: {
|
|
180
|
+
username: 'nixos',
|
|
181
|
+
public_key: '${file("~/.ssh/id_ed25519.pub")}'
|
|
182
|
+
},
|
|
183
|
+
os_disk: {
|
|
184
|
+
caching: 'ReadWrite',
|
|
185
|
+
storage_account_type: 'Premium_LRS',
|
|
186
|
+
disk_size_gb: pool_config.disk_size_gb
|
|
187
|
+
},
|
|
188
|
+
source_image_id: tags[:ImageId],
|
|
189
|
+
custom_data: cloud_init,
|
|
190
|
+
network_interface: {
|
|
191
|
+
name: "#{name}-#{pool_config.name}-nic",
|
|
192
|
+
primary: true,
|
|
193
|
+
ip_configuration: {
|
|
194
|
+
name: 'internal',
|
|
195
|
+
subnet_id: tags[:SubnetId],
|
|
196
|
+
primary: true
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
identity: { type: 'SystemAssigned' },
|
|
200
|
+
tags: tags.merge(
|
|
201
|
+
NodePool: pool_config.name.to_s,
|
|
202
|
+
Role: 'worker'
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Autoscale setting
|
|
207
|
+
ctx.azurerm_monitor_autoscale_setting(
|
|
208
|
+
:"#{pool_name}_autoscale",
|
|
209
|
+
name: "#{name}-#{pool_config.name}-autoscale",
|
|
210
|
+
resource_group_name: tags[:ResourceGroupName] || "#{name}-rg",
|
|
211
|
+
location: tags[:Region] || 'eastus',
|
|
212
|
+
target_resource_id: vmss.id,
|
|
213
|
+
profile: {
|
|
214
|
+
name: 'default',
|
|
215
|
+
capacity: {
|
|
216
|
+
default: pool_config.effective_desired_size,
|
|
217
|
+
minimum: pool_config.min_size,
|
|
218
|
+
maximum: pool_config.max_size
|
|
219
|
+
},
|
|
220
|
+
rule: [{
|
|
221
|
+
metric_trigger: {
|
|
222
|
+
metric_name: 'Percentage CPU',
|
|
223
|
+
metric_resource_id: vmss.id,
|
|
224
|
+
operator: 'GreaterThan',
|
|
225
|
+
threshold: 70,
|
|
226
|
+
time_aggregation: 'Average',
|
|
227
|
+
time_grain: 'PT1M',
|
|
228
|
+
time_window: 'PT5M',
|
|
229
|
+
statistic: 'Average'
|
|
230
|
+
},
|
|
231
|
+
scale_action: {
|
|
232
|
+
direction: 'Increase',
|
|
233
|
+
type: 'ChangeCount',
|
|
234
|
+
value: 1,
|
|
235
|
+
cooldown: 'PT5M'
|
|
236
|
+
}
|
|
237
|
+
}]
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
vmss
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
private
|
|
245
|
+
|
|
246
|
+
def azure_nsg_rules(distribution)
|
|
247
|
+
priority = 100
|
|
248
|
+
base_firewall_ports(distribution).map do |_port_name, port_def|
|
|
249
|
+
rule = {
|
|
250
|
+
name: nsg_rule_name(port_def[:description]),
|
|
251
|
+
priority: priority,
|
|
252
|
+
direction: 'Inbound',
|
|
253
|
+
access: 'Allow',
|
|
254
|
+
protocol: port_def[:protocol] == :tcp ? 'Tcp' : 'Udp',
|
|
255
|
+
source_port_range: '*',
|
|
256
|
+
destination_port_range: port_def[:port].to_s,
|
|
257
|
+
source_address_prefix: port_def[:public] ? '*' : '10.0.0.0/8',
|
|
258
|
+
destination_address_prefix: '*'
|
|
259
|
+
}
|
|
260
|
+
priority += 10
|
|
261
|
+
rule
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Convert description to Azure NSG rule name (strip spaces/hyphens, capitalize each word)
|
|
266
|
+
# "controller-manager" => "ControllerManager", "scheduler" => "Scheduler"
|
|
267
|
+
# Single words like "SSH", "HTTPS", "Kubelet" pass through unchanged
|
|
268
|
+
def nsg_rule_name(description)
|
|
269
|
+
description.split(/[\s-]+/).map { |w| w.sub(/\A./, &:upcase) }.join
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2025 The Pangea Authors
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
module Pangea
|
|
18
|
+
module Kubernetes
|
|
19
|
+
module Backends
|
|
20
|
+
# Contract interface for Kubernetes backends. Each backend module
|
|
21
|
+
# must implement these class methods (via class << self):
|
|
22
|
+
#
|
|
23
|
+
# Identity methods:
|
|
24
|
+
# backend_name → Symbol (:aws, :gcp, :azure, :hcloud, etc.)
|
|
25
|
+
# managed_kubernetes? → true for EKS/GKE/AKS, false for NixOS/k3s
|
|
26
|
+
# required_gem → String gem name to require
|
|
27
|
+
# load_provider! → Require the provider gem (or raise LoadError)
|
|
28
|
+
#
|
|
29
|
+
# Infrastructure pipeline methods (all class-level):
|
|
30
|
+
# create_network(ctx, name, config, tags) → Pangea::Contracts::NetworkResult
|
|
31
|
+
# create_iam(ctx, name, config, tags) → Pangea::Contracts::IamResult
|
|
32
|
+
# create_cluster(ctx, name, config, result, tags) → control plane ref
|
|
33
|
+
# create_node_pool(ctx, name, cluster_ref, pool_config, tags) → ResourceReference
|
|
34
|
+
#
|
|
35
|
+
# Backends implement all pipeline methods in `class << self` so they
|
|
36
|
+
# are called as e.g. AwsNixos.create_network(ctx, ...).
|
|
37
|
+
module Base
|
|
38
|
+
def self.included(base)
|
|
39
|
+
base.extend(ClassMethods)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
module ClassMethods
|
|
43
|
+
def backend_name
|
|
44
|
+
raise NotImplementedError, "#{self} must implement .backend_name"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def managed_kubernetes?
|
|
48
|
+
raise NotImplementedError, "#{self} must implement .managed_kubernetes?"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def required_gem
|
|
52
|
+
raise NotImplementedError, "#{self} must implement .required_gem"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def load_provider!
|
|
56
|
+
require required_gem
|
|
57
|
+
rescue LoadError => e
|
|
58
|
+
raise LoadError,
|
|
59
|
+
"Backend #{backend_name} requires gem '#{required_gem}'. " \
|
|
60
|
+
"Add it to your Gemfile: gem '#{required_gem}'\n" \
|
|
61
|
+
"Original error: #{e.message}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Create networking resources (VPC, subnets, security groups, etc.).
|
|
65
|
+
# Must return a Pangea::Contracts::NetworkResult (or subclass).
|
|
66
|
+
#
|
|
67
|
+
# @param ctx [Object] Synthesizer context (provides resource methods)
|
|
68
|
+
# @param name [Symbol] Cluster name
|
|
69
|
+
# @param config [Types::ClusterConfig] Cluster configuration
|
|
70
|
+
# @param tags [Hash] Resource tags
|
|
71
|
+
# @return [Pangea::Contracts::NetworkResult]
|
|
72
|
+
def create_network(_ctx, _name, _config, _tags)
|
|
73
|
+
raise NotImplementedError, "#{self} must implement .create_network"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Create IAM resources (roles, policies, service accounts).
|
|
77
|
+
# Must return a Pangea::Contracts::IamResult (or subclass).
|
|
78
|
+
#
|
|
79
|
+
# @param ctx [Object] Synthesizer context
|
|
80
|
+
# @param name [Symbol] Cluster name
|
|
81
|
+
# @param config [Types::ClusterConfig] Cluster configuration
|
|
82
|
+
# @param tags [Hash] Resource tags
|
|
83
|
+
# @return [Pangea::Contracts::IamResult]
|
|
84
|
+
def create_iam(_ctx, _name, _config, _tags)
|
|
85
|
+
raise NotImplementedError, "#{self} must implement .create_iam"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Create the cluster control plane (EKS cluster, ASG+NLB, GKE cluster, etc.).
|
|
89
|
+
# Return type is backend-specific (ControlPlaneRef, resource ref, etc.).
|
|
90
|
+
#
|
|
91
|
+
# @param ctx [Object] Synthesizer context
|
|
92
|
+
# @param name [Symbol] Cluster name
|
|
93
|
+
# @param config [Types::ClusterConfig] Cluster configuration
|
|
94
|
+
# @param result [Pangea::Contracts::ArchitectureResult] Accumulated result with network/iam
|
|
95
|
+
# @param tags [Hash] Resource tags
|
|
96
|
+
# @return [Object] Control plane reference (wrapped in ClusterResult by Architecture)
|
|
97
|
+
def create_cluster(_ctx, _name, _config, _result, _tags)
|
|
98
|
+
raise NotImplementedError, "#{self} must implement .create_cluster"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Create a worker node pool for the cluster.
|
|
102
|
+
#
|
|
103
|
+
# @param ctx [Object] Synthesizer context
|
|
104
|
+
# @param name [Symbol] Cluster name
|
|
105
|
+
# @param cluster_ref [Object] Reference to the control plane
|
|
106
|
+
# @param pool_config [Types::NodePoolConfig] Node pool configuration
|
|
107
|
+
# @param tags [Hash] Resource tags
|
|
108
|
+
# @return [Object] Node pool resource reference
|
|
109
|
+
def create_node_pool(_ctx, _name, _cluster_ref, _pool_config, _tags)
|
|
110
|
+
raise NotImplementedError, "#{self} must implement .create_node_pool"
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|