diff --git a/docs/source/markdown/podman-kube-play.1.md.in b/docs/source/markdown/podman-kube-play.1.md.in index f757821df9..9dd37172e9 100644 --- a/docs/source/markdown/podman-kube-play.1.md.in +++ b/docs/source/markdown/podman-kube-play.1.md.in @@ -174,6 +174,7 @@ Build images even if they are found in the local storage. Use `--build=false` to Use Kubernetes configmap YAML at path to provide a source for environment variable values within the containers of the pod. (This option is not available with the remote Podman client) Note: The *--configmap* option can be used multiple times or a comma-separated list of paths can be used to pass multiple Kubernetes configmap YAMLs. +The YAML file may be in a multi-doc YAML format. But, it must container only configmaps #### **--context-dir**=*path* diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 8fb628ee16..c5258b9fd4 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -524,16 +524,18 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } defer f.Close() - cm, err := readConfigMapFromFile(f) + cms, err := readConfigMapFromFile(f) if err != nil { return nil, nil, fmt.Errorf("%q: %w", p, err) } - if _, present := configMapIndex[cm.Name]; present { - return nil, nil, fmt.Errorf("ambiguous configuration: the same config map %s is present in YAML and in --configmaps %s file", cm.Name, p) - } + for _, cm := range cms { + if _, present := configMapIndex[cm.Name]; present { + return nil, nil, fmt.Errorf("ambiguous configuration: the same config map %s is present in YAML and in --configmaps %s file", cm.Name, p) + } - configMaps = append(configMaps, cm) + configMaps = append(configMaps, cm) + } } mountLabel, err := getMountLabel(podYAML.Spec.SecurityContext) @@ -1152,23 +1154,38 @@ func (ic *ContainerEngine) importVolume(ctx context.Context, vol *libpod.Volume, } // readConfigMapFromFile returns a kubernetes configMap obtained from --configmap flag -func readConfigMapFromFile(r io.Reader) (v1.ConfigMap, error) { - var cm v1.ConfigMap +func readConfigMapFromFile(r io.Reader) ([]v1.ConfigMap, error) { + configMaps := make([]v1.ConfigMap, 0) content, err := io.ReadAll(r) if err != nil { - return cm, fmt.Errorf("unable to read ConfigMap YAML content: %w", err) + return nil, fmt.Errorf("unable to read ConfigMap YAML content: %w", err) } - if err := yaml.Unmarshal(content, &cm); err != nil { - return cm, fmt.Errorf("unable to read YAML as Kube ConfigMap: %w", err) + // split yaml document + documentList, err := splitMultiDocYAML(content) + if err != nil { + return nil, fmt.Errorf("unable to read as kube YAML: %w", err) } - if cm.Kind != "ConfigMap" { - return cm, fmt.Errorf("invalid YAML kind: %q. [ConfigMap] is the only supported by --configmap", cm.Kind) + for _, document := range documentList { + kind, err := getKubeKind(document) + if err != nil { + return nil, fmt.Errorf("unable to read as kube YAML: %w", err) + } + + if kind != "ConfigMap" { + return nil, fmt.Errorf("invalid YAML kind: %q. [ConfigMap] is the only supported by --configmap", kind) + } + + var configMap v1.ConfigMap + if err := yaml.Unmarshal(document, &configMap); err != nil { + return nil, fmt.Errorf("unable to read YAML as Kube ConfigMap: %w", err) + } + configMaps = append(configMaps, configMap) } - return cm, nil + return configMaps, nil } // splitMultiDocYAML reads multiple documents in a YAML file and diff --git a/pkg/domain/infra/abi/play_test.go b/pkg/domain/infra/abi/play_test.go index 88d5696c11..39b99f4633 100644 --- a/pkg/domain/infra/abi/play_test.go +++ b/pkg/domain/infra/abi/play_test.go @@ -15,7 +15,7 @@ func TestReadConfigMapFromFile(t *testing.T) { configMapContent string expectError bool expectedErrorMsg string - expected v1.ConfigMap + expected []v1.ConfigMap }{ { "ValidConfigMap", @@ -29,16 +29,18 @@ data: `, false, "", - v1.ConfigMap{ - TypeMeta: v12.TypeMeta{ - Kind: "ConfigMap", - APIVersion: "v1", - }, - ObjectMeta: v12.ObjectMeta{ - Name: "foo", - }, - Data: map[string]string{ - "myvar": "foo", + []v1.ConfigMap{ + { + TypeMeta: v12.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: v12.ObjectMeta{ + Name: "foo", + }, + Data: map[string]string{ + "myvar": "foo", + }, }, }, }, @@ -54,8 +56,8 @@ data: myvar: foo `, true, - "unable to read YAML as Kube ConfigMap", - v1.ConfigMap{}, + "unable to read as kube YAML", + []v1.ConfigMap{}, }, { "InvalidKind", @@ -69,7 +71,7 @@ data: `, true, "invalid YAML kind", - v1.ConfigMap{}, + []v1.ConfigMap{}, }, { "ValidBinaryDataConfigMap", @@ -83,15 +85,64 @@ binaryData: `, false, "", - v1.ConfigMap{ - TypeMeta: v12.TypeMeta{ - Kind: "ConfigMap", - APIVersion: "v1", + []v1.ConfigMap{ + { + TypeMeta: v12.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: v12.ObjectMeta{ + Name: "foo", + }, + BinaryData: map[string][]byte{"data.zip": {0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0xc9, 0xbb, 0x4a, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54, 0x0d, 0x00, 0x07, 0xea, 0x8e, 0x44, 0x63, 0x3b, 0x8f, 0x44, 0x63, 0xea, 0x8e, 0x44, 0x63, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x4b, 0xca, 0xcc, 0x4b, 0x2c, 0xaa, 0x54, 0x48, 0x49, 0x2c, 0x49, 0xe4, 0x02, 0x00, 0x50, 0x4b, 0x07, 0x08, 0xdd, 0x09, 0xd9, 0xa0, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0xc9, 0xbb, 0x4a, 0x55, 0xdd, 0x09, 0xd9, 0xa0, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x00, 0x00, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54, 0x0d, 0x00, 0x07, 0xea, 0x8e, 0x44, 0x63, 0x3b, 0x8f, 0x44, 0x63, 0xea, 0x8e, 0x44, 0x63, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x56, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00}}, + }, + }, + }, + { + "MultiDocConfigMapFile", + ` +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: foo +data: + myvar: foo +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: bar +data: + myvar: bar +`, + false, + "", + []v1.ConfigMap{ + { + TypeMeta: v12.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: v12.ObjectMeta{ + Name: "foo", + }, + Data: map[string]string{ + "myvar": "foo", + }, }, - ObjectMeta: v12.ObjectMeta{ - Name: "foo", + { + TypeMeta: v12.TypeMeta{ + Kind: "ConfigMap", + APIVersion: "v1", + }, + ObjectMeta: v12.ObjectMeta{ + Name: "bar", + }, + Data: map[string]string{ + "myvar": "bar", + }, }, - BinaryData: map[string][]byte{"data.zip": {0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0xc9, 0xbb, 0x4a, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54, 0x0d, 0x00, 0x07, 0xea, 0x8e, 0x44, 0x63, 0x3b, 0x8f, 0x44, 0x63, 0xea, 0x8e, 0x44, 0x63, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x4b, 0xca, 0xcc, 0x4b, 0x2c, 0xaa, 0x54, 0x48, 0x49, 0x2c, 0x49, 0xe4, 0x02, 0x00, 0x50, 0x4b, 0x07, 0x08, 0xdd, 0x09, 0xd9, 0xa0, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0xc9, 0xbb, 0x4a, 0x55, 0xdd, 0x09, 0xd9, 0xa0, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x00, 0x00, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54, 0x0d, 0x00, 0x07, 0xea, 0x8e, 0x44, 0x63, 0x3b, 0x8f, 0x44, 0x63, 0xea, 0x8e, 0x44, 0x63, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x04, 0xe8, 0x03, 0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x56, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00}}, }, }, } @@ -107,7 +158,9 @@ binaryData: assert.Contains(t, err.Error(), test.expectedErrorMsg) } else { assert.NoError(t, err) - assert.Equal(t, test.expected, cm) + for _, expected := range test.expected { + assert.Contains(t, cm, expected) + } } }) } diff --git a/test/system/700-play.bats b/test/system/700-play.bats index 3c7a1eaeee..ad1e0ba646 100644 --- a/test/system/700-play.bats +++ b/test/system/700-play.bats @@ -651,3 +651,63 @@ spec: run_podman pod ps run_podman rmi $(pause_image) } + +@test "podman kube play with configmaps" { + skip_if_remote "the configmap argument is supported only locally" + + configmap_file=${PODMAN_TMPDIR}/play_kube_configmap_configmaps$(random_string 6).yaml + echo " +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: foo +data: + value: foo +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: bar +data: + value: bar +" > $configmap_file + + pod_file=${PODMAN_TMPDIR}/play_kube_configmap_pod$(random_string 6).yaml + echo " +apiVersion: v1 +kind: Pod +metadata: + labels: + app: test + name: test_pod +spec: + restartPolicy: Never + containers: + - name: server + image: $IMAGE + env: + - name: FOO + valueFrom: + configMapKeyRef: + name: foo + key: value + - name: BAR + valueFrom: + configMapKeyRef: + name: bar + key: value + command: + - /bin/sh + args: + - -c + - "echo \$FOO:\$BAR" +" > $pod_file + + run_podman kube play --configmap=$configmap_file $pod_file + run_podman wait test_pod-server + run_podman logs test_pod-server + is $output "foo:bar" + + run_podman kube down $pod_file +}