Skip to content

Commit a2b1d73

Browse files
committed
feat: support stringData in secretGenerator
1 parent dd93fb1 commit a2b1d73

File tree

7 files changed

+87
-6
lines changed

7 files changed

+87
-6
lines changed

api/internal/generators/secret.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,27 @@ import (
1111

1212
// MakeSecret makes a kubernetes Secret.
1313
//
14-
// Secret: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#secret-v1-core
14+
// Secret: https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/secret-v1/
1515
//
1616
// ConfigMaps and Secrets are similar.
1717
//
1818
// Like a ConfigMap, a Secret has a `data` field, but unlike a ConfigMap it has
19-
// no `binaryData` field.
19+
// no `binaryData` field. Secret also provides a `stringData` field.
2020
//
21-
// All of a Secret's data is assumed to be opaque in nature, and assumed to be
21+
// A Secret's `data` is assumed to be opaque in nature, and assumed to be
2222
// base64 encoded from its original representation, regardless of whether the
2323
// original data was UTF-8 text or binary.
2424
//
2525
// This encoding provides no secrecy. It's just a neutral, common means to
2626
// represent opaque text and binary data. Beneath the base64 encoding
2727
// is presumably further encoding under control of the Secret's consumer.
2828
//
29+
// A Secret's `stringData` field is similar to ConfigMap's `data` field.
30+
// `stringData` allows specifying non-binary, UTF-8 secret data in string form.
31+
// It is provided as a write-only input field for convenience.
32+
// All keys and values are merged into the data field on write, overwriting any
33+
// existing values. The stringData field is never output when reading from the API.
34+
//
2935
// A Secret has string field `type` which holds an identifier, used by the
3036
// client, to choose the algorithm to interpret the `data` field. Kubernetes
3137
// cannot make use of this data; it's up to a controller or some pod's service
@@ -50,8 +56,14 @@ func MakeSecret(
5056
if err != nil {
5157
return nil, err
5258
}
53-
if err = rn.LoadMapIntoSecretData(m); err != nil {
54-
return nil, err
59+
if args.StringData {
60+
if err = rn.LoadMapIntoSecretStringData(m); err != nil {
61+
return nil, err
62+
}
63+
} else {
64+
if err = rn.LoadMapIntoSecretData(m); err != nil {
65+
return nil, err
66+
}
5567
}
5668
copyLabelsAndAnnotations(rn, args.Options)
5769
setImmutable(rn, args.Options)

api/resmap/reswrangler.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,7 @@ func (m *resWrangler) appendReplaceOrMerge(res *resource.Resource) error {
591591
res.CopyMergeMetaDataFieldsFrom(old)
592592
res.MergeDataMapFrom(old)
593593
res.MergeBinaryDataMapFrom(old)
594+
res.MergeStringDataMapFrom(old)
594595
if orig != nil {
595596
res.SetOrigin(orig)
596597
}

api/resource/resource.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ func (r *Resource) DeepCopy() *Resource {
157157
// the resource.
158158
// TODO: move to RNode, use GetMeta to improve performance.
159159
// TODO: make a version of mergeStringMaps that is build-annotation aware
160-
// to avoid repeatedly setting refby and genargs annotations
160+
// to avoid repeatedly setting refby and genargs annotations
161161
// Must remove the kustomize bit at the end.
162162
func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
163163
if err := r.SetLabels(
@@ -197,6 +197,10 @@ func (r *Resource) MergeBinaryDataMapFrom(o *Resource) {
197197
r.SetBinaryDataMap(mergeStringMaps(o.GetBinaryDataMap(), r.GetBinaryDataMap()))
198198
}
199199

200+
func (r *Resource) MergeStringDataMapFrom(o *Resource) {
201+
r.SetStringDataMap(mergeStringMaps(o.GetStringDataMap(), r.GetStringDataMap()))
202+
}
203+
200204
func (r *Resource) ErrIfNotEquals(o *Resource) error {
201205
meYaml, err := r.AsYAML()
202206
if err != nil {

api/types/secretargs.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,10 @@ type SecretArgs struct {
1616
// If type is "kubernetes.io/tls", then "literals" or "files" must have exactly two
1717
// keys: "tls.key" and "tls.crt"
1818
Type string `json:"type,omitempty" yaml:"type,omitempty"`
19+
20+
// StringData if true generates a v1/Secret with plain-text stringData fields
21+
// instead of base64-encoded data fields. If any fields are not UTF-8, they
22+
// are still base64-encoded and stored as data as a fallback behavior. This
23+
// is similar to the default behavior of a ConfigMap.
24+
StringData bool `json:"stringData,omitempty" yaml:"stringData,omitempty"`
1925
}

kyaml/yaml/const.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const (
2424
MetadataField = "metadata"
2525
DataField = "data"
2626
BinaryDataField = "binaryData"
27+
StringDataField = "stringData"
2728
NameField = "name"
2829
NamespaceField = "namespace"
2930
LabelsField = "labels"

kyaml/yaml/datamap.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,35 @@ func makeSecretValueRNode(s string) *RNode {
9595
return NewRNode(yN)
9696
}
9797

98+
func (rn *RNode) LoadMapIntoSecretStringData(m map[string]string) error {
99+
for _, k := range SortedMapKeys(m) {
100+
fldName, vrN := makeSecretStringValueRNode(m[k])
101+
if _, err := rn.Pipe(
102+
LookupCreate(MappingNode, fldName),
103+
SetField(k, vrN)); err != nil {
104+
return err
105+
}
106+
}
107+
return nil
108+
}
109+
110+
// valid UTF-8 strings can be stringData, but fallback to Base64 for non UTF-8
111+
func makeSecretStringValueRNode(s string) (field string, rN *RNode) {
112+
yN := &Node{Kind: ScalarNode}
113+
yN.Tag = NodeTagString
114+
if utf8.ValidString(s) {
115+
field = StringDataField
116+
yN.Value = s
117+
} else {
118+
field = DataField
119+
yN.Value = encodeBase64(s)
120+
}
121+
if strings.Contains(yN.Value, "\n") {
122+
yN.Style = LiteralStyle
123+
}
124+
return field, NewRNode(yN)
125+
}
126+
98127
// encodeBase64 encodes s as base64 that is broken up into multiple lines
99128
// as appropriate for the resulting length.
100129
func encodeBase64(s string) string {

kyaml/yaml/rnode.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,19 @@ func (rn *RNode) GetBinaryDataMap() map[string]string {
632632
return result
633633
}
634634

635+
func (rn *RNode) GetStringDataMap() map[string]string {
636+
n, err := rn.Pipe(Lookup(StringDataField))
637+
if err != nil {
638+
return nil
639+
}
640+
result := map[string]string{}
641+
_ = n.VisitFields(func(node *MapNode) error {
642+
result[GetValue(node.Key)] = GetValue(node.Value)
643+
return nil
644+
})
645+
return result
646+
}
647+
635648
// GetValidatedDataMap retrieves the data map and returns an error if the data
636649
// map contains entries which are not included in the expectedKeys set.
637650
func (rn *RNode) GetValidatedDataMap(expectedKeys []string) (map[string]string, error) {
@@ -688,6 +701,21 @@ func (rn *RNode) SetBinaryDataMap(m map[string]string) {
688701
}
689702
}
690703

704+
func (rn *RNode) SetStringDataMap(m map[string]string) {
705+
if rn == nil {
706+
log.Fatal("cannot set stringData map on nil Rnode")
707+
}
708+
if err := rn.PipeE(Clear(StringDataField)); err != nil {
709+
log.Fatal(err)
710+
}
711+
if len(m) == 0 {
712+
return
713+
}
714+
if err := rn.LoadMapIntoSecretStringData(m); err != nil {
715+
log.Fatal(err)
716+
}
717+
}
718+
691719
// AppendToFieldPath appends a field name to the FieldPath.
692720
func (rn *RNode) AppendToFieldPath(parts ...string) {
693721
rn.fieldPath = append(rn.fieldPath, parts...)

0 commit comments

Comments
 (0)