Compare commits

...

74 Commits

Author SHA1 Message Date
Natasha Sarkar
cbbd1599f2 Revert "Disable CI Code Checks if all PR changes fall under site/" 2023-09-29 09:50:39 -05:00
Kubernetes Prow Robot
4b34ff3075 Merge pull request #5132 from samstride/issue/5127
Update README.md
2023-09-28 12:10:34 -07:00
Kubernetes Prow Robot
654d7953d9 Merge pull request #5193 from varshaprasad96/refactor/api
[refactor]: Internalize loader api
2023-09-28 08:36:32 -07:00
Varsha Prasad Narsing
7911b2c001 [refactor]: Internalize loader api
This PR intends to move the loader api to
internal. Only the necessary methods which
are needed for the api have been put into
`pkg/loader.go`.

Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
2023-09-28 10:49:45 -04:00
Nick
e3b9afcfaa Add CLI page to Reference Section (#5304)
* Add CLI page

Start operations section

Refine operations section

Add common operations

Address comments

* Move the cli content to the CLI section

Update the CLI description
2023-09-22 23:30:56 -07:00
Kubernetes Prow Robot
af781bfa5e Merge pull request #5313 from bugoverdose/docs/api
Add API section to Reference Section
2023-09-22 22:56:55 -07:00
Kubernetes Prow Robot
7dd9637b1a Merge pull request #5340 from koba1t/add_koba1t_to_approvers
add koba1t to approvers
2023-09-22 13:48:49 -07:00
koba1t
ddce57b585 add koba1t to approvers 2023-09-23 03:37:24 +09:00
Natasha Sarkar
11fc419cee be more specific about reqs in contributing guide (#5330)
* be more specific about reqs in contributing guide

* fix wording
2023-09-22 11:14:48 -07:00
Kubernetes Prow Robot
a8dec66a84 Merge pull request #4486 from koba1t/feature/add_edit_set_labels_subcommand_add_option_for_labels_on_without_selector
add: "edit add labels" command adds an option for labels without selector
2023-09-22 09:36:50 -07:00
Kubernetes Prow Robot
7e541ca380 Merge pull request #5332 from roopeshvs/master
Disable CI Code Checks if all PR changes fall under `site/`
2023-09-20 14:46:02 -07:00
Kubernetes Prow Robot
1bd58bdc15 Merge pull request #5327 from stormqueen1990/feat/add-remove-configmap-command
feat: add remove configmap command
2023-09-20 14:32:01 -07:00
Mauren Berti
9107fa3c81 fix: changes from code review
* Replace last assert directive with require in the test.
* Change copyright notice.
2023-09-19 22:29:33 -04:00
Roopesh V S
433be59ea7 ignore pr code checks for docs only changes 2023-09-18 19:49:11 +05:30
Jeong Jinwoo
2c444d6bf4 docs(api/kustomizaion-file): migrate field sections 2023-09-17 16:05:32 +09:00
yugo kobayashi
59696d1ace fix a patch files accept multiple patches (#5194)
* fix a patch files accept multiple patches

* fix comments and variable name

* add error handling when using not allowed multiple strategic-merge patches

* fix error message of Multiple Strategic-Merge Patch file

* refactor transformStrategicMerge()

* add TODO comment and test for Multiple JSON patch Yaml documents are not allowed

* refactoring PatchTransformer

* add multiple patch test for PatchTransformer package

* improve error message to PatchTransformer

* refactor const and error message check

* fix some error messages
2023-09-15 16:20:13 -07:00
Jeong Jinwoo
d30e457031 docs(api/patches): fix broken links 2023-09-14 21:01:25 +09:00
Jeong Jinwoo
ae041b5c54 docs(api/generatorOptions): fill up empty links 2023-09-14 20:54:06 +09:00
Jeong Jinwoo
a2b60e4bcb docs(api): fix broken links 2023-09-14 20:52:11 +09:00
Jeong Jinwoo
9d126f6dd8 docs(api/components): add Components guide from cli-experimental
- add reference to Concept page
2023-09-14 12:55:50 +09:00
Jeong Jinwoo
bd8045b648 docs(api/replacements): fix heavy check mark issue 2023-09-14 12:40:57 +09:00
Jeong Jinwoo
5c1a022a3c docs(api): fix broken hyperlinks 2023-09-14 12:40:57 +09:00
Jeong Jinwoo
e19ca5405a docs: setup API section with cli-experimental site 2023-09-14 12:40:54 +09:00
Kubernetes Prow Robot
56d37acc7d Merge pull request #5261 from varshaprasad96/refactor/image
[Refactor] Move image to internal
2023-09-13 10:22:12 -07:00
Mauren Berti
0571a2f15d Incorporate feedback from original PR
* Incorporate feedback left on original PR.
* Add more test cases.
* Minor refactoring and update to copyright notice.
2023-09-12 20:18:45 -04:00
yufei.li
0cdfa5b3dc feat: add remove configmap command
(cherry picked from commit 0d7c56dcf8)

fix: add logging when configmap not exists

(cherry picked from commit 0235f10b09)

fix: correct lint issues

(cherry picked from commit 8ca1f3813b)

fix: Resolve conversation

(cherry picked from commit 927804dfe5)
2023-09-12 19:21:55 -04:00
Anna Song
7c36ed21b3 Document AnnotationsTransformer (#5247)
* Restructure existing Reference docs

Restructure Reference section for site to better match k8s.io. Change
descriptions to complete sentences. Improve instructions to locally load
site.

* Document AnnotationsTransformer on site

Dcoument AnnotationsTransformer API under Reference on site.

* Document required fields

Document required fields and explain effects of optional ones.

* Make site setup instructions more explicit

* Link required K8s fields
2023-09-08 20:54:10 -07:00
Ed Overton
985835f96f perf: limit initSchema calls from openapi.IsNamespaceScoped (#5076)
* test: add openapi.IsNamespaceScoped benchmark

Add a benchmark test for IsNamespaceScoped performance when the default
schema is in use.

* perf: limit initSchema calls from openapi.IsNamespaceScoped

Avoid calling initSchema from openapi.IsNamespaceScoped when possible.
Work done in #4152 introduced a precomputed namespace scope map based on
the default built-in schema. This commit extends that work by avoiding
calls to initSchema when a resource is not found in the precomputed map
and the default built-in schema is in use. In those cases, there is no
benefit to calling initSchema since the precomputed map is exactly what
will be calculated by parsing the default built-in schema.

* fix: delay parsing of default built-in schema

When namespace scope can be determined by the precomputed map but the
type is not present in the precomputed map, delay the parsing of the
default built-in schema.

If the schema to be initialized is the default built-in schema and the
type is not in the precomputed map, then the type will not be found in
the default built-in schema. There is no need to parse the default
built-in schema for that answer; its parsing may be delayed until it
is needed for some other purpose.

In cases where the schema is used solely for namespace scope checks, the
schema might not ever be parsed. Skipping the parsing reduces both
execution time and memory use.

* fix: correct openapi.go's schemaNotParsed value

openapiData initializes with defaultBuiltInSchemaParseStatus set to 0,
so schemaNotParsed should have 0 as its value.
2023-09-08 12:34:30 -07:00
Kubernetes Prow Robot
f81765b96e Merge pull request #5320 from natasha41575/updatedeps
small fixes
2023-09-08 10:06:14 -07:00
natasha41575
a2ceaff053 small fixes 2023-09-08 11:38:23 -05:00
Kubernetes Prow Robot
94181b1be7 Merge pull request #5291 from irvifa/refactor/remove-setters-config-from-kyaml
refactor: Remove setters from kyaml
2023-09-08 07:54:14 -07:00
Kubernetes Prow Robot
169fdd7330 Merge pull request #5196 from ephesused/issue4928-append-honors-key-style
fix: patch additions honor source key style
2023-09-01 09:06:33 -07:00
Ed Overton
78b8139d46 Merge branch 'master' into issue4928-append-honors-key-style 2023-08-30 12:30:49 -04:00
Kubernetes Prow Robot
76f8d2828b Merge pull request #5197 from skitt/json-patch.v5
Switch to json-patch v5
2023-08-28 09:35:45 -07:00
Stephen Kitt
b692e49b1e Switch to json-patch v5
json-patch was bumped to v5 in k/k as a result of
https://github.com/kubernetes/kubernetes/pull/118384; this aligns with
that, but switches to the v5 module, which is documented as
gopkg.in/evanphx/json-patch.v5.

Signed-off-by: Stephen Kitt <skitt@redhat.com>
2023-08-26 08:42:07 +02:00
Kubernetes Prow Robot
bd7f001c26 Merge pull request #5239 from koba1t/chore/change_release_flow
change release flow from using goreleaser to build scripts
2023-08-25 11:54:52 -07:00
Kubernetes Prow Robot
d6ff768298 Merge pull request #5262 from varshaprasad96/refactor/konfig
[Refactor] Internalize konfig constants
2023-08-25 10:18:52 -07:00
Irvi Aini
4947a905fa refactor: Remove setters from kyaml
Kind:

Refactor

Summary:

Setters functionality is provided as a KRM function. We should remove code related to setters in cmd/config and kyaml.
As of now most setters2 and setters usage are related to fork of kpt, however, these:
[fluxcd/image-automation-controller](6827808a1a/pkg/update/filter.go (L24)) with [kyml](6827808a1a/go.mod (L42))
[rancher/fleet](0a6cf6cb92/internal/cmd/controller/controllers/image/update/setters.go (L16)) with [kyaml](0a6cf6cb92/go.mod (L75))

Repositories still using them, They pinned these two into a specific kyaml version. If we decide to go for this removal then we can make a release note that this is actually removed on the next version since we already marked this as deprecated before.
2023-08-24 00:39:47 +08:00
Kubernetes Prow Robot
cd9a16cfab Merge pull request #5273 from natasha41575/pm
add description of PM role to contributing doc
2023-08-22 14:11:55 -07:00
natasha41575
02a53f193d add description of PM role to contributing doc 2023-08-22 15:11:53 -05:00
koba1t
b1717c8a97 change_release_flow_to_build_scripts 2023-08-17 22:57:44 +09:00
Kubernetes Prow Robot
911ddcda40 Merge pull request #5263 from bugoverdose/fix/reject-on-id-change
Fix `reject` needing both current and previous ids
2023-08-14 09:19:22 -07:00
Jeong Jinwoo
9f1d5acdc8 fix: check both prev and current ids for filtering reject targets 2023-08-11 21:37:55 +09:00
Jeong Jinwoo
9756d92a91 test: demonstrate reject not working on targets with changed id 2023-08-11 21:37:07 +09:00
Natasha Sarkar
d32d1937e6 Merge pull request #5254 from natasha41575/unpineverything
release cleanup
2023-08-10 12:03:25 -05:00
Varsha Prasad Narsing
ddcbae54ab [Refactor] Internalize konfig constants
This PR is an effort towards internalizing public APIs.
It moves some of the builtinconstants to internal/

Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
2023-08-09 14:14:48 -04:00
Varsha Prasad Narsing
ca748faa3f [Refactor] Move image/ to internal
This PR is an effort towards reducing the public surface
of APIs. It move image/ to internal/image

Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
(cherry picked from commit 1f5890709fdc17de6f44f42b5138dd0f7e64bc74)
2023-08-09 13:40:07 -04:00
Kubernetes Prow Robot
5e6cbac589 Merge pull request #5203 from varshaprasad96/add/goapi-diff
[Chore] Enable go-api-diff checker
2023-08-07 08:42:32 -07:00
natasha41575
ff75dd6cd5 release cleanup 2023-07-31 12:27:44 -05:00
Natasha Sarkar
f8391994b4 Merge pull request #5253 from natasha41575/pincmdconfigandapi
pin to cmd/config and api
2023-07-31 11:49:43 -05:00
natasha41575
5ce14e5024 pin to cmd/config and api 2023-07-31 11:28:25 -05:00
Natasha Sarkar
ee22c9cab7 Merge pull request #5252 from natasha41575/pinkyaml
pin to kyaml 0.14.3
2023-07-31 11:24:02 -05:00
natasha41575
4a893ce8c6 pin to kyaml 2023-07-31 11:08:00 -05:00
Kubernetes Prow Robot
9ce923ebeb Merge pull request #5245 from yedayak/customization-typo
Fix typo in help for the create subcommand
2023-07-25 12:47:57 -07:00
Yedaya
494a807f28 Fix typo in help for the create subcommand 2023-07-18 09:58:14 +03:00
Varsha Prasad Narsing
820f17c73b [Chore] Enable go-api-diff checker
This PR introduces go-api-diff checker in Makefile
and in CI to make sure we don't end up breaking APIs
unintentionally.

Signed-off-by: Varsha Prasad Narsing <varshaprasad96@gmail.com>
2023-07-12 13:55:07 -04:00
Kubernetes Prow Robot
cd7ba1744e Merge pull request #5189 from annasong20/test-relative-submodules
Add test for issue #5131
2023-07-12 10:03:13 -07:00
Anna Song
da4e881007 Add accumulateResources error tests for local files (#5225)
* Add accumulateResources error tests for local files.

Add tests demonstrating accumulateResources errors when the resource is
a local file. Works to address #4807.

* Improve readability
2023-07-01 11:46:49 -07:00
Natasha Sarkar
878cda7c55 Merge pull request #5215 from natasha41575/unpin
release cleanup
2023-06-19 12:23:20 -05:00
natasha41575
47327616df update latest release version 2023-06-19 12:06:16 -05:00
natasha41575
eeff67d88d unpin everything 2023-06-19 12:05:59 -05:00
Natasha Sarkar
6adf4f294a Merge pull request #5214 from natasha41575/pintoapi
pin to api
2023-06-19 11:53:36 -05:00
natasha41575
5e7cc3437d pintoapi 2023-06-19 11:35:33 -05:00
Ed Overton
096b2c4435 test: add psm test for different key types 2023-06-15 16:12:29 -04:00
Anna Song
129d0f90af Add test for issue #5131 2023-06-14 18:32:12 +00:00
Ed Overton
c76fd5eb85 test: update psm key style test 2023-06-13 14:50:22 -04:00
Ed Overton
691b7d1df3 fix: patch additions honor source key style
When a patch appends a new node, it should honor the key style from the
patch. Prior to this commit, no style was applied, leading to problems
when the key value could be interpreted as a different type based on its
content. For example, "9110" needs quoting to ensure it is seen as a
string in yaml.
2023-06-08 17:21:46 -04:00
Vijay
91078545e6 Update README.md 2023-05-05 09:57:10 +12:00
Vijay
467633c2de Update README.md 2023-05-04 12:30:46 +12:00
Vijay
e44df7b386 Delete images directory 2023-05-03 13:49:41 +12:00
Vijay
bbff6768da Update README.md 2023-05-03 13:49:15 +12:00
Vijay
0020a496f7 Merge branch 'kubernetes-sigs:master' into issue/5127 2023-05-03 13:34:39 +12:00
Vijay
9eddc3c5a7 Update README.md
- Changes to address https://github.com/kubernetes-sigs/kustomize/issues/5127
2023-04-14 18:09:34 +12:00
koba1t
7aa33ff471 add edit add labels command add option for labels without selector 2022-02-24 09:49:31 +09:00
222 changed files with 6027 additions and 7321 deletions

36
.github/workflows/apidiff.yml vendored Normal file
View 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
View 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 }}

View File

@@ -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

View File

@@ -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 \

View File

@@ -8,6 +8,7 @@ aliases:
- knverey
- natasha41575
- annasong20
- koba1t
kustomize-reviewers:
- knverey
- natasha41575

View File

@@ -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

View File

@@ -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"
)

View File

@@ -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"

View File

@@ -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"
`,
},
}

View File

@@ -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

View File

@@ -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

View File

@@ -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=

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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)

View File

@@ -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()
}

View File

@@ -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()

View File

@@ -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"

View File

@@ -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"
)

View File

@@ -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"

View File

@@ -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"
)

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -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"
)

View File

@@ -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"

View File

@@ -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
---

View File

@@ -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))

View File

@@ -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
`)
}

View 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).

View File

@@ -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
View 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)
}

View File

@@ -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)

View File

@@ -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()) {

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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
}

View File

@@ -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

View File

@@ -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=

View File

@@ -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

View File

@@ -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=

View File

@@ -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=

View File

@@ -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

View File

@@ -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

View 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=="
}
}
}

View File

@@ -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"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

View File

@@ -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
}

View File

@@ -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"

View File

@@ -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)
}

View File

@@ -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"}})
}

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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))
}

View File

@@ -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),

View File

@@ -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()),

View 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
}

View 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))
})
}
}

View File

@@ -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

View File

@@ -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=

View File

@@ -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
}

View File

@@ -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()
}

View File

@@ -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)
})
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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)
}
})
}
}

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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

View File

@@ -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"}
}

View File

@@ -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