mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-22 14:28:18 +00:00
Compare commits
74 Commits
release-ap
...
revert-533
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbbd1599f2 | ||
|
|
4b34ff3075 | ||
|
|
654d7953d9 | ||
|
|
7911b2c001 | ||
|
|
e3b9afcfaa | ||
|
|
af781bfa5e | ||
|
|
7dd9637b1a | ||
|
|
ddce57b585 | ||
|
|
11fc419cee | ||
|
|
a8dec66a84 | ||
|
|
7e541ca380 | ||
|
|
1bd58bdc15 | ||
|
|
9107fa3c81 | ||
|
|
433be59ea7 | ||
|
|
2c444d6bf4 | ||
|
|
59696d1ace | ||
|
|
d30e457031 | ||
|
|
ae041b5c54 | ||
|
|
a2b60e4bcb | ||
|
|
9d126f6dd8 | ||
|
|
bd8045b648 | ||
|
|
5c1a022a3c | ||
|
|
e19ca5405a | ||
|
|
56d37acc7d | ||
|
|
0571a2f15d | ||
|
|
0cdfa5b3dc | ||
|
|
7c36ed21b3 | ||
|
|
985835f96f | ||
|
|
f81765b96e | ||
|
|
a2ceaff053 | ||
|
|
94181b1be7 | ||
|
|
169fdd7330 | ||
|
|
78b8139d46 | ||
|
|
76f8d2828b | ||
|
|
b692e49b1e | ||
|
|
bd7f001c26 | ||
|
|
d6ff768298 | ||
|
|
4947a905fa | ||
|
|
cd9a16cfab | ||
|
|
02a53f193d | ||
|
|
b1717c8a97 | ||
|
|
911ddcda40 | ||
|
|
9f1d5acdc8 | ||
|
|
9756d92a91 | ||
|
|
d32d1937e6 | ||
|
|
ddcbae54ab | ||
|
|
ca748faa3f | ||
|
|
5e6cbac589 | ||
|
|
ff75dd6cd5 | ||
|
|
f8391994b4 | ||
|
|
5ce14e5024 | ||
|
|
ee22c9cab7 | ||
|
|
4a893ce8c6 | ||
|
|
9ce923ebeb | ||
|
|
494a807f28 | ||
|
|
820f17c73b | ||
|
|
cd7ba1744e | ||
|
|
da4e881007 | ||
|
|
878cda7c55 | ||
|
|
47327616df | ||
|
|
eeff67d88d | ||
|
|
6adf4f294a | ||
|
|
5e7cc3437d | ||
|
|
096b2c4435 | ||
|
|
129d0f90af | ||
|
|
c76fd5eb85 | ||
|
|
691b7d1df3 | ||
|
|
91078545e6 | ||
|
|
467633c2de | ||
|
|
e44df7b386 | ||
|
|
bbff6768da | ||
|
|
0020a496f7 | ||
|
|
9eddc3c5a7 | ||
|
|
7aa33ff471 |
36
.github/workflows/apidiff.yml
vendored
Normal file
36
.github/workflows/apidiff.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: APIDiff
|
||||
|
||||
# Trigger the workflow on pull requests and direct pushes to any branch
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
go-apidiff:
|
||||
name: Verify API differences
|
||||
runs-on: ubuntu-latest
|
||||
# Pull requests from different repository only trigger this checks
|
||||
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository)
|
||||
steps:
|
||||
- name: Clone the code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version-file: go.work
|
||||
- name: Execute go-apidiff
|
||||
uses: joelanford/go-apidiff@v0.6.0
|
||||
with:
|
||||
compare-imports: true
|
||||
print-compatible: true
|
||||
- name: Report failure
|
||||
uses: nashmaniac/create-issue-action@v1.1
|
||||
# Only report failures of pushes (PRs have are visible through the Checks section) to the default branch
|
||||
if: failure() && github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||
with:
|
||||
title: 🐛 go-apidiff failed for ${{ github.sha }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
labels: kind/bug
|
||||
body: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
27
.github/workflows/release.yaml
vendored
Normal file
27
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- kyaml/v*
|
||||
- cmd/config/v*
|
||||
- api/v*
|
||||
- kustomize/v*
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- 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
|
||||
- run: ./releasing/create-release.sh "${tag}"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref_name }}
|
||||
129
CONTRIBUTING.md
129
CONTRIBUTING.md
@@ -59,11 +59,140 @@ Kustomize follows the [Kubernetes Community Membership] contributor ladder. Role
|
||||
|
||||
The kyaml module within the Kustomize repo has additional owners following the same ladder.
|
||||
|
||||
For the kustomize project, we have defined some specific guidelines on each step of the ladder:
|
||||
|
||||
To reach reviewer status, you must:
|
||||
- Have been actively involved in kustomize for 3+ months
|
||||
- Review at least 8 PRs that have been driven through to completion (see the reviewer guide below)
|
||||
- Author at least 5 PRs that have been approved and merged
|
||||
- Be a member of the kubernetes-sigs org. This should not be a blocker though, as once you meet the requirements for reviewer here,
|
||||
the existing kustomize maintainers will be happy to sponsor your request to join the kubernetes-sigs org.
|
||||
- Once you have met the above requirements, you may submit a PR adding yourself to the kustomize reviewers list, with links to your
|
||||
contributions in the description.
|
||||
|
||||
To reach approver status, you must:
|
||||
- Meet all the requirements of a reviewer
|
||||
- Have been actively involved in kustomize for 6+ months
|
||||
- Review at least 15 PRs that have been driven through to completion (see the reviewer guide below)
|
||||
- Authored PRs meeting *either* of the following requirements:
|
||||
- 15 PRs that have been approved and merged
|
||||
- *OR* 10 PRs that have been approved and merged where some were more difficult, required greater thought/design,
|
||||
or built up to larger features/long-term goals.
|
||||
- File 3 issues. This can be any number of things, including but not limited to:
|
||||
- Bugs with kustomize usage that you've found
|
||||
- CI or release improvements
|
||||
- Creating subtasks of a larger feature or project that you are in charge of.
|
||||
- Long term improvements for the health of the project
|
||||
- Triage at least 10 untriaged issues, including at least 1 feature request. The kustomize bug scrub is a great place to get practice with doing this, but you can
|
||||
also follow the triage guide below to get started on your own.
|
||||
- Demonstrate deeper understanding of kustomize goals. This can take many forms and is a bit subjective, but here are a few examples:
|
||||
- saying no to an eschewed feature, instead recommending an alternative solution that is more aligned with the declarative configuration model
|
||||
- active participation in discussion on a feature request issue
|
||||
- filing an issue describing a long term problem and solution aligned with kustomize goals, for example: https://github.com/kubernetes-sigs/kustomize/issues/5140
|
||||
- writing up KEPs for features that will improve the kustomize workflow while being aligned with kustomize goals, for example: https://github.com/kubernetes-sigs/kustomize/pull/4558
|
||||
- Regularly interact with the existing kustomize maintainers, with clear communication about what you are working on or planning to work on. The kustomize
|
||||
maintainers should know who you are and be familiar with your contributions.
|
||||
- If you meet *most* of the above requirements while going above and beyond in a few areas, we will still consider your request to become an approver even
|
||||
if you are missing one or two of the requirements. Please contact the maintainers directly to ask about getting approver status if you fall into this category.
|
||||
- Otherwise, once you meet all the above requirements, you may:
|
||||
- request to be added to the kustomize maintainer meeting that occurs each week with the kustomize PMs.
|
||||
- submit a PR adding yourself to the kustomize approvers list, with links to your contributions in the description.
|
||||
|
||||
To reach owner status, you must:
|
||||
- Meet all the requirements of an approver
|
||||
- Have been actively involved with kustomize for 1+ year
|
||||
- Assisted the current owner in driving the roadmap. This can be explicit or implicit help, such as:
|
||||
- Editing the roadmap directly
|
||||
- Reviewing the roadmap
|
||||
- Providing suggestions for issues or prioritization in meetings that indirectly influence the roadmap
|
||||
- Regularly triage issues and attend the kustomize bug scrub
|
||||
- Regularly review PRs (1-2 a week)
|
||||
- Periodically lead the kustomize bug scrub
|
||||
- Periodically release kustomize (ensuring that there are no release blockers and that release notes are clean)
|
||||
- Be the primary owner or point of contact for a particular project or area of code
|
||||
- Ideally, there should be 2-3 owners at a time. Reach out to the current owners if you are interested in ownership. These
|
||||
requirements are not strict and evaluation is somewhat subjective.
|
||||
|
||||
## Reviewer guide
|
||||
Please watch this talk on how to review code from Tim Hockin: https://www.youtube.com/watch?v=OZVv7-o8i40
|
||||
|
||||
For reviewing PRs in kustomize, we have some specific guidelines:
|
||||
- If the PR is introducing a new feature:
|
||||
- *It must be implementing an issue that has already been triage/accepted or
|
||||
a KEP that has been approved.* If it is not, then request the PR author to first file an issue.
|
||||
- The PR must include thorough tests for the new feature, including unit and integration tests
|
||||
- The code must be clean and readable, with thought given to how we will maintain the code in the future
|
||||
- If the feature requires being broken up into multiple PRs to ease review, the feature should not be exposed to users
|
||||
until the feature is completed in the last PR. For example, while we were building `kustomize localize`, we
|
||||
built the feature almost entirely under the `api` module as a library with all the needed tests. There was no way
|
||||
for users to invoke the localize code until the last PR that actually exposed the `kustomize localize` command in the
|
||||
kustomize binary. This allowed us to continue development of `kustomize localize` without blocking kustomize releases.
|
||||
If this type of development is not possible, then new features requiring multiple PRs should be
|
||||
developed in their own feature branch.
|
||||
- If the PR is introducing a bug fix:
|
||||
- If the PR is not fixing an issue that has already been triage/accepted, follow the triage guide below on bug
|
||||
fixes to decide if this is a PR we want to accept.
|
||||
- The PR should have two distinct commits:
|
||||
- The first commit should add a test demonstrating incorrect behavior
|
||||
- The second commit should include the bug fix
|
||||
- Some sample PRs:
|
||||
- https://github.com/kubernetes-sigs/kustomize/pull/5263/commits
|
||||
- https://github.com/kubernetes-sigs/kustomize/pull/3931/commits
|
||||
- The regression test is absolutely required, and we cannot accept bug fixes without tests.
|
||||
- If the PR is introducing a performance improvement:
|
||||
- The PR description should give an indication of how much the performance is being improved and how we
|
||||
can measure it - benchmark tests are fantastic.
|
||||
- Other PRs (documentation, CI improvements, etc.) should be reviewed based on your best judgment.
|
||||
|
||||
## Triage guide
|
||||
The possible triage labels are listed here: https://github.com/kubernetes-sigs/kustomize/labels?q=triage.
|
||||
|
||||
Triaging a feature request means:
|
||||
- Understand what the user is asking for, and their use case.
|
||||
- Verify that it is not an [eschewed feature](https://kubectl.docs.kubernetes.io/faq/kustomize/eschewedfeatures/#build-time-side-effects-from-cli-args-or-env-variables)
|
||||
- Verify that it is not a duplicate issue.
|
||||
- Look into workarounds. Is there another way that the user can achieve their use case with existing features?
|
||||
- If you are new to this role, prior to leaving a comment on the issue, please bring it to weekly standup
|
||||
for group discussion to make sure that we are all on the same page.
|
||||
- Once you feel ready, you can label it with a triage label. Here's an [example](https://github.com/kubernetes-sigs/kustomize/issues/5049). You can also
|
||||
look at other feature request issues to see how they were triaged and resolved. There are a few different triage labels that you can use, you can see the
|
||||
full list [here](https://github.com/kubernetes-sigs/kustomize/labels?q=triage).
|
||||
|
||||
Triaging a bug means:
|
||||
- First, verify that you can reproduce the issue. If you cannot reproduce the issue or need more information to give
|
||||
it a go, triage it accordingly.
|
||||
- Try to understand if this is really a bug or if this is intended behavior from kustomize. If it seems like intended
|
||||
behavior, do your best to explain to the user why this is the case.
|
||||
- If it seems to be a genuine bug, you can /triage accept the issue. In addition, investigate if there are workarounds or
|
||||
alternative solutions for the user that they can try until the issue gets resolved.
|
||||
|
||||
Administrative notes:
|
||||
|
||||
- The [OWNERS file spec] is a useful resources in making changes.
|
||||
- Maintainers and admins must be added to the appropriate lists in both [Kustomize OWNERS_ALIASES] and [SIG-CLI Teams]. If this isn't done, the individual in question will lack either PR approval rights (Kustomize list) or the appropriate Github repository permissions (community list).
|
||||
|
||||
## Project/Product Managers
|
||||
|
||||
Kustomize will have opportunities to join in a project/product manager role. You can reach out to
|
||||
the existing kustomize maintainers if you are interested in this type of role. Project management work
|
||||
can greatly help supplement your contributions as you climb from reviewer to approver
|
||||
to owner.
|
||||
|
||||
Expectations for this role are:
|
||||
|
||||
- Triage 1 feature request each week, and bring it to weekly stand-up for discussion. Feature
|
||||
requests are issues labeled kind/feature, and you can find them [here](https://github.com/kubernetes-sigs/kustomize/issues?q=is%3Aissue+is%3Aopen+kind+feature+label%3Akind%2Ffeature).
|
||||
Please view the above triage guide for details on how to approach feature request triage.
|
||||
- Monitor the kustomize Slack channel and try to help users if you can. It is a pretty
|
||||
active channel, so responding to 4-5 users per week is sufficient even if some
|
||||
questions go unanswered. If there is an interesting topic or a recurring problem that many
|
||||
users are having, please bring it up in weekly stand-up.
|
||||
- Keeping track of a queue of backlog issues or PRs that are not being actively looked at in any existing project board.
|
||||
- Organizing or reorganizing project tracking boards when it makes sense.
|
||||
|
||||
You will also be asked to help with roadmap planning, deprecation communication, prioritization,
|
||||
and doing research on kustomize usage when appropriate, though these responsibilities will occur less
|
||||
frequently.
|
||||
|
||||
## Contact Information
|
||||
|
||||
|
||||
10
Makefile
10
Makefile
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# Makefile for kustomize CLI and API.
|
||||
|
||||
LATEST_RELEASE=v5.0.3
|
||||
LATEST_RELEASE=v5.1.1
|
||||
|
||||
SHELL := /usr/bin/env bash
|
||||
GOOS = $(shell go env GOOS)
|
||||
@@ -126,6 +126,14 @@ check-license: $(MYGOBIN)/addlicense
|
||||
lint: $(MYGOBIN)/golangci-lint $(MYGOBIN)/goimports $(builtinplugins)
|
||||
./hack/for-each-module.sh "make lint"
|
||||
|
||||
.PHONY: apidiff
|
||||
apidiff: go-apidiff ## Run the go-apidiff to verify any API differences compared with origin/master
|
||||
$(GOBIN)/go-apidiff master --compare-imports --print-compatible --repo-path=.
|
||||
|
||||
.PHONY: go-apidiff
|
||||
go-apidiff:
|
||||
go install github.com/joelanford/go-apidiff@v0.6.0
|
||||
|
||||
.PHONY: test-unit-all
|
||||
test-unit-all: \
|
||||
test-unit-non-plugin \
|
||||
|
||||
@@ -8,6 +8,7 @@ aliases:
|
||||
- knverey
|
||||
- natasha41575
|
||||
- annasong20
|
||||
- koba1t
|
||||
kustomize-reviewers:
|
||||
- knverey
|
||||
- natasha41575
|
||||
|
||||
84
README.md
84
README.md
@@ -36,11 +36,16 @@ be updated on a regular basis going forward, and such updates
|
||||
will be reflected in the Kubernetes release notes.
|
||||
|
||||
| Kubectl version | Kustomize version |
|
||||
| --- | --- |
|
||||
| < v1.14 | n/a |
|
||||
| v1.14-v1.20 | v2.0.3 |
|
||||
| v1.21 | v4.0.5 |
|
||||
| v1.22 | v4.2.0 |
|
||||
| --------------- | ----------------- |
|
||||
| < v1.14 | n/a |
|
||||
| v1.14-v1.20 | v2.0.3 |
|
||||
| v1.21 | v4.0.5 |
|
||||
| v1.22 | v4.2.0 |
|
||||
| v1.23 | v4.4.1 |
|
||||
| v1.24 | v4.5.4 |
|
||||
| v1.25 | v4.5.7 |
|
||||
| v1.26 | v4.5.7 |
|
||||
| v1.27 | v5.0.1 |
|
||||
|
||||
[v2.0.3]: https://github.com/kubernetes-sigs/kustomize/releases/tag/v2.0.3
|
||||
[#2506]: https://github.com/kubernetes-sigs/kustomize/issues/2506
|
||||
@@ -63,7 +68,37 @@ This file should declare those resources, and any
|
||||
customization to apply to them, e.g. _add a common
|
||||
label_.
|
||||
|
||||
![base image][imageBase]
|
||||
```
|
||||
|
||||
base: kustomization + resources
|
||||
|
||||
kustomization.yaml deployment.yaml service.yaml
|
||||
+---------------------------------------------+ +-------------------------------------------------------+ +-----------------------------------+
|
||||
| apiVersion: kustomize.config.k8s.io/v1beta1 | | apiVersion: apps/v1 | | apiVersion: v1 |
|
||||
| kind: Kustomization | | kind: Deployment | | kind: Service |
|
||||
|.commonLabels: | | metadata: | | metadata: |
|
||||
| app: myapp | | name: myapp | | name: myapp |
|
||||
| resources: | | spec: | | spec: |
|
||||
| - deployment.yaml | | selector: | | selector: |
|
||||
| - service.yaml | | matchLabels: | | app: myapp |
|
||||
| configMapGenerator: | | app: myapp | | ports: |
|
||||
| - name: myapp-map | | template: | | - port: 6060 |
|
||||
| literals: | | metadata: | | targetPort: 6060 |
|
||||
| - KEY=value | | labels: | +-----------------------------------+
|
||||
+---------------------------------------------+ | app: myapp |
|
||||
| spec: |
|
||||
| containers: |
|
||||
| - name: myapp |
|
||||
| image: myapp |
|
||||
| resources: |
|
||||
| limits: |
|
||||
| memory: "128Mi" |
|
||||
| cpu: "500m" |
|
||||
| ports: |
|
||||
| - containerPort: 6060 |
|
||||
+-------------------------------------------------------+
|
||||
|
||||
```
|
||||
|
||||
File structure:
|
||||
|
||||
@@ -99,20 +134,41 @@ Manage traditional [variants] of a configuration - like
|
||||
_development_, _staging_ and _production_ - using
|
||||
[overlays] that modify a common [base].
|
||||
|
||||
![overlay image][imageOverlay]
|
||||
```
|
||||
|
||||
overlay: kustomization + patches
|
||||
|
||||
kustomization.yaml replica_count.yaml cpu_count.yaml
|
||||
+-----------------------------------------------+ +-------------------------------+ +------------------------------------------+
|
||||
| apiVersion: kustomize.config.k8s.io/v1beta1 | | apiVersion: apps/v1 | | apiVersion: apps/v1 |
|
||||
| kind: Kustomization | | kind: Deployment | | kind: Deployment |
|
||||
| commonLabels: | | metadata: | | metadata: |
|
||||
| variant: prod | | name: myapp | | name: myapp |
|
||||
| resources: | | spec: | | spec: |
|
||||
| - ../../base | | replicas: 80 | | template: |
|
||||
| patches: | +-------------------------------+ | spec: |
|
||||
| - path: replica_count.yaml | | containers: |
|
||||
| - path: cpu_count.yaml | | - name: myapp |
|
||||
+-----------------------------------------------+ | resources: |
|
||||
| limits: |
|
||||
| memory: "128Mi" |
|
||||
| cpu: "7000m" |
|
||||
+------------------------------------------+
|
||||
```
|
||||
|
||||
|
||||
File structure:
|
||||
> ```
|
||||
> ~/someApp
|
||||
> ├── base
|
||||
> │ ├── deployment.yaml
|
||||
> │ ├── kustomization.yaml
|
||||
> │ └── service.yaml
|
||||
> │ ├── deployment.yaml
|
||||
> │ ├── kustomization.yaml
|
||||
> │ └── service.yaml
|
||||
> └── overlays
|
||||
> ├── development
|
||||
> │ ├── cpu_count.yaml
|
||||
> │ ├── kustomization.yaml
|
||||
> │ └── replica_count.yaml
|
||||
> │ ├── cpu_count.yaml
|
||||
> │ ├── kustomization.yaml
|
||||
> │ └── replica_count.yaml
|
||||
> └── production
|
||||
> ├── cpu_count.yaml
|
||||
> ├── kustomization.yaml
|
||||
@@ -166,8 +222,6 @@ is governed by the [Kubernetes Code of Conduct].
|
||||
[applied]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#apply
|
||||
[base]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#base
|
||||
[declarative configuration]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#declarative-application-management
|
||||
[imageBase]: images/base.jpg
|
||||
[imageOverlay]: images/overlay.jpg
|
||||
[kubectl announcement]: https://kubernetes.io/blog/2019/03/25/kubernetes-1-14-release-announcement
|
||||
[kubernetes documentation]: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/
|
||||
[kubernetes style]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#kubernetes-style-object
|
||||
|
||||
@@ -6,7 +6,7 @@ package imagetag
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/image"
|
||||
"sigs.k8s.io/kustomize/api/internal/image"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@ package patchjson6902
|
||||
import (
|
||||
"strings"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
jsonpatch "gopkg.in/evanphx/json-patch.v5"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
k8syaml "sigs.k8s.io/yaml"
|
||||
|
||||
@@ -776,6 +776,133 @@ spec:
|
||||
autoscaling: true
|
||||
deepgram-api:
|
||||
some: value
|
||||
`,
|
||||
},
|
||||
|
||||
// Issue #4928
|
||||
"support numeric keys": {
|
||||
input: `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blabla
|
||||
namespace: blabla-ns
|
||||
data:
|
||||
"6443": "foobar"
|
||||
`,
|
||||
patch: yaml.MustParse(`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blabla
|
||||
namespace: blabla-ns
|
||||
data:
|
||||
"6443": "barfoo"
|
||||
"9110": "foo-foo"
|
||||
`),
|
||||
expected: `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blabla
|
||||
namespace: blabla-ns
|
||||
data:
|
||||
"6443": "barfoo"
|
||||
"9110": "foo-foo"
|
||||
`,
|
||||
},
|
||||
|
||||
"honor different key style one": {
|
||||
input: `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blabla
|
||||
namespace: blabla-ns
|
||||
data:
|
||||
'6443': "foobar"
|
||||
`,
|
||||
patch: yaml.MustParse(`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blabla
|
||||
namespace: blabla-ns
|
||||
data:
|
||||
"6443": "barfoo"
|
||||
9110: "foo-foo"
|
||||
`),
|
||||
expected: `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blabla
|
||||
namespace: blabla-ns
|
||||
data:
|
||||
'6443': "barfoo"
|
||||
9110: "foo-foo"
|
||||
`,
|
||||
},
|
||||
|
||||
"honor different key style two": {
|
||||
input: `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blabla
|
||||
namespace: blabla-ns
|
||||
data:
|
||||
"6443": "foobar"
|
||||
`,
|
||||
patch: yaml.MustParse(`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blabla
|
||||
namespace: blabla-ns
|
||||
data:
|
||||
"6443": "barfoo"
|
||||
'9110': "foo-foo"
|
||||
`),
|
||||
expected: `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blabla
|
||||
namespace: blabla-ns
|
||||
data:
|
||||
"6443": "barfoo"
|
||||
'9110': "foo-foo"
|
||||
`,
|
||||
},
|
||||
|
||||
"different key types": {
|
||||
input: `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blabla
|
||||
namespace: blabla-ns
|
||||
data:
|
||||
"6443": "key-string-double-quoted"
|
||||
`,
|
||||
patch: yaml.MustParse(`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blabla
|
||||
namespace: blabla-ns
|
||||
data:
|
||||
6443: "key-int"
|
||||
`),
|
||||
expected: `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: blabla
|
||||
namespace: blabla-ns
|
||||
data:
|
||||
"6443": "key-int"
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -126,8 +126,8 @@ func applyReplacement(nodes []*yaml.RNode, value *yaml.RNode, targetSelectors []
|
||||
}
|
||||
|
||||
// filter targets by matching resource IDs
|
||||
for i, id := range ids {
|
||||
if id.IsSelectedBy(selector.Select.ResId) && !rejectId(selector.Reject, &ids[i]) {
|
||||
for _, id := range ids {
|
||||
if id.IsSelectedBy(selector.Select.ResId) && !containsRejectId(selector.Reject, ids) {
|
||||
err := copyValueToTarget(possibleTarget, value, selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -168,10 +168,15 @@ func matchesAnnoAndLabelSelector(n *yaml.RNode, selector *types.Selector) (bool,
|
||||
return annoMatch && labelMatch, nil
|
||||
}
|
||||
|
||||
func rejectId(rejects []*types.Selector, id *resid.ResId) bool {
|
||||
func containsRejectId(rejects []*types.Selector, ids []resid.ResId) bool {
|
||||
for _, r := range rejects {
|
||||
if !r.ResId.IsEmpty() && id.IsSelectedBy(r.ResId) {
|
||||
return true
|
||||
if r.ResId.IsEmpty() {
|
||||
continue
|
||||
}
|
||||
for _, id := range ids {
|
||||
if id.IsSelectedBy(r.ResId) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -3,11 +3,11 @@ module sigs.k8s.io/kustomize/api
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible
|
||||
github.com/go-errors/errors v1.4.2
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/imdario/mergo v0.3.6
|
||||
github.com/stretchr/testify v1.8.1
|
||||
gopkg.in/evanphx/json-patch.v5 v5.6.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
k8s.io/kube-openapi v0.0.0-20230601164746-7562a1006961
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.3
|
||||
@@ -35,3 +35,5 @@ require (
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace sigs.k8s.io/kustomize/kyaml => ../kyaml
|
||||
|
||||
@@ -5,8 +5,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
|
||||
github.com/evanphx/json-patch v4.12.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=
|
||||
@@ -75,6 +73,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
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/evanphx/json-patch.v5 v5.6.0 h1:BMT6KIwBD9CaU91PJCZIe46bDmBWa9ynTQgJIOpfQBk=
|
||||
gopkg.in/evanphx/json-patch.v5 v5.6.0/go.mod h1:/kvTRh1TVm5wuM6OkHxqXtE/1nUZZpihg29RtuIyfvk=
|
||||
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.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
@@ -85,7 +85,5 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/kube-openapi v0.0.0-20230601164746-7562a1006961 h1:pqRVJGQJz6oeZby8qmPKXYIBjyrcv7EHCe/33UkZMYA=
|
||||
k8s.io/kube-openapi v0.0.0-20230601164746-7562a1006961/go.mod h1:l8HTwL5fqnlns4jOveW1L75eo7R9KFHxiE0bsPGy428=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.3 h1:WpabVAKZe2YEp/irTSHwD6bfjwZnTtSDewd2BVJGMZs=
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.3/go.mod h1:npvh9epWysfQ689Rtt/U+dpOJDTBn8kUnF1O6VzvmZA=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
. "sigs.k8s.io/kustomize/api/internal/accumulator"
|
||||
"sigs.k8s.io/kustomize/api/internal/loader"
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
|
||||
@@ -6,7 +6,7 @@ package builtins
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
jsonpatch "gopkg.in/evanphx/json-patch.v5"
|
||||
"sigs.k8s.io/kustomize/api/filters/patchjson6902"
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
|
||||
@@ -7,104 +7,123 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
jsonpatch "gopkg.in/evanphx/json-patch.v5"
|
||||
"sigs.k8s.io/kustomize/api/filters/patchjson6902"
|
||||
"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/kioutil"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
type PatchTransformerPlugin struct {
|
||||
loadedPatch *resource.Resource
|
||||
decodedPatch jsonpatch.Patch
|
||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
|
||||
Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"`
|
||||
Options map[string]bool `json:"options,omitempty" yaml:"options,omitempty"`
|
||||
smPatches []*resource.Resource // strategic-merge patches
|
||||
jsonPatches jsonpatch.Patch // json6902 patch
|
||||
// patchText is pure patch text created by Path or Patch
|
||||
patchText string
|
||||
// patchSource is patch source message
|
||||
patchSource string
|
||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
|
||||
Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"`
|
||||
Options map[string]bool `json:"options,omitempty" yaml:"options,omitempty"`
|
||||
}
|
||||
|
||||
func (p *PatchTransformerPlugin) Config(
|
||||
h *resmap.PluginHelpers, c []byte) error {
|
||||
err := yaml.Unmarshal(c, p)
|
||||
if err != nil {
|
||||
func (p *PatchTransformerPlugin) Config(h *resmap.PluginHelpers, c []byte) error {
|
||||
if err := yaml.Unmarshal(c, p); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Patch = strings.TrimSpace(p.Patch)
|
||||
if p.Patch == "" && p.Path == "" {
|
||||
return fmt.Errorf(
|
||||
"must specify one of patch and path in\n%s", string(c))
|
||||
}
|
||||
if p.Patch != "" && p.Path != "" {
|
||||
return fmt.Errorf(
|
||||
"patch and path can't be set at the same time\n%s", string(c))
|
||||
}
|
||||
if p.Path != "" {
|
||||
loaded, loadErr := h.Loader().Load(p.Path)
|
||||
if loadErr != nil {
|
||||
return loadErr
|
||||
switch {
|
||||
case p.Patch == "" && p.Path == "":
|
||||
return fmt.Errorf("must specify one of patch and path in\n%s", string(c))
|
||||
case p.Patch != "" && p.Path != "":
|
||||
return fmt.Errorf("patch and path can't be set at the same time\n%s", string(c))
|
||||
case p.Patch != "":
|
||||
p.patchText = p.Patch
|
||||
p.patchSource = fmt.Sprintf("[patch: %q]", p.patchText)
|
||||
case p.Path != "":
|
||||
loaded, err := h.Loader().Load(p.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get the patch file from path(%s): %w", p.Path, err)
|
||||
}
|
||||
p.Patch = string(loaded)
|
||||
p.patchText = string(loaded)
|
||||
p.patchSource = fmt.Sprintf("[path: %q]", p.Path)
|
||||
}
|
||||
|
||||
patchSM, errSM := h.ResmapFactory().RF().FromBytes([]byte(p.Patch))
|
||||
patchJson, errJson := jsonPatchFromBytes([]byte(p.Patch))
|
||||
patchesSM, errSM := h.ResmapFactory().RF().SliceFromBytes([]byte(p.patchText))
|
||||
patchesJson, errJson := jsonPatchFromBytes([]byte(p.patchText))
|
||||
|
||||
if (errSM == nil && errJson == nil) ||
|
||||
(patchSM != nil && patchJson != nil) {
|
||||
(patchesSM != nil && patchesJson != nil) {
|
||||
return fmt.Errorf(
|
||||
"illegally qualifies as both an SM and JSON patch: [%v]",
|
||||
p.Patch)
|
||||
"illegally qualifies as both an SM and JSON patch: %s",
|
||||
p.patchSource)
|
||||
}
|
||||
if errSM != nil && errJson != nil {
|
||||
return fmt.Errorf(
|
||||
"unable to parse SM or JSON patch from [%v]", p.Patch)
|
||||
"unable to parse SM or JSON patch from %s", p.patchSource)
|
||||
}
|
||||
if errSM == nil {
|
||||
p.loadedPatch = patchSM
|
||||
if p.Options["allowNameChange"] {
|
||||
p.loadedPatch.AllowNameChange()
|
||||
}
|
||||
if p.Options["allowKindChange"] {
|
||||
p.loadedPatch.AllowKindChange()
|
||||
p.smPatches = patchesSM
|
||||
for _, loadedPatch := range p.smPatches {
|
||||
if p.Options["allowNameChange"] {
|
||||
loadedPatch.AllowNameChange()
|
||||
}
|
||||
if p.Options["allowKindChange"] {
|
||||
loadedPatch.AllowKindChange()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p.decodedPatch = patchJson
|
||||
p.jsonPatches = patchesJson
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||
if p.loadedPatch == nil {
|
||||
return p.transformJson6902(m, p.decodedPatch)
|
||||
if p.smPatches != nil {
|
||||
return p.transformStrategicMerge(m)
|
||||
}
|
||||
// The patch was a strategic merge patch
|
||||
return p.transformStrategicMerge(m, p.loadedPatch)
|
||||
return p.transformJson6902(m)
|
||||
}
|
||||
|
||||
// transformStrategicMerge applies the provided strategic merge patch
|
||||
// to all the resources in the ResMap that match either the Target or
|
||||
// the identifier of the patch.
|
||||
func (p *PatchTransformerPlugin) transformStrategicMerge(m resmap.ResMap, patch *resource.Resource) error {
|
||||
if p.Target == nil {
|
||||
// transformStrategicMerge applies each loaded strategic merge patch
|
||||
// to the resource in the ResMap that matches the identifier of the patch.
|
||||
// If only one patch is specified, the Target can be used instead.
|
||||
func (p *PatchTransformerPlugin) transformStrategicMerge(m resmap.ResMap) error {
|
||||
if p.Target != nil {
|
||||
if len(p.smPatches) > 1 {
|
||||
// detail: https://github.com/kubernetes-sigs/kustomize/issues/5049#issuecomment-1440604403
|
||||
return fmt.Errorf("Multiple Strategic-Merge Patches in one `patches` entry is not allowed to set `patches.target` field: %s", p.patchSource)
|
||||
}
|
||||
|
||||
// single patch
|
||||
patch := p.smPatches[0]
|
||||
selected, err := m.Select(*p.Target)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to find patch target %q in `resources`: %w", p.Target, err)
|
||||
}
|
||||
return errors.Wrap(m.ApplySmPatch(resource.MakeIdSet(selected), patch))
|
||||
}
|
||||
|
||||
for _, patch := range p.smPatches {
|
||||
target, err := m.GetById(patch.OrgId())
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("no resource matches strategic merge patch %q: %w", patch.OrgId(), err)
|
||||
}
|
||||
if err := target.ApplySmPatch(patch); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
return target.ApplySmPatch(patch)
|
||||
}
|
||||
selected, err := m.Select(*p.Target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.ApplySmPatch(resource.MakeIdSet(selected), patch)
|
||||
return nil
|
||||
}
|
||||
|
||||
// transformJson6902 applies the provided json6902 patch
|
||||
// to all the resources in the ResMap that match the Target.
|
||||
func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpatch.Patch) error {
|
||||
// transformJson6902 applies json6902 Patch to all the resources in the ResMap that match Target.
|
||||
func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap) error {
|
||||
if p.Target == nil {
|
||||
return fmt.Errorf("must specify a target for patch %s", p.Patch)
|
||||
return fmt.Errorf("must specify a target for JSON patch %s", p.patchSource)
|
||||
}
|
||||
resources, err := m.Select(*p.Target)
|
||||
if err != nil {
|
||||
@@ -114,7 +133,7 @@ func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpa
|
||||
res.StorePreviousId()
|
||||
internalAnnotations := kioutil.GetInternalAnnotations(&res.RNode)
|
||||
err = res.ApplyFilter(patchjson6902.Filter{
|
||||
Patch: p.Patch,
|
||||
Patch: p.patchText,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -129,16 +148,17 @@ func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpa
|
||||
return nil
|
||||
}
|
||||
|
||||
// jsonPatchFromBytes loads a Json 6902 patch from
|
||||
// a bytes input
|
||||
func jsonPatchFromBytes(
|
||||
in []byte) (jsonpatch.Patch, error) {
|
||||
// jsonPatchFromBytes loads a Json 6902 patch from a bytes input
|
||||
func jsonPatchFromBytes(in []byte) (jsonpatch.Patch, error) {
|
||||
ops := string(in)
|
||||
if ops == "" {
|
||||
return nil, fmt.Errorf("empty json patch operations")
|
||||
}
|
||||
|
||||
if ops[0] != '[' {
|
||||
// TODO(5049):
|
||||
// In the case of multiple yaml documents, return error instead of ignoring all but first.
|
||||
// Details: https://github.com/kubernetes-sigs/kustomize/pull/5194#discussion_r1256686728
|
||||
jsonOps, err := yaml.YAMLToJSON(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
. "sigs.k8s.io/kustomize/api/internal/generators"
|
||||
"sigs.k8s.io/kustomize/api/kv"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/api/pkg/loader"
|
||||
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
. "sigs.k8s.io/kustomize/api/internal/generators"
|
||||
"sigs.k8s.io/kustomize/api/kv"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/api/pkg/loader"
|
||||
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package image
|
||||
package image_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/internal/image"
|
||||
)
|
||||
|
||||
func TestIsImageMatched(t *testing.T) {
|
||||
@@ -50,7 +51,7 @@ func TestIsImageMatched(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
assert.Equal(t, tc.isMatched, IsImageMatched(tc.value, tc.name))
|
||||
assert.Equal(t, tc.isMatched, image.IsImageMatched(tc.value, tc.name))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -116,7 +117,7 @@ func TestSplit(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
name, tag, digest := Split(tc.value)
|
||||
name, tag, digest := image.Split(tc.value)
|
||||
assert.Equal(t, tc.name, name)
|
||||
assert.Equal(t, tc.tag, tag)
|
||||
assert.Equal(t, tc.digest, digest)
|
||||
@@ -25,7 +25,7 @@ func IsRemoteFile(path string) bool {
|
||||
return err == nil && (u.Scheme == "http" || u.Scheme == "https")
|
||||
}
|
||||
|
||||
// fileLoader is a kustomization's interface to files.
|
||||
// FileLoader is a kustomization's interface to files.
|
||||
//
|
||||
// The directory in which a kustomization file sits
|
||||
// is referred to below as the kustomization's _root_.
|
||||
@@ -38,49 +38,48 @@ func IsRemoteFile(path string) bool {
|
||||
//
|
||||
// * supplemental data paths
|
||||
//
|
||||
// `Load` is used to visit these paths.
|
||||
// `Load` is used to visit these paths.
|
||||
//
|
||||
// These paths refer to resources, patches,
|
||||
// data for ConfigMaps and Secrets, etc.
|
||||
// These paths refer to resources, patches,
|
||||
// data for ConfigMaps and Secrets, etc.
|
||||
//
|
||||
// The loadRestrictor may disallow certain paths
|
||||
// or classes of paths.
|
||||
// The loadRestrictor may disallow certain paths
|
||||
// or classes of paths.
|
||||
//
|
||||
// * bases (other kustomizations)
|
||||
//
|
||||
// `New` is used to load bases.
|
||||
// `New` is used to load bases.
|
||||
//
|
||||
// A base can be either a remote git repo URL, or
|
||||
// a directory specified relative to the current
|
||||
// root. In the former case, the repo is locally
|
||||
// cloned, and the new loader is rooted on a path
|
||||
// in that clone.
|
||||
// A base can be either a remote git repo URL, or
|
||||
// a directory specified relative to the current
|
||||
// root. In the former case, the repo is locally
|
||||
// cloned, and the new loader is rooted on a path
|
||||
// in that clone.
|
||||
//
|
||||
// As loaders create new loaders, a root history
|
||||
// is established, and used to disallow:
|
||||
// As loaders create new loaders, a root history
|
||||
// is established, and used to disallow:
|
||||
//
|
||||
// - A base that is a repository that, in turn,
|
||||
// specifies a base repository seen previously
|
||||
// in the loading stack (a cycle).
|
||||
// - A base that is a repository that, in turn,
|
||||
// specifies a base repository seen previously
|
||||
// in the loading stack (a cycle).
|
||||
//
|
||||
// - An overlay depending on a base positioned at
|
||||
// or above it. I.e. '../foo' is OK, but '.',
|
||||
// '..', '../..', etc. are disallowed. Allowing
|
||||
// such a base has no advantages and encourages
|
||||
// cycles, particularly if some future change
|
||||
// were to introduce globbing to file
|
||||
// specifications in the kustomization file.
|
||||
// - An overlay depending on a base positioned at
|
||||
// or above it. I.e. '../foo' is OK, but '.',
|
||||
// '..', '../..', etc. are disallowed. Allowing
|
||||
// such a base has no advantages and encourages
|
||||
// cycles, particularly if some future change
|
||||
// were to introduce globbing to file
|
||||
// specifications in the kustomization file.
|
||||
//
|
||||
// These restrictions assure that kustomizations
|
||||
// are self-contained and relocatable, and impose
|
||||
// some safety when relying on remote kustomizations,
|
||||
// e.g. a remotely loaded ConfigMap generator specified
|
||||
// to read from /etc/passwd will fail.
|
||||
//
|
||||
type fileLoader struct {
|
||||
type FileLoader struct {
|
||||
// Loader that spawned this loader.
|
||||
// Used to avoid cycles.
|
||||
referrer *fileLoader
|
||||
referrer *FileLoader
|
||||
|
||||
// An absolute, cleaned path to a directory.
|
||||
// The Load function will read non-absolute
|
||||
@@ -107,23 +106,9 @@ type fileLoader struct {
|
||||
cleaner func() error
|
||||
}
|
||||
|
||||
// NewFileLoaderAtCwd returns a loader that loads from PWD.
|
||||
// A convenience for kustomize edit commands.
|
||||
func NewFileLoaderAtCwd(fSys filesys.FileSystem) *fileLoader {
|
||||
return newLoaderOrDie(
|
||||
RestrictionRootOnly, fSys, filesys.SelfDir)
|
||||
}
|
||||
|
||||
// NewFileLoaderAtRoot returns a loader that loads from "/".
|
||||
// A convenience for tests.
|
||||
func NewFileLoaderAtRoot(fSys filesys.FileSystem) *fileLoader {
|
||||
return newLoaderOrDie(
|
||||
RestrictionRootOnly, fSys, filesys.Separator)
|
||||
}
|
||||
|
||||
// Repo returns the absolute path to the repo that contains Root if this fileLoader was created from a url
|
||||
// or the empty string otherwise.
|
||||
func (fl *fileLoader) Repo() string {
|
||||
func (fl *FileLoader) Repo() string {
|
||||
if fl.repoSpec != nil {
|
||||
return fl.repoSpec.Dir.String()
|
||||
}
|
||||
@@ -132,13 +117,13 @@ func (fl *fileLoader) Repo() string {
|
||||
|
||||
// Root returns the absolute path that is prepended to any
|
||||
// relative paths used in Load.
|
||||
func (fl *fileLoader) Root() string {
|
||||
func (fl *FileLoader) Root() string {
|
||||
return fl.root.String()
|
||||
}
|
||||
|
||||
func newLoaderOrDie(
|
||||
func NewLoaderOrDie(
|
||||
lr LoadRestrictorFunc,
|
||||
fSys filesys.FileSystem, path string) *fileLoader {
|
||||
fSys filesys.FileSystem, path string) *FileLoader {
|
||||
root, err := filesys.ConfirmDir(fSys, path)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to make loader at '%s'; %v", path, err)
|
||||
@@ -147,12 +132,12 @@ func newLoaderOrDie(
|
||||
lr, root, fSys, nil, git.ClonerUsingGitExec)
|
||||
}
|
||||
|
||||
// newLoaderAtConfirmedDir returns a new fileLoader with given root.
|
||||
// newLoaderAtConfirmedDir returns a new FileLoader with given root.
|
||||
func newLoaderAtConfirmedDir(
|
||||
lr LoadRestrictorFunc,
|
||||
root filesys.ConfirmedDir, fSys filesys.FileSystem,
|
||||
referrer *fileLoader, cloner git.Cloner) *fileLoader {
|
||||
return &fileLoader{
|
||||
referrer *FileLoader, cloner git.Cloner) *FileLoader {
|
||||
return &FileLoader{
|
||||
loadRestrictor: lr,
|
||||
root: root,
|
||||
referrer: referrer,
|
||||
@@ -164,7 +149,7 @@ func newLoaderAtConfirmedDir(
|
||||
|
||||
// New returns a new Loader, rooted relative to current loader,
|
||||
// or rooted in a temp directory holding a git repo clone.
|
||||
func (fl *fileLoader) New(path string) (ifc.Loader, error) {
|
||||
func (fl *FileLoader) New(path string) (ifc.Loader, error) {
|
||||
if path == "" {
|
||||
return nil, errors.Errorf("new root cannot be empty")
|
||||
}
|
||||
@@ -200,7 +185,7 @@ func (fl *fileLoader) New(path string) (ifc.Loader, error) {
|
||||
// directory holding a cloned git repo.
|
||||
func newLoaderAtGitClone(
|
||||
repoSpec *git.RepoSpec, fSys filesys.FileSystem,
|
||||
referrer *fileLoader, cloner git.Cloner) (ifc.Loader, error) {
|
||||
referrer *FileLoader, cloner git.Cloner) (ifc.Loader, error) {
|
||||
cleaner := repoSpec.Cleaner(fSys)
|
||||
err := cloner(repoSpec)
|
||||
if err != nil {
|
||||
@@ -229,7 +214,7 @@ func newLoaderAtGitClone(
|
||||
return nil, fmt.Errorf("%q refers to directory outside of repo %q", repoSpec.AbsPath(),
|
||||
repoSpec.CloneDir())
|
||||
}
|
||||
return &fileLoader{
|
||||
return &FileLoader{
|
||||
// Clones never allowed to escape root.
|
||||
loadRestrictor: RestrictionRootOnly,
|
||||
root: root,
|
||||
@@ -241,7 +226,7 @@ func newLoaderAtGitClone(
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (fl *fileLoader) errIfGitContainmentViolation(
|
||||
func (fl *FileLoader) errIfGitContainmentViolation(
|
||||
base filesys.ConfirmedDir) error {
|
||||
containingRepo := fl.containingRepo()
|
||||
if containingRepo == nil {
|
||||
@@ -259,7 +244,7 @@ func (fl *fileLoader) errIfGitContainmentViolation(
|
||||
|
||||
// Looks back through referrers for a git repo, returning nil
|
||||
// if none found.
|
||||
func (fl *fileLoader) containingRepo() *git.RepoSpec {
|
||||
func (fl *FileLoader) containingRepo() *git.RepoSpec {
|
||||
if fl.repoSpec != nil {
|
||||
return fl.repoSpec
|
||||
}
|
||||
@@ -271,7 +256,7 @@ func (fl *fileLoader) containingRepo() *git.RepoSpec {
|
||||
|
||||
// errIfArgEqualOrHigher tests whether the argument,
|
||||
// is equal to or above the root of any ancestor.
|
||||
func (fl *fileLoader) errIfArgEqualOrHigher(
|
||||
func (fl *FileLoader) errIfArgEqualOrHigher(
|
||||
candidateRoot filesys.ConfirmedDir) error {
|
||||
if fl.root.HasPrefix(candidateRoot) {
|
||||
return fmt.Errorf(
|
||||
@@ -288,7 +273,7 @@ func (fl *fileLoader) errIfArgEqualOrHigher(
|
||||
// I.e. Allow a distinction between git URI with
|
||||
// path foo and tag bar and a git URI with the same
|
||||
// path but a different tag?
|
||||
func (fl *fileLoader) errIfRepoCycle(newRepoSpec *git.RepoSpec) error {
|
||||
func (fl *FileLoader) errIfRepoCycle(newRepoSpec *git.RepoSpec) error {
|
||||
// TODO(monopole): Use parsed data instead of Raw().
|
||||
if fl.repoSpec != nil &&
|
||||
strings.HasPrefix(fl.repoSpec.Raw(), newRepoSpec.Raw()) {
|
||||
@@ -305,7 +290,7 @@ func (fl *fileLoader) errIfRepoCycle(newRepoSpec *git.RepoSpec) error {
|
||||
// Load returns the content of file at the given path,
|
||||
// else an error. Relative paths are taken relative
|
||||
// to the root.
|
||||
func (fl *fileLoader) Load(path string) ([]byte, error) {
|
||||
func (fl *FileLoader) Load(path string) ([]byte, error) {
|
||||
if IsRemoteFile(path) {
|
||||
return fl.httpClientGetContent(path)
|
||||
}
|
||||
@@ -319,7 +304,7 @@ func (fl *fileLoader) Load(path string) ([]byte, error) {
|
||||
return fl.fSys.ReadFile(path)
|
||||
}
|
||||
|
||||
func (fl *fileLoader) httpClientGetContent(path string) ([]byte, error) {
|
||||
func (fl *FileLoader) httpClientGetContent(path string) ([]byte, error) {
|
||||
var hc *http.Client
|
||||
if fl.http != nil {
|
||||
hc = fl.http
|
||||
@@ -344,6 +329,6 @@ func (fl *fileLoader) httpClientGetContent(path string) ([]byte, error) {
|
||||
}
|
||||
|
||||
// Cleanup runs the cleaner.
|
||||
func (fl *fileLoader) Cleanup() error {
|
||||
func (fl *FileLoader) Cleanup() error {
|
||||
return fl.cleaner()
|
||||
}
|
||||
@@ -31,6 +31,11 @@ func TestIsRemoteFile(t *testing.T) {
|
||||
"https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/examples/helloWorld/configMap.yaml",
|
||||
true,
|
||||
},
|
||||
"malformed https": {
|
||||
// TODO(annasong): Maybe we want to fix this. Needs more research.
|
||||
"https:/raw.githubusercontent.com/kubernetes-sigs/kustomize/master/examples/helloWorld/configMap.yaml",
|
||||
true,
|
||||
},
|
||||
"https dir": {
|
||||
"https://github.com/kubernetes-sigs/kustomize//examples/helloWorld/",
|
||||
true,
|
||||
@@ -88,8 +93,9 @@ func MakeFakeFs(td []testData) filesys.FileSystem {
|
||||
return fSys
|
||||
}
|
||||
|
||||
func makeLoader() *fileLoader {
|
||||
return NewFileLoaderAtRoot(MakeFakeFs(testCases))
|
||||
func makeLoader() *FileLoader {
|
||||
return NewLoaderOrDie(
|
||||
RestrictionRootOnly, MakeFakeFs(testCases), filesys.Separator)
|
||||
}
|
||||
|
||||
func TestLoaderLoad(t *testing.T) {
|
||||
@@ -201,23 +207,44 @@ func TestNewEmptyLoader(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestNewLoaderHTTP(t *testing.T) {
|
||||
t.Run("doesn't exist", func(t *testing.T) {
|
||||
_, err := makeLoader().New("https://google.com/project")
|
||||
require.Error(t, err)
|
||||
})
|
||||
// Though it is unlikely and we do not weigh this use case in our designs,
|
||||
// it is possible for a reference that is considered a remote file to
|
||||
// actually be a directory
|
||||
t.Run("exists", func(t *testing.T) {
|
||||
const remoteFileLikeRoot = "https://domain"
|
||||
require.True(t, IsRemoteFile(remoteFileLikeRoot))
|
||||
func TestNewRemoteLoaderDoesNotExist(t *testing.T) {
|
||||
_, err := makeLoader().New("https://example.com/org/repo")
|
||||
require.ErrorContains(t, err, "fetch")
|
||||
}
|
||||
|
||||
func TestLoaderLocalScheme(t *testing.T) {
|
||||
// It is unlikely but possible for a reference with a url scheme to
|
||||
// actually refer to a local file or directory.
|
||||
t.Run("file", func(t *testing.T) {
|
||||
fSys, dir := setupOnDisk(t)
|
||||
require.NoError(t, fSys.MkdirAll(dir.Join("https:/domain")))
|
||||
ldr, err := newLoaderOrDie(RestrictionRootOnly,
|
||||
parts := []string{
|
||||
"ssh:",
|
||||
"resource.yaml",
|
||||
}
|
||||
require.NoError(t, fSys.Mkdir(dir.Join(parts[0])))
|
||||
const content = "resource config"
|
||||
require.NoError(t, fSys.WriteFile(
|
||||
dir.Join(filepath.Join(parts...)),
|
||||
[]byte(content),
|
||||
))
|
||||
actualContent, err := NewLoaderOrDie(RestrictionRootOnly,
|
||||
fSys,
|
||||
dir.String(),
|
||||
).New(remoteFileLikeRoot)
|
||||
).Load(strings.Join(parts, "//"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, content, string(actualContent))
|
||||
})
|
||||
t.Run("directory", func(t *testing.T) {
|
||||
fSys, dir := setupOnDisk(t)
|
||||
parts := []string{
|
||||
"https:",
|
||||
"root",
|
||||
}
|
||||
require.NoError(t, fSys.MkdirAll(dir.Join(filepath.Join(parts...))))
|
||||
ldr, err := NewLoaderOrDie(RestrictionRootOnly,
|
||||
fSys,
|
||||
dir.String(),
|
||||
).New(strings.Join(parts, "//"))
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, ldr.Repo())
|
||||
})
|
||||
@@ -296,7 +323,7 @@ func TestRestrictionRootOnlyInRealLoader(t *testing.T) {
|
||||
|
||||
var l ifc.Loader
|
||||
|
||||
l = newLoaderOrDie(RestrictionRootOnly, fSys, dir)
|
||||
l = NewLoaderOrDie(RestrictionRootOnly, fSys, dir)
|
||||
|
||||
l = doSanityChecksAndDropIntoBase(t, l)
|
||||
|
||||
@@ -317,7 +344,7 @@ func TestRestrictionNoneInRealLoader(t *testing.T) {
|
||||
|
||||
var l ifc.Loader
|
||||
|
||||
l = newLoaderOrDie(RestrictionNone, fSys, dir)
|
||||
l = NewLoaderOrDie(RestrictionNone, fSys, dir)
|
||||
|
||||
l = doSanityChecksAndDropIntoBase(t, l)
|
||||
|
||||
@@ -416,7 +443,7 @@ func TestLoaderDisallowsLocalBaseFromRemoteOverlay(t *testing.T) {
|
||||
|
||||
// Establish that a local overlay can navigate
|
||||
// to the local bases.
|
||||
l1 = newLoaderOrDie(
|
||||
l1 = NewLoaderOrDie(
|
||||
RestrictionRootOnly, fSys, cloneRoot+"/foo/overlay")
|
||||
require.Equal(cloneRoot+"/foo/overlay", l1.Root())
|
||||
|
||||
@@ -574,7 +601,8 @@ func TestLoaderHTTP(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
l1 := NewFileLoaderAtRoot(MakeFakeFs(testCasesFile))
|
||||
l1 := NewLoaderOrDie(
|
||||
RestrictionRootOnly, MakeFakeFs(testCasesFile), filesys.Separator)
|
||||
require.Equal("/", l1.Root())
|
||||
|
||||
for _, x := range testCasesFile {
|
||||
@@ -635,6 +663,8 @@ func TestLoaderHTTP(t *testing.T) {
|
||||
|
||||
// setupOnDisk sets up a file system on disk and directory that is cleaned after
|
||||
// test completion.
|
||||
// TODO(annasong): Move all loader tests that require real file system into
|
||||
// api/krusty.
|
||||
func setupOnDisk(t *testing.T) (filesys.FileSystem, filesys.ConfirmedDir) {
|
||||
t.Helper()
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/internal/generators"
|
||||
"sigs.k8s.io/kustomize/api/internal/loader"
|
||||
"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"
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/internal/git"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/api/internal/loader"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/api/internal/loader"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"sort"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts"
|
||||
"sigs.k8s.io/kustomize/api/internal/konfig/builtinpluginconsts"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
)
|
||||
|
||||
@@ -10,10 +10,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
fLdr "sigs.k8s.io/kustomize/api/internal/loader"
|
||||
. "sigs.k8s.io/kustomize/api/internal/plugins/execplugin"
|
||||
pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/utils"
|
||||
fLdr "sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/api/provider"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"sigs.k8s.io/kustomize/api/internal/loader"
|
||||
. "sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/api/provider"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
|
||||
@@ -13,12 +13,12 @@ import (
|
||||
"sigs.k8s.io/kustomize/api/internal/accumulator"
|
||||
"sigs.k8s.io/kustomize/api/internal/builtins"
|
||||
"sigs.k8s.io/kustomize/api/internal/kusterr"
|
||||
load "sigs.k8s.io/kustomize/api/internal/loader"
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
||||
"sigs.k8s.io/kustomize/api/internal/utils"
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
load "sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"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/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/api/provider"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
|
||||
@@ -6,9 +6,9 @@ package target_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
fLdr "sigs.k8s.io/kustomize/api/internal/loader"
|
||||
pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
||||
"sigs.k8s.io/kustomize/api/internal/target"
|
||||
fLdr "sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/api/provider"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||
|
||||
@@ -17,6 +17,18 @@ import (
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
)
|
||||
|
||||
const validResource = `
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: myService
|
||||
spec:
|
||||
selector:
|
||||
backend: bungie
|
||||
ports:
|
||||
- port: 7002
|
||||
`
|
||||
|
||||
func TestTargetMustHaveKustomizationFile(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteF("service.yaml", `
|
||||
@@ -63,17 +75,7 @@ func TestBaseMustHaveKustomizationFile(t *testing.T) {
|
||||
resources:
|
||||
- base
|
||||
`)
|
||||
th.WriteF("base/service.yaml", `
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: myService
|
||||
spec:
|
||||
selector:
|
||||
backend: bungie
|
||||
ports:
|
||||
- port: 7002
|
||||
`)
|
||||
th.WriteF("base/service.yaml", validResource)
|
||||
err := th.RunWithErr(".", th.MakeDefaultOptions())
|
||||
if err == nil {
|
||||
t.Fatalf("expected an error")
|
||||
@@ -167,12 +169,35 @@ spec:
|
||||
|
||||
func TestAccumulateResourcesErrors(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
resource string
|
||||
// regex error message that is output when kustomize tries to
|
||||
// accumulate resource as file and dir, respectively
|
||||
name string
|
||||
resource string
|
||||
isAbsolute bool
|
||||
files map[string]string
|
||||
// errFile, errDir are regex for the expected error message output
|
||||
// when kustomize tries to accumulate resource as file and dir,
|
||||
// respectively. The test substitutes occurrences of "%s" in the
|
||||
// error strings with the absolute path where kustomize looks for it.
|
||||
errFile, errDir string
|
||||
}
|
||||
populateAbsolutePaths := func(tc testcase, dir string) testcase {
|
||||
filePaths := make(map[string]string, len(tc.files)+1)
|
||||
for file, content := range tc.files {
|
||||
filePaths[filepath.Join(dir, file)] = content
|
||||
}
|
||||
resourcePath := filepath.Join(dir, tc.resource)
|
||||
if tc.isAbsolute {
|
||||
tc.resource = resourcePath
|
||||
}
|
||||
filePaths[filepath.Join(dir, "kustomization.yaml")] = fmt.Sprintf(`
|
||||
resources:
|
||||
- %s
|
||||
`, tc.resource)
|
||||
tc.files = filePaths
|
||||
regPath := regexp.QuoteMeta(resourcePath)
|
||||
tc.errFile = strings.ReplaceAll(tc.errFile, "%s", regPath)
|
||||
tc.errDir = strings.ReplaceAll(tc.errDir, "%s", regPath)
|
||||
return tc
|
||||
}
|
||||
buildError := func(tc testcase) string {
|
||||
const (
|
||||
prefix = "accumulating resources"
|
||||
@@ -196,39 +221,89 @@ func TestAccumulateResourcesErrors(t *testing.T) {
|
||||
for _, test := range []testcase{
|
||||
{
|
||||
name: "remote file not considered repo",
|
||||
// This url is too short to be considered a remote repo.
|
||||
resource: "https://raw.githubusercontent.com/kustomize",
|
||||
// It is acceptable that the error for a remote file-like reference
|
||||
// (that is not long enough to be considered a repo) does not
|
||||
// indicate said reference is not a local directory.
|
||||
// Though it is possible for the remote file-like reference to be
|
||||
// a local directory, it is very unlikely.
|
||||
errFile: `HTTP Error: status code 400 \(Bad Request\)\z`,
|
||||
// The example.com second-level domain is reserved and
|
||||
// safe to access, see RFC 2606.
|
||||
resource: "https://example.com/segments-too-few-to-be-repo",
|
||||
// It's acceptable for the error output of a remote file-like
|
||||
// resource to not indicate the resource's status as a
|
||||
// local directory. Though it is possible for a remote file-like
|
||||
// resource to be a local directory, it is very unlikely.
|
||||
errFile: `HTTP Error: status code 404 \(Not Found\)\z`,
|
||||
},
|
||||
{
|
||||
name: "remote file qualifies as repo",
|
||||
resource: "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/kustomize/v5.0.0/examples/helloWorld/configMap",
|
||||
resource: "https://example.com/long/enough/to/have/org/and/repo",
|
||||
// TODO(4788): This error message is technically wrong. Just
|
||||
// because we fail to GET a reference does not mean the reference is
|
||||
// not a remote file. We should return the GET status code instead.
|
||||
// because we fail to GET a resource does not mean the resource is
|
||||
// not a remote file. We should return the GET status code as well.
|
||||
errFile: "URL is a git repository",
|
||||
errDir: `failed to run \S+/git fetch --depth=1 .+`,
|
||||
},
|
||||
{
|
||||
name: "local file qualifies as repo",
|
||||
// The .example top level domain is reserved for example purposes,
|
||||
// see RFC 2606.
|
||||
resource: "package@v1.28.0.example/configs/base",
|
||||
errFile: `evalsymlink failure on '%s' .+`,
|
||||
errDir: `failed to run \S+/git fetch --depth=1 .+`,
|
||||
},
|
||||
{
|
||||
name: "relative path does not exist",
|
||||
resource: "file-or-directory",
|
||||
errFile: `evalsymlink failure on '%s' .+`,
|
||||
errDir: `must build at directory: not a valid directory: evalsymlink failure .+`,
|
||||
},
|
||||
{
|
||||
name: "absolute path does not exist",
|
||||
resource: "file-or-directory",
|
||||
isAbsolute: true,
|
||||
errFile: `evalsymlink failure on '%s' .+`,
|
||||
errDir: `new root '%s' cannot be absolute`,
|
||||
},
|
||||
{
|
||||
name: "relative file violates restrictions",
|
||||
resource: "../base/resource.yaml",
|
||||
files: map[string]string{
|
||||
"../base/resource.yaml": validResource,
|
||||
},
|
||||
errFile: "security; file '%s' is not in or below .+",
|
||||
// TODO(4348): Over-inclusion of directory error message when we
|
||||
// know resource is file.
|
||||
errDir: "must build at directory: '%s': file is not directory",
|
||||
},
|
||||
{
|
||||
name: "absolute file violates restrictions",
|
||||
resource: "../base/resource.yaml",
|
||||
isAbsolute: true,
|
||||
files: map[string]string{
|
||||
"../base/resource.yaml": validResource,
|
||||
},
|
||||
errFile: "security; file '%s' is not in or below .+",
|
||||
// TODO(4348): Over-inclusion of directory error message when we
|
||||
// know resource is file.
|
||||
errDir: `new root '%s' cannot be absolute`,
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// should use real file system to indicate that we are creating
|
||||
// new temporary directories on disk when we attempt to fetch repos
|
||||
fSys, tmpDir := kusttest_test.CreateKustDir(t, fmt.Sprintf(`
|
||||
resources:
|
||||
- %s
|
||||
`, test.resource))
|
||||
// Should use real file system to indicate that we are creating
|
||||
// new temporary directories on disk when we attempt to fetch repos.
|
||||
fs, tmpDir := kusttest_test.Setup(t)
|
||||
root := tmpDir.Join("root")
|
||||
require.NoError(t, fs.Mkdir(root))
|
||||
|
||||
test = populateAbsolutePaths(test, root)
|
||||
for file, content := range test.files {
|
||||
dir := filepath.Dir(file)
|
||||
require.NoError(t, fs.MkdirAll(dir))
|
||||
require.NoError(t, fs.WriteFile(file, []byte(content)))
|
||||
}
|
||||
|
||||
b := krusty.MakeKustomizer(krusty.MakeDefaultOptions())
|
||||
_, err := b.Run(fSys, tmpDir.String())
|
||||
_, err := b.Run(fs, root)
|
||||
require.Regexp(t, buildError(test), err.Error())
|
||||
})
|
||||
}
|
||||
// TODO(annasong): add tests that check accumulateResources errors for
|
||||
// - local files
|
||||
// - repos
|
||||
// - local directories
|
||||
// - files that yield malformed yaml errors
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/internal/loader"
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ import (
|
||||
"log"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/internal/builtins"
|
||||
fLdr "sigs.k8s.io/kustomize/api/internal/loader"
|
||||
pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
||||
"sigs.k8s.io/kustomize/api/internal/target"
|
||||
"sigs.k8s.io/kustomize/api/internal/utils"
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
fLdr "sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/api/provenance"
|
||||
"sigs.k8s.io/kustomize/api/provider"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
|
||||
@@ -6,8 +6,6 @@ package krusty_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
)
|
||||
|
||||
@@ -303,20 +301,15 @@ patchesStrategicMerge:
|
||||
m = th.Run("overlay", th.MakeDefaultOptions())
|
||||
th.AssertActualEqualsExpected(m, expected)
|
||||
|
||||
// Technique 4: "patches:" field, one patch file. Fails.
|
||||
// Technique 4: "patches:" field, one patch file.
|
||||
th.WriteK("overlay", `
|
||||
resources:
|
||||
- ../base
|
||||
patches:
|
||||
- path: twoPatchesInOneFile.yaml
|
||||
`)
|
||||
err := th.RunWithErr("overlay", th.MakeDefaultOptions())
|
||||
assert.Error(t, err)
|
||||
// This should fail, because the semantics of the `patches` field.
|
||||
// That field allows specific patch targeting to a list of targets,
|
||||
// while the `patchesStrategicMerge` field accepts patches that
|
||||
// implicitly identify their targets via GVKN.
|
||||
assert.Contains(t, err.Error(), "unable to parse SM or JSON patch from ")
|
||||
m = th.Run("overlay", th.MakeDefaultOptions())
|
||||
th.AssertActualEqualsExpected(m, expected)
|
||||
}
|
||||
|
||||
func TestRemoveEmptyDirWithNullFieldInSmp(t *testing.T) {
|
||||
@@ -1670,7 +1663,7 @@ spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: fluentd:latest
|
||||
- image: fluentd:latest
|
||||
name: fluentd
|
||||
serviceAccountName: fluentd-sa
|
||||
---
|
||||
|
||||
@@ -10,13 +10,14 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"sigs.k8s.io/kustomize/api/internal/loader"
|
||||
"sigs.k8s.io/kustomize/api/krusty"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
@@ -27,6 +28,7 @@ func TestRemoteLoad_LocalProtocol(t *testing.T) {
|
||||
root string
|
||||
simple string
|
||||
noSuffix string
|
||||
hash string
|
||||
multiBaseDev string
|
||||
withSubmodule string
|
||||
}
|
||||
@@ -35,9 +37,10 @@ func TestRemoteLoad_LocalProtocol(t *testing.T) {
|
||||
// root/
|
||||
// simple.git/ - base with just a pod
|
||||
// nosuffix/ - same as simple.git/ without the .git suffix
|
||||
// hash-xx/ - same as simple.git/ with random hash at xx
|
||||
// multibase.git/ - base with a dev overlay
|
||||
// with-submodule.git/ - includes `simple` as a submodule
|
||||
// submodule/ - the submodule referencing `simple
|
||||
// submodule/ - the submodule referencing `simple`
|
||||
createGitRepos := func(t *testing.T) testRepos {
|
||||
t.Helper()
|
||||
|
||||
@@ -49,10 +52,16 @@ func TestRemoteLoad_LocalProtocol(t *testing.T) {
|
||||
}
|
||||
}
|
||||
root := t.TempDir()
|
||||
|
||||
hashPath, err := os.MkdirTemp(root, "hash-")
|
||||
require.NoError(t, err)
|
||||
hashDir := filepath.Base(hashPath)
|
||||
|
||||
bash(fmt.Sprintf(`
|
||||
set -eux
|
||||
|
||||
export ROOT="%s"
|
||||
export HASH_DIR="%s"
|
||||
export GIT_AUTHOR_EMAIL=nobody@kustomize.io
|
||||
export GIT_AUTHOR_NAME=Nobody
|
||||
export GIT_COMMITTER_EMAIL=nobody@kustomize.io
|
||||
@@ -84,19 +93,27 @@ cp -r testdata/remoteload/multibase $ROOT/multibase.git
|
||||
git add .
|
||||
git commit -m "import"
|
||||
)
|
||||
cp -r testdata/remoteload/with-submodule $ROOT/with-submodule.git # see README
|
||||
cp -r $ROOT/simple.git/. $ROOT/$HASH_DIR
|
||||
(
|
||||
mkdir $ROOT/with-submodule.git
|
||||
cd $ROOT/with-submodule.git
|
||||
git init --initial-branch=main
|
||||
git submodule add $ROOT/simple.git submodule
|
||||
git add .
|
||||
git commit -m "import"
|
||||
git checkout -b relative-submodule
|
||||
git submodule add ../$HASH_DIR submodule
|
||||
git commit -m "relative submodule"
|
||||
git checkout main
|
||||
git submodule add $ROOT/simple.git submodule
|
||||
git commit -m "submodule"
|
||||
)
|
||||
`, root))
|
||||
`, root, hashDir))
|
||||
return testRepos{
|
||||
root: root,
|
||||
// The strings below aren't currently used, and more serve as documentation.
|
||||
simple: "simple.git",
|
||||
noSuffix: "nosuffix",
|
||||
hash: hashDir,
|
||||
multiBaseDev: "multibase.git",
|
||||
withSubmodule: "with-submodule.git",
|
||||
}
|
||||
@@ -182,6 +199,15 @@ resources:
|
||||
`,
|
||||
expected: simpleBuild,
|
||||
},
|
||||
{
|
||||
name: "has relative submodule",
|
||||
kustomization: `
|
||||
resources:
|
||||
- file://$ROOT/with-submodule.git/submodule?ref=relative-submodule
|
||||
`,
|
||||
// TODO(annasong): Replace with simpleBuild once #5131 is fixed.
|
||||
err: `failed to run '\S+/git submodule update --init --recursive'`,
|
||||
},
|
||||
{
|
||||
name: "has timeout",
|
||||
kustomization: `
|
||||
@@ -272,7 +298,7 @@ resources:
|
||||
|
||||
if test.err != "" {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), test.err)
|
||||
require.Regexp(t, test.err, err.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
checkYaml(t, m, strings.ReplaceAll(test.expected, "$ROOT", repos.root))
|
||||
|
||||
@@ -546,3 +546,71 @@ metadata:
|
||||
name: red-dc6gc5btkc
|
||||
`)
|
||||
}
|
||||
|
||||
func TestReplacementTransformerWithSuffixTransformerAndReject(t *testing.T) {
|
||||
th := kusttest_test.MakeEnhancedHarness(t)
|
||||
defer th.Reset()
|
||||
|
||||
th.WriteF("base/app.yaml", `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: original-name
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: app1:1.0
|
||||
name: app
|
||||
`)
|
||||
th.WriteK("base", `
|
||||
resources:
|
||||
- app.yaml
|
||||
`)
|
||||
th.WriteK("overlay", `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
nameSuffix: -dev
|
||||
resources:
|
||||
- ../base
|
||||
|
||||
configMapGenerator:
|
||||
- name: app-config
|
||||
literals:
|
||||
- name=something-else
|
||||
|
||||
replacements:
|
||||
- source:
|
||||
kind: ConfigMap
|
||||
name: app-config
|
||||
fieldPath: data.name
|
||||
targets:
|
||||
- fieldPaths:
|
||||
- spec.template.spec.containers.0.name
|
||||
select:
|
||||
kind: Deployment
|
||||
reject:
|
||||
- name: original-name
|
||||
`)
|
||||
m := th.Run("overlay", th.MakeDefaultOptions())
|
||||
th.AssertActualEqualsExpected(m, `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: original-name-dev
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: app1:1.0
|
||||
name: app
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
name: something-else
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: app-config-dev-97544dk6t8
|
||||
`)
|
||||
}
|
||||
|
||||
10
api/krusty/testdata/remoteload/with-submodule/README.md
vendored
Normal file
10
api/krusty/testdata/remoteload/with-submodule/README.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# submodule
|
||||
|
||||
This repo demonstrates kustomize's ability to download git repos
|
||||
with submodules. The following branches contain
|
||||
* main: submodule via absolute path
|
||||
* relative-submodule: submodule via relative path
|
||||
|
||||
For the submodule accessed via a relative path, we include a random hash in the
|
||||
submodule name to avoid accessing an unintended directory in the case kustomize
|
||||
contains loader bugs (issue #5131).
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
ldr "sigs.k8s.io/kustomize/api/loader"
|
||||
ldr "sigs.k8s.io/kustomize/api/pkg/loader"
|
||||
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
|
||||
24
api/pkg/loader/loader.go
Normal file
24
api/pkg/loader/loader.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2023 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package pkg has all the helpers to interact with the api.
|
||||
package loader
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/api/internal/loader"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
)
|
||||
|
||||
// NewFileLoaderAtCwd returns a loader that loads from PWD.
|
||||
// A convenience for kustomize edit commands.
|
||||
func NewFileLoaderAtCwd(fSys filesys.FileSystem) *loader.FileLoader {
|
||||
return loader.NewLoaderOrDie(
|
||||
loader.RestrictionRootOnly, fSys, filesys.SelfDir)
|
||||
}
|
||||
|
||||
// NewFileLoaderAtRoot returns a loader that loads from "/".
|
||||
// A convenience for tests.
|
||||
func NewFileLoaderAtRoot(fSys filesys.FileSystem) *loader.FileLoader {
|
||||
return loader.NewLoaderOrDie(
|
||||
loader.RestrictionRootOnly, fSys, filesys.Separator)
|
||||
}
|
||||
@@ -9,8 +9,9 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
loader "sigs.k8s.io/kustomize/api/internal/loader"
|
||||
"sigs.k8s.io/kustomize/api/kv"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
ldrpkg "sigs.k8s.io/kustomize/api/pkg/loader"
|
||||
. "sigs.k8s.io/kustomize/api/resmap"
|
||||
resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest"
|
||||
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||
@@ -249,7 +250,7 @@ func TestNewResMapFromSecretArgs(t *testing.T) {
|
||||
|
||||
actual, err := rmF.NewResMapFromSecretArgs(
|
||||
kv.NewLoader(
|
||||
loader.NewFileLoaderAtRoot(fSys),
|
||||
ldrpkg.NewFileLoaderAtRoot(fSys),
|
||||
valtest_test.MakeFakeValidator()), secrets)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
|
||||
@@ -696,8 +696,7 @@ func (m *resWrangler) DeAnchor() (err error) {
|
||||
}
|
||||
|
||||
// ApplySmPatch applies the patch, and errors on Id collisions.
|
||||
func (m *resWrangler) ApplySmPatch(
|
||||
selectedSet *resource.IdSet, patch *resource.Resource) error {
|
||||
func (m *resWrangler) ApplySmPatch(selectedSet *resource.IdSet, patch *resource.Resource) error {
|
||||
var list []*resource.Resource
|
||||
for _, res := range m.rList {
|
||||
if selectedSet.Contains(res.CurId()) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/api/internal/loader"
|
||||
. "sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/internal/konfig/builtinpluginconsts"
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
"sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts"
|
||||
"sigs.k8s.io/kustomize/api/krusty"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
fLdr "sigs.k8s.io/kustomize/api/internal/loader"
|
||||
pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
fLdr "sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/api/provider"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||
|
||||
@@ -11,19 +11,27 @@ import (
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
)
|
||||
|
||||
// Setup sets up a file system on disk and directory that is cleaned after
|
||||
// test completion.
|
||||
func Setup(t *testing.T) (filesys.FileSystem, filesys.ConfirmedDir) {
|
||||
t.Helper()
|
||||
|
||||
fSys := filesys.MakeFsOnDisk()
|
||||
dir, err := filesys.NewTmpConfirmedDir()
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
_ = fSys.RemoveAll(dir.String())
|
||||
})
|
||||
return fSys, dir
|
||||
}
|
||||
|
||||
// CreateKustDir creates a file system on disk and a new directory
|
||||
// that holds a kustomization file with content. The directory is removed on
|
||||
// test completion.
|
||||
func CreateKustDir(t *testing.T, content string) (filesys.FileSystem, filesys.ConfirmedDir) {
|
||||
t.Helper()
|
||||
|
||||
fSys := filesys.MakeFsOnDisk()
|
||||
tmpDir, err := filesys.NewTmpConfirmedDir()
|
||||
require.NoError(t, err)
|
||||
fSys, tmpDir := Setup(t)
|
||||
require.NoError(t, fSys.WriteFile(filepath.Join(tmpDir.String(), "kustomization.yaml"), []byte(content)))
|
||||
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, fSys.RemoveAll(tmpDir.String()))
|
||||
})
|
||||
return fSys, tmpDir
|
||||
}
|
||||
|
||||
@@ -35,3 +35,5 @@ require (
|
||||
k8s.io/kube-openapi v0.0.0-20230601164746-7562a1006961 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
replace sigs.k8s.io/kustomize/kyaml => ../../kyaml
|
||||
|
||||
@@ -87,7 +87,5 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/kube-openapi v0.0.0-20230601164746-7562a1006961 h1:pqRVJGQJz6oeZby8qmPKXYIBjyrcv7EHCe/33UkZMYA=
|
||||
k8s.io/kube-openapi v0.0.0-20230601164746-7562a1006961/go.mod h1:l8HTwL5fqnlns4jOveW1L75eo7R9KFHxiE0bsPGy428=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.3 h1:WpabVAKZe2YEp/irTSHwD6bfjwZnTtSDewd2BVJGMZs=
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.3/go.mod h1:npvh9epWysfQ689Rtt/U+dpOJDTBn8kUnF1O6VzvmZA=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
||||
@@ -5,7 +5,7 @@ go 1.20
|
||||
require (
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
sigs.k8s.io/kustomize/api v0.13.4
|
||||
sigs.k8s.io/kustomize/api v0.14.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.3
|
||||
)
|
||||
|
||||
@@ -37,3 +37,5 @@ require (
|
||||
)
|
||||
|
||||
replace sigs.k8s.io/kustomize/api => ../../api
|
||||
|
||||
replace sigs.k8s.io/kustomize/kyaml => ../../kyaml
|
||||
|
||||
@@ -5,7 +5,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
|
||||
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=
|
||||
@@ -73,6 +72,7 @@ google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/evanphx/json-patch.v5 v5.6.0 h1:BMT6KIwBD9CaU91PJCZIe46bDmBWa9ynTQgJIOpfQBk=
|
||||
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=
|
||||
@@ -83,7 +83,5 @@ k8s.io/kube-openapi v0.0.0-20230601164746-7562a1006961/go.mod h1:l8HTwL5fqnlns4j
|
||||
k8s.io/utils v0.0.0-20230505201702-9f6742963106 h1:EObNQ3TW2D+WptiYXlApGNLVy0zm/JIBVY9i+M4wpAU=
|
||||
k8s.io/utils v0.0.0-20230505201702-9f6742963106/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.3 h1:WpabVAKZe2YEp/irTSHwD6bfjwZnTtSDewd2BVJGMZs=
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.3/go.mod h1:npvh9epWysfQ689Rtt/U+dpOJDTBn8kUnF1O6VzvmZA=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
||||
32
go.work.sum
32
go.work.sum
@@ -5,43 +5,57 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY
|
||||
github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk=
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
|
||||
github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo=
|
||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||
github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw=
|
||||
github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY=
|
||||
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY=
|
||||
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg=
|
||||
k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.11.3/go.mod h1:ENTZ8Ds12gewUpdxF5PJq/9qPVQFd5VPvMIL11wrBIU=
|
||||
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
set -e
|
||||
|
||||
KUSTOMIZE_EXEC=kustomize
|
||||
KUSTOMIZE_FLAGS="build --enable_alpha_plugins --enable-exec"
|
||||
KUSTOMIZE_FLAGS="build --enable-exec --enable-alpha-plugins"
|
||||
|
||||
function build_label_namespace_exec {
|
||||
cd label_namespace/execfn
|
||||
|
||||
@@ -5,7 +5,8 @@ FROM alpine:latest
|
||||
|
||||
ENV BUILD_HOME=/usr/local/build
|
||||
RUN apk update && apk add --no-cache git nodejs npm
|
||||
RUN npm install -g typescript
|
||||
COPY ["./package.json", "./package-lock.json", "./"]
|
||||
RUN npm install -g
|
||||
|
||||
RUN mkdir -p $BUILD_HOME
|
||||
|
||||
|
||||
16
hack/krmFunctionBenchmark/label_namespace/execfn/package-lock.json
generated
Normal file
16
hack/krmFunctionBenchmark/label_namespace/execfn/package-lock.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"help": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/help/-/help-3.0.2.tgz",
|
||||
"integrity": "sha512-jDd0MU+9xzvOQRC6CIzdjvb+agCvpzQY/Fp11quDnugDO4QQzh134EsLkRQMvFIJBleFkvnXagHFm4MTefkkpA=="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
||||
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w=="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "execfn",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
||||
BIN
images/base.jpg
BIN
images/base.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 40 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 73 KiB |
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
ldrhelper "sigs.k8s.io/kustomize/api/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/kustomize/v5/commands/internal/kustfile"
|
||||
"sigs.k8s.io/kustomize/kustomize/v5/commands/internal/util"
|
||||
@@ -61,7 +61,7 @@ func NewCmdCreate(fSys filesys.FileSystem, rf *resource.Factory) *cobra.Command
|
||||
&opts.namespace,
|
||||
"namespace",
|
||||
"",
|
||||
"Set the value of the namespace field in the customization file.")
|
||||
"Set the value of the namespace field in the kustomization file.")
|
||||
c.Flags().StringVar(
|
||||
&opts.annotations,
|
||||
"annotations",
|
||||
@@ -99,7 +99,7 @@ func runCreate(opts createFlags, fSys filesys.FileSystem, rf *resource.Factory)
|
||||
var resources []string
|
||||
var err error
|
||||
if opts.resources != "" {
|
||||
resources, err = util.GlobPatternsWithLoader(fSys, loader.NewFileLoaderAtCwd(fSys), strings.Split(opts.resources, ","))
|
||||
resources, err = util.GlobPatternsWithLoader(fSys, ldrhelper.NewFileLoaderAtCwd(fSys), strings.Split(opts.resources, ","))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/api/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/kustomize/v5/commands/internal/kustfile"
|
||||
"sigs.k8s.io/kustomize/kustomize/v5/commands/internal/util"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
|
||||
@@ -34,10 +34,11 @@ func (k kindOfAdd) String() string {
|
||||
}
|
||||
|
||||
type addMetadataOptions struct {
|
||||
force bool
|
||||
metadata map[string]string
|
||||
mapValidator func(map[string]string) error
|
||||
kind kindOfAdd
|
||||
force bool
|
||||
metadata map[string]string
|
||||
mapValidator func(map[string]string) error
|
||||
kind kindOfAdd
|
||||
labelsWithoutSelector bool
|
||||
}
|
||||
|
||||
// newCmdAddAnnotation adds one or more commonAnnotations to the kustomization file.
|
||||
@@ -79,6 +80,9 @@ func newCmdAddLabel(fSys filesys.FileSystem, v func(map[string]string) error) *c
|
||||
cmd.Flags().BoolVarP(&o.force, "force", "f", false,
|
||||
"overwrite commonLabel if it already exists",
|
||||
)
|
||||
cmd.Flags().BoolVar(&o.labelsWithoutSelector, "without-selector", false,
|
||||
"using add labels without selector option",
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -127,6 +131,10 @@ func (o *addMetadataOptions) addAnnotations(m *types.Kustomization) error {
|
||||
}
|
||||
|
||||
func (o *addMetadataOptions) addLabels(m *types.Kustomization) error {
|
||||
if o.labelsWithoutSelector {
|
||||
m.Labels = append(m.Labels, types.Label{Pairs: make(map[string]string), IncludeSelectors: false})
|
||||
return o.writeToMap(m.Labels[len(m.Labels)-1].Pairs, label)
|
||||
}
|
||||
if m.CommonLabels == nil {
|
||||
m.CommonLabels = make(map[string]string)
|
||||
}
|
||||
|
||||
@@ -274,3 +274,27 @@ func TestAddLabelForce(t *testing.T) {
|
||||
assert.NoError(t, cmd.RunE(cmd, args))
|
||||
v.VerifyCall()
|
||||
}
|
||||
|
||||
func TestAddLabelWithoutSelector(t *testing.T) {
|
||||
var o addMetadataOptions
|
||||
o.labelsWithoutSelector = true
|
||||
m := makeKustomization(t)
|
||||
o.metadata = map[string]string{"new": "label"}
|
||||
assert.NoError(t, o.addLabels(m))
|
||||
assert.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"new": "label"}})
|
||||
}
|
||||
|
||||
func TestAddLabelWithoutSelectorAddLabel(t *testing.T) {
|
||||
var o addMetadataOptions
|
||||
o.metadata = map[string]string{"owls": "cute", "otters": "adorable"}
|
||||
o.labelsWithoutSelector = true
|
||||
|
||||
m := makeKustomization(t)
|
||||
assert.NoError(t, o.addLabels(m))
|
||||
// adding new labels should work
|
||||
o.metadata = map[string]string{"new": "label"}
|
||||
assert.NoError(t, o.addLabels(m))
|
||||
|
||||
assert.Equal(t, m.Labels[0], types.Label{Pairs: map[string]string{"owls": "cute", "otters": "adorable"}})
|
||||
assert.Equal(t, m.Labels[1], types.Label{Pairs: map[string]string{"new": "label"}})
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
ldrhelper "sigs.k8s.io/kustomize/api/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/kustomize/v5/commands/internal/kustfile"
|
||||
"sigs.k8s.io/kustomize/kustomize/v5/commands/internal/util"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
@@ -49,7 +49,7 @@ func (o *addResourceOptions) Validate(args []string) error {
|
||||
|
||||
// RunAddResource runs addResource command (do real work).
|
||||
func (o *addResourceOptions) RunAddResource(fSys filesys.FileSystem) error {
|
||||
resources, err := util.GlobPatternsWithLoader(fSys, loader.NewFileLoaderAtCwd(fSys), o.resourceFilePaths)
|
||||
resources, err := util.GlobPatternsWithLoader(fSys, ldrhelper.NewFileLoaderAtCwd(fSys), o.resourceFilePaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/kv"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
ldrhelper "sigs.k8s.io/kustomize/api/pkg/loader"
|
||||
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
@@ -19,7 +19,7 @@ func TestNewAddConfigMapIsNotNil(t *testing.T) {
|
||||
assert.NotNil(t, newCmdAddConfigMap(
|
||||
fSys,
|
||||
kv.NewLoader(
|
||||
loader.NewFileLoaderAtCwd(fSys),
|
||||
ldrhelper.NewFileLoaderAtCwd(fSys),
|
||||
valtest_test.MakeFakeValidator()),
|
||||
nil))
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/kv"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
ldrhelper "sigs.k8s.io/kustomize/api/pkg/loader"
|
||||
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
@@ -19,7 +19,7 @@ func TestNewCmdAddSecretIsNotNil(t *testing.T) {
|
||||
assert.NotNil(t, newCmdAddSecret(
|
||||
fSys,
|
||||
kv.NewLoader(
|
||||
loader.NewFileLoaderAtCwd(fSys),
|
||||
ldrhelper.NewFileLoaderAtCwd(fSys),
|
||||
valtest_test.MakeFakeValidator()),
|
||||
nil))
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/kv"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
ldrhelper "sigs.k8s.io/kustomize/api/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/kustomize/v5/commands/edit/add"
|
||||
"sigs.k8s.io/kustomize/kustomize/v5/commands/edit/fix"
|
||||
@@ -43,11 +43,11 @@ func NewCmdEdit(
|
||||
c.AddCommand(
|
||||
add.NewCmdAdd(
|
||||
fSys,
|
||||
kv.NewLoader(loader.NewFileLoaderAtCwd(fSys), v),
|
||||
kv.NewLoader(ldrhelper.NewFileLoaderAtCwd(fSys), v),
|
||||
rf),
|
||||
set.NewCmdSet(
|
||||
fSys,
|
||||
kv.NewLoader(loader.NewFileLoaderAtCwd(fSys), v),
|
||||
kv.NewLoader(ldrhelper.NewFileLoaderAtCwd(fSys), v),
|
||||
v),
|
||||
fix.NewCmdFix(fSys, w),
|
||||
remove.NewCmdRemove(fSys, v),
|
||||
|
||||
@@ -22,6 +22,9 @@ func NewCmdRemove(
|
||||
kustomize edit remove resource {filepath} {filepath}
|
||||
kustomize edit remove resource {pattern}
|
||||
|
||||
# Removes one or more configmap from the kustomization file
|
||||
kustomize edit remove configmap {name1},{name2}
|
||||
|
||||
# Removes one or more patches from the kustomization file
|
||||
kustomize edit remove patch --path {filepath} --group {target group name} --version {target version}
|
||||
|
||||
@@ -37,6 +40,7 @@ func NewCmdRemove(
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
}
|
||||
c.AddCommand(
|
||||
newCmdRemoveConfigMap(fSys),
|
||||
newCmdRemoveResource(fSys),
|
||||
newCmdRemoveLabel(fSys, v.MakeLabelNameValidator()),
|
||||
newCmdRemoveAnnotation(fSys, v.MakeAnnotationNameValidator()),
|
||||
|
||||
99
kustomize/commands/edit/remove/removeconfigmap.go
Normal file
99
kustomize/commands/edit/remove/removeconfigmap.go
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright 2023 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package remove
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kustomize/v5/commands/internal/kustfile"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
)
|
||||
|
||||
type removeConfigMapOptions struct {
|
||||
configMapNamesToRemove []string
|
||||
}
|
||||
|
||||
// newCmdRemoveResource remove the name of a file containing a resource to the kustomization file.
|
||||
func newCmdRemoveConfigMap(fSys filesys.FileSystem) *cobra.Command {
|
||||
var o removeConfigMapOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "configmap",
|
||||
Short: "Removes the specified configmap(s) from " +
|
||||
konfig.DefaultKustomizationFileName(),
|
||||
Long: "",
|
||||
Example: `
|
||||
remove configmap my-configmap
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := o.Validate(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.RunRemoveConfigMap(fSys)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Validate validates removeConfigMap command.
|
||||
func (o *removeConfigMapOptions) Validate(args []string) error {
|
||||
switch {
|
||||
case len(args) == 0:
|
||||
return errors.New("at least one configmap name must be specified")
|
||||
case len(args) > 1:
|
||||
return fmt.Errorf("too many arguments: %s; to provide multiple configmaps to remove, please separate configmap names by commas", args)
|
||||
}
|
||||
|
||||
o.configMapNamesToRemove = strings.Split(args[0], ",")
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunRemoveConfigMap runs ConfigMap command (do real work).
|
||||
func (o *removeConfigMapOptions) RunRemoveConfigMap(fSys filesys.FileSystem) error {
|
||||
mf, err := kustfile.NewKustomizationFile(fSys)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read kustomization file: %w", err)
|
||||
}
|
||||
|
||||
m, err := mf.Read()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read kustomization file contents: %w", err)
|
||||
}
|
||||
|
||||
foundConfigMaps := make(map[string]struct{})
|
||||
|
||||
newConfigMaps := make([]types.ConfigMapArgs, 0, len(m.ConfigMapGenerator))
|
||||
for _, currentConfigMap := range m.ConfigMapGenerator {
|
||||
if kustfile.StringInSlice(currentConfigMap.Name, o.configMapNamesToRemove) {
|
||||
foundConfigMaps[currentConfigMap.Name] = struct{}{}
|
||||
continue
|
||||
}
|
||||
newConfigMaps = append(newConfigMaps, currentConfigMap)
|
||||
}
|
||||
|
||||
if len(foundConfigMaps) == 0 {
|
||||
return fmt.Errorf("no specified configmap(s) were found in the %s file",
|
||||
konfig.DefaultKustomizationFileName())
|
||||
}
|
||||
|
||||
for _, name := range o.configMapNamesToRemove {
|
||||
if _, found := foundConfigMaps[name]; !found {
|
||||
log.Printf("configmap %s doesn't exist in kustomization file", name)
|
||||
}
|
||||
}
|
||||
|
||||
m.ConfigMapGenerator = newConfigMaps
|
||||
err = mf.Write(m)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write kustomization file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
116
kustomize/commands/edit/remove/removeconfigmap_test.go
Normal file
116
kustomize/commands/edit/remove/removeconfigmap_test.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2023 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package remove //nolint:testpackage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
testutils_test "sigs.k8s.io/kustomize/kustomize/v5/commands/internal/testutils"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
)
|
||||
|
||||
func TestRemoveConfigMap(t *testing.T) {
|
||||
const configMapName01 = "example-configmap-01"
|
||||
const configMapName02 = "example-configmap-02"
|
||||
|
||||
tests := map[string]struct {
|
||||
input string
|
||||
args []string
|
||||
expectedOutput string
|
||||
expectedErr string
|
||||
}{
|
||||
"happy path": {
|
||||
input: fmt.Sprintf(`
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
configMapGenerator:
|
||||
- name: %s
|
||||
files:
|
||||
- application.properties
|
||||
`, configMapName01),
|
||||
args: []string{configMapName01},
|
||||
expectedOutput: `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
`,
|
||||
},
|
||||
"multiple": {
|
||||
input: fmt.Sprintf(`
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
configMapGenerator:
|
||||
- name: %s
|
||||
files:
|
||||
- application.properties
|
||||
- name: %s
|
||||
files:
|
||||
- application.properties
|
||||
`, configMapName01, configMapName02),
|
||||
args: []string{
|
||||
fmt.Sprintf("%s,%s", configMapName01, configMapName02),
|
||||
},
|
||||
expectedOutput: `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
`,
|
||||
},
|
||||
"miss": {
|
||||
input: fmt.Sprintf(`
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
configMapGenerator:
|
||||
- name: %s
|
||||
files:
|
||||
- application.properties
|
||||
`, configMapName01),
|
||||
args: []string{"foo"},
|
||||
expectedErr: "no specified configmap(s) were found",
|
||||
},
|
||||
"no configmap name specified": {
|
||||
args: []string{},
|
||||
expectedErr: "at least one configmap name must be specified",
|
||||
},
|
||||
"too many configmap names specified": {
|
||||
args: []string{"test1", "test2"},
|
||||
expectedErr: "too many arguments",
|
||||
},
|
||||
"one existing and one non-existing": {
|
||||
input: fmt.Sprintf(`
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
configMapGenerator:
|
||||
- name: %s
|
||||
files:
|
||||
- application.properties
|
||||
`, configMapName01),
|
||||
args: []string{fmt.Sprintf("%s,%s", configMapName01, "foo")},
|
||||
expectedOutput: `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
fSys := filesys.MakeFsInMemory()
|
||||
testutils_test.WriteTestKustomizationWith(fSys, []byte(tc.input))
|
||||
cmd := newCmdRemoveConfigMap(fSys)
|
||||
err := cmd.RunE(cmd, tc.args)
|
||||
|
||||
if tc.expectedErr != "" {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tc.expectedErr)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
content, err := testutils_test.ReadTestKustomization(fSys)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expectedOutput, string(content))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ require (
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.8.1
|
||||
golang.org/x/text v0.8.0
|
||||
sigs.k8s.io/kustomize/api v0.13.4
|
||||
sigs.k8s.io/kustomize/api v0.14.0
|
||||
sigs.k8s.io/kustomize/cmd/config v0.11.3
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.3
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
@@ -16,7 +16,6 @@ require (
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
@@ -34,8 +33,9 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/xlab/treeprint v1.2.0 // indirect
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/evanphx/json-patch.v5 v5.6.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
@@ -43,3 +43,7 @@ require (
|
||||
)
|
||||
|
||||
replace sigs.k8s.io/kustomize/api => ../api
|
||||
|
||||
replace sigs.k8s.io/kustomize/cmd/config => ../cmd/config
|
||||
|
||||
replace sigs.k8s.io/kustomize/kyaml => ../kyaml
|
||||
|
||||
@@ -6,8 +6,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
|
||||
github.com/evanphx/json-patch v4.12.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=
|
||||
@@ -69,8 +67,8 @@ github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd
|
||||
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/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -81,6 +79,8 @@ google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/evanphx/json-patch.v5 v5.6.0 h1:BMT6KIwBD9CaU91PJCZIe46bDmBWa9ynTQgJIOpfQBk=
|
||||
gopkg.in/evanphx/json-patch.v5 v5.6.0/go.mod h1:/kvTRh1TVm5wuM6OkHxqXtE/1nUZZpihg29RtuIyfvk=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
@@ -91,9 +91,5 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/kube-openapi v0.0.0-20230601164746-7562a1006961 h1:pqRVJGQJz6oeZby8qmPKXYIBjyrcv7EHCe/33UkZMYA=
|
||||
k8s.io/kube-openapi v0.0.0-20230601164746-7562a1006961/go.mod h1:l8HTwL5fqnlns4jOveW1L75eo7R9KFHxiE0bsPGy428=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.11.3 h1:QLukJoe/0sjhUrtylmBS1MXhvkdLtbpHJvAClXDra54=
|
||||
sigs.k8s.io/kustomize/cmd/config v0.11.3/go.mod h1:ENTZ8Ds12gewUpdxF5PJq/9qPVQFd5VPvMIL11wrBIU=
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.3 h1:WpabVAKZe2YEp/irTSHwD6bfjwZnTtSDewd2BVJGMZs=
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.3/go.mod h1:npvh9epWysfQ689Rtt/U+dpOJDTBn8kUnF1O6VzvmZA=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package fixsetters
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// FieldMeta contains metadata that may be attached to fields as comments
|
||||
type FieldMetaV1 struct {
|
||||
Schema spec.Schema
|
||||
|
||||
Extensions XKustomize
|
||||
}
|
||||
|
||||
type XKustomize struct {
|
||||
SetBy string `yaml:"setBy,omitempty" json:"setBy,omitempty"`
|
||||
PartialFieldSetters []PartialFieldSetter `yaml:"partialSetters,omitempty" json:"partialSetters,omitempty"`
|
||||
FieldSetter *PartialFieldSetter `yaml:"setter,omitempty" json:"setter,omitempty"`
|
||||
}
|
||||
|
||||
// PartialFieldSetter defines how to set part of a field rather than the full field
|
||||
// value. e.g. the tag part of an image field
|
||||
type PartialFieldSetter struct {
|
||||
// Name is the name of this setter.
|
||||
Name string `yaml:"name" json:"name"`
|
||||
|
||||
// Value is the current value that has been set.
|
||||
Value string `yaml:"value" json:"value"`
|
||||
}
|
||||
|
||||
// UpgradeV1SetterComment reads the FieldMeta from a node and upgrade the
|
||||
// setters comment to latest
|
||||
func (fm *FieldMetaV1) UpgradeV1SetterComment(n *yaml.RNode) error {
|
||||
// check for metadata on head and line comments
|
||||
comments := []string{n.YNode().LineComment, n.YNode().HeadComment}
|
||||
for _, c := range comments {
|
||||
if c == "" {
|
||||
continue
|
||||
}
|
||||
c := strings.TrimLeft(c, "#")
|
||||
|
||||
if err := fm.Schema.UnmarshalJSON([]byte(c)); err != nil {
|
||||
// note: don't return an error if the comment isn't a fieldmeta struct
|
||||
return nil
|
||||
}
|
||||
|
||||
fe := fm.Schema.VendorExtensible.Extensions["x-kustomize"]
|
||||
if fe == nil {
|
||||
return nil
|
||||
}
|
||||
b, err := json.Marshal(fe)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
// delete line comment after parsing info into fieldmeta
|
||||
n.YNode().HeadComment = ""
|
||||
n.YNode().LineComment = ""
|
||||
err = json.Unmarshal(b, &fm.Extensions)
|
||||
if fm.Extensions.FieldSetter != nil {
|
||||
n.YNode().LineComment = fmt.Sprintf(`{"%s":"%s"}`, fieldmeta.ShortHandRef(), fm.Extensions.FieldSetter.Name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package fixsetters
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
||||
)
|
||||
|
||||
// SetterFixer fixes setters in the input package
|
||||
type SetterFixer struct {
|
||||
// PkgPath is path to the resource package
|
||||
PkgPath string
|
||||
|
||||
// OpenAPIPath is path to the openAPI file in the package
|
||||
OpenAPIPath string
|
||||
|
||||
// DryRun only displays the actions without performing
|
||||
DryRun bool
|
||||
}
|
||||
|
||||
// SetterFixerV1Result holds the results of V1 setters fix
|
||||
type SetterFixerV1Result struct {
|
||||
// NeedFix indicates if the resource in pkgPath are on V1 version of setters
|
||||
// and need to be fixed
|
||||
NeedFix bool
|
||||
|
||||
// CreatedSetters are setters created as part of this fix
|
||||
CreatedSetters []string
|
||||
|
||||
// CreatedSubst are substitutions created as part of this fix
|
||||
CreatedSubst []string
|
||||
|
||||
// FailedSetters are setters failed to create from current V1 setters
|
||||
FailedSetters map[string]error
|
||||
|
||||
// FailedSubst are substitutions failed to be created from V1 partial setters
|
||||
FailedSubst map[string]error
|
||||
}
|
||||
|
||||
// FixSettersV1 reads the package and upgrades v1 version of setters
|
||||
// to latest
|
||||
func (f *SetterFixer) FixV1Setters() (SetterFixerV1Result, error) {
|
||||
sfr := SetterFixerV1Result{
|
||||
FailedSetters: make(map[string]error),
|
||||
FailedSubst: make(map[string]error),
|
||||
}
|
||||
// KrmFile need not exist for dryRun
|
||||
if !f.DryRun {
|
||||
_, err := os.Stat(f.OpenAPIPath)
|
||||
if err != nil {
|
||||
return sfr, err
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
// lookup for all setters and partial setters in v1 format
|
||||
// delete the v1 format comments after lookup
|
||||
l := UpgradeV1Setters{}
|
||||
if f.DryRun {
|
||||
err = applyReadFilter(&l, f.PkgPath)
|
||||
} else {
|
||||
err = applyWriteFilter(&l, f.PkgPath)
|
||||
}
|
||||
if err != nil {
|
||||
return sfr, err
|
||||
}
|
||||
if len(l.SetterCounts) > 0 {
|
||||
sfr.NeedFix = true
|
||||
} else {
|
||||
return sfr, nil
|
||||
}
|
||||
|
||||
// for each v1 setter create the equivalent in v2,
|
||||
for _, setter := range l.SetterCounts {
|
||||
sd := setters2.SetterDefinition{
|
||||
Name: setter.Name,
|
||||
Value: setter.Value,
|
||||
Description: setter.Description,
|
||||
SetBy: setter.SetBy,
|
||||
Type: setter.Type,
|
||||
}
|
||||
var err error
|
||||
if !f.DryRun {
|
||||
err = sd.AddToFile(f.OpenAPIPath)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
sfr.FailedSetters[setter.Name] = err
|
||||
} else {
|
||||
sfr.CreatedSetters = append(sfr.CreatedSetters, setter.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// for each group of partial setters, create equivalent substitution
|
||||
for _, subst := range l.Substitutions {
|
||||
sc := settersutil.SubstitutionCreator{
|
||||
Name: subst.Name,
|
||||
FieldValue: subst.FieldVale,
|
||||
Pattern: subst.Pattern,
|
||||
ResourcesPath: f.PkgPath,
|
||||
OpenAPIPath: f.OpenAPIPath,
|
||||
}
|
||||
var err error
|
||||
if !f.DryRun {
|
||||
err = applyWriteFilter(&sc, f.PkgPath)
|
||||
}
|
||||
if err != nil {
|
||||
sfr.FailedSubst[subst.Name] = err
|
||||
} else {
|
||||
sfr.CreatedSubst = append(sfr.CreatedSubst, subst.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return sfr, nil
|
||||
}
|
||||
|
||||
func applyWriteFilter(f kio.Filter, pkgPath string) error {
|
||||
rw := &kio.LocalPackageReadWriter{
|
||||
PackagePath: pkgPath,
|
||||
}
|
||||
return kio.Pipeline{
|
||||
Inputs: []kio.Reader{rw},
|
||||
Filters: []kio.Filter{f},
|
||||
Outputs: []kio.Writer{rw},
|
||||
}.Execute()
|
||||
}
|
||||
|
||||
func applyReadFilter(f kio.Filter, pkgPath string) error {
|
||||
rw := &kio.LocalPackageReader{
|
||||
PackagePath: pkgPath,
|
||||
}
|
||||
return kio.Pipeline{
|
||||
Inputs: []kio.Reader{rw},
|
||||
Filters: []kio.Filter{f},
|
||||
}.Execute()
|
||||
}
|
||||
@@ -1,460 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package fixsetters
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFixSettersV1(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
input string
|
||||
err string
|
||||
dryRun bool
|
||||
openAPIFile string
|
||||
expectedOutput string
|
||||
expectedOpenAPI string
|
||||
needFix bool
|
||||
createdSetters []string
|
||||
createdSubst []string
|
||||
failedSetters map[string]error
|
||||
failedSubst map[string]error
|
||||
}{
|
||||
{
|
||||
name: "upgrade-delete-partial-setters",
|
||||
input: `
|
||||
apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
cluster: "someproj/someclus" # {"type":"string","x-kustomize":{"partialSetters":[{"name":"project","value":"someproj"},{"name":"cluster","value":"someclus"}]}}
|
||||
spec:
|
||||
profile: asm # {"type":"string","x-kustomize":{"setter":{"name":"profile","value":"asm"}}}
|
||||
cluster: "someproj/someclus" # {"type":"string","x-kustomize":{"partialSetters":[{"name":"project","value":"someproj"},{"name":"cluster","value":"someclus"}]}}
|
||||
`,
|
||||
|
||||
openAPIFile: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization`,
|
||||
|
||||
needFix: true,
|
||||
createdSetters: []string{"cluster", "profile", "project"},
|
||||
createdSubst: []string{"project-cluster-54235872"},
|
||||
failedSetters: map[string]error{},
|
||||
failedSubst: map[string]error{},
|
||||
|
||||
expectedOutput: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
cluster: "someproj/someclus" # {"$openapi":"project-cluster-54235872"}
|
||||
spec:
|
||||
profile: asm # {"$openapi":"profile"}
|
||||
cluster: "someproj/someclus" # {"$openapi":"project-cluster-54235872"}
|
||||
`,
|
||||
|
||||
expectedOpenAPI: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.cluster:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: cluster
|
||||
value: someclus
|
||||
io.k8s.cli.setters.profile:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: profile
|
||||
value: asm
|
||||
io.k8s.cli.setters.project:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: project
|
||||
value: someproj
|
||||
io.k8s.cli.substitutions.project-cluster-54235872:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: project-cluster-54235872
|
||||
pattern: ${project}/${cluster}
|
||||
values:
|
||||
- marker: ${project}
|
||||
ref: '#/definitions/io.k8s.cli.setters.project'
|
||||
- marker: ${cluster}
|
||||
ref: '#/definitions/io.k8s.cli.setters.cluster'
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "upgrade-delete-partial-setters-dryRun",
|
||||
dryRun: true,
|
||||
input: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
cluster: "someproj/someclus" # {"type":"string","x-kustomize":{"partialSetters":[{"name":"project","value":"someproj"},{"name":"cluster","value":"someclus"}]}}
|
||||
spec:
|
||||
profile: asm # {"type":"string","x-kustomize":{"setter":{"name":"profile","value":"asm"}}}
|
||||
cluster: "someproj/someclus" # {"type":"string","x-kustomize":{"partialSetters":[{"name":"project","value":"someproj"},{"name":"cluster","value":"someclus"}]}}
|
||||
`,
|
||||
needFix: true,
|
||||
createdSetters: []string{"cluster", "profile", "project"},
|
||||
createdSubst: []string{"project-cluster-54235872"},
|
||||
failedSetters: map[string]error{},
|
||||
failedSubst: map[string]error{},
|
||||
|
||||
expectedOutput: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
cluster: "someproj/someclus" # {"type":"string","x-kustomize":{"partialSetters":[{"name":"project","value":"someproj"},{"name":"cluster","value":"someclus"}]}}
|
||||
spec:
|
||||
profile: asm # {"type":"string","x-kustomize":{"setter":{"name":"profile","value":"asm"}}}
|
||||
cluster: "someproj/someclus" # {"type":"string","x-kustomize":{"partialSetters":[{"name":"project","value":"someproj"},{"name":"cluster","value":"someclus"}]}}
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "partial-setters-same-value",
|
||||
input: `
|
||||
apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
spec:
|
||||
profile: asm # {"type":"string","x-kustomize":{"setter":{"name":"profile","value":"asm"}}}
|
||||
team: asm # {"type":"string","x-kustomize":{"setter":{"name":"team","value":"asm"}}}
|
||||
profile-team: asm/asm # {"type":"string","x-kustomize":{"partialSetters":[{"name":"profile","value":"asm"},{"name":"team","value":"asm"}]}}
|
||||
`,
|
||||
|
||||
openAPIFile: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization`,
|
||||
|
||||
needFix: true,
|
||||
createdSetters: []string{"profile", "team"},
|
||||
createdSubst: []string{"profile-team-1851878264"},
|
||||
failedSetters: map[string]error{},
|
||||
failedSubst: map[string]error{},
|
||||
|
||||
expectedOutput: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
spec:
|
||||
profile: asm # {"$openapi":"profile"}
|
||||
team: asm # {"$openapi":"team"}
|
||||
profile-team: asm/asm # {"$openapi":"profile-team-1851878264"}
|
||||
`,
|
||||
|
||||
expectedOpenAPI: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.profile:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: profile
|
||||
value: asm
|
||||
io.k8s.cli.setters.team:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: team
|
||||
value: asm
|
||||
io.k8s.cli.substitutions.profile-team-1851878264:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: profile-team-1851878264
|
||||
pattern: ${profile}/${team}
|
||||
values:
|
||||
- marker: ${profile}
|
||||
ref: '#/definitions/io.k8s.cli.setters.profile'
|
||||
- marker: ${team}
|
||||
ref: '#/definitions/io.k8s.cli.setters.team'
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "partial-setters-suffix-subst",
|
||||
input: `
|
||||
apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
spec:
|
||||
profile: asm-profile # {"type":"string","x-kustomize":{"partialSetters":[{"name":"asm","value":"asm"}]}}
|
||||
team: asm-team # {"type":"string","x-kustomize":{"partialSetters":[{"name":"asm","value":"asm"}]}}
|
||||
`,
|
||||
|
||||
openAPIFile: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization`,
|
||||
|
||||
needFix: true,
|
||||
createdSetters: []string{"asm"},
|
||||
createdSubst: []string{"asm-3472570278", "asm-3647054792"},
|
||||
failedSetters: map[string]error{},
|
||||
failedSubst: map[string]error{},
|
||||
|
||||
expectedOutput: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
spec:
|
||||
profile: asm-profile # {"$openapi":"asm-3647054792"}
|
||||
team: asm-team # {"$openapi":"asm-3472570278"}
|
||||
`,
|
||||
|
||||
expectedOpenAPI: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.asm:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: asm
|
||||
value: asm
|
||||
io.k8s.cli.substitutions.asm-3472570278:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: asm-3472570278
|
||||
pattern: ${asm}-team
|
||||
values:
|
||||
- marker: ${asm}
|
||||
ref: '#/definitions/io.k8s.cli.setters.asm'
|
||||
io.k8s.cli.substitutions.asm-3647054792:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: asm-3647054792
|
||||
pattern: ${asm}-profile
|
||||
values:
|
||||
- marker: ${asm}
|
||||
ref: '#/definitions/io.k8s.cli.setters.asm'
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "upgrade-with-both-versions",
|
||||
|
||||
input: `
|
||||
apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
clusterName: "project-id/us-east1-d/cluster-name"
|
||||
spec:
|
||||
profile: asm # {"type":"string","x-kustomize":{"setter":{"name":"profilesetter","value":"asm"}}}
|
||||
hub: gcr.io/asm-testing # {"$openapi":"hubsetter"}
|
||||
`,
|
||||
|
||||
openAPIFile: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.hubsetter:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: hubsetter
|
||||
value: gcr.io/asm-testing`,
|
||||
|
||||
needFix: true,
|
||||
createdSetters: []string{"profilesetter"},
|
||||
failedSetters: map[string]error{},
|
||||
failedSubst: map[string]error{},
|
||||
|
||||
expectedOutput: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
clusterName: "project-id/us-east1-d/cluster-name"
|
||||
spec:
|
||||
profile: asm # {"$openapi":"profilesetter"}
|
||||
hub: gcr.io/asm-testing # {"$openapi":"hubsetter"}
|
||||
`,
|
||||
|
||||
expectedOpenAPI: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.hubsetter:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: hubsetter
|
||||
value: gcr.io/asm-testing
|
||||
io.k8s.cli.setters.profilesetter:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: profilesetter
|
||||
value: asm
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "setter-already-exists",
|
||||
|
||||
input: `
|
||||
apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
clusterName: "project-id/us-east1-d/cluster-name"
|
||||
spec:
|
||||
profile: asm # {"type":"string","x-kustomize":{"setter":{"name":"profilesetter","value":"asm"}}}
|
||||
hub: asm # {"$openapi":"profilesetter"}
|
||||
`,
|
||||
|
||||
openAPIFile: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.profilesetter:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: profilesetter
|
||||
value: asm
|
||||
`,
|
||||
|
||||
needFix: true,
|
||||
createdSetters: []string{"profilesetter"},
|
||||
failedSetters: map[string]error{},
|
||||
failedSubst: map[string]error{},
|
||||
expectedOutput: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
clusterName: "project-id/us-east1-d/cluster-name"
|
||||
spec:
|
||||
profile: asm # {"$openapi":"profilesetter"}
|
||||
hub: asm # {"$openapi":"profilesetter"}
|
||||
`,
|
||||
|
||||
expectedOpenAPI: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.profilesetter:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: profilesetter
|
||||
value: asm
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "do-not-delete-latest setters",
|
||||
|
||||
input: `
|
||||
apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
clusterName: "project-id/us-east1-d/cluster-name"
|
||||
spec:
|
||||
profile: asm # {"$openapi":"profilesetter"}
|
||||
hub: gcr.io/asm-testing
|
||||
`,
|
||||
failedSetters: map[string]error{},
|
||||
failedSubst: map[string]error{},
|
||||
|
||||
openAPIFile: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.profilesetter:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: profilesetter
|
||||
value: asm
|
||||
`,
|
||||
|
||||
expectedOutput: `apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
clusterName: "project-id/us-east1-d/cluster-name"
|
||||
spec:
|
||||
profile: asm # {"$openapi":"profilesetter"}
|
||||
hub: gcr.io/asm-testing
|
||||
`,
|
||||
|
||||
expectedOpenAPI: `apiVersion: kustomization.dev/v1alpha1
|
||||
kind: Kustomization
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.profilesetter:
|
||||
type: string
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: profilesetter
|
||||
value: asm
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "no-openAPI-file-error",
|
||||
input: `
|
||||
apiVersion: install.istio.io/v1alpha2
|
||||
kind: IstioControlPlane
|
||||
metadata:
|
||||
clusterName: "project-id/us-east1-d/cluster-name"
|
||||
spec:
|
||||
profile: asm # {"type":"string","x-kustomize":{"setter":{"name":"profilesetter","value":"asm"}}}
|
||||
hub: gcr.io/asm-testing
|
||||
`,
|
||||
|
||||
err: "Krmfile:",
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
openAPIFileName := "Krmfile"
|
||||
|
||||
dir := t.TempDir()
|
||||
|
||||
err := os.WriteFile(filepath.Join(dir, "deploy.yaml"), []byte(test.input), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if test.openAPIFile != "" {
|
||||
err = os.WriteFile(filepath.Join(dir, openAPIFileName), []byte(test.openAPIFile), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
sf := SetterFixer{
|
||||
PkgPath: dir,
|
||||
DryRun: test.dryRun,
|
||||
OpenAPIPath: filepath.Join(dir, "Krmfile"),
|
||||
}
|
||||
|
||||
sfr, err := sf.FixV1Setters()
|
||||
if test.err == "" {
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
} else {
|
||||
if !assert.Contains(t, err.Error(), test.err) {
|
||||
t.FailNow()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
actualOutput, err := os.ReadFile(filepath.Join(dir, "deploy.yaml"))
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(t, test.expectedOutput, string(actualOutput))
|
||||
|
||||
if test.expectedOpenAPI != "" {
|
||||
actualOpenAPI, err := os.ReadFile(filepath.Join(dir, openAPIFileName))
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(t, test.expectedOpenAPI, string(actualOpenAPI))
|
||||
}
|
||||
assert.Equal(t, test.needFix, sfr.NeedFix)
|
||||
assert.Equal(t, test.createdSetters, sfr.CreatedSetters)
|
||||
assert.Equal(t, test.createdSubst, sfr.CreatedSubst)
|
||||
assert.Equal(t, test.failedSubst, sfr.FailedSubst)
|
||||
assert.Equal(t, test.failedSetters, sfr.FailedSetters)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package fixsetters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
var _ yaml.Filter = &upgradeV1Setters{}
|
||||
|
||||
// upgradeV1Setters looks up v1 setters in a Resource and upgrades
|
||||
// all the setters related comments
|
||||
type upgradeV1Setters struct {
|
||||
// Name of the setter to lookup. Optional
|
||||
Name string
|
||||
|
||||
// Setters is a list of setters that were found
|
||||
Setters []setter
|
||||
|
||||
// Substitutions is a list of substitutions that were found
|
||||
Substitutions []substitution
|
||||
}
|
||||
|
||||
type substitution struct {
|
||||
Name string
|
||||
FieldVale string
|
||||
Pattern string
|
||||
}
|
||||
|
||||
type setter struct {
|
||||
PartialFieldSetter
|
||||
Description string
|
||||
Type string
|
||||
SetBy string
|
||||
SubstName string
|
||||
}
|
||||
|
||||
func (ls *upgradeV1Setters) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
switch object.YNode().Kind {
|
||||
case yaml.DocumentNode:
|
||||
// skip the document node
|
||||
return ls.Filter(yaml.NewRNode(object.YNode().Content[0]))
|
||||
case yaml.MappingNode:
|
||||
return object, object.VisitFields(func(node *yaml.MapNode) error {
|
||||
return node.Value.PipeE(ls)
|
||||
})
|
||||
case yaml.SequenceNode:
|
||||
return object, object.VisitElements(func(node *yaml.RNode) error {
|
||||
return node.PipeE(ls)
|
||||
})
|
||||
case yaml.ScalarNode:
|
||||
return object, ls.lookupAndUpgrade(object)
|
||||
default:
|
||||
return object, nil
|
||||
}
|
||||
}
|
||||
|
||||
// lookupAndUpgrade finds any setters for a field and upgrades the setters comment
|
||||
func (ls *upgradeV1Setters) lookupAndUpgrade(field *yaml.RNode) error {
|
||||
// check if there is a substitution for this field
|
||||
var fm = FieldMetaV1{}
|
||||
if err := fm.UpgradeV1SetterComment(field); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if fm.Extensions.FieldSetter != nil {
|
||||
if ls.Name != "" && ls.Name != fm.Extensions.FieldSetter.Name {
|
||||
// skip this setter, it doesn't match the specified setter
|
||||
return nil
|
||||
}
|
||||
// full setter
|
||||
ls.Setters = append(ls.Setters, setter{
|
||||
PartialFieldSetter: *fm.Extensions.FieldSetter,
|
||||
Description: fm.Schema.Description,
|
||||
Type: fm.Schema.Type[0],
|
||||
SetBy: fm.Extensions.SetBy,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(fm.Extensions.PartialFieldSetters) > 0 {
|
||||
fieldValue := field.YNode().Value
|
||||
pattern := fieldValue
|
||||
|
||||
var substName string
|
||||
// derive substitution pattern from partial setters
|
||||
for i := range fm.Extensions.PartialFieldSetters {
|
||||
substName += fm.Extensions.PartialFieldSetters[i].Name + "-"
|
||||
pattern = strings.Replace(pattern, fm.Extensions.PartialFieldSetters[i].Value, `${`+fm.Extensions.PartialFieldSetters[i].Name+"}", 1)
|
||||
}
|
||||
|
||||
fvHash, err := FNV32aHash(fieldValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
substName += fvHash
|
||||
ls.Substitutions = append(ls.Substitutions, substitution{
|
||||
Name: substName,
|
||||
FieldVale: fieldValue,
|
||||
Pattern: pattern,
|
||||
})
|
||||
}
|
||||
|
||||
for i := range fm.Extensions.PartialFieldSetters {
|
||||
if ls.Name != "" && ls.Name != fm.Extensions.PartialFieldSetters[i].Name {
|
||||
// skip this setter
|
||||
continue
|
||||
}
|
||||
ls.Setters = append(ls.Setters, setter{
|
||||
PartialFieldSetter: fm.Extensions.PartialFieldSetters[i],
|
||||
Description: fm.Schema.Description,
|
||||
Type: fm.Schema.Type[0],
|
||||
SetBy: fm.Extensions.SetBy,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FNV32aHash generates 32-bit FNV-1a hash for input string
|
||||
func FNV32aHash(text string) (string, error) {
|
||||
algorithm := fnv.New32a()
|
||||
_, err := algorithm.Write([]byte(text))
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err)
|
||||
}
|
||||
return fmt.Sprint(algorithm.Sum32()), nil
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package fixsetters
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
var _ kio.Filter = &UpgradeV1Setters{}
|
||||
|
||||
// UpgradeV1Setters identifies setters for a collection of Resources to upgrade
|
||||
type UpgradeV1Setters struct {
|
||||
// Name is the name of the setter to match. Optional.
|
||||
Name string
|
||||
|
||||
// SetterCounts is populated by Filter and contains the count of fields matching each setter.
|
||||
SetterCounts []setterCount
|
||||
|
||||
// Substitutions are groups of partial setters
|
||||
Substitutions []substitution
|
||||
}
|
||||
|
||||
// setterCount records the identified setters and number of fields matching those setters
|
||||
type setterCount struct {
|
||||
// Count is the number of substitutions possible to perform
|
||||
Count int
|
||||
|
||||
// setter is the substitution found
|
||||
setter
|
||||
}
|
||||
|
||||
// Filter implements kio.Filter
|
||||
func (l *UpgradeV1Setters) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
setters := map[string]*setterCount{}
|
||||
substitutions := map[string]*substitution{}
|
||||
|
||||
for i := range input {
|
||||
// lookup substitutions for this object
|
||||
ls := &upgradeV1Setters{Name: l.Name}
|
||||
if err := input[i].PipeE(ls); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// aggregate counts for each setter by name. takes the description and value from
|
||||
// the first setter for each name encountered.
|
||||
for j := range ls.Setters {
|
||||
setter := ls.Setters[j]
|
||||
curr, found := setters[setter.Name]
|
||||
if !found {
|
||||
curr = &setterCount{setter: setter}
|
||||
setters[setter.Name] = curr
|
||||
}
|
||||
curr.Count++
|
||||
}
|
||||
|
||||
for j := range ls.Substitutions {
|
||||
subst := ls.Substitutions[j]
|
||||
_, found := substitutions[subst.Name]
|
||||
if !found {
|
||||
substitutions[subst.Name] = &subst
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pull out and sort the results by setter name
|
||||
for _, v := range setters {
|
||||
l.SetterCounts = append(l.SetterCounts, *v)
|
||||
}
|
||||
|
||||
for _, subst := range substitutions {
|
||||
l.Substitutions = append(l.Substitutions, *subst)
|
||||
}
|
||||
|
||||
sort.Slice(l.Substitutions, func(i, j int) bool {
|
||||
return l.Substitutions[i].Name < l.Substitutions[j].Name
|
||||
})
|
||||
|
||||
sort.Slice(l.SetterCounts, func(i, j int) bool {
|
||||
return l.SetterCounts[i].Name < l.SetterCounts[j].Name
|
||||
})
|
||||
return input, nil
|
||||
}
|
||||
@@ -44,6 +44,16 @@ var (
|
||||
customSchema []byte //nolint:gochecknoglobals
|
||||
)
|
||||
|
||||
// schemaParseStatus is used in cases when a schema should be parsed, but the
|
||||
// parsing may be delayed to a later time.
|
||||
type schemaParseStatus uint32
|
||||
|
||||
const (
|
||||
schemaNotParsed schemaParseStatus = iota
|
||||
schemaParseDelayed
|
||||
schemaParsed
|
||||
)
|
||||
|
||||
// openapiData contains the parsed openapi state. this is in a struct rather than
|
||||
// a list of vars so that it can be reset from tests.
|
||||
type openapiData struct {
|
||||
@@ -57,13 +67,17 @@ type openapiData struct {
|
||||
// is namespaceable or not
|
||||
namespaceabilityByResourceType map[yaml.TypeMeta]bool
|
||||
|
||||
// noUseBuiltInSchema stores whether we want to prevent using the built-n
|
||||
// noUseBuiltInSchema stores whether we want to prevent using the built-in
|
||||
// Kubernetes schema as part of the global schema
|
||||
noUseBuiltInSchema bool
|
||||
|
||||
// schemaInit stores whether or not we've parsed the schema already,
|
||||
// so that we only reparse the when necessary (to speed up performance)
|
||||
schemaInit bool
|
||||
|
||||
// defaultBuiltInSchemaParseStatus stores the parse status of the default
|
||||
// built-in schema.
|
||||
defaultBuiltInSchemaParseStatus schemaParseStatus
|
||||
}
|
||||
|
||||
type format string
|
||||
@@ -387,18 +401,45 @@ func GetSchema(s string, schema *spec.Schema) (*ResourceSchema, error) {
|
||||
// be true if the resource is namespace-scoped, and false if the type is
|
||||
// cluster-scoped.
|
||||
func IsNamespaceScoped(typeMeta yaml.TypeMeta) (bool, bool) {
|
||||
if res, f := precomputedIsNamespaceScoped[typeMeta]; f {
|
||||
return res, true
|
||||
if isNamespaceScoped, found := precomputedIsNamespaceScoped[typeMeta]; found {
|
||||
return isNamespaceScoped, found
|
||||
}
|
||||
if isInitSchemaNeededForNamespaceScopeCheck() {
|
||||
initSchema()
|
||||
}
|
||||
return isNamespaceScopedFromSchema(typeMeta)
|
||||
}
|
||||
|
||||
func isNamespaceScopedFromSchema(typeMeta yaml.TypeMeta) (bool, bool) {
|
||||
initSchema()
|
||||
isNamespaceScoped, found := globalSchema.namespaceabilityByResourceType[typeMeta]
|
||||
return isNamespaceScoped, found
|
||||
}
|
||||
|
||||
// isInitSchemaNeededForNamespaceScopeCheck returns true if initSchema is needed
|
||||
// to ensure globalSchema.namespaceabilityByResourceType is fully populated for
|
||||
// cases where a custom or non-default built-in schema is in use.
|
||||
func isInitSchemaNeededForNamespaceScopeCheck() bool {
|
||||
schemaLock.Lock()
|
||||
defer schemaLock.Unlock()
|
||||
|
||||
if globalSchema.schemaInit {
|
||||
return false // globalSchema already is initialized.
|
||||
}
|
||||
if customSchema != nil {
|
||||
return true // initSchema is needed.
|
||||
}
|
||||
if kubernetesOpenAPIVersion == "" || kubernetesOpenAPIVersion == kubernetesOpenAPIDefaultVersion {
|
||||
// The default built-in schema is in use. Since
|
||||
// precomputedIsNamespaceScoped aligns with the default built-in schema
|
||||
// (verified by TestIsNamespaceScopedPrecompute), there is no need to
|
||||
// call initSchema.
|
||||
if globalSchema.defaultBuiltInSchemaParseStatus == schemaNotParsed {
|
||||
// The schema may be needed for purposes other than namespace scope
|
||||
// checks. Flag it to be parsed when that need arises.
|
||||
globalSchema.defaultBuiltInSchemaParseStatus = schemaParseDelayed
|
||||
}
|
||||
return false
|
||||
}
|
||||
// A non-default built-in schema is in use. initSchema is needed.
|
||||
return true
|
||||
}
|
||||
|
||||
// IsCertainlyClusterScoped returns true for Node, Namespace, etc. and
|
||||
// false for Pod, Deployment, etc. and kinds that aren't recognized in the
|
||||
// openapi data. See:
|
||||
@@ -638,13 +679,19 @@ func initSchema() {
|
||||
panic(fmt.Errorf("invalid schema file: %w", err))
|
||||
}
|
||||
} else {
|
||||
if kubernetesOpenAPIVersion == "" {
|
||||
if kubernetesOpenAPIVersion == "" || kubernetesOpenAPIVersion == kubernetesOpenAPIDefaultVersion {
|
||||
parseBuiltinSchema(kubernetesOpenAPIDefaultVersion)
|
||||
globalSchema.defaultBuiltInSchemaParseStatus = schemaParsed
|
||||
} else {
|
||||
parseBuiltinSchema(kubernetesOpenAPIVersion)
|
||||
}
|
||||
}
|
||||
|
||||
if globalSchema.defaultBuiltInSchemaParseStatus == schemaParseDelayed {
|
||||
parseBuiltinSchema(kubernetesOpenAPIDefaultVersion)
|
||||
globalSchema.defaultBuiltInSchemaParseStatus = schemaParsed
|
||||
}
|
||||
|
||||
if err := parse(kustomizationapi.MustAsset(kustomizationAPIAssetName), JsonOrYaml); err != nil {
|
||||
// this should never happen
|
||||
panic(err)
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
openapi_v2 "github.com/google/gnostic-models/openapiv2"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func BenchmarkProtoUnmarshal(t *testing.B) {
|
||||
@@ -31,3 +32,19 @@ func BenchmarkProtoUnmarshal(t *testing.B) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPrecomputedIsNamespaceScoped(b *testing.B) {
|
||||
testcases := map[string]yaml.TypeMeta{
|
||||
"namespace scoped": {APIVersion: "apps/v1", Kind: "ControllerRevision"},
|
||||
"cluster scoped": {APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole"},
|
||||
"unknown resource": {APIVersion: "custom.io/v1", Kind: "Custom"},
|
||||
}
|
||||
for name, testcase := range testcases {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ResetOpenAPI()
|
||||
_, _ = IsNamespaceScoped(testcase)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,302 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Add creates or updates setter or substitution references from resource fields.
|
||||
// Requires that at least one of FieldValue and FieldName have been set.
|
||||
type Add struct {
|
||||
// FieldValue if set will add the OpenAPI reference to fields if they have this value.
|
||||
// Optional. If unspecified match all field values.
|
||||
FieldValue string
|
||||
|
||||
// FieldName if set will add the OpenAPI reference to fields with this name or path
|
||||
// FieldName may be the full name of the field, full path to the field, or the path suffix.
|
||||
// e.g. all of the following would match spec.template.spec.containers.image --
|
||||
// [image, containers.image, spec.containers.image, template.spec.containers.image,
|
||||
// spec.template.spec.containers.image]
|
||||
// Optional. If unspecified match all field names.
|
||||
FieldName string
|
||||
|
||||
// Ref is the OpenAPI reference to set on the matching fields as a comment.
|
||||
Ref string
|
||||
|
||||
// ListValues are the value of a list setter.
|
||||
ListValues []string
|
||||
|
||||
// Type is the type of the setter value
|
||||
Type string
|
||||
|
||||
// Count is the number of fields the setter applies to
|
||||
Count int
|
||||
|
||||
SettersSchema *spec.Schema
|
||||
}
|
||||
|
||||
// Filter implements yaml.Filter
|
||||
func (a *Add) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
if a.FieldName == "" && a.FieldValue == "" {
|
||||
return nil, errors.Errorf("must specify either fieldName or fieldValue")
|
||||
}
|
||||
if a.Ref == "" {
|
||||
return nil, errors.Errorf("must specify ref")
|
||||
}
|
||||
return object, accept(a, object, a.SettersSchema)
|
||||
}
|
||||
|
||||
func (a *Add) visitSequence(_ *yaml.RNode, _ string, _ *openapi.ResourceSchema) error {
|
||||
// no-op
|
||||
return nil
|
||||
}
|
||||
|
||||
// visitMapping implements visitor
|
||||
// visitMapping visits the fields in input MappingNode and adds setter/subst ref
|
||||
// if the path path spec matches with input FiledName
|
||||
func (a *Add) visitMapping(object *yaml.RNode, p string, _ *openapi.ResourceSchema) error {
|
||||
return object.VisitFields(func(node *yaml.MapNode) error {
|
||||
if node.Value.YNode().Kind != yaml.SequenceNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
key, err := node.Key.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// derive the list values for the sequence node to write it to openAPI definitions
|
||||
var values []string
|
||||
for _, sc := range node.Value.Content() {
|
||||
values = append(values, sc.Value)
|
||||
}
|
||||
|
||||
// pathToKey refers to the path address of the key node ex: metadata.annotations
|
||||
// p is the path till parent node, pathToKey is obtained by appending child key
|
||||
pathToKey := p + "." + strings.Trim(key, "\n")
|
||||
if a.FieldName != "" && strings.HasSuffix(pathToKey, a.FieldName) {
|
||||
// check if there are different values for field path before adding ref to the field
|
||||
if len(a.ListValues) > 0 && !reflect.DeepEqual(values, a.ListValues) {
|
||||
return errors.Errorf("setters can only be created for fields with same values, "+
|
||||
"encountered different array values for specified field path: %s, %s", values, a.ListValues)
|
||||
}
|
||||
a.ListValues = values
|
||||
a.Count++
|
||||
return a.addRef(node.Key)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// visitScalar implements visitor
|
||||
// visitScalar will set the field metadata on each scalar field whose name + value match
|
||||
func (a *Add) visitScalar(object *yaml.RNode, p string, _, _ *openapi.ResourceSchema) error {
|
||||
// check if the field matches
|
||||
if a.Type == "array" {
|
||||
return nil
|
||||
}
|
||||
if a.FieldName != "" && !strings.HasSuffix(p, a.FieldName) {
|
||||
return nil
|
||||
}
|
||||
if a.FieldValue != "" && a.FieldValue != object.YNode().Value {
|
||||
return nil
|
||||
}
|
||||
a.Count++
|
||||
return a.addRef(object)
|
||||
}
|
||||
|
||||
// addRef adds the setter/subst ref to the object node as a line comment
|
||||
func (a *Add) addRef(object *yaml.RNode) error {
|
||||
// read the field metadata
|
||||
fm := fieldmeta.FieldMeta{SettersSchema: a.SettersSchema}
|
||||
if err := fm.Read(object); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create the ref on the field metadata
|
||||
r, err := spec.NewRef(a.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fm.Schema.Ref = r
|
||||
|
||||
// write the field metadata
|
||||
if err := fm.Write(object); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetterDefinition may be used to update a files OpenAPI definitions with a new setter.
|
||||
type SetterDefinition struct {
|
||||
// Name is the name of the setter to create or update.
|
||||
Name string `yaml:"name"`
|
||||
|
||||
// Value is the value of the setter.
|
||||
Value string `yaml:"value"`
|
||||
|
||||
// ListValues are the value of a list setter.
|
||||
ListValues []string `yaml:"listValues,omitempty"`
|
||||
|
||||
// SetBy is the person or role that last set the value.
|
||||
SetBy string `yaml:"setBy,omitempty"`
|
||||
|
||||
// Description is a description of the value.
|
||||
Description string `yaml:"description,omitempty"`
|
||||
|
||||
// Count is the number of fields set by this setter.
|
||||
Count int `yaml:"count,omitempty"`
|
||||
|
||||
// Type is the type of the setter value.
|
||||
Type string `yaml:"type,omitempty"`
|
||||
|
||||
// Schema is the openAPI schema for setter constraints.
|
||||
Schema string `yaml:"schema,omitempty"`
|
||||
|
||||
// EnumValues is a map of possible setter values to actual field values.
|
||||
// If EnumValues is specified, then the value set the by user 1) MUST
|
||||
// be present in the enumValues map as a key, and 2) the map entry value
|
||||
// MUST be used as the value to set in the configuration (rather than the key)
|
||||
// Example -- may be used for t-shirt sizing values by allowing cpu to be
|
||||
// set to small, medium or large, and then mapping these values to cpu values -- 0.5, 2, 8
|
||||
EnumValues map[string]string `yaml:"enumValues,omitempty"`
|
||||
|
||||
// Required indicates that the setter must be set by package consumer before
|
||||
// live apply/preview. This field is added to the setter definition to record
|
||||
// the package publisher's intent to make the setter required to be set.
|
||||
Required bool `yaml:"required,omitempty"`
|
||||
|
||||
// IsSet indicates the specified field has been explicitly assigned.
|
||||
IsSet bool `yaml:"isSet,omitempty"`
|
||||
}
|
||||
|
||||
func (sd SetterDefinition) AddToFile(path string) error {
|
||||
return yaml.UpdateFile(sd, path)
|
||||
}
|
||||
|
||||
func (sd SetterDefinition) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
key := fieldmeta.SetterDefinitionPrefix + sd.Name
|
||||
|
||||
definitions, err := object.Pipe(yaml.LookupCreate(
|
||||
yaml.MappingNode, openapi.SupplementaryOpenAPIFieldName, "definitions"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
setterDef, err := definitions.Pipe(yaml.LookupCreate(yaml.MappingNode, key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sd.Schema != "" {
|
||||
schNode, err := yaml.ConvertJSONToYamlNode(sd.Schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = definitions.PipeE(yaml.SetField(key, schNode))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// don't write the schema to the extension
|
||||
sd.Schema = ""
|
||||
}
|
||||
|
||||
if sd.Description != "" {
|
||||
err = setterDef.PipeE(yaml.FieldSetter{Name: "description", StringValue: sd.Description})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// don't write the description to the extension
|
||||
sd.Description = ""
|
||||
}
|
||||
|
||||
if sd.Type != "" {
|
||||
err = setterDef.PipeE(yaml.FieldSetter{Name: "type", StringValue: sd.Type})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// don't write the type to the extension
|
||||
sd.Type = ""
|
||||
}
|
||||
|
||||
ext, err := setterDef.Pipe(yaml.LookupCreate(yaml.MappingNode, K8sCliExtensionKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := yaml.Marshal(sd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
y, err := yaml.Parse(string(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := ext.PipeE(yaml.SetField("setter", y)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return object, nil
|
||||
}
|
||||
|
||||
// SetterDefinition may be used to update a files OpenAPI definitions with a new substitution.
|
||||
type SubstitutionDefinition struct {
|
||||
// Name is the name of the substitution to create or update
|
||||
Name string `yaml:"name"`
|
||||
|
||||
// Pattern is the substitution pattern into which setter values are substituted
|
||||
Pattern string `yaml:"pattern"`
|
||||
|
||||
// Values are setters which are substituted into pattern to produce a field value
|
||||
Values []Value `yaml:"values"`
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
// Marker is the string marker in pattern that is replace by the referenced setter.
|
||||
Marker string `yaml:"marker"`
|
||||
|
||||
// Ref is a reference to a setter to pull the replacement value from.
|
||||
Ref string `yaml:"ref"`
|
||||
}
|
||||
|
||||
func (sd SubstitutionDefinition) AddToFile(path string) error {
|
||||
return yaml.UpdateFile(sd, path)
|
||||
}
|
||||
|
||||
func (sd SubstitutionDefinition) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
// create the substitution extension value by marshalling the SubstitutionDefinition itself
|
||||
b, err := yaml.Marshal(sd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sub, err := yaml.Parse(string(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// lookup or create the definition for the substitution
|
||||
defKey := fieldmeta.SubstitutionDefinitionPrefix + sd.Name
|
||||
def, err := object.Pipe(yaml.LookupCreate(
|
||||
yaml.MappingNode, openapi.SupplementaryOpenAPIFieldName, "definitions", defKey, "x-k8s-cli"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// set the substitution on the definition
|
||||
if err := def.PipeE(yaml.SetField("substitution", sub)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return object, nil
|
||||
}
|
||||
@@ -1,414 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestAdd_Filter(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
add Add
|
||||
input string
|
||||
expected string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "add-replicas",
|
||||
add: Add{
|
||||
FieldValue: "3",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$openapi":"replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "add-replicas-annotations",
|
||||
add: Add{
|
||||
FieldValue: "3",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
something: 3
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
something: 3 # {"$openapi":"replicas"}
|
||||
spec:
|
||||
replicas: 3 # {"$openapi":"replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "add-replicas-name",
|
||||
add: Add{
|
||||
FieldValue: "3",
|
||||
FieldName: "replicas",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
something: 3
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
something: 3
|
||||
spec:
|
||||
replicas: 3 # {"$openapi":"replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "add-replicas-2x",
|
||||
add: Add{
|
||||
FieldValue: "3",
|
||||
FieldName: "replicas",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
replicas: 3
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
replicas: 3 # {"$openapi":"replicas"}
|
||||
spec:
|
||||
replicas: 3 # {"$openapi":"replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "add-replicas-1x",
|
||||
add: Add{
|
||||
FieldValue: "3",
|
||||
FieldName: "spec.replicas",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
replicas: 3
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
replicas: 3
|
||||
spec:
|
||||
replicas: 3 # {"$openapi":"replicas"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "add-field-inside-sequence",
|
||||
add: Add{
|
||||
FieldValue: "/usr/share/nginx",
|
||||
FieldName: "spec.containers.volumeMounts.mountPath",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.mountPath",
|
||||
},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
volumeMounts:
|
||||
- name: nginx
|
||||
mountPath: /usr/share/nginx
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
volumeMounts:
|
||||
- name: nginx
|
||||
mountPath: /usr/share/nginx # {"$openapi":"mountPath"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "add-replicas-error",
|
||||
add: Add{
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
},
|
||||
err: "must specify either fieldName or fieldValue",
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "ref has . in name of setter",
|
||||
add: Add{
|
||||
FieldValue: "3",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.foo.bar",
|
||||
},
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3 # {"$openapi":"foo.bar"}
|
||||
`,
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// parse the input to be modified
|
||||
r, err := yaml.Parse(test.input)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// invoke add
|
||||
result, err := test.add.Filter(r)
|
||||
if test.err != "" {
|
||||
if !assert.Equal(t, test.err, err.Error()) {
|
||||
t.FailNow()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// compare the actual and expected output
|
||||
actual, err := result.String()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
actual = strings.TrimSpace(actual)
|
||||
expected := strings.TrimSpace(test.expected)
|
||||
if !assert.Equal(t, expected, actual) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var resourcefile = `apiVersion: resource.dev/v1alpha1
|
||||
kind: resourcefile
|
||||
metadata:
|
||||
name: hello-world-set
|
||||
upstream:
|
||||
type: git
|
||||
git:
|
||||
commit: 5c1c019b59299a4f6c7edd1ff5ff54d720621bbe
|
||||
directory: /package-examples/helloworld-set
|
||||
ref: v0.1.0
|
||||
packageMetadata:
|
||||
shortDescription: example package using setters`
|
||||
|
||||
func TestAdd_Filter2(t *testing.T) {
|
||||
path := filepath.Join(os.TempDir(), "resourcefile")
|
||||
|
||||
// write initial resourcefile to temp path
|
||||
err := os.WriteFile(path, []byte(resourcefile), 0644)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// add a setter definition
|
||||
sd := SetterDefinition{
|
||||
Name: "image",
|
||||
Value: "1",
|
||||
}
|
||||
|
||||
err = sd.AddToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// update setter definition
|
||||
sd2 := SetterDefinition{
|
||||
Name: "image",
|
||||
Value: "2",
|
||||
}
|
||||
|
||||
err = sd2.AddToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
expected := `apiVersion: resource.dev/v1alpha1
|
||||
kind: resourcefile
|
||||
metadata:
|
||||
name: hello-world-set
|
||||
upstream:
|
||||
type: git
|
||||
git:
|
||||
commit: 5c1c019b59299a4f6c7edd1ff5ff54d720621bbe
|
||||
directory: /package-examples/helloworld-set
|
||||
ref: v0.1.0
|
||||
packageMetadata:
|
||||
shortDescription: example package using setters
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "2"
|
||||
`
|
||||
assert.Equal(t, expected, string(b))
|
||||
}
|
||||
|
||||
func TestAddUpdateSubstitution(t *testing.T) {
|
||||
path := filepath.Join(os.TempDir(), "resourcefile")
|
||||
|
||||
// write initial resourcefile to temp path
|
||||
err := os.WriteFile(path, []byte(resourcefile), 0644)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
value1 := Value{
|
||||
Marker: "IMAGE_NAME",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.image-name",
|
||||
}
|
||||
|
||||
value2 := Value{
|
||||
Marker: "IMAGE_TAG",
|
||||
Ref: "#/definitions/io.k8s.cli.setters.image-tag",
|
||||
}
|
||||
|
||||
values := []Value{value1, value2}
|
||||
|
||||
// add a setter definition
|
||||
subd := SubstitutionDefinition{
|
||||
Name: "image",
|
||||
Pattern: "IMAGE_NAME:IMAGE_TAG",
|
||||
Values: values,
|
||||
}
|
||||
|
||||
err = subd.AddToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// update setter definition
|
||||
subd2 := SubstitutionDefinition{
|
||||
Name: "image",
|
||||
Pattern: "IMAGE_NAME:IMAGE_TAG2",
|
||||
}
|
||||
|
||||
err = subd2.AddToFile(path)
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
expected := `apiVersion: resource.dev/v1alpha1
|
||||
kind: resourcefile
|
||||
metadata:
|
||||
name: hello-world-set
|
||||
upstream:
|
||||
type: git
|
||||
git:
|
||||
commit: 5c1c019b59299a4f6c7edd1ff5ff54d720621bbe
|
||||
directory: /package-examples/helloworld-set
|
||||
ref: v0.1.0
|
||||
packageMetadata:
|
||||
shortDescription: example package using setters
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.substitutions.image:
|
||||
x-k8s-cli:
|
||||
substitution:
|
||||
name: image
|
||||
pattern: IMAGE_NAME:IMAGE_TAG2
|
||||
values: []
|
||||
`
|
||||
assert.Equal(t, expected, string(b))
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Delete delete openAPI definition references from resource fields.
|
||||
type Delete struct {
|
||||
// Name is the name of the openAPI definition to delete.
|
||||
Name string
|
||||
|
||||
// DefinitionPrefix is the prefix of the OpenAPI definition type
|
||||
DefinitionPrefix string
|
||||
|
||||
SettersSchema *spec.Schema
|
||||
}
|
||||
|
||||
// Filter implements yaml.Filter
|
||||
func (d *Delete) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
return object, accept(d, object, d.SettersSchema)
|
||||
}
|
||||
|
||||
func (d *Delete) visitSequence(_ *yaml.RNode, _ string, _ *openapi.ResourceSchema) error {
|
||||
// no-op
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Delete) visitMapping(object *yaml.RNode, _ string, _ *openapi.ResourceSchema) error {
|
||||
fieldRNodes, err := object.FieldRNodes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// for each of the field node key visit it as scalar to delete the array setter comment
|
||||
for _, fieldRNode := range fieldRNodes {
|
||||
err := d.visitScalar(fieldRNode, "", nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// visitScalar implements visitor
|
||||
// visitScalar will remove the reference on each scalar field whose name matches.
|
||||
func (d *Delete) visitScalar(object *yaml.RNode, _ string, _, _ *openapi.ResourceSchema) error {
|
||||
// read the field metadata
|
||||
fm := fieldmeta.FieldMeta{SettersSchema: d.SettersSchema}
|
||||
if err := fm.Read(object); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete the reference iff the ref string matches with DefinitionPrefix
|
||||
if strings.HasSuffix(fm.Schema.Ref.String(), d.DefinitionPrefix+d.Name) {
|
||||
// remove the ref on the metadata
|
||||
fm.Schema.Ref = spec.Ref{}
|
||||
|
||||
// write the field metadata
|
||||
if err := fm.Write(object); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleterDefinition may be used to update a files OpenAPI definitions with a new setter.
|
||||
type DeleterDefinition struct {
|
||||
// Name is the name of the openAPI definition to delete.
|
||||
Name string `yaml:"name"`
|
||||
|
||||
// DefinitionPrefix is the prefix of the OpenAPI definition type
|
||||
DefinitionPrefix string `yaml:"definitionPrefix"`
|
||||
}
|
||||
|
||||
func (dd DeleterDefinition) DeleteFromFile(path string) error {
|
||||
return yaml.UpdateFile(dd, path)
|
||||
}
|
||||
|
||||
// SubstReferringDefinition check if the definition used in substitution and return the substitution name if true
|
||||
func SubstReferringDefinition(definitions *yaml.RNode, key string) string {
|
||||
fieldNames, err := definitions.Fields()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
for _, fieldName := range fieldNames {
|
||||
// the definition key -- contains the substitution name
|
||||
subkey := definitions.Field(fieldName).Key.YNode().Value
|
||||
if strings.HasPrefix(subkey, fieldmeta.SubstitutionDefinitionPrefix) {
|
||||
substNode, err := definitions.Field(fieldName).Value.Pipe(yaml.Lookup(K8sCliExtensionKey, "substitution"))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
b, err := substNode.MarshalJSON()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
subst := SubstitutionDefinition{}
|
||||
if err := yaml.Unmarshal(b, &subst); err != nil {
|
||||
continue
|
||||
}
|
||||
// Check the ref in value to see if it contains the setter key
|
||||
for _, v := range subst.Values {
|
||||
if strings.HasSuffix(v.Ref, key) {
|
||||
return subst.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (dd DeleterDefinition) Filter(object *yaml.RNode) (*yaml.RNode, error) {
|
||||
key := dd.DefinitionPrefix + dd.Name
|
||||
var defType string
|
||||
|
||||
switch dd.DefinitionPrefix {
|
||||
case fieldmeta.SubstitutionDefinitionPrefix:
|
||||
defType = "substitution"
|
||||
case fieldmeta.SetterDefinitionPrefix:
|
||||
defType = "setter"
|
||||
default:
|
||||
return nil, errors.Errorf("the input delete definitionPrefix does't match any of openAPI definitions, "+
|
||||
"allowed values [%s, %s]", fieldmeta.SetterDefinitionPrefix, fieldmeta.SubstitutionDefinitionPrefix)
|
||||
}
|
||||
|
||||
definitions, err := object.Pipe(yaml.Lookup(openapi.SupplementaryOpenAPIFieldName, "definitions"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// return error if the setter to be deleted doesn't exist
|
||||
if definitions == nil || definitions.Field(key) == nil {
|
||||
return nil, errors.Errorf("%s %q does not exist", defType, dd.Name)
|
||||
}
|
||||
|
||||
subst := SubstReferringDefinition(definitions, key)
|
||||
|
||||
if subst != "" {
|
||||
return nil, errors.Errorf("%s %q is used in substitution %q, please delete the parent substitution first", defType, dd.Name, subst)
|
||||
}
|
||||
|
||||
_, err = definitions.Pipe(yaml.FieldClearer{Name: key})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// remove definitions if it's empty
|
||||
_, err = object.Pipe(yaml.Lookup(openapi.SupplementaryOpenAPIFieldName), yaml.FieldClearer{Name: "definitions", IfEmpty: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// remove openApi if it's empty
|
||||
_, err = object.Pipe(yaml.FieldClearer{Name: openapi.SupplementaryOpenAPIFieldName, IfEmpty: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return object, nil
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||
)
|
||||
|
||||
var resourcefile2 = `apiVersion: resource.dev/v1alpha1
|
||||
kind: resourcefile
|
||||
metadata:
|
||||
name: hello-world-set
|
||||
upstream:
|
||||
type: git
|
||||
git:
|
||||
commit: 5c1c019b59299a4f6c7edd1ff5ff54d720621bbe
|
||||
directory: /package-examples/helloworld-set
|
||||
ref: v0.1.0
|
||||
packageMetadata:
|
||||
shortDescription: example package using setters
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.image:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: image
|
||||
value: "2"
|
||||
io.k8s.cli.setters.tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "sometag"
|
||||
`
|
||||
|
||||
func TestDelete_Filter2(t *testing.T) {
|
||||
path := filepath.Join(os.TempDir(), "resourcefile2")
|
||||
|
||||
// write initial resourcefile to temp path
|
||||
err := os.WriteFile(path, []byte(resourcefile2), 0644)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// add a deleter definition
|
||||
dd := DeleterDefinition{
|
||||
Name: "image",
|
||||
DefinitionPrefix: fieldmeta.SetterDefinitionPrefix,
|
||||
}
|
||||
|
||||
err = dd.DeleteFromFile(path)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
expected := `apiVersion: resource.dev/v1alpha1
|
||||
kind: resourcefile
|
||||
metadata:
|
||||
name: hello-world-set
|
||||
upstream:
|
||||
type: git
|
||||
git:
|
||||
commit: 5c1c019b59299a4f6c7edd1ff5ff54d720621bbe
|
||||
directory: /package-examples/helloworld-set
|
||||
ref: v0.1.0
|
||||
packageMetadata:
|
||||
shortDescription: example package using setters
|
||||
openAPI:
|
||||
definitions:
|
||||
io.k8s.cli.setters.tag:
|
||||
x-k8s-cli:
|
||||
setter:
|
||||
name: tag
|
||||
value: "sometag"
|
||||
`
|
||||
assert.Equal(t, expected, string(b))
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Package setters2 contains libraries for setting resource field values from OpenAPI setter
|
||||
// extensions.
|
||||
//
|
||||
// Setters
|
||||
//
|
||||
// Setters are used to programmatically set configuration field values -- e.g. through a cli or ui.
|
||||
//
|
||||
// Setters are defined through OpenAPI definitions using the x-k8s-cli extension.
|
||||
// Note: additional OpenAPI definitions may be registered through openapi.AddSchema([]byte)
|
||||
//
|
||||
// Example OpenAPI schema containing a setter:
|
||||
//
|
||||
// {
|
||||
// "definitions": {
|
||||
// "io.k8s.cli.setters.replicas": {
|
||||
// "x-k8s-cli": {
|
||||
// "setter": {
|
||||
// "name": "replicas",
|
||||
// "value": "4"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Setter fields:
|
||||
//
|
||||
// x-k8s-cli.setter.name: name of the setter
|
||||
// x-k8s-cli.setter.value: value of the setter that should be applied to fields
|
||||
//
|
||||
// The setter definition key must be of the form "io.k8s.cli.setters.NAME", where NAME matches the
|
||||
// value of "x-k8s-cli.setter.name".
|
||||
//
|
||||
// When Set.Filter is called, the named setter will have its value applied to all resource
|
||||
// fields referencing it.
|
||||
//
|
||||
// Fields may reference setters through a yaml comment containing the serialized JSON OpenAPI.
|
||||
//
|
||||
// Example Deployment resource with a "spec.replicas" field set by the "replicas" setter:
|
||||
//
|
||||
// apiVersion: apps/v1
|
||||
// kind: Deployment
|
||||
// metadata:
|
||||
// name: nginx-deployment
|
||||
// spec:
|
||||
// replicas: 4 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
//
|
||||
// If the OpenAPI io.k8s.cli.setters.replicas x-k8s-cli.setter.value was changed from "4" to "5",
|
||||
// then calling Set{Name: "replicas"}.Filter(deployment) would update the Deployment spec.replicas
|
||||
// value from 4 to 5.
|
||||
//
|
||||
// Updated OpenAPI:
|
||||
//
|
||||
// {
|
||||
// "definitions": {
|
||||
// "io.k8s.cli.setters.replicas": {
|
||||
// "x-k8s-cli": {
|
||||
// "setter": {
|
||||
// "name": "replicas",
|
||||
// "value": "5"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Updated Deployment Configuration:
|
||||
//
|
||||
// apiVersion: apps/v1
|
||||
// kind: Deployment
|
||||
// metadata:
|
||||
// name: nginx-deployment
|
||||
// spec:
|
||||
// replicas: 5 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
|
||||
//
|
||||
// Substitutions
|
||||
//
|
||||
// Substitutions are used to programmatically set configuration field values using multiple
|
||||
// setters which are substituted into a pattern string.
|
||||
//
|
||||
// Substitutions may be used when a field value does not cleanly map to a single setter, but
|
||||
// instead matches some string pattern where setters may be substituted in.
|
||||
//
|
||||
// Fields may reference substitutions the same way they do setters, however substitutions
|
||||
// reference setters from which they are derived.
|
||||
//
|
||||
// Example OpenAPI schema containing a substitution derived from 2 setters:
|
||||
//
|
||||
// {
|
||||
// "definitions": {
|
||||
// "io.k8s.cli.setters.image-name": {
|
||||
// "x-k8s-cli": {
|
||||
// "setter": {
|
||||
// "name": "image-name",
|
||||
// "value": "nginx"
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// "io.k8s.cli.setters.image-tag": {
|
||||
// "x-k8s-cli": {
|
||||
// "setter": {
|
||||
// "name": "image-tag",
|
||||
// "value": "1.8.1"
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// "io.k8s.cli.substitutions.image-name-tag": {
|
||||
// "x-k8s-cli": {
|
||||
// "substitution": {
|
||||
// "name": "image-name-tag",
|
||||
// "pattern": "IMAGE_NAME:IMAGE_TAG",
|
||||
// "values": [
|
||||
// {"marker": "IMAGE_NAME", "ref": "#/definitions/io.k8s.cli.setters.image-name"}
|
||||
// {"marker": "IMAGE_TAG", "ref": "#/definitions/io.k8s.cli.setters.image-tag"}
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Substitution Fields.
|
||||
//
|
||||
// x-k8s-cli.substitution.name: name of the substitution
|
||||
// x-k8s-cli.substitution.pattern: string pattern to substitute markers into
|
||||
// x-k8s-cli.substitution.values.marker: the marker substring within pattern to replace
|
||||
// x-k8s-cli.substitution.values.ref: the setter ref containing the value to replace the marker with
|
||||
//
|
||||
// The substitution is composed of a "pattern" containing markers, and a list of setter "values"
|
||||
// which are substituted into the markers.
|
||||
//
|
||||
// Example Deployment with substitution:
|
||||
//
|
||||
// apiVersion: apps/v1
|
||||
// kind: Deployment
|
||||
// metadata:
|
||||
// name: nginx-deployment
|
||||
// spec:
|
||||
// template:
|
||||
// spec:
|
||||
// containers:
|
||||
// - name: nginx
|
||||
// image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-name-tag"}
|
||||
//
|
||||
// spec.template.spec.containers[name=nginx].image is set by the "image" substitution any time
|
||||
// either "image-name" or "image-tag" is set. Whenever any setter referenced by a substitution
|
||||
// is set, the substitution will be recalculated by substituting its values into its pattern.
|
||||
//
|
||||
//
|
||||
// If the OpenAPI io.k8s.cli.setters.image-name x-k8s-cli.setter.value was changed from "1.8.1"
|
||||
// to "1.8.2", then calling either Set{Name: "image-name"}.Filter(deployment) or
|
||||
// Set{Name: "image-tag"}.Filter(deployment) would update the Deployment field
|
||||
// spec.template.spec.container[name=nginx].image from "nginx:1.8.1" to "nginx:1.8.2".
|
||||
//
|
||||
// Adding Field References
|
||||
//
|
||||
// References to setters and substitutions may be added to fields using the Add Filter.
|
||||
// Add will write a JSON OpenAPI string as a comment to any fields matching the specified
|
||||
// FieldName add FieldValue.
|
||||
package setters2
|
||||
@@ -1,82 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// ExampleAdd demonstrates adding a setter reference to fields.
|
||||
func ExampleAdd_fieldName() {
|
||||
deployment := `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
something: 3
|
||||
spec:
|
||||
replicas: 3
|
||||
`
|
||||
|
||||
object := yaml.MustParse(deployment) // parse the configuration
|
||||
err := object.PipeE(&Add{
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
FieldName: "replicas",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Print the object with the update value
|
||||
fmt.Println(object.MustString())
|
||||
|
||||
// Output:
|
||||
// apiVersion: apps/v1
|
||||
// kind: Deployment
|
||||
// metadata:
|
||||
// name: nginx-deployment
|
||||
// annotations:
|
||||
// something: 3
|
||||
// spec:
|
||||
// replicas: 3 # {"$openapi":"replicas"}
|
||||
}
|
||||
|
||||
// ExampleAdd demonstrates adding a setter reference to fields.
|
||||
func ExampleAdd_fieldValue() {
|
||||
deployment := `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
something: 3
|
||||
spec:
|
||||
replicas: 3
|
||||
`
|
||||
|
||||
object := yaml.MustParse(deployment) // parse the configuration
|
||||
err := object.PipeE(&Add{
|
||||
Ref: "#/definitions/io.k8s.cli.setters.replicas",
|
||||
FieldValue: "3",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Print the object with the update value
|
||||
fmt.Println(object.MustString())
|
||||
|
||||
// Output:
|
||||
// apiVersion: apps/v1
|
||||
// kind: Deployment
|
||||
// metadata:
|
||||
// name: nginx-deployment
|
||||
// annotations:
|
||||
// something: 3 # {"$openapi":"replicas"}
|
||||
// spec:
|
||||
// replicas: 3 # {"$openapi":"replicas"}
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package setters2
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// List lists the setters specified in the OpenAPI
|
||||
// excludes the subpackages which contain file with
|
||||
// name OpenAPIFileName in them
|
||||
type List struct {
|
||||
Name string
|
||||
|
||||
OpenAPIFileName string
|
||||
|
||||
Setters []SetterDefinition
|
||||
|
||||
Substitutions []SubstitutionDefinition
|
||||
|
||||
SettersSchema *spec.Schema
|
||||
}
|
||||
|
||||
// ListSetters initializes l.Setters with the setters from the OpenAPI definitions in the file
|
||||
func (l *List) ListSetters(openAPIPath, resourcePath string) error {
|
||||
y, err := yaml.ReadFile(openAPIPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return l.listSetters(y, resourcePath)
|
||||
}
|
||||
|
||||
// ListSubst initializes l.Substitutions with the substitutions from the OpenAPI definitions in the file
|
||||
func (l *List) ListSubst(openAPIPath string) error {
|
||||
y, err := yaml.ReadFile(openAPIPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return l.listSubst(y)
|
||||
}
|
||||
|
||||
func (l *List) listSetters(object *yaml.RNode, resourcePath string) error {
|
||||
// read the OpenAPI definitions
|
||||
def, err := object.Pipe(yaml.LookupCreate(yaml.MappingNode, "openAPI", "definitions"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if yaml.IsMissingOrNull(def) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// iterate over definitions -- find those that are setters
|
||||
err = def.VisitFields(func(node *yaml.MapNode) error {
|
||||
setter := SetterDefinition{}
|
||||
|
||||
// the definition key -- contains the setter name
|
||||
key := node.Key.YNode().Value
|
||||
|
||||
if !strings.HasPrefix(key, fieldmeta.SetterDefinitionPrefix) {
|
||||
// not a setter -- doesn't have the right prefix
|
||||
return nil
|
||||
}
|
||||
|
||||
setterNode, err := node.Value.Pipe(yaml.Lookup(K8sCliExtensionKey, "setter"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if yaml.IsMissingOrNull(setterNode) {
|
||||
// has the setter prefix, but missing the setter extension
|
||||
return errors.Errorf("missing x-k8s-cli.setter for %s", key)
|
||||
}
|
||||
|
||||
// unmarshal the yaml for the setter extension into the definition struct
|
||||
b, err := setterNode.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := yaml.Unmarshal([]byte(b), &setter); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if l.Name != "" && l.Name != setter.Name {
|
||||
// not the setter that was requested by list
|
||||
return nil
|
||||
}
|
||||
|
||||
// the description is not part of the extension, and should be pulled out
|
||||
// separately from the extension values.
|
||||
description := node.Value.Field("description")
|
||||
if description != nil {
|
||||
setter.Description = description.Value.YNode().Value
|
||||
}
|
||||
|
||||
// count the number of fields set by this setter
|
||||
setter.Count, err = l.count(resourcePath, setter.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.Setters = append(l.Setters, setter)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// sort the setters by their name
|
||||
sort.Slice(l.Setters, func(i, j int) bool {
|
||||
return l.Setters[i].Name < l.Setters[j].Name
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *List) listSubst(object *yaml.RNode) error {
|
||||
// read the OpenAPI definitions
|
||||
def, err := object.Pipe(yaml.LookupCreate(yaml.MappingNode, "openAPI", "definitions"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if yaml.IsMissingOrNull(def) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// iterate over definitions -- find those that are substitutions
|
||||
err = def.VisitFields(func(node *yaml.MapNode) error {
|
||||
subst := SubstitutionDefinition{}
|
||||
|
||||
// the definition key -- contains the substitution name
|
||||
key := node.Key.YNode().Value
|
||||
|
||||
if !strings.HasPrefix(key, fieldmeta.SubstitutionDefinitionPrefix) {
|
||||
// not a substitution -- doesn't have the right prefix
|
||||
return nil
|
||||
}
|
||||
|
||||
substNode, err := node.Value.Pipe(yaml.Lookup(K8sCliExtensionKey, "substitution"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if yaml.IsMissingOrNull(substNode) {
|
||||
// has the substitution prefix, but missing the setter extension
|
||||
return errors.Errorf("missing x-k8s-cli.substitution for %s", key)
|
||||
}
|
||||
|
||||
// unmarshal the yaml for the substitution extension into the definition struct
|
||||
b, err := substNode.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := yaml.Unmarshal([]byte(b), &subst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if l.Name != "" && l.Name != subst.Name {
|
||||
// not the substitution that was requested by list
|
||||
return nil
|
||||
}
|
||||
|
||||
l.Substitutions = append(l.Substitutions, subst)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// sort the substitutions by their name
|
||||
sort.Slice(l.Substitutions, func(i, j int) bool {
|
||||
return l.Substitutions[i].Name < l.Substitutions[j].Name
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// count returns the number of fields set by the setter with name
|
||||
// this excludes all the subpackages with openAPI file in them
|
||||
// set filter is leveraged for this but the resources are not written
|
||||
// back to files as only LocalPackageReader is invoked and not writer
|
||||
func (l *List) count(path, name string) (int, error) {
|
||||
s := &Set{Name: name, SettersSchema: l.SettersSchema}
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.LocalPackageReader{PackagePath: path, PackageFileName: l.OpenAPIFileName}},
|
||||
Filters: []kio.Filter{kio.FilterAll(s)},
|
||||
}.Execute()
|
||||
|
||||
return s.Count, err
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user