pangea-orchestrator 0.0.1 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +1 -1
- data/gemset.nix +1 -1
- data/lib/pangea-orchestrator/resources/eks.rb +10 -2
- data/lib/pangea-orchestrator/version.rb +1 -1
- metadata +1 -24
- data/lib/pangea-orchestrator/cli.rb +0 -73
- data/lib/pangea-orchestrator/errors/incorrect_subcommand_error.rb +0 -2
- data/lib/pangea-orchestrator/errors/namespace_not_found_error.rb +0 -2
- data/lib/pangea-orchestrator/errors/no_infra_target_error.rb +0 -2
- data/lib/pangea-orchestrator/errors/project_not_found_error.rb +0 -2
- data/lib/pangea-orchestrator/errors/site_not_found_error.rb +0 -2
- data/lib/pangea-orchestrator/executor.rb +0 -10
- data/lib/pangea-orchestrator/modules.rb +0 -134
- data/lib/pangea-orchestrator/renderer.rb +0 -241
- data/lib/pangea-orchestrator/sandbox.rb +0 -90
- data/lib/pangea-orchestrator/say/init.rb +0 -27
- data/lib/pangea-orchestrator/shell/README.md +0 -1
- data/lib/pangea-orchestrator/shell/terraform.rb +0 -21
- data/lib/pangea-orchestrator/shell.rb +0 -27
- data/lib/pangea-orchestrator/stack.rb +0 -11
- data/lib/pangea-orchestrator/state.rb +0 -96
- data/lib/pangea-orchestrator/structures/README.md +0 -3
- data/lib/pangea-orchestrator/structures/abstract.rb +0 -2
- data/lib/pangea-orchestrator/structures/namespace.rb +0 -4
- data/lib/pangea-orchestrator/structures/project.rb +0 -4
- data/lib/pangea-orchestrator/structures/site.rb +0 -4
- data/lib/pangea-orchestrator/synthesizer/config.rb +0 -38
- data/lib/pangea-orchestrator/utils.rb +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eea60208eafbfc9698d5d86189f203af5dfee5d9578f81d3635b6c0bb7a56801
|
4
|
+
data.tar.gz: da7d8aea4b030a38f3109ea794a913f64871a6d1b5a74db60d5e531422ced75c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65cd3ca6770923599209f26ab85fef33c9409491e11b01c212e530aeea9bbf14024a417aae9321c0c79483d71e82e9ff0835c2aa62fba69a081608a69f9bb5ec
|
7
|
+
data.tar.gz: 39ec6bcf0b1e05eb87b4b25ca35551497bbb0eb3cce44b039d27fc1a1c2f79fbde9161431e0fb380b2a8775ca09f31e7e8c2f3a640683624e8b7b25026fb34f7
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/gemset.nix
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
# lib/pangea-orchestrator/resources/eks.rb
|
2
2
|
require 'json'
|
3
|
+
require 'terraform-synthesizer'
|
4
|
+
|
3
5
|
module PangeaOrchestrator
|
4
6
|
module Resources
|
5
7
|
class EKS
|
6
8
|
class << self
|
9
|
+
def synthesizer
|
10
|
+
@synthesizer ||= Terraform::Synthesizer.new
|
11
|
+
end
|
12
|
+
|
7
13
|
def symbolize(hash)
|
8
14
|
JSON[JSON[hash, symbolic_names: true]]
|
9
15
|
end
|
@@ -12,8 +18,10 @@ module PangeaOrchestrator
|
|
12
18
|
kwargs = symbolize(kwargs)
|
13
19
|
resource_name = kwargs[:resource_name]
|
14
20
|
name = kwargs[:name]
|
15
|
-
|
16
|
-
|
21
|
+
synthesizer.synthesize do
|
22
|
+
resource :aws_eks_cluster, resource_name do
|
23
|
+
name name
|
24
|
+
end
|
17
25
|
end
|
18
26
|
end
|
19
27
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pangea-orchestrator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- drzthslnt@gmail.com
|
@@ -168,31 +168,8 @@ files:
|
|
168
168
|
- flake.nix
|
169
169
|
- gemset.nix
|
170
170
|
- lib/pangea-orchestrator.rb
|
171
|
-
- lib/pangea-orchestrator/cli.rb
|
172
|
-
- lib/pangea-orchestrator/errors/incorrect_subcommand_error.rb
|
173
|
-
- lib/pangea-orchestrator/errors/namespace_not_found_error.rb
|
174
|
-
- lib/pangea-orchestrator/errors/no_infra_target_error.rb
|
175
|
-
- lib/pangea-orchestrator/errors/project_not_found_error.rb
|
176
|
-
- lib/pangea-orchestrator/errors/site_not_found_error.rb
|
177
|
-
- lib/pangea-orchestrator/executor.rb
|
178
|
-
- lib/pangea-orchestrator/modules.rb
|
179
|
-
- lib/pangea-orchestrator/renderer.rb
|
180
171
|
- lib/pangea-orchestrator/resources.rb
|
181
172
|
- lib/pangea-orchestrator/resources/eks.rb
|
182
|
-
- lib/pangea-orchestrator/sandbox.rb
|
183
|
-
- lib/pangea-orchestrator/say/init.rb
|
184
|
-
- lib/pangea-orchestrator/shell.rb
|
185
|
-
- lib/pangea-orchestrator/shell/README.md
|
186
|
-
- lib/pangea-orchestrator/shell/terraform.rb
|
187
|
-
- lib/pangea-orchestrator/stack.rb
|
188
|
-
- lib/pangea-orchestrator/state.rb
|
189
|
-
- lib/pangea-orchestrator/structures/README.md
|
190
|
-
- lib/pangea-orchestrator/structures/abstract.rb
|
191
|
-
- lib/pangea-orchestrator/structures/namespace.rb
|
192
|
-
- lib/pangea-orchestrator/structures/project.rb
|
193
|
-
- lib/pangea-orchestrator/structures/site.rb
|
194
|
-
- lib/pangea-orchestrator/synthesizer/config.rb
|
195
|
-
- lib/pangea-orchestrator/utils.rb
|
196
173
|
- lib/pangea-orchestrator/version.rb
|
197
174
|
- pangea-orchestrator.gemspec
|
198
175
|
homepage: https://github.com/drzln/pangea-orchestrator
|
@@ -1,73 +0,0 @@
|
|
1
|
-
require 'terraform-synthesizer'
|
2
|
-
require 'pangea/processor'
|
3
|
-
require 'pangea/renderer'
|
4
|
-
require 'pangea/config'
|
5
|
-
require 'pangea/state'
|
6
|
-
require 'pangea/utils'
|
7
|
-
require 'thor'
|
8
|
-
|
9
|
-
module TheseUtils
|
10
|
-
class << self
|
11
|
-
def cfg
|
12
|
-
@cfg ||= Pangea::Utils.symbolize(
|
13
|
-
Pangea::Config.config
|
14
|
-
)
|
15
|
-
end
|
16
|
-
|
17
|
-
def state_init
|
18
|
-
state = Pangea::S3State.new
|
19
|
-
cfg[:namespaces].each_key do |nk|
|
20
|
-
ns_config = cfg[:namespaces][nk]
|
21
|
-
case ns_config[:state][:type].to_sym
|
22
|
-
when :s3
|
23
|
-
bucket_name = ns_config[:state][:config][:bucket]
|
24
|
-
region = ns_config[:state][:config][:region]
|
25
|
-
lock_table_name = ns_config[:state][:config][:lock]
|
26
|
-
|
27
|
-
state.create_bucket(
|
28
|
-
name: bucket_name,
|
29
|
-
region: region
|
30
|
-
)
|
31
|
-
|
32
|
-
state.create_dynamodb_table_for_lock(
|
33
|
-
name: lock_table_name,
|
34
|
-
region: region
|
35
|
-
)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
module Pangea
|
43
|
-
class Cli < Thor
|
44
|
-
desc 'apply FILE', 'apply a FILE of pangea code'
|
45
|
-
def apply(file)
|
46
|
-
Pangea::Processor.register_action('apply')
|
47
|
-
Pangea::Processor.process(File.read(file))
|
48
|
-
end
|
49
|
-
|
50
|
-
desc 'show FILE', 'transpile a FILE of pangea code to json'
|
51
|
-
def show(file)
|
52
|
-
Pangea::Processor.register_action('show')
|
53
|
-
Pangea::Processor.process(File.read(file))
|
54
|
-
end
|
55
|
-
|
56
|
-
desc 'plan FILE', 'plan a FILE of pangea code'
|
57
|
-
def plan(file)
|
58
|
-
Pangea::Processor.register_action('plan')
|
59
|
-
Pangea::Processor.process(File.read(file))
|
60
|
-
end
|
61
|
-
|
62
|
-
desc 'destroy FILE', 'destroy a FILE of pangea code'
|
63
|
-
def destroy(file)
|
64
|
-
Pangea::Processor.register_action('destroy')
|
65
|
-
Pangea::Processor.process(File.read(file))
|
66
|
-
end
|
67
|
-
|
68
|
-
desc 'init', 'initialize an s3 state configuation according to pangea.yml'
|
69
|
-
def init
|
70
|
-
TheseUtils.state_init
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
@@ -1,134 +0,0 @@
|
|
1
|
-
###############################################################################
|
2
|
-
# modules
|
3
|
-
# module for handling pangea modules
|
4
|
-
# pangea modules are execution units
|
5
|
-
# for terraform code.
|
6
|
-
###############################################################################
|
7
|
-
|
8
|
-
require %(terraform-synthesizer)
|
9
|
-
require %(bundler)
|
10
|
-
|
11
|
-
module PangeaBase
|
12
|
-
BASE_DIR = File.join(
|
13
|
-
Dir.home,
|
14
|
-
%(.pangea)
|
15
|
-
)
|
16
|
-
end
|
17
|
-
|
18
|
-
module PangeaRbenv
|
19
|
-
include PangeaBase
|
20
|
-
|
21
|
-
BIN = %(rbenv).freeze
|
22
|
-
|
23
|
-
RBENV_DIR = File.join(
|
24
|
-
BASE_DIR,
|
25
|
-
%(rbenv)
|
26
|
-
)
|
27
|
-
|
28
|
-
VERSIONS_DIR = File.join(
|
29
|
-
RBENV_DIR,
|
30
|
-
%(versions)
|
31
|
-
)
|
32
|
-
|
33
|
-
class << self
|
34
|
-
def versions_dir
|
35
|
-
VERSIONS_DIR
|
36
|
-
end
|
37
|
-
|
38
|
-
def rbenv_installed?
|
39
|
-
`which rbenv`.strip != ''
|
40
|
-
end
|
41
|
-
|
42
|
-
def rbenv_install(version, path)
|
43
|
-
system %(mkdir -p #{VERSIONS_DIR}) unless Dir.exist?(VERSIONS_DIR)
|
44
|
-
if rbenv_installed? && !Dir.exist?(File.join(path.to_s))
|
45
|
-
system [BIN, %(install), version.to_s, path.to_s].join(%( ))
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
module PangeaRubyBuild
|
52
|
-
include PangeaBase
|
53
|
-
BIN = %(ruby-build).freeze
|
54
|
-
RUBY_BUILD_DIR = File.join(
|
55
|
-
BASE_DIR,
|
56
|
-
%(rbenv)
|
57
|
-
)
|
58
|
-
class << self
|
59
|
-
def ruby_build_installed?
|
60
|
-
`which rbenv`.strip != ''
|
61
|
-
end
|
62
|
-
|
63
|
-
def ruby_build(version, path)
|
64
|
-
system %(mkdir -p #{PangeaRbenv.versions_dir}) unless Dir.exist?(PangeaRbenv.versions_dir)
|
65
|
-
system [BIN, version.to_s, path.to_s].join(%( )) if ruby_build_installed? && !Dir.exist?(File.join(path.to_s))
|
66
|
-
end
|
67
|
-
|
68
|
-
def gem_install(gem, ruby_version, gem_version, gemset_path)
|
69
|
-
gem_path = File.join(gemset_path, %(lib), %(ruby), %(gems), ruby_version.to_s, %(gems), %(#{gem}-#{gem_version}))
|
70
|
-
unless Dir.exist?(gem_path)
|
71
|
-
gembin = File.join(gemset_path, %(bin), %(gem))
|
72
|
-
system [gembin, %(install), gem.to_s.strip, %(-v), gem_version.to_s].join(%( ))
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def bundle_install(mpath, gemset_path)
|
77
|
-
@bundlebin = File.join(gemset_path, %(bin), %(bundle))
|
78
|
-
bundlehint = File.join(gemset_path, %(bundle_hint))
|
79
|
-
unless File.exist?(bundlehint)
|
80
|
-
cmd = [
|
81
|
-
# %(cd #{mpath} &&),
|
82
|
-
%(BUNDLE_GEMFILE=#{File.join(mpath, %(Gemfile))}), @bundlebin,
|
83
|
-
%(install)
|
84
|
-
].join(%( ))
|
85
|
-
system cmd
|
86
|
-
system [%(touch), bundlehint].join(%( ))
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
module PangeaModule
|
93
|
-
include PangeaBase
|
94
|
-
|
95
|
-
CACHE_DIR = File.join(
|
96
|
-
BASE_DIR,
|
97
|
-
%(cache)
|
98
|
-
)
|
99
|
-
|
100
|
-
RUBIES_DIR = File.join(
|
101
|
-
BASE_DIR,
|
102
|
-
%(rubies)
|
103
|
-
)
|
104
|
-
|
105
|
-
class << self
|
106
|
-
def symbolize(hash)
|
107
|
-
JSON[JSON[hash], symbolize_names: true]
|
108
|
-
end
|
109
|
-
|
110
|
-
def terraform_synth
|
111
|
-
@terraform_synth ||= TerraformSynthesizer.new
|
112
|
-
end
|
113
|
-
|
114
|
-
# entrypoint for module processing
|
115
|
-
def process(mod)
|
116
|
-
mod = symbolize(mod)
|
117
|
-
|
118
|
-
name = mod.fetch(:name)
|
119
|
-
data = mod.fetch(:data, {})
|
120
|
-
|
121
|
-
raise ArgumentError, %(name cannot be nil) if name.nil?
|
122
|
-
|
123
|
-
# understanding that module entrypoint loading
|
124
|
-
# will work with #{context}-#{name}
|
125
|
-
context = mod.fetch(:context, %(pangea-component))
|
126
|
-
require_name = %(#{context}-#{name})
|
127
|
-
|
128
|
-
require require_name
|
129
|
-
render(data)
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
# end modules
|
@@ -1,241 +0,0 @@
|
|
1
|
-
# a renderer takes an artifact.json and implements
|
2
|
-
# it against a rest api, then potentially runs
|
3
|
-
# packaged checks and verifications.
|
4
|
-
# it also exports values into state for the
|
5
|
-
# child renderer to pick up on
|
6
|
-
|
7
|
-
require %(terraform-synthesizer)
|
8
|
-
require %(pangea/modcache)
|
9
|
-
require %(pangea/config)
|
10
|
-
require %(pangea/utils)
|
11
|
-
require %(aws-sdk-s3)
|
12
|
-
require %(digest)
|
13
|
-
require %(json)
|
14
|
-
|
15
|
-
module Pangea
|
16
|
-
class DirectoryRenderer
|
17
|
-
BIN = %(tofu).freeze
|
18
|
-
|
19
|
-
def home_dir
|
20
|
-
%(#{Dir.home}/.pangea)
|
21
|
-
end
|
22
|
-
|
23
|
-
def infra_dir
|
24
|
-
%(#{home_dir}/infra)
|
25
|
-
end
|
26
|
-
|
27
|
-
def init_dir
|
28
|
-
%(#{infra_dir}/init)
|
29
|
-
end
|
30
|
-
|
31
|
-
def artifact_json
|
32
|
-
File.join(init_dir, %(artifact.tf.json))
|
33
|
-
end
|
34
|
-
|
35
|
-
def pretty(content)
|
36
|
-
JSON.pretty_generate(content)
|
37
|
-
end
|
38
|
-
|
39
|
-
def create_prepped_state_directory(dir, synthesis)
|
40
|
-
system %(mkdir -p #{dir}) unless Dir.exist?(dir)
|
41
|
-
File.write(File.join(dir, %(artifact.tf.json)), JSON[synthesis])
|
42
|
-
system %(cd #{dir} && #{BIN} init -json) unless Dir.exist?(File.join(dir, %(.terraform)))
|
43
|
-
true
|
44
|
-
end
|
45
|
-
|
46
|
-
def resource(dir)
|
47
|
-
JSON[File.read(File.join(dir, %(artifact.tf.json)))]
|
48
|
-
end
|
49
|
-
|
50
|
-
def state(dir)
|
51
|
-
pretty(JSON[File.read(File.join(dir, %(terraform.tfstate)))])
|
52
|
-
end
|
53
|
-
|
54
|
-
def plan(dir)
|
55
|
-
pretty(JSON[File.read(File.join(dir, %(plan.json)))])
|
56
|
-
end
|
57
|
-
|
58
|
-
# component is a single resource wrapped in state
|
59
|
-
# with available attributes
|
60
|
-
def render_component(&block)
|
61
|
-
synthesizer.synthesize(&block)
|
62
|
-
resource_type = synthesizer.synthesis[:resource].keys[0]
|
63
|
-
resource_name = synthesizer.synthesis[:resource][synthesizer.synthesis[:resource].keys[0]].keys[0]
|
64
|
-
dir = File.join(init_dir, resource_type.to_s, resource_name.to_s)
|
65
|
-
create_prepped_state_directory(dir, synthesizer.synthesis)
|
66
|
-
system %(cd #{dir} && #{BIN} show -json tfplan > plan.json)
|
67
|
-
system %(cd #{dir} && #{BIN} apply -auto-approve)
|
68
|
-
|
69
|
-
synthesizer.clear_synthesis!
|
70
|
-
|
71
|
-
{
|
72
|
-
resource: resource(dir),
|
73
|
-
state: state(dir),
|
74
|
-
plan: plan(dir)
|
75
|
-
}
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
class S3Renderer
|
80
|
-
class << self
|
81
|
-
def synthesizer
|
82
|
-
@synthesizer ||= TerraformSynthesizer.new
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
# BIN = %(tofu).freeze
|
87
|
-
|
88
|
-
# attr_reader :namespace
|
89
|
-
|
90
|
-
# def initialize
|
91
|
-
# raise ArgumentError, 'provide PANGEA_NAMESPACE ENVVAR' if ENV.fetch('PANGEA_NAMESPACE').nil?
|
92
|
-
#
|
93
|
-
# @namespace = ENV.fetch('PANGEA_NAMESPACE', nil)
|
94
|
-
# end
|
95
|
-
|
96
|
-
# def config
|
97
|
-
# @config ||= Pangea::Utils.symbolize(
|
98
|
-
# Pangea::Config.config
|
99
|
-
# )
|
100
|
-
# end
|
101
|
-
|
102
|
-
# def s3
|
103
|
-
# @s3 = Aws::S3::Client.new
|
104
|
-
# end
|
105
|
-
|
106
|
-
# def verify_state(state)
|
107
|
-
# raise Argumenterror, 'must have a bucket' unless state[:config][:bucket]
|
108
|
-
# raise Argumenterror, 'must have a region' unless state[:config][:region]
|
109
|
-
# raise Argumenterror, 'must have a lock' unless state[:config][:lock]
|
110
|
-
# end
|
111
|
-
|
112
|
-
# def pangea_home
|
113
|
-
# %(#{Dir.home}/.pangea/#{namespace})
|
114
|
-
# end
|
115
|
-
|
116
|
-
# def bin
|
117
|
-
# %(tofu)
|
118
|
-
# end
|
119
|
-
|
120
|
-
# def selected_namespace_configuration
|
121
|
-
# sns = ''
|
122
|
-
# config[:namespaces].each_key do |ns|
|
123
|
-
# sns = config[:namespaces][ns] if ns.to_s.eql?(namespace.to_s)
|
124
|
-
# end
|
125
|
-
# @selected_namespace_configuration ||= sns
|
126
|
-
# end
|
127
|
-
|
128
|
-
# render things in a resource context
|
129
|
-
# without using terraform modules
|
130
|
-
# def state(name, &block)
|
131
|
-
# if block.nil?
|
132
|
-
# File.write(File.join(local_cache, 'main.tf.json'), JSON[{}])
|
133
|
-
# system("cd #{local_cache} && #{bin} init -input=false")
|
134
|
-
# system("cd #{local_cache} && #{bin} plan")
|
135
|
-
# system("cd #{local_cache} && #{bin} apply -auto-approve")
|
136
|
-
# return {}
|
137
|
-
# end
|
138
|
-
# S3Renderer.synthesizer.synthesize(&block)
|
139
|
-
# synth = Pangea::Utils.symbolize(S3Renderer.synthesizer.synthesis)
|
140
|
-
# prefix = "#{name}/pangea"
|
141
|
-
# local_cache = File.join(pangea_home, prefix)
|
142
|
-
# `mkdir -p #{local_cache}` unless Dir.exist?(local_cache)
|
143
|
-
# sns = selected_namespace_configuration
|
144
|
-
# verify_state(sns[:state])
|
145
|
-
#
|
146
|
-
# # apply state configuration
|
147
|
-
# unless synth[:terraform]
|
148
|
-
# S3Renderer.synthesizer.synthesize do
|
149
|
-
# terraform do
|
150
|
-
# backend(
|
151
|
-
# s3: {
|
152
|
-
# key: prefix,
|
153
|
-
# dynamodb_table: sns[:state][:config][:lock].to_s,
|
154
|
-
# bucket: sns[:state][:config][:bucket].to_s,
|
155
|
-
# region: sns[:state][:config][:region].to_s,
|
156
|
-
# encrypt: true
|
157
|
-
# }
|
158
|
-
# )
|
159
|
-
# end
|
160
|
-
# end
|
161
|
-
# end
|
162
|
-
#
|
163
|
-
# File.write(File.join(local_cache, 'main.tf.json'), JSON[S3Renderer.synthesizer.synthesis])
|
164
|
-
# template = Pangea::Utils.symbolize(JSON[File.read(File.join(local_cache, 'main.tf.json'))])
|
165
|
-
# system("cd #{local_cache} && #{bin} init -input=false")
|
166
|
-
# system("cd #{local_cache} && #{bin} plan")
|
167
|
-
# system("cd #{local_cache} && #{bin} apply -auto-approve")
|
168
|
-
# # puts s3.list_objects_v2(bucket: sns[:state][:config][:bucket], prefix: prefix).contents.map(&:key)
|
169
|
-
# { template: template }
|
170
|
-
# end
|
171
|
-
|
172
|
-
# def state_keys
|
173
|
-
# sns = selected_namespace_configuration
|
174
|
-
# end
|
175
|
-
|
176
|
-
# def state; end
|
177
|
-
|
178
|
-
# def render_component(template)
|
179
|
-
# mod = 'stump'
|
180
|
-
# sns = ''
|
181
|
-
# config[:namespaces].each_key do |ns|
|
182
|
-
# sns = config[:namespaces][ns] if ns.to_s.eql?(namespace.to_s)
|
183
|
-
# end
|
184
|
-
#
|
185
|
-
# unless sns[:state][:type].to_s.eql?('s3')
|
186
|
-
# raise ArgumentError,
|
187
|
-
# 'state type must be s3 '
|
188
|
-
# end
|
189
|
-
#
|
190
|
-
# if sns.nil? || sns.empty?
|
191
|
-
# raise ArgumentError,
|
192
|
-
# "namespace #{namespace} not found in #{Pangea::Utils.pretty(config)}"
|
193
|
-
# end
|
194
|
-
#
|
195
|
-
# synthesizer.synthesize(template)
|
196
|
-
# syn = Pangea::Utils.symbolize(synthesizer.synthesis)
|
197
|
-
# raise ArgumentError, 'must provide at least one resource' if syn[:resource].nil?
|
198
|
-
#
|
199
|
-
# resource_name = syn[:resource].keys[0]
|
200
|
-
# virtual_name = syn[:resource][syn[:resource].keys[0]].keys[0]
|
201
|
-
#
|
202
|
-
# synthesizer.synthesize do
|
203
|
-
# provider do
|
204
|
-
# aws(region: sns[:state][:config][:region].to_s)
|
205
|
-
# end
|
206
|
-
# variable do
|
207
|
-
# name(type: 'string', description: 'the module name')
|
208
|
-
# end
|
209
|
-
# end
|
210
|
-
#
|
211
|
-
# synthesizer.synthesize do
|
212
|
-
# terraform do
|
213
|
-
# backend(
|
214
|
-
# s3: {
|
215
|
-
# key: "#{sns[:name]}/#{mod}/#{resource_name}/#{virtual_name}/module",
|
216
|
-
# dynamodb_table: sns[:state][:config][:lock].to_s,
|
217
|
-
# bucket: sns[:state][:config][:bucket].to_s,
|
218
|
-
# region: sns[:state][:config][:region].to_s,
|
219
|
-
# encrypt: true
|
220
|
-
# }
|
221
|
-
# )
|
222
|
-
# end
|
223
|
-
# end
|
224
|
-
#
|
225
|
-
# # modcache_address = "#{sns[:name]}/#{mod}/#{resource_name}/#{virtual_name}/module"
|
226
|
-
#
|
227
|
-
# # create the modcache directory
|
228
|
-
# modcache = Pangea::ModCache.new(
|
229
|
-
# sns[:name],
|
230
|
-
# mod,
|
231
|
-
# resource_name,
|
232
|
-
# virtual_name
|
233
|
-
# )
|
234
|
-
# # place the internal module
|
235
|
-
# modcache.place_internal_module(syn)
|
236
|
-
# modcache.place_caller_template
|
237
|
-
#
|
238
|
-
# Pangea::Utils.symbolize(synthesizer.synthesis)
|
239
|
-
# end
|
240
|
-
end
|
241
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
###############################################################################
|
2
|
-
# sandbox
|
3
|
-
#
|
4
|
-
# modules need an execution space which I'm naming sandboxes
|
5
|
-
# sandboxes will contain the ruby interpreter, associated packages
|
6
|
-
# and pangea DSL code for the execution of a single module
|
7
|
-
###############################################################################
|
8
|
-
|
9
|
-
class SandboxRuby
|
10
|
-
attr_reader(*%i[base_dir name gemset version])
|
11
|
-
|
12
|
-
def initialize(base_dir:, name:, gemset:, version:)
|
13
|
-
@base_dir = base_dir
|
14
|
-
@version = version
|
15
|
-
@name = name
|
16
|
-
@gemset = gemset
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class SandBox
|
21
|
-
attr_reader(*%i[base_dir name rubies])
|
22
|
-
|
23
|
-
def initialize(
|
24
|
-
name:,
|
25
|
-
base_dir: %(~/.pangea/sandbox),
|
26
|
-
rubies: []
|
27
|
-
)
|
28
|
-
@base_dir = base_dir
|
29
|
-
@name = name
|
30
|
-
@rubies = rubies
|
31
|
-
|
32
|
-
@rubies_dir = File.join(@base_dir, @name, %(rubies))
|
33
|
-
|
34
|
-
@ruby_build_cmd = %(ruby-build)
|
35
|
-
end
|
36
|
-
|
37
|
-
def prepare_sandbox
|
38
|
-
ensure_base_dir_exists
|
39
|
-
ensure_rubies_directories_exist
|
40
|
-
check_ruby_build_installed
|
41
|
-
ensure_rubies_installed
|
42
|
-
end
|
43
|
-
|
44
|
-
def clean_sandbox
|
45
|
-
system %(rm -rf #{base_dir})
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def ensure_base_dir_exists
|
51
|
-
system %(mkdir -p #{base_dir}) unless Dir.exist?(base_dir)
|
52
|
-
end
|
53
|
-
|
54
|
-
def ensure_rubies_directories_exist
|
55
|
-
rubies.each do |ruby|
|
56
|
-
ruby_dir = File.join(@rubies_dir, ruby.version, ruby.gemset, ruby.name)
|
57
|
-
system %(mkdir -p #{ruby_dir}) unless Dir.exist?(ruby_dir)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def command_exists_on_path?(cmd)
|
62
|
-
system("which #{cmd} > /dev/null 2>&1")
|
63
|
-
end
|
64
|
-
|
65
|
-
def ruby_build_installed?
|
66
|
-
command_exists_on_path?(@ruby_build_cmd)
|
67
|
-
end
|
68
|
-
|
69
|
-
def ensure_rubies_installed
|
70
|
-
rubies.each do |ruby|
|
71
|
-
ruby_dir = File.join(@rubies_dir, ruby.version, ruby.gemset, ruby.name)
|
72
|
-
system %(#{@ruby_build_cmd} #{ruby.version} #{ruby_dir}) unless File.exist?(
|
73
|
-
File.join(
|
74
|
-
ruby_dir,
|
75
|
-
%(.installed_by_pangea)
|
76
|
-
)
|
77
|
-
)
|
78
|
-
system %(touch #{File.join(ruby_dir, %(.installed_by_pangea))}) unless File.exist?(
|
79
|
-
File.join(
|
80
|
-
ruby_dir,
|
81
|
-
%(.installed_by_pangea)
|
82
|
-
)
|
83
|
-
)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def check_ruby_build_installed
|
88
|
-
raise %(ruby-build not installed) unless ruby_build_installed?
|
89
|
-
end
|
90
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require %(tty-color)
|
2
|
-
require %(tty-box)
|
3
|
-
|
4
|
-
module Say
|
5
|
-
class << self
|
6
|
-
def terminal(msg)
|
7
|
-
spec = {
|
8
|
-
width: 80,
|
9
|
-
style: {
|
10
|
-
fg: :yellow,
|
11
|
-
bg: :blue,
|
12
|
-
border: { fg: :green, bg: :black }
|
13
|
-
},
|
14
|
-
align: :right,
|
15
|
-
border: :thick
|
16
|
-
# padding: 0,
|
17
|
-
# height: 1
|
18
|
-
}
|
19
|
-
|
20
|
-
box = TTY::Box.frame(**spec)
|
21
|
-
|
22
|
-
puts box + "\n"
|
23
|
-
puts msg.strip
|
24
|
-
puts "\n" + box
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1 +0,0 @@
|
|
1
|
-
# localize calling out to the shell
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module Shell
|
2
|
-
module Terraform
|
3
|
-
BIN = ENV[%(TERRAFORM_BIN)] || %(terraform).freeze
|
4
|
-
|
5
|
-
class << self
|
6
|
-
def run(terraform_cmd)
|
7
|
-
cmd = []
|
8
|
-
# cmd << %(cd)
|
9
|
-
# cmd << Dir.pwd
|
10
|
-
# cmd << %(&&)
|
11
|
-
cmd << BIN
|
12
|
-
cmd << terraform_cmd
|
13
|
-
system cmd.join(%( ))
|
14
|
-
end
|
15
|
-
|
16
|
-
def plan
|
17
|
-
run(%(plan))
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'open3'
|
2
|
-
module Pangea
|
3
|
-
module Shell
|
4
|
-
class << self
|
5
|
-
def run(command)
|
6
|
-
Open3.popen3(command) do |_stdin, stdout, stderr, wait_thr|
|
7
|
-
# Process standard output
|
8
|
-
stdout.each_line do |line|
|
9
|
-
parsed = JSON.parse(line.strip)
|
10
|
-
puts JSON.pretty_generate(parsed)
|
11
|
-
puts "\n---\n" # Separator between JSON objects
|
12
|
-
rescue JSON::ParserError
|
13
|
-
warn "⚠️ Invalid JSON received: #{line.inspect}"
|
14
|
-
end
|
15
|
-
|
16
|
-
# Handle standard error
|
17
|
-
unless (err = stderr.read).empty?
|
18
|
-
warn "\n❌ Command errors:\n#{err}"
|
19
|
-
end
|
20
|
-
|
21
|
-
exit_status = wait_thr.value
|
22
|
-
warn "\n🔥 Command failed with status #{exit_status.exitstatus}" unless exit_status.success?
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,96 +0,0 @@
|
|
1
|
-
require 'aws-sdk-s3'
|
2
|
-
require 'aws-sdk-dynamodb'
|
3
|
-
|
4
|
-
module Pangea
|
5
|
-
# Base state management class
|
6
|
-
class State
|
7
|
-
def initialize
|
8
|
-
# Initialize common state if needed.
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
# Manage local state
|
13
|
-
class LocalState < Pangea::State; end
|
14
|
-
|
15
|
-
# Manage S3 state
|
16
|
-
class S3State < Pangea::State
|
17
|
-
# Creates a DynamoDB table to be used for state locking.
|
18
|
-
# Optional parameters allow you to customize the table name, AWS region, and whether to check for existing table.
|
19
|
-
def create_dynamodb_table_for_lock(name:, region:, check: true)
|
20
|
-
dynamodb = Aws::DynamoDB::Client.new(region: region)
|
21
|
-
|
22
|
-
if check
|
23
|
-
begin
|
24
|
-
# Check if the table already exists
|
25
|
-
dynamodb.describe_table(table_name: name)
|
26
|
-
puts "DynamoDB table '#{name}' already exists, skipping creation."
|
27
|
-
return
|
28
|
-
rescue Aws::DynamoDB::Errors::ResourceNotFoundException
|
29
|
-
# Table does not exist; proceed with creation.
|
30
|
-
rescue Aws::DynamoDB::Errors::ServiceError => e
|
31
|
-
puts "Failed to check DynamoDB table existence: #{e.message}"
|
32
|
-
return
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
begin
|
37
|
-
dynamodb.create_table(
|
38
|
-
{
|
39
|
-
table_name: name,
|
40
|
-
attribute_definitions: [
|
41
|
-
{
|
42
|
-
attribute_name: 'LockID',
|
43
|
-
attribute_type: 'S'
|
44
|
-
}
|
45
|
-
],
|
46
|
-
key_schema: [
|
47
|
-
{
|
48
|
-
attribute_name: 'LockID',
|
49
|
-
key_type: 'HASH'
|
50
|
-
}
|
51
|
-
],
|
52
|
-
provisioned_throughput: {
|
53
|
-
read_capacity_units: 1,
|
54
|
-
write_capacity_units: 1
|
55
|
-
}
|
56
|
-
}
|
57
|
-
)
|
58
|
-
puts "DynamoDB table '#{name}' created successfully!"
|
59
|
-
rescue Aws::DynamoDB::Errors::ResourceInUseException
|
60
|
-
puts "DynamoDB table '#{name}' already exists."
|
61
|
-
rescue Aws::DynamoDB::Errors::ServiceError => e
|
62
|
-
puts "Failed to create DynamoDB table: #{e.message}"
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# Creates an S3 bucket for state storage.
|
67
|
-
# If `check` is true, the method first checks if the bucket exists.
|
68
|
-
def create_bucket(name:, region:, check: true)
|
69
|
-
s3 = Aws::S3::Client.new(region: region)
|
70
|
-
|
71
|
-
if check
|
72
|
-
begin
|
73
|
-
# Attempt to check if the bucket exists
|
74
|
-
s3.head_bucket(bucket: name)
|
75
|
-
# Bucket exists; nothing to do.
|
76
|
-
return
|
77
|
-
rescue Aws::S3::Errors::NotFound, Aws::S3::Errors::NoSuchBucket
|
78
|
-
# Bucket does not exist; proceed with creation.
|
79
|
-
rescue Aws::S3::Errors::Forbidden => e
|
80
|
-
# The bucket exists but is inaccessible (perhaps owned by someone else).
|
81
|
-
puts "Bucket '#{name}' exists but is not accessible: #{e.message}"
|
82
|
-
return
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
begin
|
87
|
-
s3.create_bucket(bucket: name)
|
88
|
-
puts "Bucket '#{name}' created successfully!"
|
89
|
-
rescue Aws::S3::Errors::BucketAlreadyOwnedByYou
|
90
|
-
puts "Bucket '#{name}' already exists and is owned by you."
|
91
|
-
rescue Aws::S3::Errors::ServiceError => e
|
92
|
-
puts "Failed to create bucket: #{e.message}"
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
require %(pangea/cli/constants)
|
2
|
-
require %(abstract-synthesizer)
|
3
|
-
require %(toml-rb)
|
4
|
-
require %(json)
|
5
|
-
require %(yaml)
|
6
|
-
|
7
|
-
###############################################################################
|
8
|
-
# read files merge config data and provide a single configuation structure
|
9
|
-
###############################################################################
|
10
|
-
|
11
|
-
class ConfigSynthesizer < AbstractSynthesizer
|
12
|
-
include Constants
|
13
|
-
|
14
|
-
def synthesize(content, ext)
|
15
|
-
case ext.to_s
|
16
|
-
when %(yaml), %(yml)
|
17
|
-
translation[:template] = YAML.safe_load(content)
|
18
|
-
when %(toml)
|
19
|
-
translation[:template] = TomlRB.parse(content)
|
20
|
-
when %(json)
|
21
|
-
translation[:template] = JSON.parse(content)
|
22
|
-
when %(rb)
|
23
|
-
if block_given?
|
24
|
-
yield
|
25
|
-
else
|
26
|
-
instance_eval(content)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def method_missing(method_name, ...)
|
32
|
-
abstract_method_missing(
|
33
|
-
method_name,
|
34
|
-
%i[namespace],
|
35
|
-
...
|
36
|
-
)
|
37
|
-
end
|
38
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
module Pangea
|
2
|
-
module Utils
|
3
|
-
class << self
|
4
|
-
def component(kwargs)
|
5
|
-
resource(kwargs[:type], kwargs[:name]) do
|
6
|
-
kwargs[:attrs].each_key do |k|
|
7
|
-
send(k, kwargs[:attrs][k])
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def pretty(hash)
|
13
|
-
JSON.pretty_generate(hash)
|
14
|
-
end
|
15
|
-
|
16
|
-
def symbolize(hash)
|
17
|
-
JSON[JSON[hash], symbolize_names: true]
|
18
|
-
end
|
19
|
-
|
20
|
-
# Recursively deep merges two hashes.
|
21
|
-
def deep_merge(hash1, hash2)
|
22
|
-
hash1.merge(hash2) do |_, old_val, new_val|
|
23
|
-
if old_val.is_a?(Hash) && new_val.is_a?(Hash)
|
24
|
-
deep_merge(old_val, new_val)
|
25
|
-
else
|
26
|
-
new_val
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|