-
Notifications
You must be signed in to change notification settings - Fork 36
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
New Command: Mixer bundle edit #155
Conversation
helpers/helpers.go
Outdated
@@ -294,6 +294,7 @@ func RunCommand(cmdname string, args ...string) error { | |||
cmd := exec.Command(cmdname, args...) | |||
cmd.Stdout = os.Stdout | |||
cmd.Stderr = os.Stderr | |||
cmd.Stdin = os.Stdin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is added to facilitate launching applications that read input (i.e., the editors). I considered adding the same thing to the RunCommandOutput
function below, but didn't know how people would want it. Perhaps a separate RunCommandInput
function that takes a bytes.Buffer
as input? We can add that if we have a need.
I made sure the helpers_tests
still ran as expected after adding this line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine until you have more than one go routine calling RunCommand, at which point you have a fight over which program gets the terminal input.
I think you should create a new routine RunCommandInput that takes an io.Reader and has 98% of the body of this function, then make RunCommand a one liner, to call RunCommandInput with nil for this parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea. I'll redo this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would find this pretty annoying to use, I think you should allow users to choose their own editor rather than being given a list. I think if something fails to parse you should offer immediately to allow the user to fix the error rather than tell them to re-edit. I think it is worth suggesting that you keep a copy of the file so the user can abandon the edit. Imaging the user deletes the entire contents of the file, telling him to fix it is not very helpful.
builder/builder.go
Outdated
if editor != "" { | ||
b.BundleEditor = editor | ||
} | ||
editorCmd, exists := validEditors[b.BundleEditor] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is very misguided. For example xemacs and gvim are not in the list, which we do ship. Trust your users!
In the past for automation I have written scripts to edit files.
The thing I would expect is
- If I pass an editor to use, then use that.
- (optional) if the environment variable BUNDLE_EDITOR is set, use the value of that
- If the environment variable VISUAL is set, use the value of that.
- If the environment variable EDITOR is set, use the value of that.
- Use 'vi' (possibly nano as it is in sysadmin-basic)
Personally I would be inclined to not have an option to pass in the editor via a flag at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually exactly the original way I had this implemented -- if the user sets (or passes in) the path to the editor, use that, else try to look up $VISUAL, then $EDITOR, then fall back to nano.
The reason I went away from this is that, while this flexibility is awesome, basic security policy says to explicitly not "trust your users!". Whatever you configure here is going to just be passed off to an exec
, effectively. Unlike git
, which lets you configure whatever editor you want for merge conflicts, mixer
is run via sudo
. Yes, these commands require no elevated privileges, but when so many of the others do, users will absolutely run them with sudo
either out of a) laziness, b) misunderstanding, or c) because the files they're editing are owned by root because of some other operation.
As far as the list not including xemacs or gvim, I just pulled the list from the packages included in editors
bundle -- if those packages include other variants I left out, I'm happy to add them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be clear (as I hope is evidenced by the fact that I originally implemented it the way you suggested), I also don't love the idea of limiting this to a small list of editors. I am absolutely open to ways to open this up that don't introduce mixer executing random binaries as root. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do understand your concern, but I think you are fixing the problem in the wrong place. We have other of those customizations of what program to call (BuildScript, --packager), that I think is unfeasible to whitelist all of them.
Also, we really don't want the user to create those files with root permissions (if he's keeping track of the repository in git for example).
I think the real way to deal with this is to contain where we really need sudo, and possibly call sudo ourselves when we really need (which also might be controversial, but I think is a fight on the right battle).
For this functionality (which I think is very good btw!), I'd say either:
- Just do what you did before or Icarus is suggesting
- Avoid editing for now and keep in some form the "copy this manifest from upstream" functionality
- Figure out a way to safely "drop privileges" by setuid yourself back to the original user when running this command (kind of the reverse of sudo). I don't think Go handles that very well yet, though. (See their issue 1435).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I would be inclined to not have an option to pass in the editor via a flag at all.
I agree with that one, we can defer the flag until a real need surfaces. The EDITOR and [name]_EDITOR are common enough in the command line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is that even if you restrict the user to only running 'vi' you can fire up vi. You then type ":sh" and enter, and you have a shell running as root! At that point you can run anything you like as root.
sudo is the wrong tool for increasing privileged status. We ought to see exactly what wants 'root', and see if we can eliminate the need as a medium term goal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have other of those customizations of what program to call (BuildScript, --packager), that I think is unfeasible to whitelist all of them
BuildScript
is hard-coded (and not read in from the config), and for --packager
, you really are running exec
on whatever the user passes in? I didn't review that part of the new BCB, but I would have definitely flagged that if I had.
Also, we really don't want the user to create those files with root permissions (if he's keeping track of the repository in git for example).
Agreed.
I think the real way to deal with this is to contain where we really need sudo
👍
Figure out a way to safely "drop privileges" by setuid yourself back to the original user when running this command (kind of the reverse of sudo). I don't think Go handles that very well yet, though. (See their issue 1435).
I've done this in other languages before. I'll look into if this is feasible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We ought to see exactly what wants 'root', and see if we can eliminate the need as a medium term goal.
@icarus-sparry created #158 with some more data on this. Want to tackle that problem? :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BuildScript is hard-coded (and not read in from the config),
That was surprising. I guess it was made that way to be configurable at some point. That should be a constant.
and for --packager, you really are running exec on whatever the user passes in? I didn't review that part of the new BCB, but I would have definitely flagged that if I had.
Yep, to let the user pick some other dnf (either the system or a compiled one for testing). In that case, we could've retrofit it to be a flag that just says "use dnf" and use whatever is in PATH.
I guess the two examples were not that strong but I stand by the other arguments I gave :-)
helpers/helpers.go
Outdated
@@ -294,6 +294,7 @@ func RunCommand(cmdname string, args ...string) error { | |||
cmd := exec.Command(cmdname, args...) | |||
cmd.Stdout = os.Stdout | |||
cmd.Stderr = os.Stderr | |||
cmd.Stdin = os.Stdin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine until you have more than one go routine calling RunCommand, at which point you have a fight over which program gets the terminal input.
I think you should create a new routine RunCommandInput that takes an io.Reader and has 98% of the body of this function, then make RunCommand a one liner, to call RunCommandInput with nil for this parameter.
builder/builder.go
Outdated
} | ||
|
||
if add { | ||
if err := b.AddBundles(bundles, false, false, false); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can just return b.AddBundles(.......)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, thanks.
builder/builder.go
Outdated
path = localPath | ||
} | ||
|
||
if !noEdit { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe
if noEdit { continue }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this, because it removes the double-negative (and some indentation).
builder/builder.go
Outdated
"vim": "/usr/bin/vim", | ||
} | ||
|
||
//EditBundles prints out a bundle list in either a flat list or tree view |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this comment correct?
Some documentation here on the expected use of noEdit==false, and add==true/false would probably help, or just a pointer to the viper help stuff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, whoa, nope. Copypasta error.
builder/builder.go
Outdated
} | ||
|
||
if _, err := parseBundleFile(path); err != nil { | ||
return errors.Wrapf(err, "Failed to parse bundle %s. Please edit again", bundle) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this offer something more like a choice Revert/Reedit/Continue (with Revert undoing any edits, Reedit looping to reinvoke the editor and Continue to move to the next bundle) rather than erroring out here?
mixer/cmd/bundles.go
Outdated
closes, the bundle file is then parsed for validity. | ||
|
||
The editor is configured in builder.conf via the BUNDLE_EDITOR field, or by flag | ||
override. Valid options include: nano, vim, emacs, joe, and ctags. The default |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ctags is a valid editor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha, is it not? Basic ignorance issue here. As mentioned above, I pulled the list from what packages are installed by editors
, but I obviously should have done a basic google. Ctags is a package used by one of the actual editors?
I'll pull it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ctags produces a 'tags' database so you can find definitions and uses of variables in C projects.
builder/builder.go
Outdated
|
||
if !noEdit { | ||
if err := helpers.RunCommand(editorCmd, path); err != nil { | ||
return errors.Wrapf(err, "Failed to edit bundle: %s", bundle) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is high risk. Some editors return a non-zero exit code if some commands fail, which might be as simple as a search failing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a great point. In theory, I don't really care what the editor returned, I just care if I can read/parse it afterward. I should probably just ignore the error here, and let the parsing trigger the error.
mixer/cmd/bundles.go
Outdated
|
||
Passing '--no-edit' will suppress launching the editor, and will thus only copy | ||
the bundle file to local-bundles if it is only found upstream. This can be | ||
useful if you want to add a bundle to local-bundles, but wish to edit it at a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a real use case? Would it be better as a different command?
Once you allow someone to choose their own editor, they can always use /bin/true to get the same effect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, this is something people asked for. I don't remember exactly who or when, but when I proposed this feature back in December, this is the feedback I got -- that people would want to be able to move the file (unchanged) to local-bundles, and edit it at their leisure.
Also, while yes -- if you could pick any editor you want, you could use /bin/true
-- expecting users to know or do that is the opposite of user-friendly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that is an important part of the feature, because that is a boring computer task that mixer can do for us: hey just get me the desktop and desktop-apps from the upstream. I've almost suggested something like mixer bundle copy desktop desktop-apps
(or copy-upstream, or import, or whatever) as a standalone thing (so you don't have to deal with the "edit but no edit").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cmarcelo I had considered at one point making that a stand-alone mixer bundle import
command, or even a flag off add
(mixer bundle add --local <bundle>
).
The reason I don't want that is that, while people might very well find it useful to pull things into local-bundles
and edit at some later time, I want them to still be aware that doing this overrides the upstream version. If you're not actually planning to edit the bundle, having a copy in local-bundles
is almost certainly just going to cause confusion later (either for you, or for the tool when it tries to migrate to new upstream version). So keeping it a part of the mixer bundle edit
command reminds people why they're moving things into local-bundles
: to edit them.
On that note, one (potentially valid, but dangerous) use-case for copying into local and not editing is exactly that -- persisting a bundle past a deletion in a future upstream version. What I mean is, if you add bundle_a to local-bundles
, and in a future version of upstream bundle_a gets removed entirely, it now becomes equivalent to a "user-created" bundle -- it is no longer masking anything upstream. That very well might be a desirable thing for someone, but for most people that will likely just cause confusion.
mixer/cmd/bundles.go
Outdated
later time. | ||
|
||
Passing '--add' will also add the bundle(s) to your mix. Please note that | ||
bundles are added after all bunles are edited, and thus will not be added if any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spelling nitpick. "all bunles are"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Requested change: no list of valid editors.
mixer/cmd/bundles.go
Outdated
override. Valid options include: nano, vim, emacs, joe, and ctags. The default | ||
value is nano. | ||
|
||
Passing '--no-edit' will suppress launching the editor, and will thus only copy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mixer bundle edit --no-edit
sounds strange, maybe mixer bundle edit --copy-only
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hahah, I had that same thought last night. I couldn't come up with a better name (it was late and I was hungry), but that works pretty well.
Besides the nits and the list of valid editors, this looks good to me. |
210d587
to
c1ed9f9
Compare
Pushed a new version that addresses some feedback (and rebased on top of #137). Still need to swap out the validEditors and, ideally, address @icarus-sparry's usability complaints (which I agree with) |
c1ed9f9
to
4c7b1d5
Compare
I just pushed an update that I believe addresses @icarus-sparry and @cmarcelo's issues. The editor is now read from If parsing fails, the user is told why and prompted to choose between re-editing as-is, reverting and editing fresh, or skipping. If they skip, both the edited and a backed-up |
FYI - If people think this is bad, I would suggest a future patch (not part of this) that either a) skips files with ".orig" at the end, or b) does the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much better thank you. I think we need a clearer prompt message.
if err = os.Remove(backup); err != nil { | ||
return errors.Wrapf(err, "Error cleaning up backup for bundle '%s'", bundle) | ||
} | ||
break editLoop |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since all that 'break editloop' does is 'return nil', in this case you could just return nil. However leave it as it is, as the structure is nice.
builder/builder.go
Outdated
text = strings.ToLower(text) | ||
text = strings.TrimSpace(text) | ||
switch { | ||
case text == "c" || text == "continue": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I suggested reedit/continue/restore I was rather thinking of editing more than one bundle.
So I would expect 'continue' to leave the current bundle and go on to the next, 'reedit' to rerun the editor on the current file, and reset to run the editor on a copy of the original file.
So your 'continue' is my 'reedit', your 'skip' is my 'continue' and your 'revert' is my 'restore'.
Let me first say there is nothing wrong with your names, but then say "More explanation is probably needed in the prompt text' so it is clear what 'continue' means.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
12795c2
to
f624273
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
9e8322b
to
ed17398
Compare
Pushed a minor update that adds a @cmarcelo I think you were requesting the same change as Icarus; can you please check if this meets your needs too? |
ed17398
to
28833bf
Compare
Adds new command, 'mixer bundle edit', that allows a user to edit local and upstream bundle definition files. This command will locate the bundle (looking first in local-bundles, then in upstream-bundles), and launch an editor to edit it. If the bundle is only found upstream, the bundle file will first be copied to your local-bundles directory for editing. When the editor closes, the bundle file is then parsed for validity. The editor is configured via environment variables. VISUAL takes precedence to EDITOR. If neither are set, the tool defaults to nano. If nano is not installed, the tool will skip editing, and act as if '--copy-only' had been passed. Passing '--copy-only' will suppress launching the editor, and will thus only copy the bundle file to local-bundles if it is only found upstream. This can be useful if you want to add a bundle to local-bundles, but wish to edit it at a later time. Passing '--add' will also add the bundle(s) to your mix. Please note that bundles are added after all bunles are edited, and thus will not be added if any errors are encountered earlier on. Signed-off-by: Kevin C. Wells <kevin.c.wells@intel.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks OK but let's incorporate the new bundle functionality ;-)
builder/builder.go
Outdated
} | ||
text = strings.ToLower(text) | ||
text = strings.TrimSpace(text) | ||
switch { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can switch on strings in Go, so switch text {
then use case "e", "edit":
and so on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh. I knew you could switch on strings, but I'd never seen the case "e", "edit":
syntax before. I definitely prefer that.
Add 'create new bundle' functionality to 'mixer bundle edit'. Now, if a bundle is not found in local-bundles or upstream-bundles, it is treated as a new bundle, and a blank bundle definition file template is created for the user to edit. Also minor syntax optimization on editing switch.
b20361a
to
4157b06
Compare
Pushed change @cmarcelo suggested. It's pushed as a new commit, so people can review just that one, without worrying about reviewing the entire |
LGTM. |
Adds new command, 'mixer bundle edit', that allows a user to edit local
and upstream bundle definition files. This command will locate the
bundle (looking first in local-bundles, then in upstream-bundles), and launch
an editor to edit it. If the bundle is only found upstream, the bundle file will
first be copied to your local-bundles directory for editing. If the bundle is
not found anywhere, a blank template is created with the correct name. When the
editor closes, the bundle file is then parsed for validity.
The editor is configured via environment variables. VISUAL takes precedence to
EDITOR. If neither are set, the tool defaults to nano. If nano is not installed,
the tool will skip editing, and act as if '--suppress-editor' had been passed.
Passing '--suppress-editor' will suppress launching the editor, and will thus
only copy the bundle file to local-bundles (if it is only found upstream), or
create the blank template (if it was not found anywhere). This can be useful if
you want to add a bundle to local-bundles, but wish to edit it at a later time.
Passing '--add' will also add the bundle(s) to your mix. Please note that
bundles are added after all bunles are edited, and thus will not be added if any
errors are encountered earlier on.
Signed-off-by: Kevin C. Wells kevin.c.wells@intel.com