Skip to content

Commit

Permalink
Introduce new yaml package with Encode func
Browse files Browse the repository at this point in the history
Comparison versus `sigs.k8s.io/yaml#Marshal`:

```
BenchmarkEncode/EncodeWithSort-12         	    475	  2419063 ns/op	2235305 B/op	   5398 allocs/op
BenchmarkEncode/EncodeWithSort-12         	    498	  2406794 ns/op	2235300 B/op	   5398 allocs/op
BenchmarkEncode/EncodeWithSort-12         	    492	  2376460 ns/op	2235312 B/op	   5398 allocs/op
BenchmarkEncode/EncodeWithSort-12         	    496	  2406756 ns/op	2235323 B/op	   5398 allocs/op
BenchmarkEncode/EncodeWithSort-12         	    488	  2402969 ns/op	2235336 B/op	   5398 allocs/op
BenchmarkEncode/SigYAMLMarshal-12         	    202	  5791549 ns/op	3124841 B/op	  19324 allocs/op
BenchmarkEncode/SigYAMLMarshal-12         	    205	  5780248 ns/op	3123193 B/op	  19320 allocs/op
BenchmarkEncode/SigYAMLMarshal-12         	    207	  5762621 ns/op	3124537 B/op	  19324 allocs/op
BenchmarkEncode/SigYAMLMarshal-12         	    214	  5748899 ns/op	3121183 B/op	  19324 allocs/op
BenchmarkEncode/SigYAMLMarshal-12         	    211	  5682105 ns/op	3120592 B/op	  19325 allocs/op
```

Signed-off-by: Hidde Beydals <hidde@hhh.computer>
  • Loading branch information
hiddeco committed Jul 17, 2023
1 parent 0f6a8d1 commit 7d044a7
Show file tree
Hide file tree
Showing 13 changed files with 4,638 additions and 222 deletions.
1 change: 1 addition & 0 deletions api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
k8s.io/apiextensions-apiserver v0.27.3
k8s.io/apimachinery v0.27.3
sigs.k8s.io/controller-runtime v0.15.0
sigs.k8s.io/yaml v1.3.0
)

