Compare commits

..

1 Commits

Author SHA1 Message Date
natasha41575
4414bdb0a7 temporarily disable linter 2022-07-29 15:13:52 -05:00
553 changed files with 69540 additions and 56135 deletions

68
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,68 @@
---
name: Bug report
about: Create a report to help us improve
title: ""
labels:
- kind/bug
assignees: ""
---
<!--
Please read this page: https://kubectl.docs.kubernetes.io/contributing/kustomize/bugs/ before
filing a bug
-->
<!-- Feel free to skip the sections if they are not applicable. -->
**Describe the bug**
<!-- A clear and concise description of what the bug is. -->
**Files that can reproduce the issue**
<!--
We cannot figure out or fix the issue if we don't know how to reproduce. Please
provide a minimum set of files that can reproduce the issue. You can paste the
file contents here or provide a link to a tarball or git repo.
Example:
kustomization.yaml
```
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
...
```
resources.yaml
```
apiVersion: v1
kind: Deployment
...
```
...
-->
**Expected output**
<!-- What's the expected output? -->
**Actual output**
<!-- What's the actual output? -->
**Kustomize version**
<!-- Please use the latest version when it's possible. -->
**Platform**
<!-- Linux/macOS/Windows? -->
**Additional context**
<!-- Add any other context about the problem here. -->

View File

@@ -1,87 +0,0 @@
name: Bug report
description: File a bug report
labels: ["kind/bug"]
body:
- type: textarea
id: problem
attributes:
label: What happened?
description: |
Please provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner.
If this matter is security related, please disclose it privately via https://kubernetes.io/security
validations:
required: true
- type: textarea
id: expected
attributes:
label: What did you expect to happen?
validations:
required: true
- type: textarea
id: repro-files
attributes:
label: How can we reproduce it (as minimally and precisely as possible)?
description: Please provide a minimum set of files that can reproduce the issue. You can paste the file contents here or provide a link to a tarball or git repo. Even better, submit a tests case in the api/krusty package! For more information on how to do that, see https://kubectl.docs.kubernetes.io/contributing/kustomize/bugs/.
value: |
```yaml
# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- resources.yaml
```
```yaml
# resources.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: test-object
data:
placeholder: data
```
validations:
required: true
- type: textarea
id: expected-output
attributes:
label: Expected output
description: If you are able to provide reproduction files, please include the output you expect here.
value: |
```yaml
```
validations:
required: false
- type: textarea
id: actual-output
attributes:
label: Actual output
description: If you are able to provide reproduction files, please include the output they currently produce here.
value: |
```yaml
```
validations:
required: false
- type: input
id: kustomize-version
attributes:
label: Kustomize version
description: Please use the latest version whenever possible.
placeholder: What version of Kustomize are you using?
validations:
required: true
- type: dropdown
id: os
attributes:
label: Operating system
options:
- Linux
- MacOS
- Windows
- Other
validations:
required: false

1
.github/ISSUE_TEMPLATE/config.yaml vendored Normal file
View File

@@ -0,0 +1 @@
blank_issues_enabled: true

View File

@@ -1,6 +0,0 @@
contact_links:
- name: Support request
url: https://discuss.kubernetes.io
about: |
Please do not submit support requests or questions as issues.
Ask your question in the Kubernetes Community Forums, or in the #kustomize channel on Kubernetes Slack (https://slack.k8s.io).

View File

@@ -0,0 +1,26 @@
---
name: Feature request
about: Suggest an idea for this project
title: ""
labels:
- kind/feature
assignees: ""
---
<!-- Feel free to skip the sections if they are not applicable. -->
**Is your feature request related to a problem? Please describe.**
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
**Describe the solution you'd like**
<!-- A clear and concise description of what you want to happen. -->
**Describe alternatives you've considered**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->

View File

