145
vendor/sigs.k8s.io/kustomize/api/resmap/factory.go
generated
vendored
Normal file
145
vendor/sigs.k8s.io/kustomize/api/resmap/factory.go
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package resmap
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/internal/kusterr"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Factory makes instances of ResMap.
|
||||
type Factory struct {
|
||||
// Makes resources.
|
||||
resF *resource.Factory
|
||||
}
|
||||
|
||||
// NewFactory returns a new resmap.Factory.
|
||||
func NewFactory(rf *resource.Factory) *Factory {
|
||||
return &Factory{resF: rf}
|
||||
}
|
||||
|
||||
// RF returns a resource.Factory.
|
||||
func (rmF *Factory) RF() *resource.Factory {
|
||||
return rmF.resF
|
||||
}
|
||||
|
||||
func New() ResMap {
|
||||
return newOne()
|
||||
}
|
||||
|
||||
// FromResource returns a ResMap with one entry.
|
||||
func (rmF *Factory) FromResource(res *resource.Resource) ResMap {
|
||||
m, err := newResMapFromResourceSlice([]*resource.Resource{res})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// FromResourceSlice returns a ResMap with a slice of resources.
|
||||
func (rmF *Factory) FromResourceSlice(ress []*resource.Resource) ResMap {
|
||||
m, err := newResMapFromResourceSlice(ress)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// FromFile returns a ResMap given a resource path.
|
||||
func (rmF *Factory) FromFile(
|
||||
loader ifc.Loader, path string) (ResMap, error) {
|
||||
content, err := loader.Load(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m, err := rmF.NewResMapFromBytes(content)
|
||||
if err != nil {
|
||||
return nil, kusterr.Handler(err, path)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// NewResMapFromBytes decodes a list of objects in byte array format.
|
||||
func (rmF *Factory) NewResMapFromBytes(b []byte) (ResMap, error) {
|
||||
resources, err := rmF.resF.SliceFromBytes(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newResMapFromResourceSlice(resources)
|
||||
}
|
||||
|
||||
// NewResMapFromConfigMapArgs returns a Resource slice given
|
||||
// a configmap metadata slice from kustomization file.
|
||||
func (rmF *Factory) NewResMapFromConfigMapArgs(
|
||||
kvLdr ifc.KvLoader, argList []types.ConfigMapArgs) (ResMap, error) {
|
||||
var resources []*resource.Resource
|
||||
for _, args := range argList {
|
||||
res, err := rmF.resF.MakeConfigMap(kvLdr, &args)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "NewResMapFromConfigMapArgs")
|
||||
}
|
||||
resources = append(resources, res)
|
||||
}
|
||||
return newResMapFromResourceSlice(resources)
|
||||
}
|
||||
|
||||
// FromConfigMapArgs creates a new ResMap containing one ConfigMap.
|
||||
func (rmF *Factory) FromConfigMapArgs(
|
||||
kvLdr ifc.KvLoader, args types.ConfigMapArgs) (ResMap, error) {
|
||||
res, err := rmF.resF.MakeConfigMap(kvLdr, &args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rmF.FromResource(res), nil
|
||||
}
|
||||
|
||||
// NewResMapFromSecretArgs takes a SecretArgs slice, generates
|
||||
// secrets from each entry, and accumulates them in a ResMap.
|
||||
func (rmF *Factory) NewResMapFromSecretArgs(
|
||||
kvLdr ifc.KvLoader, argsList []types.SecretArgs) (ResMap, error) {
|
||||
var resources []*resource.Resource
|
||||
for _, args := range argsList {
|
||||
res, err := rmF.resF.MakeSecret(kvLdr, &args)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "NewResMapFromSecretArgs")
|
||||
}
|
||||
resources = append(resources, res)
|
||||
}
|
||||
return newResMapFromResourceSlice(resources)
|
||||
}
|
||||
|
||||
// FromSecretArgs creates a new ResMap containing one secret.
|
||||
func (rmF *Factory) FromSecretArgs(
|
||||
kvLdr ifc.KvLoader, args types.SecretArgs) (ResMap, error) {
|
||||
res, err := rmF.resF.MakeSecret(kvLdr, &args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rmF.FromResource(res), nil
|
||||
}
|
||||
|
||||
func newResMapFromResourceSlice(
|
||||
resources []*resource.Resource) (ResMap, error) {
|
||||
result := New()
|
||||
for _, res := range resources {
|
||||
err := result.Append(res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// NewResMapFromRNodeSlice returns a ResMap from a slice of RNodes
|
||||
func (rmF *Factory) NewResMapFromRNodeSlice(s []*yaml.RNode) (ResMap, error) {
|
||||
rs, err := rmF.resF.ResourcesFromRNodes(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newResMapFromResourceSlice(rs)
|
||||
}
|
||||
37
vendor/sigs.k8s.io/kustomize/api/resmap/idslice.go
generated
vendored
Normal file
37
vendor/sigs.k8s.io/kustomize/api/resmap/idslice.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resmap
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
)
|
||||
|
||||
// IdSlice implements the sort interface.
|
||||
type IdSlice []resid.ResId
|
||||
|
||||
var _ sort.Interface = IdSlice{}
|
||||
|
||||
func (a IdSlice) Len() int { return len(a) }
|
||||
func (a IdSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a IdSlice) Less(i, j int) bool {
|
||||
if !a[i].Gvk.Equals(a[j].Gvk) {
|
||||
return a[i].Gvk.IsLessThan(a[j].Gvk)
|
||||
}
|
||||
return a[i].String() < a[j].String()
|
||||
}
|
||||
257
vendor/sigs.k8s.io/kustomize/api/resmap/resmap.go
generated
vendored
Normal file
257
vendor/sigs.k8s.io/kustomize/api/resmap/resmap.go
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package resmap implements a map from ResId to Resource that
|
||||
// tracks all resources in a kustomization.
|
||||
package resmap
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// A Transformer modifies an instance of ResMap.
|
||||
type Transformer interface {
|
||||
// Transform modifies data in the argument,
|
||||
// e.g. adding labels to resources that can be labelled.
|
||||
Transform(m ResMap) error
|
||||
}
|
||||
|
||||
// A Generator creates an instance of ResMap.
|
||||
type Generator interface {
|
||||
Generate() (ResMap, error)
|
||||
}
|
||||
|
||||
// Something that's configurable accepts an
|
||||
// instance of PluginHelpers and a raw config
|
||||
// object (YAML in []byte form).
|
||||
type Configurable interface {
|
||||
Config(h *PluginHelpers, config []byte) error
|
||||
}
|
||||
|
||||
// NewPluginHelpers makes an instance of PluginHelpers.
|
||||
func NewPluginHelpers(
|
||||
ldr ifc.Loader, v ifc.Validator, rf *Factory,
|
||||
pc *types.PluginConfig) *PluginHelpers {
|
||||
return &PluginHelpers{ldr: ldr, v: v, rf: rf, pc: pc}
|
||||
}
|
||||
|
||||
// PluginHelpers holds things that any or all plugins might need.
|
||||
// This should be available to each plugin, in addition to
|
||||
// any plugin-specific configuration.
|
||||
type PluginHelpers struct {
|
||||
ldr ifc.Loader
|
||||
v ifc.Validator
|
||||
rf *Factory
|
||||
pc *types.PluginConfig
|
||||
}
|
||||
|
||||
func (c *PluginHelpers) GeneralConfig() *types.PluginConfig {
|
||||
return c.pc
|
||||
}
|
||||
|
||||
func (c *PluginHelpers) Loader() ifc.Loader {
|
||||
return c.ldr
|
||||
}
|
||||
|
||||
func (c *PluginHelpers) ResmapFactory() *Factory {
|
||||
return c.rf
|
||||
}
|
||||
|
||||
func (c *PluginHelpers) Validator() ifc.Validator {
|
||||
return c.v
|
||||
}
|
||||
|
||||
type GeneratorPlugin interface {
|
||||
Generator
|
||||
Configurable
|
||||
}
|
||||
|
||||
type TransformerPlugin interface {
|
||||
Transformer
|
||||
Configurable
|
||||
}
|
||||
|
||||
// ResMap is an interface describing operations on the
|
||||
// core kustomize data structure, a list of Resources.
|
||||
//
|
||||
// Every Resource has two ResIds: OrgId and CurId.
|
||||
//
|
||||
// In a ResMap, no two resources may have the same CurId,
|
||||
// but they may have the same OrgId. The latter can happen
|
||||
// when mixing two or more different overlays apply different
|
||||
// transformations to a common base. When looking for a
|
||||
// resource to transform, try the OrgId first, and if this
|
||||
// fails or finds too many, it might make sense to then try
|
||||
// the CurrId. Depends on the situation.
|
||||
//
|
||||
// TODO: get rid of this interface (use bare resWrangler).
|
||||
// There aren't multiple implementations any more.
|
||||
type ResMap interface {
|
||||
// Size reports the number of resources.
|
||||
Size() int
|
||||
|
||||
// Resources provides a discardable slice
|
||||
// of resource pointers, returned in the order
|
||||
// as appended.
|
||||
Resources() []*resource.Resource
|
||||
|
||||
// Append adds a Resource. Error on CurId collision.
|
||||
//
|
||||
// A class invariant of ResMap is that all of its
|
||||
// resources must differ in their value of
|
||||
// CurId(), aka current Id. The Id is the tuple
|
||||
// of {namespace, group, version, kind, name}
|
||||
// (see ResId).
|
||||
//
|
||||
// This invariant reflects the invariant of a
|
||||
// kubernetes cluster, where if one tries to add
|
||||
// a resource to the cluster whose Id matches
|
||||
// that of a resource already in the cluster,
|
||||
// only two outcomes are allowed. Either the
|
||||
// incoming resource is _merged_ into the existing
|
||||
// one, or the incoming resource is rejected.
|
||||
// One cannot end up with two resources
|
||||
// in the cluster with the same Id.
|
||||
Append(*resource.Resource) error
|
||||
|
||||
// AppendAll appends another ResMap to self,
|
||||
// failing on any CurId collision.
|
||||
AppendAll(ResMap) error
|
||||
|
||||
// AbsorbAll appends, replaces or merges the contents
|
||||
// of another ResMap into self,
|
||||
// allowing and sometimes demanding ID collisions.
|
||||
// A collision would be demanded, say, when a generated
|
||||
// ConfigMap has the "replace" option in its generation
|
||||
// instructions, meaning it _must_ replace
|
||||
// something in the known set of resources.
|
||||
// If a resource id for resource X is found to already
|
||||
// be in self, then the behavior field for X must
|
||||
// be BehaviorMerge or BehaviorReplace. If X is not in
|
||||
// self, then its behavior _cannot_ be merge or replace.
|
||||
AbsorbAll(ResMap) error
|
||||
|
||||
// AsYaml returns the yaml form of resources.
|
||||
AsYaml() ([]byte, error)
|
||||
|
||||
// GetByIndex returns a resource at the given index,
|
||||
// nil if out of range.
|
||||
GetByIndex(int) *resource.Resource
|
||||
|
||||
// GetIndexOfCurrentId returns the index of the resource
|
||||
// with the given CurId.
|
||||
// Returns error if there is more than one match.
|
||||
// Returns (-1, nil) if there is no match.
|
||||
GetIndexOfCurrentId(id resid.ResId) (int, error)
|
||||
|
||||
// GetMatchingResourcesByCurrentId returns the resources
|
||||
// who's CurId is matched by the argument.
|
||||
GetMatchingResourcesByCurrentId(matches IdMatcher) []*resource.Resource
|
||||
|
||||
// GetMatchingResourcesByAnyId returns the resources
|
||||
// who's current or previous IDs is matched by the argument.
|
||||
GetMatchingResourcesByAnyId(matches IdMatcher) []*resource.Resource
|
||||
|
||||
// GetByCurrentId is shorthand for calling
|
||||
// GetMatchingResourcesByCurrentId with a matcher requiring
|
||||
// an exact match, returning an error on multiple or no matches.
|
||||
GetByCurrentId(resid.ResId) (*resource.Resource, error)
|
||||
|
||||
// GetById is shorthand for calling
|
||||
// GetMatchingResourcesByAnyId with a matcher requiring
|
||||
// an exact match, returning an error on multiple or no matches.
|
||||
GetById(resid.ResId) (*resource.Resource, error)
|
||||
|
||||
// GroupedByCurrentNamespace returns a map of namespace
|
||||
// to a slice of *Resource in that namespace.
|
||||
// Resources for whom IsNamespaceableKind is false are
|
||||
// are not included at all (see NonNamespaceable).
|
||||
// Resources with an empty namespace are placed
|
||||
// in the resid.DefaultNamespace entry.
|
||||
GroupedByCurrentNamespace() map[string][]*resource.Resource
|
||||
|
||||
// GroupByOrginalNamespace performs as GroupByNamespace
|
||||
// but use the original namespace instead of the current
|
||||
// one to perform the grouping.
|
||||
GroupedByOriginalNamespace() map[string][]*resource.Resource
|
||||
|
||||
// NonNamespaceable returns a slice of resources that
|
||||
// cannot be placed in a namespace, e.g.
|
||||
// Node, ClusterRole, Namespace itself, etc.
|
||||
NonNamespaceable() []*resource.Resource
|
||||
|
||||
// AllIds returns all CurrentIds.
|
||||
AllIds() []resid.ResId
|
||||
|
||||
// Replace replaces the resource with the matching CurId.
|
||||
// Error if there's no match or more than one match.
|
||||
// Returns the index where the replacement happened.
|
||||
Replace(*resource.Resource) (int, error)
|
||||
|
||||
// Remove removes the resource whose CurId matches the argument.
|
||||
// Error if not found.
|
||||
Remove(resid.ResId) error
|
||||
|
||||
// Clear removes all resources and Ids.
|
||||
Clear()
|
||||
|
||||
// DropEmpties drops empty resources from the ResMap.
|
||||
DropEmpties()
|
||||
|
||||
// SubsetThatCouldBeReferencedByResource returns a ResMap subset
|
||||
// of self with resources that could be referenced by the
|
||||
// resource argument.
|
||||
// This is a filter; it excludes things that cannot be
|
||||
// referenced by the resource, e.g. objects in other
|
||||
// namespaces. Cluster wide objects are never excluded.
|
||||
SubsetThatCouldBeReferencedByResource(*resource.Resource) ResMap
|
||||
|
||||
// DeepCopy copies the ResMap and underlying resources.
|
||||
DeepCopy() ResMap
|
||||
|
||||
// ShallowCopy copies the ResMap but
|
||||
// not the underlying resources.
|
||||
ShallowCopy() ResMap
|
||||
|
||||
// ErrorIfNotEqualSets returns an error if the
|
||||
// argument doesn't have the same resources as self.
|
||||
// Ordering is _not_ taken into account,
|
||||
// as this function was solely used in tests written
|
||||
// before internal resource order was maintained,
|
||||
// and those tests are initialized with maps which
|
||||
// by definition have random ordering, and will
|
||||
// fail spuriously.
|
||||
// TODO: modify tests to not use resmap.FromMap,
|
||||
// TODO: - and replace this with a stricter equals.
|
||||
ErrorIfNotEqualSets(ResMap) error
|
||||
|
||||
// ErrorIfNotEqualLists returns an error if the
|
||||
// argument doesn't have the resource objects
|
||||
// data as self, in the same order.
|
||||
// Meta information is ignored; this is similar
|
||||
// to comparing the AsYaml() strings, but allows
|
||||
// for more informed errors on not equals.
|
||||
ErrorIfNotEqualLists(ResMap) error
|
||||
|
||||
// Debug prints the ResMap.
|
||||
Debug(title string)
|
||||
|
||||
// Select returns a list of resources that
|
||||
// are selected by a Selector
|
||||
Select(types.Selector) ([]*resource.Resource, error)
|
||||
|
||||
// ToRNodeSlice returns a copy of the resources as RNodes.
|
||||
ToRNodeSlice() []*yaml.RNode
|
||||
|
||||
// ApplySmPatch applies a strategic-merge patch to the
|
||||
// selected set of resources.
|
||||
ApplySmPatch(
|
||||
selectedSet *resource.IdSet, patch *resource.Resource) error
|
||||
|
||||
// RemoveBuildAnnotations removes annotations created by the build process.
|
||||
RemoveBuildAnnotations()
|
||||
}
|
||||
620
vendor/sigs.k8s.io/kustomize/api/resmap/reswrangler.go
generated
vendored
Normal file
620
vendor/sigs.k8s.io/kustomize/api/resmap/reswrangler.go
generated
vendored
Normal file
@@ -0,0 +1,620 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package resmap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// resWrangler implements ResMap.
|
||||
type resWrangler struct {
|
||||
// Resource list maintained in load (append) order.
|
||||
// This is important for transformers, which must
|
||||
// be performed in a specific order, and for users
|
||||
// who for whatever reasons wish the order they
|
||||
// specify in kustomizations to be maintained and
|
||||
// available as an option for final YAML rendering.
|
||||
rList []*resource.Resource
|
||||
}
|
||||
|
||||
func newOne() *resWrangler {
|
||||
result := &resWrangler{}
|
||||
result.Clear()
|
||||
return result
|
||||
}
|
||||
|
||||
// Clear implements ResMap.
|
||||
func (m *resWrangler) Clear() {
|
||||
m.rList = nil
|
||||
}
|
||||
|
||||
// DropEmpties quickly drops empty resources.
|
||||
// It doesn't use Append, which checks for Id collisions.
|
||||
func (m *resWrangler) DropEmpties() {
|
||||
var rList []*resource.Resource
|
||||
for _, r := range m.rList {
|
||||
if !r.IsEmpty() {
|
||||
rList = append(rList, r)
|
||||
}
|
||||
}
|
||||
m.rList = rList
|
||||
}
|
||||
|
||||
// Size implements ResMap.
|
||||
func (m *resWrangler) Size() int {
|
||||
return len(m.rList)
|
||||
}
|
||||
|
||||
func (m *resWrangler) indexOfResource(other *resource.Resource) int {
|
||||
for i, r := range m.rList {
|
||||
if r == other {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Resources implements ResMap.
|
||||
func (m *resWrangler) Resources() []*resource.Resource {
|
||||
tmp := make([]*resource.Resource, len(m.rList))
|
||||
copy(tmp, m.rList)
|
||||
return tmp
|
||||
}
|
||||
|
||||
// Append implements ResMap.
|
||||
func (m *resWrangler) Append(res *resource.Resource) error {
|
||||
id := res.CurId()
|
||||
if r := m.GetMatchingResourcesByCurrentId(id.Equals); len(r) > 0 {
|
||||
return fmt.Errorf(
|
||||
"may not add resource with an already registered id: %s", id)
|
||||
}
|
||||
m.append(res)
|
||||
return nil
|
||||
}
|
||||
|
||||
// append appends without performing an Id check
|
||||
func (m *resWrangler) append(res *resource.Resource) {
|
||||
m.rList = append(m.rList, res)
|
||||
}
|
||||
|
||||
// Remove implements ResMap.
|
||||
func (m *resWrangler) Remove(adios resid.ResId) error {
|
||||
var rList []*resource.Resource
|
||||
for _, r := range m.rList {
|
||||
if r.CurId() != adios {
|
||||
rList = append(rList, r)
|
||||
}
|
||||
}
|
||||
if len(rList) != m.Size()-1 {
|
||||
return fmt.Errorf("id %s not found in removal", adios)
|
||||
}
|
||||
m.rList = rList
|
||||
return nil
|
||||
}
|
||||
|
||||
// Replace implements ResMap.
|
||||
func (m *resWrangler) Replace(res *resource.Resource) (int, error) {
|
||||
id := res.CurId()
|
||||
i, err := m.GetIndexOfCurrentId(id)
|
||||
if err != nil {
|
||||
return -1, errors.Wrap(err, "in Replace")
|
||||
}
|
||||
if i < 0 {
|
||||
return -1, fmt.Errorf("cannot find resource with id %s to replace", id)
|
||||
}
|
||||
m.rList[i] = res
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// AllIds implements ResMap.
|
||||
func (m *resWrangler) AllIds() (ids []resid.ResId) {
|
||||
ids = make([]resid.ResId, m.Size())
|
||||
for i, r := range m.rList {
|
||||
ids[i] = r.CurId()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Debug implements ResMap.
|
||||
func (m *resWrangler) Debug(title string) {
|
||||
fmt.Println("--------------------------- " + title)
|
||||
firstObj := true
|
||||
for i, r := range m.rList {
|
||||
if firstObj {
|
||||
firstObj = false
|
||||
} else {
|
||||
fmt.Println("---")
|
||||
}
|
||||
fmt.Printf("# %d %s\n%s\n", i, r.OrgId(), r.String())
|
||||
}
|
||||
}
|
||||
|
||||
type IdMatcher func(resid.ResId) bool
|
||||
|
||||
// GetByIndex implements ResMap.
|
||||
func (m *resWrangler) GetByIndex(i int) *resource.Resource {
|
||||
if i < 0 || i >= m.Size() {
|
||||
return nil
|
||||
}
|
||||
return m.rList[i]
|
||||
}
|
||||
|
||||
// GetIndexOfCurrentId implements ResMap.
|
||||
func (m *resWrangler) GetIndexOfCurrentId(id resid.ResId) (int, error) {
|
||||
count := 0
|
||||
result := -1
|
||||
for i, r := range m.rList {
|
||||
if id.Equals(r.CurId()) {
|
||||
count++
|
||||
result = i
|
||||
}
|
||||
}
|
||||
if count > 1 {
|
||||
return -1, fmt.Errorf("id matched %d resources", count)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type IdFromResource func(r *resource.Resource) resid.ResId
|
||||
|
||||
func GetCurrentId(r *resource.Resource) resid.ResId { return r.CurId() }
|
||||
|
||||
// GetMatchingResourcesByCurrentId implements ResMap.
|
||||
func (m *resWrangler) GetMatchingResourcesByCurrentId(
|
||||
matches IdMatcher) []*resource.Resource {
|
||||
return m.filteredById(matches, GetCurrentId)
|
||||
}
|
||||
|
||||
// GetMatchingResourcesByAnyId implements ResMap.
|
||||
func (m *resWrangler) GetMatchingResourcesByAnyId(
|
||||
matches IdMatcher) []*resource.Resource {
|
||||
var result []*resource.Resource
|
||||
for _, r := range m.rList {
|
||||
for _, id := range append(r.PrevIds(), r.CurId()) {
|
||||
if matches(id) {
|
||||
result = append(result, r)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *resWrangler) filteredById(
|
||||
matches IdMatcher, idGetter IdFromResource) []*resource.Resource {
|
||||
var result []*resource.Resource
|
||||
for _, r := range m.rList {
|
||||
if matches(idGetter(r)) {
|
||||
result = append(result, r)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetByCurrentId implements ResMap.
|
||||
func (m *resWrangler) GetByCurrentId(
|
||||
id resid.ResId) (*resource.Resource, error) {
|
||||
return demandOneMatch(m.GetMatchingResourcesByCurrentId, id, "Current")
|
||||
}
|
||||
|
||||
// GetById implements ResMap.
|
||||
func (m *resWrangler) GetById(
|
||||
id resid.ResId) (*resource.Resource, error) {
|
||||
r, err := demandOneMatch(m.GetMatchingResourcesByAnyId, id, "Id")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"%s; failed to find unique target for patch %s",
|
||||
err.Error(), id.GvknString())
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
type resFinder func(IdMatcher) []*resource.Resource
|
||||
|
||||
func demandOneMatch(
|
||||
f resFinder, id resid.ResId, s string) (*resource.Resource, error) {
|
||||
r := f(id.Equals)
|
||||
if len(r) == 1 {
|
||||
return r[0], nil
|
||||
}
|
||||
if len(r) > 1 {
|
||||
return nil, fmt.Errorf("multiple matches for %s %s", s, id)
|
||||
}
|
||||
return nil, fmt.Errorf("no matches for %sId %s", s, id)
|
||||
}
|
||||
|
||||
// GroupedByCurrentNamespace implements ResMap.GroupByCurrentNamespace
|
||||
func (m *resWrangler) GroupedByCurrentNamespace() map[string][]*resource.Resource {
|
||||
items := m.groupedByCurrentNamespace()
|
||||
delete(items, resid.TotallyNotANamespace)
|
||||
return items
|
||||
}
|
||||
|
||||
// NonNamespaceable implements ResMap.NonNamespaceable
|
||||
func (m *resWrangler) NonNamespaceable() []*resource.Resource {
|
||||
return m.groupedByCurrentNamespace()[resid.TotallyNotANamespace]
|
||||
}
|
||||
|
||||
func (m *resWrangler) groupedByCurrentNamespace() map[string][]*resource.Resource {
|
||||
byNamespace := make(map[string][]*resource.Resource)
|
||||
for _, res := range m.rList {
|
||||
namespace := res.CurId().EffectiveNamespace()
|
||||
if _, found := byNamespace[namespace]; !found {
|
||||
byNamespace[namespace] = []*resource.Resource{}
|
||||
}
|
||||
byNamespace[namespace] = append(byNamespace[namespace], res)
|
||||
}
|
||||
return byNamespace
|
||||
}
|
||||
|
||||
// GroupedByNamespace implements ResMap.GroupByOrginalNamespace
|
||||
func (m *resWrangler) GroupedByOriginalNamespace() map[string][]*resource.Resource {
|
||||
items := m.groupedByOriginalNamespace()
|
||||
delete(items, resid.TotallyNotANamespace)
|
||||
return items
|
||||
}
|
||||
|
||||
func (m *resWrangler) groupedByOriginalNamespace() map[string][]*resource.Resource {
|
||||
byNamespace := make(map[string][]*resource.Resource)
|
||||
for _, res := range m.rList {
|
||||
namespace := res.OrgId().EffectiveNamespace()
|
||||
if _, found := byNamespace[namespace]; !found {
|
||||
byNamespace[namespace] = []*resource.Resource{}
|
||||
}
|
||||
byNamespace[namespace] = append(byNamespace[namespace], res)
|
||||
}
|
||||
return byNamespace
|
||||
}
|
||||
|
||||
// AsYaml implements ResMap.
|
||||
func (m *resWrangler) AsYaml() ([]byte, error) {
|
||||
firstObj := true
|
||||
var b []byte
|
||||
buf := bytes.NewBuffer(b)
|
||||
for _, res := range m.rList {
|
||||
out, err := res.AsYAML()
|
||||
if err != nil {
|
||||
m, _ := res.Map()
|
||||
return nil, errors.Wrapf(err, "%#v", m)
|
||||
}
|
||||
if firstObj {
|
||||
firstObj = false
|
||||
} else {
|
||||
if _, err = buf.WriteString("---\n"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if _, err = buf.Write(out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// ErrorIfNotEqualSets implements ResMap.
|
||||
func (m *resWrangler) ErrorIfNotEqualSets(other ResMap) error {
|
||||
m2, ok := other.(*resWrangler)
|
||||
if !ok {
|
||||
return fmt.Errorf("bad cast to resWrangler 1")
|
||||
}
|
||||
if m.Size() != m2.Size() {
|
||||
return fmt.Errorf(
|
||||
"lists have different number of entries: %#v doesn't equal %#v",
|
||||
m.rList, m2.rList)
|
||||
}
|
||||
seen := make(map[int]bool)
|
||||
for _, r1 := range m.rList {
|
||||
id := r1.CurId()
|
||||
others := m2.GetMatchingResourcesByCurrentId(id.Equals)
|
||||
if len(others) == 0 {
|
||||
return fmt.Errorf(
|
||||
"id in self missing from other; id: %s", id)
|
||||
}
|
||||
if len(others) > 1 {
|
||||
return fmt.Errorf(
|
||||
"id in self matches %d in other; id: %s", len(others), id)
|
||||
}
|
||||
r2 := others[0]
|
||||
if !r1.NodeEqual(r2) {
|
||||
return fmt.Errorf(
|
||||
"nodes unequal: \n -- %s,\n -- %s\n\n--\n%#v\n------\n%#v\n",
|
||||
r1, r2, r1, r2)
|
||||
}
|
||||
seen[m2.indexOfResource(r2)] = true
|
||||
}
|
||||
if len(seen) != m.Size() {
|
||||
return fmt.Errorf("counting problem %d != %d", len(seen), m.Size())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ErrorIfNotEqualList implements ResMap.
|
||||
func (m *resWrangler) ErrorIfNotEqualLists(other ResMap) error {
|
||||
m2, ok := other.(*resWrangler)
|
||||
if !ok {
|
||||
return fmt.Errorf("bad cast to resWrangler 2")
|
||||
}
|
||||
if m.Size() != m2.Size() {
|
||||
return fmt.Errorf(
|
||||
"lists have different number of entries: %#v doesn't equal %#v",
|
||||
m.rList, m2.rList)
|
||||
}
|
||||
for i, r1 := range m.rList {
|
||||
r2 := m2.rList[i]
|
||||
if err := r1.ErrIfNotEquals(r2); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type resCopier func(r *resource.Resource) *resource.Resource
|
||||
|
||||
// ShallowCopy implements ResMap.
|
||||
func (m *resWrangler) ShallowCopy() ResMap {
|
||||
return m.makeCopy(
|
||||
func(r *resource.Resource) *resource.Resource {
|
||||
return r
|
||||
})
|
||||
}
|
||||
|
||||
// DeepCopy implements ResMap.
|
||||
func (m *resWrangler) DeepCopy() ResMap {
|
||||
return m.makeCopy(
|
||||
func(r *resource.Resource) *resource.Resource {
|
||||
return r.DeepCopy()
|
||||
})
|
||||
}
|
||||
|
||||
// makeCopy copies the ResMap.
|
||||
func (m *resWrangler) makeCopy(copier resCopier) ResMap {
|
||||
result := &resWrangler{}
|
||||
result.rList = make([]*resource.Resource, m.Size())
|
||||
for i, r := range m.rList {
|
||||
result.rList[i] = copier(r)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// SubsetThatCouldBeReferencedByResource implements ResMap.
|
||||
func (m *resWrangler) SubsetThatCouldBeReferencedByResource(
|
||||
referrer *resource.Resource) ResMap {
|
||||
referrerId := referrer.CurId()
|
||||
if !referrerId.IsNamespaceableKind() {
|
||||
// A cluster scoped resource can refer to anything.
|
||||
return m
|
||||
}
|
||||
result := newOne()
|
||||
roleBindingNamespaces := getNamespacesForRoleBinding(referrer)
|
||||
for _, possibleTarget := range m.rList {
|
||||
id := possibleTarget.CurId()
|
||||
if !id.IsNamespaceableKind() {
|
||||
// A cluster-scoped resource can be referred to by anything.
|
||||
result.append(possibleTarget)
|
||||
continue
|
||||
}
|
||||
if id.IsNsEquals(referrerId) {
|
||||
// The two objects are in the same namespace.
|
||||
result.append(possibleTarget)
|
||||
continue
|
||||
}
|
||||
// The two objects are namespaced (not cluster-scoped), AND
|
||||
// are in different namespaces.
|
||||
// There's still a chance they can refer to each other.
|
||||
ns := possibleTarget.GetNamespace()
|
||||
if roleBindingNamespaces[ns] {
|
||||
result.append(possibleTarget)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// getNamespacesForRoleBinding returns referenced ServiceAccount namespaces
|
||||
// if the resource is a RoleBinding
|
||||
func getNamespacesForRoleBinding(r *resource.Resource) map[string]bool {
|
||||
result := make(map[string]bool)
|
||||
if r.GetKind() != "RoleBinding" {
|
||||
return result
|
||||
}
|
||||
subjects, err := r.GetSlice("subjects")
|
||||
if err != nil || subjects == nil {
|
||||
return result
|
||||
}
|
||||
for _, s := range subjects {
|
||||
subject := s.(map[string]interface{})
|
||||
if ns, ok1 := subject["namespace"]; ok1 {
|
||||
if kind, ok2 := subject["kind"]; ok2 {
|
||||
if kind.(string) == "ServiceAccount" {
|
||||
result[ns.(string)] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// AppendAll implements ResMap.
|
||||
func (m *resWrangler) AppendAll(other ResMap) error {
|
||||
if other == nil {
|
||||
return nil
|
||||
}
|
||||
m2, ok := other.(*resWrangler)
|
||||
if !ok {
|
||||
return fmt.Errorf("bad cast to resWrangler 3")
|
||||
}
|
||||
return m.appendAll(m2.rList)
|
||||
}
|
||||
|
||||
// appendAll appends all the resources, error on Id collision.
|
||||
func (m *resWrangler) appendAll(list []*resource.Resource) error {
|
||||
for _, res := range list {
|
||||
if err := m.Append(res); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AbsorbAll implements ResMap.
|
||||
func (m *resWrangler) AbsorbAll(other ResMap) error {
|
||||
if other == nil {
|
||||
return nil
|
||||
}
|
||||
m2, ok := other.(*resWrangler)
|
||||
if !ok {
|
||||
return fmt.Errorf("bad cast to resWrangler 4")
|
||||
}
|
||||
for _, r := range m2.rList {
|
||||
err := m.appendReplaceOrMerge(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *resWrangler) appendReplaceOrMerge(res *resource.Resource) error {
|
||||
id := res.CurId()
|
||||
matches := m.GetMatchingResourcesByAnyId(id.Equals)
|
||||
switch len(matches) {
|
||||
case 0:
|
||||
switch res.Behavior() {
|
||||
case types.BehaviorMerge, types.BehaviorReplace:
|
||||
return fmt.Errorf(
|
||||
"id %#v does not exist; cannot merge or replace", id)
|
||||
default:
|
||||
// presumably types.BehaviorCreate
|
||||
return m.Append(res)
|
||||
}
|
||||
case 1:
|
||||
old := matches[0]
|
||||
if old == nil {
|
||||
return fmt.Errorf("id lookup failure")
|
||||
}
|
||||
index := m.indexOfResource(old)
|
||||
if index < 0 {
|
||||
return fmt.Errorf("indexing problem")
|
||||
}
|
||||
switch res.Behavior() {
|
||||
case types.BehaviorReplace:
|
||||
res.CopyMergeMetaDataFieldsFrom(old)
|
||||
case types.BehaviorMerge:
|
||||
res.CopyMergeMetaDataFieldsFrom(old)
|
||||
res.MergeDataMapFrom(old)
|
||||
res.MergeBinaryDataMapFrom(old)
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"id %#v exists; behavior must be merge or replace", id)
|
||||
}
|
||||
i, err := m.Replace(res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i != index {
|
||||
return fmt.Errorf("unexpected target index in replacement")
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"found multiple objects %v that could accept merge of %v",
|
||||
matches, id)
|
||||
}
|
||||
}
|
||||
|
||||
// Select returns a list of resources that
|
||||
// are selected by a Selector
|
||||
func (m *resWrangler) Select(s types.Selector) ([]*resource.Resource, error) {
|
||||
var result []*resource.Resource
|
||||
sr, err := types.NewSelectorRegex(&s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, r := range m.rList {
|
||||
curId := r.CurId()
|
||||
orgId := r.OrgId()
|
||||
|
||||
// It first tries to match with the original namespace
|
||||
// then matches with the current namespace
|
||||
if !sr.MatchNamespace(orgId.EffectiveNamespace()) &&
|
||||
!sr.MatchNamespace(curId.EffectiveNamespace()) {
|
||||
continue
|
||||
}
|
||||
|
||||
// It first tries to match with the original name
|
||||
// then matches with the current name
|
||||
if !sr.MatchName(orgId.Name) &&
|
||||
!sr.MatchName(curId.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
// matches the GVK
|
||||
if !sr.MatchGvk(r.GetGvk()) {
|
||||
continue
|
||||
}
|
||||
|
||||
// matches the label selector
|
||||
matched, err := r.MatchesLabelSelector(s.LabelSelector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
|
||||
// matches the annotation selector
|
||||
matched, err = r.MatchesAnnotationSelector(s.AnnotationSelector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
result = append(result, r)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ToRNodeSlice returns a copy of the resources as RNodes.
|
||||
func (m *resWrangler) ToRNodeSlice() []*kyaml.RNode {
|
||||
result := make([]*kyaml.RNode, len(m.rList))
|
||||
for i := range m.rList {
|
||||
result[i] = m.rList[i].AsRNode()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ApplySmPatch applies the patch, and errors on Id collisions.
|
||||
func (m *resWrangler) ApplySmPatch(
|
||||
selectedSet *resource.IdSet, patch *resource.Resource) error {
|
||||
var list []*resource.Resource
|
||||
for _, res := range m.rList {
|
||||
if selectedSet.Contains(res.CurId()) {
|
||||
patchCopy := patch.DeepCopy()
|
||||
patchCopy.CopyMergeMetaDataFieldsFrom(patch)
|
||||
patchCopy.SetGvk(res.GetGvk())
|
||||
patchCopy.SetKind(patch.GetKind())
|
||||
if err := res.ApplySmPatch(patchCopy); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !res.IsEmpty() {
|
||||
list = append(list, res)
|
||||
}
|
||||
}
|
||||
m.Clear()
|
||||
return m.appendAll(list)
|
||||
}
|
||||
|
||||
func (m *resWrangler) RemoveBuildAnnotations() {
|
||||
for _, r := range m.rList {
|
||||
r.RemoveBuildAnnotations()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user