Skip to content

Commit

Permalink
feat: sanitize templated helm CRDs
Browse files Browse the repository at this point in the history
  • Loading branch information
Skarlso committed Jun 11, 2024
1 parent 1c19934 commit 5606cf7
Show file tree
Hide file tree
Showing 12 changed files with 3,964 additions and 41 deletions.
16 changes: 4 additions & 12 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
run:
go: "1.21"
go: "1.22"
timeout: 10m
tests: false
allow-parallel-runners: true
issues-exit-code: 2
skip-dirs:
- "hack"

linters:
enable-all: true
Expand All @@ -16,7 +14,7 @@ linters:
# Logical next step
- forcetypeassert # Priority: that can lead to serious crashes.
- exportloopref
- goerr113 # Do not define dynamic errors with Errorf.
- goerr113
- varnamelen # m, d, p < These are not so meaningful variables.
- testpackage # Blackbox testing is preffered.
- funlen # Break long functions.
Expand Down Expand Up @@ -50,7 +48,7 @@ linters:
- dogsled
- exhaustruct # Doesn't really make sense.
- exhaustive # Doesn't really make sense.
- logrlint # Doesn't really make sense.
- loggercheck # Doesn't really make sense.
- goimports # acts weirdly, dci handles imports anyway

# Disabled because of generics in go 1.18
Expand All @@ -60,6 +58,7 @@ linters:
- wastedassign

# Deprecated
- execinquery
- deadcode
- exhaustivestruct
- golint
Expand All @@ -81,10 +80,6 @@ linters-settings:
- default
- prefix(github.com/open-component-model/ocm)
custom-order: true
staticcheck:
go: "1.21"
stylecheck:
go: "1.21"
funlen:
lines: 110
statements: 60
Expand All @@ -104,8 +99,6 @@ linters-settings:
- err
- wg
- id
govet:
check-shadowing: true
lll:
line-length: 120
gosec:
Expand All @@ -132,7 +125,6 @@ issues:
- govet
- path: _test\.go
linters:
- goerr113
- gocyclo
- errcheck
- gosec
Expand Down
32 changes: 30 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,36 @@ cty generate -c sample-crd/infrastructure.cluster.x-k8s.io_awsclusters.yaml --co

The frontend also has a checkbox to add comments to the generated yaml output.

TODO: add showcase for new frontend.
## Templated CRDs

It's possible to provide a templated CRD like this one for flux: [Helm Controller](https://raw.githubusercontent.com/fluxcd-community/helm-charts/main/charts/flux2/templates/helm-controller.crds.yaml).

It contains template definition like:

```yaml
{{- if and .Values.installCRDs .Values.helmController.create }}
```

These are trimmed so that the CRD parses correctly. Any values that might be in-lined are replaced with `replaced`.
This is done to avoid trying to parse a breaking yaml.

Things like this:
```yaml
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.15.0
labels:
app.kubernetes.io/component: helm-controller
app.kubernetes.io/instance: {{ .Release.Namespace }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/part-of: flux
app.kubernetes.io/version: {{ .Chart.AppVersion }}
helm.sh/chart: '{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}'
name: helmreleases.helm.toolkit.fluxcd.io
```

