repo: detect JSON and unmarshal efficiently

When an index is in a JSON format, the `sigs.k8s.io/yaml` package uses
an inefficient approach to unmarshaling the data, as it does an
unnecessary roundtrip on the data to transform the YAML to valid JSON.

To prevent this from happening, detect if the bytes which we attempt
to load contain valid JSON, and unmarshal them directly using
`json.Unmarshal` instead.

Signed-off-by: Hidde Beydals <hidde@hhh.computer>
This commit is contained in:
Hidde Beydals 2023-07-20 23:23:35 +02:00
parent a50206fed2
commit e21c9cf7e2
No known key found for this signature in database
GPG Key ID: 979F380FC2341744
3 changed files with 74 additions and 1 deletions

View File

@ -18,6 +18,7 @@ package repo
import (
"bytes"
"encoding/json"
"log"
"os"
"path"
@ -336,7 +337,7 @@ func loadIndex(data []byte, source string) (*IndexFile, error) {
return i, ErrEmptyIndexYaml
}
if err := yaml.UnmarshalStrict(data, i); err != nil {
if err := jsonOrYamlUnmarshal(data, i); err != nil {
return i, err
}
@ -361,3 +362,17 @@ func loadIndex(data []byte, source string) (*IndexFile, error) {
}
return i, nil
}
// jsonOrYamlUnmarshal unmarshals the given byte slice containing JSON or YAML
// into the provided interface.
//
// It automatically detects whether the data is in JSON or YAML format by
// checking its validity as JSON. If the data is valid JSON, it will use the
// `encoding/json` package to unmarshal it. Otherwise, it will use the
// `sigs.k8s.io/yaml` package to unmarshal the YAML data.
func jsonOrYamlUnmarshal(b []byte, i interface{}) error {
if json.Valid(b) {
return json.Unmarshal(b, i)
}
return yaml.UnmarshalStrict(b, i)
}

View File

@ -37,6 +37,7 @@ const (
annotationstestfile = "testdata/local-index-annotations.yaml"
chartmuseumtestfile = "testdata/chartmuseum-index.yaml"
unorderedTestfile = "testdata/local-index-unordered.yaml"
jsonTestfile = "testdata/local-index.json"
testRepo = "test-repo"
indexWithDuplicates = `
apiVersion: v1
@ -145,6 +146,10 @@ func TestLoadIndex(t *testing.T) {
Name: "chartmuseum index file",
Filename: chartmuseumtestfile,
},
{
Name: "JSON index file",
Filename: jsonTestfile,
},
}
for _, tc := range tests {

53
pkg/repo/testdata/local-index.json vendored Normal file
View File

@ -0,0 +1,53 @@
{
"apiVersion": "v1",
"entries": {
"nginx": [
{
"urls": ["https://charts.helm.sh/stable/nginx-0.2.0.tgz"],
"name": "nginx",
"description": "string",
"version": "0.2.0",
"home": "https://github.com/something/else",
"digest": "sha256:1234567890abcdef",
"keywords": ["popular", "web server", "proxy"],
"apiVersion": "v2"
},
{
"urls": ["https://charts.helm.sh/stable/nginx-0.1.0.tgz"],
"name": "nginx",
"description": "string",
"version": "0.1.0",
"home": "https://github.com/something",
"digest": "sha256:1234567890abcdef",
"keywords": ["popular", "web server", "proxy"],
"apiVersion": "v2"
}
],
"alpine": [
{
"urls": [
"https://charts.helm.sh/stable/alpine-1.0.0.tgz",
"http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz"
],
"name": "alpine",
"description": "string",
"version": "1.0.0",
"home": "https://github.com/something",
"keywords": ["linux", "alpine", "small", "sumtin"],
"digest": "sha256:1234567890abcdef",
"apiVersion": "v2"
}
],
"chartWithNoURL": [
{
"name": "chartWithNoURL",
"description": "string",
"version": "1.0.0",
"home": "https://github.com/something",
"keywords": ["small", "sumtin"],
"digest": "sha256:1234567890abcdef",
"apiVersion": "v2"
}
]
}
}