@@ -25,15 +25,25 @@ import (
25
25
"runtime"
26
26
"sync"
27
27
28
+ "github.com/pkg/errors"
29
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
30
+ yamlutil "k8s.io/apimachinery/pkg/util/yaml"
28
31
"k8s.io/cli-runtime/pkg/kustomize"
32
+ "sigs.k8s.io/kustomize/pkg/constants"
29
33
"sigs.k8s.io/kustomize/pkg/fs"
34
+ "sigs.k8s.io/kustomize/pkg/ifc"
30
35
"sigs.k8s.io/kustomize/pkg/loader"
36
+ "sigs.k8s.io/kustomize/pkg/patch"
37
+ "sigs.k8s.io/kustomize/pkg/types"
38
+ "sigs.k8s.io/yaml"
31
39
)
32
40
33
41
// Manager define a manager that allow access to kustomize capabilities
34
42
type Manager struct {
35
- kustomizeDir string
36
- us UnstructuredSlice
43
+ kustomizeDir string
44
+ kustomizationFile * types.Kustomization
45
+ strategicMergePatches strategicMergeSlice
46
+ json6902Patches json6902Slice
37
47
}
38
48
39
49
var (
42
52
)
43
53
44
54
// GetManager return the KustomizeManager singleton instance
55
+ // NB. this is done at singleton instance level because kubeadm has a unique pool
56
+ // of patches that are applied to different content, at different time
45
57
func GetManager (kustomizeDir string ) (* Manager , error ) {
46
58
lock .Lock ()
47
59
defer lock .Unlock ()
@@ -52,91 +64,135 @@ func GetManager(kustomizeDir string) (*Manager, error) {
52
64
kustomizeDir : kustomizeDir ,
53
65
}
54
66
55
- // loads the UnstructuredSlice with all the patches into the Manager
56
- // NB. this is done at singleton instance level because kubeadm has a unique pool
57
- // of patches that are applied to different content, at different time
58
- if err := km .getUnstructuredSlice (); err != nil {
67
+ // Create a loader that mimics the behavior of kubectl kustomize, including support for reading from
68
+ // a local folder or git repository like [email protected] :someOrg/someRepo.git or https://github.com/someOrg/someRepo?ref=someHash
69
+ // in order to do so you must use ldr.Root() instead of km.kustomizeDir and ldr.Load instead of other ways to read files
70
+ fSys := fs .MakeRealFS ()
71
+ ldr , err := loader .NewLoader (km .kustomizeDir , fSys )
72
+ if err != nil {
73
+ return nil , err
74
+ }
75
+ defer ldr .Cleanup ()
76
+
77
+ // read the Kustomization file and all the patches it is
78
+ // referencing (either stategicMerge or json6902 patches)
79
+ if err := km .loadFromKustomizationFile (ldr ); err != nil {
59
80
return nil , err
60
81
}
61
82
83
+ // if a Kustomization file was not found, kubeadm creates
84
+ // one using all the patches in the folder; however in this
85
+ // case only stategicMerge patches are supported
86
+ if km .kustomizationFile == nil {
87
+ km .kustomizationFile = & types.Kustomization {}
88
+ if err := km .loadFromFolder (ldr ); err != nil {
89
+ return nil , err
90
+ }
91
+ }
92
+
62
93
instances [kustomizeDir ] = km
63
94
}
64
95
65
96
return instances [kustomizeDir ], nil
66
97
}
67
98
68
- // getUnstructuredSlice returns a UnstructuredSlice with all the patches.
69
- func (km * Manager ) getUnstructuredSlice () error {
70
- // kubeadm does not require a kustomization.yaml file listing all the resources/patches, so it is necessary
71
- // to rebuild the list of patches manually
72
- // TODO: make this git friendly - currently this works only for patches in local folders -
73
- files , err := ioutil .ReadDir (km .kustomizeDir )
74
- if err != nil {
75
- return err
99
+ // loadFromKustomizationFile reads a Kustomization file and all the patches it is
100
+ // referencing (either stategicMerge or json6902 patches)
101
+ func (km * Manager ) loadFromKustomizationFile (ldr ifc.Loader ) error {
102
+ // Kustomize support different KustomizationFileNames, so we try to read all
103
+ var content []byte
104
+ match := 0
105
+ for _ , kf := range constants .KustomizationFileNames {
106
+ c , err := ldr .Load (kf )
107
+ if err == nil {
108
+ match ++
109
+ content = c
110
+ }
76
111
}
77
112
78
- var paths = []string {}
79
- for _ , file := range files {
80
- if file .IsDir () {
81
- continue
113
+ // if no kustomization file is found return
114
+ if match == 0 {
115
+ return nil
116
+ }
117
+
118
+ // if more that one kustomization file is found, return error
119
+ if match > 1 {
120
+ return errors .Errorf ("Found multiple kustomization files under: %s\n " , ldr .Root ())
121
+ }
122
+
123
+ // Decode the kustomization file
124
+ decoder := yamlutil .NewYAMLOrJSONDecoder (bytes .NewReader (content ), 1024 )
125
+ var k = & types.Kustomization {}
126
+ if err := decoder .Decode (k ); err != nil {
127
+ return errors .Wrap (err , "Error decoding kustomization file" )
128
+ }
129
+ km .kustomizationFile = k
130
+
131
+ // gets all the strategic merge patches
132
+ for _ , f := range km .kustomizationFile .PatchesStrategicMerge {
133
+ smp , err := newStrategicMergeSliceFromFile (ldr , string (f ))
134
+ if err != nil {
135
+ return err
82
136
}
83
- paths = append (paths , file . Name () )
137
+ km . strategicMergePatches = append (km . strategicMergePatches , smp ... )
84
138
}
85
139
86
- // Create a loader that mimics the behavior of kubectl kustomize, including support for reading from
87
- // a local git repository like [email protected] :someOrg/someRepo.git or https://github.com/someOrg/someRepo?ref=someHash
88
- fSys := fs .MakeRealFS ()
89
- ldr , err := loader .NewLoader (km .kustomizeDir , fSys )
90
- if err != nil {
91
- return err
140
+ // gets all the json6902 patches
141
+ for _ , f := range km .kustomizationFile .PatchesJson6902 {
142
+ jp , err := newJSON6902FromFile (f , ldr , f .Path )
143
+ if err != nil {
144
+ return err
145
+ }
146
+ km .json6902Patches = append (km .json6902Patches , jp )
92
147
}
93
- defer ldr .Cleanup ()
94
148
95
- // read all the kustomizations and build the UnstructuredSlice
96
- us , err := NewUnstructuredSliceFromFiles (ldr , paths )
149
+ return nil
150
+ }
151
+
152
+ // loadFromFolder returns all the stategicMerge patches in a folder
153
+ func (km * Manager ) loadFromFolder (ldr ifc.Loader ) error {
154
+ files , err := ioutil .ReadDir (ldr .Root ())
97
155
if err != nil {
98
156
return err
99
157
}
158
+ for _ , fileInfo := range files {
159
+ if fileInfo .IsDir () {
160
+ continue
161
+ }
100
162
101
- km .us = us
163
+ smp , err := newStrategicMergeSliceFromFile (ldr , fileInfo .Name ())
164
+ if err != nil {
165
+ return err
166
+ }
167
+ km .strategicMergePatches = append (km .strategicMergePatches , smp ... )
168
+ }
102
169
return nil
103
170
}
104
171
105
172
// Kustomize apply a set of patches to a resource.
106
173
// Portions of the kustomize logic in this function are taken from the kubernetes-sigs/kind project
107
- func (km * Manager ) Kustomize (res []byte ) ([]byte , error ) {
108
- // create a loader that mimics the behavior of kubectl kustomize
109
- // and converts the resource into a UnstructuredSlice
110
- // Nb. in kubeadm we are controlling resource generation, and so we
111
- // we are expecting 1 object into each resource, eg. the static pod.
112
- // Nevertheless, this code is ready for more than one object per resource
113
- resList , err := NewUnstructuredSliceFromBytes (res )
114
- if err != nil {
174
+ func (km * Manager ) Kustomize (data []byte ) ([]byte , error ) {
175
+ // parse the resource to kustomize
176
+ decoder := yamlutil .NewYAMLOrJSONDecoder (bytes .NewReader (data ), 1024 )
177
+ var resource * unstructured.Unstructured
178
+ if err := decoder .Decode (& resource ); err != nil {
115
179
return nil , err
116
180
}
117
181
118
- // create a list of resource and corresponding patches
119
- var resources , patches UnstructuredSlice
120
- for _ , r := range resList {
121
- resources = append (resources , r )
122
-
123
- resourcePatches := km .us .FilterResource (r .GroupVersionKind (), r .GetNamespace (), r .GetName ())
124
-
125
- if len (resourcePatches ) > 0 {
126
- fmt .Printf ("[kustomize] Applying %d patches to %s Resource=%s/%s\n " , len (resourcePatches ), r .GroupVersionKind (), r .GetNamespace (), r .GetName ())
127
- patches = append (patches , resourcePatches ... )
128
- }
129
- }
182
+ // get patches corresponding to this resource
183
+ strategicMerge := km .strategicMergePatches .filterByResource (resource )
184
+ json6902 := km .json6902Patches .filterByResource (resource )
130
185
131
186
// if there are no patches, for the target resources, exit
132
- if len (patches ) == 0 {
133
- return res , nil
187
+ if len (strategicMerge ) + len ( json6902 ) == 0 {
188
+ return data , nil
134
189
}
135
190
191
+ fmt .Printf ("[kustomize] Applying %d patches to %s Resource=%s/%s\n " , len (strategicMerge )+ len (json6902 ), resource .GroupVersionKind (), resource .GetNamespace (), resource .GetName ())
192
+
136
193
// create an in memory fs to use for the kustomization
137
194
memFS := fs .MakeFakeFS ()
138
195
139
- var kustomization bytes.Buffer
140
196
fakeDir := "/"
141
197
// for Windows we need this to be a drive because kustomize uses filepath.Abs()
142
198
// which will add a drive letter if there is none. which drive letter is
@@ -145,33 +201,44 @@ func (km *Manager) Kustomize(res []byte) ([]byte, error) {
145
201
fakeDir = `C:\`
146
202
}
147
203
148
- // write resources and patches to the in memory fs, generate the kustomization.yaml
149
- // that ties everything together
150
- kustomization .WriteString ("resources:\n " )
151
- for i , r := range resources {
152
- b , err := r .MarshalJSON ()
204
+ // writes the resource to a file in the temp file system
205
+ b , err := yaml .Marshal (resource )
206
+ if err != nil {
207
+ return nil , err
208
+ }
209
+ name := "resource.yaml"
210
+ _ = memFS .WriteFile (filepath .Join (fakeDir , name ), b )
211
+
212
+ km .kustomizationFile .Resources = []string {name }
213
+
214
+ // writes strategic merge patches to files in the temp file system
215
+ km .kustomizationFile .PatchesStrategicMerge = []patch.StrategicMerge {}
216
+ for i , p := range strategicMerge {
217
+ b , err := yaml .Marshal (p )
153
218
if err != nil {
154
219
return nil , err
155
220
}
156
-
157
- name := fmt .Sprintf ("resource-%d.json" , i )
221
+ name := fmt .Sprintf ("patch-%d.yaml" , i )
158
222
_ = memFS .WriteFile (filepath .Join (fakeDir , name ), b )
159
- fmt .Fprintf (& kustomization , " - %s\n " , name )
223
+
224
+ km .kustomizationFile .PatchesStrategicMerge = append (km .kustomizationFile .PatchesStrategicMerge , patch .StrategicMerge (name ))
160
225
}
161
226
162
- kustomization .WriteString ("patches:\n " )
163
- for i , p := range patches {
164
- b , err := p .MarshalJSON ()
165
- if err != nil {
166
- return nil , err
167
- }
227
+ // writes json6902 patches to files in the temp file system
228
+ km .kustomizationFile .PatchesJson6902 = []patch.Json6902 {}
229
+ for i , p := range json6902 {
230
+ name := fmt .Sprintf ("patchjson-%d.yaml" , i )
231
+ _ = memFS .WriteFile (filepath .Join (fakeDir , name ), []byte (p .Patch ))
168
232
169
- name := fmt .Sprintf ("patch-%d.json" , i )
170
- _ = memFS .WriteFile (filepath .Join (fakeDir , name ), b )
171
- fmt .Fprintf (& kustomization , " - %s\n " , name )
233
+ km .kustomizationFile .PatchesJson6902 = append (km .kustomizationFile .PatchesJson6902 , patch.Json6902 {Target : p .Target , Path : name })
172
234
}
173
235
174
- memFS .WriteFile (filepath .Join (fakeDir , "kustomization.yaml" ), kustomization .Bytes ())
236
+ // writes the kustomization file to the temp file system
237
+ kbytes , err := yaml .Marshal (km .kustomizationFile )
238
+ if err != nil {
239
+ return nil , err
240
+ }
241
+ memFS .WriteFile (filepath .Join (fakeDir , "kustomization.yaml" ), kbytes )
175
242
176
243
// Finally customize the target resource
177
244
var out bytes.Buffer
0 commit comments