pangea-core 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/.gitignore +6 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +78 -0
- data/LICENSE +201 -0
- data/Rakefile +8 -0
- data/flake.lock +835 -0
- data/flake.nix +60 -0
- data/gemset.nix +262 -0
- data/lib/pangea/resource_registry.rb +68 -0
- data/lib/pangea/resources/base.rb +81 -0
- data/lib/pangea/resources/base_attributes.rb +52 -0
- data/lib/pangea/resources/helpers.rb +54 -0
- data/lib/pangea/resources/network_helpers.rb +75 -0
- data/lib/pangea/resources/reference.rb +138 -0
- data/lib/pangea/resources/types/core.rb +84 -0
- data/lib/pangea/resources/types/index.rb +20 -0
- data/lib/pangea/resources/types.rb +17 -0
- data/lib/pangea/utilities/ip_discovery.rb +109 -0
- data/lib/pangea-core/version.rb +5 -0
- data/lib/pangea-core.rb +59 -0
- data/pangea-core.gemspec +33 -0
- metadata +164 -0
data/flake.nix
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
description = "Pangea Core — shared types and utilities for Pangea infrastructure DSL";
|
|
3
|
+
|
|
4
|
+
inputs = {
|
|
5
|
+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
6
|
+
ruby-nix.url = "github:inscapist/ruby-nix";
|
|
7
|
+
flake-utils.url = "github:numtide/flake-utils";
|
|
8
|
+
substrate = {
|
|
9
|
+
url = "github:pleme-io/substrate";
|
|
10
|
+
inputs.nixpkgs.follows = "nixpkgs";
|
|
11
|
+
};
|
|
12
|
+
forge = {
|
|
13
|
+
url = "github:pleme-io/forge";
|
|
14
|
+
inputs.nixpkgs.follows = "nixpkgs";
|
|
15
|
+
inputs.substrate.follows = "substrate";
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
outputs = {
|
|
20
|
+
self,
|
|
21
|
+
nixpkgs,
|
|
22
|
+
ruby-nix,
|
|
23
|
+
flake-utils,
|
|
24
|
+
substrate,
|
|
25
|
+
forge,
|
|
26
|
+
...
|
|
27
|
+
}:
|
|
28
|
+
flake-utils.lib.eachSystem ["x86_64-linux" "aarch64-linux" "aarch64-darwin"] (system: let
|
|
29
|
+
pkgs = import nixpkgs {
|
|
30
|
+
inherit system;
|
|
31
|
+
overlays = [ruby-nix.overlays.ruby];
|
|
32
|
+
};
|
|
33
|
+
rnix = ruby-nix.lib pkgs;
|
|
34
|
+
rnix-env = rnix {
|
|
35
|
+
name = "pangea-core";
|
|
36
|
+
gemset = ./gemset.nix;
|
|
37
|
+
};
|
|
38
|
+
env = rnix-env.env;
|
|
39
|
+
ruby = rnix-env.ruby;
|
|
40
|
+
|
|
41
|
+
rubyBuild = import "${substrate}/lib/ruby-build.nix" {
|
|
42
|
+
inherit pkgs;
|
|
43
|
+
forgeCmd = "${forge.packages.${system}.default}/bin/forge";
|
|
44
|
+
defaultGhcrToken = "";
|
|
45
|
+
};
|
|
46
|
+
in {
|
|
47
|
+
devShells.default = pkgs.mkShell {
|
|
48
|
+
buildInputs = [env ruby];
|
|
49
|
+
shellHook = ''
|
|
50
|
+
export RUBYLIB=$PWD/lib:$RUBYLIB
|
|
51
|
+
export DRY_TYPES_WARNINGS=false
|
|
52
|
+
'';
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
apps = rubyBuild.mkRubyGemApps {
|
|
56
|
+
srcDir = self;
|
|
57
|
+
name = "pangea-core";
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
}
|
data/gemset.nix
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
{
|
|
2
|
+
abstract-synthesizer = {
|
|
3
|
+
groups = ["default"];
|
|
4
|
+
platforms = [];
|
|
5
|
+
source = {
|
|
6
|
+
remotes = ["https://rubygems.org"];
|
|
7
|
+
sha256 = "1724yzbklcmiiahb7s3y1ir1i0n03b9c3arlib35g85d8hf0h75d";
|
|
8
|
+
type = "gem";
|
|
9
|
+
};
|
|
10
|
+
version = "0.0.15";
|
|
11
|
+
};
|
|
12
|
+
base64 = {
|
|
13
|
+
groups = ["default"];
|
|
14
|
+
platforms = [];
|
|
15
|
+
source = {
|
|
16
|
+
remotes = ["https://rubygems.org"];
|
|
17
|
+
sha256 = "0yx9yn47a8lkfcjmigk79fykxvr80r4m1i35q82sxzynpbm7lcr7";
|
|
18
|
+
type = "gem";
|
|
19
|
+
};
|
|
20
|
+
version = "0.3.0";
|
|
21
|
+
};
|
|
22
|
+
bigdecimal = {
|
|
23
|
+
groups = ["default"];
|
|
24
|
+
platforms = [];
|
|
25
|
+
source = {
|
|
26
|
+
remotes = ["https://rubygems.org"];
|
|
27
|
+
sha256 = "19y406nx17arzsbc515mjmr6k5p59afprspa1k423yd9cp8d61wb";
|
|
28
|
+
type = "gem";
|
|
29
|
+
};
|
|
30
|
+
version = "4.0.1";
|
|
31
|
+
};
|
|
32
|
+
concurrent-ruby = {
|
|
33
|
+
groups = ["default"];
|
|
34
|
+
platforms = [];
|
|
35
|
+
source = {
|
|
36
|
+
remotes = ["https://rubygems.org"];
|
|
37
|
+
sha256 = "1aymcakhzl83k77g2f2krz07bg1cbafbcd2ghvwr4lky3rz86mkb";
|
|
38
|
+
type = "gem";
|
|
39
|
+
};
|
|
40
|
+
version = "1.3.6";
|
|
41
|
+
};
|
|
42
|
+
diff-lcs = {
|
|
43
|
+
groups = ["default" "development"];
|
|
44
|
+
platforms = [];
|
|
45
|
+
source = {
|
|
46
|
+
remotes = ["https://rubygems.org"];
|
|
47
|
+
sha256 = "0qlrj2qyysc9avzlr4zs1py3x684hqm61n4czrsk1pyllz5x5q4s";
|
|
48
|
+
type = "gem";
|
|
49
|
+
};
|
|
50
|
+
version = "1.6.2";
|
|
51
|
+
};
|
|
52
|
+
docile = {
|
|
53
|
+
groups = ["default" "development"];
|
|
54
|
+
platforms = [];
|
|
55
|
+
source = {
|
|
56
|
+
remotes = ["https://rubygems.org"];
|
|
57
|
+
sha256 = "07pj4z3h8wk4fgdn6s62vw1lwvhj0ac0x10vfbdkr9xzk7krn5cn";
|
|
58
|
+
type = "gem";
|
|
59
|
+
};
|
|
60
|
+
version = "1.4.1";
|
|
61
|
+
};
|
|
62
|
+
dry-core = {
|
|
63
|
+
dependencies = ["concurrent-ruby" "logger" "zeitwerk"];
|
|
64
|
+
groups = ["default"];
|
|
65
|
+
platforms = [];
|
|
66
|
+
source = {
|
|
67
|
+
remotes = ["https://rubygems.org"];
|
|
68
|
+
sha256 = "18cn9s2p7cbgacy0z41h3sf9jvl75vjfmvj774apyffzi3dagi8c";
|
|
69
|
+
type = "gem";
|
|
70
|
+
};
|
|
71
|
+
version = "1.2.0";
|
|
72
|
+
};
|
|
73
|
+
dry-inflector = {
|
|
74
|
+
groups = ["default"];
|
|
75
|
+
platforms = [];
|
|
76
|
+
source = {
|
|
77
|
+
remotes = ["https://rubygems.org"];
|
|
78
|
+
sha256 = "1k1dd35sqqqg2abd2g2w78m94pa3mcwvmrsjbkr3hxpn0jxw5c3z";
|
|
79
|
+
type = "gem";
|
|
80
|
+
};
|
|
81
|
+
version = "1.3.1";
|
|
82
|
+
};
|
|
83
|
+
dry-logic = {
|
|
84
|
+
dependencies = ["bigdecimal" "concurrent-ruby" "dry-core" "zeitwerk"];
|
|
85
|
+
groups = ["default"];
|
|
86
|
+
platforms = [];
|
|
87
|
+
source = {
|
|
88
|
+
remotes = ["https://rubygems.org"];
|
|
89
|
+
sha256 = "18nf8mbnhgvkw34drj7nmvpx2afmyl2nyzncn3wl3z4h1yyfsvys";
|
|
90
|
+
type = "gem";
|
|
91
|
+
};
|
|
92
|
+
version = "1.6.0";
|
|
93
|
+
};
|
|
94
|
+
dry-struct = {
|
|
95
|
+
dependencies = ["dry-core" "dry-types" "ice_nine" "zeitwerk"];
|
|
96
|
+
groups = ["default"];
|
|
97
|
+
platforms = [];
|
|
98
|
+
source = {
|
|
99
|
+
remotes = ["https://rubygems.org"];
|
|
100
|
+
sha256 = "0ri9iqxknxvvhpbshf6jn7bq581k8l67iv23mii69yr4k5aqphvl";
|
|
101
|
+
type = "gem";
|
|
102
|
+
};
|
|
103
|
+
version = "1.8.0";
|
|
104
|
+
};
|
|
105
|
+
dry-types = {
|
|
106
|
+
dependencies = ["bigdecimal" "concurrent-ruby" "dry-core" "dry-inflector" "dry-logic" "zeitwerk"];
|
|
107
|
+
groups = ["default"];
|
|
108
|
+
platforms = [];
|
|
109
|
+
source = {
|
|
110
|
+
remotes = ["https://rubygems.org"];
|
|
111
|
+
sha256 = "0y7icwaa26ycikz6h97gwd1hji3r280n4yr2kmn5sfgqp76yxsxs";
|
|
112
|
+
type = "gem";
|
|
113
|
+
};
|
|
114
|
+
version = "1.9.1";
|
|
115
|
+
};
|
|
116
|
+
ice_nine = {
|
|
117
|
+
groups = ["default"];
|
|
118
|
+
platforms = [];
|
|
119
|
+
source = {
|
|
120
|
+
remotes = ["https://rubygems.org"];
|
|
121
|
+
sha256 = "1nv35qg1rps9fsis28hz2cq2fx1i96795f91q4nmkm934xynll2x";
|
|
122
|
+
type = "gem";
|
|
123
|
+
};
|
|
124
|
+
version = "0.11.2";
|
|
125
|
+
};
|
|
126
|
+
logger = {
|
|
127
|
+
groups = ["default"];
|
|
128
|
+
platforms = [];
|
|
129
|
+
source = {
|
|
130
|
+
remotes = ["https://rubygems.org"];
|
|
131
|
+
sha256 = "00q2zznygpbls8asz5knjvvj2brr3ghmqxgr83xnrdj4rk3xwvhr";
|
|
132
|
+
type = "gem";
|
|
133
|
+
};
|
|
134
|
+
version = "1.7.0";
|
|
135
|
+
};
|
|
136
|
+
pangea-core = {
|
|
137
|
+
dependencies = ["base64" "dry-struct" "dry-types" "terraform-synthesizer"];
|
|
138
|
+
groups = ["default"];
|
|
139
|
+
platforms = [];
|
|
140
|
+
source = {
|
|
141
|
+
path = ./.;
|
|
142
|
+
type = "path";
|
|
143
|
+
};
|
|
144
|
+
version = "0.1.0";
|
|
145
|
+
};
|
|
146
|
+
rake = {
|
|
147
|
+
groups = ["development"];
|
|
148
|
+
platforms = [];
|
|
149
|
+
source = {
|
|
150
|
+
remotes = ["https://rubygems.org"];
|
|
151
|
+
sha256 = "175iisqb211n0qbfyqd8jz2g01q6xj038zjf4q0nm8k6kz88k7lc";
|
|
152
|
+
type = "gem";
|
|
153
|
+
};
|
|
154
|
+
version = "13.3.1";
|
|
155
|
+
};
|
|
156
|
+
rspec = {
|
|
157
|
+
dependencies = ["rspec-core" "rspec-expectations" "rspec-mocks"];
|
|
158
|
+
groups = ["development"];
|
|
159
|
+
platforms = [];
|
|
160
|
+
source = {
|
|
161
|
+
remotes = ["https://rubygems.org"];
|
|
162
|
+
sha256 = "11q5hagj6vr694innqj4r45jrm8qcwvkxjnphqgyd66piah88qi0";
|
|
163
|
+
type = "gem";
|
|
164
|
+
};
|
|
165
|
+
version = "3.13.2";
|
|
166
|
+
};
|
|
167
|
+
rspec-core = {
|
|
168
|
+
dependencies = ["rspec-support"];
|
|
169
|
+
groups = ["default" "development"];
|
|
170
|
+
platforms = [];
|
|
171
|
+
source = {
|
|
172
|
+
remotes = ["https://rubygems.org"];
|
|
173
|
+
sha256 = "0bcbh9yv6cs6pv299zs4bvalr8yxa51kcdd1pjl60yv625j3r0m8";
|
|
174
|
+
type = "gem";
|
|
175
|
+
};
|
|
176
|
+
version = "3.13.6";
|
|
177
|
+
};
|
|
178
|
+
rspec-expectations = {
|
|
179
|
+
dependencies = ["diff-lcs" "rspec-support"];
|
|
180
|
+
groups = ["default" "development"];
|
|
181
|
+
platforms = [];
|
|
182
|
+
source = {
|
|
183
|
+
remotes = ["https://rubygems.org"];
|
|
184
|
+
sha256 = "0dl8npj0jfpy31bxi6syc7jymyd861q277sfr6jawq2hv6hx791k";
|
|
185
|
+
type = "gem";
|
|
186
|
+
};
|
|
187
|
+
version = "3.13.5";
|
|
188
|
+
};
|
|
189
|
+
rspec-mocks = {
|
|
190
|
+
dependencies = ["diff-lcs" "rspec-support"];
|
|
191
|
+
groups = ["default" "development"];
|
|
192
|
+
platforms = [];
|
|
193
|
+
source = {
|
|
194
|
+
remotes = ["https://rubygems.org"];
|
|
195
|
+
sha256 = "071bqrk2rblk3zq3jk1xxx0dr92y0szi5pxdm8waimxici706y89";
|
|
196
|
+
type = "gem";
|
|
197
|
+
};
|
|
198
|
+
version = "3.13.7";
|
|
199
|
+
};
|
|
200
|
+
rspec-support = {
|
|
201
|
+
groups = ["default" "development"];
|
|
202
|
+
platforms = [];
|
|
203
|
+
source = {
|
|
204
|
+
remotes = ["https://rubygems.org"];
|
|
205
|
+
sha256 = "0z64h5rznm2zv21vjdjshz4v0h7bxvg02yc6g7yzxakj11byah06";
|
|
206
|
+
type = "gem";
|
|
207
|
+
};
|
|
208
|
+
version = "3.13.7";
|
|
209
|
+
};
|
|
210
|
+
simplecov = {
|
|
211
|
+
dependencies = ["docile" "simplecov-html" "simplecov_json_formatter"];
|
|
212
|
+
groups = ["development"];
|
|
213
|
+
platforms = [];
|
|
214
|
+
source = {
|
|
215
|
+
remotes = ["https://rubygems.org"];
|
|
216
|
+
sha256 = "198kcbrjxhhzca19yrdcd6jjj9sb51aaic3b0sc3pwjghg3j49py";
|
|
217
|
+
type = "gem";
|
|
218
|
+
};
|
|
219
|
+
version = "0.22.0";
|
|
220
|
+
};
|
|
221
|
+
simplecov-html = {
|
|
222
|
+
groups = ["default" "development"];
|
|
223
|
+
platforms = [];
|
|
224
|
+
source = {
|
|
225
|
+
remotes = ["https://rubygems.org"];
|
|
226
|
+
sha256 = "0ikjfwydgs08nm3xzc4cn4b6z6rmcrj2imp84xcnimy2wxa8w2xx";
|
|
227
|
+
type = "gem";
|
|
228
|
+
};
|
|
229
|
+
version = "0.13.2";
|
|
230
|
+
};
|
|
231
|
+
simplecov_json_formatter = {
|
|
232
|
+
groups = ["default" "development"];
|
|
233
|
+
platforms = [];
|
|
234
|
+
source = {
|
|
235
|
+
remotes = ["https://rubygems.org"];
|
|
236
|
+
sha256 = "0a5l0733hj7sk51j81ykfmlk2vd5vaijlq9d5fn165yyx3xii52j";
|
|
237
|
+
type = "gem";
|
|
238
|
+
};
|
|
239
|
+
version = "0.1.4";
|
|
240
|
+
};
|
|
241
|
+
terraform-synthesizer = {
|
|
242
|
+
dependencies = ["abstract-synthesizer"];
|
|
243
|
+
groups = ["default"];
|
|
244
|
+
platforms = [];
|
|
245
|
+
source = {
|
|
246
|
+
remotes = ["https://rubygems.org"];
|
|
247
|
+
sha256 = "01yl1s6xnxn3qh42ybqanxdgcfpppg2cvjk8pka7xcf5hxz9qxda";
|
|
248
|
+
type = "gem";
|
|
249
|
+
};
|
|
250
|
+
version = "0.0.28";
|
|
251
|
+
};
|
|
252
|
+
zeitwerk = {
|
|
253
|
+
groups = ["default"];
|
|
254
|
+
platforms = [];
|
|
255
|
+
source = {
|
|
256
|
+
remotes = ["https://rubygems.org"];
|
|
257
|
+
sha256 = "1pbkiwwla5gldgb3saamn91058nl1sq1344l5k36xsh9ih995nnq";
|
|
258
|
+
type = "gem";
|
|
259
|
+
};
|
|
260
|
+
version = "2.7.5";
|
|
261
|
+
};
|
|
262
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Copyright 2025 The Pangea Authors
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
require 'set'
|
|
18
|
+
|
|
19
|
+
module Pangea
|
|
20
|
+
# Global registry for resource modules that auto-register when loaded
|
|
21
|
+
module ResourceRegistry
|
|
22
|
+
@registered_modules = Set.new
|
|
23
|
+
@provider_modules = Hash.new { |h, k| h[k] = Set.new }
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
# Register a module to be available in template contexts
|
|
27
|
+
def register_module(mod)
|
|
28
|
+
@registered_modules.add(mod)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Get all registered modules
|
|
32
|
+
def registered_modules
|
|
33
|
+
@registered_modules.to_a
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Clear registry (useful for testing)
|
|
37
|
+
def clear!
|
|
38
|
+
@registered_modules.clear
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Check if a module is registered
|
|
42
|
+
def registered?(mod)
|
|
43
|
+
@registered_modules.include?(mod)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Support provider-based registration used by individual resources
|
|
47
|
+
def register(provider, mod)
|
|
48
|
+
@provider_modules[provider].add(mod)
|
|
49
|
+
# Also add to global registry for backward compatibility
|
|
50
|
+
@registered_modules.add(mod)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Get modules for a specific provider
|
|
54
|
+
def modules_for(provider)
|
|
55
|
+
@provider_modules[provider].to_a
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Get registry statistics
|
|
59
|
+
def stats
|
|
60
|
+
{
|
|
61
|
+
total_modules: @registered_modules.size,
|
|
62
|
+
modules: @registered_modules.map(&:name),
|
|
63
|
+
by_provider: @provider_modules.transform_values(&:size)
|
|
64
|
+
}
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Copyright 2025 The Pangea Authors
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
require 'pangea/resources/types'
|
|
18
|
+
|
|
19
|
+
module Pangea
|
|
20
|
+
module Resources
|
|
21
|
+
# Base functionality for all resource abstractions
|
|
22
|
+
module Base
|
|
23
|
+
# Resource definition that gets passed to terraform-synthesizer
|
|
24
|
+
class ResourceDefinition
|
|
25
|
+
attr_reader :type, :name, :attributes
|
|
26
|
+
|
|
27
|
+
def initialize(type, name, attributes)
|
|
28
|
+
@type = type
|
|
29
|
+
@name = name
|
|
30
|
+
@attributes = attributes
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Convert to terraform-synthesizer resource block
|
|
34
|
+
def to_terraform_resource(&block)
|
|
35
|
+
resource(type, name, &block)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
protected
|
|
40
|
+
|
|
41
|
+
# Helper method to create resource definitions
|
|
42
|
+
def create_resource(type, name, attributes_class, attributes = {})
|
|
43
|
+
# Validate attributes with dry-struct
|
|
44
|
+
validated_attrs = attributes_class.new(attributes)
|
|
45
|
+
|
|
46
|
+
# Create resource definition
|
|
47
|
+
ResourceDefinition.new(type, name, validated_attrs)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Helper to convert hash keys to terraform-synthesizer method calls
|
|
51
|
+
def apply_attributes_to_resource(resource_block, attributes)
|
|
52
|
+
attributes.each do |key, value|
|
|
53
|
+
case value
|
|
54
|
+
when Hash
|
|
55
|
+
resource_block.public_send(key) do
|
|
56
|
+
apply_attributes_to_resource(self, value)
|
|
57
|
+
end
|
|
58
|
+
when Array
|
|
59
|
+
value.each do |item|
|
|
60
|
+
if item.is_a?(Hash)
|
|
61
|
+
resource_block.public_send(key) do
|
|
62
|
+
apply_attributes_to_resource(self, item)
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
resource_block.public_send(key, item)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
else
|
|
69
|
+
resource_block.public_send(key, value)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Helper for reference generation
|
|
75
|
+
def resource_ref(type, name, attribute)
|
|
76
|
+
# This would integrate with terraform-synthesizer's ref functionality
|
|
77
|
+
"${#{type}.#{name}.#{attribute}}"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'dry-struct'
|
|
4
|
+
|
|
5
|
+
module Pangea
|
|
6
|
+
module Resources
|
|
7
|
+
# Base class for all provider resource attribute structs.
|
|
8
|
+
#
|
|
9
|
+
# Provides:
|
|
10
|
+
# - transform_keys(&:to_sym) so hash keys are normalized
|
|
11
|
+
# - T constant aliasing Resources::Types for short, unambiguous type references
|
|
12
|
+
#
|
|
13
|
+
# All provider attribute classes should inherit from this:
|
|
14
|
+
# class VpcAttributes < Pangea::Resources::BaseAttributes
|
|
15
|
+
# attribute :cidr_block, T::CidrBlock
|
|
16
|
+
# attribute :tags, T::AwsTags.default({}.freeze)
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
class BaseAttributes < Dry::Struct
|
|
20
|
+
# Short alias for Pangea::Resources::Types — works inside class bodies
|
|
21
|
+
# because it's a real constant (not const_missing-based)
|
|
22
|
+
T = Pangea::Resources::Types
|
|
23
|
+
|
|
24
|
+
transform_keys(&:to_sym)
|
|
25
|
+
|
|
26
|
+
# Terraform reference pattern — matches ${...} interpolation syntax.
|
|
27
|
+
# Use in self.new validators to skip format checks on values that are
|
|
28
|
+
# terraform references (they will be resolved at plan/apply time).
|
|
29
|
+
TERRAFORM_REF_PATTERN = /\$\{.*\}/.freeze
|
|
30
|
+
|
|
31
|
+
# Returns true if the value contains a terraform/HCL interpolation reference.
|
|
32
|
+
# Works for both class-level (self.terraform_reference?) and instance-level usage.
|
|
33
|
+
def self.terraform_reference?(value)
|
|
34
|
+
return false unless value.is_a?(String)
|
|
35
|
+
|
|
36
|
+
value.match?(TERRAFORM_REF_PATTERN)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def terraform_reference?(value)
|
|
40
|
+
self.class.terraform_reference?(value)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Create a copy with merged attributes.
|
|
44
|
+
# Uses Dry::Struct's load method to bypass custom self.new overrides,
|
|
45
|
+
# preventing infinite recursion when copy_with is called inside validators.
|
|
46
|
+
def copy_with(changes = {})
|
|
47
|
+
merged = to_h.merge(changes.transform_keys(&:to_sym))
|
|
48
|
+
self.class.load(merged)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Copyright 2025 The Pangea Authors
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
module Pangea
|
|
18
|
+
module Resources
|
|
19
|
+
# Helper functions available in template context
|
|
20
|
+
module Helpers
|
|
21
|
+
# Create a terraform reference to another resource
|
|
22
|
+
# @param resource_type [Symbol] The resource type (e.g., :aws_vpc)
|
|
23
|
+
# @param resource_name [Symbol] The resource name
|
|
24
|
+
# @param attribute [Symbol] The attribute to reference (e.g., :id)
|
|
25
|
+
# @return [String] Terraform reference string
|
|
26
|
+
def ref(resource_type, resource_name, attribute)
|
|
27
|
+
"${#{resource_type}.#{resource_name}.#{attribute}}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Create a data source reference
|
|
31
|
+
# @param data_type [Symbol] The data source type
|
|
32
|
+
# @param data_name [Symbol] The data source name
|
|
33
|
+
# @param attribute [Symbol] The attribute to reference
|
|
34
|
+
# @return [String] Terraform data reference string
|
|
35
|
+
def data_ref(data_type, data_name, attribute)
|
|
36
|
+
"${data.#{data_type}.#{data_name}.#{attribute}}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Create a variable reference
|
|
40
|
+
# @param var_name [Symbol] The variable name
|
|
41
|
+
# @return [String] Terraform variable reference string
|
|
42
|
+
def var(var_name)
|
|
43
|
+
"${var.#{var_name}}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Create a local value reference
|
|
47
|
+
# @param local_name [Symbol] The local value name
|
|
48
|
+
# @return [String] Terraform local reference string
|
|
49
|
+
def local(local_name)
|
|
50
|
+
"${local.#{local_name}}"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Copyright 2025 The Pangea Authors
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
require 'pangea/utilities/ip_discovery'
|
|
17
|
+
require 'pangea/resource_registry'
|
|
18
|
+
|
|
19
|
+
module Pangea
|
|
20
|
+
module Resources
|
|
21
|
+
# Network helpers for templates
|
|
22
|
+
module NetworkHelpers
|
|
23
|
+
# Discover public IP address - available in template context
|
|
24
|
+
def discover_public_ip(timeout: 5)
|
|
25
|
+
# Cache the IP discovery result to avoid multiple calls
|
|
26
|
+
@_discovered_ip ||= begin
|
|
27
|
+
discovery = Utilities::IpDiscovery.new(timeout: timeout)
|
|
28
|
+
ip = discovery.discover
|
|
29
|
+
puts "[Pangea] Discovered public IP: #{ip}"
|
|
30
|
+
ip
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Create CIDR block from IP and mask
|
|
35
|
+
def cidr_block(ip, mask)
|
|
36
|
+
"#{ip}/#{mask}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Calculate subnet CIDR from base and offset
|
|
40
|
+
def subnet_cidr(base_cidr, subnet_bits, index)
|
|
41
|
+
base_ip, base_mask = base_cidr.split('/')
|
|
42
|
+
octets = base_ip.split('.').map(&:to_i)
|
|
43
|
+
|
|
44
|
+
# Calculate new IP based on subnet bits and index
|
|
45
|
+
subnet_size = 2 ** subnet_bits
|
|
46
|
+
offset = index * subnet_size
|
|
47
|
+
|
|
48
|
+
# Apply offset to appropriate octet
|
|
49
|
+
octet_index = (32 - base_mask.to_i - subnet_bits) / 8
|
|
50
|
+
octets[octet_index] += offset
|
|
51
|
+
|
|
52
|
+
new_ip = octets.join('.')
|
|
53
|
+
new_mask = base_mask.to_i + subnet_bits
|
|
54
|
+
|
|
55
|
+
"#{new_ip}/#{new_mask}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Generate availability zones for a region
|
|
59
|
+
def availability_zones(region, count = 3)
|
|
60
|
+
zones = ('a'..'f').to_a
|
|
61
|
+
zones.take(count).map { |zone| "#{region}#{zone}" }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Validate IP address format
|
|
65
|
+
def valid_ip?(ip)
|
|
66
|
+
return false unless ip =~ /\A(?:\d{1,3}\.){3}\d{1,3}\z/
|
|
67
|
+
|
|
68
|
+
ip.split('.').all? { |octet| octet.to_i <= 255 }
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Auto-register the module when loaded
|
|
75
|
+
Pangea::ResourceRegistry.register_module(Pangea::Resources::NetworkHelpers)
|