Change:
/kind bug What happened: When using the downward api to render a file via a volume mount from and annotation that has newlines in it, it appears that the file is rendered with the content sorted by line. A concrete example: Expected is that would contain But it does not The above is obviously contrived, this is creating a problem for my team when we are trying to use the downward api to render a yaml file (which obviously has newlines) Environment: Kubernetes version (use ): 1.9.7 Cloud provider or hardware configuration: aws OS (e.g. from /etc/os-release): centos
/sig apps Not sure about the sig here
/sig node /sig storage
Just tested it on 1.10.5 and this bug still happens.
thanks!

Discussion:
importpath = "k8s.io/kubernetes/pkg/fieldpath", deps = [ "//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library", <ins> "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", </ins> "//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library", ], )

"strings" "k8s.io/apimachinery/pkg/api/meta" <ins> "k8s.io/apimachinery/pkg/util/sets" </ins> "k8s.io/apimachinery/pkg/util/validation" ) // FormatMap formats map[string]string to a string. func FormatMap(m map[string]string) (fmtStr string) { <del> for key, value := range m { fmtStr += fmt.Sprintf("%v=%qn", key, value) </del> <ins> // output with keys in sorted order to provide stable output keys := sets.NewString() for key := range m { keys.Insert(key) } for _, key := range keys.List() { fmtStr += fmt.Sprintf("%v=%qn", key, m[key]) </ins> } fmtStr = strings.TrimSuffix(fmtStr, "n")

"fmt" "path" "path/filepath" <del> "sort" "strings" </del> "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types"

glog.Errorf("Unable to extract field %s: %s", fileInfo.FieldRef.FieldPath, err.Error()) errlist = append(errlist, err) } else { <del> fileProjection.Data = []byte(sortLines(values)) </del> <ins> fileProjection.Data = []byte(values) </ins> } } else if fileInfo.ResourceFieldRef != nil { containerName := fileInfo.ResourceFieldRef.ContainerName

glog.Errorf("Unable to extract field %s: %s", fileInfo.ResourceFieldRef.Resource, err.Error()) errlist = append(errlist, err) } else { <del> fileProjection.Data = []byte(sortLines(values)) </del> <ins> fileProjection.Data = []byte(values) </ins> } }

return data, utilerrors.NewAggregate(errlist) } <del> // sortLines sorts the strings generated from map based data // (annotations and labels) func sortLines(values string) string { splitted := strings.Split(values, "n") sort.Strings(splitted) return strings.Join(splitted, "n") } </del> func (d *downwardAPIVolume) GetPath() string { return d.plugin.host.GetPodVolumeDir(d.podUID, utilstrings.EscapeQualifiedNameForDisk(downwardAPIPluginName), d.volName) }

"key3": "value3", } annotations := map[string]string{ <del> "a1": "value1", "a2": "value2", </del> <ins> "a1":        "value1", "a2":        "value2", "multiline": "cnbna", </ins> } testCases := []struct { name           string

t.Errorf(err.Error()) return } <del> actualStr := sortLines(string(data)) expectedStr := sortLines(expected) </del> <ins> actualStr := string(data) expectedStr := expected </ins> if actualStr != expectedStr { t.Errorf("Found `%s`, expected `%s`", actualStr, expectedStr) }