Merge ~ajorgens/cloud-init:user-shell into cloud-init:master
| Status: | Merged |
|---|---|
| Approved by: | Scott Moser on 2017-08-25 |
| Approved revision: | 5d1034b426e89f1d1799427ec8470d5f152badab |
| Merged at revision: | 20ca23cab0bdfdffa567f8fb4b49f3727bac6444 |
| Proposed branch: | ~ajorgens/cloud-init:user-shell |
| Merge into: | cloud-init:master |
| Diff against target: |
118 lines (+24/-9) 2 files modified
cloudinit/util.py (+12/-8) tests/unittests/test_util.py (+12/-1) |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Scott Moser | 2017-06-16 | Approve on 2017-08-25 | |
| Server Team CI bot | continuous-integration | Approve on 2017-08-25 | |
|
Review via email:
|
|||
Commit Message
Log a helpful message if a user script doesn't include a usable #!
A patch to allow scripts missing a #! to run by using shell=True was proposed but rejected. Instead we emit a log message to help the user understand what went wrong.
| Scott Moser (smoser) wrote : | # |
I kind of see this as something that should be fixed in another way.
a. The user gave cloud-init bad data.
b. cloud-init probably did a bad job of telling them that.
I don't think, though, that a + b =
c. cloud-init should make a guess as to what they meant to do.
It feels like you're trying to fix the scenario where a user typed something at
a prompt and it worked and they just want to put that in a script. However,
the prompt they most likely typed at was bash, and the fix you've proposed is
to execute that code with posix sh, which is *not* bash. So you've allowed the
ignorant user to remain in a state of bliss a little longer, but I'd really
rather somehow just error and educate them.
The suggested fix here ends up with cloud-init doing:
chmod 755 FILE
subp(["sh", FILE])
rather than just
chmod 755 FILE
subp(FILE)
(Note that I'm familiar with shell=True in subprocess.popen, the above I think just illustrates the difference better).
I believe that that makes the situation better for this user-data, which is valid posix shell:
echo hi mom
But ends up failing on this (bash specific) user-data:
x=( "hi" "mom")
echo "${x[@]}"
I don't know exactly why, but it seems to work correctly on this:
#!/usr/bin/env python
print("hi mom")
| Scott Moser (smoser) wrote : | # |
I'm going to say 'needs fixing'.
Because at very least this needs to warn somewhere that "hey, you really werent specific, and you really should be".
| Andrew Jorgensen (ajorgens) wrote : | # |
When a shell tries to execute a script that doesn't have a #! it simply tries to interpret it itself, rather than hand it off to something else (or re-exec itself, not actually sure which but the same end result).
Missing a #! could happen for a variety of reasons, but the only case where it will be seen in a user-provided script under cloud-init is when it's part of a MIME-Multipart (or the YAML equiv.) document with the right content type, so we know it's intended to be executed.
subprocess.popen does use /bin/sh explicitly . Different distros provide different shells as /bin/sh. Amazon Linux happens to use bash rather than some more minimal posix shell:
$ rpm -qf /bin/sh
bash-4.
SUSE and Red Hat both use bash also:
> rpm -qf /bin/sh
bash-4.
$ rpm -qf /bin/sh
bash-4.
Ubuntu uses Dash, but via alternatives:
(sidenote: There's an Ubuntu "sliminess" section on the Almquist Shell Wikipedia article? https:/
$ dpkg -S /bin/sh
diversion by dash from: /bin/sh
diversion by dash to: /bin/sh.distrib
dash: /bin/sh
So it seems Ubuntu is the odd man out here (FWIW I think Dash is probably the better choice for /bin/sh) which is why the concern didn't occur to me.
There's no reason I couldn't ship a binary executable as a user script, so I'm reluctant to peek at the file to decide if I need to warn the user that their file might get interpreted with /bin/sh, which might not be bash. I'd have to look far enough to tell that it's not in the set of executable types the running Linux kernel knows how to execute. I'm not even sure that's possible, since the kernel can be told how to run things like .NET assemblies and Windows executables.
The user in this case has expressed intent that it be executed and running it under a shell gives it the best possible chance of executing successfully.
Further thoughts on this?
| Andrew Jorgensen (ajorgens) wrote : | # |
> There's no reason I couldn't ship a binary executable as a user script
Hmm, need to test that assumption. You've got the content-type as text/x-shellscript.
| Andrew Jorgensen (ajorgens) wrote : | # |
> > There's no reason I couldn't ship a binary executable as a user script
> Hmm, need to test that assumption. You've got the content-type as text/x-shellscript.
Fails because write_file presumes files are text.
Traceback (most recent call last):
File "/usr/lib/
payload, frequency)
File "/usr/lib/
util.
File "/usr/lib/
content = encode_
File "/usr/lib/
return text.encode(
UnicodeEncodeError: 'utf-8' codec can't encode character '\udcf0' in position 24: surrogates not allowed
| Andrew Jorgensen (ajorgens) wrote : | # |
So I guess peeking at the first two bytes of the file might be okay to do, but I also think a better thing to do would be to make binaries work. :-/
| Scott Moser (smoser) wrote : | # |
I talked with Andrew some in irc.
We decided to not use 'shell=True', but rather to issue a WARNING more appropriately when failure is seen.
see discussion at https:/
| Scott Moser (smoser) wrote : | # |
I've marked this as work-in-progress.
When you have addressed the above, please set back to 'needs review'
PASSED: Continuous integration, rev:8f4a377e1b0
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
SUCCESS: CentOS 6 & 7: Build & Test
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
| Scott Moser (smoser) wrote : | # |
I'm not certain that i like the location of this log.... its awfully generic. some unit test suggestions inline.
Could we move it to be a more specific path that was used by the run parts ? If you did move up, you'd also want to verify that the exit_code of the ProcessExecutio
I'm interested in thoughts on moving to a more generic place.
If you're opposed to that, lets at least
a.) change it to WARN (very few places in cloud-init use ERROR) for better or worse, but consistency is good for now.
b.) maybe list the file in the error ?
LOG.warn("Exec format error executing %s" % (args if shell is True else args[0]))
also... we could even have ProcessExecutio
| Scott Moser (smoser) wrote : | # |
It seems right to me to just move this to ProcessExecutio
We could update 'reason' in the __init__ there to contain the error message if exit_code == self.empty_attr and errno is ENOEXEC
| Andrew Jorgensen (ajorgens) wrote : | # |
Looking at subp as it was, it looks like 'reason' holds the exception object. Did you mean that 'description' should contain the message?
| Andrew Jorgensen (ajorgens) wrote : | # |
Updated with the helpfulness pushed into the ProcessExecutio
It might also be reasonable to push something into util.runparts to suppress the unfriendliness of the exception, but I think this is sufficient and fairly simple as is.
PASSED: Continuous integration, rev:5d1034b426e
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
SUCCESS: MAAS Compatability Testing
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/


FAILED: Continuous integration, rev:74223d8aef2 d22ef1d43e85f1a 9881960f7cd5da /code.launchpad .net/~ajorgens/ cloud-init/ +git/cloud- init/+merge/ 325857/ +edit-commit- message
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
https:/ /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 7/ /jenkins. ubuntu. com/server/ job/cloud- init-ci/ nodes=metal- amd64/7 /jenkins. ubuntu. com/server/ job/cloud- init-ci/ nodes=metal- arm64/7 /jenkins. ubuntu. com/server/ job/cloud- init-ci/ nodes=metal- ppc64el/ 7 /jenkins. ubuntu. com/server/ job/cloud- init-ci/ nodes=metal- s390x/7 /jenkins. ubuntu. com/server/ job/cloud- init-ci/ nodes=vm- i386/7
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild: /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 7/rebuild
https:/