kustomizer 0.1.0 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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