@@ -1,64 +0,0 @@
name: Feature request
description: Propose an enhancement to Kustomize
labels: kind/feature
body:
- type: markdown
attributes:
value: |
Small, straightforward enhancements can be proposed in regular GitHub issues using the template below. As a rule of thumb, the enhancement should be resolvable in a single PR that is at most size L. Anything more involved requires a mini (in-repo) enhancement proposal, and features with implications for kubectl require a full [KEP](https://github.com/kubernetes/enhancements).
For more information on the Kustomize enhancement process, see: https://github.com/kubernetes-sigs/kustomize/tree/master/proposals.
When in doubt, go ahead and fill out the template below; the maintainers will let you know if a KEP is required.
- type: checkboxes
attributes:
label: Eschewed features
description: Some features are out of scope for Kustomize because they are incompatible with its foundational design principles. Please review the [Eschewed Features](https://kubectl.docs.kubernetes.io/faq/kustomize/eschewedfeatures/) documentation before submitting your feature request.
options:
- label: This issue is not requesting templating, unstuctured edits, build-time side-effects from args or env vars, or any other eschewed feature.
required: true
- type: textarea
id: feature-description
attributes:
label: What would you like to have added?
validations:
required: true
- type: textarea
id: rationale
attributes:
label: Why is this needed?
validations:
required: true
- type: textarea
id: current-alternatives
attributes:
label: Can you accomplish the motivating task without this feature, and if so, how?
validations:
required: true
- type: textarea
id: design-alternatives
attributes:
label: What other solutions have you considered?
validations:
required: true
- type: textarea
id: additional-info
attributes:
label: Anything else we should know?
validations:
required: false
- type: checkboxes
attributes:
label: Feature ownership
description: The Kustomize project, like many areas of Kubernetes, currently lacks enough contributors to adequately respond to all proposals that have merit. Offering to build and support the feature yourself can help get traction for your request.
options:
- label: I am interested in contributing this feature myself! 🎉
required: false

9
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View File

@@ -0,0 +1,9 @@
---
name: Question
about: Ask a question about the kustomize
title: "[Question]"
labels: ""
assignees: ""
---
<!-- Please describe your question here -->

View File

@@ -10,21 +10,26 @@ permissions:
contents: read
jobs:
lint:
name: Lint
runs-on: [ubuntu-latest]
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v3
with:
go-version: ^1.18
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Go 1.x
uses: actions/setup-go@v3
with:
go-version-file: go.work
id: go
- name: Lint
run: make lint
- name: Verify boilerplate
run: make check-license
@@ -32,56 +37,66 @@ jobs:
name: Test Linux
runs-on: [ubuntu-latest]
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Set up Go 1.x
uses: actions/setup-go@v3
with:
go-version-file: go.work
id: go
- name: Test all modules
run: make test-unit-non-plugin
env:
KUSTOMIZE_DOCKER_E2E: true
- name: Set up Go 1.x
uses: actions/setup-go@v3
with:
go-version: ^1.18
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Test all modules
run: make test-unit-non-plugin
env:
KUSTOMIZE_DOCKER_E2E: true
test-macos:
name: Test MacOS
runs-on: [macos-latest]
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Set up Go 1.x
uses: actions/setup-go@v3
with:
go-version-file: go.work
id: go
- name: Test all modules
run: make test-unit-non-plugin
env:
KUSTOMIZE_DOCKER_E2E: false # docker not installed on mac
- name: Set up Go 1.x
uses: actions/setup-go@v3
with:
go-version: ^1.18
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Test all modules
run: make test-unit-non-plugin
env:
KUSTOMIZE_DOCKER_E2E: false # docker not installed on mac
test-windows:
name: Test Windows
runs-on: [windows-latest]
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Set up Go 1.x
uses: actions/setup-go@v3
with:
go-version-file: go.work
id: go
- name: Test kyaml
run: go test -cover ./...
working-directory: ./kyaml
- name: Test cmd/config
run: go test -cover ./...
working-directory: ./cmd/config
env:
KUSTOMIZE_DOCKER_E2E: false # docker on windows not working well yet
# TODO (#4001): replace specific modules above with this once Windows tests are passing.
#- name: Test all modules
# run: make test-unit-non-plugin
# env:
# KUSTOMIZE_DOCKER_E2E: false # docker on windows not working well yet
- name: Set up Go 1.x
uses: actions/setup-go@v3
with:
go-version: ^1.18
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Test kyaml
run: go test -cover ./...
working-directory: ./kyaml
- name: Test cmd/config
run: go test -cover ./...
working-directory: ./cmd/config
env:
KUSTOMIZE_DOCKER_E2E: false # docker on windows not working well yet
# TODO (#4001): replace specific modules above with this once Windows tests are passing.
#- name: Test all modules
# run: make test-unit-non-plugin
# env:
# KUSTOMIZE_DOCKER_E2E: false # docker on windows not working well yet

View File

@@ -3,37 +3,91 @@
run:
deadline: 5m
go: '1.19'
linters:
enable-all: true
disable:
- cyclop
- exhaustivestruct
- forbidigo
- funlen
- gci
- gocognit
- godot
- godox
- goerr113
- gofumpt
- ifshort # too many false positives
- ireturn
- nilnil
- nlreturn
- noctx
- paralleltest
- stylecheck
- varnamelen
- wsl
- exhaustruct
# please, do not use `enable-all`: it's deprecated and will be removed soon.
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
disable-all: true
enable:
- asciicheck
- bidichk
- bodyclose
- contextcheck
# - cyclop
- deadcode
- scopelint
- nonamedreturns
- golint
- maintidx
- nosnakecase
- depguard
- dogsled
- dupl
- durationcheck
- errcheck
- errname
- errorlint
- exhaustive
# - exhaustivestruct
- exportloopref
# - forbidigo
- forcetypeassert
# - funlen
# - gci
- gochecknoglobals
- gochecknoinits
# - gocognit
- goconst
- gocritic
- gocyclo
# - godot
# - godox
# - goerr113
- gofmt
# - gofumpt
- goheader
- goimports
- gomnd
- gomoddirectives
- gomodguard
- goprintffuncname
- gosec
- gosimple
- govet
# - ifshort # too many false positives
- importas
- ineffassign
# - ireturn
- lll
- makezero
- misspell
- nakedret
- nestif
- nilerr
# - nilnil
# - nlreturn
# - noctx
- nolintlint
# - paralleltest
- prealloc
- predeclared
- promlinter
- revive
- rowserrcheck
- sqlclosecheck
- staticcheck
- structcheck
# - stylecheck
- tagliatelle
- tenv
- testpackage
- thelper
- tparallel
- typecheck
- unconvert
- unparam
- unused
- varcheck
# - varnamelen
- wastedassign
- whitespace
- wrapcheck
# - wsl
linters-settings:
dupl:
@@ -48,15 +102,14 @@ linters-settings:
arguments:
- [ "ID", "API", "JSON" ] # AllowList
- [ ] # DenyList
gomnd:
ignored-functions:
- os.WriteFile
- make
gomoddirectives:
replace-local: true
gosec:
config:
G306: "0644"
gomnd:
ignored-functions:
- ioutil.WriteFile
wrapcheck:
ignoreSigs:
# defaults

View File

@@ -3,7 +3,7 @@
#
# Makefile for kustomize CLI and API.
LATEST_RELEASE=v5.0.1
LATEST_V4_RELEASE=v4.5.5
SHELL := /usr/bin/env bash
GOOS = $(shell go env GOOS)
@@ -75,8 +75,7 @@ $(MYGOBIN)/pluginator:
# Build from local source.
$(MYGOBIN)/kustomize: build-kustomize-api
cd kustomize; \
go install -ldflags "-X sigs.k8s.io/kustomize/api/provenance.buildDate=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')" \
.
go install .
kustomize: $(MYGOBIN)/kustomize
@@ -95,13 +94,12 @@ generate-kustomize-api:
.PHONY: verify-kustomize-repo
verify-kustomize-repo: \
install-tools \
lint \
check-license \
test-unit-all \
build-non-plugin-all \
test-go-mod \
test-examples-kustomize-against-HEAD \
test-examples-kustomize-against-latest-release
test-examples-kustomize-against-v4-release
# The following target referenced by a file in
# https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize
@@ -112,7 +110,7 @@ prow-presubmit-check: \
test-go-mod \
build-non-plugin-all \
test-examples-kustomize-against-HEAD \
test-examples-kustomize-against-latest-release
test-examples-kustomize-against-v4-release
.PHONY: license
license: $(MYGOBIN)/addlicense
@@ -134,11 +132,11 @@ test-unit-all: \
# This target is used by our Github Actions CI to run unit tests for all non-plugin modules in multiple GOOS environments.
.PHONY: test-unit-non-plugin
test-unit-non-plugin:
./hack/for-each-module.sh "make test" "./plugin/*" 16
./hack/for-each-module.sh "make test" "./plugin/*" 15
.PHONY: build-non-plugin-all
build-non-plugin-all:
./hack/for-each-module.sh "make build" "./plugin/*" 16
./hack/for-each-module.sh "make build" "./plugin/*" 15
.PHONY: test-unit-kustomize-plugins
test-unit-kustomize-plugins:
@@ -153,7 +151,7 @@ functions-examples-all:
done
test-go-mod:
./hack/for-each-module.sh "go mod tidy -v"
./hack/for-each-module.sh "go list -m -json all > /dev/null && go mod tidy -v"
.PHONY:
verify-kustomize-e2e: $(MYGOBIN)/mdrip $(MYGOBIN)/kind
@@ -170,8 +168,8 @@ test-examples-kustomize-against-HEAD: $(MYGOBIN)/kustomize $(MYGOBIN)/mdrip
./hack/testExamplesAgainstKustomize.sh HEAD
.PHONY:
test-examples-kustomize-against-latest-release: $(MYGOBIN)/mdrip
./hack/testExamplesAgainstKustomize.sh v5@$(LATEST_RELEASE)
test-examples-kustomize-against-v4-release: $(MYGOBIN)/mdrip
./hack/testExamplesAgainstKustomize.sh v4@$(LATEST_V4_RELEASE)
# --- Cleanup targets ---

View File

@@ -14,7 +14,6 @@ include $(KUSTOMIZE_ROOT)/Makefile-tools.mk
.PHONY: lint test fix fmt tidy vet build
lint: $(MYGOBIN)/golangci-lint
$(MYGOBIN)/golangci-lint cache clean # Workaround for https://github.com/golangci/golangci-lint/issues/3228
$(MYGOBIN)/golangci-lint \
-c $$KUSTOMIZE_ROOT/.golangci.yml \
--path-prefix $(shell pwd | sed -E 's|(.*\/kustomize)/(.*)|\2|') \

View File

@@ -34,7 +34,7 @@ _builtinplugins = \
HashTransformer.go \
ImageTagTransformer.go \
LabelTransformer.go \
SortOrderTransformer.go \
LegacyOrderTransformer.go \
NamespaceTransformer.go \
PatchJson6902Transformer.go \
PatchStrategicMergeTransformer.go \
@@ -63,7 +63,7 @@ $(pGen)/GkeSaGenerator.go: $(pSrc)/gkesagenerator/GkeSaGenerator.go
$(pGen)/HashTransformer.go: $(pSrc)/hashtransformer/HashTransformer.go
$(pGen)/ImageTagTransformer.go: $(pSrc)/imagetagtransformer/ImageTagTransformer.go
$(pGen)/LabelTransformer.go: $(pSrc)/labeltransformer/LabelTransformer.go
$(pGen)/SortOrderTransformer.go: $(pSrc)/sortordertransformer/SortOrderTransformer.go
$(pGen)/LegacyOrderTransformer.go: $(pSrc)/legacyordertransformer/LegacyOrderTransformer.go
$(pGen)/NamespaceTransformer.go: $(pSrc)/namespacetransformer/NamespaceTransformer.go
$(pGen)/PatchJson6902Transformer.go: $(pSrc)/patchjson6902transformer/PatchJson6902Transformer.go
$(pGen)/PatchStrategicMergeTransformer.go: $(pSrc)/patchstrategicmergetransformer/PatchStrategicMergeTransformer.go
@@ -79,7 +79,7 @@ $(pGen)/HelmChartInflationGenerator.go: $(pSrc)/helmchartinflationgenerator/Helm
# The (verbose but portable) Makefile way to convert to lowercase.
toLowerCase = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1))))))))))))))))))))))))))
$(pGen)/%.go: $(MYGOBIN)/pluginator $(MYGOBIN)/goimports
$(pGen)/%.go: $(MYGOBIN)/pluginator
@echo "generating $*"
( \
set -e; \

View File

@@ -1,8 +1,6 @@
# Copyright 2022 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
GOLANGCI_LINT_VERSION=v1.51.2
MYGOBIN = $(shell go env GOBIN)
ifeq ($(MYGOBIN),)
MYGOBIN = $(shell go env GOPATH)/bin
@@ -30,7 +28,7 @@ uninstall-out-of-tree-tools:
rm -f $(MYGOBIN)/stringer
$(MYGOBIN)/golangci-lint:
go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.2
$(MYGOBIN)/mdrip:
go install github.com/monopole/mdrip@v1.0.2
@@ -47,6 +45,9 @@ $(MYGOBIN)/mdtogo:
$(MYGOBIN)/addlicense:
go install github.com/google/addlicense@latest
$(MYGOBIN)/statik:
go install github.com/rakyll/statik@latest
$(MYGOBIN)/goreleaser:
go install github.com/goreleaser/goreleaser@v0.179.0 # https://github.com/kubernetes-sigs/kustomize/issues/4542
@@ -94,7 +95,7 @@ $(MYGOBIN)/helmV3:
( \
set -e; \
d=$(shell mktemp -d); cd $$d; \
tgzFile=helm-v3.10.2-$(GOOS)-$(GOARCH).tar.gz; \
tgzFile=helm-v3.6.3-$(GOOS)-$(GOARCH).tar.gz; \
wget https://get.helm.sh/$$tgzFile; \
tar -xvzf $$tgzFile; \
mv $(GOOS)-$(GOARCH)/helm $(MYGOBIN)/helmV3; \

1
OWNERS
View File

@@ -1,5 +1,6 @@
# See https://github.com/kubernetes/community/blob/master/community-membership.md
approvers:
- kustomize-approvers
reviewers:
- kustomize-reviewers

View File

@@ -11,14 +11,19 @@ aliases:
- knverey
- natasha41575
- yuwenma
- annasong20
- koba1t
# emeritus:
# - liujingfang1
# - Shell32-Natsu
# - justinsb
# - monopole
# - pwittrock
# - mengqiy
# - mortent
# - phanimarupaka
kyaml-approvers:
- mengqiy
- mortent
- phanimarupaka
kyaml-reviewers:
- mengqiy
- mortent
- phanimarupaka
emeritus-approvers:
- liujingfang1
- Shell32-Natsu
- justinsb
- monopole
- pwittrock

View File

@@ -20,14 +20,6 @@ This tool is sponsored by [sig-cli] ([KEP]).
## kubectl integration
To find the kustomize version embedded in recent versions of kubectl, run `kubectl version`:
```sh
> kubectl version --short --client
Client Version: v1.26.0
Kustomize Version: v4.5.7
```
The kustomize build flow at [v2.0.3] was added
to [kubectl v1.14][kubectl announcement]. The kustomize
flow in kubectl remained frozen at v2.0.3 until kubectl v1.21,

View File

@@ -4,10 +4,11 @@
include ../Makefile-modules.mk
test:
go test -v -timeout 45m -cover ./... -ldflags "-X sigs.k8s.io/kustomize/api/provenance.buildDate=2023-01-31T23:38:41Z -X sigs.k8s.io/kustomize/api/provenance.version=(test)"
go test -v -timeout 45m -cover ./... -ldflags "-X sigs.k8s.io/kustomize/api/provenance.version=v444.333.222"
cd krusty/openapitests; OPENAPI_TEST=true go test -v -timeout 45m -p 1 -cover ./...
build:
go build -ldflags "-X sigs.k8s.io/kustomize/api/provenance.buildDate=$(shell date -u +"%Y-%m-%dT%H:%M:%SZ")" ./...
go build -ldflags "-X sigs.k8s.io/kustomize/api/provenance.version=v444.333.222" ./...
generate: $(MYGOBIN)/k8scopy $(MYGOBIN)/stringer
go generate ./...

View File

@@ -16,6 +16,7 @@ type (
IAMPolicyGeneratorPlugin = internal.IAMPolicyGeneratorPlugin
ImageTagTransformerPlugin = internal.ImageTagTransformerPlugin
LabelTransformerPlugin = internal.LabelTransformerPlugin
LegacyOrderTransformerPlugin = internal.LegacyOrderTransformerPlugin
NamespaceTransformerPlugin = internal.NamespaceTransformerPlugin
PatchJson6902TransformerPlugin = internal.PatchJson6902TransformerPlugin
PatchStrategicMergeTransformerPlugin = internal.PatchStrategicMergeTransformerPlugin
@@ -36,6 +37,7 @@ var (
NewIAMPolicyGeneratorPlugin = internal.NewIAMPolicyGeneratorPlugin
NewImageTagTransformerPlugin = internal.NewImageTagTransformerPlugin
NewLabelTransformerPlugin = internal.NewLabelTransformerPlugin
NewLegacyOrderTransformerPlugin = internal.NewLegacyOrderTransformerPlugin
NewNamespaceTransformerPlugin = internal.NewNamespaceTransformerPlugin
NewPatchJson6902TransformerPlugin = internal.NewPatchJson6902TransformerPlugin
NewPatchStrategicMergeTransformerPlugin = internal.NewPatchStrategicMergeTransformerPlugin

View File

@@ -102,7 +102,5 @@ func TestTrackableSetter_SetEntryIfEmpty_BadInputNodeKind(t *testing.T) {
fn := filtersutil.TrackableSetter{}.SetEntryIfEmpty("foo", "false", yaml.NodeTagBool)
rn := yaml.NewListRNode("nope")
rn.AppendToFieldPath("dummy", "path")
assert.EqualError(t, fn(rn), `wrong node kind: expected MappingNode but got SequenceNode: node contents:
- nope
`)
assert.EqualError(t, fn(rn), "wrong Node Kind for dummy.path expected: MappingNode was SequenceNode: value: {- nope}")
}

View File

@@ -7,11 +7,11 @@ import (
"fmt"
"strings"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/filters/fieldspec"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/resid"
"sigs.k8s.io/kustomize/kyaml/yaml"
@@ -64,7 +64,7 @@ func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
FieldSpec: f.NameFieldToUpdate,
SetValue: f.set,
}); err != nil {
return nil, errors.WrapPrefixf(
return nil, errors.Wrapf(
err, "updating name reference in '%s' field of '%s'",
f.NameFieldToUpdate.Path, f.Referrer.CurId().String())
}
@@ -104,7 +104,7 @@ func (f Filter) setMapping(node *yaml.RNode) error {
}
nameNode, err := node.Pipe(yaml.FieldMatcher{Name: "name"})
if err != nil {
return errors.WrapPrefixf(err, "trying to match 'name' field")
return errors.Wrap(err, "trying to match 'name' field")
}
if nameNode == nil {
// This is a _configuration_ error; the field path
@@ -153,7 +153,7 @@ func (f Filter) filterMapCandidatesByNamespace(
node *yaml.RNode) ([]*resource.Resource, error) {
namespaceNode, err := node.Pipe(yaml.FieldMatcher{Name: "namespace"})
if err != nil {
return nil, errors.WrapPrefixf(err, "trying to match 'namespace' field")
return nil, errors.Wrap(err, "trying to match 'namespace' field")
}
if namespaceNode == nil {
return f.ReferralCandidates.Resources(), nil

View File

@@ -56,11 +56,9 @@ func (ns Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
// Run runs the filter on a single node rather than a slice
func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
// Special handling for metadata.namespace and metadata.name -- :(
// Special handling for metadata.namespace -- :(
// never let SetEntry handle metadata.namespace--it will incorrectly include cluster-scoped resources
// only update metadata.name if api version is expected one--so-as it leaves other resources of kind namespace alone
apiVersion := node.GetApiVersion()
ns.FsSlice = ns.removeUnneededMetaFieldSpecs(apiVersion, ns.FsSlice)
ns.FsSlice = ns.removeMetaNamespaceFieldSpecs(ns.FsSlice)
gvk := resid.GvkFromNode(node)
if err := ns.metaNamespaceHack(node, gvk); err != nil {
return nil, err
@@ -81,11 +79,7 @@ func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode
CreateTag: yaml.NodeTagString,
})
invalidKindErr := &yaml.InvalidNodeKindError{}
if err != nil && errors.As(err, &invalidKindErr) && invalidKindErr.ActualNodeKind() != yaml.ScalarNode {
return nil, errors.WrapPrefixf(err, "namespace field specs must target scalar nodes")
}
return node, errors.WrapPrefixf(err, "namespace transformation failed")
return node, err
}
// metaNamespaceHack is a hack for implementing the namespace transform
@@ -180,7 +174,8 @@ func setNamespaceField(node *yaml.RNode, setter filtersutil.SetFn) error {
func (ns Filter) removeRoleBindingSubjectFieldSpecs(fs types.FsSlice) types.FsSlice {
var val types.FsSlice
for i := range fs {
if isRoleBinding(fs[i].Kind) && fs[i].Path == subjectsNamespacePath {
if isRoleBinding(fs[i].Kind) &&
(fs[i].Path == subjectsNamespacePath || fs[i].Path == subjectsField) {
continue
}
val = append(val, fs[i])
@@ -188,15 +183,12 @@ func (ns Filter) removeRoleBindingSubjectFieldSpecs(fs types.FsSlice) types.FsSl
return val
}
func (ns Filter) removeUnneededMetaFieldSpecs(apiVersion string, fs types.FsSlice) types.FsSlice {
func (ns Filter) removeMetaNamespaceFieldSpecs(fs types.FsSlice) types.FsSlice {
var val types.FsSlice
for i := range fs {
if fs[i].Path == types.MetadataNamespacePath {
continue
}
if apiVersion != types.MetadataNamespaceApiVersion && fs[i].Path == types.MetadataNamePath {
continue
}
val = append(val, fs[i])
}
return val

View File

@@ -732,50 +732,6 @@ spec:
protocol: "TCP"
- containerPort: 8301
protocol: "UDP"
`,
},
// Issue #4628
"should retain existing null values in targets": {
input: `
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: chart
spec:
releaseName: helm-chart
timeout: 15m
values:
chart:
replicaCount: null
autoscaling: true
`,
patch: yaml.MustParse(`
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: chart
spec:
releaseName: helm-chart
timeout: 15m
values:
deepgram-api:
some: value
`),
expected: `
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: chart
spec:
releaseName: helm-chart
timeout: 15m
values:
chart:
replicaCount: null
autoscaling: true
deepgram-api:
some: value
`,
},
}

View File

@@ -4,13 +4,13 @@
package replacement
import (
"errors"
"fmt"
"strings"
"sigs.k8s.io/kustomize/api/internal/utils"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/resid"
kyaml_utils "sigs.k8s.io/kustomize/kyaml/utils"
"sigs.k8s.io/kustomize/kyaml/yaml"
@@ -105,7 +105,7 @@ func getRefinedValue(options *types.FieldOptions, rn *yaml.RNode) (*yaml.RNode,
func applyReplacement(nodes []*yaml.RNode, value *yaml.RNode, targetSelectors []*types.TargetSelector) ([]*yaml.RNode, error) {
for _, selector := range targetSelectors {
if selector.Select == nil {
return nil, errors.Errorf("target must specify resources to select")
return nil, errors.New("target must specify resources to select")
}
if len(selector.FieldPaths) == 0 {
selector.FieldPaths = []string{types.DefaultReplacementFieldPath}
@@ -179,22 +179,29 @@ func rejectId(rejects []*types.Selector, id *resid.ResId) bool {
func copyValueToTarget(target *yaml.RNode, value *yaml.RNode, selector *types.TargetSelector) error {
for _, fp := range selector.FieldPaths {
createKind := yaml.Kind(0) // do not create
if selector.Options != nil && selector.Options.Create {
createKind = value.YNode().Kind
}
targetFieldList, err := target.Pipe(&yaml.PathMatcher{
Path: kyaml_utils.SmarterPathSplitter(fp, "."),
Create: createKind})
fieldPath := kyaml_utils.SmarterPathSplitter(fp, ".")
create, err := shouldCreateField(selector.Options, fieldPath)
if err != nil {
return errors.WrapPrefixf(err, fieldRetrievalError(fp, createKind != 0))
return err
}
targetFields, err := targetFieldList.Elements()
if err != nil {
return errors.WrapPrefixf(err, fieldRetrievalError(fp, createKind != 0))
}
if len(targetFields) == 0 {
return errors.Errorf(fieldRetrievalError(fp, createKind != 0))
var targetFields []*yaml.RNode
if create {
createdField, createErr := target.Pipe(yaml.LookupCreate(value.YNode().Kind, fieldPath...))
if createErr != nil {
return fmt.Errorf("error creating replacement node: %w", createErr)
}
targetFields = append(targetFields, createdField)
} else {
// may return multiple fields, always wrapped in a sequence node
foundFieldSequence, lookupErr := target.Pipe(&yaml.PathMatcher{Path: fieldPath})
if lookupErr != nil {
return fmt.Errorf("error finding field in replacement target: %w", lookupErr)
}
targetFields, err = foundFieldSequence.Elements()
if err != nil {
return fmt.Errorf("error fetching elements in replacement target: %w", err)
}
}
for _, t := range targetFields {
@@ -202,17 +209,11 @@ func copyValueToTarget(target *yaml.RNode, value *yaml.RNode, selector *types.Ta
return err
}
}
}
return nil
}
func fieldRetrievalError(fieldPath string, isCreate bool) string {
if isCreate {
return fmt.Sprintf("unable to find or create field %q in replacement target", fieldPath)
}
return fmt.Sprintf("unable to find field %q in replacement target", fieldPath)
}
func setFieldValue(options *types.FieldOptions, targetField *yaml.RNode, value *yaml.RNode) error {
value = value.Copy()
if options != nil && options.Delimiter != "" {
@@ -242,3 +243,16 @@ func setFieldValue(options *types.FieldOptions, targetField *yaml.RNode, value *
return nil
}
func shouldCreateField(options *types.FieldOptions, fieldPath []string) (bool, error) {
if options == nil || !options.Create {
return false, nil
}
// create option is not supported in a wildcard matching
for _, f := range fieldPath {
if f == "*" {
return false, fmt.Errorf("cannot support create option in a multi-value target")
}
}
return true, nil
}

View File

@@ -9,7 +9,7 @@ import (
"github.com/stretchr/testify/assert"
filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest"
"sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/yaml"
)
func TestFilter(t *testing.T) {
@@ -1198,6 +1198,11 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy2
`,
replacements: `replacements:
- source:
@@ -1211,6 +1216,15 @@ metadata:
- spec.template.spec.containers
options:
create: true
- source:
kind: Pod
name: pod
fieldPath: spec.containers
targets:
- select:
name: deploy2
fieldPaths:
- spec.template.spec.containers
`,
expected: `apiVersion: v1
kind: Pod
@@ -1231,6 +1245,11 @@ spec:
containers:
- image: busybox
name: myapp-container
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy2
`,
},
"complex type with delimiter in source": {
@@ -1479,85 +1498,6 @@ spec:
value: sample-deploy
- name: foo
value: bar
- image: nginx
name: sidecar
env:
- name: deployment-name
value: sample-deploy`,
},
"one replacements target should create multiple values": {
input: `apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: sample-deploy
name: sample-deploy
spec:
replicas: 1
selector:
matchLabels:
app: sample-deploy
template:
metadata:
labels:
app: sample-deploy
spec:
containers:
- image: other
name: do-not-modify-me
env:
- name: foo
value: bar
- image: nginx
name: main
env:
- name: foo
value: bar
- image: nginx
name: sidecar
`,
replacements: `replacements:
- source:
kind: Deployment
name: sample-deploy
fieldPath: metadata.name
targets:
- select:
kind: Deployment
options:
create: true
fieldPaths:
- spec.template.spec.containers.[image=nginx].env.[name=deployment-name].value
`,
expected: `apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: sample-deploy
name: sample-deploy
spec:
replicas: 1
selector:
matchLabels:
app: sample-deploy
template:
metadata:
labels:
app: sample-deploy
spec:
containers:
- image: other
name: do-not-modify-me
env:
- name: foo
value: bar
- image: nginx
name: main
env:
- name: foo
value: bar
- name: deployment-name
value: sample-deploy
- image: nginx
name: sidecar
env:
@@ -1731,110 +1671,7 @@ spec:
options:
create: true
`,
expected: `apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: sample-deploy
name: sample-deploy
spec:
replicas: 1
selector:
matchLabels:
app: sample-deploy
template:
metadata:
labels:
app: sample-deploy
spec:
containers:
- image: nginx
name: main
env:
- name: other-env
value: YYYYY
- name: deployment-name
value: sample-deploy
`,
},
"Issue 1493: wildcard to create or replace field in all containers in all workloads": {
input: `apiVersion: v1
kind: ConfigMap
metadata:
name: policy
data:
restart: OnFailure
---
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- image: nginx
name: main
- image: nginx
name: sidecar
imagePullPolicy: Always
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
spec:
containers:
- image: nginx
name: main
imagePullPolicy: Always
- image: nginx
name: sidecar
`,
replacements: `replacements:
- source:
kind: ConfigMap
name: policy
fieldPath: data.restart
targets:
- select:
kind: Pod
fieldPaths:
- spec.containers.*.imagePullPolicy
options:
create: true
`,
expected: `apiVersion: v1
kind: ConfigMap
metadata:
name: policy
data:
restart: OnFailure
---
apiVersion: v1
kind: Pod
metadata:
name: pod1
spec:
containers:
- image: nginx
name: main
imagePullPolicy: OnFailure
- image: nginx
name: sidecar
imagePullPolicy: OnFailure
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
spec:
containers:
- image: nginx
name: main
imagePullPolicy: OnFailure
- image: nginx
name: sidecar
imagePullPolicy: OnFailure
`,
expectedErr: "cannot support create option in a multi-value target",
},
"multiple field paths in target": {
input: `apiVersion: v1
@@ -2579,257 +2416,6 @@ spec:
restartPolicy: new
`,
},
"issue4761 - path not in target with create: true": {
input: `
---
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: request-id
spec:
configPatches:
- applyTo: NETWORK_FILTER
- applyTo: NETWORK_FILTER
---
apiVersion: v1
kind: ConfigMap
metadata:
name: istio-version
annotations:
config.kubernetes.io/local-config: true
data:
ISTIO_REGEX: '^1\.14.*'
`,
replacements: `
replacements:
- source:
kind: ConfigMap
name: istio-version
fieldPath: data.ISTIO_REGEX
targets:
- select:
kind: EnvoyFilter
fieldPaths:
- spec.configPatches.0.match.proxy.proxyVersion
- spec.configPatches.1.match.proxy.proxyVersion
- spec.configPatches.2.match.proxy.proxyVersion
- spec.configPatches.3.match.proxy.proxyVersion
options:
create: true
`,
expected: `
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: request-id
spec:
configPatches:
- applyTo: NETWORK_FILTER
match:
proxy:
proxyVersion: ^1\.14.*
- applyTo: NETWORK_FILTER
match:
proxy:
proxyVersion: ^1\.14.*
- match:
proxy:
proxyVersion: ^1\.14.*
- match:
proxy:
proxyVersion: ^1\.14.*
---
apiVersion: v1
kind: ConfigMap
metadata:
name: istio-version
annotations:
config.kubernetes.io/local-config: true
data:
ISTIO_REGEX: '^1\.14.*'
`,
},
"issue4761 - path not in target with create: false": {
input: `
---
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: request-id
spec:
configPatches:
- applyTo: NETWORK_FILTER
- applyTo: NETWORK_FILTER
---
apiVersion: v1
kind: ConfigMap
metadata:
name: istio-version
annotations:
config.kubernetes.io/local-config: true
data:
ISTIO_REGEX: '^1\.14.*'
`,
replacements: `
replacements:
- source:
kind: ConfigMap
name: istio-version
fieldPath: data.ISTIO_REGEX
targets:
- select:
kind: EnvoyFilter
fieldPaths:
- spec.configPatches.0.match.proxy.proxyVersion
- spec.configPatches.1.match.proxy.proxyVersion
- spec.configPatches.2.match.proxy.proxyVersion
- spec.configPatches.3.match.proxy.proxyVersion
options:
create: false
`,
expectedErr: "unable to find field \"spec.configPatches.0.match.proxy.proxyVersion\" in replacement target",
},
"issue4761 - wildcard solution": {
input: `
---
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: request-id
spec:
configPatches:
- applyTo: NETWORK_FILTER
- applyTo: NETWORK_FILTER
---
apiVersion: v1
kind: ConfigMap
metadata:
name: istio-version
annotations:
config.kubernetes.io/local-config: true
data:
ISTIO_REGEX: '^1\.14.*'
`,
replacements: `
replacements:
- source:
kind: ConfigMap
name: istio-version
fieldPath: data.ISTIO_REGEX
targets:
- select:
kind: EnvoyFilter
fieldPaths:
- spec.configPatches.*.match.proxy.proxyVersion
options:
create: true
`,
expected: `
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: request-id
spec:
configPatches:
- applyTo: NETWORK_FILTER
match:
proxy:
proxyVersion: ^1\.14.*
- applyTo: NETWORK_FILTER
match:
proxy:
proxyVersion: ^1\.14.*
---
apiVersion: v1
kind: ConfigMap
metadata:
name: istio-version
annotations:
config.kubernetes.io/local-config: true
data:
ISTIO_REGEX: '^1\.14.*'
`,
},
"append to sequence using index": {
input: `apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myingress
spec:
rules:
- host: myingress.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-svc
port:
number: 80
`,
replacements: `replacements:
- source:
kind: Ingress
name: myingress
fieldPath: spec.rules.0.host
targets:
- select:
kind: Ingress
name: myingress
fieldPaths:
- spec.tls.0.hosts.0
- spec.tls.0.secretName
options:
create: true
`,
expected: `apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myingress
spec:
rules:
- host: myingress.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-svc
port:
number: 80
tls:
- hosts:
- myingress.example.com
secretName: myingress.example.com`,
},
"fail to append to sequence using a distant index": {
input: `apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myingress
spec:
rules:
- host: myingress.example.com
`,
replacements: `replacements:
- source:
kind: Ingress
name: myingress
fieldPath: spec.rules.0.host
targets:
- select:
kind: Ingress
name: myingress
fieldPaths:
- spec.tls.5.hosts.5
- spec.tls.5.secretName
options:
create: true
`,
expectedErr: "unable to find or create field \"spec.tls.5.hosts.5\" in replacement target: index 5 specified but only 0 elements found",
},
}
for tn, tc := range testCases {

View File

@@ -1,37 +1,38 @@
module sigs.k8s.io/kustomize/api
go 1.19
go 1.18
require (
github.com/evanphx/json-patch v4.11.0+incompatible
github.com/go-errors/errors v1.4.2
github.com/go-errors/errors v1.0.1
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/imdario/mergo v0.3.6
github.com/stretchr/testify v1.8.1
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.7.0
gopkg.in/yaml.v2 v2.4.0
k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596
sigs.k8s.io/kustomize/kyaml v0.14.2
sigs.k8s.io/yaml v1.3.0
k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661
sigs.k8s.io/kustomize/kyaml v0.13.8
sigs.k8s.io/yaml v1.2.0
)
require (
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.1 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.5 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.5 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/xlab/treeprint v1.1.0 // indirect
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

View File

@@ -1,5 +1,11 @@
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/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/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
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=
@@ -10,18 +16,26 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
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/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/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs=
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8=
github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
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-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
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=
@@ -43,75 +57,113 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.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.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
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.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
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/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_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk=
github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@@ -131,25 +183,34 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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.3.0/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-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596 h1:8cNCQs+WqqnSpZ7y0LMQPKD+RZUHU17VqLPMW3qxnxc=
k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596/go.mod h1:/BYxry62FuDzmI+i9B+X2pqfySRmSOW2ARmj5Zbqhj0=
sigs.k8s.io/kustomize/kyaml v0.14.2 h1:9WSwztbzwGszG1bZTziQUmVMrJccnyrLb5ZMKpJGvXw=
sigs.k8s.io/kustomize/kyaml v0.14.2/go.mod h1:AN1/IpawKilWD7V+YvQwRGUvuUOOWpjsHu6uHwonSF4=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661 h1:nqYOUleKLC/0P1zbU29F5q6aoezM6MOAVz+iyfQbZ5M=
k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M=
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
sigs.k8s.io/kustomize/kyaml v0.13.8 h1:L4dSaDb6dL5mzv0UWSrUw8bskcEW+EnNtIObT5BoRsU=
sigs.k8s.io/kustomize/kyaml v0.13.8/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4=
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=

View File

@@ -28,20 +28,12 @@ type KvLoader interface {
// Loader interface exposes methods to read bytes.
type Loader interface {
// Repo returns the repo location if this Loader was created from a url
// or the empty string otherwise.
Repo() string
// Root returns the root location for this Loader.
Root() string
// New returns Loader located at newRoot.
New(newRoot string) (Loader, error)
// Load returns the bytes read from the location or an error.
Load(location string) ([]byte, error)
// Cleanup cleans the loader
Cleanup() error
}

View File

@@ -7,11 +7,11 @@ import (
"encoding/json"
"strings"
"github.com/pkg/errors"
"k8s.io/kube-openapi/pkg/validation/spec"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/filesys"
"sigs.k8s.io/kustomize/kyaml/resid"
"sigs.k8s.io/yaml"
@@ -39,7 +39,7 @@ func LoadConfigFromCRDs(
}
m, err := makeNameToApiMap(content)
if err != nil {
return nil, errors.WrapPrefixf(err, "unable to parse open API definition from '%s'", path)
return nil, errors.Wrapf(err, "unable to parse open API definition from '%s'", path)
}
otherTc, err := makeConfigFromApiMap(m)
if err != nil {

View File

@@ -170,10 +170,9 @@ func (ra *ResAccumulator) FixBackReferences() (err error) {
// Intersection drops the resources which "other" does not have.
func (ra *ResAccumulator) Intersection(other resmap.ResMap) error {
otherIds := other.AllIds()
for _, curId := range ra.resMap.AllIds() {
toDelete := true
for _, otherId := range otherIds {
for _, otherId := range other.AllIds() {
if otherId == curId {
toDelete = false
break

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on AnnotationsTransformer; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on ConfigMapGenerator; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on HashTransformer; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins

View File

@@ -1,11 +1,12 @@
// Code generated by pluginator on HelmChartInflationGenerator; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
@@ -13,14 +14,14 @@ import (
"strings"
"github.com/imdario/mergo"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/yaml"
)
// Generate resources from a remote or local helm chart.
// HelmChartInflationGeneratorPlugin is a plugin to generate resources
// from a remote or local helm chart.
type HelmChartInflationGeneratorPlugin struct {
h *resmap.PluginHelpers
types.HelmGlobals
@@ -28,6 +29,8 @@ type HelmChartInflationGeneratorPlugin struct {
tmpDir string
}
var KustomizePlugin HelmChartInflationGeneratorPlugin
const (
valuesMergeOptionMerge = "merge"
valuesMergeOptionOverride = "override"
@@ -70,7 +73,7 @@ func (p *HelmChartInflationGeneratorPlugin) establishTmpDir() (err error) {
// already done.
return nil
}
p.tmpDir, err = os.MkdirTemp("", "kustomize-helm-")
p.tmpDir, err = ioutil.TempDir("", "kustomize-helm-")
return err
}
@@ -84,23 +87,15 @@ func (p *HelmChartInflationGeneratorPlugin) validateArgs() (err error) {
// the loader root (unless root restrictions are
// disabled, in which case this can be an absolute path).
if p.ChartHome == "" {
p.ChartHome = types.HelmDefaultHome
p.ChartHome = "charts"
}
// The ValuesFile(s) may be consulted by the plugin, so it must
// The ValuesFile may be consulted by the plugin, so it must
// be under the loader root (unless root restrictions are
// disabled).
if p.ValuesFile == "" {
p.ValuesFile = filepath.Join(p.ChartHome, p.Name, "values.yaml")
}
for i, file := range p.AdditionalValuesFiles {
// use Load() to enforce root restrictions
if _, err := p.h.Loader().Load(file); err != nil {
return errors.WrapPrefixf(err, "could not load additionalValuesFile")
}
// the additional values filepaths must be relative to the kust root
p.AdditionalValuesFiles[i] = filepath.Join(p.h.Loader().Root(), file)
}
if err = p.errIfIllegalValuesMerge(); err != nil {
return err
@@ -109,7 +104,7 @@ func (p *HelmChartInflationGeneratorPlugin) validateArgs() (err error) {
// ConfigHome is not loaded by the plugin, and can be located anywhere.
if p.ConfigHome == "" {
if err = p.establishTmpDir(); err != nil {
return errors.WrapPrefixf(
return errors.Wrap(
err, "unable to create tmp dir for HELM_CONFIG_HOME")
}
p.ConfigHome = filepath.Join(p.tmpDir, "helm")
@@ -153,10 +148,10 @@ func (p *HelmChartInflationGeneratorPlugin) runHelmCommand(
err := cmd.Run()
if err != nil {
helm := p.h.GeneralConfig().HelmConfig.Command
err = errors.WrapPrefixf(
err = errors.Wrap(
fmt.Errorf(
"unable to run: '%s %s' with env=%s (is '%s' installed?): %w",
helm, strings.Join(args, " "), env, helm, err),
"unable to run: '%s %s' with env=%s (is '%s' installed?)",
helm, strings.Join(args, " "), env, helm),
stderr.String(),
)
}
@@ -216,7 +211,7 @@ func (p *HelmChartInflationGeneratorPlugin) writeValuesBytes(
return "", fmt.Errorf("cannot create tmp dir to write helm values")
}
path := filepath.Join(p.tmpDir, p.Name+"-kustomize-values.yaml")
return path, errors.WrapPrefixf(os.WriteFile(path, b, 0644), "failed to write values file")
return path, ioutil.WriteFile(path, b, 0644)
}
func (p *HelmChartInflationGeneratorPlugin) cleanup() {
@@ -249,31 +244,46 @@ func (p *HelmChartInflationGeneratorPlugin) Generate() (rm resmap.ResMap, err er
return nil, err
}
var stdout []byte
stdout, err = p.runHelmCommand(p.AsHelmArgs(p.absChartHome()))
stdout, err = p.runHelmCommand(p.templateCommand())
if err != nil {
return nil, err
}
rm, resMapErr := p.h.ResmapFactory().NewResMapFromBytes(stdout)
if resMapErr == nil {
rm, err = p.h.ResmapFactory().NewResMapFromBytes(stdout)
if err == nil {
return rm, nil
}
// try to remove the contents before first "---" because
// helm may produce messages to stdout before it
r := &kio.ByteReader{Reader: bytes.NewBufferString(string(stdout)), OmitReaderAnnotations: true}
nodes, err := r.Read()
if err != nil {
return nil, fmt.Errorf("error reading helm output: %w", err)
stdoutStr := string(stdout)
if idx := strings.Index(stdoutStr, "---"); idx != -1 {
return p.h.ResmapFactory().NewResMapFromBytes([]byte(stdoutStr[idx:]))
}
return nil, err
}
if len(nodes) != 0 {
rm, err = p.h.ResmapFactory().NewResMapFromRNodeSlice(nodes)
if err != nil {
return nil, fmt.Errorf("could not parse rnode slice into resource map: %w", err)
}
return rm, nil
func (p *HelmChartInflationGeneratorPlugin) templateCommand() []string {
args := []string{"template"}
if p.ReleaseName != "" {
args = append(args, p.ReleaseName)
}
return nil, fmt.Errorf("could not parse bytes into resource map: %w", resMapErr)
if p.Namespace != "" {
args = append(args, "--namespace", p.Namespace)
}
args = append(args, filepath.Join(p.absChartHome(), p.Name))
if p.ValuesFile != "" {
args = append(args, "--values", p.ValuesFile)
}
if p.ReleaseName == "" {
// AFAICT, this doesn't work as intended due to a bug in helm.
// See https://github.com/helm/helm/issues/6019
// I've tried placing the flag before and after the name argument.
args = append(args, "--generate-name")
}
if p.IncludeCRDs {
args = append(args, "--include-crds")
}
return args
}
func (p *HelmChartInflationGeneratorPlugin) pullCommand() []string {

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on IAMPolicyGenerator; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on ImageTagTransformer; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on LabelTransformer; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins

View File

@@ -0,0 +1,46 @@
// Code generated by pluginator on LegacyOrderTransformer; DO NOT EDIT.
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"sort"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
)
// Sort the resources using an ordering defined in the Gvk class.
// This puts cluster-wide basic resources with no
// dependencies (like Namespace, StorageClass, etc.)
// first, and resources with a high number of dependencies
// (like ValidatingWebhookConfiguration) last.
type LegacyOrderTransformerPlugin struct{}
// Nothing needed for configuration.
func (p *LegacyOrderTransformerPlugin) Config(
_ *resmap.PluginHelpers, _ []byte) (err error) {
return nil
}
func (p *LegacyOrderTransformerPlugin) Transform(m resmap.ResMap) (err error) {
resources := make([]*resource.Resource, m.Size())
ids := m.AllIds()
sort.Sort(resmap.IdSlice(ids))
for i, id := range ids {
resources[i], err = m.GetByCurrentId(id)
if err != nil {
return errors.Wrap(err, "expected match for sorting")
}
}
m.Clear()
for _, r := range resources {
m.Append(r)
}
return nil
}
func NewLegacyOrderTransformerPlugin() resmap.TransformerPlugin {
return &LegacyOrderTransformerPlugin{}
}

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on NamespaceTransformer; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on PatchJson6902Transformer; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
@@ -7,11 +7,11 @@ import (
"fmt"
jsonpatch "github.com/evanphx/json-patch"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/filters/patchjson6902"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/yaml"
)
@@ -61,7 +61,7 @@ func (p *PatchJson6902TransformerPlugin) Config(
}
p.decodedPatch, err = jsonpatch.DecodePatch([]byte(p.JsonOp))
if err != nil {
return errors.WrapPrefixf(err, "decoding %s", p.JsonOp)
return errors.Wrapf(err, "decoding %s", p.JsonOp)
}
if len(p.decodedPatch) == 0 {
return fmt.Errorf(

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on PatchStrategicMergeTransformer; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on PatchTransformer; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on PrefixTransformer; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on ReplacementTransformer; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on ReplicaCountTransformer; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on SecretGenerator; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins

View File

@@ -1,244 +0,0 @@
// Code generated by pluginator on SortOrderTransformer; DO NOT EDIT.
// pluginator {(devel) unknown }
package builtins
import (
"sort"
"strings"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/resid"
"sigs.k8s.io/yaml"
)
// Sort the resources using a customizable ordering based of Kind.
// Defaults to the ordering of the GVK struct, which puts cluster-wide basic
// resources with no dependencies (like Namespace, StorageClass, etc.) first,
// and resources with a high number of dependencies
// (like ValidatingWebhookConfiguration) last.
type SortOrderTransformerPlugin struct {
SortOptions *types.SortOptions `json:"sortOptions,omitempty" yaml:"sortOptions,omitempty"`
}
func (p *SortOrderTransformerPlugin) Config(
_ *resmap.PluginHelpers, c []byte) error {
return errors.WrapPrefixf(yaml.Unmarshal(c, p), "Failed to unmarshal SortOrderTransformer config")
}
func (p *SortOrderTransformerPlugin) applyDefaults() {
// Default to FIFO sort, aka no-op.
if p.SortOptions == nil {
p.SortOptions = &types.SortOptions{
Order: types.FIFOSortOrder,
}
}
// If legacy sort is selected and no options are given, default to
// hardcoded order.
if p.SortOptions.Order == types.LegacySortOrder && p.SortOptions.LegacySortOptions == nil {
p.SortOptions.LegacySortOptions = &types.LegacySortOptions{
OrderFirst: defaultOrderFirst,
OrderLast: defaultOrderLast,
}
}
}
func (p *SortOrderTransformerPlugin) validate() error {
// Check valid values for SortOrder
if p.SortOptions.Order != types.FIFOSortOrder && p.SortOptions.Order != types.LegacySortOrder {
return errors.Errorf("the field 'sortOptions.order' must be one of [%s, %s]",
types.FIFOSortOrder, types.LegacySortOrder)
}
// Validate that the only options set are the ones corresponding to the
// selected sort order.
if p.SortOptions.Order == types.FIFOSortOrder &&
p.SortOptions.LegacySortOptions != nil {
return errors.Errorf("the field 'sortOptions.legacySortOptions' is"+
" set but the selected sort order is '%v', not 'legacy'",
p.SortOptions.Order)
}
return nil
}
func (p *SortOrderTransformerPlugin) Transform(m resmap.ResMap) (err error) {
p.applyDefaults()
err = p.validate()
if err != nil {
return err
}
// Sort
if p.SortOptions.Order == types.LegacySortOrder {
s := newLegacyIDSorter(m.AllIds(), p.SortOptions.LegacySortOptions)
sort.Sort(s)
err = applyOrdering(m, s.resids)
if err != nil {
return err
}
}
return nil
}
// applyOrdering takes resources (given in ResMap) and a desired ordering given
// as a sequence of ResIds, and updates the ResMap's resources to match the
// ordering.
func applyOrdering(m resmap.ResMap, ordering []resid.ResId) error {
var err error
resources := make([]*resource.Resource, m.Size())
// Clear and refill with the correct order
for i, id := range ordering {
resources[i], err = m.GetByCurrentId(id)
if err != nil {
return errors.WrapPrefixf(err, "expected match for sorting")
}
}
m.Clear()
for _, r := range resources {
err = m.Append(r)
if err != nil {
return errors.WrapPrefixf(err, "SortOrderTransformer: Failed to append to resources")
}
}
return nil
}
// Code for legacy sorting.
// Legacy sorting is a "fixed" order sorting maintained for backwards
// compatibility.
// legacyIDSorter sorts resources based on two priority lists:
// - orderFirst: Resources that should be placed in the start, in the given order.
// - orderLast: Resources that should be placed in the end, in the given order.
type legacyIDSorter struct {
// resids only stores the metadata of the object. This is an optimization as
// it's expensive to compute these again and again during ordering.
resids []resid.ResId
typeOrders map[string]int
}
func newLegacyIDSorter(
resids []resid.ResId,
options *types.LegacySortOptions) *legacyIDSorter {
// Precalculate a resource ranking based on the priority lists.
var typeOrders = func() map[string]int {
m := map[string]int{}
for i, n := range options.OrderFirst {
m[n] = -len(options.OrderFirst) + i
}
for i, n := range options.OrderLast {
m[n] = 1 + i
}
return m
}()
return &legacyIDSorter{
resids: resids,
typeOrders: typeOrders,
}
}
var _ sort.Interface = legacyIDSorter{}
func (a legacyIDSorter) Len() int { return len(a.resids) }
func (a legacyIDSorter) Swap(i, j int) {
a.resids[i], a.resids[j] = a.resids[j], a.resids[i]
}
func (a legacyIDSorter) Less(i, j int) bool {
if !a.resids[i].Gvk.Equals(a.resids[j].Gvk) {
return gvkLessThan(a.resids[i].Gvk, a.resids[j].Gvk, a.typeOrders)
}
return legacyResIDSortString(a.resids[i]) < legacyResIDSortString(a.resids[j])
}
func gvkLessThan(gvk1, gvk2 resid.Gvk, typeOrders map[string]int) bool {
index1 := typeOrders[gvk1.Kind]
index2 := typeOrders[gvk2.Kind]
if index1 != index2 {
return index1 < index2
}
return legacyGVKSortString(gvk1) < legacyGVKSortString(gvk2)
}
// legacyGVKSortString returns a string representation of given GVK used for
// stable sorting.
func legacyGVKSortString(x resid.Gvk) string {
legacyNoGroup := "~G"
legacyNoVersion := "~V"
legacyNoKind := "~K"
legacyFieldSeparator := "_"
g := x.Group
if g == "" {
g = legacyNoGroup
}
v := x.Version
if v == "" {
v = legacyNoVersion
}
k := x.Kind
if k == "" {
k = legacyNoKind
}
return strings.Join([]string{g, v, k}, legacyFieldSeparator)
}
// legacyResIDSortString returns a string representation of given ResID used for
// stable sorting.
func legacyResIDSortString(id resid.ResId) string {
legacyNoNamespace := "~X"
legacyNoName := "~N"
legacySeparator := "|"
ns := id.Namespace
if ns == "" {
ns = legacyNoNamespace
}
nm := id.Name
if nm == "" {
nm = legacyNoName
}
return strings.Join(
[]string{id.Gvk.String(), ns, nm}, legacySeparator)
}
// DO NOT CHANGE!
// Final legacy ordering provided as a default by kustomize.
// Originally an attempt to apply resources in the correct order, an effort
// which later proved impossible as not all types are known beforehand.
// See: https://github.com/kubernetes-sigs/kustomize/issues/3913
var defaultOrderFirst = []string{ //nolint:gochecknoglobals
"Namespace",
"ResourceQuota",
"StorageClass",
"CustomResourceDefinition",
"ServiceAccount",
"PodSecurityPolicy",
"Role",
"ClusterRole",
"RoleBinding",
"ClusterRoleBinding",
"ConfigMap",
"Secret",
"Endpoints",
"Service",
"LimitRange",
"PriorityClass",
"PersistentVolume",
"PersistentVolumeClaim",
"Deployment",
"StatefulSet",
"CronJob",
"PodDisruptionBudget",
}
var defaultOrderLast = []string{ //nolint:gochecknoglobals
"MutatingWebhookConfiguration",
"ValidatingWebhookConfiguration",
}
func NewSortOrderTransformerPlugin() resmap.TransformerPlugin {
return &SortOrderTransformerPlugin{}
}

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on SuffixTransformer; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins

View File

@@ -1,5 +1,5 @@
// Code generated by pluginator on ValueAddTransformer; DO NOT EDIT.
// pluginator {(devel) unknown }
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins

View File

@@ -5,8 +5,6 @@ package generators
import (
"fmt"
"path"
"strings"
"github.com/go-errors/errors"
"sigs.k8s.io/kustomize/api/ifc"
@@ -97,28 +95,3 @@ func setImmutable(
return nil
}
// ParseFileSource parses the source given.
//
// Acceptable formats include:
// 1. source-path: the basename will become the key name
// 2. source-name=source-path: the source-name will become the key name and
// source-path is the path to the key file.
//
// Key names cannot include '='.
func ParseFileSource(source string) (keyName, filePath string, err error) {
numSeparators := strings.Count(source, "=")
switch {
case numSeparators == 0:
return path.Base(source), source, nil
case numSeparators == 1 && strings.HasPrefix(source, "="):
return "", "", errors.Errorf("missing key name for file path %q in source %q", strings.TrimPrefix(source, "="), source)
case numSeparators == 1 && strings.HasSuffix(source, "="):
return "", "", errors.Errorf("missing file path for key name %q in source %q", strings.TrimSuffix(source, "="), source)
case numSeparators > 1:
return "", "", errors.Errorf("source %q key name or file path contains '='", source)
default:
components := strings.Split(source, "=")
return components[0], components[1], nil
}
}

View File

@@ -1,51 +0,0 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package generators_test
import (
"testing"
"github.com/stretchr/testify/require"
. "sigs.k8s.io/kustomize/api/internal/generators"
)
func TestParseFileSource(t *testing.T) {
tests := map[string]*struct {
Input string
Error string
Key string
Filename string
}{
"filename only": {
Input: "./path/myfile",
Key: "myfile",
Filename: "./path/myfile",
},
"key and filename": {
Input: "newName.ini=oldName",
Key: "newName.ini",
Filename: "oldName",
},
"multiple =": {
Input: "newName.ini==oldName",
Error: `source "newName.ini==oldName" key name or file path contains '='`,
},
"missing key": {
Input: "=myfile",
Error: `missing key name for file path "myfile" in source "=myfile"`,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
key, file, err := ParseFileSource(test.Input)
if test.Error != "" {
require.EqualError(t, err, test.Error)
} else {
require.NoError(t, err)
require.Equal(t, test.Key, key)
require.Equal(t, test.Filename, file)
}
})
}
}

View File

@@ -22,11 +22,15 @@ func ClonerUsingGitExec(repoSpec *RepoSpec) error {
if err = r.run("init"); err != nil {
return err
}
if err = r.run(
"remote", "add", "origin", repoSpec.CloneSpec()); err != nil {
return err
}
ref := "HEAD"
if repoSpec.Ref != "" {
ref = repoSpec.Ref
}
if err = r.run("fetch", "--depth=1", repoSpec.CloneSpec(), ref); err != nil {
if err = r.run("fetch", "--depth=1", "origin", ref); err != nil {
return err
}
if err = r.run("checkout", "FETCH_HEAD"); err != nil {

View File

@@ -7,8 +7,8 @@ import (
"os/exec"
"time"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/internal/utils"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
@@ -24,7 +24,7 @@ type gitRunner struct {
func newCmdRunner(timeout time.Duration) (*gitRunner, error) {
gitProgram, err := exec.LookPath("git")
if err != nil {
return nil, errors.WrapPrefixf(err, "no 'git' program on path")
return nil, errors.Wrap(err, "no 'git' program on path")
}
dir, err := filesys.NewTmpConfirmedDir()
if err != nil {
@@ -46,9 +46,9 @@ func (r gitRunner) run(args ...string) error {
cmd.String(),
r.duration,
func() error {
out, err := cmd.CombinedOutput()
_, err := cmd.CombinedOutput()
if err != nil {
return errors.WrapPrefixf(err, "failed to run '%s': %s", cmd.String(), string(out))
return errors.Wrapf(err, "git cmd = '%s'", cmd.String())
}
return err
})

View File

@@ -5,15 +5,12 @@ package git
import (
"fmt"
"log"
"net/url"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
@@ -30,23 +27,26 @@ type RepoSpec struct {
// TODO(monopole): Drop raw, use processed fields instead.
raw string
// Host, e.g. https://github.com/
// Host, e.g. github.com
Host string
// RepoPath name (Path to repository),
// orgRepo name (organization/repoName),
// e.g. kubernetes-sigs/kustomize
RepoPath string
OrgRepo string
// Dir is where the repository is cloned to.
// Dir where the orgRepo is cloned to.
Dir filesys.ConfirmedDir
// Relative path in the repository, and in the cloneDir,
// to a Kustomization.
KustRootPath string
Path string
// Branch or tag reference.
Ref string
// e.g. .git or empty in case of _git is present
GitSuffix string
// Submodules indicates whether or not to clone git submodules.
Submodules bool
@@ -56,7 +56,10 @@ type RepoSpec struct {
// CloneSpec returns a string suitable for "git clone {spec}".
func (x *RepoSpec) CloneSpec() string {
return x.Host + x.RepoPath
if isAzureHost(x.Host) || isAWSHost(x.Host) {
return x.Host + x.OrgRepo
}
return x.Host + x.OrgRepo + x.GitSuffix
}
func (x *RepoSpec) CloneDir() filesys.ConfirmedDir {
@@ -68,140 +71,81 @@ func (x *RepoSpec) Raw() string {
}
func (x *RepoSpec) AbsPath() string {
return x.Dir.Join(x.KustRootPath)
return x.Dir.Join(x.Path)
}
func (x *RepoSpec) Cleaner(fSys filesys.FileSystem) func() error {
return func() error { return fSys.RemoveAll(x.Dir.String()) }
}
const (
refQuery = "?ref="
gitSuffix = ".git"
gitRootDelimiter = "_git/"
pathSeparator = "/" // do not use filepath.Separator, as this is a URL
)
// NewRepoSpecFromURL parses git-like urls.
// From strings like git@github.com:someOrg/someRepo.git or
// https://github.com/someOrg/someRepo?ref=someHash, extract
// the different parts of URL, set into a RepoSpec object and return RepoSpec object.
// It MUST return an error if the input is not a git-like URL, as this is used by some code paths
// to distinguish between local and remote paths.
//
// In particular, NewRepoSpecFromURL separates the URL used to clone the repo from the
// elements Kustomize uses for other purposes (e.g. query params that turn into args, and
// the path to the kustomization root within the repo).
// the parts.
func NewRepoSpecFromURL(n string) (*RepoSpec, error) {
repoSpec := &RepoSpec{raw: n, Dir: notCloned, Timeout: defaultTimeout, Submodules: defaultSubmodules}
if filepath.IsAbs(n) {
return nil, fmt.Errorf("uri looks like abs path: %s", n)
}
// Parse the query first. This is safe because according to rfc3986 "?" is only allowed in the
// query and is not recognized %-encoded.
// Note that parseQuery returns default values for empty parameters.
n, query, _ := strings.Cut(n, "?")
repoSpec.Ref, repoSpec.Timeout, repoSpec.Submodules = parseQuery(query)
var err error
// Parse the host (e.g. scheme, username, domain) segment.
repoSpec.Host, n, err = extractHost(n)
if err != nil {
return nil, err
host, orgRepo, path, gitRef, gitSubmodules, suffix, gitTimeout := parseGitURL(n)
if orgRepo == "" {
return nil, fmt.Errorf("url lacks orgRepo: %s", n)
}
// In some cases, we're given a path to a git repo + a path to the kustomization root within
// that repo. We need to split them so that we can ultimately give the repo only to the cloner.
repoSpec.RepoPath, repoSpec.KustRootPath, err = parsePathParts(n, defaultRepoPathLength(repoSpec.Host))
if err != nil {
return nil, err
if host == "" {
return nil, fmt.Errorf("url lacks host: %s", n)
}
return repoSpec, nil
return &RepoSpec{
raw: n, Host: host, OrgRepo: orgRepo,
Dir: notCloned, Path: path, Ref: gitRef, GitSuffix: suffix,
Submodules: gitSubmodules, Timeout: gitTimeout}, nil
}
const allSegments = -999999
const orgRepoSegments = 2
const (
refQuery = "?ref="
gitSuffix = ".git"
gitDelimiter = "_git/"
)
func defaultRepoPathLength(host string) int {
if strings.HasPrefix(host, fileScheme) {
return allSegments
// From strings like git@github.com:someOrg/someRepo.git or
// https://github.com/someOrg/someRepo?ref=someHash, extract
// the parts.
func parseGitURL(n string) (
host string, orgRepo string, path string, gitRef string, gitSubmodules bool, gitSuff string, gitTimeout time.Duration) {
if strings.Contains(n, gitDelimiter) {
index := strings.Index(n, gitDelimiter)
// Adding _git/ to host
host = normalizeGitHostSpec(n[:index+len(gitDelimiter)])
orgRepo = strings.Split(strings.Split(n[index+len(gitDelimiter):], "/")[0], "?")[0]
path, gitRef, gitTimeout, gitSubmodules = peelQuery(n[index+len(gitDelimiter)+len(orgRepo):])
return
}
return orgRepoSegments
}
// parsePathParts splits the repo path that will ultimately be passed to git to clone the
// repo from the kustomization root path, which Kustomize will execute the build in after the repo
// is cloned.
//
// We first try to do this based on explicit markers in the URL (e.g. _git, .git or //).
// If none are present, we try to apply a historical default repo path length that is derived from
// Github URLs. If there aren't enough segments, we have historically considered the URL invalid.
func parsePathParts(n string, defaultSegmentLength int) (string, string, error) {
repoPath, kustRootPath, success := tryExplicitMarkerSplit(n)
if !success {
repoPath, kustRootPath, success = tryDefaultLengthSplit(n, defaultSegmentLength)
host, n = parseHostSpec(n)
gitSuff = gitSuffix
if strings.Contains(n, gitSuffix) {
index := strings.Index(n, gitSuffix)
orgRepo = n[0:index]
n = n[index+len(gitSuffix):]
if len(n) > 0 && n[0] == '/' {
n = n[1:]
}
path, gitRef, gitTimeout, gitSubmodules = peelQuery(n)
return
}
// Validate the result
if !success || len(repoPath) == 0 {
return "", "", fmt.Errorf("failed to parse repo path segment")
i := strings.Index(n, "/")
if i < 1 {
path, gitRef, gitTimeout, gitSubmodules = peelQuery(n)
return
}
if kustRootPathExitsRepo(kustRootPath) {
return "", "", fmt.Errorf("url path exits repo: %s", n)
j := strings.Index(n[i+1:], "/")
if j >= 0 {
j += i + 1
orgRepo = n[:j]
path, gitRef, gitTimeout, gitSubmodules = peelQuery(n[j+1:])
return
}
return repoPath, strings.TrimPrefix(kustRootPath, pathSeparator), nil
}
func tryExplicitMarkerSplit(n string) (string, string, bool) {
// Look for the _git delimiter, which by convention is expected to be ONE directory above the repo root.
// If found, split on the NEXT path element, which is the repo root.
// Example: https://username@dev.azure.com/org/project/_git/repo/path/to/kustomization/root
if gitRootIdx := strings.Index(n, gitRootDelimiter); gitRootIdx >= 0 {
gitRootPath := n[:gitRootIdx+len(gitRootDelimiter)]
subpathSegments := strings.Split(n[gitRootIdx+len(gitRootDelimiter):], pathSeparator)
return gitRootPath + subpathSegments[0], strings.Join(subpathSegments[1:], pathSeparator), true
// Look for a double-slash in the path, which if present separates the repo root from the kust path.
// It is a convention, not a real path element, so do not preserve it in the returned value.
// Example: https://github.com/org/repo//path/to/kustomozation/root
} else if repoRootIdx := strings.Index(n, "//"); repoRootIdx >= 0 {
return n[:repoRootIdx], n[repoRootIdx+2:], true
// Look for .git in the path, which if present is part of the directory name of the git repo.
// This means we want to grab everything up to and including that suffix
// Example: https://github.com/org/repo.git/path/to/kustomozation/root
} else if gitSuffixIdx := strings.Index(n, gitSuffix); gitSuffixIdx >= 0 {
upToGitSuffix := n[:gitSuffixIdx+len(gitSuffix)]
afterGitSuffix := n[gitSuffixIdx+len(gitSuffix):]
return upToGitSuffix, afterGitSuffix, true
}
return "", "", false
}
func tryDefaultLengthSplit(n string, defaultSegmentLength int) (string, string, bool) {
// If the default is to take all segments, do so.
if defaultSegmentLength == allSegments {
return n, "", true
// If the default is N segments, make sure we have at least that many and take them if so.
// If we have less than N, we have historically considered the URL invalid.
} else if segments := strings.Split(n, pathSeparator); len(segments) >= defaultSegmentLength {
firstNSegments := strings.Join(segments[:defaultSegmentLength], pathSeparator)
rest := strings.Join(segments[defaultSegmentLength:], pathSeparator)
return firstNSegments, rest, true
}
return "", "", false
}
func kustRootPathExitsRepo(kustRootPath string) bool {
cleanedPath := filepath.Clean(strings.TrimPrefix(kustRootPath, string(filepath.Separator)))
pathElements := strings.Split(cleanedPath, string(filepath.Separator))
return len(pathElements) > 0 &&
pathElements[0] == filesys.ParentDir
path = ""
orgRepo, gitRef, gitTimeout, gitSubmodules = peelQuery(n)
return host, orgRepo, path, gitRef, gitSubmodules, gitSuff, gitTimeout
}
// Clone git submodules by default.
@@ -210,12 +154,14 @@ const defaultSubmodules = true
// Arbitrary, but non-infinite, timeout for running commands.
const defaultTimeout = 27 * time.Second
func parseQuery(query string) (string, time.Duration, bool) {
values, err := url.ParseQuery(query)
// in event of parse failure, return defaults
func peelQuery(arg string) (string, string, time.Duration, bool) {
// Parse the given arg into a URL. In the event of a parse failure, return
// our defaults.
parsed, err := url.Parse(arg)
if err != nil {
return "", defaultTimeout, defaultSubmodules
return arg, "", defaultTimeout, defaultSubmodules
}
values := parsed.Query()
// ref is the desired git ref to target. Can be specified by in a git URL
// with ?ref=<string> or ?version=<string>, although ref takes precedence.
@@ -246,142 +192,76 @@ func parseQuery(query string) (string, time.Duration, bool) {
}
}
return ref, duration, submodules
return parsed.Path, ref, duration, submodules
}
func extractHost(n string) (string, string, error) {
n = ignoreForcedGitProtocol(n)
scheme, n := extractScheme(n)
username, n := extractUsername(n)
stdGithub := isStandardGithubHost(n)
acceptSCP := acceptSCPStyle(scheme, username, stdGithub)
// Validate the username and scheme before attempting host/path parsing, because if the parsing
// so far has not succeeded, we will not be able to extract the host and path correctly.
if err := validateScheme(scheme, acceptSCP); err != nil {
return "", "", err
}
// Now that we have extracted a valid scheme+username, we can parse host itself.
// The file protocol specifies an absolute path to a local git repo.
// Everything after the scheme (including any 'username' we found) is actually part of that path.
if scheme == fileScheme {
return scheme, username + n, nil
}
var host, rest = n, ""
if sepIndex := findPathSeparator(n, acceptSCP); sepIndex >= 0 {
host, rest = n[:sepIndex+1], n[sepIndex+1:]
}
// Github URLs are strictly normalized in a way that may discard scheme and username components.
if stdGithub {
scheme, username, host = normalizeGithubHostParts(scheme, username)
}
// Host is required, so do not concat the scheme and username if we didn't find one.
if host == "" {
return "", "", errors.Errorf("failed to parse host segment")
}
return scheme + username + host, rest, nil
}
// ignoreForcedGitProtocol strips the "git::" prefix from URLs.
// We used to use go-getter to handle our urls: https://github.com/hashicorp/go-getter.
// The git:: prefix signaled go-getter to use the git protocol to fetch the url's contents.
// We silently strip this prefix to allow these go-getter-style urls to continue to work,
// although the git protocol (which is insecure and unsupported on many platforms, including Github)
// will not actually be used as intended.
func ignoreForcedGitProtocol(n string) string {
n, found := trimPrefixIgnoreCase(n, "git::")
if found {
log.Println("Warning: Forcing the git protocol using the 'git::' URL prefix is not supported. " +
"Kustomize currently strips this invalid prefix, but will stop doing so in a future release. " +
"Please remove the 'git::' prefix from your configuration.")
}
return n
}
// acceptSCPStyle returns true if the scheme and username indicate potential use of an SCP-style URL.
// With this style, the scheme is not explicit and the path is delimited by a colon.
// Strictly speaking the username is optional in SCP-like syntax, but Kustomize has always
// required it for non-Github URLs.
// Example: user@host.xz:path/to/repo.git/
func acceptSCPStyle(scheme, username string, isGithubURL bool) bool {
return scheme == "" && (username != "" || isGithubURL)
}
func validateScheme(scheme string, acceptSCPStyle bool) error {
// see https://git-scm.com/docs/git-fetch#_git_urls for info relevant to these validations
switch scheme {
case "":
// Empty scheme is only ok if it's a Github URL or if it looks like SCP-style syntax
if !acceptSCPStyle {
return fmt.Errorf("failed to parse scheme")
}
case sshScheme, fileScheme, httpsScheme, httpScheme:
// These are all supported schemes
default:
// At time of writing, we should never end up here because we do not parse out
// unsupported schemes to begin with.
return fmt.Errorf("unsupported scheme %q", scheme)
}
return nil
}
const fileScheme = "file://"
const httpScheme = "http://"
const httpsScheme = "https://"
const sshScheme = "ssh://"
func extractScheme(s string) (string, string) {
for _, prefix := range []string{sshScheme, httpsScheme, httpScheme, fileScheme} {
if rest, found := trimPrefixIgnoreCase(s, prefix); found {
return prefix, rest
func parseHostSpec(n string) (string, string) {
var host string
// Start accumulating the host part.
for _, p := range []string{
// Order matters here.
"git::", "gh:", "ssh://", "https://", "http://",
"git@", "github.com:", "github.com/"} {
if len(p) < len(n) && strings.ToLower(n[:len(p)]) == p {
n = n[len(p):]
host += p
}
}
return "", s
}
func extractUsername(s string) (string, string) {
var userRegexp = regexp.MustCompile(`^([a-zA-Z][a-zA-Z0-9-]*)@`)
if m := userRegexp.FindStringSubmatch(s); m != nil {
username := m[1] + "@"
return username, s[len(username):]
if host == "git@" {
i := strings.Index(n, "/")
if i > -1 {
host += n[:i+1]
n = n[i+1:]
} else {
i = strings.Index(n, ":")
if i > -1 {
host += n[:i+1]
n = n[i+1:]
}
}
return host, n
}
return "", s
}
func isStandardGithubHost(s string) bool {
lowerCased := strings.ToLower(s)
return strings.HasPrefix(lowerCased, "github.com/") ||
strings.HasPrefix(lowerCased, "github.com:")
}
// trimPrefixIgnoreCase returns the rest of s and true if prefix, ignoring case, prefixes s.
// Otherwise, trimPrefixIgnoreCase returns s and false.
func trimPrefixIgnoreCase(s, prefix string) (string, bool) {
if len(prefix) <= len(s) && strings.ToLower(s[:len(prefix)]) == prefix {
return s[len(prefix):], true
}
return s, false
}
func findPathSeparator(hostPath string, acceptSCP bool) int {
sepIndex := strings.Index(hostPath, pathSeparator)
if acceptSCP {
colonIndex := strings.Index(hostPath, ":")
// The colon acts as a delimiter in scp-style ssh URLs only if not prefixed by '/'.
if sepIndex == -1 || (colonIndex > 0 && colonIndex < sepIndex) {
sepIndex = colonIndex
// If host is a http(s) or ssh URL, grab the domain part.
for _, p := range []string{
"ssh://", "https://", "http://"} {
if strings.HasSuffix(host, p) {
i := strings.Index(n, "/")
if i > -1 {
host += n[0 : i+1]
n = n[i+1:]
}
break
}
}
return sepIndex
return normalizeGitHostSpec(host), n
}
func normalizeGithubHostParts(scheme, username string) (string, string, string) {
if strings.HasPrefix(scheme, sshScheme) || username != "" {
return "", username, "github.com:"
func normalizeGitHostSpec(host string) string {
s := strings.ToLower(host)
if strings.Contains(s, "github.com") {
if strings.Contains(s, "git@") || strings.Contains(s, "ssh:") {
host = "git@github.com:"
} else {
host = "https://github.com/"
}
}
return httpsScheme, "", "github.com/"
if strings.HasPrefix(s, "git::") {
host = strings.TrimPrefix(s, "git::")
}
return host
}
// The format of Azure repo URL is documented
// https://docs.microsoft.com/en-us/azure/devops/repos/git/clone?view=vsts&tabs=visual-studio#clone_url
func isAzureHost(host string) bool {
return strings.Contains(host, "dev.azure.com") ||
strings.Contains(host, "visualstudio.com")
}
// The format of AWS repo URL is documented
// https://docs.aws.amazon.com/codecommit/latest/userguide/regions.html
func isAWSHost(host string) bool {
return strings.Contains(host, "amazonaws.com")
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,152 +0,0 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package localizer
import (
"sigs.k8s.io/kustomize/api/filters/fieldspec"
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/filters/fsslice"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/resid"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// localizeBuiltinPlugins localizes built-in plugins with file paths.
// Note that this excludes helm, which needs a repo.
type localizeBuiltinPlugins struct {
lc *localizer
// locPathFn is used by localizeNode to set the localized path on the plugin.
locPathFn func(string) (string, error)
}
var _ kio.Filter = &localizeBuiltinPlugins{}
// Filter localizes the built-in plugins with file paths.
func (lbp *localizeBuiltinPlugins) Filter(plugins []*yaml.RNode) ([]*yaml.RNode, error) {
for _, singlePlugin := range plugins {
err := singlePlugin.PipeE(fsslice.Filter{
FsSlice: types.FsSlice{
types.FieldSpec{
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.ConfigMapGenerator.String()},
Path: "env",
},
types.FieldSpec{
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.ConfigMapGenerator.String()},
Path: "envs",
},
types.FieldSpec{
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.SecretGenerator.String()},
Path: "env",
},
types.FieldSpec{
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.SecretGenerator.String()},
Path: "envs",
},
types.FieldSpec{
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.HelmChartInflationGenerator.String()},
Path: "valuesFile",
},
types.FieldSpec{
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.HelmChartInflationGenerator.String()},
Path: "additionalValuesFiles",
},
types.FieldSpec{
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.PatchTransformer.String()},
Path: "path",
},
types.FieldSpec{
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.PatchJson6902Transformer.String()},
Path: "path",
},
types.FieldSpec{
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.ReplacementTransformer.String()},
Path: "replacements/path",
},
},
SetValue: func(node *yaml.RNode) error {
lbp.locPathFn = lbp.lc.localizeFile
return lbp.localizeAll(node)
},
},
fsslice.Filter{
FsSlice: types.FsSlice{
types.FieldSpec{
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.ConfigMapGenerator.String()},
Path: "files",
},
types.FieldSpec{
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.SecretGenerator.String()},
Path: "files",
},
},
SetValue: func(node *yaml.RNode) error {
lbp.locPathFn = lbp.lc.localizeFileSource
return lbp.localizeAll(node)
},
},
yaml.FilterFunc(func(node *yaml.RNode) (*yaml.RNode, error) {
isHelm := node.GetApiVersion() == konfig.BuiltinPluginApiVersion &&
node.GetKind() == builtinhelpers.HelmChartInflationGenerator.String()
if !isHelm {
return node, nil
}
home, err := node.Pipe(yaml.Lookup("chartHome"))
if err != nil {
return nil, errors.Wrap(err)
}
if home == nil {
_, err = lbp.lc.copyChartHomeEntry("")
} else {
lbp.locPathFn = lbp.lc.copyChartHomeEntry
err = lbp.localizeScalar(home)
}
return node, errors.WrapPrefixf(err, "plugin %s", resid.FromRNode(node))
}),
fieldspec.Filter{
FieldSpec: types.FieldSpec{
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.PatchStrategicMergeTransformer.String()},
Path: "paths",
},
SetValue: func(node *yaml.RNode) error {
lbp.locPathFn = lbp.lc.localizeK8sResource
return lbp.localizeAll(node)
},
})
if err != nil {
return nil, errors.Wrap(err)
}
}
return plugins, nil
}
// localizeAll sets each entry in node to its value localized by locPathFn.
// Node is a sequence or scalar value.
func (lbp *localizeBuiltinPlugins) localizeAll(node *yaml.RNode) error {
// We rely on the build command to throw errors for nodes in
// built-in plugins that are sequences when expected to be scalar,
// and vice versa.
//nolint: exhaustive
switch node.YNode().Kind {
case yaml.SequenceNode:
return errors.Wrap(node.VisitElements(lbp.localizeScalar))
case yaml.ScalarNode:
return lbp.localizeScalar(node)
default:
return errors.Errorf("expected sequence or scalar node")
}
}
// localizeScalar sets the scalar node to its value localized by locPathFn.
func (lbp *localizeBuiltinPlugins) localizeScalar(node *yaml.RNode) error {
localizedPath, err := lbp.locPathFn(node.YNode().Value)
if err != nil {
return err
}
return filtersutil.SetScalar(localizedPath)(node)
}

View File

@@ -1,7 +0,0 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package localizer contains utilities for the command kustomize localize, which is
// documented under proposals/localize-command or at
// https://github.com/kubernetes-sigs/kustomize/blob/master/proposals/22-04-localize-command.md
package localizer

View File

@@ -1,27 +0,0 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package localizer
import "fmt"
type ResourceLoadError struct {
InlineError error
FileError error
}
func (rle ResourceLoadError) Error() string {
return fmt.Sprintf(`when parsing as inline received error: %s
when parsing as filepath received error: %s`, rle.InlineError, rle.FileError)
}
type PathLocalizeError struct {
Path string
FileError error
RootError error
}
func (ple PathLocalizeError) Error() string {
return fmt.Sprintf(`could not localize path %q as file: %s; could not localize path %q as directory: %s`,
ple.Path, ple.FileError, ple.Path, ple.RootError)
}

View File

@@ -1,613 +0,0 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package localizer
import (
"io/fs"
"log"
"os"
"path/filepath"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/generators"
"sigs.k8s.io/kustomize/api/internal/target"
"sigs.k8s.io/kustomize/api/loader"
"sigs.k8s.io/kustomize/api/provider"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/filesys"
"sigs.k8s.io/yaml"
)
// localizer encapsulates all state needed to localize the root at ldr.
type localizer struct {
fSys filesys.FileSystem
// underlying type is Loader
ldr ifc.Loader
// root is at ldr.Root()
root filesys.ConfirmedDir
rFactory *resmap.Factory
// destination directory in newDir that mirrors root
dst string
}
// Run attempts to localize the kustomization root at target with the given localize arguments
// and returns the path to the created newDir.
func Run(target, scope, newDir string, fSys filesys.FileSystem) (string, error) {
ldr, args, err := NewLoader(target, scope, newDir, fSys)
if err != nil {
return "", errors.Wrap(err)
}
defer func() { _ = ldr.Cleanup() }()
toDst, err := filepath.Rel(args.Scope.String(), args.Target.String())
if err != nil {
log.Panicf("cannot find path from %q to child directory %q: %s", args.Scope, args.Target, err)
}
dst := args.NewDir.Join(toDst)
if err = fSys.MkdirAll(dst); err != nil {
return "", errors.WrapPrefixf(err, "unable to create directory in localize destination")
}
err = (&localizer{
fSys: fSys,
ldr: ldr,
root: args.Target,
rFactory: resmap.NewFactory(provider.NewDepProvider().GetResourceFactory()),
dst: dst,
}).localize()
if err != nil {
errCleanup := fSys.RemoveAll(args.NewDir.String())
if errCleanup != nil {
log.Printf("unable to clean localize destination: %s", errCleanup)
}
return "", errors.WrapPrefixf(err, "unable to localize target %q", target)
}
return args.NewDir.String(), nil
}
// localize localizes the root that lc is at
func (lc *localizer) localize() error {
kustomization, kustFileName, err := lc.load()
if err != nil {
return err
}
err = lc.localizeNativeFields(kustomization)
if err != nil {
return err
}
err = lc.localizeBuiltinPlugins(kustomization)
if err != nil {
return err
}
content, err := yaml.Marshal(kustomization)
if err != nil {
return errors.WrapPrefixf(err, "unable to serialize localized kustomization file")
}
if err = lc.fSys.WriteFile(filepath.Join(lc.dst, kustFileName), content); err != nil {
return errors.WrapPrefixf(err, "unable to write localized kustomization file")
}
return nil
}
// load returns the kustomization at lc.root and the file name under which it was found
func (lc *localizer) load() (*types.Kustomization, string, error) {
content, kustFileName, err := target.LoadKustFile(lc.ldr)
if err != nil {
return nil, "", errors.Wrap(err)
}
var kust types.Kustomization
err = (&kust).Unmarshal(content)
if err != nil {
return nil, "", errors.Wrap(err)
}
// Localize intentionally does not replace legacy fields to return a localized kustomization
// with as much resemblance to the original as possible.
// Localize also intentionally does not enforce fields, as localize does not wish to unnecessarily
// repeat the responsibilities of kustomize build.
return &kust, kustFileName, nil
}
// localizeNativeFields localizes paths on kustomize-native fields, like configMapGenerator, that kustomize has a
// built-in understanding of. This excludes helm-related fields, such as `helmGlobals` and `helmCharts`.
func (lc *localizer) localizeNativeFields(kust *types.Kustomization) error {
if path, exists := kust.OpenAPI["path"]; exists {
locPath, err := lc.localizeFile(path)
if err != nil {
return errors.WrapPrefixf(err, "unable to localize openapi path")
}
kust.OpenAPI["path"] = locPath
}
for fieldName, field := range map[string]struct {
paths []string
locFn func(string) (string, error)
}{
"bases": {
// Allow use of deprecated field
//nolint:staticcheck
kust.Bases,
lc.localizeRoot,
},
"components": {
kust.Components,
lc.localizeRoot,
},
"configurations": {
kust.Configurations,
lc.localizeFile,
},
"crds": {
kust.Crds,
lc.localizeFile,
},
"resources": {
kust.Resources,
lc.localizeResource,
},
} {
for i, path := range field.paths {
locPath, err := field.locFn(path)
if err != nil {
return errors.WrapPrefixf(err, "unable to localize %s entry", fieldName)
}
field.paths[i] = locPath
}
}
for i := range kust.ConfigMapGenerator {
if err := lc.localizeGenerator(&kust.ConfigMapGenerator[i].GeneratorArgs); err != nil {
return errors.WrapPrefixf(err, "unable to localize configMapGenerator")
}
}
for i := range kust.SecretGenerator {
if err := lc.localizeGenerator(&kust.SecretGenerator[i].GeneratorArgs); err != nil {
return errors.WrapPrefixf(err, "unable to localize secretGenerator")
}
}
if err := lc.localizeHelmInflationGenerator(kust); err != nil {
return err
}
if err := lc.localizeHelmCharts(kust); err != nil {
return err
}
if err := lc.localizePatches(kust.Patches); err != nil {
return errors.WrapPrefixf(err, "unable to localize patches")
}
//nolint:staticcheck
if err := lc.localizePatches(kust.PatchesJson6902); err != nil {
return errors.WrapPrefixf(err, "unable to localize patchesJson6902")
}
//nolint:staticcheck
for i, patch := range kust.PatchesStrategicMerge {
locPath, err := lc.localizeK8sResource(string(patch))
if err != nil {
return errors.WrapPrefixf(err, "unable to localize patchesStrategicMerge entry")
}
kust.PatchesStrategicMerge[i] = types.PatchStrategicMerge(locPath)
}
for i, replacement := range kust.Replacements {
locPath, err := lc.localizeFile(replacement.Path)
if err != nil {
return errors.WrapPrefixf(err, "unable to localize replacements entry")
}
kust.Replacements[i].Path = locPath
}
return nil
}
// localizeGenerator localizes the file paths on generator.
func (lc *localizer) localizeGenerator(generator *types.GeneratorArgs) error {
locEnvSrc, err := lc.localizeFile(generator.EnvSource)
if err != nil {
return errors.WrapPrefixf(err, "unable to localize generator env file")
}
locEnvs := make([]string, len(generator.EnvSources))
for i, env := range generator.EnvSources {
locEnvs[i], err = lc.localizeFile(env)
if err != nil {
return errors.WrapPrefixf(err, "unable to localize generator envs file")
}
}
locFiles := make([]string, len(generator.FileSources))
for i, file := range generator.FileSources {
locFiles[i], err = lc.localizeFileSource(file)
if err != nil {
return err
}
}
generator.EnvSource = locEnvSrc
generator.EnvSources = locEnvs
generator.FileSources = locFiles
return nil
}
// localizeFileSource returns the localized file source found in configMap and
// secretGenerators.
func (lc *localizer) localizeFileSource(source string) (string, error) {
key, file, err := generators.ParseFileSource(source)
if err != nil {
return "", errors.Wrap(err)
}
locFile, err := lc.localizeFile(file)
if err != nil {
return "", errors.WrapPrefixf(err, "invalid file source %q", source)
}
var locSource string
if source == file {
locSource = locFile
} else {
locSource = key + "=" + locFile
}
return locSource, nil
}
// localizeHelmInflationGenerator localizes helmChartInflationGenerator on kust.
// localizeHelmInflationGenerator localizes values files and copies local chart homes.
func (lc *localizer) localizeHelmInflationGenerator(kust *types.Kustomization) error {
for i, chart := range kust.HelmChartInflationGenerator {
locFile, err := lc.localizeFile(chart.Values)
if err != nil {
return errors.WrapPrefixf(err, "unable to localize helmChartInflationGenerator entry %d values", i)
}
kust.HelmChartInflationGenerator[i].Values = locFile
locDir, err := lc.copyChartHomeEntry(chart.ChartHome)
if err != nil {
return errors.WrapPrefixf(err, "unable to copy helmChartInflationGenerator entry %d", i)
}
kust.HelmChartInflationGenerator[i].ChartHome = locDir
}
return nil
}
// localizeHelmCharts localizes helmCharts and helmGlobals on kust.
// localizeHelmCharts localizes values files and copies a local chart home.
func (lc *localizer) localizeHelmCharts(kust *types.Kustomization) error {
for i, chart := range kust.HelmCharts {
locFile, err := lc.localizeFile(chart.ValuesFile)
if err != nil {
return errors.WrapPrefixf(err, "unable to localize helmCharts entry %d valuesFile", i)
}
kust.HelmCharts[i].ValuesFile = locFile
for j, valuesFile := range chart.AdditionalValuesFiles {
locFile, err = lc.localizeFile(valuesFile)
if err != nil {
return errors.WrapPrefixf(err, "unable to localize helmCharts entry %d additionalValuesFiles", i)
}
kust.HelmCharts[i].AdditionalValuesFiles[j] = locFile
}
}
if kust.HelmGlobals != nil {
locDir, err := lc.copyChartHomeEntry(kust.HelmGlobals.ChartHome)
if err != nil {
return errors.WrapPrefixf(err, "unable to copy helmGlobals")
}
kust.HelmGlobals.ChartHome = locDir
} else if len(kust.HelmCharts) > 0 {
_, err := lc.copyChartHomeEntry("")
if err != nil {
return errors.WrapPrefixf(err, "unable to copy default chart home")
}
}
return nil
}
// localizePatches localizes the file paths on patches if they are non-empty
func (lc *localizer) localizePatches(patches []types.Patch) error {
for i := range patches {
locPath, err := lc.localizeFile(patches[i].Path)
if err != nil {
return err
}
patches[i].Path = locPath
}
return nil
}
// localizeResource localizes resource path, a file or root, and returns the
// localized path
func (lc *localizer) localizeResource(path string) (string, error) {
var locPath string
content, fileErr := lc.ldr.Load(path)
// The following check catches the case where path is a repo root.
// Load on a repo will successfully return its README in HTML.
// Because HTML does not follow resource formatting, we then correctly try
// to localize path as a root.
if fileErr == nil {
_, resErr := lc.rFactory.NewResMapFromBytes(content)
if resErr != nil {
fileErr = errors.WrapPrefixf(resErr, "invalid resource at file %q", path)
} else {
locPath, fileErr = lc.localizeFileWithContent(path, content)
}
}
if fileErr != nil {
var rootErr error
locPath, rootErr = lc.localizeRoot(path)
if rootErr != nil {
err := PathLocalizeError{
Path: path,
FileError: fileErr,
RootError: rootErr,
}
return "", err
}
}
return locPath, nil
}
// localizeFile localizes file path if set and returns the localized path
func (lc *localizer) localizeFile(path string) (string, error) {
// Some localizable fields can be empty, for example, replacements.path.
// We rely on the build command to throw errors for the ones that cannot.
if path == "" {
return "", nil
}
content, err := lc.ldr.Load(path)
if err != nil {
return "", errors.Wrap(err)
}
return lc.localizeFileWithContent(path, content)
}
// localizeFileWithContent writes content to the localized file path and returns the localized path.
func (lc *localizer) localizeFileWithContent(path string, content []byte) (string, error) {
var locPath string
if loader.IsRemoteFile(path) {
if lc.fSys.Exists(lc.root.Join(LocalizeDir)) {
return "", errors.Errorf("%s already contains %s needed to store file %q", lc.root, LocalizeDir, path)
}
locPath = locFilePath(path)
} else {
// ldr has checked that path must be relative; this is subject to change in beta.
// We must clean path to:
// 1. avoid symlinks. A `kustomize build` run will fail if we write files to
// symlink paths outside the current root, given that we don't want to recreate
// the symlinks. Even worse, we could be writing files outside the localize destination.
// 2. avoid paths that temporarily traverse outside the current root,
// i.e. ../../../scope/target/current-root. The localized file will be surrounded by
// different directories than its source, and so an uncleaned path may no longer be valid.
locPath = cleanFilePath(lc.fSys, lc.root, path)
}
absPath := filepath.Join(lc.dst, locPath)
if err := lc.fSys.MkdirAll(filepath.Dir(absPath)); err != nil {
return "", errors.WrapPrefixf(err, "unable to create directories to localize file %q", path)
}
if err := lc.fSys.WriteFile(absPath, content); err != nil {
return "", errors.WrapPrefixf(err, "unable to localize file %q", path)
}
return locPath, nil
}
// localizeRoot localizes root path if set and returns the localized path
func (lc *localizer) localizeRoot(path string) (string, error) {
if path == "" {
return "", nil
}
ldr, err := lc.ldr.New(path)
if err != nil {
return "", errors.Wrap(err)
}
defer func() { _ = ldr.Cleanup() }()
root, err := filesys.ConfirmDir(lc.fSys, ldr.Root())
if err != nil {
log.Panicf("unable to establish validated root reference %q: %s", path, err)
}
var locPath string
if repo := ldr.Repo(); repo != "" {
if lc.fSys.Exists(lc.root.Join(LocalizeDir)) {
return "", errors.Errorf("%s already contains %s needed to store root %q", lc.root, LocalizeDir, path)
}
locPath, err = locRootPath(path, repo, root, lc.fSys)
if err != nil {
return "", err
}
} else {
locPath, err = filepath.Rel(lc.root.String(), root.String())
if err != nil {
log.Panicf("cannot find relative path between scoped localize roots %q and %q: %s", lc.root, root, err)
}
}
newDst := filepath.Join(lc.dst, locPath)
if err = lc.fSys.MkdirAll(newDst); err != nil {
return "", errors.WrapPrefixf(err, "unable to create root %q in localize destination", path)
}
err = (&localizer{
fSys: lc.fSys,
ldr: ldr,
root: root,
rFactory: lc.rFactory,
dst: newDst,
}).localize()
if err != nil {
return "", errors.WrapPrefixf(err, "unable to localize root %q", path)
}
return locPath, nil
}
// copyChartHomeEntry copies the helm chart home entry to lc dst
// at the same location relative to the root and returns said relative path.
// If entry is empty, copyChartHomeEntry returns the empty string.
// If entry does not exist, copyChartHome returns entry.
//
// copyChartHomeEntry copies the default home to the same location at dst,
// without following symlinks. An empty entry also indicates the default home.
func (lc *localizer) copyChartHomeEntry(entry string) (string, error) {
path := entry
if entry == "" {
path = types.HelmDefaultHome
}
if filepath.IsAbs(path) {
return "", errors.Errorf("absolute path %q not handled in alpha", path)
}
isDefault := lc.root.Join(path) == lc.root.Join(types.HelmDefaultHome)
locPath, err := lc.copyChartHome(path, !isDefault)
if err != nil {
return "", errors.WrapPrefixf(err, "unable to copy home %q", entry)
}
if entry == "" {
return "", nil
}
return locPath, nil
}
// copyChartHome copies path relative to lc root to dst and returns the
// copied location relative to dst. If clean is true, copyChartHome uses path's
// delinked location as the copy destination.
//
// If path does not exist, copyChartHome returns path.
func (lc *localizer) copyChartHome(path string, clean bool) (string, error) {
path, err := filepath.Rel(lc.root.String(), lc.root.Join(path))
if err != nil {
return "", errors.WrapPrefixf(err, "no path to chart home %q", path)
}
// Chart home may serve as untar destination.
// Note that we don't check if path is in scope.
if !lc.fSys.Exists(lc.root.Join(path)) {
return path, nil
}
// Perform localize directory checks.
ldr, err := lc.ldr.New(path)
if err != nil {
return "", errors.WrapPrefixf(err, "invalid chart home")
}
cleaned, err := filesys.ConfirmDir(lc.fSys, ldr.Root())
if err != nil {
log.Panicf("unable to confirm validated directory %q: %s", ldr.Root(), err)
}
toDst := path
if clean {
toDst, err = filepath.Rel(lc.root.String(), cleaned.String())
if err != nil {
log.Panicf("no path between scoped directories %q and %q: %s", lc.root, cleaned, err)
}
}
// Note this check does not guarantee that we copied the entire directory.
if dst := filepath.Join(lc.dst, toDst); !lc.fSys.Exists(dst) {
err = lc.copyDir(cleaned, filepath.Join(lc.dst, toDst))
if err != nil {
return "", errors.WrapPrefixf(err, "unable to copy chart home %q", path)
}
}
return toDst, nil
}
// copyDir copies src to dst. copyDir does not follow symlinks.
func (lc *localizer) copyDir(src filesys.ConfirmedDir, dst string) error {
err := lc.fSys.Walk(src.String(),
func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
pathToCreate, err := filepath.Rel(src.String(), path)
if err != nil {
log.Panicf("no path from %q to child file %q: %s", src, path, err)
}
pathInDst := filepath.Join(dst, pathToCreate)
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
return nil
}
if info.IsDir() {
err = lc.fSys.MkdirAll(pathInDst)
} else {
var content []byte
content, err = lc.fSys.ReadFile(path)
if err != nil {
return errors.Wrap(err)
}
err = lc.fSys.WriteFile(pathInDst, content)
}
return errors.Wrap(err)
})
if err != nil {
return errors.WrapPrefixf(err, "unable to copy directory %q", src)
}
return nil
}
// localizeBuiltinPlugins localizes built-in plugins on kust that can contain file paths. The built-in plugins
// can be inline or in a file. This excludes the HelmChartInflationGenerator.
//
// Note that the localization in this function has not been implemented yet.
func (lc *localizer) localizeBuiltinPlugins(kust *types.Kustomization) error {
for fieldName, entries := range map[string][]string{
"generators": kust.Generators,
"transformers": kust.Transformers,
"validators": kust.Validators,
} {
for i, entry := range entries {
rm, isPath, err := lc.loadK8sResource(entry)
if err != nil {
return errors.WrapPrefixf(err, "unable to load %s entry", fieldName)
}
err = rm.ApplyFilter(&localizeBuiltinPlugins{lc: lc})
if err != nil {
return errors.Wrap(err)
}
localizedPlugin, err := rm.AsYaml()
if err != nil {
return errors.WrapPrefixf(err, "unable to serialize localized %s entry %q", fieldName, entry)
}
var localizedEntry string
if isPath {
localizedEntry, err = lc.localizeFileWithContent(entry, localizedPlugin)
if err != nil {
return errors.WrapPrefixf(err, "unable to localize %s entry", fieldName)
}
} else {
localizedEntry = string(localizedPlugin)
}
entries[i] = localizedEntry
}
}
return nil
}
// localizeK8sResource returns the localized resourceEntry if it is a file
// containing a kubernetes resource.
// localizeK8sResource returns resourceEntry if it is an inline resource.
func (lc *localizer) localizeK8sResource(resourceEntry string) (string, error) {
_, isFile, err := lc.loadK8sResource(resourceEntry)
if err != nil {
return "", err
}
if isFile {
return lc.localizeFile(resourceEntry)
}
return resourceEntry, nil
}
// loadK8sResource tries to load resourceEntry as a file path or inline
// kubernetes resource.
// On success, loadK8sResource returns the loaded resource map and whether
// resourceEntry is a file path.
func (lc *localizer) loadK8sResource(resourceEntry string) (resmap.ResMap, bool, error) {
rm, inlineErr := lc.rFactory.NewResMapFromBytes([]byte(resourceEntry))
if inlineErr != nil {
var fileErr error
rm, fileErr = lc.rFactory.FromFile(lc.ldr, resourceEntry)
if fileErr != nil {
err := ResourceLoadError{
InlineError: inlineErr,
FileError: fileErr,
}
return nil, false, errors.WrapPrefixf(err, "unable to load resource entry %q", resourceEntry)
}
}
return rm, inlineErr != nil, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,135 +0,0 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package localizer
import (
"path/filepath"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/git"
"sigs.k8s.io/kustomize/api/loader"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
// Args holds localize arguments
type Args struct {
// target; local copy if remote
Target filesys.ConfirmedDir
// directory that bounds target's local references
// repo directory of local copy if target is remote
Scope filesys.ConfirmedDir
// localize destination
NewDir filesys.ConfirmedDir
}
// Loader is an ifc.Loader that enforces additional constraints specific to kustomize localize.
type Loader struct {
fSys filesys.FileSystem
args *Args
// loader at Loader's current directory
ifc.Loader
// whether Loader and all its ancestors are the result of local references
local bool
}
var _ ifc.Loader = &Loader{}
// NewLoader is the factory method for Loader, under localize constraints, at rawTarget. For invalid localize arguments,
// NewLoader returns an error.
func NewLoader(rawTarget string, rawScope string, rawNewDir string, fSys filesys.FileSystem) (*Loader, Args, error) {
// check earlier to avoid cleanup
repoSpec, err := git.NewRepoSpecFromURL(rawTarget)
if err == nil && repoSpec.Ref == "" {
return nil, Args{}, errors.Errorf("localize remote root %q missing ref query string parameter", rawTarget)
}
// for security, should enforce load restrictions
ldr, err := loader.NewLoader(loader.RestrictionRootOnly, rawTarget, fSys)
if err != nil {
return nil, Args{}, errors.WrapPrefixf(err, "unable to establish localize target %q", rawTarget)
}
scope, err := establishScope(rawScope, rawTarget, ldr, fSys)
if err != nil {
_ = ldr.Cleanup()
return nil, Args{}, errors.WrapPrefixf(err, "invalid localize scope %q", rawScope)
}
newDir, err := createNewDir(rawNewDir, ldr, repoSpec, fSys)
if err != nil {
_ = ldr.Cleanup()
return nil, Args{}, errors.WrapPrefixf(err, "invalid localize destination %q", rawNewDir)
}
args := Args{
Target: filesys.ConfirmedDir(ldr.Root()),
Scope: scope,
NewDir: newDir,
}
return &Loader{
fSys: fSys,
args: &args,
Loader: ldr,
local: scope != "",
}, args, nil
}
// Load returns the contents of path if path is a valid localize file.
// Otherwise, Load returns an error.
func (ll *Loader) Load(path string) ([]byte, error) {
// checks in root, and thus in scope
content, err := ll.Loader.Load(path)
if err != nil {
return nil, errors.WrapPrefixf(err, "invalid file reference")
}
if filepath.IsAbs(path) {
return nil, errors.Errorf("absolute paths not yet supported in alpha: file path %q is absolute", path)
}
if !loader.IsRemoteFile(path) && ll.local {
cleanPath := cleanFilePath(ll.fSys, filesys.ConfirmedDir(ll.Root()), path)
cleanAbs := filepath.Join(ll.Root(), cleanPath)
dir := filesys.ConfirmedDir(filepath.Dir(cleanAbs))
// target cannot reference newDir, as this load would've failed prior to localize;
// not a problem if remote because then reference could only be in newDir if repo copy,
// which will be cleaned, is inside newDir
if dir.HasPrefix(ll.args.NewDir) {
return nil, errors.Errorf("file %q at %q enters localize destination %q", path, cleanAbs, ll.args.NewDir)
}
}
return content, nil
}
// New returns a Loader at path if path is a valid localize root.
// Otherwise, New returns an error.
func (ll *Loader) New(path string) (ifc.Loader, error) {
ldr, err := ll.Loader.New(path)
if err != nil {
return nil, errors.WrapPrefixf(err, "invalid root reference")
}
if repo := ldr.Repo(); repo == "" {
if ll.local && !filesys.ConfirmedDir(ldr.Root()).HasPrefix(ll.args.Scope) {
return nil, errors.Errorf("root %q outside localize scope %q", ldr.Root(), ll.args.Scope)
}
if ll.local && filesys.ConfirmedDir(ldr.Root()).HasPrefix(ll.args.NewDir) {
return nil, errors.Errorf(
"root %q references into localize destination %q", ldr.Root(), ll.args.NewDir)
}
} else if !hasRef(path) {
return nil, errors.Errorf("localize remote root %q missing ref query string parameter", path)
}
return &Loader{
fSys: ll.fSys,
args: ll.args,
Loader: ldr,
local: ll.local && ldr.Repo() == "",
}, nil
}

View File

@@ -1,300 +0,0 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package localizer_test
import (
"bytes"
"log"
"os"
"testing"
"github.com/stretchr/testify/require"
"sigs.k8s.io/kustomize/api/ifc"
. "sigs.k8s.io/kustomize/api/internal/localizer"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
func checkNewLoader(req *require.Assertions, ldr *Loader, args *Args, target string, scope string, newDir string, fSys filesys.FileSystem) {
checkLoader(req, ldr, target)
checkArgs(req, args, target, scope, newDir, fSys)
}
func checkLoader(req *require.Assertions, ldr ifc.Loader, root string) {
req.Equal(root, ldr.Root())
req.Empty(ldr.Repo())
}
func checkArgs(req *require.Assertions, args *Args, target string, scope string, newDir string, fSys filesys.FileSystem) {
req.Equal(target, args.Target.String())
req.Equal(scope, args.Scope.String())
req.Equal(newDir, args.NewDir.String())
req.True(fSys.Exists(newDir))
}
func TestLocalLoadNewAndCleanup(t *testing.T) {
req := require.New(t)
fSys := makeMemoryFs(t)
var buf bytes.Buffer
log.SetOutput(&buf)
defer func() {
log.SetOutput(os.Stderr)
}()
// typical setup
ldr, args, err := NewLoader("a", "/", "/newDir", fSys)
req.NoError(err)
checkNewLoader(req, ldr, &args, "/a", "/", "/newDir", fSys)
fSysCopy := makeMemoryFs(t)
req.NoError(fSysCopy.Mkdir("/newDir"))
req.Equal(fSysCopy, fSys)
// easy load directly in root
content, err := ldr.Load("pod.yaml")
req.NoError(err)
req.Equal([]byte(podConfiguration), content)
// typical sibling root reference
sibLdr, err := ldr.New("../alpha")
req.NoError(err)
checkLoader(req, sibLdr, "/alpha")
// only need to test once, since don't need to call Cleanup() on local target
req.NoError(sibLdr.Cleanup())
req.NoError(ldr.Cleanup())
// file system and buffer checks are also one-time
req.Equal(fSysCopy, fSys)
req.Empty(buf.String())
}
func TestNewLocLoaderDefaultForRootTarget(t *testing.T) {
cases := map[string]struct {
target string
scope string
}{
"explicit": {
"/",
".",
},
"implicit": {
".",
"",
},
}
for name, params := range cases {
params := params
t.Run(name, func(t *testing.T) {
req := require.New(t)
fSys := makeMemoryFs(t)
ldr, args, err := NewLoader(params.target, params.scope, "", fSys)
req.NoError(err)
checkNewLoader(req, ldr, &args, "/", "/", "/"+DstPrefix, fSys)
// file in root, but nested
content, err := ldr.Load("a/pod.yaml")
req.NoError(err)
req.Equal([]byte(podConfiguration), content)
childLdr, err := ldr.New("a")
req.NoError(err)
checkLoader(req, childLdr, "/a")
// messy, uncleaned path
content, err = childLdr.Load("./../a/pod.yaml")
req.NoError(err)
req.Equal([]byte(podConfiguration), content)
})
}
}
func TestNewMultiple(t *testing.T) {
req := require.New(t)
fSys := makeMemoryFs(t)
// default destination for non-file system root target
// destination outside of scope
ldr, args, err := NewLoader("/alpha/beta", "/alpha", "", fSys)
req.NoError(err)
checkNewLoader(req, ldr, &args, "/alpha/beta", "/alpha", "/"+DstPrefix+"-beta", fSys)
// nested child root that isn't cleaned
descLdr, err := ldr.New("../beta/gamma/delta")
req.NoError(err)
checkLoader(req, descLdr, "/alpha/beta/gamma/delta")
// upwards traversal
higherLdr, err := descLdr.New("../../say")
req.NoError(err)
checkLoader(req, higherLdr, "/alpha/beta/say")
}
func makeWdFs(t *testing.T) map[string]filesys.FileSystem {
t.Helper()
req := require.New(t)
root := filesys.MakeEmptyDirInMemory()
req.NoError(root.MkdirAll("a/b/c/d/e"))
outer, err := root.Find("a")
req.NoError(err)
middle, err := root.Find("a/b/c")
req.NoError(err)
return map[string]filesys.FileSystem{
"a": outer,
"a/b/c": middle,
}
}
func TestNewLocLoaderCwdNotRoot(t *testing.T) {
cases := map[string]struct {
wd string
target string
scope string
newDir string
}{
// target not immediate child of scope
"outer dir": {
"a",
"b/c/d/e",
"b/c",
"b/newDir",
},
"scope": {
"a/b/c",
"d/e",
".",
"d/e/newDir",
},
}
for name, test := range cases {
test := test
t.Run(name, func(t *testing.T) {
req := require.New(t)
fSys := makeWdFs(t)[test.wd]
ldr, args, err := NewLoader(test.target, test.scope, test.newDir, fSys)
req.NoError(err)
checkLoader(req, ldr, "a/b/c/d/e")
req.Equal("a/b/c/d/e", args.Target.String())
req.Equal("a/b/c", args.Scope.String())
req.Equal(test.wd+"/"+test.newDir, args.NewDir.String())
// memory file system can only find paths rooted at current node
req.True(fSys.Exists(test.newDir))
})
}
}
func TestNewLocLoaderFails(t *testing.T) {
cases := map[string]struct {
target string
scope string
dest string
}{
"non-existent target": {
"/b",
"/",
"/newDir",
},
"file target": {
"/a/pod.yaml",
"/",
"/newDir",
},
"inner scope": {
"/alpha",
"/alpha/beta",
"/newDir",
},
"side scope": {
"/alpha",
"/a",
"/newDir",
},
"existing dst": {
"/alpha",
"/",
"/a",
},
}
for name, params := range cases {
params := params
t.Run(name, func(t *testing.T) {
var buf bytes.Buffer
log.SetOutput(&buf)
defer func() {
log.SetOutput(os.Stderr)
}()
_, _, err := NewLoader(params.target, params.scope, params.dest, makeMemoryFs(t))
require.Error(t, err)
require.Empty(t, buf.String())
})
}
}
func TestNewFails(t *testing.T) {
req := require.New(t)
fSys := makeMemoryFs(t)
ldr, args, err := NewLoader("/alpha/beta/gamma", "alpha", "alpha/beta/gamma/newDir", fSys)
req.NoError(err)
checkNewLoader(req, ldr, &args, "/alpha/beta/gamma", "/alpha", "/alpha/beta/gamma/newDir", fSys)
cases := map[string]string{
"outside scope": "../../../a",
"at dst": "newDir",
"ancestor": "../../beta",
"non-existent root": "delt",
"file": "delta/deployment.yaml",
}
for name, root := range cases {
root := root
t.Run(name, func(t *testing.T) {
fSys := makeMemoryFs(t)
ldr, _, err := NewLoader("/alpha/beta/gamma", "alpha", "alpha/beta/gamma/newDir", fSys)
require.NoError(t, err)
_, err = ldr.New(root)
require.Error(t, err)
})
}
}
func TestLoadFails(t *testing.T) {
req := require.New(t)
fSys := makeMemoryFs(t)
ldr, args, err := NewLoader("./a/../a", "/a/../a", "/a/newDir", fSys)
req.NoError(err)
checkNewLoader(req, ldr, &args, "/a", "/a", "/a/newDir", fSys)
cases := map[string]string{
"absolute path": "/a/pod.yaml",
"directory": "b",
"non-existent file": "kubectl.yaml",
"file outside root": "../alpha/beta/gamma/delta/deployment.yaml",
"inside dst": "newDir/pod.yaml",
}
for name, file := range cases {
file := file
t.Run(name, func(t *testing.T) {
req := require.New(t)
fSys := makeMemoryFs(t)
ldr, _, err := NewLoader("./a/../a", "/a/../a", "/a/newDir", fSys)
req.NoError(err)
req.NoError(fSys.WriteFile("/a/newDir/pod.yaml", []byte(podConfiguration)))
_, err = ldr.Load(file)
req.Error(err)
})
}
}

View File

@@ -1,218 +0,0 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package localizer
import (
"log"
"net/url"
"path/filepath"
"strings"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/git"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
const (
// DstPrefix prefixes the target and ref, if target is remote, in the default localize destination directory name
DstPrefix = "localized"
// LocalizeDir is the name of the localize directories used to store remote content in the localize destination
LocalizeDir = "localized-files"
// FileSchemeDir is the name of the directory immediately inside LocalizeDir used to store file-schemed repos
FileSchemeDir = "file-schemed"
)
// establishScope returns the effective scope given localize arguments and targetLdr at rawTarget. For remote rawTarget,
// the effective scope is the downloaded repo.
func establishScope(rawScope string, rawTarget string, targetLdr ifc.Loader, fSys filesys.FileSystem) (filesys.ConfirmedDir, error) {
if repo := targetLdr.Repo(); repo != "" {
if rawScope != "" {
return "", errors.Errorf("scope %q specified for remote localize target %q", rawScope, rawTarget)
}
return filesys.ConfirmedDir(repo), nil
}
// default scope
if rawScope == "" {
return filesys.ConfirmedDir(targetLdr.Root()), nil
}
scope, err := filesys.ConfirmDir(fSys, rawScope)
if err != nil {
return "", errors.WrapPrefixf(err, "unable to establish localize scope")
}
if !filesys.ConfirmedDir(targetLdr.Root()).HasPrefix(scope) {
return scope, errors.Errorf("localize scope %q does not contain target %q at %q", rawScope, rawTarget,
targetLdr.Root())
}
return scope, nil
}
// createNewDir returns the localize destination directory or error. Note that spec is nil if targetLdr is at local
// target.
func createNewDir(rawNewDir string, targetLdr ifc.Loader, spec *git.RepoSpec, fSys filesys.FileSystem) (filesys.ConfirmedDir, error) {
if rawNewDir == "" {
rawNewDir = defaultNewDir(targetLdr, spec)
}
if fSys.Exists(rawNewDir) {
return "", errors.Errorf("localize destination %q already exists", rawNewDir)
}
// destination directory must sit in an existing directory
if err := fSys.Mkdir(rawNewDir); err != nil {
return "", errors.WrapPrefixf(err, "unable to create localize destination directory")
}
newDir, err := filesys.ConfirmDir(fSys, rawNewDir)
if err != nil {
if errCleanup := fSys.RemoveAll(newDir.String()); errCleanup != nil {
log.Printf("%s", errors.WrapPrefixf(errCleanup, "unable to clean localize destination"))
}
return "", errors.WrapPrefixf(err, "unable to establish localize destination")
}
return newDir, nil
}
// defaultNewDir calculates the default localize destination directory name from targetLdr at the localize target
// and spec of target, which is nil if target is local
func defaultNewDir(targetLdr ifc.Loader, spec *git.RepoSpec) string {
targetDir := filepath.Base(targetLdr.Root())
if repo := targetLdr.Repo(); repo != "" {
// kustomize doesn't download repo into repo-named folder
// must find repo folder name from url
if repo == targetLdr.Root() {
targetDir = urlBase(spec.RepoPath)
}
return strings.Join([]string{DstPrefix, targetDir, strings.ReplaceAll(spec.Ref, "/", "-")}, "-")
}
// special case for local target directory since destination directory cannot have "/" in name
if targetDir == string(filepath.Separator) {
return DstPrefix
}
return strings.Join([]string{DstPrefix, targetDir}, "-")
}
// urlBase is the url equivalent of filepath.Base
func urlBase(url string) string {
cleaned := strings.TrimRight(url, "/")
i := strings.LastIndex(cleaned, "/")
if i < 0 {
return cleaned
}
return cleaned[i+1:]
}
// hasRef checks if repoURL has ref query string parameter
func hasRef(repoURL string) bool {
repoSpec, err := git.NewRepoSpecFromURL(repoURL)
if err != nil {
log.Fatalf("unable to parse validated root url: %s", err)
}
return repoSpec.Ref != ""
}
// cleanFilePath returns file cleaned, where file is a relative path to root on fSys
func cleanFilePath(fSys filesys.FileSystem, root filesys.ConfirmedDir, file string) string {
abs := root.Join(file)
dir, f, err := fSys.CleanedAbs(abs)
if err != nil {
log.Fatalf("cannot clean validated file path %q: %s", abs, err)
}
locPath, err := filepath.Rel(root.String(), dir.Join(f))
if err != nil {
log.Fatalf("cannot find path from parent %q to file %q: %s", root, dir.Join(f), err)
}
return locPath
}
// locFilePath converts a URL to its localized form, e.g.
// https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/api/krusty/testdata/localize/simple/service.yaml ->
// localized-files/raw.githubusercontent.com/kubernetes-sigs/kustomize/master/api/krusty/testdata/localize/simple/service.yaml.
//
// fileURL must be a validated file URL.
func locFilePath(fileURL string) string {
// File urls must have http or https scheme, so it is safe to use url.Parse.
u, err := url.Parse(fileURL)
if err != nil {
log.Panicf("cannot parse validated file url %q: %s", fileURL, err)
}
// HTTP requests use the escaped path, so we use it here. Escaped paths also help us
// preserve percent-encoding in the original path, in the absence of illegal characters,
// in case they have special meaning to the host.
// Extraneous '..' parent directory dot-segments should be removed.
path := filepath.Join(string(filepath.Separator), filepath.FromSlash(u.EscapedPath()))
// We intentionally exclude userinfo and port.
// Raw github urls are the only type of file urls kustomize officially accepts.
// In this case, the path already consists of org, repo, version, and path in repo, in order,
// so we can use it as is.
return filepath.Join(LocalizeDir, u.Hostname(), path)
}
// locRootPath returns the relative localized path of the validated root url rootURL, where the local copy of its repo
// is at repoDir and the copy of its root is at root on fSys.
func locRootPath(rootURL, repoDir string, root filesys.ConfirmedDir, fSys filesys.FileSystem) (string, error) {
repoSpec, err := git.NewRepoSpecFromURL(rootURL)
if err != nil {
log.Panicf("cannot parse validated repo url %q: %s", rootURL, err)
}
host, err := parseHost(repoSpec)
if err != nil {
return "", errors.WrapPrefixf(err, "unable to parse host of remote root %q", rootURL)
}
repo, err := filesys.ConfirmDir(fSys, repoDir)
if err != nil {
log.Panicf("unable to establish validated repo download location %q: %s", repoDir, err)
}
// calculate from copy instead of url to straighten symlinks
inRepo, err := filepath.Rel(repo.String(), root.String())
if err != nil {
log.Panicf("cannot find path from %q to child directory %q: %s", repo, root, err)
}
// the git-server-side directory name conventionally (but not universally) ends in .git, which
// is conventionally stripped from the client-side directory name used for the clone.
localRepoPath := strings.TrimSuffix(repoSpec.RepoPath, ".git")
// We do not need to escape RepoPath, a path on the git server.
// However, like git, we clean dot-segments from RepoPath.
// Git does not allow ref value to contain dot-segments.
return filepath.Join(LocalizeDir,
host,
filepath.Join(string(filepath.Separator), filepath.FromSlash(localRepoPath)),
filepath.FromSlash(repoSpec.Ref),
inRepo), nil
}
// parseHost returns the localize directory path corresponding to repoSpec.Host
func parseHost(repoSpec *git.RepoSpec) (string, error) {
var target string
switch scheme, _, _ := strings.Cut(repoSpec.Host, "://"); scheme {
case "gh:":
// 'gh' was meant to be a local github.com shorthand, in which case
// the .gitconfig file could map it to any host. See origin here:
// https://github.com/kubernetes-sigs/kustomize/blob/kustomize/v4.5.7/api/internal/git/repospec.go#L203
// We give it a special host directory here under the assumption
// that we are unlikely to have another host simply named 'gh'.
return "gh", nil
case "file":
// We put file-scheme repos under a special directory to avoid
// colluding local absolute paths with hosts.
return FileSchemeDir, nil
case "https", "http", "ssh":
target = repoSpec.Host
default:
// We must have relative ssh url; in other words, the url has scp-like syntax.
// We attach a scheme to avoid url.Parse errors.
target = "ssh://" + repoSpec.Host
}
// url.Parse will not recognize ':' delimiter that both RepoSpec and git accept.
target = strings.TrimSuffix(target, ":")
u, err := url.Parse(target)
if err != nil {
return "", errors.Wrap(err)
}
// strip scheme, userinfo, port, and any trailing slashes.
return u.Hostname(), nil
}

View File

@@ -1,303 +0,0 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package localizer //nolint:testpackage
import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/require"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/git"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
func TestDefaultNewDirRepo(t *testing.T) {
for name, test := range map[string]struct {
url, dst string
}{
"simple": {
url: "https://github.com/org/repo?ref=value",
dst: "localized-repo-value",
},
"slashed_ref": {
url: "https://github.com/org/repo?ref=group/version",
dst: "localized-repo-group-version",
},
} {
t.Run(name, func(t *testing.T) {
repoSpec, err := git.NewRepoSpecFromURL(test.url)
require.NoError(t, err)
require.Equal(t, test.dst, defaultNewDir(&fakeLoader{t.TempDir()}, repoSpec))
})
}
}
type fakeLoader struct {
root string
}
func (fl *fakeLoader) Root() string {
return fl.root
}
func (fl *fakeLoader) Repo() string {
return fl.root
}
func (fl *fakeLoader) Load(_ string) ([]byte, error) {
return []byte{}, nil
}
func (fl *fakeLoader) New(path string) (ifc.Loader, error) {
return &fakeLoader{path}, nil
}
func (fl *fakeLoader) Cleanup() error {
return nil
}
func TestUrlBase(t *testing.T) {
require.Equal(t, "repo", urlBase("https://github.com/org/repo"))
}
func TestUrlBaseTrailingSlash(t *testing.T) {
require.Equal(t, "repo", urlBase("github.com/org/repo//"))
}
// simpleJoin is filepath.Join() without the side effects of filepath.Clean()
func simpleJoin(t *testing.T, elems ...string) string {
t.Helper()
return strings.Join(elems, string(filepath.Separator))
}
func TestLocFilePath(t *testing.T) {
for name, tUnit := range map[string]struct {
url, path string
}{
"official": {
url: "https://raw.githubusercontent.com/org/repo/ref/path/to/file.yaml",
path: simpleJoin(t, "raw.githubusercontent.com", "org", "repo", "ref", "path", "to", "file.yaml"),
},
"http-scheme": {
url: "http://host/path",
path: simpleJoin(t, "host", "path"),
},
"extraneous_components": {
url: "http://userinfo@host:1234/path/file?query",
path: simpleJoin(t, "host", "path", "file"),
},
"empty_path": {
url: "https://host",
path: "host",
},
"empty_path_segment": {
url: "https://host//",
path: "host",
},
"percent-encoded_path": {
url: "https://host/file%2Eyaml",
path: simpleJoin(t, "host", "file%2Eyaml"),
},
"dot-segments": {
url: "https://host/path/blah/../to/foo/bar/../../file/./",
path: simpleJoin(t, "host", "path", "to", "file"),
},
"extraneous_dot-segments": {
url: "https://host/foo/bar/baz/../../../../file",
path: simpleJoin(t, "host", "file"),
},
} {
t.Run(name, func(t *testing.T) {
require.Equal(t, simpleJoin(t, LocalizeDir, tUnit.path), locFilePath(tUnit.url))
})
}
}
func TestLocFilePathColon(t *testing.T) {
req := require.New(t)
// The colon is special because it was once used as the unix file separator.
const url = "https://[2001:4860:4860::8888]/file.yaml"
const host = "2001:4860:4860::8888"
const file = "file.yaml"
req.Equal(simpleJoin(t, LocalizeDir, host, file), locFilePath(url))
fSys := filesys.MakeFsOnDisk()
targetDir := simpleJoin(t, t.TempDir(), host)
// We check that we can create single directory, meaning ':' not used as file separator.
req.NoError(fSys.Mkdir(targetDir))
_, err := fSys.Create(simpleJoin(t, targetDir, file))
req.NoError(err)
// We check that the directory with such name is readable.
files, err := fSys.ReadDir(targetDir)
req.NoError(err)
req.Equal([]string{file}, files)
}
func TestLocFilePath_SpecialChar(t *testing.T) {
req := require.New(t)
// The wild card character is one of the legal uri characters with more meaning
// to the system, so we test it here.
const wildcard = "*"
req.Equal(simpleJoin(t, LocalizeDir, "host", wildcard), locFilePath("https://host/*"))
fSys := filesys.MakeFsOnDisk()
testDir := t.TempDir()
req.NoError(fSys.Mkdir(simpleJoin(t, testDir, "a")))
req.NoError(fSys.WriteFile(simpleJoin(t, testDir, "b"), []byte{}))
// We check that we can create and read from wild card-named file.
// We check that the file system is not matching it to existing file names.
req.NoError(fSys.WriteFile(simpleJoin(t, testDir, wildcard), []byte("test")))
content, err := fSys.ReadFile(simpleJoin(t, testDir, wildcard))
req.NoError(err)
req.Equal("test", string(content))
}
func TestLocFilePath_SpecialFiles(t *testing.T) {
for name, tFSys := range map[string]struct {
urlPath string
pathDir, pathFile string
}{
"windows_reserved_name": {
urlPath: "/aux/file",
pathDir: "aux",
pathFile: "file",
},
"hidden_files": {
urlPath: "/.../.file",
pathDir: "...",
pathFile: ".file",
},
} {
t.Run(name, func(t *testing.T) {
req := require.New(t)
expectedPath := simpleJoin(t, LocalizeDir, "host", tFSys.pathDir, tFSys.pathFile)
req.Equal(expectedPath, locFilePath("https://host"+tFSys.urlPath))
fSys := filesys.MakeFsOnDisk()
targetDir := simpleJoin(t, t.TempDir(), tFSys.pathDir)
req.NoError(fSys.Mkdir(targetDir))
req.NoError(fSys.WriteFile(simpleJoin(t, targetDir, tFSys.pathFile), []byte("test")))
content, err := fSys.ReadFile(simpleJoin(t, targetDir, tFSys.pathFile))
req.NoError(err)
req.Equal([]byte("test"), content)
})
}
}
func makeConfirmedDir(t *testing.T) (filesys.FileSystem, filesys.ConfirmedDir) {
t.Helper()
fSys := filesys.MakeFsOnDisk()
testDir, err := filesys.NewTmpConfirmedDir()
require.NoError(t, err)
t.Cleanup(func() {
_ = fSys.RemoveAll(testDir.String())
})
return fSys, testDir
}
func TestLocRootPath_URLComponents(t *testing.T) {
for name, test := range map[string]struct {
urlf, path string
}{
"ssh": {
urlf: "ssh://git@github.com/org/repo//%s?ref=value",
path: simpleJoin(t, "github.com", "org", "repo", "value"),
},
"rel_ssh": {
urlf: "git@github.com:org/repo//%s?ref=value",
path: simpleJoin(t, "github.com", "org", "repo", "value"),
},
"https": {
urlf: "https://gitlab.com/org/repo//%s?ref=value",
path: simpleJoin(t, "gitlab.com", "org", "repo", "value"),
},
"file": {
urlf: "file:///var/run/repo//%s?ref=value",
path: simpleJoin(t, FileSchemeDir, "var", "run", "repo", "value"),
},
"IPv6": {
urlf: "https://[2001:4860:4860::8888]/org/repo//%s?ref=value",
path: simpleJoin(t, "2001:4860:4860::8888", "org", "repo", "value"),
},
"port": {
urlf: "https://localhost.com:8080/org/repo//%s?ref=value",
path: simpleJoin(t, "localhost.com", "org", "repo", "value"),
},
"no_org": {
urlf: "https://github.com/repo//%s?ref=value",
path: simpleJoin(t, "github.com", "repo", "value"),
},
".git_suffix": {
urlf: "https://github.com/org1/org2/repo.git//%s?ref=value",
path: simpleJoin(t, "github.com", "org1", "org2", "repo", "value"),
},
"dot-segments": {
urlf: "https://github.com/./../org/../org/repo.git//%s?ref=value",
path: simpleJoin(t, "github.com", "org", "repo", "value"),
},
"no_path_delimiter": {
urlf: "https://github.com/org/repo/%s?ref=value",
path: simpleJoin(t, "github.com", "org", "repo", "value"),
},
"illegal_windows_dir": {
urlf: "https://gitlab.com/org./repo..git//%s?ref=value",
path: simpleJoin(t, "gitlab.com", "org.", "repo.", "value"),
},
"ref_has_slash": {
urlf: "https://gitlab.com/org/repo//%s?ref=group/version/kind",
path: simpleJoin(t, "gitlab.com", "org", "repo", "group", "version", "kind"),
},
} {
t.Run(name, func(t *testing.T) {
u := fmt.Sprintf(test.urlf, "path/to/root")
path := simpleJoin(t, LocalizeDir, test.path, "path", "to", "root")
fSys, testDir := makeConfirmedDir(t)
repoDir := simpleJoin(t, testDir.String(), "repo_random-hash")
require.NoError(t, fSys.Mkdir(repoDir))
rootDir := simpleJoin(t, repoDir, "path", "to", "root")
require.NoError(t, fSys.MkdirAll(rootDir))
actual, err := locRootPath(u, repoDir, filesys.ConfirmedDir(rootDir), fSys)
require.NoError(t, err)
require.Equal(t, path, actual)
require.NoError(t, fSys.MkdirAll(simpleJoin(t, testDir.String(), path)))
})
}
}
func TestLocRootPath_Repo(t *testing.T) {
const url = "https://github.com/org/repo?ref=value"
expected := simpleJoin(t, LocalizeDir, "github.com", "org", "repo", "value")
fSys, testDir := makeConfirmedDir(t)
actual, err := locRootPath(url, testDir.String(), testDir, fSys)
require.NoError(t, err)
require.Equal(t, expected, actual)
}
func TestLocRootPath_SymlinkPath(t *testing.T) {
const url = "https://github.com/org/repo//symlink?ref=value"
fSys, repoDir := makeConfirmedDir(t)
rootDir := simpleJoin(t, repoDir.String(), "actual-root")
require.NoError(t, fSys.Mkdir(rootDir))
require.NoError(t, os.Symlink(rootDir, simpleJoin(t, repoDir.String(), "symlink")))
expected := simpleJoin(t, LocalizeDir, "github.com", "org", "repo", "value", "actual-root")
actual, err := locRootPath(url, repoDir.String(), filesys.ConfirmedDir(rootDir), fSys)
require.NoError(t, err)
require.Equal(t, expected, actual)
}

View File

@@ -22,7 +22,7 @@ import (
// contains a Pod; Deployment, Job, StatefulSet, etc.
// The ConfigMap is the ReferralTarget, the others are Referrers.
//
// If the name of a ConfigMap instance changed from 'alice' to 'bob',
// If the the name of a ConfigMap instance changed from 'alice' to 'bob',
// one must
// - visit all objects that could refer to the ConfigMap (the Referrers)
// - see if they mention 'alice',

View File

@@ -10,7 +10,6 @@ import (
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/errors"
)
// TransformerConfig holds the data needed to perform transformations.
@@ -19,7 +18,6 @@ type TransformerConfig struct {
NameSuffix types.FsSlice `json:"nameSuffix,omitempty" yaml:"nameSuffix,omitempty"`
NameSpace types.FsSlice `json:"namespace,omitempty" yaml:"namespace,omitempty"`
CommonLabels types.FsSlice `json:"commonLabels,omitempty" yaml:"commonLabels,omitempty"`
TemplateLabels types.FsSlice `json:"templateLabels,omitempty" yaml:"templateLabels,omitempty"`
CommonAnnotations types.FsSlice `json:"commonAnnotations,omitempty" yaml:"commonAnnotations,omitempty"`
NameReference nbrSlice `json:"nameReference,omitempty" yaml:"nameReference,omitempty"`
VarReference types.FsSlice `json:"varReference,omitempty" yaml:"varReference,omitempty"`
@@ -60,10 +58,8 @@ func MakeTransformerConfig(
// sortFields provides determinism in logging, tests, etc.
func (t *TransformerConfig) sortFields() {
sort.Sort(t.NamePrefix)
sort.Sort(t.NameSuffix)
sort.Sort(t.NameSpace)
sort.Sort(t.CommonLabels)
sort.Sort(t.TemplateLabels)
sort.Sort(t.CommonAnnotations)
sort.Sort(t.NameReference)
sort.Sort(t.VarReference)
@@ -112,44 +108,40 @@ func (t *TransformerConfig) Merge(input *TransformerConfig) (
merged = &TransformerConfig{}
merged.NamePrefix, err = t.NamePrefix.MergeAll(input.NamePrefix)
if err != nil {
return nil, errors.WrapPrefixf(err, "failed to merge NamePrefix fieldSpec")
return nil, err
}
merged.NameSuffix, err = t.NameSuffix.MergeAll(input.NameSuffix)
if err != nil {
return nil, errors.WrapPrefixf(err, "failed to merge NameSuffix fieldSpec")
return nil, err
}
merged.NameSpace, err = t.NameSpace.MergeAll(input.NameSpace)
if err != nil {
return nil, errors.WrapPrefixf(err, "failed to merge NameSpace fieldSpec")
return nil, err
}
merged.CommonAnnotations, err = t.CommonAnnotations.MergeAll(
input.CommonAnnotations)
if err != nil {
return nil, errors.WrapPrefixf(err, "failed to merge CommonAnnotations fieldSpec")
return nil, err
}
merged.CommonLabels, err = t.CommonLabels.MergeAll(input.CommonLabels)
if err != nil {
return nil, errors.WrapPrefixf(err, "failed to merge CommonLabels fieldSpec")
}
merged.TemplateLabels, err = t.TemplateLabels.MergeAll(input.TemplateLabels)
if err != nil {
return nil, errors.WrapPrefixf(err, "failed to merge TemplateLabels fieldSpec")
return nil, err
}
merged.VarReference, err = t.VarReference.MergeAll(input.VarReference)
if err != nil {
return nil, errors.WrapPrefixf(err, "failed to merge VarReference fieldSpec")
return nil, err
}
merged.NameReference, err = t.NameReference.mergeAll(input.NameReference)
if err != nil {
return nil, errors.WrapPrefixf(err, "failed to merge NameReference fieldSpec")
return nil, err
}
merged.Images, err = t.Images.MergeAll(input.Images)
if err != nil {
return nil, errors.WrapPrefixf(err, "failed to merge Images fieldSpec")
return nil, err
}
merged.Replicas, err = t.Replicas.MergeAll(input.Replicas)
if err != nil {
return nil, errors.WrapPrefixf(err, "failed to merge Replicas fieldSpec")
return nil, err
}
merged.sortFields()
return merged, nil

View File

@@ -15,23 +15,24 @@ func _() {
_ = x[HashTransformer-4]
_ = x[ImageTagTransformer-5]
_ = x[LabelTransformer-6]
_ = x[NamespaceTransformer-7]
_ = x[PatchJson6902Transformer-8]
_ = x[PatchStrategicMergeTransformer-9]
_ = x[PatchTransformer-10]
_ = x[PrefixSuffixTransformer-11]
_ = x[PrefixTransformer-12]
_ = x[SuffixTransformer-13]
_ = x[ReplicaCountTransformer-14]
_ = x[SecretGenerator-15]
_ = x[ValueAddTransformer-16]
_ = x[HelmChartInflationGenerator-17]
_ = x[ReplacementTransformer-18]
_ = x[LegacyOrderTransformer-7]
_ = x[NamespaceTransformer-8]
_ = x[PatchJson6902Transformer-9]
_ = x[PatchStrategicMergeTransformer-10]
_ = x[PatchTransformer-11]
_ = x[PrefixSuffixTransformer-12]
_ = x[PrefixTransformer-13]
_ = x[SuffixTransformer-14]
_ = x[ReplicaCountTransformer-15]
_ = x[SecretGenerator-16]
_ = x[ValueAddTransformer-17]
_ = x[HelmChartInflationGenerator-18]
_ = x[ReplacementTransformer-19]
}
const _BuiltinPluginType_name = "UnknownAnnotationsTransformerConfigMapGeneratorIAMPolicyGeneratorHashTransformerImageTagTransformerLabelTransformerNamespaceTransformerPatchJson6902TransformerPatchStrategicMergeTransformerPatchTransformerPrefixSuffixTransformerPrefixTransformerSuffixTransformerReplicaCountTransformerSecretGeneratorValueAddTransformerHelmChartInflationGeneratorReplacementTransformer"
const _BuiltinPluginType_name = "UnknownAnnotationsTransformerConfigMapGeneratorIAMPolicyGeneratorHashTransformerImageTagTransformerLabelTransformerLegacyOrderTransformerNamespaceTransformerPatchJson6902TransformerPatchStrategicMergeTransformerPatchTransformerPrefixSuffixTransformerPrefixTransformerSuffixTransformerReplicaCountTransformerSecretGeneratorValueAddTransformerHelmChartInflationGeneratorReplacementTransformer"
var _BuiltinPluginType_index = [...]uint16{0, 7, 29, 47, 65, 80, 99, 115, 135, 159, 189, 205, 228, 245, 262, 285, 300, 319, 346, 368}
var _BuiltinPluginType_index = [...]uint16{0, 7, 29, 47, 65, 80, 99, 115, 137, 157, 181, 211, 227, 250, 267, 284, 307, 322, 341, 368, 390}
func (i BuiltinPluginType) String() string {
if i < 0 || i >= BuiltinPluginType(len(_BuiltinPluginType_index)-1) {

View File

@@ -19,6 +19,7 @@ const (
HashTransformer
ImageTagTransformer
LabelTransformer
LegacyOrderTransformer
NamespaceTransformer
PatchJson6902Transformer
PatchStrategicMergeTransformer
@@ -99,6 +100,7 @@ var TransformerFactories = map[BuiltinPluginType]func() resmap.TransformerPlugin
HashTransformer: builtins.NewHashTransformerPlugin,
ImageTagTransformer: builtins.NewImageTagTransformerPlugin,
LabelTransformer: builtins.NewLabelTransformerPlugin,
LegacyOrderTransformer: builtins.NewLegacyOrderTransformerPlugin,
NamespaceTransformer: builtins.NewNamespaceTransformerPlugin,
PatchJson6902Transformer: builtins.NewPatchJson6902TransformerPlugin,
PatchStrategicMergeTransformer: builtins.NewPatchStrategicMergeTransformerPlugin,
@@ -109,7 +111,4 @@ var TransformerFactories = map[BuiltinPluginType]func() resmap.TransformerPlugin
ReplacementTransformer: builtins.NewReplacementTransformerPlugin,
ReplicaCountTransformer: builtins.NewReplicaCountTransformerPlugin,
ValueAddTransformer: builtins.NewValueAddTransformerPlugin,
// Do not wired SortOrderTransformer as a builtin plugin.
// We only want it to be available in the top-level kustomization.
// See: https://github.com/kubernetes-sigs/kustomize/issues/3913
}

View File

@@ -12,8 +12,8 @@ import (
"path/filepath"
"strings"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/internal/plugins/utils"
"sigs.k8s.io/kustomize/kyaml/errors"
)
// Compiler creates Go plugin object files.
@@ -86,7 +86,7 @@ func (b *Compiler) Compile() error {
cmd.Dir = b.workDir
if err := cmd.Run(); err != nil {
b.report()
return errors.WrapPrefixf(
return errors.Wrapf(
err, "cannot compile %s:\nSTDERR\n%s\n",
b.srcPath(), b.stderr.String())
}

View File

@@ -6,6 +6,7 @@ package execplugin
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"runtime"
@@ -13,9 +14,9 @@ import (
"github.com/google/shlex"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/internal/plugins/utils"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/yaml"
)
@@ -149,19 +150,19 @@ func (p *ExecPlugin) Transform(rm resmap.ResMap) error {
// passes the full temp file path as the first arg to a process
// running the plugin binary. Process output is returned.
func (p *ExecPlugin) invokePlugin(input []byte) ([]byte, error) {
f, err := os.CreateTemp("", tmpConfigFilePrefix)
f, err := ioutil.TempFile("", tmpConfigFilePrefix)
if err != nil {
return nil, errors.WrapPrefixf(
return nil, errors.Wrap(
err, "creating tmp plugin config file")
}
_, err = f.Write(p.cfg)
if err != nil {
return nil, errors.WrapPrefixf(
return nil, errors.Wrap(
err, "writing plugin config to "+f.Name())
}
err = f.Close()
if err != nil {
return nil, errors.WrapPrefixf(
return nil, errors.Wrap(
err, "closing plugin config file "+f.Name())
}
//nolint:gosec
@@ -175,7 +176,7 @@ func (p *ExecPlugin) invokePlugin(input []byte) ([]byte, error) {
}
result, err := cmd.Output()
if err != nil {
return nil, errors.WrapPrefixf(
return nil, errors.Wrapf(
err, "failure in plugin configured via %s; %v",
f.Name(), err.Error())
}

View File

@@ -7,7 +7,7 @@ import (
"bytes"
"fmt"
"sigs.k8s.io/kustomize/kyaml/errors"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/internal/plugins/utils"
"sigs.k8s.io/kustomize/api/resmap"
@@ -51,16 +51,13 @@ func resourceToRNode(res *resource.Resource) (*yaml.RNode, error) {
}
// GetFunctionSpec return function spec is there is. Otherwise return nil
func GetFunctionSpec(res *resource.Resource) (*runtimeutil.FunctionSpec, error) {
func GetFunctionSpec(res *resource.Resource) *runtimeutil.FunctionSpec {
rnode, err := resourceToRNode(res)
if err != nil {
return nil, fmt.Errorf("could not convert resource to RNode: %w", err)
return nil
}
functionSpec, err := runtimeutil.GetFunctionSpec(rnode)
if err != nil {
return nil, fmt.Errorf("failed to get FunctionSpec: %w", err)
}
return functionSpec, nil
return runtimeutil.GetFunctionSpec(rnode)
}
func toStorageMounts(mounts []string) []runtimeutil.StorageMount {
@@ -194,7 +191,7 @@ func (p *FnPlugin) invokePlugin(input []byte) ([]byte, error) {
err = p.runFns.Execute()
if err != nil {
return nil, errors.WrapPrefixf(
return nil, errors.Wrap(
err, "couldn't execute function")
}

View File

@@ -12,6 +12,7 @@ import (
"reflect"
"strings"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
"sigs.k8s.io/kustomize/api/internal/plugins/execplugin"
@@ -21,7 +22,6 @@ import (
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/filesys"
"sigs.k8s.io/kustomize/kyaml/resid"
)
@@ -42,35 +42,27 @@ func NewLoader(
return &Loader{pc: pc, rf: rf, fs: fs}
}
// LoaderWithWorkingDir returns loader after setting its working directory.
// NOTE: This is not really a new loader since some of the Loader struct fields are pointers.
func (l *Loader) LoaderWithWorkingDir(wd string) *Loader {
lpc := &types.PluginConfig{
PluginRestrictions: l.pc.PluginRestrictions,
BpLoadingOptions: l.pc.BpLoadingOptions,
FnpLoadingOptions: l.pc.FnpLoadingOptions,
HelmConfig: l.pc.HelmConfig,
}
lpc.FnpLoadingOptions.WorkingDir = wd
return &Loader{pc: lpc, rf: l.rf, fs: l.fs}
}
// Config provides the global (not plugin specific) PluginConfig data.
func (l *Loader) Config() *types.PluginConfig {
return l.pc
}
// SetWorkDir sets the working directory for this loader's plugins
func (l *Loader) SetWorkDir(wd string) {
l.pc.FnpLoadingOptions.WorkingDir = wd
}
func (l *Loader) LoadGenerators(
ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) (
result []*resmap.GeneratorWithProperties, err error) {
for _, res := range rm.Resources() {
g, err := l.LoadGenerator(ldr, v, res)
if err != nil {
return nil, fmt.Errorf("failed to load generator: %w", err)
return nil, err
}
generatorOrigin, err := resource.OriginFromCustomPlugin(res)
if err != nil {
return nil, fmt.Errorf("failed to get origin from CustomPlugin: %w", err)
return nil, err
}
result = append(result, &resmap.GeneratorWithProperties{Generator: g, Origin: generatorOrigin})
}
@@ -138,19 +130,15 @@ func (l *Loader) AbsolutePluginPath(id resid.ResId) (string, error) {
// absPluginHome is the home of kustomize Exec and Go plugins.
// Kustomize plugin configuration files are k8s-style objects
// containing the fields 'apiVersion' and 'kind', e.g.
//
// apiVersion: apps/v1
// kind: Deployment
//
// apiVersion: apps/v1
// kind: Deployment
// kustomize reads plugin configuration data from a file path
// specified in the 'generators:' or 'transformers:' field of a
// kustomization file. For Exec and Go plugins, kustomize
// uses this data to both locate the plugin and configure it.
// Each Exec or Go plugin (its code, its tests, its supporting data
// files, etc.) must be housed in its own directory at
//
// ${absPluginHome}/${pluginApiVersion}/LOWERCASE(${pluginKind})
//
// ${absPluginHome}/${pluginApiVersion}/LOWERCASE(${pluginKind})
// where
// - ${absPluginHome} is an absolute path, defined below.
// - ${pluginApiVersion} is taken from the plugin config file.
@@ -216,11 +204,11 @@ func (l *Loader) loadAndConfigurePlugin(
}
yaml, err := res.AsYAML()
if err != nil {
return nil, errors.WrapPrefixf(err, "marshalling yaml from res %s", res.OrgId())
return nil, errors.Wrapf(err, "marshalling yaml from res %s", res.OrgId())
}
err = c.Config(resmap.NewPluginHelpers(ldr, v, l.rf, l.pc), yaml)
if err != nil {
return nil, errors.WrapPrefixf(
return nil, errors.Wrapf(
err, "plugin %s fails configuration", res.OrgId())
}
return c, nil
@@ -238,20 +226,17 @@ func (l *Loader) makeBuiltinPlugin(r resid.Gvk) (resmap.Configurable, error) {
}
func (l *Loader) loadPlugin(res *resource.Resource) (resmap.Configurable, error) {
spec, err := fnplugin.GetFunctionSpec(res)
if err != nil {
return nil, fmt.Errorf("loader: %w", err)
}
spec := fnplugin.GetFunctionSpec(res)
if spec != nil {
// validation check that function mounts are under the current kustomization directory
for _, mount := range spec.Container.StorageMounts {
if filepath.IsAbs(mount.Src) {
return nil, errors.Errorf("plugin %s with mount path '%s' is not permitted; "+
"mount paths must be relative to the current kustomization directory", res.OrgId(), mount.Src)
return nil, errors.New(fmt.Sprintf("plugin %s with mount path '%s' is not permitted; "+
"mount paths must be relative to the current kustomization directory", res.OrgId(), mount.Src))
}
if strings.HasPrefix(filepath.Clean(mount.Src), "../") {
return nil, errors.Errorf("plugin %s with mount path '%s' is not permitted; "+
"mount paths must be under the current kustomization directory", res.OrgId(), mount.Src)
return nil, errors.New(fmt.Sprintf("plugin %s with mount path '%s' is not permitted; "+
"mount paths must be under the current kustomization directory", res.OrgId(), mount.Src))
}
}
return fnplugin.NewFnPlugin(&l.pc.FnpLoadingOptions), nil
@@ -307,11 +292,11 @@ func (l *Loader) loadGoPlugin(id resid.ResId, absPath string) (resmap.Configurab
log.Printf("Attempting plugin load from '%s'", absPath)
p, err := plugin.Open(absPath)
if err != nil {
return nil, errors.WrapPrefixf(err, "plugin %s fails to load", absPath)
return nil, errors.Wrapf(err, "plugin %s fails to load", absPath)
}
symbol, err := p.Lookup(konfig.PluginSymbol)
if err != nil {
return nil, errors.WrapPrefixf(
return nil, errors.Wrapf(
err, "plugin %s doesn't have symbol %s",
regId, konfig.PluginSymbol)
}

View File

@@ -6,7 +6,6 @@ package loader_test
import (
"testing"
"github.com/stretchr/testify/require"
. "sigs.k8s.io/kustomize/api/internal/plugins/loader"
"sigs.k8s.io/kustomize/api/loader"
"sigs.k8s.io/kustomize/api/provider"
@@ -79,20 +78,3 @@ func TestLoader(t *testing.T) {
}
}
}
func TestLoaderWithWorkingDir(t *testing.T) {
p := provider.NewDefaultDepProvider()
rmF := resmap.NewFactory(p.GetResourceFactory())
fsys := filesys.MakeFsInMemory()
c := types.EnabledPluginConfig(types.BploLoadFromFileSys)
pLdr := NewLoader(c, rmF, fsys)
npLdr := pLdr.LoaderWithWorkingDir("/tmp/dummy")
require.Equal(t,
"",
pLdr.Config().FnpLoadingOptions.WorkingDir,
"the plugin working dir should not change")
require.Equal(t,
"/tmp/dummy",
npLdr.Config().FnpLoadingOptions.WorkingDir,
"the plugin working dir is not updated")
}

View File

@@ -7,8 +7,8 @@ import (
"fmt"
"strings"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/kyaml/errors"
)
type errMissingKustomization struct {
@@ -23,8 +23,12 @@ func (e *errMissingKustomization) Error() string {
}
func IsMissingKustomizationFileError(err error) bool {
e := &errMissingKustomization{}
return errors.As(err, &e)
_, ok := err.(*errMissingKustomization)
if ok {
return true
}
_, ok = errors.Cause(err).(*errMissingKustomization)
return ok
}
func NewErrMissingKustomization(p string) *errMissingKustomization {

View File

@@ -6,9 +6,10 @@ package target
import (
"encoding/json"
"fmt"
"os"
"strings"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/accumulator"
"sigs.k8s.io/kustomize/api/internal/builtins"
@@ -22,7 +23,6 @@ import (
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/yaml"
)
@@ -44,35 +44,32 @@ func NewKustTarget(
validator ifc.Validator,
rFactory *resmap.Factory,
pLdr *loader.Loader) *KustTarget {
pLdrCopy := *pLdr
pLdrCopy.SetWorkDir(ldr.Root())
return &KustTarget{
ldr: ldr,
validator: validator,
rFactory: rFactory,
pLdr: pLdr.LoaderWithWorkingDir(ldr.Root()),
pLdr: &pLdrCopy,
}
}
// Load attempts to load the target's kustomization file.
func (kt *KustTarget) Load() error {
content, kustFileName, err := LoadKustFile(kt.ldr)
content, kustFileName, err := loadKustFile(kt.ldr)
if err != nil {
return err
}
var k types.Kustomization
if err := k.Unmarshal(content); err != nil {
content, err = types.FixKustomizationPreUnmarshalling(content)
if err != nil {
return err
}
// show warning message when using deprecated fields.
if warningMessages := k.CheckDeprecatedFields(); warningMessages != nil {
for _, msg := range *warningMessages {
fmt.Fprintf(os.Stderr, "%v\n", msg)
}
var k types.Kustomization
err = k.Unmarshal(content)
if err != nil {
return err
}
k.FixKustomization()
k.FixKustomizationPostUnmarshalling()
errs := k.EnforceFields()
if len(errs) > 0 {
return fmt.Errorf(
@@ -92,7 +89,7 @@ func (kt *KustTarget) Kustomization() types.Kustomization {
return result
}
func LoadKustFile(ldr ifc.Loader) ([]byte, string, error) {
func loadKustFile(ldr ifc.Loader) ([]byte, string, error) {
var content []byte
match := 0
var kustFileName string
@@ -153,11 +150,6 @@ func (kt *KustTarget) makeCustomizedResMap() (resmap.ResMap, error) {
return nil, err
}
err = kt.IgnoreLocal(ra)
if err != nil {
return nil, err
}
return ra.ResMap(), nil
}
@@ -195,11 +187,11 @@ func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator) (
resRa *accumulator.ResAccumulator, err error) {
ra, err = kt.accumulateResources(ra, kt.kustomization.Resources)
if err != nil {
return nil, errors.WrapPrefixf(err, "accumulating resources")
return nil, errors.Wrap(err, "accumulating resources")
}
ra, err = kt.accumulateComponents(ra, kt.kustomization.Components)
if err != nil {
return nil, errors.WrapPrefixf(err, "accumulating components")
return nil, errors.Wrap(err, "accumulating components")
}
tConfig, err := builtinconfig.MakeTransformerConfig(
kt.ldr, kt.kustomization.Configurations)
@@ -208,17 +200,17 @@ func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator) (
}
err = ra.MergeConfig(tConfig)
if err != nil {
return nil, errors.WrapPrefixf(
return nil, errors.Wrapf(
err, "merging config %v", tConfig)
}
crdTc, err := accumulator.LoadConfigFromCRDs(kt.ldr, kt.kustomization.Crds)
if err != nil {
return nil, errors.WrapPrefixf(
return nil, errors.Wrapf(
err, "loading CRDs %v", kt.kustomization.Crds)
}
err = ra.MergeConfig(crdTc)
if err != nil {
return nil, errors.WrapPrefixf(
return nil, errors.Wrapf(
err, "merging CRDs %v", crdTc)
}
err = kt.runGenerators(ra)
@@ -235,9 +227,13 @@ func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator) (
}
err = ra.MergeVars(kt.kustomization.Vars)
if err != nil {
return nil, errors.WrapPrefixf(
return nil, errors.Wrapf(
err, "merging vars %v", kt.kustomization.Vars)
}
err = kt.IgnoreLocal(ra)
if err != nil {
return nil, err
}
return ra, nil
}
@@ -265,7 +261,7 @@ func (kt *KustTarget) runGenerators(
gs, err = kt.configureExternalGenerators()
if err != nil {
return errors.WrapPrefixf(err, "loading generator plugins")
return errors.Wrap(err, "loading generator plugins")
}
generators = append(generators, gs...)
for i, g := range generators {
@@ -276,12 +272,12 @@ func (kt *KustTarget) runGenerators(
if resMap != nil {
err = resMap.AddOriginAnnotation(generators[i].Origin)
if err != nil {
return errors.WrapPrefixf(err, "adding origin annotations for generator %v", g)
return errors.Wrapf(err, "adding origin annotations for generator %v", g)
}
}
err = ra.AbsorbAll(resMap)
if err != nil {
return errors.WrapPrefixf(err, "merging from generator %v", g)
return errors.Wrapf(err, "merging from generator %v", g)
}
}
return nil
@@ -308,7 +304,7 @@ func (kt *KustTarget) configureExternalGenerators() (
}
}
if err = ra.AppendAll(rm); err != nil {
return nil, errors.WrapPrefixf(err, "configuring external generator")
return nil, errors.Wrapf(err, "configuring external generator")
}
}
ra, err := kt.accumulateResources(ra, generatorPaths)
@@ -355,7 +351,7 @@ func (kt *KustTarget) configureExternalTransformers(transformers []string) ([]*r
}
if err = ra.AppendAll(rm); err != nil {
return nil, errors.WrapPrefixf(err, "configuring external transformer")
return nil, errors.Wrapf(err, "configuring external transformer")
}
}
ra, err := kt.accumulateResources(ra, transformerPaths)
@@ -419,7 +415,7 @@ func (kt *KustTarget) accumulateResources(
if kusterr.IsMalformedYAMLError(errF) { // Some error occurred while tyring to decode YAML file
return nil, errF
}
return nil, errors.WrapPrefixf(
return nil, errors.Wrapf(
err, "accumulation err='%s'", errF.Error())
}
// store the origin, we'll need it later
@@ -436,7 +432,7 @@ func (kt *KustTarget) accumulateResources(
if kusterr.IsMalformedYAMLError(errF) { // Some error occurred while tyring to decode YAML file
return nil, errF
}
return nil, errors.WrapPrefixf(
return nil, errors.Wrapf(
err, "accumulation err='%s'", errF.Error())
}
}
@@ -478,7 +474,7 @@ func (kt *KustTarget) accumulateDirectory(
subKt := NewKustTarget(ldr, kt.validator, kt.rFactory, kt.pLdr)
err := subKt.Load()
if err != nil {
return nil, errors.WrapPrefixf(
return nil, errors.Wrapf(
err, "couldn't make target for path '%s'", ldr.Root())
}
subKt.kustomization.BuildMetadata = kt.kustomization.BuildMetadata
@@ -513,12 +509,12 @@ func (kt *KustTarget) accumulateDirectory(
subRa, err = subKt.AccumulateTarget()
}
if err != nil {
return nil, errors.WrapPrefixf(
return nil, errors.Wrapf(
err, "recursed accumulation of path '%s'", ldr.Root())
}
err = ra.MergeAccumulator(subRa)
if err != nil {
return nil, errors.WrapPrefixf(
return nil, errors.Wrapf(
err, "recursed merging from path '%s'", ldr.Root())
}
return ra, nil
@@ -528,21 +524,21 @@ func (kt *KustTarget) accumulateFile(
ra *accumulator.ResAccumulator, path string) error {
resources, err := kt.rFactory.FromFile(kt.ldr, path)
if err != nil {
return errors.WrapPrefixf(err, "accumulating resources from '%s'", path)
return errors.Wrapf(err, "accumulating resources from '%s'", path)
}
if kt.origin != nil {
originAnno, err := kt.origin.Append(path).String()
if err != nil {
return errors.WrapPrefixf(err, "cannot add path annotation for '%s'", path)
return errors.Wrapf(err, "cannot add path annotation for '%s'", path)
}
err = resources.AnnotateAll(utils.OriginAnnotationKey, originAnno)
if err != nil || originAnno == "" {
return errors.WrapPrefixf(err, "cannot add path annotation for '%s'", path)
return errors.Wrapf(err, "cannot add path annotation for '%s'", path)
}
}
err = ra.AppendAll(resources)
if err != nil {
return errors.WrapPrefixf(err, "merging resources from '%s'", path)
return errors.Wrapf(err, "merging resources from '%s'", path)
}
return nil
}
@@ -553,7 +549,7 @@ func (kt *KustTarget) configureBuiltinPlugin(
if c != nil {
y, err = yaml.Marshal(c)
if err != nil {
return errors.WrapPrefixf(
return errors.Wrapf(
err, "builtin %s marshal", bpt)
}
}
@@ -562,7 +558,7 @@ func (kt *KustTarget) configureBuiltinPlugin(
kt.ldr, kt.validator, kt.rFactory, kt.pLdr.Config()),
y)
if err != nil {
return errors.WrapPrefixf(
return errors.Wrapf(
err, "trouble configuring builtin %s with config: `\n%s`", bpt, string(y))
}
return nil

View File

@@ -7,12 +7,12 @@ import (
"fmt"
"path/filepath"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -286,11 +286,11 @@ var transformerConfigurators = map[builtinhelpers.BuiltinPluginType]func(
if label.IncludeSelectors {
fss, err = fss.MergeAll(tc.CommonLabels)
} else {
// merge spec/template/metadata fieldSpecs if includeTemplate flag is true
// merge spec/template/metadata fieldSpec if includeTemplate flag is true
if label.IncludeTemplates {
fss, err = fss.MergeAll(tc.TemplateLabels)
fss, err = fss.MergeOne(types.FieldSpec{Path: "spec/template/metadata/labels", CreateIfNotPresent: false})
if err != nil {
return nil, errors.WrapPrefixf(err, "failed to merge template fieldSpec")
return nil, errors.Wrap(err, "failed to merge template fieldSpec")
}
}
// only add to metadata by default

View File

@@ -5,15 +5,12 @@ package target_test
import (
"encoding/base64"
"fmt"
"reflect"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/kustomize/api/ifc"
. "sigs.k8s.io/kustomize/api/internal/target"
"sigs.k8s.io/kustomize/api/loader"
"sigs.k8s.io/kustomize/api/provider"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
@@ -24,44 +21,6 @@ import (
// KustTarget is primarily tested in the krusty package with
// high level tests.
func TestLoadKustFile(t *testing.T) {
for name, test := range map[string]struct {
fileNames []string
kustFileName, errMsg string
}{
"missing": {
fileNames: []string{"kustomization"},
errMsg: `unable to find one of 'kustomization.yaml', 'kustomization.yml' or 'Kustomization' in directory '/'`,
},
"multiple": {
fileNames: []string{"kustomization.yaml", "Kustomization"},
errMsg: `Found multiple kustomization files under: /
`,
},
"valid": {
fileNames: []string{"kustomization.yml", "kust"},
kustFileName: "kustomization.yml",
},
} {
t.Run(name, func(t *testing.T) {
th := kusttest_test.MakeHarness(t)
fSys := th.GetFSys()
for _, file := range test.fileNames {
require.NoError(t, fSys.WriteFile(file, []byte(fmt.Sprintf("namePrefix: test-%s", file))))
}
content, fileName, err := LoadKustFile(loader.NewFileLoaderAtCwd(fSys))
if test.kustFileName != "" {
require.NoError(t, err)
require.Equal(t, fmt.Sprintf("namePrefix: test-%s", test.kustFileName), string(content))
require.Equal(t, test.kustFileName, fileName)
} else {
require.EqualError(t, err, test.errMsg)
}
})
}
}
func TestLoad(t *testing.T) {
th := kusttest_test.MakeHarness(t)
expectedTypeMeta := types.TypeMeta{
@@ -81,7 +40,7 @@ func TestLoad(t *testing.T) {
},
},
"nonsenseLatin": {
errContains: "found a tab character that violates indentation",
errContains: "error converting YAML to JSON",
content: `
Lorem ipsum dolor sit amet, consectetur
adipiscing elit, sed do eiusmod tempor
@@ -139,7 +98,10 @@ commonLabels:
func TestMakeCustomizedResMap(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/whatever", `namePrefix: foo-
th.WriteK("/whatever", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: foo-
nameSuffix: -bar
namespace: ns1
commonLabels:
@@ -296,106 +258,12 @@ metadata:
assert.Equal(t, string(expYaml), string(actYaml))
}
func TestConfigurationsOverrideDefault(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/merge-config", `namePrefix: foo-
nameSuffix: -bar
namespace: ns1
resources:
- deployment.yaml
- config.yaml
- secret.yaml
configurations:
- name-prefix-rules.yaml
- name-suffix-rules.yaml
`)
th.WriteF("/merge-config/name-prefix-rules.yaml", `
namePrefix:
- path: metadata/name
apiVersion: v1
kind: Deployment
- path: metadata/name
apiVersion: v1
kind: Secret
`)
th.WriteF("/merge-config/name-suffix-rules.yaml", `
nameSuffix:
- path: metadata/name
apiVersion: v1
kind: ConfigMap
- path: metadata/name
apiVersion: v1
kind: Deployment
`)
th.WriteF("/merge-config/deployment.yaml", `
apiVersion: apps/v1
metadata:
name: deployment1
kind: Deployment
`)
th.WriteF("/merge-config/config.yaml", `
apiVersion: v1
kind: ConfigMap
metadata:
name: config
`)
th.WriteF("/merge-config/secret.yaml", `
apiVersion: v1
kind: Secret
metadata:
name: secret
`)
pvd := provider.NewDefaultDepProvider()
resFactory := pvd.GetResourceFactory()
resources := []*resource.Resource{
resFactory.FromMapWithName("deployment1", map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "foo-deployment1-bar",
"namespace": "ns1",
},
}), resFactory.FromMapWithName("config", map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "config-bar",
"namespace": "ns1",
},
}), resFactory.FromMapWithName("secret", map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": "foo-secret",
"namespace": "ns1",
},
}),
}
expected := resmap.New()
for _, r := range resources {
err := expected.Append(r)
require.NoError(t, err)
}
expected.RemoveBuildAnnotations()
expYaml, err := expected.AsYaml()
require.NoError(t, err)
kt := makeKustTargetWithRf(t, th.GetFSys(), "/merge-config", pvd)
require.NoError(t, kt.Load())
actual, err := kt.MakeCustomizedResMap()
require.NoError(t, err)
actual.RemoveBuildAnnotations()
actYaml, err := actual.AsYaml()
require.NoError(t, err)
require.Equal(t, string(expYaml), string(actYaml))
}
func TestDuplicateExternalGeneratorsForbidden(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/generator", `generators:
th.WriteK("/generator", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
generators:
- |-
apiVersion: generators.example/v1
kind: ManifestGenerator
@@ -428,7 +296,10 @@ func TestDuplicateExternalGeneratorsForbidden(t *testing.T) {
func TestDuplicateExternalTransformersForbidden(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/transformer", `transformers:
th.WriteK("/transformer", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
transformers:
- |-
apiVersion: transformers.example.co/v1
kind: ValueAnnotator

View File

@@ -7,7 +7,7 @@ import (
"fmt"
"time"
"sigs.k8s.io/kustomize/kyaml/errors"
"github.com/pkg/errors"
)
type errTimeOut struct {
@@ -24,6 +24,13 @@ func (e errTimeOut) Error() string {
}
func IsErrTimeout(err error) bool {
e := &errTimeOut{}
return errors.As(err, &e)
if err == nil {
return false
}
_, ok := err.(errTimeOut)
if ok {
return true
}
_, ok = errors.Cause(err).(errTimeOut)
return ok
}

View File

@@ -35,10 +35,7 @@ func PrevIds(n *yaml.RNode) ([]resid.ResId, error) {
var ids []resid.ResId
// TODO: merge previous names and namespaces into one list of
// pairs on one annotation so there is no chance of error
annotations := n.GetAnnotations(
BuildAnnotationPreviousNames,
BuildAnnotationPreviousNamespaces,
BuildAnnotationPreviousKinds)
annotations := n.GetAnnotations()
if _, ok := annotations[BuildAnnotationPreviousNames]; !ok {
return nil, nil
}
@@ -52,10 +49,12 @@ func PrevIds(n *yaml.RNode) ([]resid.ResId, error) {
"number of previous namespaces, " +
"number of previous kinds not equal")
}
apiVersion := n.GetApiVersion()
group, version := resid.ParseGroupVersion(apiVersion)
ids = make([]resid.ResId, 0, len(names))
for i := range names {
meta, err := n.GetMeta()
if err != nil {
return nil, err
}
group, version := resid.ParseGroupVersion(meta.APIVersion)
gvk := resid.Gvk{
Group: group,
Version: version,

View File

@@ -5,6 +5,9 @@ package builtinpluginconsts
const commonLabelFieldSpecs = `
commonLabels:
- path: metadata/labels
create: true
- path: spec/selector
create: true
version: v1
@@ -14,10 +17,20 @@ commonLabels:
create: true
version: v1
kind: ReplicationController
- path: spec/template/metadata/labels
create: true
version: v1
kind: ReplicationController
- path: spec/selector/matchLabels
create: true
kind: Deployment
- path: spec/template/metadata/labels
create: true
kind: Deployment
- path: spec/template/spec/affinity/podAffinity/preferredDuringSchedulingIgnoredDuringExecution/podAffinityTerm/labelSelector/matchLabels
create: false
group: apps
@@ -47,15 +60,28 @@ commonLabels:
create: true
kind: ReplicaSet
- path: spec/template/metadata/labels
create: true
kind: ReplicaSet
- path: spec/selector/matchLabels
create: true
kind: DaemonSet
- path: spec/template/metadata/labels
create: true
kind: DaemonSet
- path: spec/selector/matchLabels
create: true
group: apps
kind: StatefulSet
- path: spec/template/metadata/labels
create: true
group: apps
kind: StatefulSet
- path: spec/template/spec/affinity/podAffinity/preferredDuringSchedulingIgnoredDuringExecution/podAffinityTerm/labelSelector/matchLabels
create: false
group: apps
@@ -81,16 +107,36 @@ commonLabels:
group: apps
kind: StatefulSet
- path: spec/volumeClaimTemplates[]/metadata/labels
create: true
group: apps
kind: StatefulSet
- path: spec/selector/matchLabels
create: false
group: batch
kind: Job
- path: spec/template/metadata/labels
create: true
group: batch
kind: Job
- path: spec/jobTemplate/spec/selector/matchLabels
create: false
group: batch
kind: CronJob
- path: spec/jobTemplate/metadata/labels
create: true
group: batch
kind: CronJob
- path: spec/jobTemplate/spec/template/metadata/labels
create: true
group: batch
kind: CronJob
- path: spec/selector/matchLabels
create: false
group: policy
@@ -110,4 +156,4 @@ commonLabels:
create: false
group: networking.k8s.io
kind: NetworkPolicy
` + metadataLabelsFieldSpecs
`

View File

@@ -13,7 +13,6 @@ func GetDefaultFieldSpecs() []byte {
[]byte(namePrefixFieldSpecs),
[]byte(nameSuffixFieldSpecs),
[]byte(commonLabelFieldSpecs),
[]byte(templateLabelFieldSpecs),
[]byte(commonAnnotationFieldSpecs),
[]byte(namespaceFieldSpecs),
[]byte(varReferenceFieldSpecs),
@@ -31,7 +30,6 @@ func GetDefaultFieldSpecsAsMap() map[string]string {
result["nameprefix"] = namePrefixFieldSpecs
result["namesuffix"] = nameSuffixFieldSpecs
result["commonlabels"] = commonLabelFieldSpecs
result["templatelabels"] = templateLabelFieldSpecs
result["commonannotations"] = commonAnnotationFieldSpecs
result["namespace"] = namespaceFieldSpecs
result["varreference"] = varReferenceFieldSpecs

View File

@@ -1,51 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package builtinpluginconsts
const metadataLabelsFieldSpecs = `
- path: metadata/labels
create: true
- path: spec/template/metadata/labels
create: true
version: v1
kind: ReplicationController
- path: spec/template/metadata/labels
create: true
kind: Deployment
- path: spec/template/metadata/labels
create: true
kind: ReplicaSet
- path: spec/template/metadata/labels
create: true
kind: DaemonSet
- path: spec/template/metadata/labels
create: true
group: apps
kind: StatefulSet
- path: spec/volumeClaimTemplates[]/metadata/labels
create: true
group: apps
kind: StatefulSet
- path: spec/template/metadata/labels
create: true
group: batch
kind: Job
- path: spec/jobTemplate/metadata/labels
create: true
group: batch
kind: CronJob
- path: spec/jobTemplate/spec/template/metadata/labels
create: true
group: batch
kind: CronJob
`

View File

@@ -1,8 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package builtinpluginconsts
const templateLabelFieldSpecs = `
templateLabels:
` + metadataLabelsFieldSpecs

View File

@@ -85,7 +85,10 @@ spec:
// test for https://github.com/kubernetes-sigs/kustomize/issues/3812#issuecomment-862339267
func TestBasicIO3812(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `resources:
th.WriteK(".", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- service.yaml
`)

View File

@@ -138,10 +138,10 @@ configMapGenerator:
- name: json
literals:
- 'v2=[{"path": "var/druid/segment-cache"}]'
- >-
druid_segmentCache_locations=[{"path":
"var/druid/segment-cache",
"maxSize": 32000000000,
- >-
druid_segmentCache_locations=[{"path":
"var/druid/segment-cache",
"maxSize": 32000000000,
"freeSpacePercent": 1.0}]
secretGenerator:
- name: bob
@@ -201,12 +201,12 @@ metadata:
---
apiVersion: v1
data:
druid_segmentCache_locations: '[{"path": "var/druid/segment-cache", "maxSize": 32000000000,
"freeSpacePercent": 1.0}]'
druid_segmentCache_locations: '[{"path": "var/druid/segment-cache", "maxSize":
32000000000, "freeSpacePercent": 1.0}]'
v2: '[{"path": "var/druid/segment-cache"}]'
kind: ConfigMap
metadata:
name: blah-json-m8529t979f
name: blah-json-5298bc8g99
---
apiVersion: v1
data:
@@ -228,9 +228,7 @@ type: Opaque
`)
}
// TODO: This should be an error instead. However, we can't strict unmarshal until we have a yaml
// lib that support case-insensitive keys and anchors.
// See https://github.com/kubernetes-sigs/kustomize/issues/5061
// TODO: These should be errors instead.
func TestGeneratorRepeatsInKustomization(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
@@ -555,6 +553,8 @@ metadata:
func TestDataEndsWithQuotes(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configMapGenerator:
- name: test
literals:
@@ -571,23 +571,3 @@ metadata:
name: test-k9cc55dfm5
`)
}
func TestDataIsSingleQuote(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
configMapGenerator:
- name: test
literals:
- TEST='
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(
m, `apiVersion: v1
data:
TEST: ''''
kind: ConfigMap
metadata:
name: test-m8t7bmb6g2
`)
}

View File

@@ -11,6 +11,8 @@ import (
func writeBaseWithCrd(th kusttest_test.Harness) {
th.WriteK("base", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
crds:
- mycrd.json
@@ -257,6 +259,8 @@ func TestCrdWithOverlay(t *testing.T) {
th := kusttest_test.MakeHarness(t)
writeBaseWithCrd(th)
th.WriteK("overlay", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: prod-
resources:
- ../base
@@ -303,6 +307,8 @@ spec:
func TestCrdWithContainers(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("crd/containers", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- crd.yaml
images:

View File

@@ -26,6 +26,8 @@ func findSecret(m resmap.ResMap, prefix string) *resource.Resource {
func TestDisableNameSuffixHash(t *testing.T) {
th := kusttest_test.MakeHarness(t)
const kustomizationContent = `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: foo-
nameSuffix: -bar
namespace: ns1
@@ -99,6 +101,8 @@ metadata:
func TestDisableNameSuffixHashPerObject(t *testing.T) {
th := kusttest_test.MakeHarness(t)
const kustomizationContent = `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
generatorOptions:
disableNameSuffixHash: false
secretGenerator:

View File

@@ -41,19 +41,7 @@ spec:
EOF
`
const krmTransformerDotSh = `#!/bin/bash
cat << EOF
apiVersion: v1
kind: Secret
metadata:
name: dummyTransformed
stringData:
foo: bar
type: Opaque
EOF
`
func TestFnExecGeneratorInBase(t *testing.T) {
func TestFnExecGenerator(t *testing.T) {
fSys := filesys.MakeFsOnDisk()
th := kusttest_test.MakeHarnessWithFs(t, fSys)
@@ -99,6 +87,7 @@ spec:
`)
m := th.Run(tmpDir.String(), o)
assert.NoError(t, err)
yml, err := m.AsYaml()
assert.NoError(t, err)
assert.Equal(t, `apiVersion: v1
@@ -137,7 +126,7 @@ spec:
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
}
func TestFnExecGeneratorInBaseWithOverlay(t *testing.T) {
func TestFnExecGeneratorWithOverlay(t *testing.T) {
fSys := filesys.MakeFsOnDisk()
th := kusttest_test.MakeHarnessWithFs(t, fSys)
@@ -228,31 +217,41 @@ spec:
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
}
func TestFnExecGeneratorInOverlay(t *testing.T) {
fSys := filesys.MakeFsOnDisk()
func skipIfNoDocker(t *testing.T) {
t.Helper()
if _, err := exec.LookPath("docker"); err != nil {
t.Skip("skipping because docker binary wasn't found in PATH")
}
}
th := kusttest_test.MakeHarnessWithFs(t, fSys)
o := th.MakeOptionsPluginsEnabled()
o.PluginConfig.FnpLoadingOptions.EnableExec = true
func TestFnContainerGenerator(t *testing.T) {
t.Skip("wait for #3881")
skipIfNoDocker(t)
tmpDir, err := filesys.NewTmpConfirmedDir()
assert.NoError(t, err)
base := filepath.Join(tmpDir.String(), "base")
prod := filepath.Join(tmpDir.String(), "prod")
assert.NoError(t, fSys.Mkdir(base))
assert.NoError(t, fSys.Mkdir(prod))
th.WriteK(base, `
// Function plugins should not need the env setup done by MakeEnhancedHarness
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
resources:
- short_secret.yaml
`)
th.WriteK(prod, `
resources:
- ../base
generators:
- gener.yaml
`)
th.WriteF(filepath.Join(base, "short_secret.yaml"),
`
// Create generator config
th.WriteF("gener.yaml", `
apiVersion: examples.config.kubernetes.io/v1beta1
kind: CockroachDB
metadata:
name: demo
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/kustomize-functions/example-cockroachdb:v0.1.0
spec:
replicas: 3
`)
// Create some additional resource just to make sure everything is added
th.WriteF("short_secret.yaml", `
apiVersion: v1
kind: Secret
metadata:
@@ -265,25 +264,9 @@ stringData:
bootcmd:
- mkdir /mnt/vda
`)
th.WriteF(filepath.Join(prod, "generateDeployment.sh"), generateDeploymentDotSh)
assert.NoError(t, os.Chmod(filepath.Join(prod, "generateDeployment.sh"), 0777))
th.WriteF(filepath.Join(prod, "gener.yaml"), `
kind: executable
metadata:
name: demo
annotations:
config.kubernetes.io/function: |
exec:
path: ./generateDeployment.sh
spec:
`)
m := th.Run(prod, o)
assert.NoError(t, err)
yml, err := m.AsYaml()
assert.NoError(t, err)
assert.Equal(t, `apiVersion: v1
m := th.Run(".", th.MakeOptionsPluginsEnabled())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: Secret
metadata:
labels:
@@ -295,6 +278,235 @@ stringData:
- mkdir /mnt/vda
type: Opaque
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
labels:
app: cockroachdb
name: demo
name: demo-budget
spec:
minAvailable: 67%
selector:
matchLabels:
app: cockroachdb
name: demo
---
apiVersion: v1
kind: Service
metadata:
labels:
app: cockroachdb
name: demo
name: demo-public
spec:
ports:
- name: grpc
port: 26257
targetPort: 26257
- name: http
port: 8080
targetPort: 8080
selector:
app: cockroachdb
name: demo
---
apiVersion: v1
kind: Service
metadata:
annotations:
prometheus.io/path: _status/vars
prometheus.io/port: "8080"
prometheus.io/scrape: "true"
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
labels:
app: cockroachdb
name: demo
name: demo
spec:
clusterIP: None
ports:
- name: grpc
port: 26257
targetPort: 26257
- name: http
port: 8080
targetPort: 8080
selector:
app: cockroachdb
name: demo
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app: cockroachdb
name: demo
name: demo
spec:
replicas: 3
selector:
matchLabels:
app: cockroachdb
name: demo
serviceName: demo
template:
metadata:
labels:
app: cockroachdb
name: demo
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- cockroachdb
topologyKey: kubernetes.io/hostname
weight: 100
containers:
- command:
- /bin/bash
- -ecx
- |
# The use of qualified `+"`hostname -f`"+` is crucial:
# Other nodes aren't able to look up the unqualified hostname.
CRARGS=("start" "--logtostderr" "--insecure" "--host" "$(hostname -f)" "--http-host" "0.0.0.0")
# We only want to initialize a new cluster (by omitting the join flag)
# if we're sure that we're the first node (i.e. index 0) and that
# there aren't any other nodes running as part of the cluster that
# this is supposed to be a part of (which indicates that a cluster
# already exists and we should make sure not to create a new one).
# It's fine to run without --join on a restart if there aren't any
# other nodes.
if [ ! "$(hostname)" == "cockroachdb-0" ] || [ -e "/cockroach/cockroach-data/cluster_exists_marker" ]
then
# We don't join cockroachdb in order to avoid a node attempting
# to join itself, which currently doesn't work
# (https://github.com/cockroachdb/cockroach/issues/9625).
CRARGS+=("--join" "cockroachdb-public")
fi
exec /cockroach/cockroach ${CRARGS[*]}
image: cockroachdb/cockroach:v1.1.0
imagePullPolicy: IfNotPresent
name: demo
ports:
- containerPort: 26257
name: grpc
- containerPort: 8080
name: http
volumeMounts:
- mountPath: /cockroach/cockroach-data
name: datadir
initContainers:
- args:
- -on-start=/on-start.sh
- -service=cockroachdb
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: cockroachdb/cockroach-k8s-init:0.1
imagePullPolicy: IfNotPresent
name: bootstrap
volumeMounts:
- mountPath: /cockroach/cockroach-data
name: datadir
terminationGracePeriodSeconds: 60
volumes:
- name: datadir
persistentVolumeClaim:
claimName: datadir
volumeClaimTemplates:
- metadata:
name: datadir
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
`)
}
func TestFnContainerTransformer(t *testing.T) {
t.Skip("wait for #3881")
skipIfNoDocker(t)
// Function plugins should not need the env setup done by MakeEnhancedHarness
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
resources:
- data.yaml
transformers:
- transf1.yaml
- transf2.yaml
`)
th.WriteF("data.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
annotations:
tshirt-size: small # this injects the resource reservations
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
`)
// This transformer should add resource reservations based on annotation in data.yaml
// See https://github.com/kubernetes-sigs/kustomize/tree/master/functions/examples/injection-tshirt-sizes
th.WriteF("transf1.yaml", `
apiVersion: examples.config.kubernetes.io/v1beta1
kind: Validator
metadata:
name: valid
annotations:
config.kubernetes.io/function: |-
container:
image: gcr.io/kustomize-functions/example-tshirt:v0.2.0
`)
// This transformer will check resources without and won't do any changes
// See https://github.com/kubernetes-sigs/kustomize/tree/master/functions/examples/validator-kubeval
th.WriteF("transf2.yaml", `
apiVersion: examples.config.kubernetes.io/v1beta1
kind: Kubeval
metadata:
name: validate
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/kustomize-functions/example-validator-kubeval:v0.1.0
spec:
strict: true
ignoreMissingSchemas: true
# TODO: Update this to use network/volumes features.
# Relevant issues:
# - https://github.com/kubernetes-sigs/kustomize/issues/1901
# - https://github.com/kubernetes-sigs/kustomize/issues/1902
kubernetesVersion: "1.16.0"
schemaLocation: "file:///schemas"
`)
m := th.Run(".", th.MakeOptionsPluginsEnabled())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
@@ -315,295 +527,11 @@ spec:
containers:
- image: nginx
name: nginx
`, string(yml))
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
}
func TestFnExecTransformerInBase(t *testing.T) {
fSys := filesys.MakeFsOnDisk()
th := kusttest_test.MakeHarnessWithFs(t, fSys)
o := th.MakeOptionsPluginsEnabled()
o.PluginConfig.FnpLoadingOptions.EnableExec = true
tmpDir, err := filesys.NewTmpConfirmedDir()
assert.NoError(t, err)
base := filepath.Join(tmpDir.String(), "base")
assert.NoError(t, fSys.Mkdir(base))
th.WriteK(base, `
resources:
- secret.yaml
transformers:
- krm-transformer.yaml
resources:
requests:
cpu: 200m
memory: 50M
`)
th.WriteF(filepath.Join(base, "secret.yaml"),
`
apiVersion: v1
kind: Secret
metadata:
name: dummy
type: Opaque
stringData:
foo: bar
`)
th.WriteF(filepath.Join(base, "krmTransformer.sh"), krmTransformerDotSh)
assert.NoError(t, os.Chmod(filepath.Join(base, "krmTransformer.sh"), 0777))
th.WriteF(filepath.Join(base, "krm-transformer.yaml"), `
apiVersion: examples.config.kubernetes.io/v1beta1
kind: MyPlugin
metadata:
name: notImportantHere
annotations:
config.kubernetes.io/function: |
exec:
path: ./krmTransformer.sh
`)
m := th.Run(base, o)
yml, err := m.AsYaml()
assert.NoError(t, err)
assert.Equal(t, `apiVersion: v1
kind: Secret
metadata:
name: dummyTransformed
stringData:
foo: bar
type: Opaque
`, string(yml))
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
}
func TestFnExecTransformerInBaseWithOverlay(t *testing.T) {
fSys := filesys.MakeFsOnDisk()
th := kusttest_test.MakeHarnessWithFs(t, fSys)
o := th.MakeOptionsPluginsEnabled()
o.PluginConfig.FnpLoadingOptions.EnableExec = true
tmpDir, err := filesys.NewTmpConfirmedDir()
assert.NoError(t, err)
base := filepath.Join(tmpDir.String(), "base")
prod := filepath.Join(tmpDir.String(), "prod")
assert.NoError(t, fSys.Mkdir(base))
assert.NoError(t, fSys.Mkdir(prod))
th.WriteK(base, `
resources:
- secret.yaml
transformers:
- krm-transformer.yaml
`)
th.WriteK(prod, `
resources:
- ../base
`)
th.WriteF(filepath.Join(base, "secret.yaml"),
`
apiVersion: v1
kind: Secret
metadata:
name: dummy
type: Opaque
stringData:
foo: bar
`)
th.WriteF(filepath.Join(base, "krmTransformer.sh"), krmTransformerDotSh)
assert.NoError(t, os.Chmod(filepath.Join(base, "krmTransformer.sh"), 0777))
th.WriteF(filepath.Join(base, "krm-transformer.yaml"), `
apiVersion: examples.config.kubernetes.io/v1beta1
kind: MyPlugin
metadata:
name: notImportantHere
annotations:
config.kubernetes.io/function: |
exec:
path: ./krmTransformer.sh
`)
m := th.Run(prod, o)
yml, err := m.AsYaml()
assert.NoError(t, err)
assert.Equal(t, `apiVersion: v1
kind: Secret
metadata:
name: dummyTransformed
stringData:
foo: bar
type: Opaque
`, string(yml))
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
}
func TestFnExecTransformerInOverlay(t *testing.T) {
fSys := filesys.MakeFsOnDisk()
th := kusttest_test.MakeHarnessWithFs(t, fSys)
o := th.MakeOptionsPluginsEnabled()
o.PluginConfig.FnpLoadingOptions.EnableExec = true
tmpDir, err := filesys.NewTmpConfirmedDir()
assert.NoError(t, err)
base := filepath.Join(tmpDir.String(), "base")
prod := filepath.Join(tmpDir.String(), "prod")
assert.NoError(t, fSys.Mkdir(base))
assert.NoError(t, fSys.Mkdir(prod))
th.WriteK(base, `
resources:
- secret.yaml
`)
th.WriteK(prod, `
resources:
- ../base
transformers:
- krm-transformer.yaml
`)
th.WriteF(filepath.Join(base, "secret.yaml"),
`
apiVersion: v1
kind: Secret
metadata:
name: dummy
type: Opaque
stringData:
foo: bar
`)
th.WriteF(filepath.Join(prod, "krmTransformer.sh"), krmTransformerDotSh)
assert.NoError(t, os.Chmod(filepath.Join(prod, "krmTransformer.sh"), 0777))
th.WriteF(filepath.Join(prod, "krm-transformer.yaml"), `
apiVersion: examples.config.kubernetes.io/v1beta1
kind: MyPlugin
metadata:
name: notImportantHere
annotations:
config.kubernetes.io/function: |
exec:
path: ./krmTransformer.sh
`)
m := th.Run(prod, o)
yml, err := m.AsYaml()
assert.NoError(t, err)
assert.Equal(t, `apiVersion: v1
kind: Secret
metadata:
name: dummyTransformed
stringData:
foo: bar
type: Opaque
`, string(yml))
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
}
func skipIfNoDocker(t *testing.T) {
t.Helper()
if _, err := exec.LookPath("docker"); err != nil {
t.Skip("skipping because docker binary wasn't found in PATH")
}
}
func TestFnContainerGenerator(t *testing.T) {
skipIfNoDocker(t)
th := kusttest_test.MakeHarness(t)
o := th.MakeOptionsPluginsEnabled()
tmpDir, err := filesys.NewTmpConfirmedDir()
assert.NoError(t, err)
th.WriteK(tmpDir.String(), `
resources:
- deployment.yaml
generators:
- project-service-set.yaml
`)
// Create generator config
th.WriteF(filepath.Join(tmpDir.String(), "project-service-set.yaml"), `
apiVersion: blueprints.cloud.google.com/v1alpha1
kind: ProjectServiceSet
metadata:
name: demo
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/kpt-fn/enable-gcp-services:v0.1.0
spec:
services:
- compute.googleapis.com
projectID: foo
`)
// Create another resource just to make sure everything is added
th.WriteF(filepath.Join(tmpDir.String(), "deployment.yaml"), `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`)
m := th.Run(tmpDir.String(), o)
actual, err := m.AsYaml()
assert.NoError(t, err)
assert.Equal(t, `apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
---
apiVersion: serviceusage.cnrm.cloud.google.com/v1beta1
kind: Service
metadata:
annotations:
blueprints.cloud.google.com/ownerReference: blueprints.cloud.google.com/ProjectServiceSet/demo
config.kubernetes.io/function: |
container:
image: gcr.io/kpt-fn/enable-gcp-services:v0.1.0
name: demo-compute
spec:
projectRef:
external: foo
resourceID: compute.googleapis.com
`, string(actual))
}
func TestFnContainerTransformer(t *testing.T) {
skipIfNoDocker(t)
th := kusttest_test.MakeHarness(t)
o := th.MakeOptionsPluginsEnabled()
tmpDir, err := filesys.NewTmpConfirmedDir()
assert.NoError(t, err)
th.WriteK(tmpDir.String(), `
resources:
- deployment.yaml
transformers:
- e2econtainerconfig.yaml
`)
th.WriteF(filepath.Join(tmpDir.String(), "deployment.yaml"), `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`)
th.WriteF(filepath.Join(tmpDir.String(), "e2econtainerconfig.yaml"), `
apiVersion: example.com/v1alpha1
kind: Input
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
container:
image: "gcr.io/kustomize-functions/e2econtainerconfig"
`)
build := exec.Command("docker", "build", ".", "-t", "gcr.io/kustomize-functions/e2econtainerconfig")
build.Dir = "../../cmd/config/internal/commands/e2e/e2econtainerconfig"
assert.NoError(t, build.Run())
m := th.Run(tmpDir.String(), o)
actual, err := m.AsYaml()
assert.NoError(t, err)
assert.Equal(t, `apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
a-bool-value: "false"
a-int-value: "0"
a-string-value: ""
name: foo
`, string(actual))
}
func TestFnContainerTransformerWithConfig(t *testing.T) {
@@ -809,7 +737,7 @@ generators:
fSys,
tmpDir.String())
assert.Error(t, err)
assert.Contains(t, err.Error(), "loading generator plugins: failed to load generator: plugin RenderHelmChart."+
assert.Contains(t, err.Error(), "loading generator plugins: plugin RenderHelmChart."+
"v1alpha1.[noGrp]/demo.[noNs] with mount path '/tmp/dir' is not permitted; mount paths must"+
" be relative to the current kustomization directory")
}
@@ -842,7 +770,7 @@ generators:
fSys,
tmpDir.String())
assert.Error(t, err)
assert.Contains(t, err.Error(), "loading generator plugins: failed to load generator: plugin RenderHelmChart."+
assert.Contains(t, err.Error(), "loading generator plugins: plugin RenderHelmChart."+
"v1alpha1.[noGrp]/demo.[noNs] with mount path './tmp/../../dir' is not permitted; mount paths must "+
"be under the current kustomization directory")
}

View File

@@ -13,6 +13,8 @@ import (
func TestSimpleBase(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("app/base", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: team-foo-
commonLabels:
app: mynginx
@@ -453,190 +455,6 @@ metadata:
`)
}
func TestMergeAndReplaceDisableNameSuffixHashGenerators(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("app", `
namePrefix: team-foo-
commonLabels:
app: mynginx
org: example.com
team: foo
commonAnnotations:
note: This is a test annotation
resources:
- deployment.yaml
configMapGenerator:
- name: configmap-in-base
literals:
- foo=bar
secretGenerator:
- name: secret-in-base
literals:
- username=admin
- password=somepw
`)
th.WriteF("app/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: nginx-persistent-storage
mountPath: /tmp/ps
volumes:
- name: nginx-persistent-storage
emptyDir: {}
- configMap:
name: configmap-in-base
name: configmap-in-base
`)
th.WriteF("overlay/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
volumes:
- name: nginx-persistent-storage
emptyDir: null
gcePersistentDisk:
pdName: nginx-persistent-storage
- configMap:
name: configmap-in-overlay
name: configmap-in-overlay
`)
th.WriteK("overlay", `
namePrefix: staging-
commonLabels:
env: staging
team: override-foo
patchesStrategicMerge:
- deployment.yaml
resources:
- ../app
configMapGenerator:
- name: configmap-in-overlay
literals:
- hello=world
options:
disableNameSuffixHash: true
- name: configmap-in-base
behavior: replace
literals:
- foo=override-bar
options:
disableNameSuffixHash: true
secretGenerator:
- name: secret-in-base
behavior: merge
literals:
- proxy=haproxy
options:
disableNameSuffixHash: true
`)
m := th.Run("overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
env: staging
org: example.com
team: override-foo
name: staging-team-foo-nginx
spec:
selector:
matchLabels:
app: mynginx
env: staging
org: example.com
team: override-foo
template:
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
env: staging
org: example.com
team: override-foo
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- mountPath: /tmp/ps
name: nginx-persistent-storage
volumes:
- gcePersistentDisk:
pdName: nginx-persistent-storage
name: nginx-persistent-storage
- configMap:
name: staging-configmap-in-overlay
name: configmap-in-overlay
- configMap:
name: staging-team-foo-configmap-in-base
name: configmap-in-base
---
apiVersion: v1
data:
foo: override-bar
kind: ConfigMap
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
env: staging
org: example.com
team: override-foo
name: staging-team-foo-configmap-in-base
---
apiVersion: v1
data:
password: c29tZXB3
proxy: aGFwcm94eQ==
username: YWRtaW4=
kind: Secret
metadata:
annotations:
note: This is a test annotation
labels:
app: mynginx
env: staging
org: example.com
team: override-foo
name: staging-team-foo-secret-in-base
type: Opaque
---
apiVersion: v1
data:
hello: world
kind: ConfigMap
metadata:
labels:
env: staging
team: override-foo
name: staging-configmap-in-overlay
`)
}
func TestGeneratingIntoNamespaces(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("app", `

View File

@@ -48,6 +48,8 @@ type: Opaque
func TestGeneratorOptionsWithBases(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("base", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
generatorOptions:
disableNameSuffixHash: true
labels:
@@ -58,6 +60,8 @@ configMapGenerator:
- foo=bar
`)
th.WriteK("overlay", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../base
generatorOptions:
@@ -90,43 +94,3 @@ metadata:
name: shouldHaveHash-c9867f8446
`)
}
func TestGeneratorOptionsOverlayDisableNameSuffixHash(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("base", `
generatorOptions:
disableNameSuffixHash: false
labels:
foo: bar
configMapGenerator:
- name: baseShouldHaveHashButOverlayShouldNot
literals:
- foo=bar
`)
th.WriteK("overlay", `
resources:
- ../base
generatorOptions:
disableNameSuffixHash: true
labels:
fruit: apple
configMapGenerator:
- name: baseShouldHaveHashButOverlayShouldNot
behavior: merge
literals:
- fruit=apple
`)
m := th.Run("overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
data:
foo: bar
fruit: apple
kind: ConfigMap
metadata:
labels:
foo: bar
fruit: apple
name: baseShouldHaveHashButOverlayShouldNot
`)
}

View File

@@ -86,7 +86,7 @@ patches:
- path: patch.yaml
target:
kind: Deployment
options:
options:
allowNameChange: true
`)
options := th.MakeDefaultOptions()
@@ -721,6 +721,8 @@ spec:
name: myvol
`)
th.WriteK(".", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: foo-
resources:
- resources.yaml
@@ -796,6 +798,8 @@ spec:
name: myvol
`)
th.WriteK(".", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- statefulset.yaml
patches:

View File

@@ -4,12 +4,9 @@
package krusty_test
import (
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
"sigs.k8s.io/kustomize/kyaml/copyutil"
)
const expectedHelm = `
@@ -236,193 +233,3 @@ spec:
type: ClusterIP
`)
}
func TestHelmChartInflationGeneratorMultipleValuesFiles(t *testing.T) {
th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t)
defer th.Reset()
if err := th.ErrIfNoHelm(); err != nil {
t.Skip("skipping: " + err.Error())
}
copyValuesFilesTestChartsIntoHarness(t, th)
th.WriteK(th.GetRoot(), `
helmCharts:
- name: test-chart
releaseName: test-chart
additionalValuesFiles:
- charts/valuesFiles/file1.yaml
- charts/valuesFiles/file2.yaml
`)
m := th.Run(th.GetRoot(), th.MakeOptionsPluginsEnabled())
asYaml, err := m.AsYaml()
require.NoError(t, err)
require.Equal(t, string(asYaml), `apiVersion: apps/v1
kind: Deployment
metadata:
labels:
chart: test-1.0.0
name: my-deploy
namespace: file-2
spec:
replicas: 1
selector:
matchLabels:
app: test
template:
spec:
containers:
- image: test-image-file1:file1
imagePullPolicy: Never
---
apiVersion: apps/v1
kind: Pod
metadata:
annotations:
helm.sh/hook: test
name: test-chart
`)
}
func TestHelmChartInflationGeneratorApiVersions(t *testing.T) {
th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t)
defer th.Reset()
if err := th.ErrIfNoHelm(); err != nil {
t.Skip("skipping: " + err.Error())
}
copyValuesFilesTestChartsIntoHarness(t, th)
th.WriteK(th.GetRoot(), `
helmCharts:
- name: test-chart
releaseName: test-chart
apiVersions:
- foo/v1
`)
m := th.Run(th.GetRoot(), th.MakeOptionsPluginsEnabled())
asYaml, err := m.AsYaml()
require.NoError(t, err)
require.Equal(t, string(asYaml), `apiVersion: foo/v1
kind: Deployment
metadata:
labels:
chart: test-1.0.0
name: my-deploy
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: test
template:
spec:
containers:
- image: test-image:v1.0.0
imagePullPolicy: Always
---
apiVersion: foo/v1
kind: Pod
metadata:
annotations:
helm.sh/hook: test
name: test-chart
`)
}
func TestHelmChartInflationGeneratorSkipTests(t *testing.T) {
th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t)
defer th.Reset()
if err := th.ErrIfNoHelm(); err != nil {
t.Skip("skipping: " + err.Error())
}
copyValuesFilesTestChartsIntoHarness(t, th)
th.WriteK(th.GetRoot(), `
helmCharts:
- name: test-chart
releaseName: test-chart
skipTests: true
`)
m := th.Run(th.GetRoot(), th.MakeOptionsPluginsEnabled())
asYaml, err := m.AsYaml()
require.NoError(t, err)
require.Equal(t, string(asYaml), `apiVersion: apps/v1
kind: Deployment
metadata:
labels:
chart: test-1.0.0
name: my-deploy
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: test
template:
spec:
containers:
- image: test-image:v1.0.0
imagePullPolicy: Always
`)
}
func TestHelmChartInflationGeneratorNameTemplate(t *testing.T) {
th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t)
defer th.Reset()
if err := th.ErrIfNoHelm(); err != nil {
t.Skip("skipping: " + err.Error())
}
copyValuesFilesTestChartsIntoHarness(t, th)
th.WriteK(th.GetRoot(), `
helmCharts:
- name: test-chart
nameTemplate: name-template
`)
m := th.Run(th.GetRoot(), th.MakeOptionsPluginsEnabled())
asYaml, err := m.AsYaml()
require.NoError(t, err)
require.Equal(t, string(asYaml), `apiVersion: apps/v1
kind: Deployment
metadata:
labels:
chart: test-1.0.0
name: my-deploy
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: test
template:
spec:
containers:
- image: test-image:v1.0.0
imagePullPolicy: Always
---
apiVersion: apps/v1
kind: Pod
metadata:
annotations:
helm.sh/hook: test
name: name-template
`)
}
func copyValuesFilesTestChartsIntoHarness(t *testing.T, th *kusttest_test.HarnessEnhanced) {
t.Helper()
thDir := filepath.Join(th.GetRoot(), "charts")
chartDir := "testdata/helmcharts"
fs := th.GetFSys()
require.NoError(t, fs.MkdirAll(filepath.Join(thDir, "templates")))
require.NoError(t, copyutil.CopyDir(th.GetFSys(), chartDir, thDir))
}

View File

@@ -92,7 +92,7 @@ spec:
`)
}
func TestKustomizationLabelsInDeploymentTemplate(t *testing.T) {
func TestKustomizationLabelsInTemplate(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("app/deployment.yaml", `
apiVersion: apps/v1
@@ -160,368 +160,3 @@ spec:
foo: bar
`)
}
func TestKustomizationLabelsInTemplateWhenLabelsIsNil(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("app/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment
spec:
replicas: 1
template:
spec:
containers:
- name: test-server
image: test-server
`)
th.WriteK("/app", `
resources:
- deployment.yaml
commonLabels:
app: test-server
labels:
- pairs:
app.kubernetes.io/component: a
app.kubernetes.io/instance: b
app.kubernetes.io/name: c
app.kubernetes.io/part-of: d
includeSelectors: false
includeTemplates: true
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: test-server
app.kubernetes.io/component: a
app.kubernetes.io/instance: b
app.kubernetes.io/name: c
app.kubernetes.io/part-of: d
name: deployment
spec:
replicas: 1
selector:
matchLabels:
app: test-server
template:
metadata:
labels:
app: test-server
app.kubernetes.io/component: a
app.kubernetes.io/instance: b
app.kubernetes.io/name: c
app.kubernetes.io/part-of: d
spec:
containers:
- image: test-server
name: test-server
`)
}
func TestKustomizationLabelsDoesNotCreateInvalidTemplatePaths(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("app/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment
spec:
replicas: 1
template:
spec:
containers:
- name: test-server
image: test-server
`)
th.WriteF("app/service.yaml", `
apiVersion: v1
kind: Service
metadata:
name: service
spec:
ports:
- port: 80
protocol: TCP
targetPort: 9376
`)
th.WriteK("/app", `
resources:
- deployment.yaml
- service.yaml
commonLabels:
app: test-server
labels:
- pairs:
app.kubernetes.io/component: a
app.kubernetes.io/instance: b
app.kubernetes.io/name: c
app.kubernetes.io/part-of: d
includeSelectors: false
includeTemplates: true
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: test-server
app.kubernetes.io/component: a
app.kubernetes.io/instance: b
app.kubernetes.io/name: c
app.kubernetes.io/part-of: d
name: deployment
spec:
replicas: 1
selector:
matchLabels:
app: test-server
template:
metadata:
labels:
app: test-server
app.kubernetes.io/component: a
app.kubernetes.io/instance: b
app.kubernetes.io/name: c
app.kubernetes.io/part-of: d
spec:
containers:
- image: test-server
name: test-server
---
apiVersion: v1
kind: Service
metadata:
labels:
app: test-server
app.kubernetes.io/component: a
app.kubernetes.io/instance: b
app.kubernetes.io/name: c
app.kubernetes.io/part-of: d
name: service
spec:
ports:
- port: 80
protocol: TCP
targetPort: 9376
selector:
app: test-server
`)
}
func TestKustomizationLabelsInDaemonSetTemplate(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("app/ds.yaml", `
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app.kubernetes.io/name: daemon
name: daemon
spec:
selector:
matchLabels:
app.kubernetes.io/name: daemon
template:
metadata:
labels:
app.kubernetes.io/name: daemon
`)
th.WriteK("/app", `
resources:
- ds.yaml
labels:
- pairs:
foo: bar
includeSelectors: false
includeTemplates: true
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
app.kubernetes.io/name: daemon
foo: bar
name: daemon
spec:
selector:
matchLabels:
app.kubernetes.io/name: daemon
template:
metadata:
labels:
app.kubernetes.io/name: daemon
foo: bar
`)
}
func TestKustomizationLabelsInStatefulSetTemplate(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("app/sts.yaml", `
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app.kubernetes.io/name: set
name: set
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: set
serviceName: set
template:
metadata:
labels:
app.kubernetes.io/name: set
`)
th.WriteK("/app", `
resources:
- sts.yaml
labels:
- pairs:
foo: bar
includeSelectors: false
includeTemplates: true
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app.kubernetes.io/name: set
foo: bar
name: set
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: set
serviceName: set
template:
metadata:
labels:
app.kubernetes.io/name: set
foo: bar
`)
}
func TestKustomizationLabelsInCronJobTemplate(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("app/cjob.yaml", `
apiVersion: batch/v1
kind: CronJob
metadata:
labels:
app.kubernetes.io/name: job
name: job
spec:
jobTemplate:
spec:
backoffLimit: 4
template:
metadata:
labels:
app.kubernetes.io/name: job
spec:
restartPolicy: Never
schedule: '* * * * *'
`)
th.WriteK("/app", `
resources:
- cjob.yaml
labels:
- pairs:
foo: bar
includeSelectors: false
includeTemplates: true
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: batch/v1
kind: CronJob
metadata:
labels:
app.kubernetes.io/name: job
foo: bar
name: job
spec:
jobTemplate:
metadata:
labels:
foo: bar
spec:
backoffLimit: 4
template:
metadata:
labels:
app.kubernetes.io/name: job
foo: bar
spec:
restartPolicy: Never
schedule: '* * * * *'
`)
}
func TestKustomizationLabelsInJobTemplate(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("app/job.yaml", `
apiVersion: batch/v1
kind: Job
metadata:
labels:
app.kubernetes.io/name: job
name: job
spec:
backoffLimit: 4
template:
metadata:
labels:
app.kubernetes.io/name: job
spec:
restartPolicy: Never
`)
th.WriteK("/app", `
resources:
- job.yaml
labels:
- pairs:
foo: bar
includeSelectors: false
includeTemplates: true
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: batch/v1
kind: Job
metadata:
labels:
app.kubernetes.io/name: job
foo: bar
name: job
spec:
backoffLimit: 4
template:
metadata:
labels:
app.kubernetes.io/name: job
foo: bar
spec:
restartPolicy: Never
`)
}

View File

@@ -234,6 +234,8 @@ spec:
func TestPathWithCronJobV1(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- resources.yaml
patches:

View File

@@ -28,6 +28,8 @@ spec:
imagePullSecrets: []`)
th.WriteK("/app", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
metadata:
annotations:
config.kubernetes.io/local-config: "true"
@@ -35,7 +37,7 @@ metadata:
foo: bar
name: test_kustomization
resources:
- resources.yaml
- resources.yaml
`)
m := th.Run("/app", th.MakeDefaultOptions())

View File

@@ -5,7 +5,6 @@ package krusty
import (
"fmt"
"log"
"sigs.k8s.io/kustomize/api/internal/builtins"
pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader"
@@ -17,7 +16,6 @@ import (
"sigs.k8s.io/kustomize/api/provider"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/filesys"
"sigs.k8s.io/kustomize/kyaml/openapi"
)
@@ -91,9 +89,11 @@ func (b *Kustomizer) Run(
if err != nil {
return nil, err
}
err = b.applySortOrder(m, kt)
if err != nil {
return nil, err
if b.options.DoLegacyResourceSort {
err = builtins.NewLegacyOrderTransformerPlugin().Transform(m)
if err != nil {
return nil, err
}
}
if b.options.AddManagedbyLabel || utils.StringSliceContains(kt.Kustomization().BuildMetadata, types.ManagedByLabelOption) {
t := builtins.LabelTransformerPlugin{
@@ -112,52 +112,10 @@ func (b *Kustomizer) Run(
}
m.RemoveBuildAnnotations()
if !utils.StringSliceContains(kt.Kustomization().BuildMetadata, types.OriginAnnotations) {
err = m.RemoveOriginAnnotations()
if err != nil {
return nil, errors.WrapPrefixf(err, "failed to clean up origin tracking annotations")
}
m.RemoveOriginAnnotations()
}
if !utils.StringSliceContains(kt.Kustomization().BuildMetadata, types.TransformerAnnotations) {
err = m.RemoveTransformerAnnotations()
if err != nil {
return nil, errors.WrapPrefixf(err, "failed to clean up transformer annotations")
}
m.RemoveTransformerAnnotations()
}
return m, nil
}
func (b *Kustomizer) applySortOrder(m resmap.ResMap, kt *target.KustTarget) error {
// Sort order can be defined in two places:
// - (new) kustomization file
// - (old) CLI flag
//
// We want the kustomization file to take precedence over the CLI flag.
// Eventually, we may want to move away from having a CLI flag altogether:
// https://github.com/kubernetes-sigs/kustomize/issues/3947
// Case 1: Sort order set in kustomization file.
if kt.Kustomization().SortOptions != nil {
// If set in CLI flag too, warn the user.
if b.options.Reorder != ReorderOptionUnspecified {
log.Println("Warning: Sorting order is set both in 'kustomization.yaml'" +
" ('sortOptions') and in a CLI flag ('--reorder'). Using the" +
" kustomization file over the CLI flag.")
}
pl := &builtins.SortOrderTransformerPlugin{
SortOptions: kt.Kustomization().SortOptions,
}
err := pl.Transform(m)
if err != nil {
return errors.Wrap(err)
}
} else if b.options.Reorder == ReorderOptionLegacy || b.options.Reorder == ReorderOptionUnspecified {
// Case 2: Sort order set in CLI flag only or not at all.
pl := &builtins.SortOrderTransformerPlugin{
SortOptions: &types.SortOptions{
Order: types.LegacySortOrder,
},
}
return errors.Wrap(pl.Transform(m))
}
return nil
}

View File

@@ -6,7 +6,6 @@ package krusty_test
import (
"testing"
"sigs.k8s.io/kustomize/api/krusty"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
@@ -39,7 +38,7 @@ data:
key: value
`)
opts := th.MakeDefaultOptions()
opts.Reorder = krusty.ReorderOptionLegacy
opts.DoLegacyResourceSort = true
m := th.Run(".", opts)
th.AssertActualEqualsExpected(m, `
apiVersion: v1

View File

@@ -14,7 +14,9 @@ import (
// See https://github.com/kubernetes-sigs/kustomize/issues/4124 for details.
func TestSKipLocalConfigAfterTransform(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `resources:
th.WriteK(".", `apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- pod.yaml
- deployment.yaml
transformers:

View File

@@ -1,17 +0,0 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package localizer
import (
"sigs.k8s.io/kustomize/api/internal/localizer"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
// Run executes `kustomize localize` on fSys given the `localize` arguments and
// returns the path to the created newDir.
func Run(fSys filesys.FileSystem, target, scope, newDir string) (string, error) {
dst, err := localizer.Run(target, scope, newDir, fSys)
return dst, errors.Wrap(err)
}

View File

@@ -1,685 +0,0 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package localizer_test
import (
"fmt"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
. "sigs.k8s.io/kustomize/api/internal/localizer"
"sigs.k8s.io/kustomize/api/krusty/localizer"
. "sigs.k8s.io/kustomize/api/testutils/localizertest"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
const (
customSchema = `{
"definitions": {
"v1alpha1.MyCRD": {
"properties": {
"apiVersion": {
"type": "string"
},
"kind": {
"type": "string"
},
"metadata": {
"type": "object"
},
"spec": {
"properties": {
"template": {
"$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec"
}
},
"type": "object"
},
"status": {
"properties": {
"success": {
"type": "boolean"
}
},
"type": "object"
}
},
"type": "object",
"x-kubernetes-group-version-kind": [
{
"group": "example.com",
"kind": "MyCRD",
"version": "v1alpha1"
},
{
"group": "",
"kind": "MyCRD",
"version": "v1alpha1"
}
]
},
"io.k8s.api.core.v1.PodTemplateSpec": {
"properties": {
"metadata": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
},
"spec": {
"$ref": "#/definitions/io.k8s.api.core.v1.PodSpec"
}
},
"type": "object"
},
"io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta": {
"properties": {
"name": {
"type": "string"
}
},
"type": "object"
},
"io.k8s.api.core.v1.PodSpec": {
"properties": {
"containers": {
"items": {
"$ref": "#/definitions/io.k8s.api.core.v1.Container"
},
"type": "array",
"x-kubernetes-patch-merge-key": "name",
"x-kubernetes-patch-strategy": "merge"
}
},
"type": "object"
},
"io.k8s.api.core.v1.Container": {
"properties": {
"command": {
"items": {
"type": "string"
},
"type": "array"
},
"image": {
"type": "string"
},
"name": {
"type": "string"
},
"ports": {
"items": {
"$ref": "#/definitions/io.k8s.api.core.v1.ContainerPort"
},
"type": "array",
"x-kubernetes-list-map-keys": [
"containerPort",
"protocol"
],
"x-kubernetes-list-type": "map",
"x-kubernetes-patch-merge-key": "containerPort",
"x-kubernetes-patch-strategy": "merge"
}
},
"type": "object"
},
"io.k8s.api.core.v1.ContainerPort": {
"properties": {
"containerPort": {
"format": "int32",
"type": "integer"
},
"name": {
"type": "string"
},
"protocol": {
"type": "string"
}
},
"type": "object"
}
}
}
`
simpleURL = "https://github.com/kubernetes-sigs/kustomize//api/krusty/testdata/localize/simple"
simpleKustomization = `apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: localize-
resources:
- deployment.yaml
- service.yaml
`
simpleDeployment = `apiVersion: apps/v1
kind: Deployment
metadata:
name: test-deployment-simple
labels:
app: deployment-simple
spec:
selector:
matchLabels:
app: simple
template:
metadata:
labels:
app: simple
spec:
containers:
- name: nginx
image: nginx:1.16
ports:
- containerPort: 8080
`
simpleService = `apiVersion: v1
kind: Service
metadata:
name: test-service-simple
spec:
selector:
app: deployment-simple
ports:
- protocol: TCP
port: 80
targetPort: 8080
`
remoteHPA = `apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: hpa-deployment
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: localize-test-deployment-simple
minReplicas: 1
maxReplicas: 10`
urlQuery = "?submodules=0&ref=kustomize/v4.5.7&timeout=300"
valuesFile = `minecraftServer:
difficulty: peaceful
`
)
func link(t *testing.T, testDir filesys.ConfirmedDir, links map[string]string) {
t.Helper()
for newLink, file := range links {
require.NoError(t, os.Symlink(testDir.Join(file), testDir.Join(newLink)))
}
}
func simplePathAndFiles(t *testing.T) (locPath string, files map[string]string) {
t.Helper()
locPath = filepath.Join(LocalizeDir, "github.com",
"kubernetes-sigs", "kustomize", "kustomize", "v4.5.7",
"api", "krusty", "testdata", "localize", "simple")
files = map[string]string{
"kustomization.yaml": simpleKustomization,
"deployment.yaml": simpleDeployment,
"service.yaml": simpleService,
}
return
}
func remotePathAndFiles(t *testing.T) (locPath string, files map[string]string) {
t.Helper()
locPath = filepath.Join(LocalizeDir, "github.com",
"kubernetes-sigs", "kustomize", "master",
"api", "krusty", "testdata", "localize", "remote")
simplePath, simpleFiles := simplePathAndFiles(t)
files = map[string]string{
"kustomization.yaml": fmt.Sprintf(`apiVersion: kustomize.config.k8s.io/v1beta1
commonLabels:
purpose: remoteReference
kind: Kustomization
resources:
- %s
- hpa.yaml
`, simplePath),
"hpa.yaml": remoteHPA,
}
for path, content := range simpleFiles {
files[filepath.Join(simplePath, path)] = content
}
return
}
func TestWorkingDir(t *testing.T) {
files := map[string]string{
filepath.Join("target", "kustomization.yaml"): fmt.Sprintf(`resources:
- %s
`, filepath.Join("..", "base")),
filepath.Join("base", "kustomization.yaml"): `resources:
- deployment.yaml
`,
filepath.Join("base", "deployment.yaml"): simpleDeployment,
}
fsExpected, fsActual, wd := PrepareFs(t, []string{"target", "base"}, files)
SetWorkingDir(t, wd.String())
dst, err := localizer.Run(fsActual, "target", ".", "")
require.NoError(t, err)
require.Equal(t, wd.Join("localized-target"), dst)
SetupDir(t, fsExpected, dst, files)
CheckFs(t, wd.String(), fsExpected, fsActual)
}
func TestLoaderSymlinks(t *testing.T) {
// test directory
// - link to target
// - link to base
// - link to file
// - target (and scope)
// - link to kustomization
// - base
// - nested root
// - file
// - kustomization
fsExpected, fsActual, testDir := PrepareFs(t, []string{"target",
filepath.Join("target", "base"),
filepath.Join("target", "nested")}, map[string]string{
filepath.Join("target", "base", "kustomization.yaml"): `namePrefix: test-
`,
filepath.Join("target", "nested", "kustomization"): fmt.Sprintf(`resources:
- %s
- %s
`, filepath.Join("..", "file-link"), filepath.Join("..", "base-link")),
filepath.Join("target", "nested", "file"): simpleDeployment,
})
link(t, testDir, map[string]string{
"target-link": "target",
"base-link": filepath.Join("target", "base"),
"file-link": filepath.Join("target", "nested", "file"),
filepath.Join("target", "kustomization.yaml"): filepath.Join("target", "nested", "kustomization"),
})
SetWorkingDir(t, testDir.String())
dst, err := localizer.Run(fsActual, "target-link", "target", "")
require.NoError(t, err)
require.Equal(t, testDir.Join("localized-target"), dst)
SetupDir(t, fsExpected, dst, map[string]string{
"kustomization.yaml": fmt.Sprintf(`resources:
- %s
- base
`, filepath.Join("nested", "file")),
filepath.Join("base", "kustomization.yaml"): `namePrefix: test-
`,
filepath.Join("nested", "file"): simpleDeployment,
})
CheckFs(t, dst, fsExpected, fsActual)
}
func TestRemoteTargetDefaultDst(t *testing.T) {
fsExpected, fsActual, testDir := PrepareFs(t, nil, nil)
SetWorkingDir(t, testDir.String())
const target = simpleURL + urlQuery
dst, err := localizer.Run(fsActual, target, "", "")
require.NoError(t, err)
require.Equal(t, testDir.Join("localized-simple-kustomize-v4.5.7"), dst)
_, files := simplePathAndFiles(t)
SetupDir(t, fsExpected,
filepath.Join(dst, "api", "krusty", "testdata", "localize", "simple"),
files)
CheckFs(t, testDir.String(), fsExpected, fsActual)
}
func TestBadArgs(t *testing.T) {
badDst := filepath.Join("non-existing", "dst")
for name, test := range map[string]struct {
target string
scope string
dst string
err string
}{
"target_no_ref": {
target: simpleURL,
err: `localize remote root "https://github.com/kubernetes-sigs/kustomize//api/krusty/testdata/localize/simple" missing ref query string parameter`,
},
"non-empty_scope": {
target: simpleURL + urlQuery,
scope: ".",
err: fmt.Sprintf(`invalid localize scope ".": scope "." specified for remote localize target "%s"`, simpleURL+urlQuery),
},
"dst_in_non-existing_dir": {
target: ".",
dst: badDst,
err: fmt.Sprintf(`invalid localize destination "%s": unable to create localize destination directory: mkdir %s: no such file or directory`, badDst, badDst),
},
} {
t.Run(name, func(t *testing.T) {
kust := map[string]string{
"kustomization.yaml": "namePrefix: test-",
}
fsExpected, fsActual, testDir := PrepareFs(t, nil, kust)
SetWorkingDir(t, testDir.String())
_, err := localizer.Run(fsActual, test.target, test.scope, test.dst)
require.EqualError(t, err, test.err)
SetupDir(t, fsExpected, testDir.String(), kust)
CheckFs(t, testDir.String(), fsExpected, fsActual)
})
}
}
func TestRemoteFile(t *testing.T) {
const kustf = `apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
openapi:
path: %s
`
fsExpected, fsActual, testDir := PrepareFs(t, nil, map[string]string{
"kustomization.yaml": fmt.Sprintf(kustf, `https://raw.githubusercontent.com/kubernetes-sigs/kustomize/kustomize/v4.5.7/api/krusty/testdata/customschema.json`),
})
newDir := testDir.Join("dst")
dst, err := localizer.Run(fsActual, testDir.String(), "", newDir)
require.NoError(t, err)
require.Equal(t, newDir, dst)
localizedPath := filepath.Join(LocalizeDir, "raw.githubusercontent.com",
"kubernetes-sigs", "kustomize", "kustomize", "v4.5.7", "api", "krusty",
"testdata", "customschema.json")
SetupDir(t, fsExpected, dst, map[string]string{
"kustomization.yaml": fmt.Sprintf(kustf, localizedPath),
localizedPath: customSchema,
})
CheckFs(t, testDir.String(), fsExpected, fsActual)
}
func TestRemoteRoot(t *testing.T) {
fsExpected, fsActual, testDir := PrepareFs(t, nil, map[string]string{
"kustomization.yaml": fmt.Sprintf(`resources:
- %s
`, simpleURL+urlQuery),
})
newDir := testDir.Join("dst")
dst, err := localizer.Run(fsActual, testDir.String(), "", newDir)
require.NoError(t, err)
require.Equal(t, newDir, dst)
localizedPath, files := simplePathAndFiles(t)
SetupDir(t, fsExpected, dst, map[string]string{
"kustomization.yaml": fmt.Sprintf(`resources:
- %s
`, localizedPath),
})
SetupDir(t, fsExpected, filepath.Join(dst, localizedPath), files)
CheckFs(t, testDir.String(), fsExpected, fsActual)
}
func TestNestedRemoteRoots(t *testing.T) {
fsExpected, fsActual, testDir := PrepareFs(t, nil, map[string]string{
// TODO(annasong): Change the ref to the release after kustomize/v4.5.7.
// We need changes to remote post-kustomize/v4.5.7.
"kustomization.yaml": `resources:
- https://github.com/kubernetes-sigs/kustomize//api/krusty/testdata/localize/remote?submodules=0&ref=master&timeout=300
`,
})
newDir := testDir.Join("dst")
dst, err := localizer.Run(fsActual, testDir.String(), "", newDir)
require.NoError(t, err)
require.Equal(t, newDir, dst)
localizedPath, files := remotePathAndFiles(t)
SetupDir(t, fsExpected, dst, map[string]string{
"kustomization.yaml": fmt.Sprintf(`resources:
- %s
`, localizedPath),
})
SetupDir(t, fsExpected, filepath.Join(dst, localizedPath), files)
CheckFs(t, testDir.String(), fsExpected, fsActual)
}
func TestResourcesRepoNotFile(t *testing.T) {
const repo = "https://github.com/kubernetes-sigs/kustomize" + urlQuery
kustomization := map[string]string{
"kustomization.yaml": fmt.Sprintf(`resources:
- %s
`, repo),
}
fsExpected, fsActual, testDir := PrepareFs(t, nil, kustomization)
_, err := localizer.Run(fsActual, testDir.String(), "", testDir.Join("dst"))
fileErr := fmt.Sprintf(`invalid resource at file "%s"`, repo)
rootErr := fmt.Sprintf(`unable to localize root "%s": unable to find one of 'kustomization.yaml', 'kustomization.yml' or 'Kustomization'`, repo)
var actualErr PathLocalizeError
require.ErrorAs(t, err, &actualErr)
require.Equal(t, repo, actualErr.Path)
require.ErrorContains(t, actualErr.FileError, fileErr)
require.ErrorContains(t, actualErr.RootError, rootErr)
SetupDir(t, fsExpected, testDir.String(), kustomization)
CheckFs(t, testDir.String(), fsExpected, fsActual)
}
func TestRemoteRootNoRef(t *testing.T) {
const root = simpleURL + "?submodules=0&timeout=300"
kustomization := map[string]string{
"kustomization.yaml": fmt.Sprintf(`resources:
- %s
`, root),
}
fsExpected, fsActual, testDir := PrepareFs(t, nil, kustomization)
_, err := localizer.Run(fsActual, testDir.String(), "", testDir.Join("dst"))
const fileErr = "invalid file reference: URL is a git repository"
rootErr := fmt.Sprintf(`localize remote root "%s" missing ref query string parameter`, root)
var actualErr PathLocalizeError
require.ErrorAs(t, err, &actualErr)
require.Equal(t, root, actualErr.Path)
require.EqualError(t, actualErr.FileError, fileErr)
require.EqualError(t, actualErr.RootError, rootErr)
SetupDir(t, fsExpected, testDir.String(), kustomization)
CheckFs(t, testDir.String(), fsExpected, fsActual)
}
func TestExistingCacheDir(t *testing.T) {
const remoteFile = `https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/api/krusty/testdata/localize/simple/deployment.yaml`
file := map[string]string{
"kustomization.yaml": fmt.Sprintf(`resources:
- %s
`, remoteFile),
filepath.Join(LocalizeDir, "file"): "existing",
}
fsExpected, fsActual, testDir := PrepareFs(t, []string{LocalizeDir}, file)
_, err := localizer.Run(fsActual, testDir.String(), "", testDir.Join("dst"))
require.ErrorContains(t, err, fmt.Sprintf(`already contains localized-files needed to store file "%s"`, remoteFile))
SetupDir(t, fsExpected, testDir.String(), file)
CheckFs(t, testDir.String(), fsExpected, fsActual)
}
func TestHelmNestedHome(t *testing.T) {
files := map[string]string{
"kustomization.yaml": fmt.Sprintf(`helmGlobals:
chartHome: %s
`, filepath.Join("nested", "dirs", "home")),
filepath.Join("nested", "dirs", "home", "name", "values.yaml"): `
minecraftServer:
difficulty: peaceful
`,
}
fsExpected, fsActual, testDir := PrepareFs(t, []string{
filepath.Join("nested", "dirs", "home", "name"),
}, files)
newDir := testDir.Join("dst")
dst, err := localizer.Run(fsActual, testDir.String(), "", newDir)
require.NoError(t, err)
require.Equal(t, newDir, dst)
SetupDir(t, fsExpected, dst, files)
CheckFs(t, dst, fsExpected, fsActual)
}
func TestHelmLinkedHome(t *testing.T) {
// scope
// - target
// - kustomization
// - myValues.yaml
// - link to home
// - home
// - name
// - values.yaml
fsExpected, fsActual, scope := PrepareFs(t, []string{
"target",
filepath.Join("home", "name"),
},
map[string]string{
filepath.Join("target", "Kustomization"): `helmCharts:
- name: name
valuesFile: myValues.yaml
helmGlobals:
chartHome: home-link
`,
filepath.Join("target", "myValues.yaml"): valuesFile,
filepath.Join("home", "name", "values.yaml"): valuesFile,
})
link(t, scope, map[string]string{
filepath.Join("target", "home-link"): "home",
})
newDir := scope.Join("dst")
dst, err := localizer.Run(fsActual, scope.Join("target"), scope.String(), newDir)
require.NoError(t, err)
require.Equal(t, newDir, dst)
SetupDir(t, fsExpected, dst, map[string]string{
filepath.Join("target", "Kustomization"): fmt.Sprintf(`helmCharts:
- name: name
valuesFile: myValues.yaml
helmGlobals:
chartHome: %s
`, filepath.Join("..", "home")),
filepath.Join("target", "myValues.yaml"): valuesFile,
filepath.Join("home", "name", "values.yaml"): valuesFile,
})
CheckFs(t, dst, fsExpected, fsActual)
}
func TestHelmLinkedDefaultHome(t *testing.T) {
// target
// - kustomization
// - link to home (named charts)
// - home
// - name
// - values.yaml
fsExpected, fsActual, target := PrepareFs(t, []string{
filepath.Join("home", "default"),
filepath.Join("home", "same"),
}, map[string]string{
"kustomization.yaml": fmt.Sprintf(`helmCharts:
- name: default
helmChartInflationGenerator:
- chartHome: %s
chartName: same
`, filepath.Join("home", "..", "charts")),
filepath.Join("home", "default", "values.yaml"): valuesFile,
filepath.Join("home", "same", "values.yaml"): valuesFile,
})
link(t, target, map[string]string{"charts": "home"})
newDir := target.Join("dst")
dst, err := localizer.Run(fsActual, target.String(), "", newDir)
require.NoError(t, err)
require.Equal(t, newDir, dst)
SetupDir(t, fsExpected, dst, map[string]string{
"kustomization.yaml": `helmChartInflationGenerator:
- chartHome: charts
chartName: same
helmCharts:
- name: default
`,
filepath.Join("charts", "default", "values.yaml"): valuesFile,
filepath.Join("charts", "same", "values.yaml"): valuesFile,
})
CheckFs(t, dst, fsExpected, fsActual)
}
func TestHelmHomeEscapesScope(t *testing.T) {
// test directory
// - dir
// - file
// - target (and scope)
// - kustomization
// - home
// - link to dir
// - link to file
fsExpected, fsActual, testDir := PrepareFs(t, []string{
"dir",
filepath.Join("target", "home"),
}, map[string]string{
"file": valuesFile,
filepath.Join("target", "kustomization.yaml"): `helmGlobals:
chartHome: home
`,
})
link(t, testDir, map[string]string{
filepath.Join("target", "home", "dir-link"): "dir",
filepath.Join("target", "home", "file-link"): "file",
})
newDir := testDir.Join("dst")
dst, err := localizer.Run(fsActual, testDir.Join("target"), "", newDir)
require.NoError(t, err)
require.Equal(t, newDir, dst)
SetupDir(t, fsExpected, dst, map[string]string{
"kustomization.yaml": `helmGlobals:
chartHome: home
`,
})
require.NoError(t, fsExpected.Mkdir(filepath.Join(dst, "home")))
CheckFs(t, dst, fsExpected, fsActual)
}
func TestSymlinkedFileSource(t *testing.T) {
// target (and scope)
// - kustomization
// - file
// - link to file
fsExpected, fsActual, target := PrepareFs(t, nil, map[string]string{
"kustomization.yaml": `configMapGenerator:
- files:
- filename-used-as-key-in-configMap
`,
"different-key": "properties",
})
link(t, target, map[string]string{
"filename-used-as-key-in-configMap": "different-key",
})
newDir := target.Join("dst")
dst, err := localizer.Run(fsActual, target.String(), "", newDir)
require.NoError(t, err)
require.Equal(t, newDir, dst)
SetupDir(t, fsExpected, dst, map[string]string{
"kustomization.yaml": `configMapGenerator:
- files:
- different-key
`,
"different-key": "properties",
})
CheckFs(t, dst, fsExpected, fsActual)
}

View File

@@ -13,14 +13,13 @@ const expected = `apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/managed-by: kustomize-(test)
app.kubernetes.io/managed-by: kustomize-v444.333.222
name: myService
spec:
ports:
- port: 7002
`
// This test may fail when running on package tests using the go command because `(test)` is set on makefile.
func TestAddManagedbyLabel(t *testing.T) {
tests := []struct {
kustFile string
@@ -29,6 +28,8 @@ func TestAddManagedbyLabel(t *testing.T) {
}{
{
kustFile: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- service.yaml
`,
@@ -37,6 +38,8 @@ resources:
},
{
kustFile: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- service.yaml
buildMetadata: [managedByLabel]

Some files were not shown because too many files have changed in this diff Show More