-
Notifications
You must be signed in to change notification settings - Fork 113
/
manifest_json.go
101 lines (88 loc) · 3.21 KB
/
manifest_json.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// Copyright 2021, Pulumi Corporation. All rights reserved.
package provider
import (
"fmt"
"strings"
"github.com/pulumi/pulumi/pkg/v3/codegen"
logger "github.com/pulumi/pulumi/sdk/v3/go/common/util/logging"
"golang.org/x/crypto/sha3"
"helm.sh/helm/v3/pkg/releaseutil"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
)
// This file is a modified version of
// https://github.com/hashicorp/terraform-provider-helm/blob/main/helm/manifest_json.go
// convertYAMLManifestToJSON converts manifests provided s a string and returns
// a deserialized map representation of the manifest (with secrets masked), a map
// grouping resource names in the manifests by group version and any error encountered.
// Not, currently only kubernetes secret data is masked.
func convertYAMLManifestToJSON(manifest string) (map[string]any, map[string][]string, error) {
releaseResources := map[string]codegen.StringSet{}
m := map[string]any{}
resources := releaseutil.SplitManifests(manifest)
for _, resource := range resources {
obj := new(unstructured.Unstructured)
// decode YAML into unstructured.Unstructured
dec := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
_, gvk, err := dec.Decode([]byte(resource), nil, obj)
if err != nil {
if runtime.IsMissingKind(err) {
// Likely empty/nil resource. Ignore.
continue
}
return nil, nil, err
}
resKey := fmt.Sprintf("%s/%s", gvk.GroupKind().String(), obj.GetAPIVersion())
resVal, has := releaseResources[resKey]
if !has {
resVal = codegen.NewStringSet()
}
resName := obj.GetName()
if namespace := obj.GetNamespace(); namespace != "" {
resName = fmt.Sprintf("%s/%s", namespace, resName)
}
resVal.Add(resName)
releaseResources[resKey] = resVal
key := fmt.Sprintf("%s/%s/%s", strings.ToLower(gvk.GroupKind().String()),
obj.GetAPIVersion(),
obj.GetName())
if namespace := obj.GetNamespace(); namespace != "" {
key = fmt.Sprintf("%s/%s", namespace, key)
}
var o any = &obj.Object
if gvk.Kind == "Secret" {
var secret corev1.Secret
err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &secret)
if err != nil {
return nil, nil, err
}
for k, v := range secret.Data {
h := hashSensitiveValue(string(v))
secret.Data[k] = []byte(h)
}
o = &secret
}
unstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(o)
if err != nil {
return nil, nil, err
}
m[key] = unstructured
}
logger.V(9).Infof("Manifest: %#v", m)
releaseResourcesGrouping := map[string][]string{}
for k, v := range releaseResources {
releaseResourcesGrouping[k] = v.SortedValues()
}
return m, releaseResourcesGrouping, nil
}
// hashSensitiveValue creates a hash of a sensitive value and returns the string
// "(sensitive value xxxxxxxx)". We have to do this because helm release manifests
// may end up embedding secrets. This allows us to try and render the manifests
// while avoiding the possibility of leaking sensitive values.
func hashSensitiveValue(v string) string {
hash := make([]byte, 8)
sha3.ShakeSum256(hash, []byte(v))
return fmt.Sprintf("(sensitive value %x)", hash)
}