kustomizer 0.1.0 → 0.1.5

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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +5 -3
  3. data/README.md +129 -9
  4. data/kustomizer.gemspec +1 -0
  5. data/lib/kustomize.rb +12 -8
  6. data/lib/kustomize/builtin_plugins/kustomizer.covalenthq.com/v1/test_generator.rb +15 -0
  7. data/lib/kustomize/builtin_plugins/kustomizer.covalenthq.com/v1/test_transformer.rb +15 -0
  8. data/lib/kustomize/emitter.rb +4 -0
  9. data/lib/kustomize/emitter/document_emitter/kustomization_document_emitter.rb +37 -14
  10. data/lib/kustomize/emitter/finalizer_emitter.rb +31 -0
  11. data/lib/kustomize/emitter/generator_plugins_emitter.rb +26 -0
  12. data/lib/kustomize/generator_plugin.rb +16 -0
  13. data/lib/kustomize/plugin.rb +30 -5
  14. data/lib/kustomize/plugin_manager.rb +31 -16
  15. data/lib/kustomize/plugin_registry.rb +39 -0
  16. data/lib/kustomize/session.rb +10 -7
  17. data/lib/kustomize/target_spec.rb +2 -2
  18. data/lib/kustomize/transform.rb +9 -1
  19. data/lib/kustomize/transform/fingerprint_suffix_transform.rb +57 -0
  20. data/lib/kustomize/transform/image_transform.rb +1 -1
  21. data/lib/kustomize/transform/json_6902_patch_transform.rb +1 -1
  22. data/lib/kustomize/transform/namespace_transform.rb +1 -1
  23. data/lib/kustomize/transform/purge_internal_annotations_transform.rb +31 -0
  24. data/lib/kustomize/transform/ref_fixup_transform.rb +59 -0
  25. data/lib/kustomize/transform/transformer_plugins_transform.rb +28 -0
  26. data/lib/kustomize/transformer_plugin.rb +16 -0
  27. data/lib/kustomize/version.rb +1 -1
  28. metadata +27 -4
  29. data/lib/kustomize/emitter/plugin_emitter.rb +0 -30
  30. data/lib/kustomize/transform/name_digest_autosuffix_transform.rb +0 -83
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b88c7e828be9c93ec00ef81cd1fa0b583f2772c6b621cfab5ce0e422418d49c
4
- data.tar.gz: a08c5c25f4c234ea9b698d64e6f327a5e3a17147d1cf17c613471fcf0f32a7cf
3
+ metadata.gz: 50dca06404547fd4209e5d04a235e286e1b3d120852e28ccc6290fa8fe186cdd
4
+ data.tar.gz: 6fc755d3355cfc7726f23242d4d1f0234c38ca0e1ee09d11144e3d4b5d1480a3
5
5
  SHA512:
6
- metadata.gz: 42ecbbe674b0b4f49c434457f99cc43084e4c6d432789d15ac4a3081e345a5dda6b459ca1db1f15057f2279f3b00454dd46e5d350a4c60fe0e391495c26d5952
7
- data.tar.gz: dbb5dbbca1c1b1ae4e95584fb9601284b5f8c3f73a2b4ba15f02d4f1bf382634d87e86632ec7319e90761beb6de8ef8cc946909531718df4bfeff3a9793351fd
6
+ metadata.gz: 8799db97dd06ad3bd6aa1156e1ea9484cfd48268003f499e4a55f8e12a38d552ae0ae612a10e0c000f5c9da674c0886035ce5b452d8e07053e7e7cdb3e1bff6f
7
+ data.tar.gz: 7e75ded7af9c2b8c4f34e58dcd14e18fc5c2df62a3fdf0e5ec0fe5eaaa67bc7cfdf4f955f700adaeaff15f59024f4d20b6d667eac412659acec71912c52fb99c
@@ -1,13 +1,15 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- kustomizer (0.1.0)
4
+ kustomizer (0.1.5)
5
5
  accessory (~> 0.1.9)
6
+ base32-multi
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
10
- accessory (0.1.9)
11
+ accessory (0.1.10)
12
+ base32-multi (0.1.1)
11
13
  diff-lcs (1.4.4)
12
14
  rake (13.0.3)
13
15
  rspec (3.10.0)
@@ -33,4 +35,4 @@ DEPENDENCIES
33
35
  rspec (~> 3.0)
34
36
 
35
37
  BUNDLED WITH
36
- 2.2.5
38
+ 2.2.7
data/README.md CHANGED
@@ -9,9 +9,14 @@ the user to have the native Go version of Kustomize installed.
9
9
  ## Roadmap
10
10
 
11
11
  KustomizeR is **not yet feature-complete**, i.e. it does not yet do everything
12
- that Kustomize does.
12
+ that Kustomize does. KustomizeR probably won't work for arbitrary
13
+ `kustomization.yaml` files.
13
14
 
14
- Status of `kustomization.yaml` support:
15
+ (KustomizeR *is*, however, in production use; it is being used with
16
+ `kustomization.yaml` files matching its current feature set. We wrote Just
17
+ Enough Library to solve our own problems :wink:)
18
+
19
+ Status of `kustomization.yaml` feature support:
15
20
 
16
21
  * [x] `bases`
17
22
  * [ ] `commonAnnotations`
