I just did a PR of my version of the Golang action implementation. It does
some "breaking" changes and there is some discussion on the slack channel.
So I report the current situation n here, looking for advices and change
recommendations. Since I am a bit confused, if I remember well, one Apache
rule is the mailing list is the ultimate source for the truth...
It currently works this way (I call it the "pipe-loop" protocol)
A golang action (or a generic binary) is expected to follow this "protocol":
* starts with {"openwhisk": 1}
* reads on line in stardard input, expecting a json ON A SINGLE LINE
* process the line, emits logs in stderr (can be multiple lines)
* outputs a line in stdout in json format ON A SINGLE LINE
* repeat forever
It is important to note this design is easy to implement and works even for
bash scripts, but it is easy to use also perl, ruby, haskell in an EFFICIENT
way. Indeed this bash script (with jq) is part of my tests:
---
#!/bin/bash
echo '{"openwhisk":1}'
while read line
do
name="$(echo $line | jq -r .name)"
logger -s "name=$name"
hello="Hello, $name"
logger -s "sent response"
echo '{"hello":"'$hello'"}'
done
---
Things discussed:
1) remove the header {"openwhisk":1}
Actually initially it was not there. But I decided to add this requirements
because the action need to speak a protocol ANYWAY.
Most important, I explain why I require it starts with "{"openwhisk: 1}".
The main reason is: I start the child process at init time, and I wanted to
detect when it does not behave properly.
The simplest problem happens when the action crashes immediately. For example,
a common reason for this problem is uploading a binary using some dynamic
libraries not available in the runtime. For example a swift action. By
defaults it load a lot of different libraries, it crashes immediately but I
cannot detect it until I try to read its stdin.
I can remove this requirement if someone can show me the go code to check that
cmd.Start("true") or cmd.Start("pwd") exited 😃
If it is not doable, and I skip the handshake, even if the command crashed, I
will not detect the problem until a /run is executed and the action times out...
Carlos say it is fine. It is ok for me but I still think an early problem
detection would be better. Also James recommended me to provide as much as
error detection to the user as early as possible. Kinda of conflicting
directives here...
Suggestions?
2) more checks at init time
I added some sanity checks. Probably too many. I tried to detect the error at
deployment time, not at invocation time.
This is different from what currently for example dockerskeleton does.
If I upload for example something wrong, like a non-zip, a non-elf executable,
my init returns {"error": "description"}, while currently the dockerskeleton
returns always OK.
Recommendations here?
3) output to another channel the result
Currently I require logs goes to stderr, and stdout is for interacting with the
parent process.
Rodric suggested to output to a separate channel (channel 3?) and use stdout
and stderr for logs.
While doable, I need to provision another pipe, and the implementation should
probably do some syscalls to retrieve file descriptor 3. It would complicate
implementation, while currently it is straightforward for any language that
does not have a library available. For swift, even to flush stdout I needed to
write "linux specific" code... I do not dare to think what I need to do to
write in fd3...
My opinion is that using stdout for I/O and stderr for logs is a better choice
than opening another file descriptor.
Thoughts here?
--
Michele Sciabarra
[email protected]