Skip to content

Commit

Permalink
[DENA-826] do not render Component kind of kustomization files (#42)
Browse files Browse the repository at this point in the history
* do not render Component kind of kustomization files

* add test for multiple kustomization fileS

* run go.mod, add bunch of go cover ignores

* a bit more linting, replace logs with returning error

* add another go-cov ignore
  • Loading branch information
MarcinGinszt authored Jul 23, 2024
1 parent cf90645 commit 0b3b6e9
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 15 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ kustomize-build-dirs takes a list of filenames, and for each one walks up the
directory tree until it finds a directory containing `kustomization.yaml` then
runs `kustomize build` on that directory, saving the output in the directory
given by `--out-dir`.
It also truncates secrets, so that we don't need to decrypt them in order to check
if manifests are correct.

This program should only be run from the root of a Git repository.

Expand Down
59 changes: 57 additions & 2 deletions cmd/kustomize-build-dirs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
Expand All @@ -11,10 +12,17 @@ import (

"github.com/urfave/cli/v2"
"golang.org/x/sync/errgroup"
"gopkg.in/yaml.v2"
)

const manifestFileName = "manifests.yaml"

// Kustomization represents the structure of a Kustomization file
type Kustomization struct {
APIVersion string `yaml:"apiVersion"`
Kind string `yaml:"kind"`
}

// variable used for testing
var getwdFunc = os.Getwd

Expand Down Expand Up @@ -66,6 +74,11 @@ func kustomizeBuildDirs(outDir string, doTruncateSecrets bool, filepaths []strin
return err
}

kustomizationRoots, err = removeComponentKustomizations(rootDir, kustomizationRoots)
if err != nil { //go-cov:skip
return err
}

// truncate secrets so we can run `kustomize build` without having to decrypt them
if doTruncateSecrets {
if err := truncateSecrets(rootDir, kustomizationRoots); err != nil {
Expand Down Expand Up @@ -111,6 +124,7 @@ func findKustomizationRoots(root string, paths []string) ([]string, error) {
if kustomizationRoot == "" {
continue
}

if _, exists := rootsMap[kustomizationRoot]; !exists {
fmt.Printf("Found kustomization build dir: %s\n", kustomizationRoot)
rootsMap[kustomizationRoot] = struct{}{}
Expand All @@ -131,8 +145,8 @@ func findKustomizationRoot(repoRoot string, relativePath string) (string, error)
case err == nil:
// found 'kustomization.yaml'
return dir, nil
case err != nil && !os.IsNotExist(err):
return "", fmt.Errorf("Error checking for file in %s: %v", dir, err)
case !os.IsNotExist(err):
return "", fmt.Errorf("error checking for file in %s: %v", dir, err)
default:
// file not found, continue up the directory tree
continue
Expand All @@ -141,6 +155,47 @@ func findKustomizationRoot(repoRoot string, relativePath string) (string, error)
return "", nil
}

// removeComponentKustomizations checks the list of the kustomization files, and removes those with
// kind: Component.
// We can't expect standalone Component kustomization files to correctly render.
func removeComponentKustomizations(kustomizationRoot string, paths []string) ([]string, error) {
pathsNoComponent := []string{}
for _, path := range paths {
isComponent, err := checkIfIsComponent(
filepath.Join(kustomizationRoot, path, "kustomization.yaml"),
)
if err != nil { //go-cov:skip
return nil, err
}
if !isComponent {
pathsNoComponent = append(pathsNoComponent, path)
}
}
return pathsNoComponent, nil
}

func checkIfIsComponent(filepath string) (bool, error) {
file, err := os.Open(filepath)
if err != nil { //go-cov:skip
return false, fmt.Errorf("failed opening kustomization file: %s: %v", filepath, err)
}
defer file.Close()

// Read the file's content
data, err := io.ReadAll(file)
if err != nil { //go-cov:skip
return false, fmt.Errorf("error reading file: %v", err)
}

// Unmarshal the YAML into the struct
var kustomization Kustomization
err = yaml.Unmarshal(data, &kustomization)
if err != nil { //go-cov:skip
return false, fmt.Errorf("error unmarshaling YAML: %v", err)
}
return kustomization.Kind == "Component", nil
}

func truncateSecrets(rootDir string, dirs []string) error {
secrets, err := findSecrets(rootDir, dirs)
if err != nil {
Expand Down
57 changes: 54 additions & 3 deletions cmd/kustomize-build-dirs/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ kind: Kustomization
resources:
- deployment.yaml
`

componentKustomization = `apiVersion: kustomize.config.k8s.io/v1beta1
kind: Component
patches:
- path: deployment.yaml
`
simpleDeploymentTemplate = `apiVersion: apps/v1
kind: Deployment
metadata:
Expand All @@ -44,7 +51,12 @@ func requireErorrPrefix(t *testing.T, err error, prefix string) {
t.Helper()

require.Error(t, err)
require.LessOrEqual(t, len(prefix), len(err.Error()), "error cannot be shorter than prefix")
require.LessOrEqual(
t,
len(prefix),
len(err.Error()),
fmt.Sprintf("error cannot be shorter than prefix, err: %s, prefix: %s", err, prefix),
)
require.Equalf(t, prefix, err.Error()[:len(prefix)], "full error: %v", err)
}

Expand Down Expand Up @@ -111,7 +123,7 @@ func TestFailsWhenUnableToListSecrets(t *testing.T) {
workDir,
)

// run command outside of any Git directory
// run command outside any Git directory
err = kustomizeBuildDirs(mockoutDir, true, []string{"kustomization.yaml"})
requireErorrPrefix(t, err, expectedErrPrefix)
}
Expand Down Expand Up @@ -148,7 +160,7 @@ func TestFailsWhenUnableToFindKustomizations(t *testing.T) {
require.NoError(t, os.Chmod(unredableDirPath, 0o600))
// restore permissions so we can cleanup
defer os.Chmod(unredableDirPath, 0o700) //nolint:errcheck
expectedErrPrefix := "Error checking for file in manifests:"
expectedErrPrefix := "error checking for file in manifests:"

err := kustomizeBuildDirs(mockoutDir, false, []string{"manifests/kustomization.yaml"})
requireErorrPrefix(t, err, expectedErrPrefix)
Expand Down Expand Up @@ -305,6 +317,20 @@ func compareResults(
}
}

func TestDontRenderComponent(t *testing.T) {
gitDir, outDir := setupTest(t)

manifestPath := filepath.Join("manifests", "deployment.yaml")
repoFiles := map[string]string{
filepath.Join("manifests", "kustomization.yaml"): componentKustomization,
manifestPath: simpleDeployment,
}
buildGitRepo(t, gitDir, repoFiles)

require.NoError(t, kustomizeBuildDirs(outDir, false, []string{manifestPath}))
require.NoFileExists(t, outDir)
}

func TestWriteSingleManifest(t *testing.T) {
gitDir, outDir := setupTest(t)

Expand Down Expand Up @@ -367,6 +393,31 @@ func TestWriteMultipleManifests(t *testing.T) {
compareResults(t, outDir, expectedContents, readOutDir(t, outDir))
}

func TestWriteMultipleManifestsOneIscomponent(t *testing.T) {
gitDir, outDir := setupTest(t)

firstDeploymentPath := filepath.Join("first-project", "deployment.yaml")
firstDeploymentcontent := fmt.Sprintf(simpleDeploymentTemplate, "first-app")
secondDeploymentPath := filepath.Join("second-project", "deployment.yaml")
secondDeploymentcontent := fmt.Sprintf(simpleDeploymentTemplate, "second-app")
repoFiles := map[string]string{
firstDeploymentPath: firstDeploymentcontent,
filepath.Join("first-project", "kustomization.yaml"): simpleKustomization,
secondDeploymentPath: secondDeploymentcontent,
filepath.Join("second-project", "kustomization.yaml"): componentKustomization,
}
buildGitRepo(t, gitDir, repoFiles)
expectedContents := map[string]string{
"first-project": firstDeploymentcontent,
}

require.NoError(
t,
kustomizeBuildDirs(outDir, false, []string{firstDeploymentPath, secondDeploymentPath}),
)
compareResults(t, outDir, expectedContents, readOutDir(t, outDir))
}

func TestSecretsStubbed(t *testing.T) {
gitDir, outDir := setupTest(t)
manifestsDir := filepath.Join("src", "manifests")
Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ require (
github.com/urfave/cli/v2 v2.27.2
gitlab.com/matthewhughes/go-cov v0.4.0
golang.org/x/sync v0.7.0
k8s.io/apimachinery v0.30.3
k8s.io/client-go v0.30.3
gopkg.in/yaml.v2 v2.4.0
k8s.io/apimachinery v0.30.2
k8s.io/client-go v0.30.2
sigs.k8s.io/controller-runtime v0.18.4
)

Expand Down Expand Up @@ -213,10 +214,9 @@ require (
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
honnef.co/go/tools v0.4.7 // indirect
k8s.io/api v0.30.3 // indirect
k8s.io/api v0.30.2 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -618,14 +618,14 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.4.7 h1:9MDAWxMoSnB6QoSqiVr7P5mtkT9pOc1kSxchzPCnqJs=
honnef.co/go/tools v0.4.7/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0=
k8s.io/api v0.30.3 h1:ImHwK9DCsPA9uoU3rVh4QHAHHK5dTSv1nxJUapx8hoQ=
k8s.io/api v0.30.3/go.mod h1:GPc8jlzoe5JG3pb0KJCSLX5oAFIW3/qNJITlDj8BH04=
k8s.io/api v0.30.2 h1:+ZhRj+28QT4UOH+BKznu4CBgPWgkXO7XAvMcMl0qKvI=
k8s.io/api v0.30.2/go.mod h1:ULg5g9JvOev2dG0u2hig4Z7tQ2hHIuS+m8MNZ+X6EmI=
k8s.io/apiextensions-apiserver v0.30.1 h1:4fAJZ9985BmpJG6PkoxVRpXv9vmPUOVzl614xarePws=
k8s.io/apiextensions-apiserver v0.30.1/go.mod h1:R4GuSrlhgq43oRY9sF2IToFh7PVlF1JjfWdoG3pixk4=
k8s.io/apimachinery v0.30.3 h1:q1laaWCmrszyQuSQCfNB8cFgCuDAoPszKY4ucAjDwHc=
k8s.io/apimachinery v0.30.3/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/client-go v0.30.3 h1:bHrJu3xQZNXIi8/MoxYtZBBWQQXwy16zqJwloXXfD3k=
k8s.io/client-go v0.30.3/go.mod h1:8d4pf8vYu665/kUbsxWAQ/JDBNWqfFeZnvFiVdmx89U=
k8s.io/apimachinery v0.30.2 h1:fEMcnBj6qkzzPGSVsAZtQThU62SmQ4ZymlXRC5yFSCg=
k8s.io/apimachinery v0.30.2/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/client-go v0.30.2 h1:sBIVJdojUNPDU/jObC+18tXWcTJVcwyqS9diGdWHk50=
k8s.io/client-go v0.30.2/go.mod h1:JglKSWULm9xlJLx4KCkfLLQ7XwtlbflV6uFFSHTMgVs=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
Expand Down

0 comments on commit 0b3b6e9

Please sign in to comment.