Skip to content

Commit

Permalink
Change kustomize edit fix to split patch files that contain multiple …
Browse files Browse the repository at this point in the history
…patches into multiple patch files, each containing a single patch.

Fixes line breaks in output.

Suppress warning message for different output when old build fails and new build succeeds.
  • Loading branch information
brianpursley committed Feb 22, 2023
1 parent 22dbd3e commit 8013049
Show file tree
Hide file tree
Showing 3 changed files with 548 additions and 7 deletions.
19 changes: 12 additions & 7 deletions kustomize/commands/edit/fix/fix.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ var flags struct {
func NewCmdFix(fSys filesys.FileSystem, w io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "fix",
Short: "Fix the missing fields in kustomization file",
Short: "Fix kustomization file by adding missing fields, updating deprecated fields, and splitting combined patch files.",
Long: "",
Example: `
# Fix the missing and deprecated fields in kustomization file
# Fix kustomization file
kustomize edit fix
`,
# Fix kustomization file (including converting vars to replacements)
kustomize edit fix --vars`,
RunE: func(cmd *cobra.Command, args []string) error {
return RunFix(fSys, w)
},
Expand All @@ -43,7 +44,7 @@ func NewCmdFix(fSys filesys.FileSystem, w io.Writer) *cobra.Command {
func RunFix(fSys filesys.FileSystem, w io.Writer) error {
var oldOutput bytes.Buffer
oldBuildCmd := build.NewCmdBuild(fSys, build.MakeHelp(konfig.ProgramName, "build"), &oldOutput)
oldBuildCmd.RunE(oldBuildCmd, nil)
oldErr := oldBuildCmd.RunE(oldBuildCmd, nil)

mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
Expand Down Expand Up @@ -84,15 +85,19 @@ and the resulting files may not produce the same output when `+"`kustomize build
We recommend doing this in a clean git repository where the change is easy to undo.`)
}

if err = splitPatches(fSys, m, w); err != nil {
return err
}

writeErr := mf.Write(m)

var fixedOutput bytes.Buffer
fixedBuildCmd := build.NewCmdBuild(fSys, build.MakeHelp(konfig.ProgramName, "build"), &fixedOutput)
err = fixedBuildCmd.RunE(fixedBuildCmd, nil)
if err != nil {
fmt.Fprintf(w, "Warning: 'Fixed' kustomization now produces the error when running `kustomize build`: %s\n", err.Error())
} else if fixedOutput.String() != oldOutput.String() {
fmt.Fprintf(w, "Warning: 'Fixed' kustomization now produces different output when running `kustomize build`:\n...%s...\n", fixedOutput.String())
fmt.Fprintf(w, "\nWarning: 'Fixed' kustomization now produces the error when running `kustomize build`: %s\n", err.Error())
} else if oldErr == nil && fixedOutput.String() != oldOutput.String() {
fmt.Fprintf(w, "\nWarning: 'Fixed' kustomization now produces different output when running `kustomize build`:\n...\n%s...\n", fixedOutput.String())
}

return writeErr
Expand Down
109 changes: 109 additions & 0 deletions kustomize/commands/edit/fix/splitpatches.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright 2023 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package fix

import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strings"

"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filesys"
)

// splitPatches splits patches that contain multiple documents into multiple files and updates the kustomization file's
// Patch field to point to the new files.
func splitPatches(fSys filesys.FileSystem, k *types.Kustomization, w io.Writer) error {
yamlSeparatorRegexp := regexp.MustCompile(`(?:^|\n)---.*`) // Matches any line that starts with ---

var messages []string

var splitPatches []types.Patch
for _, patch := range k.Patches {
patchContentBytes, err := fSys.ReadFile(patch.Path)
if errors.Is(err, os.ErrNotExist) {
// If the patch file does not exist, there is nothing to do. It is not this function's responsibility
// to validate the existence of patch files.
splitPatches = append(splitPatches, patch)
continue
}
if err != nil {
return err
}

splitPatchContent := yamlSeparatorRegexp.Split(string(patchContentBytes), -1)

// If there are no separators, there is nothing to do, so keep the original patch file and continue.
if len(splitPatchContent) == 1 {
splitPatches = append(splitPatches, patch)
continue
}

// Find the new patches, removing any empty ones.
var newPatches []string
for _, pc := range splitPatchContent {
trimmedPatchContent := strings.TrimSpace(pc)
if len(trimmedPatchContent) > 0 {
newPatches = append(newPatches, trimmedPatchContent+"\n")
}
}

// If there is only one new patch, Overwrite the original patch file and continue.
if len(newPatches) == 1 {
err := fSys.WriteFile(patch.Path, []byte(newPatches[0]))
if err != nil {
return err
}
splitPatches = append(splitPatches, patch)
messages = append(messages, fmt.Sprintf("%s: removed unnecessary document separators", patch.Path))
continue
}

// If there are multiple new patches, create new patch files for each one and remove the original patch file.
var newPatchPaths []string
for i, newPatchContent := range newPatches {
newPatchPath, err := availableFilename(fSys, patch.Path, i+1)
if err != nil {
return err
}
err = fSys.WriteFile(newPatchPath, []byte(newPatchContent))
if err != nil {
return err
}
splitPatches = append(splitPatches, types.Patch{Path: newPatchPath})
newPatchPaths = append(newPatchPaths, newPatchPath)
}
messages = append(messages, fmt.Sprintf("%s -> %s", patch.Path, strings.Join(newPatchPaths, ", ")))

err = fSys.RemoveAll(patch.Path)
if err != nil {
return err
}
}
k.Patches = splitPatches

if len(messages) > 0 {
fmt.Fprintf(w, "\nSplit patches:\n %s\n", strings.Join(messages, "\n "))
}

return nil
}

// availableFilename returns a filename that does not already exist in the filesystem, by repeatedly appending a suffix
// to the filename until a non-existing filename is found.
func availableFilename(fSys filesys.FileSystem, originalFilename string, suffix int) (string, error) {
ext := filepath.Ext(originalFilename)
base := strings.TrimSuffix(originalFilename, ext)
for i := 0; i < 100; i++ {
base += fmt.Sprintf("-%d", suffix)
if !fSys.Exists(base + ext) {
return base + ext, nil
}
}
return "", fmt.Errorf("unable to find available filename for %s and suffix %d", originalFilename, suffix)
}
Loading

0 comments on commit 8013049

Please sign in to comment.