123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- /*
- 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 transformers
- import (
- "fmt"
- "log"
- "sigs.k8s.io/kustomize/pkg/gvk"
- "sigs.k8s.io/kustomize/pkg/resmap"
- "sigs.k8s.io/kustomize/pkg/transformers/config"
- )
- type nameReferenceTransformer struct {
- backRefs []config.NameBackReferences
- }
- var _ Transformer = &nameReferenceTransformer{}
- // NewNameReferenceTransformer constructs a nameReferenceTransformer
- // with a given slice of NameBackReferences.
- func NewNameReferenceTransformer(br []config.NameBackReferences) Transformer {
- if br == nil {
- log.Fatal("backrefs not expected to be nil")
- }
- return &nameReferenceTransformer{backRefs: br}
- }
- // Transform updates name references in resource A that refer to resource B,
- // given that B's name may have changed.
- //
- // For example, a HorizontalPodAutoscaler (HPA) necessarily refers to a
- // Deployment (the thing that the HPA scales). The Deployment name might change
- // (e.g. prefix added), and the reference in the HPA has to be fixed.
- //
- // In the outer loop below, we encounter an HPA. In scanning backrefs, we
- // find that HPA refers to a Deployment. So we find all resources in the same
- // namespace as the HPA (and with the same prefix and suffix), and look through
- // them to find all the Deployments with a resId that has a Name matching the
- // field in HPA. For each match, we overwrite the HPA name field with the value
- // found in the Deployment's name field (the name in the raw object - the
- // modified name - not the unmodified name in the resId).
- //
- // This assumes that the name stored in a ResId (the ResMap key) isn't modified
- // by name transformers. Name transformers should only modify the name in the
- // body of the resource object (the value in the ResMap).
- func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error {
- // TODO: Too much looping.
- // Even more hidden loops in FilterBy,
- // updateNameReference and FindByGVKN.
- for id := range m {
- for _, backRef := range o.backRefs {
- for _, fSpec := range backRef.FieldSpecs {
- if id.Gvk().IsSelected(&fSpec.Gvk) {
- err := mutateField(
- m[id].Map(), fSpec.PathSlice(),
- fSpec.CreateIfNotPresent,
- o.updateNameReference(
- backRef.Gvk, m.FilterBy(id)))
- if err != nil {
- return err
- }
- }
- }
- }
- }
- return nil
- }
- func (o *nameReferenceTransformer) updateNameReference(
- backRef gvk.Gvk, m resmap.ResMap) func(in interface{}) (interface{}, error) {
- return func(in interface{}) (interface{}, error) {
- switch in.(type) {
- case string:
- s, _ := in.(string)
- for id, res := range m {
- if id.Gvk().IsSelected(&backRef) && id.Name() == s {
- matchedIds := m.GetMatchingIds(id.GvknEquals)
- // If there's more than one match, there's no way
- // to know which one to pick, so emit error.
- if len(matchedIds) > 1 {
- return nil, fmt.Errorf(
- "Multiple matches for name %s:\n %v", id, matchedIds)
- }
- // Return transformed name of the object,
- // complete with prefixes, hashes, etc.
- return res.GetName(), nil
- }
- }
- return in, nil
- case []interface{}:
- l, _ := in.([]interface{})
- var names []string
- for _, item := range l {
- name, ok := item.(string)
- if !ok {
- return nil, fmt.Errorf("%#v is expected to be %T", item, name)
- }
- names = append(names, name)
- }
- for id, res := range m {
- indexes := indexOf(id.Name(), names)
- if id.Gvk().IsSelected(&backRef) && len(indexes) > 0 {
- matchedIds := m.GetMatchingIds(id.GvknEquals)
- if len(matchedIds) > 1 {
- return nil, fmt.Errorf(
- "Multiple matches for name %s:\n %v", id, matchedIds)
- }
- for _, index := range indexes {
- l[index] = res.GetName()
- }
- return l, nil
- }
- }
- return in, nil
- default:
- return nil, fmt.Errorf("%#v is expected to be either a string or a []interface{}", in)
- }
- }
- }
- func indexOf(s string, slice []string) []int {
- var index []int
- for i, item := range slice {
- if item == s {
- index = append(index, i)
- }
- }
- return index
- }
|