Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal for new command "kustomize localize" #4590

Merged
merged 4 commits into from May 15, 2022
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
270 changes: 133 additions & 137 deletions proposals/22-04-localize-command.md
Expand Up @@ -35,14 +35,20 @@ The proposed command nearly achieves the solution by downloading all remote file
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 kustomize cannot know the form that custom fields will
take. Thus, neither is worth localizing.
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
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.
Expand All @@ -58,16 +64,22 @@ take. Thus, neither is worth localizing.
The command takes the following form:

<pre>
<b>kustomize localize</b> <ins>target</ins> <ins>scope</ins> <ins>newDir</ins>
<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
* `scope`: optional root directory, files outside which kustomize is not allowed to copy and localize; if not specified,
* `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`
* `newDir`: destination directory of the localized copy of `target`
* `-n`, `--no-verify`: do not verify that the outputs of `kustomize build` for `target` and `newDir` are the same after
annasong20 marked this conversation as resolved.
Show resolved Hide resolved
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:
Expand All @@ -76,59 +88,79 @@ define the "files that `target` references" as:
* configuration files that referenced kustomization files reference
* exec binaries of referenced KRM functions

Here, configuration file means a non-kustomization yaml file. The command only copies referenced files that reside
inside `scope`.
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. The
downloaded files placed in a new `localized-files` directory next to the file that referenced the downloaded files.
Inside `localized-files`, the downloads are located on path:
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
annasong20 marked this conversation as resolved.
Show resolved Hide resolved
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>remote-host</ins> / <ins>organization</ins> / <ins>repo</ins> / <ins>version</ins> / <ins>path-to-file-in-repo</ins>
<ins>domain</ins> / <ins>organization</ins> / <ins>repo</ins> / <ins>version</ins> / <ins>path/to/file/in/repo</ins>
</pre>

The command rewrites remote references in `newDir` to the local paths of the downloaded files. To help ensure
where `version` corresponds to the `ref` query string parameter in the url, though ideally `version` ia a stable tag as
annasong20 marked this conversation as resolved.
Show resolved Hide resolved
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
annasong20 marked this conversation as resolved.
Show resolved Hide resolved
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
annasong20 marked this conversation as resolved.
Show resolved Hide resolved

**Error cases**:

* `target` does not have a top-level kustomization file
* `scope` does not contain `target`
* `kustomize build` needs `--load-restrictor LoadRestrictionsNone` to run on `target`
* `newDir` already exists
annasong20 marked this conversation as resolved.
Show resolved Hide resolved
* `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
* remote reference does not have a version
* 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`

**Warning cases**:
* `target` references a local path that traverses outside of `scope`
* KRM function has container image that the user might not have locally
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.

For the warning cases, the command will ignore the reference and continue execution.
**Warning cases**:

If the command runs without any errors or warnings, `kustomize build` without `--load-restrictor LoadRestrictionsNone`
on `target` and `newDir` should produce the same output.
* 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
Expand All @@ -141,144 +173,104 @@ 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 example/localized-overlay`, but I get the following warning:

```
$ kustomize localize example/overlay example/localized-overlay
Warning: File example/overlay/kustomization.yaml refers to ../base on line 2. This reference is outside of scope:
example/overlay. kustomize localize will skip this path.
```

because I forgot that `kustomize localize` can only process local references to files within
`scope`. Therefore, the command could not copy `example/base` to `example/localized-overlay`or localize the remote
references in `example/base`. The resulting file structure is as follows:
`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
```
└── example
├── overlay
│ └── kustomization.yaml
├── localized-overlay
│ ├── kustomization.yaml
│ └── localized-files
│ └── github.com
│ └── kubernetes-sigs
│ └── kustomize
│ └── v1.0.6
│ └── examples
│ └── helloWorld
│ └── deployment.yaml
└── base
└── kustomization.yaml
```

`kustomize build example/localized-overlay` will still run correctly outside the CI/CD pipeline because I chose to
place `example/localized-overlay` in the same directory as `example/overlay`. As a result, relative paths,
namely `../base`, in `example/localized-overlay` will point to the same files as their counterparts in `example/overlay`
. However, `kustomize build example/localized-overlay` will still require network access to run.

#### Story 2

I am back again from **Story 1**, but this time ready to localize `base` too, instead of just the `overlay` directory.
To localize both, I set my `scope` argument to `example` to make a copy of both directories. My setup still looks like
that at the end of **Story 1**:

```
└── example
├── overlay
│ └── kustomization.yaml
├── localized-overlay
```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
│ └── deployment.yaml
└── base
└── kustomization.yaml
```

After I run `kustomize localize example/overlay example new-space/localized-example`, I get the
following:

```
├── example # old kustomization directory
│ ├── overlay
│ │ └── kustomization.yaml
│ ├── localized-overlay
│ │ ├── kustomization.yaml
│ │ └── localized-files
│ │ └── github.com
│ │ └── kubernetes-sigs
│ │ └── kustomize
│ │ └── v1.0.6
│ │ └── examples
│ │ └── helloWorld
│ │ └── deployment.yaml
│ └── base
│ └── kustomization.yaml
└── new-space
└── localized-example # the new, localized kustomization directory
│ ├── helloWorld
│ │ └── configMap.yaml
│ └── multibases
│ ├── base
│ │ └── pod.yaml
│ ├── dev
│ ├── kustomization.yaml
│ ├── production
│ └── staging
└── overlay
├── 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
└── localized-files
└── github.com
└── kubernetes-sigs
└── kustomize
└── v1.0.6
└── examples
└── helloWorld
└── deployment.yaml
```
```
# new-space/localized-example/overlay/kustomization.yaml
```shell
# localized-overlay/overlay/kustomization.yaml
resources:
- ../base
- ./localized-files/github.com/kubernetes-sigs/kustomize/examples/helloWorld/deployment.yaml
```
```
# new-space/localized-example/base/kustomization.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 `new-space/localized-example` from my local setup to my company’s internal package management site. I
change the commands in my CI/CD pipeline to pull `localized-example` before running `kustomize build localized-example`,
and the command executes successfully!
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
N/A

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
annasong20 marked this conversation as resolved.
Show resolved Hide resolved
mitigate this risk as well.

### Dependencies

N/A

### Scalability

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.
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.
annasong20 marked this conversation as resolved.
Show resolved Hide resolved

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,
Expand Down Expand Up @@ -322,10 +314,14 @@ This command will have at least alpha and GA releases. Depending on user feedbac

### Alpha

This release will ignore
This release will not support

* local kustomization files that `target` references
* load restrictions check
annasong20 marked this conversation as resolved.
Show resolved Hide resolved
* KRM functions
* absolute paths
* `target` kustomization with reference cycles
annasong20 marked this conversation as resolved.
Show resolved Hide resolved
* 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
annasong20 marked this conversation as resolved.
Show resolved Hide resolved
available in `kubectl kustomize` either as kubectl only has `kustomize build` builtin.
Expand Down