mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +00:00
Proposal for new command "kustomize localize" (#4590)
* Propose kustomize localize Write mini KEP proposal for new command kustomize localize. * Incomplete commit * Update proposal based on Katrina's feedback Changes are summarized in comments in PR * Address feedback in comments
This commit is contained in:
331
proposals/22-04-localize-command.md
Normal file
331
proposals/22-04-localize-command.md
Normal file
@@ -0,0 +1,331 @@
|
||||
# Localize Command
|
||||
|
||||
**Authors**:
|
||||
|
||||
- annasong20
|
||||
|
||||
**Reviewers**:
|
||||
|
||||
- natasha41575
|
||||
- KnVerey
|
||||
|
||||
**Status**: implementable
|
||||
|
||||
## Summary
|
||||
|
||||
The `kustomize localize` command creates a “localized” copy, of both the target kustomization and files target
|
||||
references, in which the kustomization files contain, instead of remote references, local paths to their downloaded
|
||||
locations. The command is part of an effort to enable `kustomize build` to run without network access.
|
||||
|
||||
## Motivation
|
||||
|
||||
monopole originally proposed the command `kustomize localize`
|
||||
in [this issue](https://github.com/kubernetes-sigs/kustomize/issues/3980).
|
||||
|
||||
Users run `kustomize build` in many environments with limited network access. For example, CI/CD pipelines often only
|
||||
have access to the internal network. Server-side applications like Config Sync are concerned with the security
|
||||
vulnerabilities of git, which `kustomize build` uses to fetch remote files.
|
||||
|
||||
These use cases would benefit from a kustomize solution that downloads all remote files that a `kustomize build` target
|
||||
references, into a copy of target that references the downloaded files instead. Admins could upload the localized copy
|
||||
to an internal repo so that pipelines and applications can run `kustomize build` on the copy without a network
|
||||
dependency.
|
||||
|
||||
The proposed command nearly achieves the solution by downloading all remote files directly referenced by the target or
|
||||
by a recursively referenced kustomization file. The command also downloads remote exec binaries of referenced KRM
|
||||
functions, which are the only potential source of remote files other than kustomizations. The only remote files that
|
||||
this proposal does not cover and that `kustomize build` still needs to run are remote images and custom fields in KRM
|
||||
functions. Downloaded images would live only in local caches and thus, are not worth localizing. Kustomize cannot
|
||||
currently identify custom fields, though this may change with one of the proposed solutions
|
||||
in [issue #4154](https://github.com/kubernetes-sigs/kustomize/issues/4154).
|
||||
|
||||
The proposed command has the added benefit of increasing user confidence in the integrity of their kustomization builds.
|
||||
Locally downloaded files, unlike urls, give users full control of file content. At the same time, the command does this
|
||||
without modifying the original kustomization so that users can always run the command on the original again to fetch
|
||||
upstream changes.
|
||||
|
||||
**Goals:**
|
||||
|
||||
1. This command should localize
|
||||
* all remote files that a kustomization file directly references
|
||||
* remote exec binaries of referenced KRM functions
|
||||
|
||||
This command achieves this goal if, in the absence of remote images and custom fields in KRM
|
||||
functions, `kustomize build` can run on the localized copy without network access.
|
||||
|
||||
**Non-goals:**
|
||||
|
||||
1. This command should not localize remote images or custom fields in KRM functions.
|
||||
2. This command should not copy files that the target kustomization does not reference.
|
||||
3. This command should not serve as a package manager.
|
||||
|
||||
## Proposal
|
||||
|
||||
The command takes the following form:
|
||||
|
||||
<pre>
|
||||
<b>kustomize localize</b> <ins>target</ins> <ins>newDir</ins> [-s <ins>scope</ins>] [-n]
|
||||
</pre>
|
||||
|
||||
where the arguments are:
|
||||
|
||||
* `target`: a directory with a top-level kustomization file that kustomize will localize; can be a path to a local
|
||||
directory or a url to a remote directory
|
||||
* `newDir`: optional destination directory of the localized copy of `target`; if not specified, the destination is a
|
||||
directory named `localized-{target}` in the same directory as `scope`
|
||||
|
||||
and the flags are:
|
||||
* `-s`, `--scope`
|
||||
`scope`: optional root directory, files outside which kustomize is not allowed to copy and localize; if not specified,
|
||||
takes on value of `target`
|
||||
* `-n`, `--no-verify`: do not verify that the outputs of `kustomize build` for `target` and `newDir` are the same after
|
||||
localization
|
||||
|
||||
The command creates a copy of the `target` kustomization and the local files that `target` references at `newDir`. We
|
||||
define the "files that `target` references" as:
|
||||
|
||||
* kustomization files that `target` directly or transitively references
|
||||
* configuration files that referenced kustomization files reference
|
||||
* exec binaries of referenced KRM functions
|
||||
|
||||
Here, configuration file means a non-kustomization yaml file. The command cannot run on `target`s that need
|
||||
the `--load-restrictor LoadRestrictionsNone` flag on `kustomize build`. The command only copies referenced files that
|
||||
reside inside `scope`.
|
||||
|
||||
The command localizes the copy of `target` at `newDir` by downloading all remote files that `target`
|
||||
references. Users do not have executable permission for downloaded exec binaries that KRM functions reference and for
|
||||
each such exec binary, the command will print a warning message to that effect.
|
||||
|
||||
The command creates a new `localized-files` directory, next to the file that referenced the downloaded files, to hold
|
||||
said files. Inside `localized-files`, the downloads are located on path:
|
||||
|
||||
<pre>
|
||||
<ins>domain</ins> / <ins>organization</ins> / <ins>repo</ins> / <ins>version</ins> / <ins>path/to/file/in/repo</ins>
|
||||
</pre>
|
||||
|
||||
where `version` corresponds to the `ref` query string parameter in the url, though ideally `version` ia a stable tag as
|
||||
opposed to a branch.
|
||||
|
||||
The command replaces remote references in `newDir` with the local paths of the downloaded files. To help ensure
|
||||
that `newDir` is a clean copy, the command additionally overwrites absolute path references into `target` to point
|
||||
to `newDir`.
|
||||
|
||||
As a convenience to the user, in the absence of the `--no-verify` flag, the command automatically tries to
|
||||
run `kustomize build`, without any flags, on `target` and the localized `newDir` to compare their outputs. The command
|
||||
indicates success if the outputs match and throws an error with the diff summary otherwise. This check, however, is not
|
||||
useful for certain `target`s, including those that need flags to build. In these cases, the command prints next steps
|
||||
that users can follow to check the output themselves. For example, for `target`s that reference KRM functions with a
|
||||
remote exec binary, the command suggests the user:
|
||||
|
||||
1. add executable permissions for the downloaded exec binaries in `newDir` **that the user trusts**
|
||||
2. run `kustomize build` with flags `--enable-alpha-plugins --enable-exec` and self-verify the outputs
|
||||
|
||||
**Error cases**:
|
||||
|
||||
* `target` does not have a top-level kustomization file
|
||||
* `kustomize build` needs `--load-restrictor LoadRestrictionsNone` to run on `target`
|
||||
* `newDir` already exists
|
||||
* `scope` does not contain `target`
|
||||
* `target` references a local path that traverses outside of `scope`
|
||||
* remote reference does not have a `version`
|
||||
* `localized-files` directory already exists
|
||||
* kustomization file is malformed
|
||||
* cycle of kustomization file references exists
|
||||
* `kustomize build` produces different output for `target` and `newDir` in the absence of `--no-verify`
|
||||
|
||||
Depending on feedback, we may add an `--overwrite` flag in the future to allow users to update an existing `newDir` by
|
||||
running the command again.
|
||||
|
||||
**Warning cases**:
|
||||
|
||||
* KRM function references remote exec binary, in which case the downloaded exec binary is not executable
|
||||
* KRM function has container image that the user might not have locally
|
||||
|
||||
### User Stories
|
||||
|
||||
#### Story 1
|
||||
|
||||
My company’s CI/CD pipeline currently pulls an `example` directory from our internal package management site. I want the
|
||||
CI/CD pipeline to additionally run `kustomize build example/overlay`. My setup looks like this:
|
||||
```shell
|
||||
└── example
|
||||
├── overlay
|
||||
│ └── kustomization.yaml
|
||||
└── base
|
||||
└── kustomization.yaml
|
||||
```
|
||||
```shell
|
||||
# example/overlay/kustomization.yaml
|
||||
resources:
|
||||
- ../base
|
||||
- https://raw.githubusercontent.com/kubernetes-sigs/kustomize/v1.0.6/examples/helloWorld/deployment.yaml
|
||||
```
|
||||
```shell
|
||||
# example/base/kustomization.yaml
|
||||
resources:
|
||||
- github.com/kubernetes-sigs/kustomize/examples/multibases?ref=v1.0.6
|
||||
- https://raw.githubusercontent.com/kubernetes-sigs/kustomize/v1.0.6/examples/helloWorld/configMap.yaml
|
||||
```
|
||||
|
||||
I get an error from `kustomize build` in the pipeline because my configurations have remote references, but my CI/CD
|
||||
pipeline does not have external network access.
|
||||
|
||||
Fortunately, I remember that I can run `kustomize localize` on `example/overlay` on my local machine. I can then upload
|
||||
the localized directory to my company’s internal package management site for the CI/CD pipeline to pull and build
|
||||
instead. I run
|
||||
`kustomize localize example/overlay -s example`, where my `target` is `example/overlay`, I accept the default location
|
||||
and name of `newDir`, and I expand my `scope` to `example` because `example/overlay` references `example/base`. I get
|
||||
the following output:
|
||||
|
||||
```shell
|
||||
$ kustomize localize example/overlay -s example
|
||||
SUCCESS: example/overlay, localized-overlay produce same kustomize build output
|
||||
```
|
||||
|
||||
```shell
|
||||
├── example # old kustomization directory
|
||||
│ ├── overlay
|
||||
│ │ └── kustomization.yaml
|
||||
│ └── base
|
||||
│ └── kustomization.yaml
|
||||
└── localized-overlay # the new, localized kustomization directory
|
||||
├── kustomization.yaml
|
||||
├── base
|
||||
│ ├── kustomization.yaml
|
||||
│ └── localized-files
|
||||
│ └── github.com
|
||||
│ └── kubernetes-sigs
|
||||
│ └── kustomize
|
||||
│ └── v1.0.6
|
||||
│ └── examples
|
||||
│ ├── helloWorld
|
||||
│ │ └── configMap.yaml
|
||||
│ └── multibases
|
||||
│ ├── base
|
||||
│ │ └── pod.yaml
|
||||
│ ├── dev
|
||||
│ ├── kustomization.yaml
|
||||
│ ├── production
|
||||
│ └── staging
|
||||
└── overlay
|
||||
├── kustomization.yaml
|
||||
└── localized-files
|
||||
└── github.com
|
||||
└── kubernetes-sigs
|
||||
└── kustomize
|
||||
└── v1.0.6
|
||||
└── examples
|
||||
└── helloWorld
|
||||
└── deployment.yaml
|
||||
```
|
||||
```shell
|
||||
# localized-overlay/overlay/kustomization.yaml
|
||||
resources:
|
||||
- ../base
|
||||
- ./localized-files/github.com/kubernetes-sigs/kustomize/examples/helloWorld/deployment.yaml
|
||||
```
|
||||
```shell
|
||||
# localized-overlay/base/kustomization.yaml
|
||||
resources:
|
||||
- ./localized-files/github.com/kubernetes-sigs/kustomize/examples/multibases
|
||||
- ./localized-files/github.com/kubernetes-sigs/kustomize/examples/helloWorld/configMap.yaml
|
||||
```
|
||||
|
||||
Now, I upload `localized-overlay` from my local setup to my company’s internal package management site. I change the
|
||||
commands in my CI/CD pipeline to pull `localized-overlay` before running `kustomize build localized-overlay`, and the
|
||||
command executes successfully!
|
||||
|
||||
### Risks and Mitigations
|
||||
|
||||
One could argue that while uploading the localized `newDir` to a repository, a user could accidentally leak Secrets that
|
||||
were originally remote in a more private repo. This event is not very likely, as the servers that the user intends to
|
||||
consume the localized kustomizations often only have access to internal, private networks and repos. Nonetheless, users
|
||||
should have enough context in the case of Secrets to make the right decision. Users should have a basic understanding of
|
||||
the files that their `target` kustomizations reference and of the files that they plan to upload to repos. Secret
|
||||
configurations are also not too difficult to identify.
|
||||
|
||||
Exec binaries that KRM functions reference are a different story. `kustomize localize` downloads remote exec binaries
|
||||
that, if malicious, are capable of almost anything during subsequent `kustomize build` calls. The command mitigates this
|
||||
risk by leaving these downloaded exec binaries without executable permissions and warning the user, as mentioned in
|
||||
**Proposal**. `kustomize build` can only run the exec binary after the user deems the binary safe and changes its
|
||||
permissions.
|
||||
|
||||
Still another risk may be that if a user's kustomization tree is large, `kustomize localize` may be copying files from
|
||||
unexpected locations. The command mitigates this risk with the `scope` flag. If not set, `kustomize localize` only
|
||||
copies files in `target`. Otherwise, the user specifies `scope`and understands that `kustomize localize` only copies
|
||||
files in `scope`. The qualification that the command can only localize `target`s that follow load restrictions helps
|
||||
mitigate this risk as well.
|
||||
|
||||
### Dependencies
|
||||
|
||||
N/A
|
||||
|
||||
### Scalability
|
||||
|
||||
Large kustomization trees slows the performance of `kustomize localize`. These trees can have large local subtrees, have
|
||||
large remote subtrees, be deeply nested, or be wide, with each overlay referencing multiple bases. Regardless of the
|
||||
cause, large kustomization trees inevitably takes longer to copy and download. Parts of the kustomize code are not
|
||||
thread-safe, which precludes parallel execution.
|
||||
|
||||
On a separate note, a chain of remote kustomization directories in which the current kustomization file references the
|
||||
next remote kustomization directories could create a `newDir` with deeply nested `localized-files`. This directory
|
||||
structure would impede users’ navigation of `newDir`. However, this scenario should be unlikely as most kustomizations
|
||||
only consist of a few layers.
|
||||
|
||||
The creation of the `localized-files` directory local to the referencing kustomization file additionally prevents the
|
||||
different layers of kustomization files from sharing the same copy of the remote files. Following the same logic,
|
||||
different potential `target` directories cannot share copies either.
|
||||
|
||||
## Drawbacks
|
||||
|
||||
Users whose layered kustomizations form a complex directory tree structure may have a hard time finding an
|
||||
appropriate `scope`. However, many kustomizations exist in repositories, allowing the user to easily choose the repo
|
||||
root as a valid `scope`. The warning messages that `kustomize localize` outputs for reference paths that extend
|
||||
beyond `scope` should also help.
|
||||
|
||||
## Alternatives
|
||||
|
||||
* Instead of downloading into `newDir`, `kustomize localize` could download all remote files into a directory specified
|
||||
by some global environment variable (like in Golang), which would preclude deeply nested directories and allow
|
||||
different kustomization files to share configurations. On top of that, if `kustomize build` had the added
|
||||
functionality to check for previous downloads of remote references at said global location, `kustomize localize` would
|
||||
not need to overwrite the remote references in `target` to the local downloads. As a result, `kustomize localize`
|
||||
would need to neither write to `target` nor copy `target` into `newDir`. The user would not need to specify a `scope`
|
||||
either. <br></br>
|
||||
|
||||
Despite its advantages, the alternative design violates the self-contained nature of each kustomize layer. Users would
|
||||
be unable to upload a fully localized kustomization directory in version control. Furthermore, this alternative
|
||||
complicates the existing kustomize workflow by requiring the setup of global environment variables.
|
||||
<br></br>
|
||||
|
||||
* The command could, instead of making a copy, modify `target` directly. However, users would not have an easy way to
|
||||
undo the command, which is undesirable.
|
||||
<br></br>
|
||||
|
||||
* Instead of requiring the user to specify a second argument `scope`, the command could by definition limit its copying
|
||||
to `target`. However, in the case of **Story 1**, the command would force the user to set `target` to `example` in
|
||||
order to include `example/base` in the localization of `example/overlay`. The user would then have to create a
|
||||
kustomization file at `example` that points to `example/overlay` under the `resources` field. The creation of the
|
||||
kustomization file solely for this purpose is messy and more work for the user.
|
||||
|
||||
## Rollout Plan
|
||||
|
||||
This command will have at least alpha and GA releases. Depending on user feedback, we may add a beta.
|
||||
|
||||
### Alpha
|
||||
|
||||
This release will not support
|
||||
|
||||
* load restrictions check
|
||||
* KRM functions
|
||||
* absolute paths
|
||||
* `target` kustomization with reference cycles
|
||||
* any verification in the form of `--no-verification` flag or automatically running `kustomize build` at the end of
|
||||
localization
|
||||
|
||||
The entire command will be new in the alpha release, and so will not require an alpha flag. The command will not be
|
||||
available in `kubectl kustomize` either as kubectl only has `kustomize build` builtin.
|
||||
|
||||
### Beta/GA
|
||||
|
||||
This release should have all features documented in this proposal. Though, we may make changes based on user feedback.
|
||||
Reference in New Issue
Block a user