@@ -31,7 +36,7 @@ Status of `kustomization.yaml` support:
31
36
  * [ ] `transformers` (see [Extending Kustomize](https://kubectl.docs.kubernetes.io/guides/extending_kustomize/))
32
37
  * [ ] `vars`
33
38
 
34
- Status of support for other features:
39
+ Status of support for other Kustomize features:
35
40
 
36
41
  * [ ] Resource loading
37
42
  * [x] resource-config files on disk
@@ -44,6 +49,15 @@ Status of support for other features:
44
49
  * [x] Secrets
45
50
  * [ ] ConfigMaps
46
51
 
52
+ Status of support for "extra" features not supported by Kustomize:
53
+
54
+ * [ ] `filters` (a plugin-type for dropping resource-configs from output)
55
+ * [ ] `rewriters` (a plugin-type for entirely replacing output; takes all
56
+ intermediate resource-config docs as a single input)
57
+ * [ ] Built-in plugins:
58
+ * [ ] `SealedSecretGenerator`
59
+ * [ ] `DerivedSecretGenerator`
60
+
47
61
  #### Differences from Kustomize
48
62
 
49
63
  * KustomizeR is **not yet feature-complete**. (KustomizeR will be bumped to
@@ -55,12 +69,16 @@ Status of support for other features:
55
69
  * KustomizeR is modular, and is intended to be loaded and used as a library,
56
70
  rather than being spawned as a subprocess. Crucially, the load path for
57
71
  plugins is under the caller's control, and so higher-level frameworks can
58
- manage project-level KustomizeR plugins.
72
+ inject plugins into a KustomizeR session to suit their needs.
59
73
 
60
74
  * Some `kustomization.yaml` features have been temporarily extended in
61
- non-compatible ways.
62
- * `patchesJson6902` accepts an inline an `ops` array
63
- * `patchesJson6902` accepts a `gsub` op
75
+ non-compatible ways:
76
+ * `patchesJson6902`
77
+ * accepts an inline `ops` array
78
+ * accepts lens accessor names in `path` (e.g. `/spec/rules/:all/host`)
79
+ * accepts a `paths` array rather than a single `path`
80
+ * accepts a `gsub` op (works like `replace` but with a regular expression;
81
+ has `pattern` and `replacement` fields)
64
82
 
65
83
  (Before v1.0, these extensions will be moved to become built-in plugins, to
66
84
  allow for inter-compatibility with Kustomize, which could support them as
@@ -86,12 +104,114 @@ Or install it yourself as:
86
104
 
87
105
  ```ruby
88
106
  require 'kustomize'
107
+ ```
108
+
109
+ ### Loading Kustomization documents
110
+
111
+ You can either load a `kustomization.yaml` file by specifying its path:
112
+
113
+ ```ruby
89
114
  k = Kustomize.load("./path/to/kustomization.yaml")
90
115
 
91
- k.emit # Array of Hashes (the final resource-configs)
92
- k.to_yaml_stream # String (merged YAML multi-document stream)
116
+ # equivalent; discovers the kustomization.yaml within the directory
117
+ k = Kustomize.load("./path/to")
93
118
  ```
94
119
 
120
+ Or you can load a `Kustomization` document spec directly:
121
+
122
+ ```ruby
123
+ k_doc = {
124
+ 'apiVersion' => 'kustomize.config.k8s.io/v1beta1',
125
+ 'kind' => 'Kustomization',
126
+ # ...
127
+ }
128
+
129
+ k = Kustomization.load(k_doc, source_path: "./path/to/kustomization.yaml")
130
+ ```
131
+
132
+ Note the `source_path` keyword parameter. Specifying a `source_path` for an
133
+ in-memory Kustomization document is optional, but will usually be necessary, as
134
+ the Kustomization document will need an effective path for itself, to use as a
135
+ relative navigation prefix for any referenced on-disk resource files/directories.
136
+
137
+ The `source_path` can be left out if all the resources a Kustomization document
138
+ references are either remote or generated.
139
+
140
+ ### Rendering resource-configuration documents
141
+
142
+ To get the final resource-configurations directly, as an Array of Hashes, call
143
+ `KustomizationDocument#emit`:
144
+
145
+ ```ruby
146
+ k.emit # => [{'kind' => 'Deployment', ...}, ...]
147
+ ```
148
+
149
+ Or, to get a merged YAML multi-document stream suitable for feeding to
150
+ `kubectl apply -f`, call `KustomizationDocument#to_yaml_stream`:
151
+
152
+ ```ruby
153
+ k.to_yaml_stream # => "---\nkind: Deployment\n..."
154
+ ```
155
+
156
+ ### Accessing intermediate resources
157
+
158
+ KustomizeR represents your Kustomization document as a digraph of `Emitter`
159
+ instances, a combination of `FileEmitter`s, `DirectoryEmitter`s,
160
+ `DocumentEmitter`s, and `PluginEmitter`s. You can think of these as being
161
+ arranged akin to an audio VST digraph, where emitters are "plugged into" other
162
+ downstream emitters, with the outputs (resource configs) of one emitter becoming
163
+ inputs to another.
164
+
165
+ All `Emitter` types support the following methods:
166
+
167
+ ```ruby
168
+ e.input_emitters # gets the emitters that feed their output into this emitter
169
+
170
+ e.input_resources # runs the input emitters, gathering their outputs and
171
+ # caching it as this emitter's input
172
+
173
+ e.emit # runs this emitter, producing the output that would be fed
174
+ # into any downstream emitters
175
+ ```
176
+
177
+ A `KustomizationDocument` constructed by `Kustomize.load` is just a regular
178
+ `Emitter`; you can use it as the starting point to explore or manipulate the
179
+ rest of the `Emitter` graph.
180
+
181
+ ### Sessions
182
+
183
+ All emitters belong to a `Kustomize::Session`. When you call `Kustomize.load`,
184
+ you pass in (or implicitly create) a new `Kustomize::Session`:
185
+
186
+ ```ruby
187
+ Kustomize.load("./foo", session: Kustomize::Session.new)
188
+ Kustomize.load("./foo") # equivalent to above
189
+ ```
190
+
191
+ #### Plugin Load-Paths
192
+
193
+ The `Kustomize::Session` manages plugin load-paths. By default, it defines a
194
+ load-path referencing only the built-in plugins embedded within this gem.
195
+
196
+ You can create your own subclass of `Kustomize::Session` to define a new
197
+ load-path, and pass in an instance of it as the `session` keyword-parameter
198
+ to `Kustomize.load`. This custom Session will be inherited by all `Emitter`s
199
+ created under the loaded `KustomizeDocument` emitter.
200
+
201
+ You can also add other features to your `Kustomize::Session` subclass. The
202
+ passed-in `Session` is accessible within `Kustomize::Plugin`s as
203
+ `this.session`, so it can be useful to pass e.g. a framework context object as
204
+ a member of your `Kustomize::Session` subclass, for use by framework-specific
205
+ plugins.
206
+
207
+ #### Plugin Discovery and Loading
208
+
209
+ The `Kustomize::Session` also holds an instance of `Kustomize::PluginManager`,
210
+ which discovers, loads, and caches plugins.
211
+
212
+ As such, if you're calling `Kustomize.load` a lot, it is recommended to reuse
213
+ your `Kustomize::Session`, so that plugins need only be discovered+loaded once.
214
+
95
215
  ## Development
96
216
 
97
217
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -25,4 +25,5 @@ Gem::Specification.new do |spec|
25
25
  spec.require_paths = ["lib"]
26
26
 
27
27
  spec.add_runtime_dependency "accessory", "~> 0.1.9"
28
+ spec.add_runtime_dependency "base32-multi"
28
29
  end
@@ -5,18 +5,22 @@ require 'kustomize/session'
5
5
 
6
6
  require 'kustomize/emitter/file_emitter'
7
7
  require 'kustomize/emitter/directory_emitter'
8
+ require 'kustomize/emitter/finalizer_emitter'
8
9
  require 'kustomize/emitter/document_emitter/kustomization_document_emitter'
9
10
 
10
11
  module Kustomize
11
12
  def self.load(rel_path_or_rc, session: Kustomize::Session.new, source_path: nil)
12
- case rel_path_or_rc
13
- when String, Pathname
14
- load_path(rel_path_or_rc, session: session)
15
- when Hash
16
- load_doc(rel_path_or_rc, session: session, source_path: source_path)
17
- else
18
- raise ArgumentError, "must be a kustomization document or a path to one, instead got: #{rel_path_or_rc.inspect}"
19
- end
13
+ base_emitter =
14
+ case rel_path_or_rc
15
+ when String, Pathname
16
+ load_path(rel_path_or_rc, session: session)
17
+ when Hash
18
+ load_doc(rel_path_or_rc, session: session, source_path: source_path)
19
+ else
20
+ raise ArgumentError, "must be a kustomization document or a path to one, instead got: #{rel_path_or_rc.inspect}"
21
+ end
22
+
23
+ Kustomize::Emitter::FinalizerEmitter.new(base_emitter)
20
24
  end
21
25
 
22
26
  def self.load_doc(rc, session: Kustomize::Session.new, source_path:)
@@ -0,0 +1,15 @@
1
+ require 'kustomize/generator_plugin'
2
+
3
+ module CovalentKustomizer; end
4
+
5
+ class CovalentKustomizer::TestGenerator < Kustomize::GeneratorPlugin
6
+ match_on api_version: 'kustomizer.covalenthq.com/v1'
7
+
8
+ def initialize(rc)
9
+ @docs = rc['emit'] || []
10
+ end
11
+
12
+ def emit
13
+ @docs
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'kustomize/transformer_plugin'
2
+
3
+ module CovalentKustomizer; end
4
+
5
+ class CovalentKustomizer::TestTransformer < Kustomize::TransformerPlugin
6
+ match_on api_version: 'kustomizer.covalenthq.com/v1'
7
+
8
+ def initialize(rc)
9
+ @insertions = rc['insert'] || {}
10
+ end
11
+
12
+ def rewrite(rc)
13
+ rc.merge(@insertions)
14
+ end
15
+ end
@@ -14,4 +14,8 @@ class Kustomize::Emitter
14
14
  def to_yaml_stream
15
15
  self.emit.map(&:to_yaml).join("")
16
16
  end
17
+
18
+ def inspect
19
+ "#<#{self.class}>"
20
+ end
17
21
  end
@@ -1,12 +1,15 @@
1
1
  require 'kustomize/emitter/document_emitter'
2
2
  require 'kustomize/emitter/file_emitter'
3
3
  require 'kustomize/emitter/directory_emitter'
4
- require 'kustomize/emitter/plugin_emitter'
4
+ require 'kustomize/emitter/generator_plugins_emitter'
5
5
 
6
6
  require 'kustomize/transform/json_6902_patch_transform'
7
7
  require 'kustomize/transform/image_transform'
8
8
  require 'kustomize/transform/namespace_transform'
9
- require 'kustomize/transform/name_digest_autosuffix_transform'
9
+ require 'kustomize/transform/fingerprint_suffix_transform'
10
+ require 'kustomize/transform/ref_fixup_transform'
11
+ require 'kustomize/transform/purge_internal_annotations_transform'
12
+ require 'kustomize/transform/transformer_plugins_transform'
10
13
 
11
14
  class Kustomize::Emitter::DocumentEmitter::KustomizationDocumentEmitter < Kustomize::Emitter::DocumentEmitter
12
15
  def source_directory
@@ -20,18 +23,24 @@ class Kustomize::Emitter::DocumentEmitter::KustomizationDocumentEmitter < Kustom
20
23
  (@doc['bases'] || []) +
21
24
  (@doc['resources'] || [])
22
25
 
23
- gen_pathspecs =
26
+ gen_plugin_pathspecs =
24
27
  (@doc['generators'] || [])
25
28
 
26
29
  input_emitters = rc_pathspecs.map do |rel_path|
27
30
  build_input_emitter(rel_path)
28
31
  end
29
32
 
30
- input_emitters += gen_pathspecs.map do |rel_path|
31
- Kustomize::Emitter::PluginEmitter.new(
32
- build_input_emitter(rel_path),
33
+ gen_plugin_rc_emitters = gen_plugin_pathspecs.map do |rel_path|
34
+ build_input_emitter(rel_path)
35
+ end
36
+
37
+ unless gen_plugin_rc_emitters.empty?
38
+ gen_plugins_emitter = Kustomize::Emitter::GeneratorPluginsEmitter.new(
39
+ gen_plugin_rc_emitters,
33
40
  session: @session
34
41
  )
42
+
43
+ input_emitters.push(gen_plugins_emitter)
35
44
  end
36
45
 
37
46
  @input_emitters = input_emitters
@@ -66,6 +75,24 @@ class Kustomize::Emitter::DocumentEmitter::KustomizationDocumentEmitter < Kustom
66
75
  end
67
76
  end
68
77
 
78
+ def transformer_plugin_transforms
79
+ xformer_plugin_rc_emitters =
80
+ (@doc['transformers'] || []).map do |rel_path|
81
+ build_input_emitter(rel_path)
82
+ end
83
+
84
+ if xformer_plugin_rc_emitters.length > 0
85
+ xform = Kustomize::Transform::TransformerPluginsTransform.create(
86
+ xformer_plugin_rc_emitters,
87
+ session: @session
88
+ )
89
+
90
+ [xform]
91
+ else
92
+ []
93
+ end
94
+ end
95
+
69
96
  def namespace_transforms
70
97
  if new_ns = @doc['namespace']
71
98
  [Kustomize::Transform::NamespaceTransform.create(new_ns)]
@@ -74,24 +101,20 @@ class Kustomize::Emitter::DocumentEmitter::KustomizationDocumentEmitter < Kustom
74
101
  end
75
102
  end
76
103
 
77
- def name_digest_autosuffix_transforms
78
- [Kustomize::Transform::NameDigestAutosuffixTransform.create(self)]
79
- end
80
-
81
104
  def transforms
82
105
  return @transforms if @transforms
83
106
 
84
107
  @transforms = [
85
108
  self.namespace_transforms,
86
109
  self.image_transforms,
87
- self.name_digest_autosuffix_transforms,
88
- self.json_6902_patch_transforms
110
+ self.json_6902_patch_transforms,
111
+ self.transformer_plugin_transforms
89
112
  ].flatten
90
113
  end
91
114
 
92
115
  def emit
93
- self.input_resources.map do |rc|
94
- self.transforms.inject(rc){ |doc, xform| xform.apply(doc) }
116
+ self.transforms.inject(self.input_resources) do |rcs, xform|
117
+ xform.rewrite_all(rcs)
95
118
  end
96
119
  end
97
120
  end
@@ -0,0 +1,31 @@
1
+ require 'kustomize/emitter'
2
+
3
+ require 'kustomize/transform/fingerprint_suffix_transform'
4
+ require 'kustomize/transform/ref_fixup_transform'
5
+ require 'kustomize/transform/purge_internal_annotations_transform'
6
+
7
+ class Kustomize::Emitter::FinalizerEmitter < Kustomize::Emitter
8
+ def initialize(input_emitter)
9
+ @input_emitter = input_emitter
10
+ end
11
+
12
+ def input_emitters
13
+ [@input_emitter]
14
+ end
15
+
16
+ def transforms
17
+ return @transforms if @transforms
18
+
19
+ @transforms = [
20
+ Kustomize::Transform::FingerprintSuffixTransform.instance,
21
+ Kustomize::Transform::RefFixupTransform.instance,
22
+ Kustomize::Transform::PurgeInternalAnnotationsTransform.instance
23
+ ].flatten
24
+ end
25
+
26
+ def emit
27
+ self.transforms.inject(self.input_resources) do |rcs, xform|
28
+ xform.rewrite_all(rcs)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ require 'kustomize/emitter'
2
+
3
+ class Kustomize::Emitter::GeneratorPluginsEmitter < Kustomize::Emitter
4
+ def initialize(plugin_rc_emitters, session:)
5
+ @session = session
6
+ @plugin_rc_emitters = plugin_rc_emitters
7
+ end
8
+
9
+ def plugin_rcs
10
+ @plugin_rc_emitters.flat_map(&:emit)
11
+ end
12
+
13
+ def plugin_instances
14
+ return @plugin_instances if @plugin_instances
15
+
16
+ @plugin_instances =
17
+ self.plugin_rcs.map do |rc|
18
+ plugin_klass = @session.plugin_manager.get(api_version: rc['apiVersion'], kind: rc['kind'])
19
+ plugin_klass.create(rc, session: @session)
20
+ end
21
+ end
22
+
23
+ def emit
24
+ self.plugin_instances.flat_map(&:emit)
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ require 'kustomize/plugin'
2
+
3
+ class Kustomize::GeneratorPlugin < Kustomize::Plugin
4
+ def self.inherited(subklass)
5
+ reg = Kustomize::PluginRegistry.instance
6
+ reg.probe_queue.push(subklass)
7
+ end
8
+
9
+ def emit
10
+ []
11
+ end
12
+
13
+ def to_yaml_stream
14
+ self.emit.map(&:to_yaml).join("")
15
+ end
16
+ end
@@ -1,11 +1,36 @@
1
- module Kustomize; end
1
+ require 'pathname'
2
2
 
3
3
  class Kustomize::Plugin
4
- def initialize(rc)
5
- @rc = rc
4
+ def self.create(rc, session:)
5
+ inst = new(rc)
6
+ inst.session = session
7
+ inst
6
8
  end
7
9
 
8
- def emit
9
- raise NotImplementedError, "Kustomize plugins must implement #emit"
10
+ class << self
11
+ private :new
12
+ end
13
+
14
+ attr_accessor :session
15
+
16
+ def self.match_on(kind: nil, api_version: nil)
17
+ if kind
18
+ @kustomize_plugin_match_kind = kind
19
+ end
20
+
21
+ if api_version
22
+ @kustomize_plugin_match_api_version = api_version
23
+ end
24
+ end
25
+
26
+ def self.kustomize_plugin_match_kind
27
+ return @kustomize_plugin_match_kind if @kustomize_plugin_match_kind
28
+ self.name.split('::').last
29
+ end
30
+
31
+ def self.kustomize_plugin_match_api_version
32
+ return @kustomize_plugin_match_api_version if @kustomize_plugin_match_api_version
33
+ api_dir = Pathname.new(__FILE__).parent
34
+ api_dir.relative_path_from(api_dir.parent.parent).to_s
10
35
  end
11
36
  end
@@ -1,31 +1,46 @@
1
- require 'kustomize/plugin'
1
+ require 'singleton'
2
+
3
+ require 'kustomize/plugin_registry'
2
4
 
3
5
  class Kustomize::PluginManager
4
6
  def initialize(session:)
5
7
  @session = session
6
- @instances = {}
8
+ @registry = Kustomize::PluginRegistry.instance
7
9
  end
8
10
 
9
- def get(api_version, kind)
10
- cache_key = [api_version, kind]
11
- cached_inst = @instances[cache_key]
12
- return cached_inst if cached_inst
11
+ def get(api_version:, kind:)
12
+ plugin_klass = @registry.get(api_version: api_version, kind: kind)
13
+ return plugin_klass if plugin_klass
13
14
 
14
- @instances[cache_key] = self.load(api_version, kind)
15
+ try_loading(api_version, kind)
15
16
  end
16
17
 
17
- def load(api_version, kind)
18
- @session.load_paths
19
- .each{ |prefix| puts(prefix / api_version / "#{kind.downcase}.rb") }
18
+ private
19
+ def try_loading(api_version, kind)
20
+ rel_load_path = Pathname.new(api_version) / "#{underscore(kind)}.rb"
20
21
 
21
- load_path =
22
- @session.load_paths
23
- .map{ |prefix| prefix / api_version / "#{kind.downcase}.rb" }
22
+ abs_load_path =
23
+ @session.effective_load_paths
24
+ .map{ |prefix| prefix / rel_load_path }
24
25
  .find{ |f| f.file? }
25
26
 
26
- raise ArgumentError, "unknown kustomize plugin #{kind}" unless load_path
27
+ raise LoadError, "could not find kustomize plugin to load" unless abs_load_path
28
+
29
+ Kernel.require(abs_load_path)
30
+
31
+ plugin_klass = @registry.get(api_version: api_version, kind: kind)
32
+
33
+ raise LoadError, "#{abs_load_path} did not define expected plugin" unless plugin_klass
34
+
35
+ plugin_klass
36
+ end
27
37
 
28
- Class.new(Kustomize::Plugin)
29
- .tap{ |klass| klass.class_eval(load_path.read) }
38
+ private
39
+ def underscore(str)
40
+ str
41
+ .gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
42
+ .gsub(/([a-z\d])([A-Z])/,'\1_\2')
43
+ .tr("-", "_")
44
+ .downcase
30
45
  end
31
46
  end
@@ -0,0 +1,39 @@
1
+ require 'singleton'
2
+
3
+ module Kustomize; end
4
+
5
+ class Kustomize::PluginRegistry
6
+ include Singleton
7
+
8
+ def initialize
9
+ @klasses = {}
10
+ @probe_queue = []
11
+ end
12
+
13
+ attr_reader :probe_queue
14
+
15
+ def get(api_version:, kind:)
16
+ drain_probe_queue!
17
+
18
+ rc_target_id = make_rc_target_id(api_version, kind)
19
+ @klasses[rc_target_id]
20
+ end
21
+
22
+ private
23
+ def drain_probe_queue!
24
+ return if @probe_queue.empty?
25
+
26
+ while plugin_klass = @probe_queue.shift
27
+ rc_target_id = make_rc_target_id(
28
+ plugin_klass.kustomize_plugin_match_api_version,
29
+ plugin_klass.kustomize_plugin_match_kind
30
+ )
31
+ @klasses[rc_target_id] = plugin_klass
32
+ end
33
+ end
34
+
35
+ private
36
+ def make_rc_target_id(api_version, kind)
37
+ [api_version, kind].join('/').to_s.intern
38
+ end
39
+ end
@@ -3,15 +3,18 @@ module Kustomize; end
3
3
  require 'kustomize/plugin_manager'
4
4
 
5
5
  class Kustomize::Session
6
- def plugin_manager
7
- return @plugin_manager if @plugin_manager
6
+ def initialize(load_paths: [])
7
+ @load_paths = load_paths
8
8
  @plugin_manager = Kustomize::PluginManager.new(session: self)
9
9
  end
10
10
 
11
- def load_paths
12
- return @load_paths if @load_paths
13
- @load_paths = [
14
- Pathname.new(__FILE__).expand_path.parent / 'plugin'
15
- ]
11
+ attr_reader :plugin_manager
12
+
13
+ def builtin_load_paths
14
+ [Pathname.new(__FILE__).expand_path.parent / 'builtin_plugins']
15
+ end
16
+
17
+ def effective_load_paths
18
+ @load_paths + self.builtin_load_paths
16
19
  end
17
20
  end
@@ -37,8 +37,8 @@ class Kustomize::TargetSpec
37
37
  end
38
38
 
39
39
  return false if @match_kind and (rc['kind'] != @match_kind)
40
- return false if @match_name and get_name(resource_doc) != @match_name
41
- return false if @match_namespace and get_namespace(resource_doc) != @match_namespace
40
+ return false if @match_name and get_name(rc) != @match_name
41
+ return false if @match_namespace and get_namespace(rc) != @match_namespace
42
42
 
43
43
  true
44
44
  end
@@ -9,7 +9,15 @@ class Kustomize::Transform
9
9
  private :new
10
10
  end
11
11
 
12
- def apply(rc)
12
+ def rewrite_all(rcs)
13
+ rcs.map{ |rc| rewrite(rc) }
14
+ end
15
+
16
+ def rewrite(rc)
13
17
  rc
14
18
  end
19
+
20
+ def inspect
21
+ "#<#{self.class}>"
22
+ end
15
23
  end
@@ -0,0 +1,57 @@
1
+ require 'json'
2
+ require 'digest'
3
+ require 'set'
4
+ require 'singleton'
5
+
6
+ require 'accessory'
7
+ require 'digest/base32'
8
+
9
+ require 'kustomize/transform'
10
+
11
+ class Kustomize::Transform::FingerprintSuffixTransform < Kustomize::Transform
12
+ include Accessory
13
+ include Singleton
14
+
15
+ SUFFIX_JOINER = "-"
16
+
17
+ APPLICABLE_KINDS = Set[
18
+ 'Secret',
19
+ 'SealedSecret',
20
+ 'ConfigMap'
21
+ ]
22
+
23
+ NAME_LENS = Lens["metadata", "name"]
24
+
25
+ CONTENT_LENS_BY_KIND = {
26
+ "Secret" => Lens["data"],
27
+ "ConfigMap" => Lens["data"],
28
+ "SealedSecret" => Lens["spec", "encryptedData"]
29
+ }
30
+
31
+ FINGERPRINT_LENS = Lens['metadata', 'annotations', 'kustomizer.covalenthq.com/effective-fingerprint']
32
+
33
+ def rewrite(rc)
34
+ rc_kind = rc['kind']
35
+ return rc unless APPLICABLE_KINDS.member?(rc_kind)
36
+
37
+ FINGERPRINT_LENS.update_in(rc) do |orig_value|
38
+ next(:keep) if orig_value
39
+
40
+ content_part = CONTENT_LENS_BY_KIND[rc_kind].get_in(rc)
41
+ content_ser = content_part.to_json
42
+ fingerprint = Digest::SHA256.base32digest(content_ser, :zbase32)[0, 6]
43
+
44
+ [:set, fingerprint]
45
+ end
46
+
47
+ base_name = NAME_LENS.get_in(rc)
48
+ fingerprint = FINGERPRINT_LENS.get_in(rc)
49
+
50
+ NAME_LENS.update_in(rc) do |base_name|
51
+ suffixed_name = [base_name, fingerprint].join(SUFFIX_JOINER)
52
+ [:set, suffixed_name]
53
+ end
54
+
55
+ rc
56
+ end
57
+ end
@@ -27,7 +27,7 @@ class Kustomize::Transform::ImageTransform < Kustomize::Transform
27
27
  "Deployment" => Lens["spec", "template", "spec", "containers", Access.all, "image"]
28
28
  }
29
29
 
30
- def apply(rc_doc)
30
+ def rewrite(rc_doc)
31
31
  lens = LENS_BY_KIND[rc_doc['kind']]
32
32
  return rc_doc unless lens
33
33
 
@@ -44,7 +44,7 @@ class Kustomize::Transform::Json6902PatchTransform < Kustomize::Transform
44
44
  attr_reader :target
45
45
  attr_reader :patches
46
46
 
47
- def apply(resource_doc)
47
+ def rewrite(resource_doc)
48
48
  if @target.match?(resource_doc)
49
49
  @patches.inject(resource_doc){ |doc, patch| patch.apply(doc) }
50
50
  else
@@ -29,7 +29,7 @@ class Kustomize::Transform::NamespaceTransform < Kustomize::Transform
29
29
  ]
30
30
  }
31
31
 
32
- def apply(rc_doc)
32
+ def rewrite(rc_doc)
33
33
  rc_kind = rc_doc['kind']
34
34
  use_lenses = []
35
35
 
@@ -0,0 +1,31 @@
1
+ require 'singleton'
2
+
3
+ require 'accessory'
4
+
5
+ require 'kustomize/transform'
6
+
7
+ class Kustomize::Transform::PurgeInternalAnnotationsTransform < Kustomize::Transform
8
+ include Accessory
9
+ include Singleton
10
+
11
+ ANNOTS_LENS = Lens['metadata', 'annotations']
12
+
13
+ INTERNAL_ANNOT_PATTERN = /^kustomizer\.covalenthq\.com\//
14
+
15
+ def rewrite(rc)
16
+ ANNOTS_LENS.update_in(rc) do |orig_annots|
17
+ next(:keep) unless orig_annots and orig_annots.length.nonzero?
18
+
19
+ new_annots =
20
+ orig_annots.reject{ |k, v| INTERNAL_ANNOT_PATTERN.match?(k) }
21
+
22
+ if new_annots.length == orig_annots.length
23
+ :keep
24
+ elsif new_annots.empty?
25
+ :pop
26
+ else
27
+ [:set, new_annots]
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,59 @@
1
+ require 'singleton'
2
+
3
+ require 'accessory'
4
+
5
+ require 'kustomize/transform'
6
+
7
+ class Kustomize::Transform::RefFixupTransform < Kustomize::Transform
8
+ include Accessory
9
+ include Singleton
10
+
11
+ SUFFIX_JOINER = "-"
12
+
13
+ NAME_LENS = Lens["metadata", "name"]
14
+
15
+ FINGERPRINT_LENS = Lens['metadata', 'annotations', 'kustomizer.covalenthq.com/effective-fingerprint']
16
+
17
+ POD_TEMPLATE_LENSES = [
18
+ Lens["spec", "template", "spec", "containers", Access.all, "envFrom", Access.all, "configMapRef", "name"],
19
+ Lens["spec", "template", "spec", "containers", Access.all, "env", Access.all, "valueFrom", "configMapKeyRef", "name"],
20
+ Lens["spec", "template", "spec", "volumes", Access.all, "configMap", "name"],
21
+
22
+ Lens["spec", "template", "spec", "containers", Access.all, "env", Access.all, "valueFrom", "secretKeyRef", "name"],
23
+ Lens["spec", "template", "spec", "volumes", Access.all, "secret", "name"],
24
+ Lens["spec", "template", "spec", "volumes", Access.all, "secret", "secretName"]
25
+ ]
26
+
27
+ KEY_REF_LENSES_BY_KIND = {
28
+ "Deployment" => POD_TEMPLATE_LENSES,
29
+ "StatefulSet" => POD_TEMPLATE_LENSES,
30
+ "DaemonSet" => POD_TEMPLATE_LENSES
31
+ }
32
+
33
+ def rewrite_all(rcs)
34
+ ref_fixups =
35
+ rcs.flat_map do |rc|
36
+ fingerprint = FINGERPRINT_LENS.get_in(rc)
37
+ next([]) unless fingerprint
38
+
39
+ suffixed_name = NAME_LENS.get_in(rc)
40
+ base_name = suffixed_name.gsub(/-#{fingerprint}$/, '')
41
+ [[base_name, suffixed_name]]
42
+ end.to_h
43
+
44
+ rcs.map do |rc|
45
+ key_ref_lenses = KEY_REF_LENSES_BY_KIND[rc['kind']]
46
+ next(rc) unless key_ref_lenses
47
+
48
+ key_ref_lenses.inject(rc) do |doc, lens|
49
+ lens.update_in(doc) do |base_name|
50
+ if suffixed_name = ref_fixups[base_name]
51
+ [:set, suffixed_name]
52
+ else
53
+ :keep
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,28 @@
1
+ require 'kustomize/transform'
2
+
3
+ class Kustomize::Transform::TransformerPluginsTransform < Kustomize::Transform
4
+ def initialize(plugin_rc_emitters, session:)
5
+ @session = session
6
+ @plugin_rc_emitters = plugin_rc_emitters
7
+ end
8
+
9
+ def plugin_rcs
10
+ @plugin_rc_emitters.flat_map(&:emit)
11
+ end
12
+
13
+ def plugin_instances
14
+ return @plugin_instances if @plugin_instances
15
+
16
+ @plugin_instances =
17
+ self.plugin_rcs.map do |rc|
18
+ plugin_klass = @session.plugin_manager.get(api_version: rc['apiVersion'], kind: rc['kind'])
19
+ plugin_klass.create(rc, session: @session)
20
+ end
21
+ end
22
+
23
+ def rewrite_all(rcs)
24
+ self.plugin_instances.inject(rcs) do |docs, plugin_inst|
25
+ plugin_inst.rewrite_all(rcs)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ require 'kustomize/plugin'
2
+
3
+ class Kustomize::TransformerPlugin < Kustomize::Plugin
4
+ def self.inherited(subklass)
5
+ reg = Kustomize::PluginRegistry.instance
6
+ reg.probe_queue.push(subklass)
7
+ end
8
+
9
+ def rewrite_all(rcs)
10
+ rcs.map{ |rc| rewrite(rc) }
11
+ end
12
+
13
+ def rewrite(rc)
14
+ rc
15
+ end
16
+ end
@@ -1,3 +1,3 @@
1
1
  module Kustomize
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.5"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kustomizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Levi Aul
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-16 00:00:00.000000000 Z
11
+ date: 2021-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: accessory
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.1.9
27
+ - !ruby/object:Gem::Dependency
28
+ name: base32-multi
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  description: A pure-ruby implementation of the Kubernetes 'kustomize' command.
28
42
  email:
29
43
  - levi@leviaul.com
@@ -42,12 +56,16 @@ files:
42
56
  - bin/setup
43
57
  - kustomizer.gemspec
44
58
  - lib/kustomize.rb
59
+ - lib/kustomize/builtin_plugins/kustomizer.covalenthq.com/v1/test_generator.rb
60
+ - lib/kustomize/builtin_plugins/kustomizer.covalenthq.com/v1/test_transformer.rb
45
61
  - lib/kustomize/emitter.rb
46
62
  - lib/kustomize/emitter/directory_emitter.rb
47
63
  - lib/kustomize/emitter/document_emitter.rb
48
64
  - lib/kustomize/emitter/document_emitter/kustomization_document_emitter.rb
49
65
  - lib/kustomize/emitter/file_emitter.rb
50
- - lib/kustomize/emitter/plugin_emitter.rb
66
+ - lib/kustomize/emitter/finalizer_emitter.rb
67
+ - lib/kustomize/emitter/generator_plugins_emitter.rb
68
+ - lib/kustomize/generator_plugin.rb
51
69
  - lib/kustomize/json_6902_patch.rb
52
70
  - lib/kustomize/json_6902_patch/add_op.rb
53
71
  - lib/kustomize/json_6902_patch/gsub_op.rb
@@ -56,13 +74,18 @@ files:
56
74
  - lib/kustomize/pathname_refinements.rb
57
75
  - lib/kustomize/plugin.rb
58
76
  - lib/kustomize/plugin_manager.rb
77
+ - lib/kustomize/plugin_registry.rb
59
78
  - lib/kustomize/session.rb
60
79
  - lib/kustomize/target_spec.rb
61
80
  - lib/kustomize/transform.rb
81
+ - lib/kustomize/transform/fingerprint_suffix_transform.rb
62
82
  - lib/kustomize/transform/image_transform.rb
63
83
  - lib/kustomize/transform/json_6902_patch_transform.rb
64
- - lib/kustomize/transform/name_digest_autosuffix_transform.rb
65
84
  - lib/kustomize/transform/namespace_transform.rb
85
+ - lib/kustomize/transform/purge_internal_annotations_transform.rb
86
+ - lib/kustomize/transform/ref_fixup_transform.rb
87
+ - lib/kustomize/transform/transformer_plugins_transform.rb
88
+ - lib/kustomize/transformer_plugin.rb
66
89
  - lib/kustomize/version.rb
67
90
  homepage: https://github.com/tsutsu/kustomize
68
91
  licenses:
@@ -1,30 +0,0 @@
1
- require 'kustomize/emitter'
2
-
3
- class Kustomize::Emitter::PluginEmitter < Kustomize::Emitter
4
- def initialize(input_emitter, session:)
5
- @session = session
6
- @input_emitter = input_emitter
7
- end
8
-
9
- def source_directory
10
- @source_path.parent
11
- end
12
-
13
- def input_emitters
14
- [@input_emitter]
15
- end
16
-
17
- def plugin_instances
18
- return @plugin_instances if @plugin_instances
19
-
20
- @plugin_instances =
21
- self.input_resources.map do |rc|
22
- plugin_klass = @session.plugin_manager.get(rc['apiVersion'], rc['kind'])
23
- plugin_klass.new(rc)
24
- end
25
- end
26
-
27
- def emit
28
- self.plugin_instances.flat_map(&:emit)
29
- end
30
- end
@@ -1,83 +0,0 @@
1
- require 'json'
2
- require 'digest'
3
- require 'set'
4
-
5
- require 'accessory'
6
- require 'digest/base32'
7
-
8
- require 'kustomize/transform'
9
- require 'kustomize/emitter/document_emitter/kustomization_document_emitter'
10
-
11
- class Kustomize::Transform::NameDigestAutosuffixTransform < Kustomize::Transform
12
- include Accessory
13
-
14
- SUFFIX_JOINER = "-"
15
-
16
- def self.create(kustomize_doc)
17
- raise ArgumentError unless kustomize_doc.kind_of?(Kustomize::Emitter::DocumentEmitter::KustomizationDocumentEmitter)
18
- self.new(kustomize_doc)
19
- end
20
-
21
- def initialize(kustomize_doc)
22
- @kustomize_doc = kustomize_doc
23
- end
24
-
25
- SECRET_KINDS = Set[
26
- 'Secret',
27
- 'SealedSecret'
28
- ]
29
-
30
- def suffixes
31
- return @suffixes if @suffixes
32
-
33
- secret_docs =
34
- @kustomize_doc.input_resources
35
- .filter{ |rc| SECRET_KINDS.member?(rc['kind']) }
36
-
37
- @suffixes =
38
- secret_docs.map do |rc|
39
- rc_kind = rc['kind']
40
-
41
- secret_name = NAME_LENSES_BY_KIND[rc_kind].first.get_in(rc)
42
- secret_content = CONTENT_LENSES_BY_KIND[rc_kind].get_in(rc)
43
- content_hash_suffix = Digest::SHA256.base32digest(secret_content.to_json, :zbase32)[0, 8]
44
-
45
- [secret_name, content_hash_suffix]
46
- end.to_h
47
- end
48
-
49
- CONTENT_LENSES_BY_KIND = {
50
- "Secret" => Lens["data"],
51
- "SealedSecret" => Lens["spec", "encryptedData"]
52
- }
53
-
54
- NAME_LENSES_BY_KIND = {
55
- "Deployment" => [
56
- Lens["spec", "template", "spec", "containers", Access.all, "env", Access.all, "valueFrom", "secretKeyRef", "name"]
57
- ],
58
-
59
- "Secret" => [
60
- Lens["metadata", "name"]
61
- ],
62
-
63
- "SealedSecret" => [
64
- Lens["spec", "template", "metadata", "name"]
65
- ]
66
- }
67
-
68
- def apply(rc_doc)
69
- name_lenses = NAME_LENSES_BY_KIND[rc_doc['kind']]
70
- return rc_doc unless name_lenses
71
-
72
- name_lenses.inject(rc_doc) do |doc, lens|
73
- lens.update_in(doc) do |orig_name|
74
- if self.suffixes.has_key?(orig_name)
75
- new_name = [orig_name, self.suffixes[orig_name]].join('-')
76
- [:set, new_name]
77
- else
78
- :keep
79
- end
80
- end
81
- end
82
- end
83
- end