Replace the regex with a simple algorithm to split the yaml documents.

Signed-off-by: boris smidt <smidtboris1@gmail.com>
pull/13441/head
boris smidt 11 months ago
parent 7853d49071
commit 3a3627e512

@ -17,8 +17,6 @@ limitations under the License.
package releaseutil package releaseutil
import ( import (
"fmt"
"regexp"
"strconv" "strconv"
"strings" "strings"
) )
@ -33,30 +31,60 @@ type SimpleHead struct {
} `json:"metadata,omitempty"` } `json:"metadata,omitempty"`
} }
var sep = regexp.MustCompile("(?:^|\\s*\n)---\\s*")
// SplitManifests takes a string of manifest and returns a map contains individual manifests // SplitManifests takes a string of manifest and returns a map contains individual manifests
func SplitManifests(bigFile string) map[string]string { func SplitManifests(bigFile string) map[string]string {
// Basically, we're quickly splitting a stream of YAML documents into an // Basically, we're quickly splitting a stream of YAML documents into an
// array of YAML docs. The file name is just a place holder, but should be // array of YAML docs. The file name is just a place holder, but should be
// integer-sortable so that manifests get output in the same order as the // integer-sortable so that manifests get output in the same order as the
// input (see `BySplitManifestsOrder`). // input (see `BySplitManifestsOrder`).
tpl := "manifest-%d"
res := map[string]string{}
// Making sure that any extra whitespace in YAML stream doesn't interfere in splitting documents correctly. // Making sure that any extra whitespace in YAML stream doesn't interfere in splitting documents correctly.
bigFileTmp := strings.TrimSpace(bigFile) bigFileTmp := strings.TrimSpace(bigFile)
docs := sep.Split(bigFileTmp, -1) docs := splitDocs(bigFileTmp)
var count int res := make(map[string]string, len(docs))
for _, d := range docs { for count, _ := range docs {
if d == "" { res["manifest-"+strconv.Itoa(count)] = docs[count]
}
return res
}
const yamlDocumentTermination = "\n---"
func splitDocs(bigFile string) []string {
docs := make([]string, 0)
docStartIdx := 0
// strip off a leading --- avoiding a special start case
bigFile = strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(bigFile), "---"))
// using our own index so we can manually skip forward
i := 0
for i < len(bigFile) {
// see if we find a document termination sequence, i.e. "\n---"
if !strings.HasPrefix(bigFile[i:], yamlDocumentTermination) {
i++
continue continue
} }
d = strings.TrimSpace(d) // this is the end of the document, slicing the bytes array
res[fmt.Sprintf(tpl, count)] = d doc := strings.TrimSpace(bigFile[docStartIdx:i])
count = count + 1
// ignore empty docs
if doc != "" {
docs = append(docs, doc)
} }
return res
// skip the document termination characters
docStartIdx = i + len(yamlDocumentTermination)
i = docStartIdx
}
// append the 'rest' of the document as the last document
doc := strings.TrimSpace(bigFile[docStartIdx:])
if doc != "" {
docs = append(docs, doc)
}
return docs
} }
// BySplitManifestsOrder sorts by in-file manifest order, as provided in function `SplitManifests` // BySplitManifestsOrder sorts by in-file manifest order, as provided in function `SplitManifests`

@ -18,6 +18,7 @@ package releaseutil // import "helm.sh/helm/v3/pkg/releaseutil"
import ( import (
"reflect" "reflect"
"strings"
"testing" "testing"
) )
@ -37,7 +38,7 @@ spec:
cmd: fake-command cmd: fake-command
` `
const expectedManifest = `apiVersion: v1 const expectedManifest1 = `apiVersion: v1
kind: Pod kind: Pod
metadata: metadata:
name: finding-nemo, name: finding-nemo,
@ -49,13 +50,99 @@ spec:
image: fake-image image: fake-image
cmd: fake-command` cmd: fake-command`
const mockManifestFile2 = `
---
apiVersion: v1
kind: Pod
metadata:
name: finding-nemo,
annotations:
"helm.sh/hook": test
spec:
containers:
- name: nemo-test
image: fake-image
cmd: fake-command
---apiVersion: v1
kind: Pod
metadata:
name: finding-nemo-2,
annotations:
"helm.sh/hook": test
spec:
containers:
- name: nemo-test
image: fake-image
cmd: fake-command
`
const expectedManifest2 = `apiVersion: v1
kind: Pod
metadata:
name: finding-nemo-2,
annotations:
"helm.sh/hook": test
spec:
containers:
- name: nemo-test
image: fake-image
cmd: fake-command`
func TestSplitManifest(t *testing.T) { func TestSplitManifest(t *testing.T) {
manifests := SplitManifests(mockManifestFile) manifests := SplitManifests(mockManifestFile)
if len(manifests) != 1 { if len(manifests) != 1 {
t.Errorf("Expected 1 manifest, got %v", len(manifests)) t.Errorf("Expected 1 manifest, got %v", len(manifests))
} }
expected := map[string]string{"manifest-0": expectedManifest} expected := map[string]string{"manifest-0": expectedManifest1}
if !reflect.DeepEqual(manifests, expected) { if !reflect.DeepEqual(manifests, expected) {
t.Errorf("Expected %v, got %v", expected, manifests) t.Errorf("Expected \n%v\n got: \n%v", expected, manifests)
}
} }
func TestSplitManifestDocBreak(t *testing.T) {
manifests := SplitManifests(mockManifestFile2)
if len(manifests) != 2 {
t.Errorf("Expected 2 manifest, got %v", len(manifests))
}
expected := map[string]string{"manifest-0": expectedManifest1, "manifest-1": expectedManifest2}
if !reflect.DeepEqual(manifests, expected) {
t.Errorf("Expected \n%v\n got: \n%v", expected, manifests)
}
}
func TestSplitManifestNoDocBreak(t *testing.T) {
manifests := SplitManifests(expectedManifest1)
if len(manifests) != 1 {
t.Errorf("Expected 1 manifest, got %v", len(manifests))
}
expected := map[string]string{"manifest-0": expectedManifest1}
if !reflect.DeepEqual(manifests, expected) {
t.Errorf("Expected \n%v\n got: \n%v", expected, manifests)
}
}
func createManifest() string {
sb := strings.Builder{}
for i := 0; i < 10000; i++ {
sb.WriteString(expectedManifest2)
sb.WriteString("\n---")
}
return sb.String()
}
var BenchmarkSplitManifestsResult map[string]string
var largeManifest = createManifest()
func BenchmarkSplitManifests(b *testing.B) {
var r map[string]string
for n := 0; n < b.N; n++ {
r = SplitManifests(largeManifest)
}
BenchmarkSplitManifestsResult = r
} }

Loading…
Cancel
Save