1 // Copyright 2019 The Kubernetes Authors. 2 // SPDX-License-Identifier: Apache-2.0 3 4 // Package resmap implements a map from ResId to Resource that 5 // tracks all resources in a kustomization. 6 package resmap 7 8 import ( 9 "sigs.k8s.io/kustomize/api/ifc" 10 "sigs.k8s.io/kustomize/api/resource" 11 "sigs.k8s.io/kustomize/api/types" 12 "sigs.k8s.io/kustomize/kyaml/kio" 13 "sigs.k8s.io/kustomize/kyaml/resid" 14 "sigs.k8s.io/kustomize/kyaml/yaml" 15 ) 16 17 // A Transformer modifies an instance of ResMap. 18 type Transformer interface { 19 // Transform modifies data in the argument, 20 // e.g. adding labels to resources that can be labelled. 21 Transform(m ResMap) error 22 } 23 24 // A TransformerWithProperties contains a Transformer and stores 25 // some of its properties 26 type TransformerWithProperties struct { 27 Transformer 28 Origin *resource.Origin 29 } 30 31 // A Generator creates an instance of ResMap. 32 type Generator interface { 33 Generate() (ResMap, error) 34 } 35 36 // A GeneratorWithProperties contains a Generator and stores 37 // some of its properties 38 type GeneratorWithProperties struct { 39 Generator 40 Origin *resource.Origin 41 } 42 43 // Something that's configurable accepts an 44 // instance of PluginHelpers and a raw config 45 // object (YAML in []byte form). 46 type Configurable interface { 47 Config(h *PluginHelpers, config []byte) error 48 } 49 50 // NewPluginHelpers makes an instance of PluginHelpers. 51 func NewPluginHelpers( 52 ldr ifc.Loader, v ifc.Validator, rf *Factory, 53 pc *types.PluginConfig) *PluginHelpers { 54 return &PluginHelpers{ldr: ldr, v: v, rf: rf, pc: pc} 55 } 56 57 // PluginHelpers holds things that any or all plugins might need. 58 // This should be available to each plugin, in addition to 59 // any plugin-specific configuration. 60 type PluginHelpers struct { 61 ldr ifc.Loader 62 v ifc.Validator 63 rf *Factory 64 pc *types.PluginConfig 65 } 66 67 func (c *PluginHelpers) GeneralConfig() *types.PluginConfig { 68 return c.pc 69 } 70 71 func (c *PluginHelpers) Loader() ifc.Loader { 72 return c.ldr 73 } 74 75 func (c *PluginHelpers) ResmapFactory() *Factory { 76 return c.rf 77 } 78 79 func (c *PluginHelpers) Validator() ifc.Validator { 80 return c.v 81 } 82 83 type GeneratorPlugin interface { 84 Generator 85 Configurable 86 } 87 88 type TransformerPlugin interface { 89 Transformer 90 Configurable 91 } 92 93 // ResMap is an interface describing operations on the 94 // core kustomize data structure, a list of Resources. 95 // 96 // Every Resource has two ResIds: OrgId and CurId. 97 // 98 // In a ResMap, no two resources may have the same CurId, 99 // but they may have the same OrgId. The latter can happen 100 // when mixing two or more different overlays apply different 101 // transformations to a common base. When looking for a 102 // resource to transform, try the OrgId first, and if this 103 // fails or finds too many, it might make sense to then try 104 // the CurrId. Depends on the situation. 105 // 106 // TODO: get rid of this interface (use bare resWrangler). 107 // There aren't multiple implementations any more. 108 type ResMap interface { 109 // Size reports the number of resources. 110 Size() int 111 112 // Resources provides a discardable slice 113 // of resource pointers, returned in the order 114 // as appended. 115 Resources() []*resource.Resource 116 117 // Append adds a Resource. Error on CurId collision. 118 // 119 // A class invariant of ResMap is that all of its 120 // resources must differ in their value of 121 // CurId(), aka current Id. The Id is the tuple 122 // of {namespace, group, version, kind, name} 123 // (see ResId). 124 // 125 // This invariant reflects the invariant of a 126 // kubernetes cluster, where if one tries to add 127 // a resource to the cluster whose Id matches 128 // that of a resource already in the cluster, 129 // only two outcomes are allowed. Either the 130 // incoming resource is _merged_ into the existing 131 // one, or the incoming resource is rejected. 132 // One cannot end up with two resources 133 // in the cluster with the same Id. 134 Append(*resource.Resource) error 135 136 // AppendAll appends another ResMap to self, 137 // failing on any CurId collision. 138 AppendAll(ResMap) error 139 140 // AbsorbAll appends, replaces or merges the contents 141 // of another ResMap into self, 142 // allowing and sometimes demanding ID collisions. 143 // A collision would be demanded, say, when a generated 144 // ConfigMap has the "replace" option in its generation 145 // instructions, meaning it _must_ replace 146 // something in the known set of resources. 147 // If a resource id for resource X is found to already 148 // be in self, then the behavior field for X must 149 // be BehaviorMerge or BehaviorReplace. If X is not in 150 // self, then its behavior _cannot_ be merge or replace. 151 AbsorbAll(ResMap) error 152 153 // AddOriginAnnotation will add the provided origin as 154 // an origin annotation to all resources in the ResMap, if 155 // the origin is not nil. 156 AddOriginAnnotation(origin *resource.Origin) error 157 158 // RemoveOriginAnnotation will remove the origin annotation 159 // from all resources in the ResMap 160 RemoveOriginAnnotations() error 161 162 // AddTransformerAnnotation will add the provided origin as 163 // an origin annotation if the resource doesn't have one; a 164 // transformer annotation otherwise; to all resources in 165 // ResMap 166 AddTransformerAnnotation(origin *resource.Origin) error 167 168 // RemoveTransformerAnnotation will remove the transformer annotation 169 // from all resources in the ResMap 170 RemoveTransformerAnnotations() error 171 172 // AnnotateAll annotates all resources in the ResMap with 173 // the provided key value pair. 174 AnnotateAll(key string, value string) error 175 176 // AsYaml returns the yaml form of resources. 177 AsYaml() ([]byte, error) 178 179 // GetByIndex returns a resource at the given index, 180 // nil if out of range. 181 GetByIndex(int) *resource.Resource 182 183 // GetIndexOfCurrentId returns the index of the resource 184 // with the given CurId. 185 // Returns error if there is more than one match. 186 // Returns (-1, nil) if there is no match. 187 GetIndexOfCurrentId(id resid.ResId) (int, error) 188 189 // GetMatchingResourcesByCurrentId returns the resources 190 // who's CurId is matched by the argument. 191 GetMatchingResourcesByCurrentId(matches IdMatcher) []*resource.Resource 192 193 // GetMatchingResourcesByAnyId returns the resources 194 // who's current or previous IDs is matched by the argument. 195 GetMatchingResourcesByAnyId(matches IdMatcher) []*resource.Resource 196 197 // GetByCurrentId is shorthand for calling 198 // GetMatchingResourcesByCurrentId with a matcher requiring 199 // an exact match, returning an error on multiple or no matches. 200 GetByCurrentId(resid.ResId) (*resource.Resource, error) 201 202 // GetById is shorthand for calling 203 // GetMatchingResourcesByAnyId with a matcher requiring 204 // an exact match, returning an error on multiple or no matches. 205 GetById(resid.ResId) (*resource.Resource, error) 206 207 // GroupedByCurrentNamespace returns a map of namespace 208 // to a slice of *Resource in that namespace. 209 // Cluster-scoped Resources are not included (see ClusterScoped). 210 // Resources with an empty namespace are placed 211 // in the resid.DefaultNamespace entry. 212 GroupedByCurrentNamespace() map[string][]*resource.Resource 213 214 // GroupedByOriginalNamespace performs as GroupByNamespace 215 // but use the original namespace instead of the current 216 // one to perform the grouping. 217 GroupedByOriginalNamespace() map[string][]*resource.Resource 218 219 // ClusterScoped returns a slice of resources that 220 // cannot be placed in a namespace, e.g. 221 // Node, ClusterRole, Namespace itself, etc. 222 ClusterScoped() []*resource.Resource 223 224 // AllIds returns all CurrentIds. 225 AllIds() []resid.ResId 226 227 // Replace replaces the resource with the matching CurId. 228 // Error if there's no match or more than one match. 229 // Returns the index where the replacement happened. 230 Replace(*resource.Resource) (int, error) 231 232 // Remove removes the resource whose CurId matches the argument. 233 // Error if not found. 234 Remove(resid.ResId) error 235 236 // Clear removes all resources and Ids. 237 Clear() 238 239 // DropEmpties drops empty resources from the ResMap. 240 DropEmpties() 241 242 // SubsetThatCouldBeReferencedByResource returns a ResMap subset 243 // of self with resources that could be referenced by the 244 // resource argument. 245 // This is a filter; it excludes things that cannot be 246 // referenced by the resource, e.g. objects in other 247 // namespaces. Cluster wide objects are never excluded. 248 SubsetThatCouldBeReferencedByResource(*resource.Resource) (ResMap, error) 249 250 // DeAnchor replaces YAML aliases with structured data copied from anchors. 251 // This cannot be undone; if desired, call DeepCopy first. 252 // Subsequent marshalling to YAML will no longer have anchor 253 // definitions ('&') or aliases ('*'). 254 // 255 // Anchors are not expected to work across YAML 'documents'. 256 // If three resources are loaded from one file containing three YAML docs: 257 // 258 // {resourceA} 259 // --- 260 // {resourceB} 261 // --- 262 // {resourceC} 263 // 264 // then anchors defined in A cannot be seen from B and C and vice versa. 265 // OTOH, cross-resource links (a field in B referencing fields in A) will 266 // work if the resources are gathered in a ResourceList: 267 // 268 // apiVersion: config.kubernetes.io/v1 269 // kind: ResourceList 270 // metadata: 271 // name: someList 272 // items: 273 // - {resourceA} 274 // - {resourceB} 275 // - {resourceC} 276 // 277 DeAnchor() error 278 279 // DeepCopy copies the ResMap and underlying resources. 280 DeepCopy() ResMap 281 282 // ShallowCopy copies the ResMap but 283 // not the underlying resources. 284 ShallowCopy() ResMap 285 286 // ErrorIfNotEqualSets returns an error if the 287 // argument doesn't have the same resources as self. 288 // Ordering is _not_ taken into account, 289 // as this function was solely used in tests written 290 // before internal resource order was maintained, 291 // and those tests are initialized with maps which 292 // by definition have random ordering, and will 293 // fail spuriously. 294 // TODO: modify tests to not use resmap.FromMap, 295 // TODO: - and replace this with a stricter equals. 296 ErrorIfNotEqualSets(ResMap) error 297 298 // ErrorIfNotEqualLists returns an error if the 299 // argument doesn't have the resource objects 300 // data as self, in the same order. 301 // Meta information is ignored; this is similar 302 // to comparing the AsYaml() strings, but allows 303 // for more informed errors on not equals. 304 ErrorIfNotEqualLists(ResMap) error 305 306 // Debug prints the ResMap. 307 Debug(title string) 308 309 // Select returns a list of resources that 310 // are selected by a Selector 311 Select(types.Selector) ([]*resource.Resource, error) 312 313 // ToRNodeSlice returns a copy of the resources as RNodes. 314 ToRNodeSlice() []*yaml.RNode 315 316 // ApplySmPatch applies a strategic-merge patch to the 317 // selected set of resources. 318 ApplySmPatch( 319 selectedSet *resource.IdSet, patch *resource.Resource) error 320 321 // RemoveBuildAnnotations removes annotations created by the build process. 322 RemoveBuildAnnotations() 323 324 // ApplyFilter applies an RNode filter to all Resources in the ResMap. 325 // TODO: Send/recover ancillary Resource data to/from subprocesses. 326 // Assure that the ancillary data in Resource (everything not in the RNode) 327 // is sent to and re-captured from transformer subprocess (as the process 328 // might edit that information). One way to do this would be to solely use 329 // RNode metadata annotation reading and writing instead of using Resource 330 // struct data members, i.e. the Resource struct is replaced by RNode 331 // and use of (slow) k8s metadata annotations inside the RNode. 332 ApplyFilter(f kio.Filter) error 333 } 334