Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kube Play - Support multi-doc YAML files for configmap argument #18538

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/source/markdown/podman-kube-play.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -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*

Expand Down
43 changes: 30 additions & 13 deletions pkg/domain/infra/abi/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
97 changes: 75 additions & 22 deletions pkg/domain/infra/abi/play_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func TestReadConfigMapFromFile(t *testing.T) {
configMapContent string
expectError bool
expectedErrorMsg string
expected v1.ConfigMap
expected []v1.ConfigMap
}{
{
"ValidConfigMap",
Expand All @@ -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",
},
},
},
},
Expand All @@ -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",
Expand All @@ -69,7 +71,7 @@ data:
`,
true,
"invalid YAML kind",
v1.ConfigMap{},
[]v1.ConfigMap{},
},
{
"ValidBinaryDataConfigMap",
Expand All @@ -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}},
},
},
}
Expand All @@ -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)
}
}
})
}
Expand Down
60 changes: 60 additions & 0 deletions test/system/700-play.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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
}