Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions functions/go/gcp-set-default-name/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM golang:1.17-alpine3.13
ENV CGO_ENABLED=0
WORKDIR /go/src/

COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN go build -o /usr/local/bin/function ./

#############################################

FROM alpine:3.13
COPY --from=0 /usr/local/bin/function /usr/local/bin/function
ENTRYPOINT ["function"]
19 changes: 19 additions & 0 deletions functions/go/gcp-set-default-name/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# set-default-name

### Feature
`set-default-name` sets two types of default name values:

- It sets the `metadata.name` fields for white listed KRM resources.
- It sets custom field specs to their referred `metadata.name` fields.

In case one, you use `ConfigMap` to set the name. The ConfigMap should
contain a `.data.name` field and be provided as input resource. If the KRM
resources are not listed, their `'metadata.name` won't be updated.
The white list can be found in `./fieldspec/name.go`.

In case two, you can find the nameReferences whiteliest in `./fieldspec/nameref.go`
A `nameReference` contains a GVK and a list of referrals. The GVK gives
the resources' `metadata.data` to read, the `referral` lists the resource
field spec to update. This is different from kustomize nameReference, which
only updates referrals metadata.name field and requires the referrals and
referror have the same `metadata.name`.
28 changes: 28 additions & 0 deletions functions/go/gcp-set-default-name/fieldspec/name.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package fieldspec

/* CustomNameFieldSpecs list should be adjusted actively before we get a variant constructor solution.
*/

const (
CustomNameFieldSpecs = `
customMetaName:
- path: metadata/name
group: storage.cnrm.cloud.google.com

- path: metadata/name
group: serviceusage.cnrm.cloud.google.com

- path: metadata/name
group: redis.cnrm.cloud.google.com

- path: metadata/name
group: spanner.cnrm.cloud.google.com

# A fieldSpec object under customMetaName.
# - path: <fieldspec>
# group: <API Group Name> if ignored, matches all
# version: <API Version> if ignored, matches all
# kind: <Kind> if ignored, matches all
# create: [true|false] default to false, if set to true, create the field path in resource.
`
)
30 changes: 30 additions & 0 deletions functions/go/gcp-set-default-name/fieldspec/nameref.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package fieldspec

const (
NameReferenceFieldSpecs = `
nameReference:
# Blueprint redis-bucket
- kind: RedisInstance
group: redis.cnrm.cloud.google.com
version: v1beta1
fieldSpecs:
- path: spec/displayName
kind: RedisInstance
group: redis.cnrm.cloud.google.com
version: v1beta1

# Blueprint spanner
- kind: SpannerInstance
group: spanner.cnrm.cloud.google.com
version: v1beta1
fieldSpecs:
- path: spec/displayName
kind: SpannerInstance
group: spanner.cnrm.cloud.google.com
version: v1beta1
- path: spec/instanceRef/name
kind: SpannerDatabase
group: spanner.cnrm.cloud.google.com
version: v1beta1
`
)
36 changes: 36 additions & 0 deletions functions/go/gcp-set-default-name/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module github.com/GoogleContainerTools/kpt-functions-catalog/functions/go/gcp-set-default-name

go 1.17

require (
sigs.k8s.io/kustomize/api v0.10.0
sigs.k8s.io/kustomize/kyaml v0.13.1-0.20211202184144-fe551be87b8d

// sigs.k8s.io/kustomize/api v0.8.11-0.20210614195535-7e8ba62e9fd9
// sigs.k8s.io/kustomize/kyaml v0.10.21
)

require (
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-openapi/jsonpointer v0.19.3 // indirect
github.com/go-openapi/jsonreference v0.19.3 // indirect
github.com/go-openapi/swag v0.19.5 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/mailru/easyjson v0.7.0 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/cobra v1.2.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.7.0 // indirect
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
golang.org/x/text v0.3.5 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
)
722 changes: 722 additions & 0 deletions functions/go/gcp-set-default-name/go.sum

Large diffs are not rendered by default.

58 changes: 58 additions & 0 deletions functions/go/gcp-set-default-name/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package main

import (
"fmt"
"os"

"sigs.k8s.io/kustomize/api/hasher"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/kyaml/fn/framework"
"sigs.k8s.io/kustomize/kyaml/fn/framework/command"
)

type Processor struct {}

func newResMapFactory() *resmap.Factory {
resourceFactory := resource.NewFactory(&hasher.Hasher{})
resourceFactory.IncludeLocalConfigs = true
return resmap.NewFactory(resourceFactory)
}

func (p *Processor) Process(resourceList *framework.ResourceList) error {
err := func() error{
trans := &CustomNameTransformer{}
err := trans.Config(resourceList.FunctionConfig)
if err != nil {
return err
}
resmapFactory := newResMapFactory()
resMap, err := resmapFactory.NewResMapFromRNodeSlice(resourceList.Items)
if err != nil {
return fmt.Errorf("failed to convert items to resource map: %w", err)
}
if err := trans.Transform(resMap); err != nil {
return err
}
resourceList.Items = resMap.ToRNodeSlice()
return nil
}()
if err != nil {
resourceList.Results = framework.Results{
&framework.Result{
Message: err.Error(),
Severity: framework.Error,
},
}
return resourceList.Results
}
return nil
}

