Need some explaination of kubectl stdin and pipe

1/4/2019

I'm daily user of kubectl, but not expert of linux. Recently I need edit some service type after deployment, so searched and used kubectl replace and it worked well.

cat yaml | kubectl replace -f -

service/tracs-pool-1sv replaced

But I don't understand why add a short dash - at the last. The doc only says:

Replace a pod based on the JSON passed into stdin.

I searched and found this SO question, and learned kubectl command may be that kind of command that does not read stdin(am I right?).

I tried

cat yaml |xargs kubectl replace -f

but error returned:

the path "apiVersion:" does not exist

So is the ending short dash(-) syntax built for kubectl ONLY? or is some more common syntax of linux bash stdin pipe? Can some one explain why xargs not work here and I must place a short dash(-) at the end?

-- Lei Yang
bash
kubectl
kubernetes
pipe
stdin

1 Answer

1/4/2019

This is a reasonably common, but not universal, Un*x convention. (It is mentioned in the POSIX specification and so most non-Linux Unices will support it as well.)

The important detail here is that the kubectl ... -f option expects a filename. If you have a file named x.yaml, a more direct way to write what you've shown is just

kubectl replace -f x.yaml

Where you say -f -, that ostensibly means "a file named -", but kubectl (along with many other tools) actually interprets this to mean "the process's standard input". For instance, you could use this for a very lightweight templating system, like

sed 's/TAG/1.2.3-20190103/g' x.yaml | kubectl replace -f -

For Un*x tooling in general, POSIX.1 states that, for many commands,

...an operand naming a file can be specified as '-', which means to use the standard input instead of a named file....

Some commands that support this include cat, grep, sort, and tar (not required by POSIX). One way to move a directory tree between two Linux machines, for instance, is to create a tar file on stdout, pipe that stream via ssh to a remote machine, and then unpack the tar file from stdin:

tar cf - . | ssh elsewhere tar xf - -C /other/dir

xargs is a tool that converts (most often) a list of filenames on standard input to command line arguments. For instance, find(1) can print a list of matching filenames to its stdout, so you could build a pipeline to delete shell backup files like

find . -name '*~' | xargs rm

You wouldn't usually use this with Kubernetes; your example tries to pass the YAML content itself as command-line arguments to kubectl, for example. You could apply kubectl across a directory tree with something like

find . name '*.yaml' | xargs -n1 kubectl apply -f

but since kubectl ... -f also supports directory names (not a universal convention) you could do the same thing more directly as

kubectl apply -f . # where . is the current directory
-- David Maze
Source: StackOverflow