mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-29 09:40:49 +00:00
Compare commits
66 Commits
api/v0.8.1
...
monopole-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f5697b154 | ||
|
|
b54093ebca | ||
|
|
db307a7084 | ||
|
|
a0c7997b66 | ||
|
|
7458a53a73 | ||
|
|
cf6e6ca4db | ||
|
|
e847ec7474 | ||
|
|
440026b9b3 | ||
|
|
64331ad845 | ||
|
|
294070b3ab | ||
|
|
cabbea0d97 | ||
|
|
732a8522df | ||
|
|
8f82c4c748 | ||
|
|
d0bc25f339 | ||
|
|
ed3200e4f5 | ||
|
|
a3ed120efb | ||
|
|
f1b191c02f | ||
|
|
1493b24b46 | ||
|
|
5993eae1aa | ||
|
|
3e506eae02 | ||
|
|
0305860078 | ||
|
|
0205090e0d | ||
|
|
da1bd901b4 | ||
|
|
636b9c7aeb | ||
|
|
942f112ef5 | ||
|
|
03bbb076bf | ||
|
|
e468d6b4d2 | ||
|
|
57206a628d | ||
|
|
f061bb887b | ||
|
|
75fd9a43a3 | ||
|
|
58165dfc89 | ||
|
|
0e8257c387 | ||
|
|
62e78f8349 | ||
|
|
84724a3ebf | ||
|
|
23544e0431 | ||
|
|
b1fda3d62e | ||
|
|
b8ae69b748 | ||
|
|
4014440d06 | ||
|
|
74b0b3adc6 | ||
|
|
382f09a126 | ||
|
|
f9afdc5c95 | ||
|
|
5e4fb4796e | ||
|
|
76f8988865 | ||
|
|
fa3e829eb6 | ||
|
|
d9435bd1b1 | ||
|
|
af96bb4bda | ||
|
|
8607e0adec | ||
|
|
5a2a7709a4 | ||
|
|
437e8f90f6 | ||
|
|
06ac670951 | ||
|
|
3ee1579688 | ||
|
|
5954314b98 | ||
|
|
c0324456a7 | ||
|
|
172adc404f | ||
|
|
501748192b | ||
|
|
f6e6ac0320 | ||
|
|
a10ce1d787 | ||
|
|
839cc2467c | ||
|
|
dbc11ed29f | ||
|
|
0f614e92f7 | ||
|
|
afaf7c62bc | ||
|
|
78d22069d7 | ||
|
|
22720a8b7a | ||
|
|
38c66d213a | ||
|
|
73da51d0ac | ||
|
|
df10d5a17d |
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: ./scripts/kyaml-pre-commit.sh
|
run: ./hack/kyaml-pre-commit.sh
|
||||||
env:
|
env:
|
||||||
KUSTOMIZE_DOCKER_E2E: false # don't need to do e2e tests for linting
|
KUSTOMIZE_DOCKER_E2E: false # don't need to do e2e tests for linting
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,20 @@ We have full documentation on how to get started contributing here:
|
|||||||
|
|
||||||
- [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers!
|
- [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers!
|
||||||
|
|
||||||
|
## Contributor Ladder
|
||||||
|
|
||||||
|
Kustomize generally follows the [Kubernetes Community Membership](https://github.com/kubernetes/community/blob/master/community-membership.md) contributor ladder. Roles are as follows:
|
||||||
|
|
||||||
|
1. Contributor: Anyone who actively contributes code, issues or reviews to the project. There are no Kustomize-specific requirements for this status. All contributors must [sign the CLA](https://github.com/kubernetes/community/tree/master/contributors/guide#prerequisites).
|
||||||
|
1. Member/Reviewer: All Kubernetes-SIGs org members have LGTM rights on the Kustomize repo. There are no Kustomize-specific requirements. Kustomize does not currently have any formal reviewers, but the role will be created if there is interest.
|
||||||
|
1. Maintainer/Approver: Highly experienced active reviewer and contributor to Kustomize. Has both LTGM and approval rights on the Kustomize repo, as well as [Github "maintain" rights](https://docs.github.com/en/organizations/managing-access-to-your-organizations-repositories/repository-permission-levels-for-an-organization#repository-access-for-each-permission-level).
|
||||||
|
1. Admin/Owner: Maintainer who sets technical direction and makes or approves design decisions for the project. Has LGTM and approval rights on the Kustomize repo as well as [Github "admin" rights](https://docs.github.com/en/organizations/managing-access-to-your-organizations-repositories/repository-permission-levels-for-an-organization#repository-access-for-each-permission-level).
|
||||||
|
|
||||||
|
Administrative notes:
|
||||||
|
- Maintainers and admins must be added to the appropriate list both [in the Kustomize repo](https://github.com/kubernetes-sigs/kustomize/blob/8049f7b1af52e8a7ec26faf6cf714f560d0043c5/OWNERS_ALIASES) and [in the community repo](https://github.com/kubernetes/org/blob/main/config/kubernetes-sigs/sig-cli/teams.yaml). If this isn't done, the individual in question will lack either PR approval rights (Kustomize list) or the appropriate Github repository permissions (community list).
|
||||||
|
- The spec for the OWNERS file is [in the community repo](https://github.com/kubernetes/community/blob/master/contributors/guide/owners.md).
|
||||||
|
|
||||||
|
|
||||||
## Contact Information
|
## Contact Information
|
||||||
|
|
||||||
- [Slack channel](https://kubernetes.slack.com/messages/sig-cli)
|
- [Slack channel](https://kubernetes.slack.com/messages/sig-cli)
|
||||||
|
|||||||
8
Makefile
8
Makefile
@@ -131,6 +131,7 @@ pSrc=plugin/builtin
|
|||||||
_builtinplugins = \
|
_builtinplugins = \
|
||||||
AnnotationsTransformer.go \
|
AnnotationsTransformer.go \
|
||||||
ConfigMapGenerator.go \
|
ConfigMapGenerator.go \
|
||||||
|
IAMPolicyGenerator.go \
|
||||||
HashTransformer.go \
|
HashTransformer.go \
|
||||||
ImageTagTransformer.go \
|
ImageTagTransformer.go \
|
||||||
LabelTransformer.go \
|
LabelTransformer.go \
|
||||||
@@ -158,6 +159,7 @@ builtinplugins = $(patsubst %,$(pGen)/%,$(_builtinplugins))
|
|||||||
# that file, will be recreated.
|
# that file, will be recreated.
|
||||||
$(pGen)/AnnotationsTransformer.go: $(pSrc)/annotationstransformer/AnnotationsTransformer.go
|
$(pGen)/AnnotationsTransformer.go: $(pSrc)/annotationstransformer/AnnotationsTransformer.go
|
||||||
$(pGen)/ConfigMapGenerator.go: $(pSrc)/configmapgenerator/ConfigMapGenerator.go
|
$(pGen)/ConfigMapGenerator.go: $(pSrc)/configmapgenerator/ConfigMapGenerator.go
|
||||||
|
$(pGen)/GkeSaGenerator.go: $(pSrc)/gkesagenerator/GkeSaGenerator.go
|
||||||
$(pGen)/HashTransformer.go: $(pSrc)/hashtransformer/HashTransformer.go
|
$(pGen)/HashTransformer.go: $(pSrc)/hashtransformer/HashTransformer.go
|
||||||
$(pGen)/ImageTagTransformer.go: $(pSrc)/imagetagtransformer/ImageTagTransformer.go
|
$(pGen)/ImageTagTransformer.go: $(pSrc)/imagetagtransformer/ImageTagTransformer.go
|
||||||
$(pGen)/LabelTransformer.go: $(pSrc)/labeltransformer/LabelTransformer.go
|
$(pGen)/LabelTransformer.go: $(pSrc)/labeltransformer/LabelTransformer.go
|
||||||
@@ -241,10 +243,10 @@ test-unit-kustomize-all: \
|
|||||||
test-unit-kustomize-plugins
|
test-unit-kustomize-plugins
|
||||||
|
|
||||||
test-unit-cmd-all:
|
test-unit-cmd-all:
|
||||||
./scripts/kyaml-pre-commit.sh
|
./hack/kyaml-pre-commit.sh
|
||||||
|
|
||||||
test-go-mod:
|
test-go-mod:
|
||||||
./scripts/check-go-mod.sh
|
./hack/check-go-mod.sh
|
||||||
|
|
||||||
# Environment variables are defined at
|
# Environment variables are defined at
|
||||||
# https://github.com/kubernetes/test-infra/blob/master/prow/jobs.md#job-environment-variables
|
# https://github.com/kubernetes/test-infra/blob/master/prow/jobs.md#job-environment-variables
|
||||||
@@ -256,7 +258,7 @@ test-multi-module: $(MYGOBIN)/prchecker
|
|||||||
export REPO_NAME=$(REPO_NAME); \
|
export REPO_NAME=$(REPO_NAME); \
|
||||||
export PULL_NUMBER=$(PULL_NUMBER); \
|
export PULL_NUMBER=$(PULL_NUMBER); \
|
||||||
export MODULES=$(MODULES); \
|
export MODULES=$(MODULES); \
|
||||||
./scripts/check-multi-module.sh; \
|
./hack/check-multi-module.sh; \
|
||||||
)
|
)
|
||||||
|
|
||||||
.PHONY:
|
.PHONY:
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
aliases:
|
aliases:
|
||||||
kustomize-admins:
|
kustomize-admins: # Please keep in sync with kustomize-admins in https://github.com/kubernetes/org/blob/main/config/kubernetes-sigs/sig-cli/teams.yaml
|
||||||
- monopole
|
|
||||||
- pwittrock
|
|
||||||
kustomize-maintainers:
|
|
||||||
- droot
|
|
||||||
- justinsb
|
|
||||||
- knverey
|
- knverey
|
||||||
- monopole
|
- monopole
|
||||||
|
- pwittrock
|
||||||
|
kustomize-maintainers: # Please keep in sync with kustomize-maintainers in https://github.com/kubernetes/org/blob/main/config/kubernetes-sigs/sig-cli/teams.yaml
|
||||||
|
- justinsb
|
||||||
- mortent
|
- mortent
|
||||||
- natasha41575
|
- natasha41575
|
||||||
- phanimarupaka
|
- phanimarupaka
|
||||||
- pwittrock
|
|
||||||
- Shell32-Natsu
|
- Shell32-Natsu
|
||||||
# emeritus
|
emeritus-maintainers:
|
||||||
# - liujingfang1
|
- liujingfang1
|
||||||
# - mengqiy
|
- mengqiy
|
||||||
|
|||||||
@@ -280,6 +280,9 @@ func (p *HelmChartInflationGeneratorPlugin) templateCommand() []string {
|
|||||||
// I've tried placing the flag before and after the name argument.
|
// I've tried placing the flag before and after the name argument.
|
||||||
args = append(args, "--generate-name")
|
args = append(args, "--generate-name")
|
||||||
}
|
}
|
||||||
|
if p.IncludeCRDs {
|
||||||
|
args = append(args, "--include-crds")
|
||||||
|
}
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
33
api/builtins/IAMPolicyGenerator.go
Normal file
33
api/builtins/IAMPolicyGenerator.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Code generated by pluginator on IAMPolicyGenerator; DO NOT EDIT.
|
||||||
|
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/iampolicygenerator"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IAMPolicyGeneratorPlugin struct {
|
||||||
|
types.IAMPolicyGeneratorArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *IAMPolicyGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte) (err error) {
|
||||||
|
p.IAMPolicyGeneratorArgs = types.IAMPolicyGeneratorArgs{}
|
||||||
|
err = yaml.Unmarshal(config, p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *IAMPolicyGeneratorPlugin) Generate() (resmap.ResMap, error) {
|
||||||
|
r := resmap.New()
|
||||||
|
err := r.ApplyFilter(iampolicygenerator.Filter{
|
||||||
|
IAMPolicyGenerator: p.IAMPolicyGeneratorArgs,
|
||||||
|
})
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIAMPolicyGeneratorPlugin() resmap.GeneratorPlugin {
|
||||||
|
return &IAMPolicyGeneratorPlugin{}
|
||||||
|
}
|
||||||
7
api/filesys/doc.go
Normal file
7
api/filesys/doc.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package filesys provides a file system abstraction,
|
||||||
|
// a subset of that provided by golang.org/pkg/os,
|
||||||
|
// with an on-disk and in-memory representation.
|
||||||
|
package filesys
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// Package filesys provides a file system abstraction layer.
|
|
||||||
package filesys
|
package filesys
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -29,6 +28,8 @@ type FileSystem interface {
|
|||||||
Open(path string) (File, error)
|
Open(path string) (File, error)
|
||||||
// IsDir returns true if the path is a directory.
|
// IsDir returns true if the path is a directory.
|
||||||
IsDir(path string) bool
|
IsDir(path string) bool
|
||||||
|
// ReadDir returns a list of files and directories within a directory.
|
||||||
|
ReadDir(path string) ([]string, error)
|
||||||
// CleanedAbs converts the given path into a
|
// CleanedAbs converts the given path into a
|
||||||
// directory and a file name, where the directory
|
// directory and a file name, where the directory
|
||||||
// is represented as a ConfirmedDir and all that implies.
|
// is represented as a ConfirmedDir and all that implies.
|
||||||
|
|||||||
@@ -349,6 +349,29 @@ func (n *fsNode) IsDir(path string) bool {
|
|||||||
return result.isNodeADir()
|
return result.isNodeADir()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadDir implements FileSystem.
|
||||||
|
func (n *fsNode) ReadDir(path string) ([]string, error) {
|
||||||
|
if !n.IsDir(path) {
|
||||||
|
return nil, fmt.Errorf("%s is not a directory", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, err := n.Find(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if dir == nil {
|
||||||
|
return nil, fmt.Errorf("could not find directory %s", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := make([]string, len(dir.dir))
|
||||||
|
i := 0
|
||||||
|
for k := range dir.dir {
|
||||||
|
keys[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return keys, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Size returns the size of the node.
|
// Size returns the size of the node.
|
||||||
func (n *fsNode) Size() int64 {
|
func (n *fsNode) Size() int64 {
|
||||||
if n.isNodeADir() {
|
if n.isNodeADir() {
|
||||||
@@ -560,7 +583,7 @@ func isLegalFileNameForCreation(n string) bool {
|
|||||||
func (n *fsNode) RegExpGlob(pattern string) ([]string, error) {
|
func (n *fsNode) RegExpGlob(pattern string) ([]string, error) {
|
||||||
var result []string
|
var result []string
|
||||||
var expression = regexp.MustCompile(pattern)
|
var expression = regexp.MustCompile(pattern)
|
||||||
n.WalkMe(func(path string, info os.FileInfo, err error) error {
|
err := n.WalkMe(func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -571,6 +594,9 @@ func (n *fsNode) RegExpGlob(pattern string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
sort.Strings(result)
|
sort.Strings(result)
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
@@ -582,7 +608,7 @@ func (n *fsNode) RegExpGlob(pattern string) ([]string, error) {
|
|||||||
// This is how /bin/ls behaves.
|
// This is how /bin/ls behaves.
|
||||||
func (n *fsNode) Glob(pattern string) ([]string, error) {
|
func (n *fsNode) Glob(pattern string) ([]string, error) {
|
||||||
var result []string
|
var result []string
|
||||||
n.WalkMe(func(path string, info os.FileInfo, err error) error {
|
err := n.WalkMe(func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -597,6 +623,9 @@ func (n *fsNode) Glob(pattern string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
sort.Strings(result)
|
sort.Strings(result)
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,19 @@ func (fsOnDisk) IsDir(name string) bool {
|
|||||||
return info.IsDir()
|
return info.IsDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadDir delegates to os.ReadDir
|
||||||
|
func (fsOnDisk) ReadDir(name string) ([]string, error) {
|
||||||
|
dirEntries, err := os.ReadDir(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make([]string, len(dirEntries))
|
||||||
|
for i := range dirEntries {
|
||||||
|
result[i] = dirEntries[i].Name()
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ReadFile delegates to ioutil.ReadFile.
|
// ReadFile delegates to ioutil.ReadFile.
|
||||||
func (fsOnDisk) ReadFile(name string) ([]byte, error) { return ioutil.ReadFile(name) }
|
func (fsOnDisk) ReadFile(name string) ([]byte, error) { return ioutil.ReadFile(name) }
|
||||||
|
|
||||||
|
|||||||
5
api/filters/doc.go
Normal file
5
api/filters/doc.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package filters
|
||||||
|
|
||||||
|
// Package filters collects various implementations
|
||||||
|
// sigs.k8s.io/kustomize/kyaml/kio.Filter used by kustomize
|
||||||
|
// transformers to modify kubernetes objects.
|
||||||
@@ -49,7 +49,7 @@ func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
|
|||||||
if match := isMatchGVK(fltr.FieldSpec, obj); !match {
|
if match := isMatchGVK(fltr.FieldSpec, obj); !match {
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
fltr.path = utils.PathSplitter(fltr.FieldSpec.Path)
|
fltr.path = utils.PathSplitter(fltr.FieldSpec.Path, "/")
|
||||||
if err := fltr.filter(obj); err != nil {
|
if err := fltr.filter(obj); err != nil {
|
||||||
s, _ := obj.String()
|
s, _ := obj.String()
|
||||||
return nil, errors.WrapPrefixf(err,
|
return nil, errors.WrapPrefixf(err,
|
||||||
|
|||||||
3
api/filters/iampolicygenerator/doc.go
Normal file
3
api/filters/iampolicygenerator/doc.go
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// Package gkesagenerator contains a kio.Filter that that generates a
|
||||||
|
// iampolicy-related resources for a given cloud provider
|
||||||
|
package iampolicygenerator
|
||||||
46
api/filters/iampolicygenerator/example_test.go
Normal file
46
api/filters/iampolicygenerator/example_test.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2021 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package iampolicygenerator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleFilter() {
|
||||||
|
f := Filter{}
|
||||||
|
var err = yaml.Unmarshal([]byte(`
|
||||||
|
cloud: gke
|
||||||
|
kubernetesService:
|
||||||
|
namespace: k8s-namespace
|
||||||
|
name: k8s-sa-name
|
||||||
|
serviceAccount:
|
||||||
|
name: gsa-name
|
||||||
|
projectId: project-id
|
||||||
|
`), &f)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = kio.Pipeline{
|
||||||
|
Inputs: []kio.Reader{},
|
||||||
|
Filters: []kio.Filter{f},
|
||||||
|
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||||
|
}.Execute()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// apiVersion: v1
|
||||||
|
// kind: ServiceAccount
|
||||||
|
// metadata:
|
||||||
|
// annotations:
|
||||||
|
// iam.gke.io/gcp-service-account: gsa-name@project-id.iam.gserviceaccount.com
|
||||||
|
// name: k8s-sa-name
|
||||||
|
// namespace: k8s-namespace
|
||||||
|
}
|
||||||
55
api/filters/iampolicygenerator/iampolicygenerator.go
Normal file
55
api/filters/iampolicygenerator/iampolicygenerator.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2021 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package iampolicygenerator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Filter struct {
|
||||||
|
IAMPolicyGenerator types.IAMPolicyGeneratorArgs `json:",inline,omitempty" yaml:",inline,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter adds a GKE service account object to nodes
|
||||||
|
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
|
switch f.IAMPolicyGenerator.Cloud {
|
||||||
|
case types.GKE:
|
||||||
|
IAMPolicyResources, err := f.generateGkeIAMPolicyResources()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nodes = append(nodes, IAMPolicyResources...)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("cloud provider %s not supported yet", f.IAMPolicyGenerator.Cloud)
|
||||||
|
}
|
||||||
|
return nodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Filter) generateGkeIAMPolicyResources() ([]*yaml.RNode, error) {
|
||||||
|
var result []*yaml.RNode
|
||||||
|
input := fmt.Sprintf(`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
iam.gke.io/gcp-service-account: %s@%s.iam.gserviceaccount.com
|
||||||
|
name: %s
|
||||||
|
`, f.IAMPolicyGenerator.ServiceAccount.Name,
|
||||||
|
f.IAMPolicyGenerator.ProjectId,
|
||||||
|
f.IAMPolicyGenerator.KubernetesService.Name)
|
||||||
|
|
||||||
|
if f.IAMPolicyGenerator.Namespace != "" {
|
||||||
|
input = input + fmt.Sprintf("\n namespace: %s", f.IAMPolicyGenerator.Namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
sa, err := yaml.Parse(input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(result, sa), nil
|
||||||
|
}
|
||||||
75
api/filters/iampolicygenerator/iampolicygenerator_test.go
Normal file
75
api/filters/iampolicygenerator/iampolicygenerator_test.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
// Copyright 2021 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package iampolicygenerator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFilter(t *testing.T) {
|
||||||
|
testCases := map[string]struct {
|
||||||
|
args types.IAMPolicyGeneratorArgs
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
"with namespace": {
|
||||||
|
args: types.IAMPolicyGeneratorArgs{
|
||||||
|
Cloud: types.GKE,
|
||||||
|
KubernetesService: types.KubernetesService{
|
||||||
|
Namespace: "k8s-namespace",
|
||||||
|
Name: "k8s-sa-name",
|
||||||
|
},
|
||||||
|
ServiceAccount: types.ServiceAccount{
|
||||||
|
Name: "gsa-name",
|
||||||
|
ProjectId: "project-id",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
iam.gke.io/gcp-service-account: gsa-name@project-id.iam.gserviceaccount.com
|
||||||
|
name: k8s-sa-name
|
||||||
|
namespace: k8s-namespace
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
"without namespace": {
|
||||||
|
args: types.IAMPolicyGeneratorArgs{
|
||||||
|
Cloud: types.GKE,
|
||||||
|
KubernetesService: types.KubernetesService{
|
||||||
|
Name: "k8s-sa-name",
|
||||||
|
},
|
||||||
|
ServiceAccount: types.ServiceAccount{
|
||||||
|
Name: "gsa-name",
|
||||||
|
ProjectId: "project-id",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
iam.gke.io/gcp-service-account: gsa-name@project-id.iam.gserviceaccount.com
|
||||||
|
name: k8s-sa-name
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for tn, tc := range testCases {
|
||||||
|
t.Run(tn, func(t *testing.T) {
|
||||||
|
f := Filter{
|
||||||
|
IAMPolicyGenerator: tc.args,
|
||||||
|
}
|
||||||
|
actual := filtertest.RunFilter(t, "", f)
|
||||||
|
if !assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(actual)) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/utils"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
@@ -66,7 +67,7 @@ func rejectId(rejects []*types.Selector, id *resid.ResId) bool {
|
|||||||
|
|
||||||
func applyToNode(node *yaml.RNode, value *yaml.RNode, target *types.TargetSelector) error {
|
func applyToNode(node *yaml.RNode, value *yaml.RNode, target *types.TargetSelector) error {
|
||||||
for _, fp := range target.FieldPaths {
|
for _, fp := range target.FieldPaths {
|
||||||
fieldPath := strings.Split(fp, ".")
|
fieldPath := utils.SmarterPathSplitter(fp, ".")
|
||||||
var t *yaml.RNode
|
var t *yaml.RNode
|
||||||
var err error
|
var err error
|
||||||
if target.Options != nil && target.Options.Create {
|
if target.Options != nil && target.Options.Create {
|
||||||
@@ -87,12 +88,11 @@ func applyToNode(node *yaml.RNode, value *yaml.RNode, target *types.TargetSelect
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setTargetValue(options *types.FieldOptions, t *yaml.RNode, value *yaml.RNode) error {
|
func setTargetValue(options *types.FieldOptions, t *yaml.RNode, value *yaml.RNode) error {
|
||||||
|
value = value.Copy()
|
||||||
if options != nil && options.Delimiter != "" {
|
if options != nil && options.Delimiter != "" {
|
||||||
|
|
||||||
if t.YNode().Kind != yaml.ScalarNode {
|
if t.YNode().Kind != yaml.ScalarNode {
|
||||||
return fmt.Errorf("delimiter option can only be used with scalar nodes")
|
return fmt.Errorf("delimiter option can only be used with scalar nodes")
|
||||||
}
|
}
|
||||||
|
|
||||||
tv := strings.Split(t.YNode().Value, options.Delimiter)
|
tv := strings.Split(t.YNode().Value, options.Delimiter)
|
||||||
v := yaml.GetValue(value)
|
v := yaml.GetValue(value)
|
||||||
// TODO: Add a way to remove an element
|
// TODO: Add a way to remove an element
|
||||||
@@ -119,7 +119,7 @@ func getReplacement(nodes []*yaml.RNode, r *types.Replacement) (*yaml.RNode, err
|
|||||||
if r.Source.FieldPath == "" {
|
if r.Source.FieldPath == "" {
|
||||||
r.Source.FieldPath = types.DefaultReplacementFieldPath
|
r.Source.FieldPath = types.DefaultReplacementFieldPath
|
||||||
}
|
}
|
||||||
fieldPath := strings.Split(r.Source.FieldPath, ".")
|
fieldPath := utils.SmarterPathSplitter(r.Source.FieldPath, ".")
|
||||||
|
|
||||||
rn, err := source.Pipe(yaml.Lookup(fieldPath...))
|
rn, err := source.Pipe(yaml.Lookup(fieldPath...))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1338,6 +1338,148 @@ spec:
|
|||||||
`,
|
`,
|
||||||
expectedErr: "delimiter option can only be used with scalar nodes",
|
expectedErr: "delimiter option can only be used with scalar nodes",
|
||||||
},
|
},
|
||||||
|
"list index contains '.' character": {
|
||||||
|
input: `apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: source
|
||||||
|
data:
|
||||||
|
value: example
|
||||||
|
---
|
||||||
|
apiVersion: kubernetes-client.io/v1
|
||||||
|
kind: ExternalSecret
|
||||||
|
metadata:
|
||||||
|
name: some-secret
|
||||||
|
spec:
|
||||||
|
backendType: secretsManager
|
||||||
|
data:
|
||||||
|
- key: some-prefix-replaceme
|
||||||
|
name: .first
|
||||||
|
version: latest
|
||||||
|
property: first
|
||||||
|
- key: some-prefix-replaceme
|
||||||
|
name: second
|
||||||
|
version: latest
|
||||||
|
property: second
|
||||||
|
`,
|
||||||
|
replacements: `replacements:
|
||||||
|
- source:
|
||||||
|
kind: ConfigMap
|
||||||
|
version: v1
|
||||||
|
name: source
|
||||||
|
fieldPath: data.value
|
||||||
|
targets:
|
||||||
|
- select:
|
||||||
|
group: kubernetes-client.io
|
||||||
|
version: v1
|
||||||
|
kind: ExternalSecret
|
||||||
|
name: some-secret
|
||||||
|
fieldPaths:
|
||||||
|
- spec.data.[name=.first].key
|
||||||
|
- spec.data.[name=second].key
|
||||||
|
options:
|
||||||
|
delimiter: "-"
|
||||||
|
index: 2
|
||||||
|
`,
|
||||||
|
expected: `apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: source
|
||||||
|
data:
|
||||||
|
value: example
|
||||||
|
---
|
||||||
|
apiVersion: kubernetes-client.io/v1
|
||||||
|
kind: ExternalSecret
|
||||||
|
metadata:
|
||||||
|
name: some-secret
|
||||||
|
spec:
|
||||||
|
backendType: secretsManager
|
||||||
|
data:
|
||||||
|
- key: some-prefix-example
|
||||||
|
name: .first
|
||||||
|
version: latest
|
||||||
|
property: first
|
||||||
|
- key: some-prefix-example
|
||||||
|
name: second
|
||||||
|
version: latest
|
||||||
|
property: second`,
|
||||||
|
},
|
||||||
|
"multiple field paths in target": {
|
||||||
|
input: `apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: source
|
||||||
|
data:
|
||||||
|
value: example
|
||||||
|
---
|
||||||
|
apiVersion: kubernetes-client.io/v1
|
||||||
|
kind: ExternalSecret
|
||||||
|
metadata:
|
||||||
|
name: some-secret
|
||||||
|
spec:
|
||||||
|
backendType: secretsManager
|
||||||
|
data:
|
||||||
|
- key: some-prefix-replaceme
|
||||||
|
name: first
|
||||||
|
version: latest
|
||||||
|
property: first
|
||||||
|
- key: some-prefix-replaceme
|
||||||
|
name: second
|
||||||
|
version: latest
|
||||||
|
property: second
|
||||||
|
- key: some-prefix-replaceme
|
||||||
|
name: third
|
||||||
|
version: latest
|
||||||
|
property: third
|
||||||
|
`,
|
||||||
|
replacements: `replacements:
|
||||||
|
- source:
|
||||||
|
kind: ConfigMap
|
||||||
|
version: v1
|
||||||
|
name: source
|
||||||
|
fieldPath: data.value
|
||||||
|
targets:
|
||||||
|
- select:
|
||||||
|
group: kubernetes-client.io
|
||||||
|
version: v1
|
||||||
|
kind: ExternalSecret
|
||||||
|
name: some-secret
|
||||||
|
fieldPaths:
|
||||||
|
- spec.data.0.key
|
||||||
|
- spec.data.1.key
|
||||||
|
- spec.data.2.key
|
||||||
|
options:
|
||||||
|
delimiter: "-"
|
||||||
|
index: 2
|
||||||
|
`,
|
||||||
|
expected: `apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: source
|
||||||
|
data:
|
||||||
|
value: example
|
||||||
|
---
|
||||||
|
apiVersion: kubernetes-client.io/v1
|
||||||
|
kind: ExternalSecret
|
||||||
|
metadata:
|
||||||
|
name: some-secret
|
||||||
|
spec:
|
||||||
|
backendType: secretsManager
|
||||||
|
data:
|
||||||
|
- key: some-prefix-example
|
||||||
|
name: first
|
||||||
|
version: latest
|
||||||
|
property: first
|
||||||
|
- key: some-prefix-example
|
||||||
|
name: second
|
||||||
|
version: latest
|
||||||
|
property: second
|
||||||
|
- key: some-prefix-example
|
||||||
|
name: third
|
||||||
|
version: latest
|
||||||
|
property: third
|
||||||
|
`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for tn, tc := range testCases {
|
for tn, tc := range testCases {
|
||||||
@@ -1353,7 +1495,7 @@ spec:
|
|||||||
t.Errorf("unexpected error: %s\n", err.Error())
|
t.Errorf("unexpected error: %s\n", err.Error())
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
if !assert.Equal(t, tc.expectedErr, err.Error()) {
|
if !assert.Contains(t, err.Error(), tc.expectedErr) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,3 +16,5 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
||||||
|
|
||||||
|
replace sigs.k8s.io/kustomize/kyaml => ../kyaml
|
||||||
|
|||||||
@@ -224,8 +224,6 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
|
|||||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.10.20 h1:L9JNKvJfCBpmYFr4tP0igpfj/pXP7nW2aXOWNtF5k1g=
|
|
||||||
sigs.k8s.io/kustomize/kyaml v0.10.20/go.mod h1:TYWhGwW9vjoRh3rWqBwB/ZOXyEGRVWe7Ggc3+KZIO+c=
|
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||||
|
|||||||
@@ -178,9 +178,12 @@ func loadCrdIntoConfig(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if property.Ref.GetURL() != nil {
|
if property.Ref.GetURL() != nil {
|
||||||
loadCrdIntoConfig(
|
err = loadCrdIntoConfig(
|
||||||
theConfig, theGvk, theMap,
|
theConfig, theGvk, theMap,
|
||||||
property.Ref.String(), append(path, propName))
|
property.Ref.String(), append(path, propName))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
. "sigs.k8s.io/kustomize/api/internal/accumulator"
|
. "sigs.k8s.io/kustomize/api/internal/accumulator"
|
||||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||||
@@ -162,16 +163,13 @@ func TestLoadCRDs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
fSys.WriteFile("/testpath/crd.json", []byte(crdContent))
|
err := fSys.WriteFile("/testpath/crd.json", []byte(crdContent))
|
||||||
|
require.NoError(t, err)
|
||||||
ldr, err := loader.NewLoader(loader.RestrictionRootOnly, "/testpath", fSys)
|
ldr, err := loader.NewLoader(loader.RestrictionRootOnly, "/testpath", fSys)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatalf("unexpected error:%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
actualTc, err := LoadConfigFromCRDs(ldr, []string{"crd.json"})
|
actualTc, err := LoadConfigFromCRDs(ldr, []string{"crd.json"})
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Fatalf("unexpected error:%v", err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(actualTc, expectedTc) {
|
if !reflect.DeepEqual(actualTc, expectedTc) {
|
||||||
t.Fatalf("expected\n %v\n but got\n %v\n", expectedTc, actualTc)
|
t.Fatalf("expected\n %v\n but got\n %v\n", expectedTc, actualTc)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
. "sigs.k8s.io/kustomize/api/internal/accumulator"
|
. "sigs.k8s.io/kustomize/api/internal/accumulator"
|
||||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||||
"sigs.k8s.io/kustomize/api/provider"
|
"sigs.k8s.io/kustomize/api/provider"
|
||||||
@@ -224,20 +225,26 @@ func TestResolveVarConflicts(t *testing.T) {
|
|||||||
// create accumulators holding apparently conflicting vars that are not
|
// create accumulators holding apparently conflicting vars that are not
|
||||||
// actually in conflict because they point to the same concrete value.
|
// actually in conflict because they point to the same concrete value.
|
||||||
rm0 := resmap.New()
|
rm0 := resmap.New()
|
||||||
rm0.Append(rf.FromMap(fooAws))
|
err := rm0.Append(rf.FromMap(fooAws))
|
||||||
|
require.NoError(t, err)
|
||||||
ac0 := MakeEmptyAccumulator()
|
ac0 := MakeEmptyAccumulator()
|
||||||
ac0.AppendAll(rm0)
|
err = ac0.AppendAll(rm0)
|
||||||
ac0.MergeVars([]types.Var{varFoo})
|
require.NoError(t, err)
|
||||||
|
err = ac0.MergeVars([]types.Var{varFoo})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
rm1 := resmap.New()
|
rm1 := resmap.New()
|
||||||
rm1.Append(rf.FromMap(barAws))
|
err = rm1.Append(rf.FromMap(barAws))
|
||||||
|
require.NoError(t, err)
|
||||||
ac1 := MakeEmptyAccumulator()
|
ac1 := MakeEmptyAccumulator()
|
||||||
ac1.AppendAll(rm1)
|
err = ac1.AppendAll(rm1)
|
||||||
ac1.MergeVars([]types.Var{varBar})
|
require.NoError(t, err)
|
||||||
|
err = ac1.MergeVars([]types.Var{varBar})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
// validate that two vars of the same name which reference the same concrete
|
// validate that two vars of the same name which reference the same concrete
|
||||||
// value do not produce a conflict.
|
// value do not produce a conflict.
|
||||||
err := ac0.MergeAccumulator(ac1)
|
err = ac0.MergeAccumulator(ac1)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("see bug gh-1600")
|
t.Fatalf("see bug gh-1600")
|
||||||
}
|
}
|
||||||
@@ -246,10 +253,13 @@ func TestResolveVarConflicts(t *testing.T) {
|
|||||||
// two above (because it contains a variable whose name is used in the other
|
// two above (because it contains a variable whose name is used in the other
|
||||||
// accumulators AND whose concrete values are different).
|
// accumulators AND whose concrete values are different).
|
||||||
rm2 := resmap.New()
|
rm2 := resmap.New()
|
||||||
rm2.Append(rf.FromMap(barGcp))
|
err = rm2.Append(rf.FromMap(barGcp))
|
||||||
|
require.NoError(t, err)
|
||||||
ac2 := MakeEmptyAccumulator()
|
ac2 := MakeEmptyAccumulator()
|
||||||
ac2.AppendAll(rm2)
|
err = ac2.AppendAll(rm2)
|
||||||
ac2.MergeVars([]types.Var{varBar})
|
require.NoError(t, err)
|
||||||
|
err = ac2.MergeVars([]types.Var{varBar})
|
||||||
|
require.NoError(t, err)
|
||||||
err = ac1.MergeAccumulator(ac2)
|
err = ac1.MergeAccumulator(ac2)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("dupe vars w/ different concrete values should conflict")
|
t.Fatalf("dupe vars w/ different concrete values should conflict")
|
||||||
|
|||||||
@@ -40,7 +40,13 @@ func MakeConfigMap(
|
|||||||
if err = rn.LoadMapIntoConfigMapData(m); err != nil {
|
if err = rn.LoadMapIntoConfigMapData(m); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
copyLabelsAndAnnotations(rn, args.Options)
|
err = copyLabelsAndAnnotations(rn, args.Options)
|
||||||
setImmutable(rn, args.Options)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = setImmutable(rn, args.Options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return rn, nil
|
return rn, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,25 +11,26 @@ func _() {
|
|||||||
_ = x[Unknown-0]
|
_ = x[Unknown-0]
|
||||||
_ = x[AnnotationsTransformer-1]
|
_ = x[AnnotationsTransformer-1]
|
||||||
_ = x[ConfigMapGenerator-2]
|
_ = x[ConfigMapGenerator-2]
|
||||||
_ = x[HashTransformer-3]
|
_ = x[IAMPolicyGenerator-3]
|
||||||
_ = x[ImageTagTransformer-4]
|
_ = x[HashTransformer-4]
|
||||||
_ = x[LabelTransformer-5]
|
_ = x[ImageTagTransformer-5]
|
||||||
_ = x[LegacyOrderTransformer-6]
|
_ = x[LabelTransformer-6]
|
||||||
_ = x[NamespaceTransformer-7]
|
_ = x[LegacyOrderTransformer-7]
|
||||||
_ = x[PatchJson6902Transformer-8]
|
_ = x[NamespaceTransformer-8]
|
||||||
_ = x[PatchStrategicMergeTransformer-9]
|
_ = x[PatchJson6902Transformer-9]
|
||||||
_ = x[PatchTransformer-10]
|
_ = x[PatchStrategicMergeTransformer-10]
|
||||||
_ = x[PrefixSuffixTransformer-11]
|
_ = x[PatchTransformer-11]
|
||||||
_ = x[ReplicaCountTransformer-12]
|
_ = x[PrefixSuffixTransformer-12]
|
||||||
_ = x[SecretGenerator-13]
|
_ = x[ReplicaCountTransformer-13]
|
||||||
_ = x[ValueAddTransformer-14]
|
_ = x[SecretGenerator-14]
|
||||||
_ = x[HelmChartInflationGenerator-15]
|
_ = x[ValueAddTransformer-15]
|
||||||
_ = x[ReplacementTransformer-16]
|
_ = x[HelmChartInflationGenerator-16]
|
||||||
|
_ = x[ReplacementTransformer-17]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _BuiltinPluginType_name = "UnknownAnnotationsTransformerConfigMapGeneratorHashTransformerImageTagTransformerLabelTransformerLegacyOrderTransformerNamespaceTransformerPatchJson6902TransformerPatchStrategicMergeTransformerPatchTransformerPrefixSuffixTransformerReplicaCountTransformerSecretGeneratorValueAddTransformerHelmChartInflationGeneratorReplacementTransformer"
|
const _BuiltinPluginType_name = "UnknownAnnotationsTransformerConfigMapGeneratorIAMPolicyGeneratorHashTransformerImageTagTransformerLabelTransformerLegacyOrderTransformerNamespaceTransformerPatchJson6902TransformerPatchStrategicMergeTransformerPatchTransformerPrefixSuffixTransformerReplicaCountTransformerSecretGeneratorValueAddTransformerHelmChartInflationGeneratorReplacementTransformer"
|
||||||
|
|
||||||
var _BuiltinPluginType_index = [...]uint16{0, 7, 29, 47, 62, 81, 97, 119, 139, 163, 193, 209, 232, 255, 270, 289, 316, 338}
|
var _BuiltinPluginType_index = [...]uint16{0, 7, 29, 47, 65, 80, 99, 115, 137, 157, 181, 211, 227, 250, 273, 288, 307, 334, 356}
|
||||||
|
|
||||||
func (i BuiltinPluginType) String() string {
|
func (i BuiltinPluginType) String() string {
|
||||||
if i < 0 || i >= BuiltinPluginType(len(_BuiltinPluginType_index)-1) {
|
if i < 0 || i >= BuiltinPluginType(len(_BuiltinPluginType_index)-1) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const (
|
|||||||
Unknown BuiltinPluginType = iota
|
Unknown BuiltinPluginType = iota
|
||||||
AnnotationsTransformer
|
AnnotationsTransformer
|
||||||
ConfigMapGenerator
|
ConfigMapGenerator
|
||||||
|
IAMPolicyGenerator
|
||||||
HashTransformer
|
HashTransformer
|
||||||
ImageTagTransformer
|
ImageTagTransformer
|
||||||
LabelTransformer
|
LabelTransformer
|
||||||
@@ -58,6 +59,7 @@ func GetBuiltinPluginType(n string) BuiltinPluginType {
|
|||||||
|
|
||||||
var GeneratorFactories = map[BuiltinPluginType]func() resmap.GeneratorPlugin{
|
var GeneratorFactories = map[BuiltinPluginType]func() resmap.GeneratorPlugin{
|
||||||
ConfigMapGenerator: builtins.NewConfigMapGeneratorPlugin,
|
ConfigMapGenerator: builtins.NewConfigMapGeneratorPlugin,
|
||||||
|
IAMPolicyGenerator: builtins.NewIAMPolicyGeneratorPlugin,
|
||||||
SecretGenerator: builtins.NewSecretGeneratorPlugin,
|
SecretGenerator: builtins.NewSecretGeneratorPlugin,
|
||||||
HelmChartInflationGenerator: builtins.NewHelmChartInflationGeneratorPlugin,
|
HelmChartInflationGenerator: builtins.NewHelmChartInflationGeneratorPlugin,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,9 +94,6 @@ TO GENERATE CODE
|
|||||||
cd $repo/plugin/builtin
|
cd $repo/plugin/builtin
|
||||||
go generate ./...
|
go generate ./...
|
||||||
|
|
||||||
See scripts/kyaml-pre-commit.sh for canonical way
|
|
||||||
to execute the above.
|
|
||||||
|
|
||||||
This creates
|
This creates
|
||||||
|
|
||||||
$repo/api/plugins/builtins/SecretGenerator.go
|
$repo/api/plugins/builtins/SecretGenerator.go
|
||||||
|
|||||||
@@ -89,7 +89,10 @@ type argsConfig struct {
|
|||||||
|
|
||||||
func (p *ExecPlugin) processOptionalArgsFields() error {
|
func (p *ExecPlugin) processOptionalArgsFields() error {
|
||||||
var c argsConfig
|
var c argsConfig
|
||||||
yaml.Unmarshal(p.cfg, &c)
|
err := yaml.Unmarshal(p.cfg, &c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if c.ArgsOneLiner != "" {
|
if c.ArgsOneLiner != "" {
|
||||||
p.args, _ = shlex.Split(c.ArgsOneLiner)
|
p.args, _ = shlex.Split(c.ArgsOneLiner)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
. "sigs.k8s.io/kustomize/api/internal/plugins/execplugin"
|
. "sigs.k8s.io/kustomize/api/internal/plugins/execplugin"
|
||||||
pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
||||||
@@ -21,11 +22,12 @@ import (
|
|||||||
|
|
||||||
func TestExecPluginConfig(t *testing.T) {
|
func TestExecPluginConfig(t *testing.T) {
|
||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
fSys.WriteFile("sed-input.txt", []byte(`
|
err := fSys.WriteFile("sed-input.txt", []byte(`
|
||||||
s/$FOO/foo/g
|
s/$FOO/foo/g
|
||||||
s/$BAR/bar baz/g
|
s/$BAR/bar baz/g
|
||||||
\ \ \
|
\ \ \
|
||||||
`))
|
`))
|
||||||
|
require.NoError(t, err)
|
||||||
ldr, err := fLdr.NewLoader(
|
ldr, err := fLdr.NewLoader(
|
||||||
fLdr.RestrictionRootOnly, filesys.Separator, fSys)
|
fLdr.RestrictionRootOnly, filesys.Separator, fSys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -62,9 +64,10 @@ s/$BAR/bar baz/g
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err: %v", err)
|
t.Fatalf("unexpected err: %v", err)
|
||||||
}
|
}
|
||||||
p.Config(
|
err = p.Config(
|
||||||
resmap.NewPluginHelpers(ldr, pvd.GetFieldValidator(), rf, pc),
|
resmap.NewPluginHelpers(ldr, pvd.GetFieldValidator(), rf, pc),
|
||||||
yaml)
|
yaml)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
expected := "someteam.example.com/v1/sedtransformer/SedTransformer"
|
expected := "someteam.example.com/v1/sedtransformer/SedTransformer"
|
||||||
if !strings.HasSuffix(p.Path(), expected) {
|
if !strings.HasSuffix(p.Path(), expected) {
|
||||||
|
|||||||
@@ -192,7 +192,9 @@ func UpdateResMapValues(pluginName string, h *resmap.PluginHelpers, output []byt
|
|||||||
for _, id := range rm.AllIds() {
|
for _, id := range rm.AllIds() {
|
||||||
newIdx, _ := newMap.GetIndexOfCurrentId(id)
|
newIdx, _ := newMap.GetIndexOfCurrentId(id)
|
||||||
if newIdx == -1 {
|
if newIdx == -1 {
|
||||||
rm.Remove(id)
|
if err = rm.Remove(id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
"sigs.k8s.io/kustomize/api/konfig"
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
"sigs.k8s.io/kustomize/api/provider"
|
"sigs.k8s.io/kustomize/api/provider"
|
||||||
@@ -86,8 +87,10 @@ func TestUpdateResourceOptions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for i, c := range cases {
|
for i, c := range cases {
|
||||||
name := fmt.Sprintf("test%d", i)
|
name := fmt.Sprintf("test%d", i)
|
||||||
in.Append(makeConfigMap(rf, name, c.behavior, c.hashValue))
|
err := in.Append(makeConfigMap(rf, name, c.behavior, c.hashValue))
|
||||||
expected.Append(makeConfigMapOptions(rf, name, c.behavior, !c.needsHash))
|
require.NoError(t, err)
|
||||||
|
err = expected.Append(makeConfigMapOptions(rf, name, c.behavior, !c.needsHash))
|
||||||
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
actual, err := UpdateResourceOptions(in)
|
actual, err := UpdateResourceOptions(in)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -105,10 +108,9 @@ func TestUpdateResourceOptionsWithInvalidHashAnnotationValues(t *testing.T) {
|
|||||||
for i, c := range cases {
|
for i, c := range cases {
|
||||||
name := fmt.Sprintf("test%d", i)
|
name := fmt.Sprintf("test%d", i)
|
||||||
in := resmap.New()
|
in := resmap.New()
|
||||||
in.Append(makeConfigMap(rf, name, "", &c))
|
err := in.Append(makeConfigMap(rf, name, "", &c))
|
||||||
_, err := UpdateResourceOptions(in)
|
require.NoError(t, err)
|
||||||
if err == nil {
|
_, err = UpdateResourceOptions(in)
|
||||||
t.Errorf("expected error from value %q", c)
|
require.Error(t, err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,18 +5,50 @@ package utils
|
|||||||
|
|
||||||
import "strings"
|
import "strings"
|
||||||
|
|
||||||
// PathSplitter splits a slash delimited string, permitting escaped slashes.
|
// TODO: Move these to kyaml
|
||||||
func PathSplitter(path string) []string {
|
|
||||||
ps := strings.Split(path, "/")
|
// PathSplitter splits a delimited string, permitting escaped delimiters.
|
||||||
|
func PathSplitter(path string, delimiter string) []string {
|
||||||
|
ps := strings.Split(path, delimiter)
|
||||||
var res []string
|
var res []string
|
||||||
res = append(res, ps[0])
|
res = append(res, ps[0])
|
||||||
for i := 1; i < len(ps); i++ {
|
for i := 1; i < len(ps); i++ {
|
||||||
last := len(res) - 1
|
last := len(res) - 1
|
||||||
if strings.HasSuffix(res[last], `\`) {
|
if strings.HasSuffix(res[last], `\`) {
|
||||||
res[last] = strings.TrimSuffix(res[last], `\`) + "/" + ps[i]
|
res[last] = strings.TrimSuffix(res[last], `\`) + delimiter + ps[i]
|
||||||
} else {
|
} else {
|
||||||
res = append(res, ps[i])
|
res = append(res, ps[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SmarterPathSplitter splits a path, retaining bracketed list entry identifiers.
|
||||||
|
// E.g. [name=com.foo.someapp] survives as one thing after splitting
|
||||||
|
// "spec.template.spec.containers.[name=com.foo.someapp].image"
|
||||||
|
// See kyaml/yaml/match.go for use of list entry identifiers.
|
||||||
|
// This function uses `PathSplitter`, so it respects list entry identifiers
|
||||||
|
// and escaped delimiters.
|
||||||
|
func SmarterPathSplitter(path string, delimiter string) []string {
|
||||||
|
var result []string
|
||||||
|
split := PathSplitter(path, delimiter)
|
||||||
|
|
||||||
|
for i := 0; i < len(split); i++ {
|
||||||
|
elem := split[i]
|
||||||
|
if strings.HasPrefix(elem, "[") && !strings.HasSuffix(elem, "]") {
|
||||||
|
// continue until we find the matching "]"
|
||||||
|
bracketed := []string{elem}
|
||||||
|
for i < len(split)-1 {
|
||||||
|
i++
|
||||||
|
bracketed = append(bracketed, split[i])
|
||||||
|
if strings.HasSuffix(split[i], "]") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = append(result, strings.Join(bracketed, delimiter))
|
||||||
|
} else {
|
||||||
|
result = append(result, elem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,6 +44,47 @@ func TestPathSplitter(t *testing.T) {
|
|||||||
"nginx.ingress.kubernetes.io/auth-secret"},
|
"nginx.ingress.kubernetes.io/auth-secret"},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
assert.Equal(t, tc.exp, PathSplitter(tc.path))
|
assert.Equal(t, tc.exp, PathSplitter(tc.path, "/"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSmarterPathSplitter(t *testing.T) {
|
||||||
|
testCases := map[string]struct {
|
||||||
|
input string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
"simple": {
|
||||||
|
input: "spec.replicas",
|
||||||
|
expected: []string{"spec", "replicas"},
|
||||||
|
},
|
||||||
|
"sequence": {
|
||||||
|
input: "spec.data.[name=first].key",
|
||||||
|
expected: []string{"spec", "data", "[name=first]", "key"},
|
||||||
|
},
|
||||||
|
"key, value with . prefix": {
|
||||||
|
input: "spec.data.[.name=.first].key",
|
||||||
|
expected: []string{"spec", "data", "[.name=.first]", "key"},
|
||||||
|
},
|
||||||
|
"key, value with . suffix": {
|
||||||
|
input: "spec.data.[name.=first.].key",
|
||||||
|
expected: []string{"spec", "data", "[name.=first.]", "key"},
|
||||||
|
},
|
||||||
|
"multiple '.' in value": {
|
||||||
|
input: "spec.data.[name=f.i.r.s.t.].key",
|
||||||
|
expected: []string{"spec", "data", "[name=f.i.r.s.t.]", "key"},
|
||||||
|
},
|
||||||
|
"with escaped delimiter": {
|
||||||
|
input: `spec\.replicas`,
|
||||||
|
expected: []string{`spec.replicas`},
|
||||||
|
},
|
||||||
|
"unmatched bracket": {
|
||||||
|
input: "spec.data.[name=f.i.[r.s.t..key",
|
||||||
|
expected: []string{"spec", "data", "[name=f.i.[r.s.t..key"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for tn, tc := range testCases {
|
||||||
|
t.Run(tn, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.expected, SmarterPathSplitter(tc.input, "."))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,6 @@
|
|||||||
|
|
||||||
package builtinpluginconsts
|
package builtinpluginconsts
|
||||||
|
|
||||||
// TODO: rename 'fieldSpecs' to 'referrers' for clarity.
|
|
||||||
// This will, however, break anyone using a custom config.
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
nameReferenceFieldSpecs = `
|
nameReferenceFieldSpecs = `
|
||||||
nameReference:
|
nameReference:
|
||||||
|
|||||||
@@ -2,5 +2,6 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// Package konfig provides configuration methods and constants
|
// Package konfig provides configuration methods and constants
|
||||||
// for the kustomize API.
|
// for the kustomize API, e.g. the set of file names to look for
|
||||||
|
// to identify a kustomization root.
|
||||||
package konfig
|
package konfig
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
)
|
)
|
||||||
@@ -18,11 +18,11 @@ func TestDefaultAbsPluginHome_NoKustomizePluginHomeEnv(t *testing.T) {
|
|||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
keep, isSet := os.LookupEnv(KustomizePluginHomeEnv)
|
keep, isSet := os.LookupEnv(KustomizePluginHomeEnv)
|
||||||
if isSet {
|
if isSet {
|
||||||
_ = os.Unsetenv(KustomizePluginHomeEnv)
|
unsetenv(t, KustomizePluginHomeEnv)
|
||||||
}
|
}
|
||||||
_, err := DefaultAbsPluginHome(fSys)
|
_, err := DefaultAbsPluginHome(fSys)
|
||||||
if isSet {
|
if isSet {
|
||||||
os.Setenv(KustomizePluginHomeEnv, keep)
|
setenv(t, KustomizePluginHomeEnv, keep)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected err")
|
t.Fatalf("expected err")
|
||||||
@@ -43,13 +43,13 @@ func TestDefaultAbsPluginHome_NoKustomizePluginHomeEnv(t *testing.T) {
|
|||||||
|
|
||||||
func TestDefaultAbsPluginHome_EmptyKustomizePluginHomeEnv(t *testing.T) {
|
func TestDefaultAbsPluginHome_EmptyKustomizePluginHomeEnv(t *testing.T) {
|
||||||
keep, isSet := os.LookupEnv(KustomizePluginHomeEnv)
|
keep, isSet := os.LookupEnv(KustomizePluginHomeEnv)
|
||||||
os.Setenv(KustomizePluginHomeEnv, "")
|
setenv(t, KustomizePluginHomeEnv, "")
|
||||||
|
|
||||||
_, err := DefaultAbsPluginHome(filesys.MakeFsInMemory())
|
_, err := DefaultAbsPluginHome(filesys.MakeFsInMemory())
|
||||||
if !isSet {
|
if !isSet {
|
||||||
_ = os.Unsetenv(KustomizePluginHomeEnv)
|
unsetenv(t, KustomizePluginHomeEnv)
|
||||||
} else {
|
} else {
|
||||||
_ = os.Setenv(KustomizePluginHomeEnv, keep)
|
setenv(t, KustomizePluginHomeEnv, keep)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected err")
|
t.Fatalf("expected err")
|
||||||
@@ -65,16 +65,15 @@ func TestDefaultAbsPluginHome_WithKustomizePluginHomeEnv(t *testing.T) {
|
|||||||
keep, isSet := os.LookupEnv(KustomizePluginHomeEnv)
|
keep, isSet := os.LookupEnv(KustomizePluginHomeEnv)
|
||||||
if !isSet {
|
if !isSet {
|
||||||
keep = "whatever"
|
keep = "whatever"
|
||||||
os.Setenv(KustomizePluginHomeEnv, keep)
|
setenv(t, KustomizePluginHomeEnv, keep)
|
||||||
}
|
}
|
||||||
fSys.Mkdir(keep)
|
err := fSys.Mkdir(keep)
|
||||||
|
require.NoError(t, err)
|
||||||
h, err := DefaultAbsPluginHome(fSys)
|
h, err := DefaultAbsPluginHome(fSys)
|
||||||
if !isSet {
|
if !isSet {
|
||||||
_ = os.Unsetenv(KustomizePluginHomeEnv)
|
unsetenv(t, KustomizePluginHomeEnv)
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected err: %v", err)
|
|
||||||
}
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
if h != keep {
|
if h != keep {
|
||||||
t.Fatalf("unexpected config dir: %s", h)
|
t.Fatalf("unexpected config dir: %s", h)
|
||||||
}
|
}
|
||||||
@@ -85,13 +84,14 @@ func TestDefaultAbsPluginHomeWithXdg(t *testing.T) {
|
|||||||
keep, isSet := os.LookupEnv(XdgConfigHomeEnv)
|
keep, isSet := os.LookupEnv(XdgConfigHomeEnv)
|
||||||
if !isSet {
|
if !isSet {
|
||||||
keep = "whatever"
|
keep = "whatever"
|
||||||
os.Setenv(XdgConfigHomeEnv, keep)
|
setenv(t, XdgConfigHomeEnv, keep)
|
||||||
}
|
}
|
||||||
configDir := filepath.Join(keep, ProgramName, RelPluginHome)
|
configDir := filepath.Join(keep, ProgramName, RelPluginHome)
|
||||||
fSys.Mkdir(configDir)
|
err := fSys.Mkdir(configDir)
|
||||||
|
require.NoError(t, err)
|
||||||
h, err := DefaultAbsPluginHome(fSys)
|
h, err := DefaultAbsPluginHome(fSys)
|
||||||
if !isSet {
|
if !isSet {
|
||||||
_ = os.Unsetenv(XdgConfigHomeEnv)
|
unsetenv(t, XdgConfigHomeEnv)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err: %v", err)
|
t.Fatalf("unexpected err: %v", err)
|
||||||
@@ -105,11 +105,11 @@ func TestDefaultAbsPluginHomeNoConfig(t *testing.T) {
|
|||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
keep, isSet := os.LookupEnv(XdgConfigHomeEnv)
|
keep, isSet := os.LookupEnv(XdgConfigHomeEnv)
|
||||||
if isSet {
|
if isSet {
|
||||||
_ = os.Unsetenv(XdgConfigHomeEnv)
|
unsetenv(t, XdgConfigHomeEnv)
|
||||||
}
|
}
|
||||||
_, err := DefaultAbsPluginHome(fSys)
|
_, err := DefaultAbsPluginHome(fSys)
|
||||||
if isSet {
|
if isSet {
|
||||||
os.Setenv(XdgConfigHomeEnv, keep)
|
setenv(t, XdgConfigHomeEnv, keep)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected err")
|
t.Fatalf("expected err")
|
||||||
@@ -121,13 +121,13 @@ func TestDefaultAbsPluginHomeNoConfig(t *testing.T) {
|
|||||||
|
|
||||||
func TestDefaultAbsPluginHomeEmptyXdgConfig(t *testing.T) {
|
func TestDefaultAbsPluginHomeEmptyXdgConfig(t *testing.T) {
|
||||||
keep, isSet := os.LookupEnv(XdgConfigHomeEnv)
|
keep, isSet := os.LookupEnv(XdgConfigHomeEnv)
|
||||||
os.Setenv(XdgConfigHomeEnv, "")
|
setenv(t, XdgConfigHomeEnv, "")
|
||||||
if isSet {
|
if isSet {
|
||||||
_ = os.Unsetenv(XdgConfigHomeEnv)
|
unsetenv(t, XdgConfigHomeEnv)
|
||||||
}
|
}
|
||||||
_, err := DefaultAbsPluginHome(filesys.MakeFsInMemory())
|
_, err := DefaultAbsPluginHome(filesys.MakeFsInMemory())
|
||||||
if isSet {
|
if isSet {
|
||||||
os.Setenv(XdgConfigHomeEnv, keep)
|
setenv(t, XdgConfigHomeEnv, keep)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expected err")
|
t.Fatalf("expected err")
|
||||||
@@ -142,14 +142,16 @@ func TestDefaultAbsPluginHomeNoXdgWithDotConfig(t *testing.T) {
|
|||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
configDir := filepath.Join(
|
configDir := filepath.Join(
|
||||||
HomeDir(), XdgConfigHomeEnvDefault, ProgramName, RelPluginHome)
|
HomeDir(), XdgConfigHomeEnvDefault, ProgramName, RelPluginHome)
|
||||||
fSys.Mkdir(configDir)
|
err := fSys.Mkdir(configDir)
|
||||||
|
require.NoError(t, err)
|
||||||
keep, isSet := os.LookupEnv(XdgConfigHomeEnv)
|
keep, isSet := os.LookupEnv(XdgConfigHomeEnv)
|
||||||
if isSet {
|
if isSet {
|
||||||
_ = os.Unsetenv(XdgConfigHomeEnv)
|
unsetenv(t, XdgConfigHomeEnv)
|
||||||
}
|
}
|
||||||
s, _ := DefaultAbsPluginHome(fSys)
|
s, err := DefaultAbsPluginHome(fSys)
|
||||||
|
require.NoError(t, err)
|
||||||
if isSet {
|
if isSet {
|
||||||
os.Setenv(XdgConfigHomeEnv, keep)
|
setenv(t, XdgConfigHomeEnv, keep)
|
||||||
}
|
}
|
||||||
if s != configDir {
|
if s != configDir {
|
||||||
t.Fatalf("unexpected config dir: %s", s)
|
t.Fatalf("unexpected config dir: %s", s)
|
||||||
@@ -160,16 +162,26 @@ func TestDefaultAbsPluginHomeNoXdgJustHomeDir(t *testing.T) {
|
|||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
configDir := filepath.Join(
|
configDir := filepath.Join(
|
||||||
HomeDir(), ProgramName, RelPluginHome)
|
HomeDir(), ProgramName, RelPluginHome)
|
||||||
fSys.Mkdir(configDir)
|
err := fSys.Mkdir(configDir)
|
||||||
|
require.NoError(t, err)
|
||||||
keep, isSet := os.LookupEnv(XdgConfigHomeEnv)
|
keep, isSet := os.LookupEnv(XdgConfigHomeEnv)
|
||||||
if isSet {
|
if isSet {
|
||||||
_ = os.Unsetenv(XdgConfigHomeEnv)
|
unsetenv(t, XdgConfigHomeEnv)
|
||||||
}
|
}
|
||||||
s, _ := DefaultAbsPluginHome(fSys)
|
s, err := DefaultAbsPluginHome(fSys)
|
||||||
|
require.NoError(t, err)
|
||||||
if isSet {
|
if isSet {
|
||||||
os.Setenv(XdgConfigHomeEnv, keep)
|
setenv(t, XdgConfigHomeEnv, keep)
|
||||||
}
|
}
|
||||||
if s != configDir {
|
if s != configDir {
|
||||||
t.Fatalf("unexpected config dir: %s", s)
|
t.Fatalf("unexpected config dir: %s", s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setenv(t *testing.T, key, value string) {
|
||||||
|
require.NoError(t, os.Setenv(key, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
func unsetenv(t *testing.T, key string) {
|
||||||
|
require.NoError(t, os.Unsetenv(key))
|
||||||
|
}
|
||||||
|
|||||||
124
api/krusty/iampolicygenerator_test.go
Normal file
124
api/krusty/iampolicygenerator_test.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package krusty_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGkeGenerator(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeEnhancedHarness(t)
|
||||||
|
defer th.Reset()
|
||||||
|
|
||||||
|
th.WriteK(".", `
|
||||||
|
generators:
|
||||||
|
- |-
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: IAMPolicyGenerator
|
||||||
|
metadata:
|
||||||
|
name: my-gke-generator
|
||||||
|
cloud: gke
|
||||||
|
kubernetesService:
|
||||||
|
name: k8s-sa-name
|
||||||
|
serviceAccount:
|
||||||
|
name: gsa-name
|
||||||
|
projectId: project-id
|
||||||
|
`)
|
||||||
|
expected := `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
iam.gke.io/gcp-service-account: gsa-name@project-id.iam.gserviceaccount.com
|
||||||
|
name: k8s-sa-name
|
||||||
|
`
|
||||||
|
m := th.Run(".", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGkeGeneratorWithNamespace(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeEnhancedHarness(t)
|
||||||
|
defer th.Reset()
|
||||||
|
|
||||||
|
th.WriteK(".", `
|
||||||
|
generators:
|
||||||
|
- |-
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: IAMPolicyGenerator
|
||||||
|
metadata:
|
||||||
|
name: my-gke-generator
|
||||||
|
cloud: gke
|
||||||
|
kubernetesService:
|
||||||
|
namespace: k8s-namespace
|
||||||
|
name: k8s-sa-name
|
||||||
|
serviceAccount:
|
||||||
|
name: gsa-name
|
||||||
|
projectId: project-id
|
||||||
|
`)
|
||||||
|
expected := `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
iam.gke.io/gcp-service-account: gsa-name@project-id.iam.gserviceaccount.com
|
||||||
|
name: k8s-sa-name
|
||||||
|
namespace: k8s-namespace
|
||||||
|
`
|
||||||
|
m := th.Run(".", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGkeGeneratorWithTwo(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeEnhancedHarness(t)
|
||||||
|
defer th.Reset()
|
||||||
|
|
||||||
|
th.WriteK(".", `
|
||||||
|
generators:
|
||||||
|
- gkegenerator1.yaml
|
||||||
|
- gkegenerator2.yaml
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteF("gkegenerator1.yaml", `
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: IAMPolicyGenerator
|
||||||
|
metadata:
|
||||||
|
name: my-gke-generator1
|
||||||
|
cloud: gke
|
||||||
|
kubernetesService:
|
||||||
|
namespace: k8s-namespace-1
|
||||||
|
name: k8s-sa-name-1
|
||||||
|
serviceAccount:
|
||||||
|
name: gsa-name-1
|
||||||
|
projectId: project-id-1
|
||||||
|
`)
|
||||||
|
th.WriteF("gkegenerator2.yaml", `
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: IAMPolicyGenerator
|
||||||
|
metadata:
|
||||||
|
name: my-gke-generator2
|
||||||
|
cloud: gke
|
||||||
|
kubernetesService:
|
||||||
|
name: k8s-sa-name-2
|
||||||
|
serviceAccount:
|
||||||
|
name: gsa-name-2
|
||||||
|
projectId: project-id-2
|
||||||
|
`)
|
||||||
|
expected := `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
iam.gke.io/gcp-service-account: gsa-name-1@project-id-1.iam.gserviceaccount.com
|
||||||
|
name: k8s-sa-name-1
|
||||||
|
namespace: k8s-namespace-1
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
iam.gke.io/gcp-service-account: gsa-name-2@project-id-2.iam.gserviceaccount.com
|
||||||
|
name: k8s-sa-name-2
|
||||||
|
`
|
||||||
|
m := th.Run(".", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, expected)
|
||||||
|
}
|
||||||
@@ -90,19 +90,25 @@ func (b *Kustomizer) Run(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if b.options.DoLegacyResourceSort {
|
if b.options.DoLegacyResourceSort {
|
||||||
builtins.NewLegacyOrderTransformerPlugin().Transform(m)
|
err = builtins.NewLegacyOrderTransformerPlugin().Transform(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if b.options.AddManagedbyLabel {
|
if b.options.AddManagedbyLabel {
|
||||||
t := builtins.LabelTransformerPlugin{
|
t := builtins.LabelTransformerPlugin{
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
konfig.ManagedbyLabelKey: fmt.Sprintf(
|
konfig.ManagedbyLabelKey: fmt.Sprintf("kustomize-%s", provenance.GetProvenance().Semver()),
|
||||||
"kustomize-%s", provenance.GetProvenance().Semver())},
|
},
|
||||||
FieldSpecs: []types.FieldSpec{{
|
FieldSpecs: []types.FieldSpec{{
|
||||||
Path: "metadata/labels",
|
Path: "metadata/labels",
|
||||||
CreateIfNotPresent: true,
|
CreateIfNotPresent: true,
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
t.Transform(m)
|
err = t.Transform(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m.RemoveBuildAnnotations()
|
m.RemoveBuildAnnotations()
|
||||||
return m, nil
|
return m, nil
|
||||||
|
|||||||
@@ -6,9 +6,319 @@ package krusty_test
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestPatchesInOneFile(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeHarness(t)
|
||||||
|
th.WriteK("base", `
|
||||||
|
resources:
|
||||||
|
- namespace.yaml
|
||||||
|
- deployment-controller-manager.yaml
|
||||||
|
- deployment-audit-manager.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("base/namespace.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
control-plane: controller-manager
|
||||||
|
admission.gatekeeper.sh/ignore: no-self-managing
|
||||||
|
name: system
|
||||||
|
`)
|
||||||
|
th.WriteF("base/deployment-controller-manager.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: controller-manager
|
||||||
|
namespace: system
|
||||||
|
labels:
|
||||||
|
control-plane: controller-manager
|
||||||
|
gatekeeper.sh/operation: webhook
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
control-plane: controller-manager
|
||||||
|
gatekeeper.sh/operation: webhook
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
container.seccomp.security.alpha.kubernetes.io/manager: runtime/default
|
||||||
|
labels:
|
||||||
|
control-plane: controller-manager
|
||||||
|
gatekeeper.sh/operation: webhook
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- command:
|
||||||
|
- /manager
|
||||||
|
args:
|
||||||
|
- "--port=8443"
|
||||||
|
- "--logtostderr"
|
||||||
|
- "--exempt-namespace=gatekeeper-system"
|
||||||
|
- "--operation=webhook"
|
||||||
|
image: openpolicyagent/gatekeeper:v3.4.0
|
||||||
|
imagePullPolicy: Always
|
||||||
|
name: manager
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/os: linux
|
||||||
|
priorityClassName: system-cluster-critical
|
||||||
|
`)
|
||||||
|
th.WriteF("base/deployment-audit-manager.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: audit
|
||||||
|
namespace: system
|
||||||
|
labels:
|
||||||
|
control-plane: audit-controller
|
||||||
|
gatekeeper.sh/operation: audit
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
control-plane: audit-controller
|
||||||
|
gatekeeper.sh/operation: audit
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
control-plane: audit-controller
|
||||||
|
gatekeeper.sh/operation: audit
|
||||||
|
annotations:
|
||||||
|
container.seccomp.security.alpha.kubernetes.io/manager: runtime/default
|
||||||
|
spec:
|
||||||
|
automountServiceAccountToken: true
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- --operation=audit
|
||||||
|
- --operation=status
|
||||||
|
- --logtostderr
|
||||||
|
command:
|
||||||
|
- /manager
|
||||||
|
env:
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
- name: POD_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.name
|
||||||
|
image: openpolicyagent/gatekeeper:v3.4.0
|
||||||
|
imagePullPolicy: Always
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 9090
|
||||||
|
name: manager
|
||||||
|
serviceAccountName: gatekeeper-admin
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/os: linux
|
||||||
|
priorityClassName: system-cluster-critical
|
||||||
|
`)
|
||||||
|
const imagePatchAuditManager = `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: audit
|
||||||
|
namespace: system
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: AUDIT_IMAGE
|
||||||
|
name: manager
|
||||||
|
args:
|
||||||
|
- --port=8443
|
||||||
|
- --logtostderr
|
||||||
|
- --emit-admission-events
|
||||||
|
- --exempt-namespace=gatekeeper-system
|
||||||
|
- --operation=webhook
|
||||||
|
- --disable-opa-builtin=http.send
|
||||||
|
`
|
||||||
|
const imagePatchControllerManager = `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: controller-manager
|
||||||
|
namespace: system
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: CONTROLLER_IMAGE
|
||||||
|
name: manager
|
||||||
|
args:
|
||||||
|
- --emit-audit-events
|
||||||
|
- --operation=audit
|
||||||
|
- --operation=status
|
||||||
|
- --logtostderr
|
||||||
|
`
|
||||||
|
th.WriteF(
|
||||||
|
"overlay/image_patch_audit_manager.yaml",
|
||||||
|
imagePatchAuditManager)
|
||||||
|
th.WriteF(
|
||||||
|
"overlay/image_patch_controller_manager.yaml",
|
||||||
|
imagePatchControllerManager)
|
||||||
|
const expected = `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
admission.gatekeeper.sh/ignore: no-self-managing
|
||||||
|
control-plane: controller-manager
|
||||||
|
name: system
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
control-plane: controller-manager
|
||||||
|
gatekeeper.sh/operation: webhook
|
||||||
|
name: controller-manager
|
||||||
|
namespace: system
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
control-plane: controller-manager
|
||||||
|
gatekeeper.sh/operation: webhook
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
container.seccomp.security.alpha.kubernetes.io/manager: runtime/default
|
||||||
|
labels:
|
||||||
|
control-plane: controller-manager
|
||||||
|
gatekeeper.sh/operation: webhook
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- --emit-audit-events
|
||||||
|
- --operation=audit
|
||||||
|
- --operation=status
|
||||||
|
- --logtostderr
|
||||||
|
command:
|
||||||
|
- /manager
|
||||||
|
image: CONTROLLER_IMAGE
|
||||||
|
imagePullPolicy: Always
|
||||||
|
name: manager
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/os: linux
|
||||||
|
priorityClassName: system-cluster-critical
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
control-plane: audit-controller
|
||||||
|
gatekeeper.sh/operation: audit
|
||||||
|
name: audit
|
||||||
|
namespace: system
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
control-plane: audit-controller
|
||||||
|
gatekeeper.sh/operation: audit
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
container.seccomp.security.alpha.kubernetes.io/manager: runtime/default
|
||||||
|
labels:
|
||||||
|
control-plane: audit-controller
|
||||||
|
gatekeeper.sh/operation: audit
|
||||||
|
spec:
|
||||||
|
automountServiceAccountToken: true
|
||||||
|
containers:
|
||||||
|
- args:
|
||||||
|
- --port=8443
|
||||||
|
- --logtostderr
|
||||||
|
- --emit-admission-events
|
||||||
|
- --exempt-namespace=gatekeeper-system
|
||||||
|
- --operation=webhook
|
||||||
|
- --disable-opa-builtin=http.send
|
||||||
|
command:
|
||||||
|
- /manager
|
||||||
|
env:
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
apiVersion: v1
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
- name: POD_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.name
|
||||||
|
image: AUDIT_IMAGE
|
||||||
|
imagePullPolicy: Always
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 9090
|
||||||
|
name: manager
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/os: linux
|
||||||
|
priorityClassName: system-cluster-critical
|
||||||
|
serviceAccountName: gatekeeper-admin
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
|
`
|
||||||
|
// Technique 1: "patchesStrategicMerge:" field, two patch files.
|
||||||
|
th.WriteK("overlay", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- image_patch_controller_manager.yaml
|
||||||
|
- image_patch_audit_manager.yaml
|
||||||
|
`)
|
||||||
|
m := th.Run("overlay", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, expected)
|
||||||
|
|
||||||
|
// Technique 2: "patches:" field, two patch files.
|
||||||
|
th.WriteK("overlay", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patches:
|
||||||
|
- path: image_patch_controller_manager.yaml
|
||||||
|
- path: image_patch_audit_manager.yaml
|
||||||
|
`)
|
||||||
|
m = th.Run("overlay", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, expected)
|
||||||
|
|
||||||
|
// Technique 3: "patchesStrategicMerge:" field, one patch file.
|
||||||
|
th.WriteK("overlay", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- twoPatchesInOneFile.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF(
|
||||||
|
"overlay/twoPatchesInOneFile.yaml",
|
||||||
|
imagePatchAuditManager+"\n---\n"+imagePatchControllerManager)
|
||||||
|
m = th.Run("overlay", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, expected)
|
||||||
|
|
||||||
|
// Technique 4: "patches:" field, one patch file. Fails.
|
||||||
|
th.WriteK("overlay", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patches:
|
||||||
|
- path: twoPatchesInOneFile.yaml
|
||||||
|
`)
|
||||||
|
err := th.RunWithErr("overlay", th.MakeDefaultOptions())
|
||||||
|
assert.Error(t, err)
|
||||||
|
// This should fail, because the semantics of the `patches` field.
|
||||||
|
// That field allows specific patch targeting to a list of targets,
|
||||||
|
// while the `patchesStrategicMerge` field accepts patches that
|
||||||
|
// implicitly identify their targets via GVKN.
|
||||||
|
assert.Contains(t, err.Error(), "unable to parse SM or JSON patch from ")
|
||||||
|
}
|
||||||
|
|
||||||
func TestRemoveEmptyDirWithNullFieldInSmp(t *testing.T) {
|
func TestRemoveEmptyDirWithNullFieldInSmp(t *testing.T) {
|
||||||
th := kusttest_test.MakeHarness(t)
|
th := kusttest_test.MakeHarness(t)
|
||||||
th.WriteK(".", `
|
th.WriteK(".", `
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
ldr "sigs.k8s.io/kustomize/api/loader"
|
ldr "sigs.k8s.io/kustomize/api/loader"
|
||||||
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||||
@@ -83,7 +84,8 @@ func TestKeyValuesFromFileSources(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
fSys.WriteFile("/files/app-init.ini", []byte("FOO=bar"))
|
err := fSys.WriteFile("/files/app-init.ini", []byte("FOO=bar"))
|
||||||
|
require.NoError(t, err)
|
||||||
kvl := makeKvLoader(fSys)
|
kvl := makeKvLoader(fSys)
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
kvs, err := kvl.keyValuesFromFileSources(tc.sources)
|
kvs, err := kvl.keyValuesFromFileSources(tc.sources)
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ import (
|
|||||||
// Factory makes instances of Resource.
|
// Factory makes instances of Resource.
|
||||||
type Factory struct {
|
type Factory struct {
|
||||||
hasher ifc.KustHasher
|
hasher ifc.KustHasher
|
||||||
|
|
||||||
|
// When set to true, IncludeLocalConfigs indicates
|
||||||
|
// that Factory should include resources with the
|
||||||
|
// annotation 'config.kubernetes.io/local-config'.
|
||||||
|
// By default these resources are ignored.
|
||||||
|
IncludeLocalConfigs bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFactory makes an instance of Factory.
|
// NewFactory makes an instance of Factory.
|
||||||
@@ -221,13 +227,15 @@ func (rf *Factory) shouldIgnore(n *yaml.RNode) (bool, error) {
|
|||||||
if n.IsNilOrEmpty() {
|
if n.IsNilOrEmpty() {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
md, err := n.GetValidatedMetadata()
|
if !rf.IncludeLocalConfigs {
|
||||||
if err != nil {
|
md, err := n.GetValidatedMetadata()
|
||||||
return true, err
|
if err != nil {
|
||||||
}
|
return true, err
|
||||||
_, ignore := md.ObjectMeta.Annotations[konfig.IgnoredByKustomizeAnnotation]
|
}
|
||||||
if ignore {
|
_, ignore := md.ObjectMeta.Annotations[konfig.IgnoredByKustomizeAnnotation]
|
||||||
return true, nil
|
if ignore {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if foundNil, path := n.HasNilEntryInList(); foundNil {
|
if foundNil, path := n.HasNilEntryInList(); foundNil {
|
||||||
return true, fmt.Errorf("empty item at %v in object %v", path, n)
|
return true, fmt.Errorf("empty item at %v in object %v", path, n)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func (g *GenArgs) ShouldAddHashSuffixToName() bool {
|
|||||||
|
|
||||||
// Behavior returns Behavior field of GeneratorArgs
|
// Behavior returns Behavior field of GeneratorArgs
|
||||||
func (g *GenArgs) Behavior() GenerationBehavior {
|
func (g *GenArgs) Behavior() GenerationBehavior {
|
||||||
if g.args == nil {
|
if g == nil || g.args == nil {
|
||||||
return BehaviorUnspecified
|
return BehaviorUnspecified
|
||||||
}
|
}
|
||||||
return NewGenerationBehavior(g.args.Behavior)
|
return NewGenerationBehavior(g.args.Behavior)
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ type HelmChart struct {
|
|||||||
// Legal values: 'merge', 'override', 'replace'.
|
// Legal values: 'merge', 'override', 'replace'.
|
||||||
// Defaults to 'override'.
|
// Defaults to 'override'.
|
||||||
ValuesMerge string `json:"valuesMerge,omitempty" yaml:"valuesMerge,omitempty"`
|
ValuesMerge string `json:"valuesMerge,omitempty" yaml:"valuesMerge,omitempty"`
|
||||||
|
|
||||||
|
// IncludeCRDs specifies if Helm should also generate CustomResourceDefinitions.
|
||||||
|
// Defaults to 'false'.
|
||||||
|
IncludeCRDs bool `json:"includeCRDs,omitempty" yaml:"includeCRDs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HelmChartArgs contains arguments to helm.
|
// HelmChartArgs contains arguments to helm.
|
||||||
|
|||||||
36
api/types/iampolicygenerator.go
Normal file
36
api/types/iampolicygenerator.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
type Cloud string
|
||||||
|
|
||||||
|
const GKE Cloud = "gke"
|
||||||
|
|
||||||
|
// IAMPolicyGeneratorArgs contains arguments to generate a GKE service account resource.
|
||||||
|
type IAMPolicyGeneratorArgs struct {
|
||||||
|
// which cloud provider to generate for (e.g. "gke")
|
||||||
|
Cloud `json:"cloud" yaml:"cloud"`
|
||||||
|
|
||||||
|
// information about the kubernetes cluster for this object
|
||||||
|
KubernetesService `json:"kubernetesService" yaml:"kubernetesService"`
|
||||||
|
|
||||||
|
// information about the service account and project
|
||||||
|
ServiceAccount `json:"serviceAccount" yaml:"serviceAccount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type KubernetesService struct {
|
||||||
|
// the name used for the Kubernetes service account
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
|
||||||
|
// the name of the Kubernetes namespace for this object
|
||||||
|
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServiceAccount struct {
|
||||||
|
// the name of the new cloud provider service account
|
||||||
|
Name string `json:"name" yaml:"name"`
|
||||||
|
|
||||||
|
// The ID of the project
|
||||||
|
ProjectId string `json:"projectId" yaml:"projectId"`
|
||||||
|
}
|
||||||
@@ -28,10 +28,10 @@ type SourceSelector struct {
|
|||||||
resid.ResId `json:",inline,omitempty" yaml:",inline,omitempty"`
|
resid.ResId `json:",inline,omitempty" yaml:",inline,omitempty"`
|
||||||
|
|
||||||
// Structured field path expected in the allowed object.
|
// Structured field path expected in the allowed object.
|
||||||
FieldPath string `json:"fieldPath" yaml:"fieldPath"`
|
FieldPath string `json:"fieldPath,omitempty" yaml:"fieldPath,omitempty"`
|
||||||
|
|
||||||
// Used to refine the interpretation of the field.
|
// Used to refine the interpretation of the field.
|
||||||
Options *FieldOptions `json:"options" yaml:"options"`
|
Options *FieldOptions `json:"options,omitempty" yaml:"options,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SourceSelector) String() string {
|
func (s *SourceSelector) String() string {
|
||||||
@@ -54,34 +54,34 @@ type TargetSelector struct {
|
|||||||
Select *Selector `json:"select" yaml:"select"`
|
Select *Selector `json:"select" yaml:"select"`
|
||||||
|
|
||||||
// From the allowed set, remove objects that match this.
|
// From the allowed set, remove objects that match this.
|
||||||
Reject []*Selector `json:"reject" yaml:"reject"`
|
Reject []*Selector `json:"reject,omitempty" yaml:"reject,omitempty"`
|
||||||
|
|
||||||
// Structured field paths expected in each allowed object.
|
// Structured field paths expected in each allowed object.
|
||||||
FieldPaths []string `json:"fieldPaths" yaml:"fieldPaths"`
|
FieldPaths []string `json:"fieldPaths,omitempty" yaml:"fieldPaths,omitempty"`
|
||||||
|
|
||||||
// Used to refine the interpretation of the field.
|
// Used to refine the interpretation of the field.
|
||||||
Options *FieldOptions `json:"options" yaml:"options"`
|
Options *FieldOptions `json:"options,omitempty" yaml:"options,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FieldOptions refine the interpretation of FieldPaths.
|
// FieldOptions refine the interpretation of FieldPaths.
|
||||||
type FieldOptions struct {
|
type FieldOptions struct {
|
||||||
// Used to split/join the field.
|
// Used to split/join the field.
|
||||||
Delimiter string `json:"delimiter" yaml:"delimiter"`
|
Delimiter string `json:"delimiter,omitempty" yaml:"delimiter,omitempty"`
|
||||||
|
|
||||||
// Which position in the split to consider.
|
// Which position in the split to consider.
|
||||||
Index int `json:"index" yaml:"index"`
|
Index int `json:"index,omitempty" yaml:"index,omitempty"`
|
||||||
|
|
||||||
// TODO (#3492): Implement use of this option
|
// TODO (#3492): Implement use of this option
|
||||||
// None, Base64, URL, Hex, etc
|
// None, Base64, URL, Hex, etc
|
||||||
Encoding string `json:"encoding" yaml:"encoding"`
|
Encoding string `json:"encoding,omitempty" yaml:"encoding,omitempty"`
|
||||||
|
|
||||||
// If field missing, add it.
|
// If field missing, add it.
|
||||||
Create bool `json:"create" yaml:"create"`
|
Create bool `json:"create,omitempty" yaml:"create,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fo *FieldOptions) String() string {
|
func (fo *FieldOptions) String() string {
|
||||||
if fo == nil || fo.Delimiter == "" {
|
if fo == nil || (fo.Delimiter == "" && !fo.Create) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s(%d)", fo.Delimiter, fo.Index)
|
return fmt.Sprintf("%s(%d), create=%t", fo.Delimiter, fo.Index, fo.Create)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ type Selector struct {
|
|||||||
LabelSelector string `json:"labelSelector,omitempty" yaml:"labelSelector,omitempty"`
|
LabelSelector string `json:"labelSelector,omitempty" yaml:"labelSelector,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Selector) Copy() Selector {
|
||||||
|
return *s
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Selector) String() string {
|
func (s *Selector) String() string {
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"%s:a=%s:l=%s", s.ResId, s.AnnotationSelector, s.LabelSelector)
|
"%s:a=%s:l=%s", s.ResId, s.AnnotationSelector, s.LabelSelector)
|
||||||
|
|||||||
@@ -17,17 +17,18 @@ func NewCommand() *cobra.Command {
|
|||||||
DisableFlagsInUseLine: true,
|
DisableFlagsInUseLine: true,
|
||||||
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
|
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
|
||||||
Args: cobra.ExactValidArgs(1),
|
Args: cobra.ExactValidArgs(1),
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "bash":
|
case "bash":
|
||||||
cmd.Root().GenBashCompletion(os.Stdout)
|
return cmd.Root().GenBashCompletion(os.Stdout)
|
||||||
case "zsh":
|
case "zsh":
|
||||||
cmd.Root().GenZshCompletion(os.Stdout)
|
return cmd.Root().GenZshCompletion(os.Stdout)
|
||||||
case "fish":
|
case "fish":
|
||||||
cmd.Root().GenFishCompletion(os.Stdout, true)
|
return cmd.Root().GenFishCompletion(os.Stdout, true)
|
||||||
case "powershell":
|
case "powershell":
|
||||||
cmd.Root().GenPowerShellCompletion(os.Stdout)
|
return cmd.Root().GenPowerShellCompletion(os.Stdout)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
# Configuration IO API Semantics
|
|
||||||
|
|
||||||
Resource Configuration may be read / written from / to sources such as directories,
|
|
||||||
stdin|out or network. Tools may be composed using pipes such that the tools writing
|
|
||||||
Resource Configuration may be a different tool from the one that read the configuration.
|
|
||||||
In order for tools to be composed in this way, while preserving origin information --
|
|
||||||
such as the original file, index, etc.:
|
|
||||||
|
|
||||||
Tools **SHOULD** insert the following annotations when reading from sources,
|
|
||||||
and **SHOULD** delete the annotations when writing to sinks.
|
|
||||||
|
|
||||||
### `config.kubernetes.io/path`
|
|
||||||
|
|
||||||
Records the slash-delimited, OS-agnostic, relative file path to a Resource.
|
|
||||||
|
|
||||||
This annotation **SHOULD** be set when reading Resources from files.
|
|
||||||
It **SHOULD** be unset when writing Resources to files.
|
|
||||||
When writing Resources to a directory, the Resource **SHOULD** be written to the corresponding
|
|
||||||
path relative to that directory.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/path: "relative/file/path.yaml"
|
|
||||||
```
|
|
||||||
|
|
||||||
### `config.kubernetes.io/index`
|
|
||||||
|
|
||||||
Records the index of a Resource in file. In a multi-object YAML file, Resources are separated
|
|
||||||
by three dashes (`---`), and the index represents the position of the Resource starting from zero.
|
|
||||||
|
|
||||||
This annotation **SHOULD** be set when reading Resources from files.
|
|
||||||
It **SHOULD** be unset when writing Resources to files.
|
|
||||||
When writing multiple Resources to the same file, the Resource **SHOULD** be written in the
|
|
||||||
relative order matching the index.
|
|
||||||
|
|
||||||
When this annotation is not specified, it implies a value of `0`.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/path: "relative/file/path.yaml"
|
|
||||||
config.kubernetes.io/index: 2
|
|
||||||
```
|
|
||||||
|
|
||||||
This represents the third Resource in the file.
|
|
||||||
|
|
||||||
### `config.kubernetes.io/local-config`
|
|
||||||
|
|
||||||
`config.kubernetes.io/local-config` declares that the configuration is to local tools
|
|
||||||
rather than a remote Resource. e.g. The `Kustomization` config in a `kustomization.yaml`
|
|
||||||
**SHOULD** contain this annotation so that tools know it is not intended to be sent to
|
|
||||||
the Kubernetes api server.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/local-config: "true"
|
|
||||||
```
|
|
||||||
@@ -1,13 +1,18 @@
|
|||||||
# Configuration Functions Specification
|
# KRM Functions Specification
|
||||||
|
|
||||||
|
_apiVersion: v1_
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
This document specifies a standard for client-side functions that operate on
|
This document specifies a standard for client-side functions that operate on
|
||||||
Kubernetes declarative configurations. This standard enables creating
|
Kubernetes declarative configurations referred to as _KRM Functions_. This
|
||||||
small, interoperable, and language-independent executable programs packaged as
|
standard enables creating small, interoperable, and language-independent
|
||||||
containers that can be chained together as part of a configuration management pipeline.
|
executable programs packaged as containers that can be chained together as part
|
||||||
The end result of such a pipeline are fully rendered configurations that can then be
|
of a configuration management pipeline. The end result of such a pipeline are
|
||||||
applied to a control plane (e.g. Using ‘kubectl apply’ for Kubernetes control plane).
|
fully rendered configurations that can then be applied to a control plane (e.g.
|
||||||
As such, although this document references Kubernetes Resource Model and API conventions,
|
Using ‘kubectl apply’ for Kubernetes control plane). As such, although this
|
||||||
it is completely decoupled from Kubernetes API machinery and does not depend on any
|
document references Kubernetes Resource Model and API conventions, it is
|
||||||
|
completely decoupled from Kubernetes API machinery and does not depend on any
|
||||||
in-cluster components.
|
in-cluster components.
|
||||||
|
|
||||||
This document references terms described in [Kubernetes API Conventions][1].
|
This document references terms described in [Kubernetes API Conventions][1].
|
||||||
@@ -18,168 +23,359 @@ interpreted as described in [RFC 2119][2].
|
|||||||
|
|
||||||
## Use Cases
|
## Use Cases
|
||||||
|
|
||||||
_Configuration functions_ enable shift-left practices (client-side) through:
|
KRM functions enable shift-left practices (client-side) through:
|
||||||
|
|
||||||
- Pre-commit / delivery validation and linting of configuration
|
- Pre-commit / delivery validation and linting of configuration
|
||||||
- e.g. Fail if any containers don't have PodSecurityPolicy or CPU / Memory limits
|
- e.g. Fail if any containers don't have PodSecurityPolicy or CPU / Memory
|
||||||
- Implementation of abstractions as client actuated APIs (e.g. templating)
|
limits
|
||||||
- e.g. Create a client-side _"CRD"_ for generating configuration checked into git
|
- Implementation of abstractions as client actuated APIs
|
||||||
- Aspect Orient configuration / Injection of cross-cutting configuration
|
- e.g. Create a client-side _"CRD"_ for generating configuration checked into
|
||||||
- e.g. T-Shirt size containers by annotating Resources with `small`, `medium`, `large`
|
git
|
||||||
and inject the cpu and memory resources into containers accordingly.
|
- Injection of cross-cutting configuration
|
||||||
- e.g. Inject `init` and `side-car` containers into Resources based off of Resource
|
- e.g. T-Shirt size containers by annotating resources with `small`, `medium`,
|
||||||
Type, annotations, etc.
|
`large` and inject the cpu and memory resources into containers accordingly.
|
||||||
|
- e.g. Inject `init` and `side-car` containers into resources based off of
|
||||||
|
resource type, annotations, etc.
|
||||||
|
|
||||||
Performing these on the client rather than the server enables:
|
Performing these on the client rather than the server enables:
|
||||||
|
|
||||||
- Configuration to be reviewed prior to being sent to the API server
|
- Configuration to be reviewed prior to being sent to the API server
|
||||||
- Configuration to be validated as part of the CI/CD pipeline
|
- Configuration to be validated as part of the CI/CD pipeline
|
||||||
- Configuration for Resources to validated holistically rather than individually
|
- Configuration for resources to validated holistically rather than individually
|
||||||
per-Resource
|
per-resource
|
||||||
- e.g. ensure the `Service.selector` and `Deployment.spec.template` labels
|
- e.g. ensure the `Service.selector` and `Deployment.spec.template` labels
|
||||||
match.
|
match.
|
||||||
- e.g. MutatingWebHooks are scoped to a single Resource instance at a time.
|
- e.g. MutatingWebHooks are scoped to a single resource instance at a time.
|
||||||
- Low-level tweaks to the output of high-level abstractions
|
- Low-level tweaks to the output of high-level abstractions
|
||||||
- e.g. add an `init container` to a client _"CRD"_ Resource after it was generated.
|
- e.g. add an `init container` to a client _"CRD"_ resource after it was
|
||||||
|
generated.
|
||||||
- Composition and layering of multiple functions together
|
- Composition and layering of multiple functions together
|
||||||
- Compose generation, injection, validation together
|
- Compose generation, injection, validation together
|
||||||
|
|
||||||
## Spec
|
## Definitions
|
||||||
|
|
||||||
### Input Type
|
- **function:** A containerized program conforming to the spec described in this
|
||||||
|
document.
|
||||||
|
- **orchestrator:** A program that invokes the function container, passing
|
||||||
|
arguments and processing its output.
|
||||||
|
|
||||||
A function MUST accept as input a single [Kubernetes List type][3].
|
## Interface
|
||||||
The `items` field in the input will contain a sequence of [Object types][3].
|
|
||||||
A function MAY not support [Simple types][3] and List types.
|
|
||||||
|
|
||||||
An example using `v1/ConfigMapList` as input:
|
The inter-process communication between the orchestrator and a function works as
|
||||||
|
follows:
|
||||||
|
|
||||||
|
1. Orchestrator runs the function container and provides the input on `stdin`.
|
||||||
|
The input is a Kubernetes object of kind `ResourceList` as described below.
|
||||||
|
2. Function reads the input from `stdin`, performs computations, and provides
|
||||||
|
the output as a `ResourceList` to `stdout`. The function MAY also emit
|
||||||
|
non-structured error message on `stderr`.
|
||||||
|
3. Orchestrator uses the `stdout`, `stderr`, and the exit code of the function
|
||||||
|
as it sees fit following to the semantics described below.
|
||||||
|
|
||||||
|
### Schema
|
||||||
|
|
||||||
|
A function MUST accept input from `stdin` and MUST output to `stdout` a
|
||||||
|
Kubernetes object of kind `ResourceList` with the following OpenAPI schema:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: v1
|
swagger: "2.0"
|
||||||
kind: ConfigMapList
|
info:
|
||||||
items:
|
title: KRM Functions Specification (ResourceList)
|
||||||
- apiVersion: v1
|
version: v1
|
||||||
kind: ConfigMap
|
definitions:
|
||||||
metadata:
|
ResourceList:
|
||||||
name: config1
|
type: object
|
||||||
data:
|
description: ResourceList is the input/output wire format for KRM functions.
|
||||||
p1: v1
|
x-kubernetes-group-version-kind:
|
||||||
p2: v2
|
- group: config.kubernetes.io
|
||||||
- apiVersion: v1
|
kind: ResourceList
|
||||||
kind: ConfigMap
|
version: v1
|
||||||
metadata:
|
- group: config.kubernetes.io
|
||||||
name: config2
|
kind: ResourceList
|
||||||
|
version: v1beta1
|
||||||
|
required:
|
||||||
|
- items
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: apiVersion of ResourceList
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: kind of ResourceList i.e. `ResourceList`
|
||||||
|
type: string
|
||||||
|
items:
|
||||||
|
type: array
|
||||||
|
description: |
|
||||||
|
[input/output]
|
||||||
|
Items is a list of Kubernetes objects:
|
||||||
|
https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds).
|
||||||
|
|
||||||
|
A function will read this field in the input ResourceList and populate
|
||||||
|
this field in the output ResourceList.
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
functionConfig:
|
||||||
|
type: object
|
||||||
|
description: |
|
||||||
|
[input]
|
||||||
|
FunctionConfig is an optional Kubernetes object for passing arguments to a
|
||||||
|
function invocation.
|
||||||
|
results:
|
||||||
|
type: array
|
||||||
|
description: |
|
||||||
|
[output]
|
||||||
|
Results is an optional list that can be used by function to emit results
|
||||||
|
for observability and debugging purposes.
|
||||||
|
items:
|
||||||
|
"$ref": "#/definitions/Result"
|
||||||
|
Result:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- message
|
||||||
|
properties:
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
description: Message is a human readable message.
|
||||||
|
severity:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- error
|
||||||
|
- warning
|
||||||
|
- info
|
||||||
|
default: error
|
||||||
|
description: |
|
||||||
|
Severity is the severity of a result:
|
||||||
|
|
||||||
|
"error": indicates an error result.
|
||||||
|
"warning": indicates a warning result.
|
||||||
|
"info": indicates an informational result.
|
||||||
|
resourceRef:
|
||||||
|
type: object
|
||||||
|
description: |
|
||||||
|
ResourceRef is the metadata for referencing a Kubernetes object
|
||||||
|
associated with a result.
|
||||||
|
required:
|
||||||
|
- apiVersion
|
||||||
|
- kind
|
||||||
|
- name
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description:
|
||||||
|
APIVersion refers to the `apiVersion` field of the object
|
||||||
|
manifest.
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: Kind refers to the `kind` field of the object.
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
description:
|
||||||
|
Namespace refers to the `metadata.namespace` field of the object
|
||||||
|
manifest.
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description:
|
||||||
|
Name refers to the `metadata.name` field of the object manifest.
|
||||||
|
type: string
|
||||||
|
field:
|
||||||
|
type: object
|
||||||
|
description: |
|
||||||
|
Field is the reference to a field in the object.
|
||||||
|
If defined, `ResourceRef` must also be provided.
|
||||||
|
required:
|
||||||
|
- path
|
||||||
|
properties:
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Path is the JSON path of the field
|
||||||
|
e.g. `spec.template.spec.containers[3].resources.limits.cpu`
|
||||||
|
currentValue:
|
||||||
|
description: |
|
||||||
|
CurrrentValue is the current value of the field.
|
||||||
|
Can be any value - string, number, boolean, array or object.
|
||||||
|
proposedValue:
|
||||||
|
description: |
|
||||||
|
PropposedValue is the proposed value of the field to fix an issue.
|
||||||
|
Can be any value - string, number, boolean, array or object.
|
||||||
|
file:
|
||||||
|
type: object
|
||||||
|
description: File references a file containing the resource.
|
||||||
|
required:
|
||||||
|
- path
|
||||||
|
properties:
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Path is the OS agnostic, slash-delimited, relative path.
|
||||||
|
e.g. `some-dir/some-file.yaml`.
|
||||||
|
index:
|
||||||
|
type: number
|
||||||
|
default: 0
|
||||||
|
description: Index of the object in a multi-object YAML file.
|
||||||
|
tags:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
Tags is an unstructured key value map stored with a result that may be set
|
||||||
|
by external tools to store and retrieve arbitrary metadata.
|
||||||
|
paths: {}
|
||||||
```
|
```
|
||||||
|
|
||||||
An example using `v1/List` as input:
|
#### Examples
|
||||||
|
|
||||||
|
The following is an example input, where the custom resource of kind
|
||||||
|
`FulfillmentCenter` is provided as `functionConfig`. The function will operate
|
||||||
|
on one resource of kind `Service`.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: v1
|
apiVersion: config.kubernetes.io/v1
|
||||||
kind: List
|
|
||||||
items:
|
|
||||||
- apiVersion: foo-corp.com/v1
|
|
||||||
kind: FulfillmentCenter
|
|
||||||
metadata:
|
|
||||||
name: staging
|
|
||||||
address: "100 Main St."
|
|
||||||
- apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: ClusterRole
|
|
||||||
metadata:
|
|
||||||
name: namespace-reader
|
|
||||||
rules:
|
|
||||||
- resources:
|
|
||||||
- namespaces
|
|
||||||
apiGroups:
|
|
||||||
- ""
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- watch
|
|
||||||
- list
|
|
||||||
```
|
|
||||||
|
|
||||||
In addition, a function MUST accept as input a List of kind `ResourceList` where the
|
|
||||||
`functionConfig` field, if present, will contain the invocation-specific configuration passed to the function
|
|
||||||
by the orchestrator.
|
|
||||||
Functions MAY consider this field optional so that they can be triggered in an ad-hoc fashion.
|
|
||||||
|
|
||||||
An example using `config.kubernetes.io/v1beta1/ResourceList` as input:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
apiVersion: config.kubernetes.io/v1beta1
|
|
||||||
kind: ResourceList
|
kind: ResourceList
|
||||||
functionConfig:
|
functionConfig:
|
||||||
apiVersion: foo-corp.com/v1
|
apiVersion: foo-corp.com/v1
|
||||||
kind: FulfillmentCenter
|
kind: FulfillmentCenter
|
||||||
metadata:
|
metadata:
|
||||||
name: staging
|
name: staging
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
config.kubernetes.io/function: |
|
|
||||||
container:
|
|
||||||
image: gcr.io/example/foo:v1.0.0
|
|
||||||
spec:
|
spec:
|
||||||
address: "100 Main St."
|
address: "100 Main St."
|
||||||
items:
|
items:
|
||||||
- apiVersion: rbac.authorization.k8s.io/v1
|
- apiVersion: v1
|
||||||
kind: ClusterRole
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: namespace-reader
|
name: wordpress
|
||||||
rules:
|
labels:
|
||||||
- resources:
|
app: wordpress
|
||||||
- namespaces
|
annotations:
|
||||||
apiGroups:
|
config.kubernetes.io/index: "0"
|
||||||
- ""
|
config.kubernetes.io/path: "service.yaml"
|
||||||
verbs:
|
spec: # Example comment
|
||||||
- get
|
type: LoadBalancer
|
||||||
- watch
|
selector:
|
||||||
- list
|
app: wordpress
|
||||||
|
tier: frontend
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 80
|
||||||
```
|
```
|
||||||
|
|
||||||
Here `FulfillmentCenter` kind with name `staging` is passed as the invocation-specific configuration
|
The following is an example output containing one result representing a
|
||||||
to the function.
|
validation error:
|
||||||
|
|
||||||
### Output Type
|
```yaml
|
||||||
|
apiVersion: config.kubernetes.io/v1
|
||||||
A function’s output MUST be the same as the input specification above
|
kind: ResourceList
|
||||||
-- i.e. `ResourceList` or `List`.
|
items:
|
||||||
This is necessary to enable chaining two or more functions together in a pipeline.
|
- apiVersion: v1
|
||||||
The serialization format of the output SHOULD match that of its input on each invocation
|
kind: Service
|
||||||
-- e.g. if the input was a `ResourceList`, the output should also be a `ResourceList`.
|
metadata:
|
||||||
|
name: wordpress
|
||||||
|
labels:
|
||||||
|
app: wordpress
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: "0"
|
||||||
|
config.kubernetes.io/path: "service.yaml"
|
||||||
|
spec: # Example comment
|
||||||
|
type: LoadBalancer
|
||||||
|
selector:
|
||||||
|
app: wordpress
|
||||||
|
tier: frontend
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 80
|
||||||
|
results:
|
||||||
|
- message: "Invalid type. Expected: integer, given: string"
|
||||||
|
severity: error
|
||||||
|
resourceRef:
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
name: wordpress
|
||||||
|
field:
|
||||||
|
path: spec.ports.0.port
|
||||||
|
file:
|
||||||
|
path: service.yaml
|
||||||
|
```
|
||||||
|
|
||||||
### Serialization Format
|
### Serialization Format
|
||||||
|
|
||||||
A function MUST support YAML as a serialization format for the input and output.
|
A function MUST support YAML as a serialization format for the input and output.
|
||||||
A function MUST use utf8 encoding (as YAML is a superset of JSON, JSON will also be supported
|
A function MUST use utf8 encoding (as YAML is a superset of JSON, JSON will also
|
||||||
by any conforming function).
|
be supported by any conforming function).
|
||||||
|
|
||||||
### Operations
|
|
||||||
|
|
||||||
A function MAY Create, Update, or Delete any number of items in the `items` field and output the
|
|
||||||
resultant list.
|
|
||||||
|
|
||||||
A function MAY modify annotations with prefix `config.kubernetes.io`, but must be careful about
|
|
||||||
doing so since they’re used for orchestration purposes and will likely impact subsequent functions
|
|
||||||
in the pipeline.
|
|
||||||
|
|
||||||
A function SHOULD preserve comments when input serialization format is YAML.
|
|
||||||
This allows for human authoring of configuration to coexist with changes made by functions.
|
|
||||||
|
|
||||||
### Containerization
|
### Containerization
|
||||||
|
|
||||||
A function MUST be implemented as a container.
|
A function MUST be implemented as a container.
|
||||||
|
|
||||||
A function container MUST be capable of running as a non-root user if it does not require
|
A function container MUST be capable of running as a non-root user `nobody` if
|
||||||
access to host filesystem or makes network calls.
|
it does not require access to host filesystem.
|
||||||
|
|
||||||
### stdin/stdout/stderr and Exit Codes
|
### stderr
|
||||||
|
|
||||||
A function MUST accept input from stdin and emit output to stdout.
|
Any non-structured error messages MUST be emitted to `stderr`. `stdout` is
|
||||||
|
reserved for `ResourceList` as described above.
|
||||||
|
|
||||||
Any error messages MUST be emitted to stderr.
|
### Exit Code
|
||||||
|
|
||||||
An exit code of zero indicates function execution was successful.
|
An exit code of zero indicates function execution was successful. A non-zero
|
||||||
A non-zero exit code indicates a failure.
|
exit code indicates a failure.
|
||||||
|
|
||||||
[1]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md
|
### Operations
|
||||||
|
|
||||||
|
A function MAY Create, Update, or Delete any number of items in the `items`
|
||||||
|
field and output the resultant list in the corresponding `items` field of the
|
||||||
|
output.
|
||||||
|
|
||||||
|
A function SHOULD preserve comments when input serialization format is YAML.
|
||||||
|
This allows for human authoring of configuration to coexist with changes made by
|
||||||
|
functions.
|
||||||
|
|
||||||
|
### Annotations
|
||||||
|
|
||||||
|
The orchestrator annotates resources in the wire format with annotation prefix
|
||||||
|
`config.kubernetes.io`. These annotations are not persisted when the
|
||||||
|
orchestrator writes the resources to the filesystem. The orchestrator sets this
|
||||||
|
annotation when reading files from the local filesystem and removes the
|
||||||
|
annotation when writing the output of functions back to the filesystem.
|
||||||
|
|
||||||
|
In general, a function MUST NOT modify these annotations except the ones
|
||||||
|
explicitly listed below.
|
||||||
|
|
||||||
|
#### `config.kubernetes.io/path`
|
||||||
|
|
||||||
|
Records the slash-delimited, OS-agnostic, relative file path to a resource. The
|
||||||
|
path is relative to a fix location on the filesystem. Different orchestrator
|
||||||
|
implementations can choose different fixed points.
|
||||||
|
|
||||||
|
A function SHOULD NOT modify this annotation.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: "relative/file/path.yaml"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `config.kubernetes.io/index`
|
||||||
|
|
||||||
|
Records the index of a Resource in file. In a multi-object YAML file, resources
|
||||||
|
are separated by three dashes (`---`), and the index represents the position of
|
||||||
|
the Resource starting from zero. When this annotation is not specified, it
|
||||||
|
implies a value of `0`.
|
||||||
|
|
||||||
|
A function SHOULD NOT modify this annotation.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: "relative/file/path.yaml"
|
||||||
|
config.kubernetes.io/index: 2
|
||||||
|
```
|
||||||
|
|
||||||
|
This represents the third resource in the file.
|
||||||
|
|
||||||
|
[1]:
|
||||||
|
https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md
|
||||||
[2]: https://tools.ietf.org/html/rfc2119
|
[2]: https://tools.ietf.org/html/rfc2119
|
||||||
[3]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
[3]:
|
||||||
|
https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||||
|
|||||||
@@ -20,3 +20,5 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
||||||
|
|
||||||
|
replace sigs.k8s.io/kustomize/kyaml => ../../kyaml
|
||||||
|
|||||||
@@ -244,7 +244,5 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
|
|||||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.10.20 h1:L9JNKvJfCBpmYFr4tP0igpfj/pXP7nW2aXOWNtF5k1g=
|
|
||||||
sigs.k8s.io/kustomize/kyaml v0.10.20/go.mod h1:TYWhGwW9vjoRh3rWqBwB/ZOXyEGRVWe7Ggc3+KZIO+c=
|
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||||
|
|||||||
@@ -104,9 +104,9 @@ func (r *AnnotateRunner) ExecuteCmd(w io.Writer, pkgPath string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// print error message and continue if there are multiple packages to annotate
|
// print error message and continue if there are multiple packages to annotate
|
||||||
fmt.Fprintf(w, "%s\n", err.Error())
|
_, _ = fmt.Fprintf(w, "%s\n", err.Error())
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprint(w, "added annotations in the package\n")
|
_, _ = fmt.Fprint(w, "added annotations in the package\n")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,8 +87,18 @@ metadata:
|
|||||||
expectedFiles: func(d string) map[string]string {
|
expectedFiles: func(d string) map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"deployment.json": `
|
"deployment.json": `
|
||||||
{"apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"name": "foo", annotations: {
|
{
|
||||||
a-string-value: '', a-int-value: '0', a-bool-value: 'false'}}}
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": {
|
||||||
|
"annotations": {
|
||||||
|
"a-bool-value": "false",
|
||||||
|
"a-int-value": "0",
|
||||||
|
"a-string-value": ""
|
||||||
|
},
|
||||||
|
"name": "foo"
|
||||||
|
}
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,23 @@
|
|||||||
## Checking openapi build issues
|
# Troubles with dependencies on openapi
|
||||||
|
|
||||||
|
|
||||||
|
If you see the error
|
||||||
|
|
||||||
|
> cannot use api.Schema.SchemaProps.Properties
|
||||||
|
|
||||||
|
then you have a set of incompatible dependencies. This doc describes the problem and a fix.
|
||||||
|
|
||||||
|
tl;dr A mix of old and new is bad.
|
||||||
|
|
||||||
|
Anyone depending on k8s.io `v0.20.x` or _older_ packages must avoid depending on anything that depends
|
||||||
|
on `k8s.io/kube-openapi` newer than v0.0.0-20210323165736-1a6458611d18.
|
||||||
|
|
||||||
|
> More context in https://github.com/kubernetes/cli-runtime/issues/19
|
||||||
|
|
||||||
|
This dir exists to test the problem.
|
||||||
|
|
||||||
Edit the `main.go` and `go.mod` in this dir to see what builds
|
Edit the `main.go` and `go.mod` in this dir to see what builds
|
||||||
with various combinations of cli-runtime kube-openapi.
|
with various combinations of `cli-runtime` and `kube-openapi`.
|
||||||
|
|
||||||
####
|
####
|
||||||
|
|
||||||
@@ -16,9 +32,9 @@ means that anyone depending on
|
|||||||
and _any other package that imports kube-openapi_ (e.g. kyaml)
|
and _any other package that imports kube-openapi_ (e.g. kyaml)
|
||||||
may see a build error like
|
may see a build error like
|
||||||
|
|
||||||
~/go/pkg/mod/sigs.k8s.io/kustomize@v2.0.3+incompatible/pkg/transformers/config/factorycrd.go:71:47:
|
> ~/go/pkg/mod/sigs.k8s.io/kustomize@v2.0.3+incompatible/pkg/transformers/config/factorycrd.go:71:47:
|
||||||
cannot use api.Schema.SchemaProps.Properties (type map[string]"k8s.io/kube-openapi/pkg/validation/spec".Schema)
|
> cannot use api.Schema.SchemaProps.Properties (type map[string]"k8s.io/kube-openapi/pkg/validation/spec".Schema)
|
||||||
as type myProperties in argument to looksLikeAk8sType
|
> as type myProperties in argument to looksLikeAk8sType
|
||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
|
|
||||||
|
|||||||
@@ -26,14 +26,14 @@ var (
|
|||||||
|
|
||||||
// TODO: make this a PATH-like flag
|
// TODO: make this a PATH-like flag
|
||||||
// e.g.: --excludes ".git:.idea:site:docs"
|
// e.g.: --excludes ".git:.idea:site:docs"
|
||||||
excSlice = []string{
|
exclusions = []string{
|
||||||
".git",
|
".git",
|
||||||
".github",
|
".github",
|
||||||
".idea",
|
".idea",
|
||||||
"docs",
|
"docs",
|
||||||
"examples",
|
"examples",
|
||||||
"hack",
|
"hack",
|
||||||
// "plugin",
|
"plugin",
|
||||||
"releasing",
|
"releasing",
|
||||||
"site",
|
"site",
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,7 @@ func (a *Args) ConditionalModule() misc.ModuleShortName {
|
|||||||
|
|
||||||
func (a *Args) Exclusions() (result []string) {
|
func (a *Args) Exclusions() (result []string) {
|
||||||
// Make sure the list has no repeats.
|
// Make sure the list has no repeats.
|
||||||
for k := range utils.SliceToSet(excSlice) {
|
for k := range utils.SliceToSet(exclusions) {
|
||||||
result = append(result, k)
|
result = append(result, k)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ require (
|
|||||||
github.com/rakyll/statik v0.1.7
|
github.com/rakyll/statik v0.1.7
|
||||||
github.com/spf13/cobra v1.0.0
|
github.com/spf13/cobra v1.0.0
|
||||||
github.com/stretchr/testify v1.5.1
|
github.com/stretchr/testify v1.5.1
|
||||||
sigs.k8s.io/kustomize/api v0.8.9
|
sigs.k8s.io/kustomize/api v0.8.10
|
||||||
sigs.k8s.io/kustomize/kyaml v0.10.20
|
sigs.k8s.io/kustomize/kyaml v0.10.20
|
||||||
)
|
)
|
||||||
|
|
||||||
replace sigs.k8s.io/kustomize/api => ../../api
|
replace sigs.k8s.io/kustomize/api => ../../api
|
||||||
|
|
||||||
|
replace sigs.k8s.io/kustomize/kyaml => ../../kyaml
|
||||||
|
|||||||
@@ -229,8 +229,6 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
|
|||||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.10.20 h1:L9JNKvJfCBpmYFr4tP0igpfj/pXP7nW2aXOWNtF5k1g=
|
|
||||||
sigs.k8s.io/kustomize/kyaml v0.10.20/go.mod h1:TYWhGwW9vjoRh3rWqBwB/ZOXyEGRVWe7Ggc3+KZIO+c=
|
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const (
|
|||||||
|
|
||||||
// ConvertToBuiltInPlugin converts the input plugin file to
|
// ConvertToBuiltInPlugin converts the input plugin file to
|
||||||
// kustomize builtin plugin and writes it to proper directory
|
// kustomize builtin plugin and writes it to proper directory
|
||||||
func ConvertToBuiltInPlugin() error {
|
func ConvertToBuiltInPlugin() (retErr error) {
|
||||||
root, err := inputFileRoot()
|
root, err := inputFileRoot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -47,7 +47,12 @@ func ConvertToBuiltInPlugin() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer w.close()
|
defer func() {
|
||||||
|
closeErr := w.Close()
|
||||||
|
if retErr == nil {
|
||||||
|
retErr = closeErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// This particular phrasing is required.
|
// This particular phrasing is required.
|
||||||
w.write(
|
w.write(
|
||||||
@@ -139,10 +144,10 @@ func makeOutputFileName(root string) string {
|
|||||||
"..", "..", "..", "api", packageForGeneratedCode, root+".go")
|
"..", "..", "..", "api", packageForGeneratedCode, root+".go")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *writer) close() {
|
func (w *writer) Close() error {
|
||||||
// Do this for debugging.
|
// Do this for debugging.
|
||||||
// fmt.Println("Generated " + makeOutputFileName(w.root))
|
// fmt.Println("Generated " + makeOutputFileName(w.root))
|
||||||
w.f.Close()
|
return w.f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *writer) write(line string) {
|
func (w *writer) write(line string) {
|
||||||
|
|||||||
@@ -5,15 +5,15 @@ English | [简体中文](zh/README.md)
|
|||||||
To run these examples, your `$PATH` must contain `kustomize`.
|
To run these examples, your `$PATH` must contain `kustomize`.
|
||||||
See the [installation instructions](../docs/INSTALL.md).
|
See the [installation instructions](../docs/INSTALL.md).
|
||||||
|
|
||||||
These examples are [tested](../scripts/kyaml-pre-commit.sh)
|
These examples are [tested](../hack/testExamplesAgainstKustomize.sh)
|
||||||
to work with the latest _released_ version of kustomize.
|
to work with the latest _released_ version of kustomize.
|
||||||
|
|
||||||
Basic Usage
|
Basic Usage
|
||||||
|
|
||||||
* [valueAdd](valueAdd.md) -
|
* [valueAdd](valueAdd.md) -
|
||||||
Add a simple string value easily to various fields, including
|
Add a simple string value easily to various fields, including
|
||||||
fields that happen to hold file paths.
|
fields that happen to hold file paths.
|
||||||
|
|
||||||
* [configGenerations](configGeneration.md) -
|
* [configGenerations](configGeneration.md) -
|
||||||
Rolling update when ConfigMapGenerator changes.
|
Rolling update when ConfigMapGenerator changes.
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ Define this base the usual way by creating a
|
|||||||
cat <<'EOF' >$DEMO_HOME/base/kustomization.yaml
|
cat <<'EOF' >$DEMO_HOME/base/kustomization.yaml
|
||||||
helmCharts:
|
helmCharts:
|
||||||
- name: minecraft
|
- name: minecraft
|
||||||
|
includeCRDs: false
|
||||||
valuesInline:
|
valuesInline:
|
||||||
minecraftServer:
|
minecraftServer:
|
||||||
eula: true
|
eula: true
|
||||||
@@ -105,6 +106,10 @@ field, specifying a single chart.
|
|||||||
|
|
||||||
The `valuesInline` field overrides some native chart values.
|
The `valuesInline` field overrides some native chart values.
|
||||||
|
|
||||||
|
The `includeCRDs` field instructs Helm to generate
|
||||||
|
`CustomResourceDefinitions`.
|
||||||
|
See [the Helm documentation](https://helm.sh/docs/chart_best_practices/custom_resource_definitions/) for details.
|
||||||
|
|
||||||
Check the directory layout:
|
Check the directory layout:
|
||||||
|
|
||||||
<!-- @tree -->
|
<!-- @tree -->
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
这些示例默认 `kustomize` 在您的 `$PATH` 中。
|
这些示例默认 `kustomize` 在您的 `$PATH` 中。
|
||||||
|
|
||||||
这些示例通过了 [pre-commit](../../scripts/kyaml-pre-commit.sh) 测试,并且应该与 HEAD 一起使用。
|
这些示例通过了 [pre-commit](../../hack/testExamplesAgainstKustomize.sh) 测试,并且应该与 HEAD 一起使用。
|
||||||
|
|
||||||
```
|
```
|
||||||
go get sigs.k8s.io/kustomize/v3/cmd/kustomize
|
go get sigs.k8s.io/kustomize/v3/cmd/kustomize
|
||||||
@@ -58,4 +58,4 @@ go get sigs.k8s.io/kustomize/v3/cmd/kustomize
|
|||||||
|
|
||||||
* [multibases](multibases.md) - 使用相同的 base 生成三个 variants(dev,staging,production)。
|
* [multibases](multibases.md) - 使用相同的 base 生成三个 variants(dev,staging,production)。
|
||||||
|
|
||||||
>声明:部分文档可能稍微滞后于英文版本,同步工作持续进行中
|
>声明:部分文档可能稍微滞后于英文版本,同步工作持续进行中
|
||||||
|
|||||||
@@ -34,9 +34,14 @@ mdrip --mode test --blockTimeOut 15m \
|
|||||||
|
|
||||||
# TODO: make work for non-linux
|
# TODO: make work for non-linux
|
||||||
if onLinuxAndNotOnRemoteCI; then
|
if onLinuxAndNotOnRemoteCI; then
|
||||||
echo "On linux, and not on remote CI. Running expensive tests."
|
if [ "$version" == "HEAD" ]; then
|
||||||
make $MYGOBIN/helmV3
|
echo "On linux, and not on remote CI. Running helm tests."
|
||||||
mdrip --mode test --label testHelm examples/chart.md
|
make $MYGOBIN/helmV3
|
||||||
|
mdrip --mode test --label testHelm examples/chart.md
|
||||||
|
else
|
||||||
|
echo "Skipping helm tests against $version."
|
||||||
|
echo "Helm chart inflator has new features (includeCRD) only in HEAD."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Force outside logic to rebuild kustomize rather than
|
# Force outside logic to rebuild kustomize rather than
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile"
|
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile"
|
||||||
testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
|
testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
|
||||||
@@ -21,7 +22,8 @@ func TestAddBaseHappyPath(t *testing.T) {
|
|||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
bases := strings.Split(baseDirectoryPaths, ",")
|
bases := strings.Split(baseDirectoryPaths, ",")
|
||||||
for _, base := range bases {
|
for _, base := range bases {
|
||||||
fSys.Mkdir(base)
|
err := fSys.Mkdir(base)
|
||||||
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
testutils_test.WriteTestKustomization(fSys)
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
|
||||||
@@ -41,7 +43,8 @@ func TestAddBaseAlreadyThere(t *testing.T) {
|
|||||||
// Create fake directories
|
// Create fake directories
|
||||||
bases := strings.Split(baseDirectoryPaths, ",")
|
bases := strings.Split(baseDirectoryPaths, ",")
|
||||||
for _, base := range bases {
|
for _, base := range bases {
|
||||||
fSys.Mkdir(base)
|
err := fSys.Mkdir(base)
|
||||||
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
testutils_test.WriteTestKustomization(fSys)
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
|
testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
|
||||||
)
|
)
|
||||||
@@ -21,8 +22,10 @@ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
|||||||
|
|
||||||
func TestAddComponentHappyPath(t *testing.T) {
|
func TestAddComponentHappyPath(t *testing.T) {
|
||||||
fSys := filesys.MakeEmptyDirInMemory()
|
fSys := filesys.MakeEmptyDirInMemory()
|
||||||
fSys.WriteFile(componentFileName, []byte(componentFileContent))
|
err := fSys.WriteFile(componentFileName, []byte(componentFileContent))
|
||||||
fSys.WriteFile(componentFileName+"another", []byte(componentFileContent))
|
require.NoError(t, err)
|
||||||
|
err = fSys.WriteFile(componentFileName+"another", []byte(componentFileContent))
|
||||||
|
require.NoError(t, err)
|
||||||
testutils_test.WriteTestKustomization(fSys)
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
|
||||||
cmd := newCmdAddComponent(fSys)
|
cmd := newCmdAddComponent(fSys)
|
||||||
@@ -36,7 +39,8 @@ func TestAddComponentHappyPath(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddComponentAlreadyThere(t *testing.T) {
|
func TestAddComponentAlreadyThere(t *testing.T) {
|
||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
fSys.WriteFile(componentFileName, []byte(componentFileContent))
|
err := fSys.WriteFile(componentFileName, []byte(componentFileContent))
|
||||||
|
require.NoError(t, err)
|
||||||
testutils_test.WriteTestKustomization(fSys)
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
|
||||||
cmd := newCmdAddComponent(fSys)
|
cmd := newCmdAddComponent(fSys)
|
||||||
@@ -49,15 +53,16 @@ func TestAddComponentAlreadyThere(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddKustomizationFileAsComponent(t *testing.T) {
|
func TestAddKustomizationFileAsComponent(t *testing.T) {
|
||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
fSys.WriteFile(componentFileName, []byte(componentFileContent))
|
err := fSys.WriteFile(componentFileName, []byte(componentFileContent))
|
||||||
|
require.NoError(t, err)
|
||||||
testutils_test.WriteTestKustomization(fSys)
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
|
||||||
cmd := newCmdAddComponent(fSys)
|
cmd := newCmdAddComponent(fSys)
|
||||||
args := []string{componentFileName}
|
args := []string{componentFileName}
|
||||||
assert.NoError(t, cmd.RunE(cmd, args))
|
require.NoError(t, cmd.RunE(cmd, args))
|
||||||
|
|
||||||
content, err := testutils_test.ReadTestKustomization(fSys)
|
content, err := testutils_test.ReadTestKustomization(fSys)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.NotContains(t, string(content), componentFileName)
|
assert.NotContains(t, string(content), componentFileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,6 +71,5 @@ func TestAddComponentNoArgs(t *testing.T) {
|
|||||||
|
|
||||||
cmd := newCmdAddComponent(fSys)
|
cmd := newCmdAddComponent(fSys)
|
||||||
err := cmd.Execute()
|
err := cmd.Execute()
|
||||||
assert.Error(t, err)
|
assert.EqualError(t, err, "must specify a component file")
|
||||||
assert.Equal(t, "must specify a component file", err.Error())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
@@ -163,8 +164,9 @@ func TestAddAnnotationForce(t *testing.T) {
|
|||||||
// but trying to add it with --force should
|
// but trying to add it with --force should
|
||||||
v = valtest_test.MakeHappyMapValidator(t)
|
v = valtest_test.MakeHappyMapValidator(t)
|
||||||
cmd = newCmdAddAnnotation(fSys, v.Validator)
|
cmd = newCmdAddAnnotation(fSys, v.Validator)
|
||||||
cmd.Flag("force").Value.Set("true")
|
err = cmd.Flag("force").Value.Set("true")
|
||||||
assert.NoError(t, cmd.RunE(cmd, args))
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, cmd.RunE(cmd, args))
|
||||||
v.VerifyCall()
|
v.VerifyCall()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,7 +268,8 @@ func TestAddLabelForce(t *testing.T) {
|
|||||||
// but trying to add it with --force should
|
// but trying to add it with --force should
|
||||||
v = valtest_test.MakeHappyMapValidator(t)
|
v = valtest_test.MakeHappyMapValidator(t)
|
||||||
cmd = newCmdAddLabel(fSys, v.Validator)
|
cmd = newCmdAddLabel(fSys, v.Validator)
|
||||||
cmd.Flag("force").Value.Set("true")
|
err = cmd.Flag("force").Value.Set("true")
|
||||||
|
require.NoError(t, err)
|
||||||
assert.NoError(t, cmd.RunE(cmd, args))
|
assert.NoError(t, cmd.RunE(cmd, args))
|
||||||
v.VerifyCall()
|
v.VerifyCall()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
|
testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
|
||||||
)
|
)
|
||||||
@@ -28,7 +29,8 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit,
|
|||||||
|
|
||||||
func TestAddPatchWithFilePath(t *testing.T) {
|
func TestAddPatchWithFilePath(t *testing.T) {
|
||||||
fSys := filesys.MakeEmptyDirInMemory()
|
fSys := filesys.MakeEmptyDirInMemory()
|
||||||
fSys.WriteFile(patchFileName, []byte(patchFileContent))
|
err := fSys.WriteFile(patchFileName, []byte(patchFileContent))
|
||||||
|
require.NoError(t, err)
|
||||||
testutils_test.WriteTestKustomization(fSys)
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
|
||||||
cmd := newCmdAddPatch(fSys)
|
cmd := newCmdAddPatch(fSys)
|
||||||
@@ -53,7 +55,8 @@ func TestAddPatchWithFilePath(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddPatchWithPatchContent(t *testing.T) {
|
func TestAddPatchWithPatchContent(t *testing.T) {
|
||||||
fSys := filesys.MakeEmptyDirInMemory()
|
fSys := filesys.MakeEmptyDirInMemory()
|
||||||
fSys.WriteFile(patchFileName, []byte(patchFileContent))
|
err := fSys.WriteFile(patchFileName, []byte(patchFileContent))
|
||||||
|
require.NoError(t, err)
|
||||||
testutils_test.WriteTestKustomization(fSys)
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
|
||||||
cmd := newCmdAddPatch(fSys)
|
cmd := newCmdAddPatch(fSys)
|
||||||
@@ -78,7 +81,8 @@ func TestAddPatchWithPatchContent(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddPatchAlreadyThere(t *testing.T) {
|
func TestAddPatchAlreadyThere(t *testing.T) {
|
||||||
fSys := filesys.MakeEmptyDirInMemory()
|
fSys := filesys.MakeEmptyDirInMemory()
|
||||||
fSys.WriteFile(patchFileName, []byte(patchFileContent))
|
err := fSys.WriteFile(patchFileName, []byte(patchFileContent))
|
||||||
|
require.NoError(t, err)
|
||||||
testutils_test.WriteTestKustomization(fSys)
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
|
||||||
cmd := newCmdAddPatch(fSys)
|
cmd := newCmdAddPatch(fSys)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
|
testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
|
||||||
)
|
)
|
||||||
@@ -21,8 +22,10 @@ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
|||||||
|
|
||||||
func TestAddResourceHappyPath(t *testing.T) {
|
func TestAddResourceHappyPath(t *testing.T) {
|
||||||
fSys := filesys.MakeEmptyDirInMemory()
|
fSys := filesys.MakeEmptyDirInMemory()
|
||||||
fSys.WriteFile(resourceFileName, []byte(resourceFileContent))
|
err := fSys.WriteFile(resourceFileName, []byte(resourceFileContent))
|
||||||
fSys.WriteFile(resourceFileName+"another", []byte(resourceFileContent))
|
require.NoError(t, err)
|
||||||
|
err = fSys.WriteFile(resourceFileName+"another", []byte(resourceFileContent))
|
||||||
|
require.NoError(t, err)
|
||||||
testutils_test.WriteTestKustomization(fSys)
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
|
||||||
cmd := newCmdAddResource(fSys)
|
cmd := newCmdAddResource(fSys)
|
||||||
@@ -36,7 +39,8 @@ func TestAddResourceHappyPath(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddResourceAlreadyThere(t *testing.T) {
|
func TestAddResourceAlreadyThere(t *testing.T) {
|
||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
fSys.WriteFile(resourceFileName, []byte(resourceFileContent))
|
err := fSys.WriteFile(resourceFileName, []byte(resourceFileContent))
|
||||||
|
require.NoError(t, err)
|
||||||
testutils_test.WriteTestKustomization(fSys)
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
|
||||||
cmd := newCmdAddResource(fSys)
|
cmd := newCmdAddResource(fSys)
|
||||||
@@ -49,7 +53,8 @@ func TestAddResourceAlreadyThere(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddKustomizationFileAsResource(t *testing.T) {
|
func TestAddKustomizationFileAsResource(t *testing.T) {
|
||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
fSys.WriteFile(resourceFileName, []byte(resourceFileContent))
|
err := fSys.WriteFile(resourceFileName, []byte(resourceFileContent))
|
||||||
|
require.NoError(t, err)
|
||||||
testutils_test.WriteTestKustomization(fSys)
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
|
||||||
cmd := newCmdAddResource(fSys)
|
cmd := newCmdAddResource(fSys)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile"
|
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile"
|
||||||
testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
|
testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
|
||||||
@@ -22,8 +23,10 @@ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
|||||||
|
|
||||||
func TestAddTransformerHappyPath(t *testing.T) {
|
func TestAddTransformerHappyPath(t *testing.T) {
|
||||||
fSys := filesys.MakeEmptyDirInMemory()
|
fSys := filesys.MakeEmptyDirInMemory()
|
||||||
fSys.WriteFile(transformerFileName, []byte(transformerFileContent))
|
err := fSys.WriteFile(transformerFileName, []byte(transformerFileContent))
|
||||||
fSys.WriteFile(transformerFileName+"another", []byte(transformerFileContent))
|
require.NoError(t, err)
|
||||||
|
err = fSys.WriteFile(transformerFileName+"another", []byte(transformerFileContent))
|
||||||
|
require.NoError(t, err)
|
||||||
testutils_test.WriteTestKustomization(fSys)
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
|
||||||
cmd := newCmdAddTransformer(fSys)
|
cmd := newCmdAddTransformer(fSys)
|
||||||
@@ -37,7 +40,8 @@ func TestAddTransformerHappyPath(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddTransformerAlreadyThere(t *testing.T) {
|
func TestAddTransformerAlreadyThere(t *testing.T) {
|
||||||
fSys := filesys.MakeEmptyDirInMemory()
|
fSys := filesys.MakeEmptyDirInMemory()
|
||||||
fSys.WriteFile(transformerFileName, []byte(transformerFileName))
|
err := fSys.WriteFile(transformerFileName, []byte(transformerFileName))
|
||||||
|
require.NoError(t, err)
|
||||||
testutils_test.WriteTestKustomization(fSys)
|
testutils_test.WriteTestKustomization(fSys)
|
||||||
|
|
||||||
cmd := newCmdAddTransformer(fSys)
|
cmd := newCmdAddTransformer(fSys)
|
||||||
@@ -61,18 +65,18 @@ func TestAddTransformerNoArgs(t *testing.T) {
|
|||||||
|
|
||||||
cmd := newCmdAddTransformer(fSys)
|
cmd := newCmdAddTransformer(fSys)
|
||||||
err := cmd.Execute()
|
err := cmd.Execute()
|
||||||
assert.Error(t, err)
|
assert.EqualError(t, err, "must specify a transformer file")
|
||||||
assert.Equal(t, "must specify a transformer file", err.Error())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddTransformerMissingKustomizationYAML(t *testing.T) {
|
func TestAddTransformerMissingKustomizationYAML(t *testing.T) {
|
||||||
fSys := filesys.MakeEmptyDirInMemory()
|
fSys := filesys.MakeEmptyDirInMemory()
|
||||||
fSys.WriteFile(transformerFileName, []byte(transformerFileContent))
|
err := fSys.WriteFile(transformerFileName, []byte(transformerFileContent))
|
||||||
fSys.WriteFile(transformerFileName+"another", []byte(transformerFileContent))
|
require.NoError(t, err)
|
||||||
|
err = fSys.WriteFile(transformerFileName+"another", []byte(transformerFileContent))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
cmd := newCmdAddTransformer(fSys)
|
cmd := newCmdAddTransformer(fSys)
|
||||||
args := []string{transformerFileName + "*"}
|
args := []string{transformerFileName + "*"}
|
||||||
err := cmd.RunE(cmd, args)
|
err = cmd.RunE(cmd, args)
|
||||||
assert.Error(t, err)
|
assert.EqualError(t, err, "Missing kustomization file 'kustomization.yaml'.\n")
|
||||||
assert.Equal(t, "Missing kustomization file 'kustomization.yaml'.\n", err.Error())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -88,13 +88,17 @@ func TestDataConfigValidation_Flags(t *testing.T) {
|
|||||||
|
|
||||||
func TestExpandFileSource(t *testing.T) {
|
func TestExpandFileSource(t *testing.T) {
|
||||||
fSys := filesys.MakeEmptyDirInMemory()
|
fSys := filesys.MakeEmptyDirInMemory()
|
||||||
fSys.Create("dir/fa1")
|
_, err := fSys.Create("dir/fa1")
|
||||||
fSys.Create("dir/fa2")
|
require.NoError(t, err)
|
||||||
fSys.Create("dir/readme")
|
_, err = fSys.Create("dir/fa2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = fSys.Create("dir/readme")
|
||||||
|
require.NoError(t, err)
|
||||||
fa := flagsAndArgs{
|
fa := flagsAndArgs{
|
||||||
FileSources: []string{"dir/fa*"},
|
FileSources: []string{"dir/fa*"},
|
||||||
}
|
}
|
||||||
fa.ExpandFileSource(fSys)
|
err = fa.ExpandFileSource(fSys)
|
||||||
|
require.NoError(t, err)
|
||||||
expected := []string{
|
expected := []string{
|
||||||
"dir/fa1",
|
"dir/fa1",
|
||||||
"dir/fa2",
|
"dir/fa2",
|
||||||
@@ -106,14 +110,19 @@ func TestExpandFileSource(t *testing.T) {
|
|||||||
|
|
||||||
func TestExpandFileSourceWithKey(t *testing.T) {
|
func TestExpandFileSourceWithKey(t *testing.T) {
|
||||||
fSys := filesys.MakeEmptyDirInMemory()
|
fSys := filesys.MakeEmptyDirInMemory()
|
||||||
fSys.Create("dir/faaaaaaaaaabbbbbbbbbccccccccccccccccc")
|
_, err := fSys.Create("dir/faaaaaaaaaabbbbbbbbbccccccccccccccccc")
|
||||||
fSys.Create("dir/foobar")
|
require.NoError(t, err)
|
||||||
fSys.Create("dir/simplebar")
|
_, err = fSys.Create("dir/foobar")
|
||||||
fSys.Create("dir/readme")
|
require.NoError(t, err)
|
||||||
|
_, err = fSys.Create("dir/simplebar")
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = fSys.Create("dir/readme")
|
||||||
|
require.NoError(t, err)
|
||||||
fa := flagsAndArgs{
|
fa := flagsAndArgs{
|
||||||
FileSources: []string{"foo-key=dir/fa*", "bar-key=dir/foobar", "dir/simplebar"},
|
FileSources: []string{"foo-key=dir/fa*", "bar-key=dir/foobar", "dir/simplebar"},
|
||||||
}
|
}
|
||||||
fa.ExpandFileSource(fSys)
|
err = fa.ExpandFileSource(fSys)
|
||||||
|
require.NoError(t, err)
|
||||||
expected := []string{
|
expected := []string{
|
||||||
"foo-key=dir/faaaaaaaaaabbbbbbbbbccccccccccccccccc",
|
"foo-key=dir/faaaaaaaaaabbbbbbbbbccccccccccccccccc",
|
||||||
"bar-key=dir/foobar",
|
"bar-key=dir/foobar",
|
||||||
@@ -126,13 +135,16 @@ func TestExpandFileSourceWithKey(t *testing.T) {
|
|||||||
|
|
||||||
func TestExpandFileSourceWithKeyAndError(t *testing.T) {
|
func TestExpandFileSourceWithKeyAndError(t *testing.T) {
|
||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
fSys.Create("dir/fa1")
|
_, err := fSys.Create("dir/fa1")
|
||||||
fSys.Create("dir/fa2")
|
require.NoError(t, err)
|
||||||
fSys.Create("dir/readme")
|
_, err = fSys.Create("dir/fa2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = fSys.Create("dir/readme")
|
||||||
|
require.NoError(t, err)
|
||||||
fa := flagsAndArgs{
|
fa := flagsAndArgs{
|
||||||
FileSources: []string{"foo-key=dir/fa*"},
|
FileSources: []string{"foo-key=dir/fa*"},
|
||||||
}
|
}
|
||||||
err := fa.ExpandFileSource(fSys)
|
err = fa.ExpandFileSource(fSys)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("FileSources should not be correctly expanded: %v", fa.FileSources)
|
t.Fatalf("FileSources should not be correctly expanded: %v", fa.FileSources)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
"sigs.k8s.io/kustomize/api/konfig"
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
@@ -125,7 +125,8 @@ configMapGenerator:
|
|||||||
`
|
`
|
||||||
for _, n := range konfig.RecognizedKustomizationFileNames() {
|
for _, n := range konfig.RecognizedKustomizationFileNames() {
|
||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
fSys.WriteFile(n, []byte(kcontent))
|
err := fSys.WriteFile(n, []byte(kcontent))
|
||||||
|
require.NoError(t, err)
|
||||||
k, err := NewKustomizationFile(fSys)
|
k, err := NewKustomizationFile(fSys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected Error: %v", err)
|
t.Fatalf("Unexpected Error: %v", err)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ require (
|
|||||||
github.com/spf13/cobra v1.0.0
|
github.com/spf13/cobra v1.0.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
sigs.k8s.io/kustomize/api v0.8.9
|
sigs.k8s.io/kustomize/api v0.8.10
|
||||||
sigs.k8s.io/kustomize/cmd/config v0.9.12
|
sigs.k8s.io/kustomize/cmd/config v0.9.12
|
||||||
sigs.k8s.io/kustomize/kyaml v0.10.20
|
sigs.k8s.io/kustomize/kyaml v0.10.20
|
||||||
sigs.k8s.io/yaml v1.2.0
|
sigs.k8s.io/yaml v1.2.0
|
||||||
@@ -20,3 +20,7 @@ exclude (
|
|||||||
)
|
)
|
||||||
|
|
||||||
replace sigs.k8s.io/kustomize/api => ../api
|
replace sigs.k8s.io/kustomize/api => ../api
|
||||||
|
|
||||||
|
replace sigs.k8s.io/kustomize/cmd/config => ../cmd/config
|
||||||
|
|
||||||
|
replace sigs.k8s.io/kustomize/kyaml => ../kyaml
|
||||||
|
|||||||
@@ -253,10 +253,6 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
|
|||||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||||
sigs.k8s.io/kustomize/cmd/config v0.9.12 h1:hDLY4mxEqDLVSqxlJoamZZEhOM6TBysYR2wDlmW+iEo=
|
|
||||||
sigs.k8s.io/kustomize/cmd/config v0.9.12/go.mod h1:hVG/nPSqccrnogZ4ehzw3cTSR2fT6hdWeyojILHZivA=
|
|
||||||
sigs.k8s.io/kustomize/kyaml v0.10.20 h1:L9JNKvJfCBpmYFr4tP0igpfj/pXP7nW2aXOWNtF5k1g=
|
|
||||||
sigs.k8s.io/kustomize/kyaml v0.10.20/go.mod h1:TYWhGwW9vjoRh3rWqBwB/ZOXyEGRVWe7Ggc3+KZIO+c=
|
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||||
|
|||||||
@@ -54,7 +54,10 @@ func (c *copier) VisitList(s walk.Sources, _ *openapi.ResourceSchema, _ walk.Lis
|
|||||||
origin := originItems[i]
|
origin := originItems[i]
|
||||||
|
|
||||||
if dest.Value == origin.Value {
|
if dest.Value == origin.Value {
|
||||||
copy(yaml.NewRNode(dest), yaml.NewRNode(origin))
|
// We copy the comments recursively on each node in the list.
|
||||||
|
if err := CopyComments(yaml.NewRNode(dest), yaml.NewRNode(origin)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,48 @@ spec:
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "associative_list_2",
|
||||||
|
from: `
|
||||||
|
apiVersion: constraints.gatekeeper.sh/v1beta1
|
||||||
|
kind: EnforceFoo
|
||||||
|
metadata:
|
||||||
|
name: enforce-foo
|
||||||
|
spec:
|
||||||
|
parameters:
|
||||||
|
naming_rules:
|
||||||
|
- kind: Bar
|
||||||
|
patterns:
|
||||||
|
# comment 1
|
||||||
|
- ^(dev|prod|staging|qa|shared)$
|
||||||
|
`,
|
||||||
|
to: `
|
||||||
|
apiVersion: constraints.gatekeeper.sh/v1beta1
|
||||||
|
kind: EnforceFoo
|
||||||
|
metadata:
|
||||||
|
name: enforce-foo
|
||||||
|
spec:
|
||||||
|
parameters:
|
||||||
|
naming_rules:
|
||||||
|
- kind: Bar
|
||||||
|
patterns:
|
||||||
|
- ^(dev|prod|staging|qa|shared)$
|
||||||
|
`,
|
||||||
|
expected: `
|
||||||
|
apiVersion: constraints.gatekeeper.sh/v1beta1
|
||||||
|
kind: EnforceFoo
|
||||||
|
metadata:
|
||||||
|
name: enforce-foo
|
||||||
|
spec:
|
||||||
|
parameters:
|
||||||
|
naming_rules:
|
||||||
|
- kind: Bar
|
||||||
|
patterns:
|
||||||
|
# comment 1
|
||||||
|
- ^(dev|prod|staging|qa|shared)$
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "keep_comments",
|
name: "keep_comments",
|
||||||
from: `# A
|
from: `# A
|
||||||
|
|||||||
@@ -320,9 +320,12 @@ func ExampleBuild_validate() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
validationResults = append(validationResults, framework.ResultItem{
|
validationResults = append(validationResults, framework.ResultItem{
|
||||||
Severity: framework.Error,
|
Severity: framework.Error,
|
||||||
Message: "field is required",
|
Message: "field is required",
|
||||||
ResourceRef: meta,
|
ResourceRef: yaml.ResourceIdentifier{
|
||||||
|
TypeMeta: meta.TypeMeta,
|
||||||
|
NameMeta: meta.ObjectMeta.NameMeta,
|
||||||
|
},
|
||||||
Field: framework.Field{
|
Field: framework.Field{
|
||||||
Path: "spec.replicas",
|
Path: "spec.replicas",
|
||||||
SuggestedValue: "1",
|
SuggestedValue: "1",
|
||||||
@@ -374,8 +377,7 @@ items:
|
|||||||
// resourceRef:
|
// resourceRef:
|
||||||
// apiVersion: apps/v1
|
// apiVersion: apps/v1
|
||||||
// kind: Deployment
|
// kind: Deployment
|
||||||
// metadata:
|
// name: foo
|
||||||
// name: foo
|
|
||||||
// field:
|
// field:
|
||||||
// path: spec.replicas
|
// path: spec.replicas
|
||||||
// suggestedValue: "1"
|
// suggestedValue: "1"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/fn/framework"
|
"sigs.k8s.io/kustomize/kyaml/fn/framework"
|
||||||
@@ -962,10 +963,27 @@ func (a *v1alpha1JavaSpringBoot) Default() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *v1alpha1JavaSpringBoot) Validate() error {
|
func (a *v1alpha1JavaSpringBoot) Validate() error {
|
||||||
|
var messages []string
|
||||||
if a.Metadata.Name == "" {
|
if a.Metadata.Name == "" {
|
||||||
return errors.Errorf("Name is required")
|
messages = append(messages, "name is required")
|
||||||
}
|
}
|
||||||
return nil
|
if a.Spec.Replicas > 10 {
|
||||||
|
messages = append(messages, "replicas must be less than 10")
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(a.Spec.Domain, "example.com") {
|
||||||
|
messages = append(messages, "domain must be a subdomain of example.com")
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(a.Spec.Image, ":latest") {
|
||||||
|
messages = append(messages, "image should not have latest tag")
|
||||||
|
}
|
||||||
|
if len(messages) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
errMsg := fmt.Sprintf("JavaSpringBoot had %d errors:\n", len(messages))
|
||||||
|
for i, msg := range messages {
|
||||||
|
errMsg += fmt.Sprintf(" [%d] %s\n", i+1, msg)
|
||||||
|
}
|
||||||
|
return errors.Errorf(errMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExampleVersionedAPIProcessor shows how to use the VersionedAPIProcessor and TemplateProcessor to
|
// ExampleVersionedAPIProcessor shows how to use the VersionedAPIProcessor and TemplateProcessor to
|
||||||
|
|||||||
@@ -18,25 +18,29 @@ func TestExecute_Result(t *testing.T) {
|
|||||||
p := framework.ResourceListProcessorFunc(func(rl *framework.ResourceList) error {
|
p := framework.ResourceListProcessorFunc(func(rl *framework.ResourceList) error {
|
||||||
err := &framework.Result{
|
err := &framework.Result{
|
||||||
Name: "Incompatible config",
|
Name: "Incompatible config",
|
||||||
Items: []framework.ResultItem{{
|
Items: []framework.ResultItem{
|
||||||
Message: "bad value for replicas",
|
{
|
||||||
Severity: framework.Error,
|
Message: "bad value for replicas",
|
||||||
ResourceRef: yaml.ResourceMeta{
|
Severity: framework.Error,
|
||||||
TypeMeta: yaml.TypeMeta{APIVersion: "v1", Kind: "Deployment"},
|
ResourceRef: yaml.ResourceIdentifier{
|
||||||
ObjectMeta: yaml.ObjectMeta{
|
TypeMeta: yaml.TypeMeta{APIVersion: "v1", Kind: "Deployment"},
|
||||||
NameMeta: yaml.NameMeta{Name: "tester", Namespace: "default"},
|
NameMeta: yaml.NameMeta{Name: "tester", Namespace: "default"},
|
||||||
},
|
},
|
||||||
|
Field: framework.Field{
|
||||||
|
Path: ".spec.Replicas",
|
||||||
|
CurrentValue: "0",
|
||||||
|
SuggestedValue: "3",
|
||||||
|
},
|
||||||
|
File: framework.File{
|
||||||
|
Path: "/path/to/deployment.yaml",
|
||||||
|
Index: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Field: framework.Field{
|
{
|
||||||
Path: ".spec.Replicas",
|
Message: "some error",
|
||||||
CurrentValue: "0",
|
Severity: framework.Error,
|
||||||
SuggestedValue: "3",
|
|
||||||
},
|
},
|
||||||
File: framework.File{
|
},
|
||||||
Path: "/path/to/deployment.yaml",
|
|
||||||
Index: 0,
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}
|
}
|
||||||
rl.Result = err
|
rl.Result = err
|
||||||
return err
|
return err
|
||||||
@@ -55,8 +59,9 @@ items:
|
|||||||
replicas: 0
|
replicas: 0
|
||||||
`), Writer: out}
|
`), Writer: out}
|
||||||
err := framework.Execute(p, source)
|
err := framework.Execute(p, source)
|
||||||
assert.EqualError(t, err, "[error] v1/Deployment/default/tester .spec."+
|
assert.EqualError(t, err, `[error] v1/Deployment/default/tester .spec.Replicas: bad value for replicas
|
||||||
"Replicas: bad value for replicas")
|
|
||||||
|
[error] : some error`)
|
||||||
assert.Equal(t, 1, err.(*framework.Result).ExitCode())
|
assert.Equal(t, 1, err.(*framework.Result).ExitCode())
|
||||||
assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
kind: ResourceList
|
kind: ResourceList
|
||||||
@@ -76,13 +81,14 @@ results:
|
|||||||
resourceRef:
|
resourceRef:
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
name: tester
|
||||||
name: tester
|
namespace: default
|
||||||
namespace: default
|
|
||||||
field:
|
field:
|
||||||
path: .spec.Replicas
|
path: .spec.Replicas
|
||||||
currentValue: "0"
|
currentValue: "0"
|
||||||
suggestedValue: "3"
|
suggestedValue: "3"
|
||||||
file:
|
file:
|
||||||
path: /path/to/deployment.yaml`, strings.TrimSpace(out.String()))
|
path: /path/to/deployment.yaml
|
||||||
|
- message: some error
|
||||||
|
severity: error`, strings.TrimSpace(out.String()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,27 +9,60 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/fn/framework"
|
"sigs.k8s.io/kustomize/kyaml/fn/framework"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CommandResultsChecker tests a function by running it with predefined inputs and comparing
|
const (
|
||||||
// the outputs to expected results.
|
DefaultTestDataDirectory = "testdata"
|
||||||
|
DefaultConfigInputFilename = "config.yaml"
|
||||||
|
DefaultInputFilename = "input.yaml"
|
||||||
|
DefaultInputFilenameGlob = "input*.yaml"
|
||||||
|
DefaultOutputFilename = "expected.yaml"
|
||||||
|
DefaultErrorFilename = "errors.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CommandResultsChecker tests a command-wrapped function by running it with predefined inputs
|
||||||
|
// and comparing the outputs to expected results.
|
||||||
type CommandResultsChecker struct {
|
type CommandResultsChecker struct {
|
||||||
// TestDataDirectory is the directory containing the testdata subdirectories.
|
// TestDataDirectory is the directory containing the testdata subdirectories.
|
||||||
// CommandResultsChecker will recurse into each test directory and run the Command
|
// CommandResultsChecker will recurse into each test directory and run the Command
|
||||||
// if the directory contains both the ConfigInputFilename and at least one
|
// if the directory contains at least one of ExpectedOutputFilename or ExpectedErrorFilename.
|
||||||
// of ExpectedOutputFilname or ExpectedErrorFilename.
|
|
||||||
// Defaults to "testdata"
|
// Defaults to "testdata"
|
||||||
TestDataDirectory string
|
TestDataDirectory string
|
||||||
|
|
||||||
|
// ExpectedOutputFilename is the file with the expected output of the function
|
||||||
|
// Defaults to "expected.yaml". Directories containing neither this file
|
||||||
|
// nor ExpectedErrorFilename will be skipped.
|
||||||
|
ExpectedOutputFilename string
|
||||||
|
|
||||||
|
// ExpectedErrorFilename is the file containing elements of an expected error message.
|
||||||
|
// Each line of the file will be treated as a regex that must match the actual error.
|
||||||
|
// Defaults to "errors.txt". Directories containing neither this file
|
||||||
|
// nor ExpectedOutputFilename will be skipped.
|
||||||
|
ExpectedErrorFilename string
|
||||||
|
|
||||||
|
// UpdateExpectedFromActual if set to true will write the actual results to the
|
||||||
|
// expected testdata files. This is useful for updating test data.
|
||||||
|
UpdateExpectedFromActual bool
|
||||||
|
|
||||||
|
// OutputAssertionFunc allows you to swap out the logic used to compare the expected output
|
||||||
|
// from the fixture file to the actual output.
|
||||||
|
// By default, it performs a string comparison after normalizing whitespace.
|
||||||
|
OutputAssertionFunc AssertionFunc
|
||||||
|
|
||||||
|
// ErrorAssertionFunc allows you to swap out the logic used to compare the expected error
|
||||||
|
// message from the fixture file to the actual error message.
|
||||||
|
// By default, it interprets each line of the fixture as a regex that the actual error must match.
|
||||||
|
ErrorAssertionFunc AssertionFunc
|
||||||
|
|
||||||
// ConfigInputFilename is the name of the config file provided as the first
|
// ConfigInputFilename is the name of the config file provided as the first
|
||||||
// argument to the function. Directories without this file will be skipped.
|
// argument to the function. Directories without this file will be skipped.
|
||||||
// Defaults to "config.yaml"
|
// Defaults to "config.yaml"
|
||||||
@@ -39,226 +72,114 @@ type CommandResultsChecker struct {
|
|||||||
// Defaults to "input*.yaml"
|
// Defaults to "input*.yaml"
|
||||||
InputFilenameGlob string
|
InputFilenameGlob string
|
||||||
|
|
||||||
// ExpectedOutputFilename is the file with the expected output of the function
|
|
||||||
// Defaults to "expected.yaml". Directories containing neither this file
|
|
||||||
// nor ExpectedErrorFilename will be skipped.
|
|
||||||
ExpectedOutputFilename string
|
|
||||||
|
|
||||||
// ExpectedErrorFilename is the file containing part of an expected error message
|
|
||||||
// Defaults to "error.yaml". Directories containing neither this file
|
|
||||||
// nor ExpectedOutputFilename will be skipped.
|
|
||||||
ExpectedErrorFilename string
|
|
||||||
|
|
||||||
// Command provides the function to run.
|
// Command provides the function to run.
|
||||||
Command func() *cobra.Command
|
Command func() *cobra.Command
|
||||||
|
|
||||||
// UpdateExpectedFromActual if set to true will write the actual results to the
|
|
||||||
// expected testdata files. This is useful for updating test data.
|
|
||||||
UpdateExpectedFromActual bool
|
|
||||||
|
|
||||||
testsCasesRun int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assert asserts the results for functions
|
// Assert runs the command with the input provided in each valid test directory
|
||||||
|
// and verifies that the actual output and error match the fixtures in the directory.
|
||||||
func (rc *CommandResultsChecker) Assert(t *testing.T) bool {
|
func (rc *CommandResultsChecker) Assert(t *testing.T) bool {
|
||||||
if rc.TestDataDirectory == "" {
|
|
||||||
rc.TestDataDirectory = "testdata"
|
|
||||||
}
|
|
||||||
if rc.ConfigInputFilename == "" {
|
if rc.ConfigInputFilename == "" {
|
||||||
rc.ConfigInputFilename = "config.yaml"
|
rc.ConfigInputFilename = DefaultConfigInputFilename
|
||||||
}
|
|
||||||
if rc.ExpectedOutputFilename == "" {
|
|
||||||
rc.ExpectedOutputFilename = "expected.yaml"
|
|
||||||
}
|
|
||||||
if rc.ExpectedErrorFilename == "" {
|
|
||||||
rc.ExpectedErrorFilename = "error.yaml"
|
|
||||||
}
|
}
|
||||||
if rc.InputFilenameGlob == "" {
|
if rc.InputFilenameGlob == "" {
|
||||||
rc.InputFilenameGlob = "input*.yaml"
|
rc.InputFilenameGlob = DefaultInputFilenameGlob
|
||||||
}
|
}
|
||||||
|
|
||||||
err := filepath.Walk(rc.TestDataDirectory, func(path string, info os.FileInfo, err error) error {
|
checker := newResultsChecker(
|
||||||
require.NoError(t, err)
|
rc.TestDataDirectory, rc.ExpectedOutputFilename, rc.ExpectedErrorFilename,
|
||||||
if !info.IsDir() {
|
rc.OutputAssertionFunc, rc.ErrorAssertionFunc,
|
||||||
// skip non-directories
|
rc.UpdateExpectedFromActual,
|
||||||
return nil
|
)
|
||||||
|
checker.assert(t, func() (string, string) {
|
||||||
|
_, err := os.Stat(rc.ConfigInputFilename)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
t.Errorf("Test case is missing FunctionConfig input file (default: %s)", DefaultConfigInputFilename)
|
||||||
}
|
}
|
||||||
rc.compare(t, path)
|
require.NoError(t, err)
|
||||||
return nil
|
args := []string{rc.ConfigInputFilename}
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.NotZero(t, rc.testsCasesRun, "No complete test cases found in %s", rc.TestDataDirectory)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rc *CommandResultsChecker) compare(t *testing.T, path string) {
|
|
||||||
// cd into the directory so we can test functions that refer
|
|
||||||
// local files by relative paths
|
|
||||||
d, err := os.Getwd()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
defer func() { require.NoError(t, os.Chdir(d)) }()
|
|
||||||
require.NoError(t, os.Chdir(path))
|
|
||||||
|
|
||||||
// make sure this directory contains test data
|
|
||||||
_, err = os.Stat(rc.ConfigInputFilename)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
// missing input
|
|
||||||
return
|
|
||||||
}
|
|
||||||
args := []string{rc.ConfigInputFilename}
|
|
||||||
|
|
||||||
expectedOutput, expectedError := getExpected(t, rc.ExpectedOutputFilename, rc.ExpectedErrorFilename)
|
|
||||||
if expectedError == "" && expectedOutput == "" && !rc.UpdateExpectedFromActual {
|
|
||||||
// missing expected and UpdateExpectedFromActual == false, return and skip this test
|
|
||||||
return
|
|
||||||
}
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// run the test
|
|
||||||
t.Run(path, func(t *testing.T) {
|
|
||||||
rc.testsCasesRun += 1
|
|
||||||
if rc.InputFilenameGlob != "" {
|
if rc.InputFilenameGlob != "" {
|
||||||
inputs, err := filepath.Glob(rc.InputFilenameGlob)
|
inputs, err := filepath.Glob(rc.InputFilenameGlob)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
args = append(args, inputs...)
|
args = append(args, inputs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
var actualOutput, actualError bytes.Buffer
|
var stdOut, stdErr bytes.Buffer
|
||||||
cmd := rc.Command()
|
cmd := rc.Command()
|
||||||
cmd.SetArgs(args)
|
cmd.SetArgs(args)
|
||||||
cmd.SetOut(&actualOutput)
|
cmd.SetOut(&stdOut)
|
||||||
cmd.SetErr(&actualError)
|
cmd.SetErr(&stdErr)
|
||||||
|
|
||||||
err = cmd.Execute()
|
err = cmd.Execute()
|
||||||
|
return stdOut.String(), stdErr.String()
|
||||||
// Update the fixtures if configured to
|
|
||||||
if rc.UpdateExpectedFromActual {
|
|
||||||
if actualError.String() != "" {
|
|
||||||
assert.NoError(t, ioutil.WriteFile(rc.ExpectedErrorFilename, actualError.Bytes(), 0600))
|
|
||||||
}
|
|
||||||
if actualOutput.String() != "" {
|
|
||||||
assert.NoError(t, ioutil.WriteFile(rc.ExpectedOutputFilename, actualOutput.Bytes(), 0600))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare the results
|
|
||||||
if expectedError != "" {
|
|
||||||
// We expected an error, so make sure there was one and it matches
|
|
||||||
require.Error(t, err, actualOutput.String())
|
|
||||||
require.Contains(t,
|
|
||||||
standardizeSpacing(actualError.String()),
|
|
||||||
standardizeSpacing(expectedError), actualOutput.String())
|
|
||||||
} else {
|
|
||||||
// We didn't expect an error, and the output should match
|
|
||||||
require.NoError(t, err, actualError.String())
|
|
||||||
require.Equal(t,
|
|
||||||
standardizeSpacing(expectedOutput),
|
|
||||||
standardizeSpacing(actualOutput.String()), actualError.String())
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func standardizeSpacing(s string) string {
|
// ProcessorResultsChecker tests a processor function by running it with predefined inputs
|
||||||
// remove extra whitespace and convert Windows line endings
|
// and comparing the outputs to expected results.
|
||||||
return strings.ReplaceAll(strings.TrimSpace(s), "\r\n", "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProcessorResultsChecker tests a function by running it with predefined inputs and comparing
|
|
||||||
// the outputs to expected results.
|
|
||||||
type ProcessorResultsChecker struct {
|
type ProcessorResultsChecker struct {
|
||||||
// TestDataDirectory is the directory containing the testdata subdirectories.
|
// TestDataDirectory is the directory containing the testdata subdirectories.
|
||||||
// CommandResultsChecker will recurse into each test directory and run the Processor
|
// ProcessorResultsChecker will recurse into each test directory and run the Command
|
||||||
// if the directory contains both the InputFilename and at least one
|
// if the directory contains at least one of ExpectedOutputFilename or ExpectedErrorFilename.
|
||||||
// of ExpectedOutputFilename or ExpectedErrorFilename.
|
|
||||||
// Defaults to "testdata"
|
// Defaults to "testdata"
|
||||||
TestDataDirectory string
|
TestDataDirectory string
|
||||||
|
|
||||||
// InputFilename is the name of the file containing the ResourceList input.
|
|
||||||
// Directories without this file will be skipped.
|
|
||||||
// Defaults to "input.yaml"
|
|
||||||
InputFilename string
|
|
||||||
|
|
||||||
// ExpectedOutputFilename is the file with the expected output of the function
|
// ExpectedOutputFilename is the file with the expected output of the function
|
||||||
// Defaults to "expected.yaml". Directories containing neither this file
|
// Defaults to "expected.yaml". Directories containing neither this file
|
||||||
// nor ExpectedErrorFilename will be skipped.
|
// nor ExpectedErrorFilename will be skipped.
|
||||||
ExpectedOutputFilename string
|
ExpectedOutputFilename string
|
||||||
|
|
||||||
// ExpectedErrorFilename is the file containing part of an expected error message
|
// ExpectedErrorFilename is the file containing elements of an expected error message.
|
||||||
// Defaults to "error.yaml". Directories containing neither this file
|
// Each line of the file will be treated as a regex that must match the actual error.
|
||||||
|
// Defaults to "errors.txt". Directories containing neither this file
|
||||||
// nor ExpectedOutputFilename will be skipped.
|
// nor ExpectedOutputFilename will be skipped.
|
||||||
ExpectedErrorFilename string
|
ExpectedErrorFilename string
|
||||||
|
|
||||||
// Processor returns a ResourceListProcessor to run.
|
|
||||||
Processor func() framework.ResourceListProcessor
|
|
||||||
|
|
||||||
// UpdateExpectedFromActual if set to true will write the actual results to the
|
// UpdateExpectedFromActual if set to true will write the actual results to the
|
||||||
// expected testdata files. This is useful for updating test data.
|
// expected testdata files. This is useful for updating test data.
|
||||||
UpdateExpectedFromActual bool
|
UpdateExpectedFromActual bool
|
||||||
|
|
||||||
testsCasesRun int
|
// InputFilename is the name of the file containing the ResourceList input.
|
||||||
|
// Directories without this file will be skipped.
|
||||||
|
// Defaults to "input.yaml"
|
||||||
|
InputFilename string
|
||||||
|
|
||||||
|
// OutputAssertionFunc allows you to swap out the logic used to compare the expected output
|
||||||
|
// from the fixture file to the actual output.
|
||||||
|
// By default, it performs a string comparison after normalizing whitespace.
|
||||||
|
OutputAssertionFunc AssertionFunc
|
||||||
|
|
||||||
|
// ErrorAssertionFunc allows you to swap out the logic used to compare the expected error
|
||||||
|
// message from the fixture file to the actual error message.
|
||||||
|
// By default, it interprets each line of the fixture as a regex that the actual error must match.
|
||||||
|
ErrorAssertionFunc AssertionFunc
|
||||||
|
|
||||||
|
// Processor returns a ResourceListProcessor to run.
|
||||||
|
Processor func() framework.ResourceListProcessor
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assert asserts the results for functions
|
// Assert runs the processor with the input provided in each valid test directory
|
||||||
|
// and verifies that the actual output and error match the fixtures in the directory.
|
||||||
func (rc *ProcessorResultsChecker) Assert(t *testing.T) bool {
|
func (rc *ProcessorResultsChecker) Assert(t *testing.T) bool {
|
||||||
if rc.TestDataDirectory == "" {
|
|
||||||
rc.TestDataDirectory = "testdata"
|
|
||||||
}
|
|
||||||
if rc.InputFilename == "" {
|
if rc.InputFilename == "" {
|
||||||
rc.InputFilename = "input.yaml"
|
rc.InputFilename = DefaultInputFilename
|
||||||
}
|
|
||||||
if rc.ExpectedOutputFilename == "" {
|
|
||||||
rc.ExpectedOutputFilename = "expected.yaml"
|
|
||||||
}
|
|
||||||
if rc.ExpectedErrorFilename == "" {
|
|
||||||
rc.ExpectedErrorFilename = "error.yaml"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := filepath.Walk(rc.TestDataDirectory, func(path string, info os.FileInfo, err error) error {
|
checker := newResultsChecker(
|
||||||
require.NoError(t, err)
|
rc.TestDataDirectory, rc.ExpectedOutputFilename, rc.ExpectedErrorFilename,
|
||||||
if !info.IsDir() {
|
rc.OutputAssertionFunc, rc.ErrorAssertionFunc,
|
||||||
// skip non-directories
|
rc.UpdateExpectedFromActual,
|
||||||
return nil
|
)
|
||||||
|
checker.assert(t, func() (string, string) {
|
||||||
|
_, err := os.Stat(rc.InputFilename)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
t.Error("Test case is missing input file")
|
||||||
}
|
}
|
||||||
rc.compare(t, path)
|
require.NoError(t, err)
|
||||||
return nil
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.NotZero(t, rc.testsCasesRun, "No complete test cases found in %s", rc.TestDataDirectory)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rc *ProcessorResultsChecker) compare(t *testing.T, path string) {
|
|
||||||
// cd into the directory so we can test functions that refer
|
|
||||||
// local files by relative paths
|
|
||||||
d, err := os.Getwd()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
defer func() { require.NoError(t, os.Chdir(d)) }()
|
|
||||||
require.NoError(t, os.Chdir(path))
|
|
||||||
|
|
||||||
// make sure this directory contains test data
|
|
||||||
_, err = os.Stat(rc.InputFilename)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
// missing input
|
|
||||||
return
|
|
||||||
}
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expectedOutput, expectedError := getExpected(t, rc.ExpectedOutputFilename, rc.ExpectedErrorFilename)
|
|
||||||
if expectedError == "" && expectedOutput == "" && !rc.UpdateExpectedFromActual {
|
|
||||||
// missing expected and UpdateExpectedFromActual == false, return and skip this test
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// run the test
|
|
||||||
t.Run(path, func(t *testing.T) {
|
|
||||||
rc.testsCasesRun += 1
|
|
||||||
actualOutput := bytes.NewBuffer([]byte{})
|
actualOutput := bytes.NewBuffer([]byte{})
|
||||||
rlBytes, err := ioutil.ReadFile(rc.InputFilename)
|
rlBytes, err := ioutil.ReadFile(rc.InputFilename)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -267,63 +188,170 @@ func (rc *ProcessorResultsChecker) compare(t *testing.T, path string) {
|
|||||||
Reader: bytes.NewBuffer(rlBytes),
|
Reader: bytes.NewBuffer(rlBytes),
|
||||||
Writer: actualOutput,
|
Writer: actualOutput,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = framework.Execute(rc.Processor(), &rw)
|
err = framework.Execute(rc.Processor(), &rw)
|
||||||
|
if err != nil {
|
||||||
|
require.NotEmptyf(t, err.Error(), "processor returned error with empty message")
|
||||||
|
return actualOutput.String(), err.Error()
|
||||||
|
}
|
||||||
|
return actualOutput.String(), ""
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Update the fixtures if configured to
|
type AssertionFunc func(t *testing.T, expected string, actual string)
|
||||||
if rc.UpdateExpectedFromActual {
|
|
||||||
if err != nil {
|
// RequireEachLineMatches is an AssertionFunc that treats each line of expected string
|
||||||
require.NoError(t, ioutil.WriteFile(rc.ExpectedErrorFilename, []byte(err.Error()), 0600))
|
// as a regex that must match the actual string.
|
||||||
|
func RequireEachLineMatches(t *testing.T, expected, actual string) {
|
||||||
|
// Check that each expected line matches the output
|
||||||
|
actual = standardizeSpacing(actual)
|
||||||
|
for _, msg := range strings.Split(standardizeSpacing(expected), "\n") {
|
||||||
|
require.Regexp(t, regexp.MustCompile(msg), actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequireStrippedStringsEqual is an AssertionFunc that does a simple string comparison
|
||||||
|
// of expected and actual after normalizing whitespace.
|
||||||
|
func RequireStrippedStringsEqual(t *testing.T, expected, actual string) {
|
||||||
|
require.Equal(t,
|
||||||
|
standardizeSpacing(expected),
|
||||||
|
standardizeSpacing(actual))
|
||||||
|
}
|
||||||
|
|
||||||
|
func standardizeSpacing(s string) string {
|
||||||
|
// remove extra whitespace and convert Windows line endings
|
||||||
|
return strings.ReplaceAll(strings.TrimSpace(s), "\r\n", "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// resultsChecker implements the core logic shared by all results checking types.
|
||||||
|
type resultsChecker struct {
|
||||||
|
testDataDirectory string
|
||||||
|
expectedOutputFilename string
|
||||||
|
expectedErrorFilename string
|
||||||
|
updateExpectedFromActual bool
|
||||||
|
outputAssertionFunc AssertionFunc
|
||||||
|
errorAssertionFunc AssertionFunc
|
||||||
|
|
||||||
|
testsCasesRun int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newResultsChecker(testDataDir string, outputFilename string, errorFilename string,
|
||||||
|
outputAsserter AssertionFunc, errorAsserter AssertionFunc,
|
||||||
|
updateFixtures bool) *resultsChecker {
|
||||||
|
rc := resultsChecker{
|
||||||
|
testDataDirectory: testDataDir,
|
||||||
|
expectedOutputFilename: outputFilename,
|
||||||
|
expectedErrorFilename: errorFilename,
|
||||||
|
updateExpectedFromActual: updateFixtures,
|
||||||
|
outputAssertionFunc: outputAsserter,
|
||||||
|
errorAssertionFunc: errorAsserter,
|
||||||
|
}
|
||||||
|
if rc.testDataDirectory == "" {
|
||||||
|
rc.testDataDirectory = DefaultTestDataDirectory
|
||||||
|
}
|
||||||
|
if rc.expectedOutputFilename == "" {
|
||||||
|
rc.expectedOutputFilename = DefaultOutputFilename
|
||||||
|
}
|
||||||
|
if rc.expectedErrorFilename == "" {
|
||||||
|
rc.expectedErrorFilename = DefaultErrorFilename
|
||||||
|
}
|
||||||
|
if rc.outputAssertionFunc == nil {
|
||||||
|
rc.outputAssertionFunc = RequireStrippedStringsEqual
|
||||||
|
}
|
||||||
|
if rc.errorAssertionFunc == nil {
|
||||||
|
rc.errorAssertionFunc = RequireEachLineMatches
|
||||||
|
}
|
||||||
|
return &rc
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert traverses TestDataDirectory to find test cases, calls getResult to invoke the function
|
||||||
|
// under test in each directory, and then runs assertions on the returned output and error results.
|
||||||
|
// It triggers a test failure if no valid test directories were found.
|
||||||
|
func (rc *resultsChecker) assert(t *testing.T, getResult func() (string, string)) {
|
||||||
|
err := filepath.Walk(rc.testDataDirectory,
|
||||||
|
func(path string, info os.FileInfo, err error) error {
|
||||||
|
require.NoError(t, err)
|
||||||
|
if !info.IsDir() {
|
||||||
|
// skip non-directories
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if len(actualOutput.String()) > 0 {
|
rc.runDirectoryTestCase(t, path, getResult)
|
||||||
require.NoError(t, ioutil.WriteFile(rc.ExpectedOutputFilename, actualOutput.Bytes(), 0600))
|
return nil
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotZero(t, rc.testsCasesRun, "No complete test cases found in %s", rc.testDataDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *resultsChecker) runDirectoryTestCase(t *testing.T, path string, getResult func() (string, string)) {
|
||||||
|
// cd into the directory so we can test functions that refer
|
||||||
|
// local files by relative paths
|
||||||
|
d, err := os.Getwd()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defer func() { require.NoError(t, os.Chdir(d)) }()
|
||||||
|
require.NoError(t, os.Chdir(path))
|
||||||
|
|
||||||
|
expectedOutput, expectedError := rc.readAssertionFiles(t)
|
||||||
|
if expectedError == "" && expectedOutput == "" && !rc.updateExpectedFromActual {
|
||||||
|
// not a test directory: missing expectations and updateExpectedFromActual == false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the test
|
||||||
|
t.Run(path, func(t *testing.T) {
|
||||||
|
rc.testsCasesRun += 1
|
||||||
|
actualOutput, actualError := getResult()
|
||||||
|
|
||||||
|
// Configured to update the assertion files instead of comparing them
|
||||||
|
if rc.updateExpectedFromActual {
|
||||||
|
if actualError != "" {
|
||||||
|
require.NoError(t, ioutil.WriteFile(rc.expectedErrorFilename, []byte(actualError), 0600))
|
||||||
}
|
}
|
||||||
return
|
if len(actualOutput) > 0 {
|
||||||
|
require.NoError(t, ioutil.WriteFile(rc.expectedOutputFilename, []byte(actualOutput), 0600))
|
||||||
|
}
|
||||||
|
t.Skip("Updated fixtures for test case")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare the results
|
// Compare the results to the assertion files
|
||||||
if expectedError != "" {
|
if expectedError != "" {
|
||||||
// We expected an error, so make sure there was one and it matches
|
// We expected an error, so make sure there was one
|
||||||
require.Error(t, err, actualOutput.String())
|
require.NotEmptyf(t, actualError, "test expected an error but message was empty, and output was:\n%s", actualOutput)
|
||||||
require.Contains(t,
|
rc.errorAssertionFunc(t, expectedError, actualError)
|
||||||
standardizeSpacing(err.Error()),
|
|
||||||
standardizeSpacing(expectedError), actualOutput.String())
|
|
||||||
} else {
|
} else {
|
||||||
// We didn't expect an error, and the output should match
|
// We didn't expect an error, and the output should match
|
||||||
require.NoError(t, err)
|
require.Emptyf(t, actualError, "test expected no error but got an error message, and output was:\n%s", actualOutput)
|
||||||
require.Equal(t,
|
rc.outputAssertionFunc(t, expectedOutput, actualOutput)
|
||||||
standardizeSpacing(expectedOutput),
|
|
||||||
standardizeSpacing(actualOutput.String()))
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// getExpected reads the expected results and error files
|
// readAssertionFiles reads the expected results and error files
|
||||||
func getExpected(t *testing.T, expectedOutFilename, expectedErrFilename string) (string, string) {
|
func (rc *resultsChecker) readAssertionFiles(t *testing.T) (string, string) {
|
||||||
// read the expected results
|
// read the expected results
|
||||||
var expectedOutput, expectedError string
|
var expectedOutput, expectedError string
|
||||||
if expectedOutFilename != "" {
|
if rc.expectedOutputFilename != "" {
|
||||||
_, err := os.Stat(expectedOutFilename)
|
_, err := os.Stat(rc.expectedOutputFilename)
|
||||||
if !os.IsNotExist(err) && err != nil {
|
if !os.IsNotExist(err) && err != nil {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// only read the file if it exists
|
// only read the file if it exists
|
||||||
b, err := ioutil.ReadFile(expectedOutFilename)
|
b, err := ioutil.ReadFile(rc.expectedOutputFilename)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
expectedOutput = string(b)
|
expectedOutput = string(b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if expectedErrFilename != "" {
|
if rc.expectedErrorFilename != "" {
|
||||||
_, err := os.Stat(expectedErrFilename)
|
_, err := os.Stat(rc.expectedErrorFilename)
|
||||||
if !os.IsNotExist(err) && err != nil {
|
if !os.IsNotExist(err) && err != nil {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// only read the file if it exists
|
// only read the file if it exists
|
||||||
b, err := ioutil.ReadFile(expectedErrFilename)
|
b, err := ioutil.ReadFile(rc.expectedErrorFilename)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fn/framework/frameworktestutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/fn/framework/parser"
|
"sigs.k8s.io/kustomize/kyaml/fn/framework/parser"
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
@@ -589,3 +590,19 @@ items:
|
|||||||
})
|
})
|
||||||
require.Nil(t, found, "openAPI schema was not reset")
|
require.Nil(t, found, "openAPI schema was not reset")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTemplateProcessor_Validator(t *testing.T) {
|
||||||
|
// This test proves the Validate method is called when implemented
|
||||||
|
// and demonstrates the use of ProcessorResultsChecker's error matching
|
||||||
|
p := func() framework.ResourceListProcessor {
|
||||||
|
return &framework.VersionedAPIProcessor{FilterProvider: framework.GVKFilterMap{
|
||||||
|
"JavaSpringBoot": {
|
||||||
|
"example.com/v1alpha1": &v1alpha1JavaSpringBoot{},
|
||||||
|
}}}
|
||||||
|
}
|
||||||
|
c := frameworktestutil.ProcessorResultsChecker{
|
||||||
|
TestDataDirectory: "testdata/validation",
|
||||||
|
Processor: p,
|
||||||
|
}
|
||||||
|
c.Assert(t)
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ type ResultItem struct {
|
|||||||
Severity Severity `yaml:"severity,omitempty"`
|
Severity Severity `yaml:"severity,omitempty"`
|
||||||
|
|
||||||
// ResourceRef is a reference to a resource
|
// ResourceRef is a reference to a resource
|
||||||
ResourceRef yaml.ResourceMeta `yaml:"resourceRef,omitempty"`
|
ResourceRef yaml.ResourceIdentifier `yaml:"resourceRef,omitempty"`
|
||||||
|
|
||||||
// Field is a reference to the field in a resource this result refers to
|
// Field is a reference to the field in a resource this result refers to
|
||||||
Field Field `yaml:"field,omitempty"`
|
Field Field `yaml:"field,omitempty"`
|
||||||
@@ -42,9 +42,25 @@ type ResultItem struct {
|
|||||||
|
|
||||||
// String provides a human-readable message for the result item
|
// String provides a human-readable message for the result item
|
||||||
func (i ResultItem) String() string {
|
func (i ResultItem) String() string {
|
||||||
identifier := i.ResourceRef.GetIdentifier()
|
identifier := i.ResourceRef
|
||||||
idString := strings.Join([]string{identifier.GetAPIVersion(), identifier.GetKind(), identifier.GetNamespace(), identifier.GetName()}, "/")
|
var idStringList []string
|
||||||
return fmt.Sprintf("[%s] %s %s: %s", i.Severity, idString, i.Field.Path, i.Message)
|
if identifier.APIVersion != "" {
|
||||||
|
idStringList = append(idStringList, identifier.APIVersion)
|
||||||
|
}
|
||||||
|
if identifier.Kind != "" {
|
||||||
|
idStringList = append(idStringList, identifier.Kind)
|
||||||
|
}
|
||||||
|
if identifier.Namespace != "" {
|
||||||
|
idStringList = append(idStringList, identifier.Namespace)
|
||||||
|
}
|
||||||
|
if identifier.Name != "" {
|
||||||
|
idStringList = append(idStringList, identifier.Name)
|
||||||
|
}
|
||||||
|
if len(idStringList) > 0 {
|
||||||
|
idString := strings.Join(idStringList, "/")
|
||||||
|
return fmt.Sprintf("[%s] %s %s: %s", i.Severity, idString, i.Field.Path, i.Message)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("[%s] %s: %s", i.Severity, i.Field.Path, i.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// File references a file containing a resource
|
// File references a file containing a resource
|
||||||
|
|||||||
5
kyaml/fn/framework/testdata/validation/error/errors.txt
vendored
Normal file
5
kyaml/fn/framework/testdata/validation/error/errors.txt
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
JavaSpringBoot had 4 errors:
|
||||||
|
\[\d\] replicas must be less than 10
|
||||||
|
\[\d\] name is required
|
||||||
|
\[\d\] image should not have latest tag
|
||||||
|
\[\d\] domain must be a subdomain of example.com
|
||||||
14
kyaml/fn/framework/testdata/validation/error/input.yaml
vendored
Normal file
14
kyaml/fn/framework/testdata/validation/error/input.yaml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Copyright 2021 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
kind: ResourceList
|
||||||
|
apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: JavaSpringBoot
|
||||||
|
metadata:
|
||||||
|
name: ""
|
||||||
|
spec:
|
||||||
|
replicas: 1000
|
||||||
|
image: foo:latest
|
||||||
|
domain: bad
|
||||||
@@ -6,13 +6,16 @@ package kio
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
|
"path/filepath"
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Writer writes ResourceNodes to bytes.
|
// ByteWriter writes ResourceNodes to bytes. Generally YAML encoding will be used but in the special
|
||||||
|
// case of writing a single, bare yaml.RNode that has a kioutil.PathAnnotation indicating that the
|
||||||
|
// target is a JSON file JSON encoding is used. See shouldJSONEncodeSingleBareNode below for more
|
||||||
|
// information.
|
||||||
type ByteWriter struct {
|
type ByteWriter struct {
|
||||||
// Writer is where ResourceNodes are encoded.
|
// Writer is where ResourceNodes are encoded.
|
||||||
Writer io.Writer
|
Writer io.Writer
|
||||||
@@ -47,7 +50,9 @@ type ByteWriter struct {
|
|||||||
|
|
||||||
var _ Writer = ByteWriter{}
|
var _ Writer = ByteWriter{}
|
||||||
|
|
||||||
func (w ByteWriter) Write(nodes []*yaml.RNode) error {
|
func (w ByteWriter) Write(inputNodes []*yaml.RNode) error {
|
||||||
|
// Copy the nodes to prevent writer from mutating the original nodes.
|
||||||
|
nodes := copyRNodes(inputNodes)
|
||||||
yaml.DoSerializationHacksOnNodes(nodes)
|
yaml.DoSerializationHacksOnNodes(nodes)
|
||||||
if w.Sort {
|
if w.Sort {
|
||||||
if err := kioutil.SortNodes(nodes); err != nil {
|
if err := kioutil.SortNodes(nodes); err != nil {
|
||||||
@@ -55,8 +60,9 @@ func (w ByteWriter) Write(nodes []*yaml.RNode) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
encoder := yaml.NewEncoder(w.Writer)
|
// Even though we use the this value further down we must check this before removing annotations
|
||||||
defer encoder.Close()
|
jsonEncodeSingleBareNode := w.shouldJSONEncodeSingleBareNode(nodes)
|
||||||
|
|
||||||
for i := range nodes {
|
for i := range nodes {
|
||||||
// clean resources by removing annotations set by the Reader
|
// clean resources by removing annotations set by the Reader
|
||||||
if !w.KeepReaderAnnotations {
|
if !w.KeepReaderAnnotations {
|
||||||
@@ -81,10 +87,18 @@ func (w ByteWriter) Write(nodes []*yaml.RNode) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if jsonEncodeSingleBareNode {
|
||||||
|
encoder := json.NewEncoder(w.Writer)
|
||||||
|
encoder.SetIndent("", " ")
|
||||||
|
return errors.Wrap(encoder.Encode(nodes[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder := yaml.NewEncoder(w.Writer)
|
||||||
|
defer encoder.Close()
|
||||||
// don't wrap the elements
|
// don't wrap the elements
|
||||||
if w.WrappingKind == "" {
|
if w.WrappingKind == "" {
|
||||||
for i := range nodes {
|
for i := range nodes {
|
||||||
if err := w.encode(encoder, nodes[i].Document()); err != nil {
|
if err := encoder.Encode(nodes[i].Document()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,23 +132,37 @@ func (w ByteWriter) Write(nodes []*yaml.RNode) error {
|
|||||||
for i := range nodes {
|
for i := range nodes {
|
||||||
items.Content = append(items.Content, nodes[i].YNode())
|
items.Content = append(items.Content, nodes[i].YNode())
|
||||||
}
|
}
|
||||||
err := w.encode(encoder, doc)
|
|
||||||
yaml.UndoSerializationHacksOnNodes(nodes)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// encode encodes the input document node to appropriate node format
|
|
||||||
func (w ByteWriter) encode(encoder *yaml.Encoder, doc *yaml.Node) error {
|
|
||||||
rNode := &yaml.RNode{}
|
|
||||||
rNode.SetYNode(doc)
|
|
||||||
str, err := rNode.String()
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err)
|
|
||||||
}
|
|
||||||
if json.Valid([]byte(str)) {
|
|
||||||
je := json.NewEncoder(w.Writer)
|
|
||||||
je.SetIndent("", " ")
|
|
||||||
return errors.Wrap(je.Encode(rNode))
|
|
||||||
}
|
|
||||||
return encoder.Encode(doc)
|
return encoder.Encode(doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func copyRNodes(in []*yaml.RNode) []*yaml.RNode {
|
||||||
|
out := make([]*yaml.RNode, len(in))
|
||||||
|
for i := range in {
|
||||||
|
out[i] = in[i].Copy()
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldJSONEncodeSingleBareNode determines if nodes contain a single node that should not be
|
||||||
|
// wrapped and has a JSON file extension, which in turn means that the node should be JSON encoded.
|
||||||
|
// Note 1: this must be checked before any annotations to avoid losing information about the target
|
||||||
|
// filename extension.
|
||||||
|
// Note 2: JSON encoding should only be used for single, unwrapped nodes because multiple unwrapped
|
||||||
|
// nodes cannot be represented in JSON (no multi doc support). Furthermore, the typical use
|
||||||
|
// cases for wrapping nodes would likely not include later writing the whole wrapper to a
|
||||||
|
// .json file, i.e. there is no point risking any edge case information loss e.g. comments
|
||||||
|
// disappearing, that could come from JSON encoding the whole wrapper just to ensure that
|
||||||
|
// one (or all nodes) can be read as JSON.
|
||||||
|
func (w ByteWriter) shouldJSONEncodeSingleBareNode(nodes []*yaml.RNode) bool {
|
||||||
|
if w.WrappingKind == "" && len(nodes) == 1 {
|
||||||
|
if path, _, _ := kioutil.GetFileAnnotations(nodes[0]); path != "" {
|
||||||
|
filename := filepath.Base(path)
|
||||||
|
for _, glob := range JSONMatch {
|
||||||
|
if match, _ := filepath.Match(glob, filename); match {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func TestByteWriter(t *testing.T) {
|
|||||||
|
|
||||||
testCases := []testCase{
|
testCases := []testCase{
|
||||||
//
|
//
|
||||||
//
|
// Test Case
|
||||||
//
|
//
|
||||||
{
|
{
|
||||||
name: "wrap_resource_list",
|
name: "wrap_resource_list",
|
||||||
@@ -60,7 +60,7 @@ functionConfig:
|
|||||||
},
|
},
|
||||||
|
|
||||||
//
|
//
|
||||||
//
|
// Test Case
|
||||||
//
|
//
|
||||||
{
|
{
|
||||||
name: "multiple_items",
|
name: "multiple_items",
|
||||||
@@ -91,6 +91,127 @@ a: b #first
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test Case
|
||||||
|
//
|
||||||
|
{
|
||||||
|
name: "handle_comments",
|
||||||
|
items: []string{
|
||||||
|
`# comment 0
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: my-nginx
|
||||||
|
namespace: my-space
|
||||||
|
labels:
|
||||||
|
env: dev
|
||||||
|
foo: bar
|
||||||
|
spec:
|
||||||
|
# comment 1
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
# comment 2
|
||||||
|
matchLabels: # comment 3
|
||||||
|
# comment 4
|
||||||
|
app: nginx # comment 5
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
# comment 6
|
||||||
|
containers:
|
||||||
|
# comment 7
|
||||||
|
- name: nginx
|
||||||
|
image: nginx:1.14.2 # comment 8
|
||||||
|
ports:
|
||||||
|
# comment 9
|
||||||
|
- containerPort: 80 # comment 10
|
||||||
|
`,
|
||||||
|
`apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: my-service
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
# comment 1
|
||||||
|
- name: etcd-server-ssl
|
||||||
|
port: 2380
|
||||||
|
# comment 2
|
||||||
|
- name: etcd-client-ssl
|
||||||
|
port: 2379
|
||||||
|
`,
|
||||||
|
`apiVersion: constraints.gatekeeper.sh/v1beta1
|
||||||
|
kind: EnforceFoo
|
||||||
|
metadata:
|
||||||
|
name: enforce-foo
|
||||||
|
spec:
|
||||||
|
parameters:
|
||||||
|
naming_rules:
|
||||||
|
- kind: Folder
|
||||||
|
patterns:
|
||||||
|
# comment 1
|
||||||
|
- ^(dev|prod|staging|qa|shared)$
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
expectedOutput: `# comment 0
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: my-nginx
|
||||||
|
namespace: my-space
|
||||||
|
labels:
|
||||||
|
env: dev
|
||||||
|
foo: bar
|
||||||
|
spec:
|
||||||
|
# comment 1
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
# comment 2
|
||||||
|
matchLabels: # comment 3
|
||||||
|
# comment 4
|
||||||
|
app: nginx # comment 5
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
# comment 6
|
||||||
|
containers:
|
||||||
|
# comment 7
|
||||||
|
- name: nginx
|
||||||
|
image: nginx:1.14.2 # comment 8
|
||||||
|
ports:
|
||||||
|
# comment 9
|
||||||
|
- containerPort: 80 # comment 10
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: my-service
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
# comment 1
|
||||||
|
- name: etcd-server-ssl
|
||||||
|
port: 2380
|
||||||
|
# comment 2
|
||||||
|
- name: etcd-client-ssl
|
||||||
|
port: 2379
|
||||||
|
---
|
||||||
|
apiVersion: constraints.gatekeeper.sh/v1beta1
|
||||||
|
kind: EnforceFoo
|
||||||
|
metadata:
|
||||||
|
name: enforce-foo
|
||||||
|
spec:
|
||||||
|
parameters:
|
||||||
|
naming_rules:
|
||||||
|
- kind: Folder
|
||||||
|
patterns:
|
||||||
|
# comment 1
|
||||||
|
- ^(dev|prod|staging|qa|shared)$
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
//
|
//
|
||||||
// Test Case
|
// Test Case
|
||||||
//
|
//
|
||||||
@@ -187,6 +308,110 @@ c: d # second
|
|||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test Case
|
||||||
|
//
|
||||||
|
{
|
||||||
|
name: "encode_valid_json",
|
||||||
|
items: []string{
|
||||||
|
`{
|
||||||
|
"a": "a long string that would certainly see a newline introduced by the YAML marshaller abcd123",
|
||||||
|
metadata: {
|
||||||
|
annotations: {
|
||||||
|
config.kubernetes.io/path: test.json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
|
||||||
|
expectedOutput: `{
|
||||||
|
"a": "a long string that would certainly see a newline introduced by the YAML marshaller abcd123",
|
||||||
|
"metadata": {
|
||||||
|
"annotations": {
|
||||||
|
"config.kubernetes.io/path": "test.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test Case
|
||||||
|
//
|
||||||
|
{
|
||||||
|
name: "encode_unformatted_valid_json",
|
||||||
|
items: []string{
|
||||||
|
`{ "a": "b", metadata: { annotations: { config.kubernetes.io/path: test.json } } }`,
|
||||||
|
},
|
||||||
|
|
||||||
|
expectedOutput: `{
|
||||||
|
"a": "b",
|
||||||
|
"metadata": {
|
||||||
|
"annotations": {
|
||||||
|
"config.kubernetes.io/path": "test.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test Case
|
||||||
|
//
|
||||||
|
{
|
||||||
|
name: "encode_wrapped_json_as_yaml",
|
||||||
|
instance: ByteWriter{
|
||||||
|
Sort: true,
|
||||||
|
WrappingKind: ResourceListKind,
|
||||||
|
WrappingAPIVersion: ResourceListAPIVersion,
|
||||||
|
},
|
||||||
|
items: []string{
|
||||||
|
`{
|
||||||
|
"a": "b",
|
||||||
|
"metadata": {
|
||||||
|
"annotations": {
|
||||||
|
"config.kubernetes.io/path": "test.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
|
||||||
|
expectedOutput: `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- {"a": "b", "metadata": {"annotations": {"config.kubernetes.io/path": "test.json"}}}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test Case
|
||||||
|
//
|
||||||
|
{
|
||||||
|
name: "encode_multi_doc_json_as_yaml",
|
||||||
|
items: []string{
|
||||||
|
`{
|
||||||
|
"a": "b",
|
||||||
|
"metadata": {
|
||||||
|
"annotations": {
|
||||||
|
"config.kubernetes.io/path": "test-1.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
`{
|
||||||
|
"c": "d",
|
||||||
|
"metadata": {
|
||||||
|
"annotations": {
|
||||||
|
"config.kubernetes.io/path": "test-2.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
|
||||||
|
expectedOutput: `
|
||||||
|
{"a": "b", "metadata": {"annotations": {"config.kubernetes.io/path": "test-1.json"}}}
|
||||||
|
---
|
||||||
|
{"c": "d", "metadata": {"annotations": {"config.kubernetes.io/path": "test-2.json"}}}
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -420,8 +420,8 @@ spec:
|
|||||||
image: nginx:1.7.9
|
image: nginx:1.7.9
|
||||||
# this is a container
|
# this is a container
|
||||||
ports:
|
ports:
|
||||||
- # this is a port
|
# this is a port
|
||||||
containerPort: 80
|
- containerPort: 80
|
||||||
`
|
`
|
||||||
s, err := FormatInput(strings.NewReader(y))
|
s, err := FormatInput(strings.NewReader(y))
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@@ -743,8 +743,8 @@ func TestFormatFileOrDirectory_ymlExtFile(t *testing.T) {
|
|||||||
assert.Equal(t, string(testyaml.FormattedYaml1), string(b))
|
assert.Equal(t, string(testyaml.FormattedYaml1), string(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestFormatFileOrDirectory_skipYamlExtFileWithJson verifies that the json content is formatted
|
// TestFormatFileOrDirectory_YamlExtFileWithJson verifies that the JSON compatible flow style
|
||||||
// as yaml
|
// YAML content is formatted as such.
|
||||||
func TestFormatFileOrDirectory_YamlExtFileWithJson(t *testing.T) {
|
func TestFormatFileOrDirectory_YamlExtFileWithJson(t *testing.T) {
|
||||||
// write the unformatted JSON file contents
|
// write the unformatted JSON file contents
|
||||||
f, err := ioutil.TempFile("", "yamlfmt*.yaml")
|
f, err := ioutil.TempFile("", "yamlfmt*.yaml")
|
||||||
@@ -760,7 +760,27 @@ func TestFormatFileOrDirectory_YamlExtFileWithJson(t *testing.T) {
|
|||||||
// check the result is formatted as yaml
|
// check the result is formatted as yaml
|
||||||
b, err := ioutil.ReadFile(f.Name())
|
b, err := ioutil.ReadFile(f.Name())
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, string(testyaml.FormattedJSON1), string(b))
|
assert.Equal(t, string(testyaml.FormattedFlowYAML1), string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFormatFileOrDirectory_JsonExtFileWithNotModified verifies that a file with .json extensions
|
||||||
|
// and JSON contents won't be modified.
|
||||||
|
func TestFormatFileOrDirectory_JsonExtFileWithNotModified(t *testing.T) {
|
||||||
|
// write the unformatted JSON file contents
|
||||||
|
f, err := ioutil.TempFile("", "yamlfmt*.json")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer os.Remove(f.Name())
|
||||||
|
err = ioutil.WriteFile(f.Name(), testyaml.UnformattedJSON1, 0600)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// format the file
|
||||||
|
err = FormatFileOrDirectory(f.Name())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// check the result is formatted as yaml
|
||||||
|
b, err := ioutil.ReadFile(f.Name())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, string(testyaml.UnformattedJSON1), string(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestFormatFileOrDirectory_partialKubernetesYamlFile verifies that if a yaml file contains both
|
// TestFormatFileOrDirectory_partialKubernetesYamlFile verifies that if a yaml file contains both
|
||||||
@@ -854,6 +874,7 @@ func TestFormatFileOrDirectory_skipJsonExtFile(t *testing.T) {
|
|||||||
func TestFormatFileOrDirectory_directory(t *testing.T) {
|
func TestFormatFileOrDirectory_directory(t *testing.T) {
|
||||||
d, err := ioutil.TempDir("", "yamlfmt")
|
d, err := ioutil.TempDir("", "yamlfmt")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
defer os.RemoveAll(d)
|
||||||
|
|
||||||
err = os.Mkdir(filepath.Join(d, "config"), 0700)
|
err = os.Mkdir(filepath.Join(d, "config"), 0700)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|||||||
@@ -97,18 +97,9 @@ status2:
|
|||||||
- 2
|
- 2
|
||||||
`)
|
`)
|
||||||
|
|
||||||
var FormattedJSON1 = []byte(`{
|
var FormattedFlowYAML1 = []byte(
|
||||||
"apiVersion": "example.com/v1beta1",
|
`{"apiVersion": "example.com/v1beta1", "kind": "MyType", "spec": "a", "status": {"conditions": [
|
||||||
"kind": "MyType",
|
3, 1, 2]}}
|
||||||
"spec": "a",
|
|
||||||
"status": {
|
|
||||||
"conditions": [
|
|
||||||
3,
|
|
||||||
1,
|
|
||||||
2
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`)
|
`)
|
||||||
|
|
||||||
var FormattedYaml3 = []byte(`apiVersion: v1
|
var FormattedYaml3 = []byte(`apiVersion: v1
|
||||||
|
|||||||
@@ -1110,6 +1110,7 @@ func TestCmd_Execute_setInput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
outDir, err := ioutil.TempDir("", "kustomize-test")
|
outDir, err := ioutil.TempDir("", "kustomize-test")
|
||||||
|
defer os.RemoveAll(outDir)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1671,7 +1672,8 @@ func TestSetName(t *testing.T) {
|
|||||||
if err := rn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
|
if err := rn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
|
||||||
t.Fatalf("unexpected unmarshaljson err: %v", err)
|
t.Fatalf("unexpected unmarshaljson err: %v", err)
|
||||||
}
|
}
|
||||||
rn.SetName("marge")
|
err := rn.SetName("marge")
|
||||||
|
require.NoError(t, err)
|
||||||
if expected, actual := "marge", rn.GetName(); expected != actual {
|
if expected, actual := "marge", rn.GetName(); expected != actual {
|
||||||
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
||||||
}
|
}
|
||||||
@@ -1682,12 +1684,15 @@ func TestSetNamespace(t *testing.T) {
|
|||||||
if err := rn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
|
if err := rn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
|
||||||
t.Fatalf("unexpected unmarshaljson err: %v", err)
|
t.Fatalf("unexpected unmarshaljson err: %v", err)
|
||||||
}
|
}
|
||||||
rn.SetNamespace("flanders")
|
err := rn.SetNamespace("flanders")
|
||||||
meta, _ := rn.GetMeta()
|
require.NoError(t, err)
|
||||||
|
meta, err := rn.GetMeta()
|
||||||
|
require.NoError(t, err)
|
||||||
if expected, actual := "flanders", meta.Namespace; expected != actual {
|
if expected, actual := "flanders", meta.Namespace; expected != actual {
|
||||||
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetLabels(t *testing.T) {
|
func TestSetLabels(t *testing.T) {
|
||||||
rn := NewRNode(nil)
|
rn := NewRNode(nil)
|
||||||
if err := rn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
|
if err := rn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
|
||||||
|
|||||||
@@ -34,43 +34,13 @@ func DoSerializationHacks(node *yaml.Node) {
|
|||||||
// https://github.com/go-yaml/yaml/issues/587 in go-yaml.v3
|
// https://github.com/go-yaml/yaml/issues/587 in go-yaml.v3
|
||||||
// Remove this hack when the issue has been resolved
|
// Remove this hack when the issue has been resolved
|
||||||
if len(node.Content) > 0 && node.Content[0].Kind == ScalarNode {
|
if len(node.Content) > 0 && node.Content[0].Kind == ScalarNode {
|
||||||
node.HeadComment = node.Content[0].HeadComment
|
// Don't clobber the head comment if it's not empty.
|
||||||
|
if node.HeadComment == "" && node.Content[0].HeadComment != "" {
|
||||||
|
node.HeadComment = node.Content[0].HeadComment
|
||||||
|
}
|
||||||
node.Content[0].HeadComment = ""
|
node.Content[0].HeadComment = ""
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func UndoSerializationHacksOnNodes(nodes []*RNode) {
|
|
||||||
for _, node := range nodes {
|
|
||||||
UndoSerializationHacks(node.YNode())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UndoSerializationHacks reverts the changes made by DoSerializationHacks
|
|
||||||
// Refer to https://github.com/go-yaml/yaml/issues/587 for more details
|
|
||||||
func UndoSerializationHacks(node *yaml.Node) {
|
|
||||||
switch node.Kind {
|
|
||||||
case DocumentNode:
|
|
||||||
for _, node := range node.Content {
|
|
||||||
DoSerializationHacks(node)
|
DoSerializationHacks(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
case MappingNode:
|
|
||||||
for _, node := range node.Content {
|
|
||||||
DoSerializationHacks(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
case SequenceNode:
|
|
||||||
for _, node := range node.Content {
|
|
||||||
// revert the changes made in DoSerializationHacks
|
|
||||||
// This is necessary to address serialization issue
|
|
||||||
// https://github.com/go-yaml/yaml/issues/587 in go-yaml.v3
|
|
||||||
// Remove this hack when the issue has been resolved
|
|
||||||
if len(node.Content) > 0 && node.Content[0].Kind == ScalarNode {
|
|
||||||
node.Content[0].HeadComment = node.HeadComment
|
|
||||||
node.HeadComment = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,7 +213,10 @@ func String(node *yaml.Node, opts ...string) (string, error) {
|
|||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
e := NewEncoder(b)
|
e := NewEncoder(b)
|
||||||
err := e.Encode(node)
|
err := e.Encode(node)
|
||||||
e.Close()
|
errClose := e.Close()
|
||||||
|
if err == nil {
|
||||||
|
err = errClose
|
||||||
|
}
|
||||||
val := b.String()
|
val := b.String()
|
||||||
if optsSet.Has(Trim) {
|
if optsSet.Has(Trim) {
|
||||||
val = strings.TrimSpace(val)
|
val = strings.TrimSpace(val)
|
||||||
|
|||||||
@@ -285,6 +285,9 @@ func (p *HelmChartInflationGeneratorPlugin) templateCommand() []string {
|
|||||||
// I've tried placing the flag before and after the name argument.
|
// I've tried placing the flag before and after the name argument.
|
||||||
args = append(args, "--generate-name")
|
args = append(args, "--generate-name")
|
||||||
}
|
}
|
||||||
|
if p.IncludeCRDs {
|
||||||
|
args = append(args, "--include-crds")
|
||||||
|
}
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package main_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -458,3 +459,92 @@ valuesMerge: replace
|
|||||||
111, // memory
|
111, // memory
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHelmChartInflationGeneratorWithIncludeCRDs(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t).
|
||||||
|
PrepBuiltin("HelmChartInflationGenerator")
|
||||||
|
defer th.Reset()
|
||||||
|
if err := th.ErrIfNoHelm(); err != nil {
|
||||||
|
t.Skip("skipping: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// we store this data outside of the _test.go file as its sort of huge
|
||||||
|
// and has backticks, which makes string literals wonky
|
||||||
|
testData, err := ioutil.ReadFile("include_crds_testdata.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unable to read test data for includeCRDs: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rm := th.LoadAndRunGenerator(`
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: HelmChartInflationGenerator
|
||||||
|
metadata:
|
||||||
|
name: terraform
|
||||||
|
name: terraform
|
||||||
|
version: 1.0.0
|
||||||
|
repo: https://helm.releases.hashicorp.com
|
||||||
|
releaseName: terraforming-mars
|
||||||
|
includeCRDs: true
|
||||||
|
valuesInline:
|
||||||
|
global:
|
||||||
|
enabled: false
|
||||||
|
tests:
|
||||||
|
enabled: false
|
||||||
|
`)
|
||||||
|
th.AssertActualEqualsExpected(rm, string(testData))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHelmChartInflationGeneratorWithExcludeCRDs(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t).
|
||||||
|
PrepBuiltin("HelmChartInflationGenerator")
|
||||||
|
defer th.Reset()
|
||||||
|
if err := th.ErrIfNoHelm(); err != nil {
|
||||||
|
t.Skip("skipping: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// we choose this helm chart as it has the ability to turn
|
||||||
|
// everything off, except CRDs.
|
||||||
|
rm := th.LoadAndRunGenerator(`
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: HelmChartInflationGenerator
|
||||||
|
metadata:
|
||||||
|
name: terraform
|
||||||
|
name: terraform
|
||||||
|
version: 1.0.0
|
||||||
|
repo: https://helm.releases.hashicorp.com
|
||||||
|
releaseName: terraforming-mars
|
||||||
|
includeCRDs: false
|
||||||
|
valuesInline:
|
||||||
|
global:
|
||||||
|
enabled: false
|
||||||
|
tests:
|
||||||
|
enabled: false
|
||||||
|
`)
|
||||||
|
th.AssertActualEqualsExpected(rm, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHelmChartInflationGeneratorWithIncludeCRDsNotSpecified(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t).
|
||||||
|
PrepBuiltin("HelmChartInflationGenerator")
|
||||||
|
defer th.Reset()
|
||||||
|
if err := th.ErrIfNoHelm(); err != nil {
|
||||||
|
t.Skip("skipping: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
rm := th.LoadAndRunGenerator(`
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: HelmChartInflationGenerator
|
||||||
|
metadata:
|
||||||
|
name: terraform
|
||||||
|
name: terraform
|
||||||
|
version: 1.0.0
|
||||||
|
repo: https://helm.releases.hashicorp.com
|
||||||
|
releaseName: terraforming-mars
|
||||||
|
valuesInline:
|
||||||
|
global:
|
||||||
|
enabled: false
|
||||||
|
tests:
|
||||||
|
enabled: false
|
||||||
|
`)
|
||||||
|
th.AssertActualEqualsExpected(rm, "")
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,262 @@
|
|||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.3.0
|
||||||
|
creationTimestamp: null
|
||||||
|
name: workspaces.app.terraform.io
|
||||||
|
spec:
|
||||||
|
group: app.terraform.io
|
||||||
|
names:
|
||||||
|
kind: Workspace
|
||||||
|
listKind: WorkspaceList
|
||||||
|
plural: workspaces
|
||||||
|
singular: workspace
|
||||||
|
scope: Namespaced
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
|
validation:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: Workspace is the Schema for the workspaces API
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: 'APIVersion defines the versioned schema of this representation
|
||||||
|
of an object. Servers should convert recognized schemas to the latest
|
||||||
|
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||||
|
type: string
|
||||||
|
kind:
|
||||||
|
description: 'Kind is a string value representing the REST resource this
|
||||||
|
object represents. Servers may infer this from the endpoint the client
|
||||||
|
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||||
|
type: string
|
||||||
|
metadata:
|
||||||
|
type: object
|
||||||
|
spec:
|
||||||
|
description: WorkspaceSpec defines the desired state of Workspace
|
||||||
|
properties:
|
||||||
|
agentPoolID:
|
||||||
|
description: Specifies the agent pool ID we wish to use.
|
||||||
|
type: string
|
||||||
|
module:
|
||||||
|
description: Module source and version to use
|
||||||
|
nullable: true
|
||||||
|
properties:
|
||||||
|
source:
|
||||||
|
description: Any remote module source (version control, registry)
|
||||||
|
type: string
|
||||||
|
version:
|
||||||
|
description: Module version for registry modules
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- source
|
||||||
|
type: object
|
||||||
|
organization:
|
||||||
|
description: Terraform Cloud organization
|
||||||
|
type: string
|
||||||
|
outputs:
|
||||||
|
description: Outputs denote outputs wanted
|
||||||
|
items:
|
||||||
|
description: OutputSpec specifies which values need to be output
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
description: Output name
|
||||||
|
type: string
|
||||||
|
moduleOutputName:
|
||||||
|
description: Attribute name in module
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
secretsMountPath:
|
||||||
|
description: File path within operator pod to load workspace secrets
|
||||||
|
type: string
|
||||||
|
sshKeyID:
|
||||||
|
description: SSH Key ID. This key must already exist in the TF Cloud
|
||||||
|
organization. This can either be the user assigned name of the SSH
|
||||||
|
Key, or the system assigned ID.
|
||||||
|
type: string
|
||||||
|
terraformVersion:
|
||||||
|
description: Terraform version used for this workspace. The default
|
||||||
|
is `latest`.
|
||||||
|
type: string
|
||||||
|
variables:
|
||||||
|
description: Variables as inputs to module
|
||||||
|
items:
|
||||||
|
description: Variable denotes an input to the module
|
||||||
|
properties:
|
||||||
|
environmentVariable:
|
||||||
|
description: EnvironmentVariable denotes if this variable should
|
||||||
|
be created as environment variable
|
||||||
|
type: boolean
|
||||||
|
hcl:
|
||||||
|
description: String input should be an HCL-formatted variable
|
||||||
|
type: boolean
|
||||||
|
key:
|
||||||
|
description: Variable name
|
||||||
|
type: string
|
||||||
|
sensitive:
|
||||||
|
description: Variable is a secret and should be retrieved from
|
||||||
|
file
|
||||||
|
type: boolean
|
||||||
|
value:
|
||||||
|
description: Variable value
|
||||||
|
type: string
|
||||||
|
valueFrom:
|
||||||
|
description: Source for the variable's value. Cannot be used if
|
||||||
|
value is not empty.
|
||||||
|
properties:
|
||||||
|
configMapKeyRef:
|
||||||
|
description: Selects a key of a ConfigMap.
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
description: The key to select.
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
TODO: Add other useful fields. apiVersion, kind, uid?'
|
||||||
|
type: string
|
||||||
|
optional:
|
||||||
|
description: Specify whether the ConfigMap or its key
|
||||||
|
must be defined
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- key
|
||||||
|
type: object
|
||||||
|
fieldRef:
|
||||||
|
description: 'Selects a field of the pod: supports metadata.name,
|
||||||
|
metadata.namespace, metadata.labels, metadata.annotations,
|
||||||
|
spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP,
|
||||||
|
status.podIPs.'
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
description: Version of the schema the FieldPath is written
|
||||||
|
in terms of, defaults to "v1".
|
||||||
|
type: string
|
||||||
|
fieldPath:
|
||||||
|
description: Path of the field to select in the specified
|
||||||
|
API version.
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- fieldPath
|
||||||
|
type: object
|
||||||
|
resourceFieldRef:
|
||||||
|
description: 'Selects a resource of the container: only resources
|
||||||
|
limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage,
|
||||||
|
requests.cpu, requests.memory and requests.ephemeral-storage)
|
||||||
|
are currently supported.'
|
||||||
|
properties:
|
||||||
|
containerName:
|
||||||
|
description: 'Container name: required for volumes, optional
|
||||||
|
for env vars'
|
||||||
|
type: string
|
||||||
|
divisor:
|
||||||
|
anyOf:
|
||||||
|
- type: integer
|
||||||
|
- type: string
|
||||||
|
description: Specifies the output format of the exposed
|
||||||
|
resources, defaults to "1"
|
||||||
|
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
|
||||||
|
x-kubernetes-int-or-string: true
|
||||||
|
resource:
|
||||||
|
description: 'Required: resource to select'
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- resource
|
||||||
|
type: object
|
||||||
|
secretKeyRef:
|
||||||
|
description: Selects a key of a secret in the pod's namespace
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
description: The key of the secret to select from. Must
|
||||||
|
be a valid secret key.
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||||
|
TODO: Add other useful fields. apiVersion, kind, uid?'
|
||||||
|
type: string
|
||||||
|
optional:
|
||||||
|
description: Specify whether the Secret or its key must
|
||||||
|
be defined
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- key
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- environmentVariable
|
||||||
|
- key
|
||||||
|
- sensitive
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
vcs:
|
||||||
|
description: Details of the VCS repository we want to connect to the
|
||||||
|
workspace
|
||||||
|
nullable: true
|
||||||
|
properties:
|
||||||
|
branch:
|
||||||
|
description: The repository branch to use
|
||||||
|
type: string
|
||||||
|
ingress_submodules:
|
||||||
|
description: Whether submodules should be fetched when cloning the
|
||||||
|
VCS repository (Defaults to false)
|
||||||
|
type: boolean
|
||||||
|
repo_identifier:
|
||||||
|
description: A reference to your VCS repository in the format org/repo
|
||||||
|
type: string
|
||||||
|
token_id:
|
||||||
|
description: Token ID of the VCS Connection (OAuth Connection Token)
|
||||||
|
to use https://www.terraform.io/docs/cloud/vcs
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- repo_identifier
|
||||||
|
- token_id
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- organization
|
||||||
|
- secretsMountPath
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: WorkspaceStatus defines the observed state of Workspace
|
||||||
|
properties:
|
||||||
|
configVersionID:
|
||||||
|
description: Configuration Version ID
|
||||||
|
type: string
|
||||||
|
outputs:
|
||||||
|
description: Outputs from state file
|
||||||
|
items:
|
||||||
|
description: OutputStatus outputs the values of Terraform output
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
description: Attribute name in module
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
description: Value
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
runID:
|
||||||
|
description: Run ID
|
||||||
|
type: string
|
||||||
|
runStatus:
|
||||||
|
description: Run Status gets the run status
|
||||||
|
type: string
|
||||||
|
workspaceID:
|
||||||
|
description: Workspace ID
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- configVersionID
|
||||||
|
- runID
|
||||||
|
- runStatus
|
||||||
|
- workspaceID
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
version: v1alpha1
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
status:
|
||||||
|
acceptedNames:
|
||||||
|
kind: ""
|
||||||
|
plural: ""
|
||||||
|
conditions: []
|
||||||
|
storedVersions: []
|
||||||
33
plugin/builtin/iampolicygenerator/IAMPolicyGenerator.go
Normal file
33
plugin/builtin/iampolicygenerator/IAMPolicyGenerator.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
//go:generate pluginator
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/filters/iampolicygenerator"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type plugin struct {
|
||||||
|
types.IAMPolicyGeneratorArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
//noinspection GoUnusedGlobalVariable
|
||||||
|
var KustomizePlugin plugin
|
||||||
|
|
||||||
|
func (p *plugin) Config(h *resmap.PluginHelpers, config []byte) (err error) {
|
||||||
|
p.IAMPolicyGeneratorArgs = types.IAMPolicyGeneratorArgs{}
|
||||||
|
err = yaml.Unmarshal(config, p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plugin) Generate() (resmap.ResMap, error) {
|
||||||
|
r := resmap.New()
|
||||||
|
err := r.ApplyFilter(iampolicygenerator.Filter{
|
||||||
|
IAMPolicyGenerator: p.IAMPolicyGeneratorArgs,
|
||||||
|
})
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
12
plugin/builtin/iampolicygenerator/go.mod
Normal file
12
plugin/builtin/iampolicygenerator/go.mod
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
module sigs.k8s.io/kustomize/plugin/builtin/configmapgenerator
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
sigs.k8s.io/kustomize/api v0.8.9
|
||||||
|
sigs.k8s.io/yaml v1.2.0
|
||||||
|
)
|
||||||
|
|
||||||
|
replace sigs.k8s.io/kustomize/api => ../../../api
|
||||||
|
|
||||||
|
replace sigs.k8s.io/kustomize/kyaml => ../../../kyaml
|
||||||
228
plugin/builtin/iampolicygenerator/go.sum
Normal file
228
plugin/builtin/iampolicygenerator/go.sum
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||||
|
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||||
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||||
|
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
|
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
|
||||||
|
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||||
|
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||||
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
|
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
|
||||||
|
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||||
|
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||||
|
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||||
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||||
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
||||||
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
|
||||||
|
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||||
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
|
||||||
|
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
|
||||||
|
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
|
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
|
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||||
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||||
|
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||||
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
|
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||||
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
|
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
|
||||||
|
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||||
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
|
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
|
||||||
|
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||||
|
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||||
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
|
||||||
|
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||||
|
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||||
|
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user