func main() {
cmd := command.Build(&Processor{}, command.StandaloneEnabled, false)
if err := cmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
96 changes: 96 additions & 0 deletions functions/go/gcp-set-default-name/namereference/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package namereference

import (
"fmt"

"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/filters/fieldspec"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/resid"
"sigs.k8s.io/kustomize/kyaml/yaml"
)

type UnlimitedNameRefFilter struct {
Referrer *resource.Resource
NameFieldToUpdate types.FieldSpec
ReferralTarget resid.Gvk
ReferralCandidates resmap.ResMap

}

func (f UnlimitedNameRefFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes)
}

func (f UnlimitedNameRefFilter) run(node *yaml.RNode) (*yaml.RNode, error) {
if node.GetNamespace() != f.Referrer.GetNamespace() {
return nil, fmt.Errorf("not in the same namespace")
}
if err := node.PipeE(fieldspec.Filter{
FieldSpec: f.NameFieldToUpdate,
SetValue: f.updateName,
}); err != nil {
return nil, errors.Wrapf(
err, "updating name reference in '%s' field of '%s'",
f.NameFieldToUpdate.Path, f.Referrer.CurId().String())
}
return node, nil
}

func (f UnlimitedNameRefFilter) updateName(node *yaml.RNode) error {
if yaml.IsMissingOrNull(node) {
return nil
}
candidates := f.ReferralCandidates.Resources()
candidates = doSieve(candidates, previousIdSelectedByGvk(&f.ReferralTarget))
candidates = doSieve(candidates, f.sameCurrentNamespaceAsReferrer())
if len(candidates) == 0 {
return nil
}
referral := candidates[0]
return node.PipeE(yaml.FieldSetter{StringValue: referral.GetName()})
}

type sieveFunc func(*resource.Resource) bool

func doSieve(list []*resource.Resource, fn sieveFunc) (s []*resource.Resource) {
for _, r := range list {
if fn(r) {
s = append(s, r)
}
}
return
}

func previousIdSelectedByGvk(gvk *resid.Gvk) sieveFunc {
return func(r *resource.Resource) bool {
if r.OrgId().IsSelected(gvk) {
return true
}
return false
}
}

func (f UnlimitedNameRefFilter) sameCurrentNamespaceAsReferrer() sieveFunc {
referrerCurId := f.Referrer.CurId()
if referrerCurId.IsClusterScoped() {
// If the referrer is cluster-scoped, let anything through.
return func(_ *resource.Resource) bool {return true}
}
return func(r *resource.Resource) bool {
if r.CurId().IsClusterScoped() {
// Allow cluster-scoped through.
return true
}
if r.GetKind() == "ServiceAccount" {
// Allow service accounts through, even though they
// are in a namespace. A RoleBinding in another namespace
// can reference them.
return true
}
return referrerCurId.IsNsEquals(r.CurId())
}
}
67 changes: 67 additions & 0 deletions functions/go/gcp-set-default-name/namereference/transformer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package namereference

import (
"log"

"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/resid"
)

type nameReferenceTransformer struct {
backRefs []NameBackReferences
}

type NameBackReferences struct {
resid.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"`
Referrers types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}

type NbrSlice []NameBackReferences

var _ resmap.Transformer = &nameReferenceTransformer{}

type filterMap map[*resource.Resource][]UnlimitedNameRefFilter

func NewNameReferenceTransformer(
br []NameBackReferences) resmap.Transformer {
if br == nil {
log.Fatal("backrefs not expected to be nil")
}
return &nameReferenceTransformer{backRefs: br}
}

func (t *nameReferenceTransformer) Transform(m resmap.ResMap) error {
fMap := t.determineFilters(m.Resources())
for r, fList := range fMap {
c := m.SubsetThatCouldBeReferencedByResource(r)
for _, f := range fList {
f.Referrer = r
f.ReferralCandidates = c
if err := f.Referrer.ApplyFilter(f); err != nil {
return err
}
}
}
return nil
}

func (t *nameReferenceTransformer) determineFilters(
resources []*resource.Resource) (fMap filterMap) {
// We cache the resource OrgId values because they don't change and otherwise are very visible in a memory pprof
fMap = make(filterMap)
for _, backReference := range t.backRefs {
for _, referrerSpec := range backReference.Referrers {
for _, res := range resources {
if res.OrgId().IsSelected(&referrerSpec.Gvk) {
fMap[res] = append(fMap[res], UnlimitedNameRefFilter{
NameFieldToUpdate: referrerSpec,
ReferralTarget: backReference.Gvk,
})
}
}
}
}
return fMap
}
Loading