require (
Expand Down
1 change: 1 addition & 0 deletions api/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,4 @@ sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h6
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
4 changes: 2 additions & 2 deletions api/v2beta2/helmrelease_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ limitations under the License.
package v2beta2

import (
"encoding/json"
"fmt"
"strings"
"time"

apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/yaml"

"github.com/fluxcd/pkg/apis/kustomize"
"github.com/fluxcd/pkg/apis/meta"
Expand Down Expand Up @@ -1118,7 +1118,7 @@ func (in HelmRelease) GetRequeueAfter() time.Duration {
func (in HelmRelease) GetValues() map[string]interface{} {
var values map[string]interface{}
if in.Spec.Values != nil {
_ = json.Unmarshal(in.Spec.Values.Raw, &values)
_ = yaml.Unmarshal(in.Spec.Values.Raw, &values)
}
return values
}
Expand Down
25 changes: 21 additions & 4 deletions internal/chartutil/digest.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,18 @@ package chartutil
import (
"github.com/opencontainers/go-digest"
"helm.sh/helm/v3/pkg/chartutil"

intyaml "github.com/fluxcd/helm-controller/internal/yaml"
)

// DigestValues calculates the digest of the values using the provided algorithm.
// The caller is responsible for ensuring that the algorithm is supported.
func DigestValues(algo digest.Algorithm, values chartutil.Values) digest.Digest {
digester := algo.Digester()
if err := values.Encode(digester.Hash()); err != nil {
return ""
if values = valuesOrNil(values); values != nil {
if err := intyaml.Encode(digester.Hash(), values, intyaml.SortMapSlice); err != nil {
return ""
}
}
return digester.Digest()
}
Expand All @@ -36,9 +40,22 @@ func VerifyValues(digest digest.Digest, values chartutil.Values) bool {
if digest.Validate() != nil {
return false
}

verifier := digest.Verifier()
if err := values.Encode(verifier); err != nil {
return false
if values = valuesOrNil(values); values != nil {
if err := intyaml.Encode(verifier, values, intyaml.SortMapSlice); err != nil {
return false
}
}
return verifier.Verified()
}

// valuesOrNil returns nil if the values are empty, otherwise the values are
// returned. This is used to ensure that the digest is calculated against nil
// opposed to an empty object.
func valuesOrNil(values chartutil.Values) chartutil.Values {
if values != nil && len(values) == 0 {
return nil
}
return values
}
199 changes: 188 additions & 11 deletions internal/chartutil/digest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,45 +23,222 @@ import (
"helm.sh/helm/v3/pkg/chartutil"
)

const testDigestAlgo = digest.SHA256

func TestDigestValues(t *testing.T) {
tests := []struct {
name string
algo digest.Algorithm
values chartutil.Values
want digest.Digest
}{
{
name: "empty",
algo: digest.SHA256,
values: chartutil.Values{},
want: "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356",
want: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
},
{
name: "nil",
algo: digest.SHA256,
values: nil,
want: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
},
{
name: "value map",
algo: digest.SHA256,
values: chartutil.Values{
"foo": "bar",
"baz": map[string]string{
"cool": "stuff",
"replicas": 3,
"image": map[string]interface{}{
"tag": "latest",
"repository": "nginx",
},
"ports": []interface{}{
map[string]interface{}{
"protocol": "TCP",
"port": 8080,
},
map[string]interface{}{
"port": 9090,
"protocol": "UDP",
},
},
},
want: "sha256:3f3641788a2d4abda3534eaa90c90b54916e4c6e3a5b2e1b24758b7bfa701ecd",
want: "sha256:fcdc2b0de1581a3633ada4afee3f918f6eaa5b5ab38c3fef03d5b48d3f85d9f6",
},
{
name: "value map in different order",
algo: digest.SHA256,
values: chartutil.Values{
"baz": map[string]string{
"cool": "stuff",
"image": map[string]interface{}{
"repository": "nginx",
"tag": "latest",
},
"ports": []interface{}{
map[string]interface{}{
"port": 8080,
"protocol": "TCP",
},
map[string]interface{}{
"port": 9090,
"protocol": "UDP",
},
},
"replicas": 3,
},
want: "sha256:fcdc2b0de1581a3633ada4afee3f918f6eaa5b5ab38c3fef03d5b48d3f85d9f6",
},
{
// Explicit test for something that does not work with sigs.k8s.io/yaml.
// See: https://go.dev/play/p/KRyfK9ZobZx
name: "values map with numeric keys",
algo: digest.SHA256,
values: chartutil.Values{
"replicas": 3,
"test": map[string]interface{}{
"632bd80235a05f4192aefade": "value1",
"632bd80ddf416cf32fd50679": "value2",
"632bd817c559818a52307da2": "value3",
"632bd82398e71231a98004b6": "value4",
},
},
want: "sha256:8a980fcbeadd6f05818f07e8aec14070c22250ca3d96af1fcd5f93b3e85b4d70",
},
{
name: "values map with numeric keys in different order",
algo: digest.SHA256,
values: chartutil.Values{
"test": map[string]interface{}{
"632bd82398e71231a98004b6": "value4",
"632bd817c559818a52307da2": "value3",
"632bd80ddf416cf32fd50679": "value2",
"632bd80235a05f4192aefade": "value1",
},
"replicas": 3,
},
want: "sha256:8a980fcbeadd6f05818f07e8aec14070c22250ca3d96af1fcd5f93b3e85b4d70",
},
{
name: "using different algorithm",
algo: digest.SHA512,
values: chartutil.Values{
"foo": "bar",
"baz": map[string]interface{}{
"cool": "stuff",
},
},
want: "sha256:3f3641788a2d4abda3534eaa90c90b54916e4c6e3a5b2e1b24758b7bfa701ecd",
want: "sha512:b5f9cd4855ca3b08afd602557f373069b1732ce2e6d52341481b0d38f1938452e9d7759ab177c66699962b592f20ceded03eea3cd405d8670578c47842e2c550",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := DigestValues(testDigestAlgo, tt.values); got != tt.want {
if got := DigestValues(tt.algo, tt.values); got != tt.want {
t.Errorf("DigestValues() = %v, want %v", got, tt.want)
}
})
}
}

func TestVerifyValues(t *testing.T) {
tests := []struct {
name string
digest digest.Digest
values chartutil.Values
want bool
}{
{
name: "empty values",
digest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
values: chartutil.Values{},
want: true,
},
{
name: "nil values",
digest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
values: nil,
want: true,
},
{
name: "empty digest",
digest: "",
want: false,
},
{
name: "invalid digest",
digest: "sha512:invalid",
values: nil,
want: false,
},
{
name: "matching values",
digest: "sha256:fcdc2b0de1581a3633ada4afee3f918f6eaa5b5ab38c3fef03d5b48d3f85d9f6",
values: chartutil.Values{
"image": map[string]interface{}{
"repository": "nginx",
"tag": "latest",
},
"ports": []interface{}{
map[string]interface{}{
"port": 8080,
"protocol": "TCP",
},
map[string]interface{}{
"port": 9090,
"protocol": "UDP",
},
},
"replicas": 3,
},
want: true,
},
{
name: "matching values in different order",
digest: "sha256:fcdc2b0de1581a3633ada4afee3f918f6eaa5b5ab38c3fef03d5b48d3f85d9f6",
values: chartutil.Values{
"replicas": 3,
"image": map[string]interface{}{
"tag": "latest",
"repository": "nginx",
},
"ports": []interface{}{
map[string]interface{}{
"protocol": "TCP",
"port": 8080,
},
map[string]interface{}{
"port": 9090,
"protocol": "UDP",
},
},
},
want: true,
},
{
name: "matching values with numeric keys",
digest: "sha256:8a980fcbeadd6f05818f07e8aec14070c22250ca3d96af1fcd5f93b3e85b4d70",
values: chartutil.Values{
"replicas": 3,
"test": map[string]interface{}{
"632bd80235a05f4192aefade": "value1",
"632bd80ddf416cf32fd50679": "value2",
"632bd817c559818a52307da2": "value3",
"632bd82398e71231a98004b6": "value4",
},
},
want: true,
},
{
name: "mismatching values",
digest: "sha256:3f3641788a2d4abda3534eaa90c90b54916e4c6e3a5b2e1b24758b7bfa701ecd",
values: chartutil.Values{
"foo": "bar",
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := VerifyValues(tt.digest, tt.values); got != tt.want {
t.Errorf("VerifyValues() = %v, want %v", got, tt.want)
}
})
}
}
2 changes: 1 addition & 1 deletion internal/chartutil/values_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client/fake"

v2 "github.com/fluxcd/helm-controller/api/v2beta1"
v2 "github.com/fluxcd/helm-controller/api/v2beta2"
)

func TestChartValuesFromReferences(t *testing.T) {
Expand Down
Loading

0 comments on commit 7d044a7

Please sign in to comment.