Skip to content

Commit ad961d7

Browse files
committed
feat(cmd): podman kube play support multiple arguments
Signed-off-by: axel7083 <[email protected]>
1 parent 4871ad1 commit ad961d7

File tree

4 files changed

+241
-8
lines changed

4 files changed

+241
-8
lines changed

cmd/podman/kube/down.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ var (
2222
Short: "Remove pods based on Kubernetes YAML",
2323
Long: downDescription,
2424
RunE: down,
25-
Args: cobra.ExactArgs(1),
25+
Args: cobra.MinimumNArgs(1),
2626
ValidArgsFunction: common.AutocompleteDefaultOneArg,
2727
Example: `podman kube down nginx.yml
2828
cat nginx.yml | podman kube down -
@@ -48,7 +48,7 @@ func downFlags(cmd *cobra.Command) {
4848
}
4949

5050
func down(cmd *cobra.Command, args []string) error {
51-
reader, err := readerFromArg(args[0])
51+
reader, err := readerFromArgs(args)
5252
if err != nil {
5353
return err
5454
}

cmd/podman/kube/play.go

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ var (
5555
Short: "Play a pod or volume based on Kubernetes YAML",
5656
Long: playDescription,
5757
RunE: play,
58-
Args: cobra.ExactArgs(1),
58+
Args: cobra.MinimumNArgs(1),
5959
ValidArgsFunction: common.AutocompleteDefaultOneArg,
6060
Example: `podman kube play nginx.yml
6161
cat nginx.yml | podman kube play -
@@ -71,7 +71,7 @@ var (
7171
Long: playDescription,
7272
Hidden: true,
7373
RunE: playKube,
74-
Args: cobra.ExactArgs(1),
74+
Args: cobra.MinimumNArgs(1),
7575
ValidArgsFunction: common.AutocompleteDefaultOneArg,
7676
Example: `podman play kube nginx.yml
7777
cat nginx.yml | podman play kube -
@@ -276,7 +276,7 @@ func play(cmd *cobra.Command, args []string) error {
276276
return errors.New("--force may be specified only with --down")
277277
}
278278

279-
reader, err := readerFromArg(args[0])
279+
reader, err := readerFromArgs(args)
280280
if err != nil {
281281
return err
282282
}
@@ -306,7 +306,7 @@ func play(cmd *cobra.Command, args []string) error {
306306
playOptions.ServiceContainer = true
307307

308308
// Read the kube yaml file again so that a reader can be passed down to the teardown function
309-
teardownReader, err = readerFromArg(args[0])
309+
teardownReader, err = readerFromArgs(args)
310310
if err != nil {
311311
return err
312312
}
@@ -364,11 +364,43 @@ func playKube(cmd *cobra.Command, args []string) error {
364364
return play(cmd, args)
365365
}
366366

367+
func readerFromArgs(args []string) (*bytes.Reader, error) {
368+
// if user tried to pipe, shortcut the reading
369+
if len(args) == 1 && args[0] == "-" {
370+
data, err := io.ReadAll(os.Stdin)
371+
if err != nil {
372+
return nil, err
373+
}
374+
return bytes.NewReader(data), nil
375+
}
376+
377+
var combined bytes.Buffer
378+
379+
for i, arg := range args {
380+
reader, err := readerFromArg(arg)
381+
if err != nil {
382+
return nil, err
383+
}
384+
385+
data, err := io.ReadAll(reader)
386+
if err != nil {
387+
return nil, err
388+
}
389+
390+
// Write the document content
391+
combined.Write(data)
392+
393+
// Only add `---` separator if it's not the last file
394+
if i < len(args)-1 {
395+
combined.WriteString("\n---\n")
396+
}
397+
}
398+
return bytes.NewReader(combined.Bytes()), nil
399+
}
400+
367401
func readerFromArg(fileName string) (*bytes.Reader, error) {
368402
var reader io.Reader
369403
switch {
370-
case fileName == "-": // Read from stdin
371-
reader = os.Stdin
372404
case parse.ValidURL(fileName) == nil:
373405
response, err := http.Get(fileName)
374406
if err != nil {

cmd/podman/kube/play_test.go

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package kube
2+
3+
import (
4+
"io"
5+
"os"
6+
"strings"
7+
"testing"
8+
)
9+
10+
// createTempFile writes content to a temp file and returns its path.
11+
func createTempFile(t *testing.T, content string) string {
12+
t.Helper()
13+
14+
tmp, err := os.CreateTemp(t.TempDir(), "testfile-*.yaml")
15+
if err != nil {
16+
t.Fatalf("failed to create temp file: %v", err)
17+
}
18+
19+
if _, err := tmp.WriteString(content); err != nil {
20+
t.Fatalf("failed to write to temp file: %v", err)
21+
}
22+
23+
if err := tmp.Close(); err != nil {
24+
t.Fatalf("failed to close temp file: %v", err)
25+
}
26+
27+
return tmp.Name()
28+
}
29+
30+
func TestReaderFromArgs(t *testing.T) {
31+
tests := []struct {
32+
name string
33+
files []string // file contents
34+
expected string // expected concatenated output
35+
}{
36+
{
37+
name: "single file",
38+
files: []string{
39+
`apiVersion: v1
40+
kind: ConfigMap
41+
metadata:
42+
name: my-config`,
43+
},
44+
expected: `apiVersion: v1
45+
kind: ConfigMap
46+
metadata:
47+
name: my-config`,
48+
},
49+
{
50+
name: "two files",
51+
files: []string{
52+
`apiVersion: v1
53+
kind: Pod
54+
metadata:
55+
name: my-pod`,
56+
`apiVersion: v1
57+
kind: Service
58+
metadata:
59+
name: my-service`,
60+
},
61+
expected: `apiVersion: v1
62+
kind: Pod
63+
metadata:
64+
name: my-pod
65+
---
66+
apiVersion: v1
67+
kind: Service
68+
metadata:
69+
name: my-service`,
70+
},
71+
{
72+
name: "empty file and normal file",
73+
files: []string{
74+
``,
75+
`apiVersion: v1
76+
kind: Secret
77+
metadata:
78+
name: my-secret`,
79+
},
80+
expected: `---
81+
apiVersion: v1
82+
kind: Secret
83+
metadata:
84+
name: my-secret`,
85+
},
86+
{
87+
name: "files with only whitespace",
88+
files: []string{
89+
"\n \n",
90+
`apiVersion: v1
91+
kind: Namespace
92+
metadata:
93+
name: test-ns`,
94+
},
95+
expected: `
96+
97+
---
98+
apiVersion: v1
99+
kind: Namespace
100+
metadata:
101+
name: test-ns`,
102+
},
103+
}
104+
105+
for _, tt := range tests {
106+
t.Run(tt.name, func(t *testing.T) {
107+
var paths []string
108+
for _, content := range tt.files {
109+
path := createTempFile(t, content)
110+
defer os.Remove(path)
111+
paths = append(paths, path)
112+
}
113+
114+
reader, err := readerFromArgs(paths)
115+
if err != nil {
116+
t.Fatalf("readerFromArgs failed: %v", err)
117+
}
118+
119+
output, err := io.ReadAll(reader)
120+
if err != nil {
121+
t.Fatalf("failed to read result: %v", err)
122+
}
123+
124+
got := strings.TrimSpace(string(output))
125+
want := strings.TrimSpace(tt.expected)
126+
127+
if got != want {
128+
t.Errorf("unexpected output:\n--- got ---\n%s\n--- want ---\n%s", got, want)
129+
}
130+
})
131+
}
132+
}
133+
134+
func TestReaderFromArgs_Stdin(t *testing.T) {
135+
const input = `apiVersion: v1
136+
kind: Namespace
137+
metadata:
138+
name: from-stdin`
139+
140+
oldStdin := os.Stdin
141+
defer func() { os.Stdin = oldStdin }()
142+
143+
r, w, _ := os.Pipe()
144+
_, _ = w.WriteString(input)
145+
_ = w.Close()
146+
os.Stdin = r
147+
148+
reader, err := readerFromArgs([]string{"-"})
149+
if err != nil {
150+
t.Fatalf("readerFromArgs failed: %v", err)
151+
}
152+
153+
data, err := io.ReadAll(reader)
154+
if err != nil {
155+
t.Fatalf("failed to read from stdin: %v", err)
156+
}
157+
158+
if got := string(data); got != input {
159+
t.Errorf("unexpected stdin result:\n--- got ---\n%s\n--- want ---\n%s", got, input)
160+
}
161+
}

test/e2e/play_kube_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2350,6 +2350,46 @@ var _ = Describe("Podman kube play", func() {
23502350
kubeYaml = filepath.Join(podmanTest.TempDir, "kube.yaml")
23512351
})
23522352

2353+
It("all arguments should be read", func() {
2354+
// let's create a matrix of pod name => filename
2355+
pods := [][]string{
2356+
{"testPod1", "testPod1.yaml"},
2357+
{"testPod2", "testPod2.yaml"},
2358+
{"testPod3", "testPod3.yaml"},
2359+
{"testPod4", "testPod4.yaml"},
2360+
}
2361+
2362+
// prepare our podman command
2363+
var cmd = []string{"play", "kube"}
2364+
2365+
// for each pod: let's create a yaml file in the tmp dir & append the path to the cmd
2366+
for _, test := range pods {
2367+
// create the path
2368+
kubeYaml = filepath.Join(podmanTest.TempDir, test[1])
2369+
2370+
// add the path to our podman kube play command
2371+
cmd = append(cmd, kubeYaml)
2372+
2373+
// generate a pod with a fake name
2374+
pod := getPod(withPodName(test[0]))
2375+
// write the yaml
2376+
err := generateKubeYaml("pod", pod, kubeYaml)
2377+
Expect(err).ToNot(HaveOccurred())
2378+
}
2379+
2380+
// run the podman command
2381+
kube := podmanTest.Podman(cmd)
2382+
kube.WaitWithDefaultTimeout()
2383+
Expect(kube).Should(ExitCleanly())
2384+
2385+
// for each pods, let's ensure it has been created nicely
2386+
for _, test := range pods {
2387+
inspect := podmanTest.Podman([]string{"pod", "inspect", test[0]})
2388+
inspect.WaitWithDefaultTimeout()
2389+
Expect(inspect).Should(ExitCleanly())
2390+
}
2391+
})
2392+
23532393
It("[play kube] fail with yaml of unsupported kind", func() {
23542394
err := writeYaml(unknownKindYaml, kubeYaml)
23552395
Expect(err).ToNot(HaveOccurred())

0 commit comments

Comments
 (0)