Where some templated value isn't escaped with `'` will create an invalid YAML that fails to parse.

## Showcase

Expand All @@ -149,4 +177,4 @@ Parsed Yaml output on the website:

![parsed1](./imgs/parsed1_website.png)
![parsed2](./imgs/parsed2_website.png)
![parsed3](./imgs/parsed1_sample_website.png)
![parsed3](./imgs/parsed1_sample_website.png)
7 changes: 7 additions & 0 deletions cmd/file_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (

"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/util/yaml"

"github.com/Skarlso/crd-to-sample-yaml/pkg/sanitize"
)

type FileHandler struct {
Expand All @@ -21,6 +23,11 @@ func (h *FileHandler) CRDs() ([]*v1beta1.CustomResourceDefinition, error) {
return nil, fmt.Errorf("failed to read file: %w", err)
}

content, err = sanitize.Sanitize(content)
if err != nil {
return nil, fmt.Errorf("failed to sanitize content: %w", err)
}

crd := &v1beta1.CustomResourceDefinition{}
if err := yaml.Unmarshal(content, crd); err != nil {
return nil, fmt.Errorf("failed to unmarshal into custom resource definition: %w", err)
Expand Down
7 changes: 7 additions & 0 deletions cmd/folder_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (

"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/util/yaml"

"github.com/Skarlso/crd-to-sample-yaml/pkg/sanitize"
)

type FolderHandler struct {
Expand Down Expand Up @@ -41,6 +43,11 @@ func (h *FolderHandler) CRDs() ([]*v1beta1.CustomResourceDefinition, error) {
return fmt.Errorf("failed to read file: %w", err)
}

content, err = sanitize.Sanitize(content)
if err != nil {
return fmt.Errorf("failed to sanitize content: %w", err)
}

crd := &v1beta1.CustomResourceDefinition{}
if err := yaml.Unmarshal(content, crd); err != nil {
fmt.Fprintln(os.Stderr, "skipping none CRD file: "+path)
Expand Down
13 changes: 11 additions & 2 deletions cmd/url_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,34 @@ import (
"net/http"
"time"

"github.com/Skarlso/crd-to-sample-yaml/pkg/fetcher"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/util/yaml"

"github.com/Skarlso/crd-to-sample-yaml/pkg/fetcher"
"github.com/Skarlso/crd-to-sample-yaml/pkg/sanitize"
)

const timeout = 10

type URLHandler struct {
url string
}

func (h *URLHandler) CRDs() ([]*v1beta1.CustomResourceDefinition, error) {
client := http.DefaultClient
client.Timeout = 10 * time.Second
client.Timeout = timeout * time.Second

f := fetcher.NewFetcher(client)
content, err := f.Fetch(h.url)
if err != nil {
return nil, fmt.Errorf("failed to fetch content: %w", err)
}

content, err = sanitize.Sanitize(content)
if err != nil {
return nil, fmt.Errorf("failed to sanitize content: %w", err)
}

crd := &v1beta1.CustomResourceDefinition{}
if err := yaml.Unmarshal(content, crd); err != nil {
return nil, fmt.Errorf("failed to unmarshal into custom resource definition: %w", err)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/Skarlso/crd-to-sample-yaml

go 1.22.0
go 1.22.4

require (
github.com/maxence-charriere/go-app/v10 v10.0.4
Expand Down
39 changes: 39 additions & 0 deletions pkg/sanitize/sanitizer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package sanitize

import (
"bytes"
)

func Sanitize(content []byte) ([]byte, error) {
// bail early if there are no template characters in the CRD
if !bytes.Contains(content, []byte("{{")) {
return content, nil
}

var result [][]byte //nolint:prealloc // no idea what the size will be

for _, line := range bytes.Split(content, []byte("\n")) {
if bytes.HasPrefix(bytes.TrimLeft(line, " "), []byte("{{")) {
// skip lines that begin with {{
continue
}

// replace {{ }} mid-lines with dummy value
if begin := bytes.Index(line, []byte("{{")); begin != -1 {
end := bytes.Index(line, []byte("}}"))
if end == -1 {
continue
}

var newLine []byte
newLine = append(newLine, line[:begin]...)
newLine = append(newLine, []byte("replaced")...)
newLine = append(newLine, line[end+2:]...)
line = newLine
}

result = append(result, line)
}

return bytes.Join(result, []byte("\n")), nil
}
83 changes: 83 additions & 0 deletions pkg/sanitize/sanitizer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package sanitize

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestSanitize(t *testing.T) {
type args struct {
content []byte
}
tests := []struct {
name string
args args
want []byte
wantErr bool
}{
{
name: "sanitize",
args: args{
content: []byte(`test: {{ Value.something }}`),
},
want: []byte(`test: replaced`),
},
{
name: "complex sanitize",
args: args{
content: []byte(`{{ if .something }}
test: {{ Value.something }}
normal: value
this:
should:
not:
mess: {{ Value.should.work }}
with: indentation
{{- end }}
`),
},
want: []byte(`test: replaced
normal: value
this:
should:
not:
mess: replaced
with: indentation
`),
},
{
name: "begins with space",
args: args{
content: []byte(`{{ if .something }}
test: {{ Value.something }}
normal: value
this:
should:
not:
mess: {{ Value.should.work }}
with: indentation
{{- end }}
`),
},
want: []byte(`test: replaced
normal: value
this:
should:
not:
mess: replaced
with: indentation
`),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Sanitize(tt.args.content)
if (err != nil) != tt.wantErr {
t.Errorf("Sanitize() error = %v, wantErr %v", err, tt.wantErr)
return
}
assert.Equal(t, tt.want, got)
})
}
}
Loading

0 comments on commit 5606cf7

Please sign in to comment.