Skip to content

Commit

Permalink
Reorganize readme; add index function (#12)
Browse files Browse the repository at this point in the history
* Update docs for plugin.

* Explain that "kubectl" is not needed.

* Add function for index. Document it as well.

* Fixes broken link.

* Fixes broken link.
  • Loading branch information
patrickdappollonio authored Oct 31, 2021
1 parent 05801ae commit 06fca1d
Show file tree
Hide file tree
Showing 7 changed files with 537 additions and 448 deletions.
422 changes: 29 additions & 393 deletions README.md

Large diffs are not rendered by default.

124 changes: 124 additions & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Examples

- [Examples](#examples)
- [Slicing the Tekton manifest](#slicing-the-tekton-manifest)

The following examples demonstrate the capabilities of `kubectl-slice`.

## Slicing the Tekton manifest

[Tekton Pipelines](https://tekton.dev/) is a powerful tool that's available through a Helm Chart from the [cd.foundation](https://cd.foundation). We can grab it from their Helm repository and render it locally, then use `kubectl-slice` to split it into multiple files.

We'll use the following filename template so there's one folder for each Kubernetes resource `kind`, so all `Secrets` for example are in the same folder, then we will use the resource name as defined in `metadata.name`. We'll also modify the name, since some of the Tekton resources have an FQDN for a name, like `tekton.pipelines.dev`, with the `dottodash` template function:

```handlebars
{{.kind|lower}}/{{.metadata.name|dottodash}}.yaml
```

We will render the Helm Chart locally to `stdout` with:

```bash
helm template tekton cdf/tekton-pipeline
```

Then we can pipe that output directly to `kubectl-slice`:

```bash
helm template tekton cdf/tekton-pipeline | kubectl-slice --template '{{.kind|lower}}/{{.metadata.name|dottodash}}.yaml'
```

Which will render the following output:

```text
Wrote rolebinding/tekton-pipelines-info.yaml -- 590 bytes.
Wrote service/tekton-pipelines-controller.yaml -- 1007 bytes.
Wrote podsecuritypolicy/tekton-pipelines.yaml -- 1262 bytes.
Wrote configmap/config-registry-cert.yaml -- 906 bytes.
Wrote configmap/feature-flags.yaml -- 646 bytes.
Wrote clusterrole/tekton-pipelines-controller-tenant-access.yaml -- 1035 bytes.
Wrote clusterrolebinding/tekton-pipelines-webhook-cluster-access.yaml -- 565 bytes.
Wrote role/tekton-pipelines-info.yaml -- 592 bytes.
Wrote service/tekton-pipelines-webhook.yaml -- 1182 bytes.
Wrote deployment/tekton-pipelines-webhook.yaml -- 3645 bytes.
Wrote serviceaccount/tekton-bot.yaml -- 883 bytes.
Wrote configmap/config-defaults.yaml -- 2424 bytes.
Wrote configmap/config-logging.yaml -- 1596 bytes.
Wrote customresourcedefinition/runs-tekton-dev.yaml -- 2308 bytes.
Wrote role/tekton-pipelines-leader-election.yaml -- 495 bytes.
Wrote rolebinding/tekton-pipelines-webhook.yaml -- 535 bytes.
Wrote customresourcedefinition/clustertasks-tekton-dev.yaml -- 2849 bytes.
Wrote customresourcedefinition/pipelineresources-tekton-dev.yaml -- 1874 bytes.
Wrote clusterrole/tekton-aggregate-view.yaml -- 1133 bytes.
Wrote role/tekton-pipelines-webhook.yaml -- 1152 bytes.
Wrote rolebinding/tekton-pipelines-webhook-leaderelection.yaml -- 573 bytes.
Wrote validatingwebhookconfiguration/validation-webhook-pipeline-tekton-dev.yaml -- 663 bytes.
Wrote serviceaccount/tekton-pipelines-webhook.yaml -- 317 bytes.
Wrote configmap/config-leader-election.yaml -- 985 bytes.
Wrote configmap/pipelines-info.yaml -- 1137 bytes.
Wrote clusterrolebinding/tekton-pipelines-controller-cluster-access.yaml -- 1163 bytes.
Wrote role/tekton-pipelines-controller.yaml -- 1488 bytes.
Wrote deployment/tekton-pipelines-controller.yaml -- 5203 bytes.
Wrote configmap/config-observability.yaml -- 2429 bytes.
Wrote customresourcedefinition/tasks-tekton-dev.yaml -- 2824 bytes.
Wrote mutatingwebhookconfiguration/webhook-pipeline-tekton-dev.yaml -- 628 bytes.
Wrote validatingwebhookconfiguration/config-webhook-pipeline-tekton-dev.yaml -- 742 bytes.
Wrote namespace/tekton-pipelines.yaml -- 808 bytes.
Wrote secret/webhook-certs.yaml -- 959 bytes.
Wrote customresourcedefinition/pipelineruns-tekton-dev.yaml -- 3801 bytes.
Wrote serviceaccount/tekton-pipelines-controller.yaml -- 908 bytes.
Wrote configmap/config-artifact-pvc.yaml -- 977 bytes.
Wrote customresourcedefinition/conditions-tekton-dev.yaml -- 1846 bytes.
Wrote clusterrolebinding/tekton-pipelines-controller-tenant-access.yaml -- 816 bytes.
Wrote rolebinding/tekton-pipelines-controller.yaml -- 1133 bytes.
Wrote rolebinding/tekton-pipelines-controller-leaderelection.yaml -- 585 bytes.
Wrote horizontalpodautoscaler/tekton-pipelines-webhook.yaml -- 1518 bytes.
Wrote configmap/config-artifact-bucket.yaml -- 1408 bytes.
Wrote customresourcedefinition/pipelines-tekton-dev.yaml -- 2840 bytes.
Wrote customresourcedefinition/taskruns-tekton-dev.yaml -- 3785 bytes.
Wrote clusterrole/tekton-aggregate-edit.yaml -- 1274 bytes.
Wrote clusterrole/tekton-pipelines-controller-cluster-access.yaml -- 1886 bytes.
Wrote clusterrole/tekton-pipelines-webhook-cluster-access.yaml -- 2480 bytes.
48 files generated.
```

We can navigate the folders:

```bash
$ tree -d
.
├── clusterrole
├── clusterrolebinding
├── configmap
├── customresourcedefinition
├── deployment
├── horizontalpodautoscaler
├── mutatingwebhookconfiguration
├── namespace
├── podsecuritypolicy
├── role
├── rolebinding
├── secret
├── service
├── serviceaccount
└── validatingwebhookconfiguration

15 directories
```

And poking into a single directory, for example, `configmap`:

```bash
$ tree configmap
configmap
├── config-artifact-bucket.yaml
├── config-artifact-pvc.yaml
├── config-defaults.yaml
├── config-leader-election.yaml
├── config-logging.yaml
├── config-observability.yaml
├── config-registry-cert.yaml
├── feature-flags.yaml
└── pipelines-info.yaml

0 directories, 9 files
```
125 changes: 125 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Frequently Asked Questions

- [Frequently Asked Questions](#frequently-asked-questions)
- [I want to exclude or include certain Kubernetes resource types, how do I do it?](#i-want-to-exclude-or-include-certain-kubernetes-resource-types-how-do-i-do-it)
- [Some of the code in my YAML is an entire YAML file commented out, how do I skip it?](#some-of-the-code-in-my-yaml-is-an-entire-yaml-file-commented-out-how-do-i-skip-it)
- [How to add namespaces to YAML resources with no namespace?](#how-to-add-namespaces-to-yaml-resources-with-no-namespace)
- [How do I access YAML fields by name?](#how-do-i-access-yaml-fields-by-name)
- [Two files will generate the same file name, what do I do?](#two-files-will-generate-the-same-file-name-what-do-i-do)
- [This app doesn't seem to work with Windows `CRLF`](#this-app-doesnt-seem-to-work-with-windows-crlf)
- [How are string conversions handled?](#how-are-string-conversions-handled)
- [I keep getting `file name template parse failed: bad character`, how do I fix it?](#i-keep-getting-file-name-template-parse-failed-bad-character-how-do-i-fix-it)

## I want to exclude or include certain Kubernetes resource types, how do I do it?

`kubectl-slice` has two available flags: `--exclude-kind` and `--include-kind`. They can be used to exclude or include specific resources. For example, to exclude all `Deployment` resources, you can use `--exclude-kind=Deployment`:

```bash
kubectl-slice -f manifest.yaml --exclude-kind=Deployment
```

Both arguments can be used comma-separated or by calling them multiple times. As such, these two invocations are the same:

```bash
kubectl-slice --exclude-kind=Deployment,ReplicaSet,DaemonSet
kubectl-slice --exclude-kind=Deployment --exclude-kind=ReplicaSet --exclude-kind=DaemonSet
```

## Some of the code in my YAML is an entire YAML file commented out, how do I skip it?

By default, `kubectl-slice` will also slice out commented YAML file sections. If you would rather want to ensure only Kubernetes resources are sliced from the original YAML file, then there's two options:

* Use `--include-kind` to only include Kubernetes resources by kind; or
* Use `--skip-non-k8s` to skip any non-Kubernetes resources

`--include-kind` can be used so you control your entire output by specifying only the resources you want. For example, if you want to only slice out `Deployment` resources, you can use `--include-kind=Deployment`.

`--skip-non-k8s`, on the other hand, works by ensuring that your YAML contains the following fields: `apiVersion`, `kind`, and `metadata.name`. Keep in mind that it won't check if these fields are empty, it will just ensure that those fields exist within each one of the YAML processed. If one of them does not contain these fields, it will be skipped.

## How to add namespaces to YAML resources with no namespace?

It's very common that Helm charts or even plain YAMLs found online might not contain the namespace, and because of that, the field isn't available in the YAML. Since this tool was created to fit a specific criteria [as seen above](../README.md#why-kubectl-slice), there's no need to implement this here. However, you can use `kustomize` to quickly add the namespace to your manifest, then run it through `kubectl-slice`.

First, create a `kustomization.yaml` file:

```yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: my-namespace
resources:
- my-file.yaml
```
Replace `my-namespace` for the namespace you want to set, and `my-file.yaml` for the name of the actual file that has no namespaces declared. Then run:

```
kustomize build
```
This will render your new file, namespaces included, to `stdout`. You can pipe this as-is to `kubectl-slice`:
```
kustomize build | kubectl-slice
```
Keep in mind that is recommended to **not add namespaces** to your YAML resources, to allow users to install in any destination they choose. For example, a namespaceless file called `foo.yaml` can be installed to the namespace `bar` by using:
```
kubectl apply -n bar -f foo.yaml
```
## How do I access YAML fields by name?
Any field from the YAML file can be used, however, non-existent fields will render an empty string. This is very common for situations such as rendering a Helm template [where the namespace shouldn't be defined](#how-to-add-namespaces-to-yaml-resources-with-no-namespace).
If you would rather fail executing `kubectl-slice` if a field was not found, consider using the `required` Go Template function. The following template will make `kubectl-slice` fail with a non-zero exit code if the namespace of any of the resources is not defined.
```handlebars
{{.metadata.namespace | required}}.yaml
```

If you would rather provide a default for those resources without, say, a namespace, you can use the `default` function:

```handlebars
{{.metadata.namespace | default "global"}}.yaml
```

This will render any resource without a namespace with the name `global.yaml`.

## Two files will generate the same file name, what do I do?

Since it's possible to provide a Go Template for a file name that might be the same for multiple resources, `kubectl-slice` will append any YAML that matches by file name to the given file using the `---` separator.

For example, considering the following file name:

```handlebars
{{.metadata.namespace | default "global"}}.yaml
```

Any cluster-scoped resource will be appended into `global.yaml`, while any resource in the namespace `production` will be appended to `production.yaml`.

## This app doesn't seem to work with Windows `CRLF`

`kubectl-slice` does not support Windows line breaks with `CRLF` -- also known as `\r\n`. Consider using Unix line breaks `\n`.

## How are string conversions handled?

Since `kubectl-slice` is built in Go, there's only a handful of primitives that can be read from the YAML manifest. All of these have been hand-picked to be stringified automatically -- in fact, multiple template functions will accept them, and yes, that means you can `lowercase` a number 😅

The decision is intentional and it's due to the fact that's impossible to map any potential Kubernetes resource, given the fact that you can teach Kubernetes new objects using Custom Resource Definitions. Because of that, resources are read as untyped and converted to strings when possible.

The following YAML untyped values are handled, [in accordance with the `json.Unmarshal` documentation](https://pkg.go.dev/encoding/json#Unmarshal):

* `bool`, for JSON booleans
* `float64`, for JSON numbers
* `string`, for JSON strings

## I keep getting `file name template parse failed: bad character`, how do I fix it?

If you're receiving this error, chances are you're attempting to access a field from the YAML whose name is not limited to alphanumeric characters, such as annotations or labels, like `app.kubernetes.io/name`.

To fix it, use the [`index` function](functions.md#index) to access the field by index. For example, if you want to access the `app.kubernetes.io/name` field, you can use the following template:

```handlebars
{{ index "app.kubernetes.io/name" .metadata.labels }}
```
Loading

0 comments on commit 06fca1d

Please sign in to comment.