Skip to content
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

storage: Install NFS packages on demand #8913

Merged
merged 8 commits into from
May 22, 2018

Conversation

mvollmer
Copy link
Member

@mvollmer mvollmer commented Mar 28, 2018

Testing

Currently, this just install the "nfs-utils" package. So to test, remove "nfs-utils" first.

Tasks

  • Make list of packages configurable
  • Say how many packages and how many bytes.
  • Test
  • Make sure everything is alright when PackageKit is not available
  • Implement mockups
  • Make sure this works as expected for non-root admins, and non-admins
  • Move "Checking installed software" into dialog

Demo for release notes: https://youtu.be/Gaioqm7sLEo

@mvollmer
Copy link
Member Author

Super quick demo: https://www.youtube.com/watch?v=gpoF3cU-Y0U

@mvollmer
Copy link
Member Author

@larskarlitski, fyi.

@andreasn andreasn self-requested a review March 28, 2018 13:23
@mvollmer
Copy link
Member Author

  • Say how many packages and how many bytes.

This probably need the DependsOn PackageKit method, which isn't supported for dnf, so yeah.

@martinpitt
Copy link
Member

Nice video! I like that it is fairly unintrusive. Curious that you already have an NFS mount even before you install necessary software :-)

@garrett
Copy link
Member

garrett commented Mar 28, 2018

That's a great demo!

I wonder if having it that easy to install software might make some sysdamins feel a bit leery. 😉

Copy link
Member

@martinpitt martinpitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initial review. Thanks!

* will take, but, PackageKit...
*/

export function check_optional_packages(names, progress_cb) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"optional" is IMHO a bit confusing - we are using this for use cases where said packages are actually required. How about check_missing_packages()?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, very good! I didn't like "optional" either.

*/

export function check_optional_packages(names, progress_cb) {
if (names.length == 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

===

{
Package: (info, package_id) => {
console.log("P", info, package_id);
var repos = package_id.split(";")[3].split(":");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use var in arrow functions (const or let), var has broken scoping.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. But is there something special with var in arrow functions? I think it's not anymore broken with arrow functions than with function functions.

console.log("P", info, package_id);
var repos = package_id.split(";")[3].split(":");
if (repos.indexOf("installed") == -1)
ids.push(package_id);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what your IRC ping from yesterday was about, right? Really weird that PK_FILTER_ENUM_NOT_INSTALLED does not work - what other purpose would that have? But this workaround looks simple enough.

return refresh().then(resolve).then(get_deps);
}

export function install_optional_packages(data, progress_cb) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function does almost nothing on top of InstallPackages, so it feels a bit like noise. But I have no strong opinion if you want to keep this wrapper. But then please call it install_packages() and give it its own documentation comment.


export function check_optional_packages(names, progress_cb) {
if (names.length == 0)
return cockpit.resolve(null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use/return standard Promise instead of cockpit.defer, as the rest of this library already does that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

},
}).then(() => {
return data;
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.then( () => data);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

if (ids.length == 0)
return null;
else
return { pkg_ids: ids };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you rely on the special null case in particular? It seems more consistent to always just return ids even if empty. install_packages() below in particular deals fine with this, and it also makes it easier to log/print/check the value without always having to consider the null special case.

Copy link
Member Author

@mvollmer mvollmer Mar 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the point, but with the current API, pkg_ids is internal and the client shouldn't access it. I had some public fields like package_count, but without a working DependsOn, it would be misleading to display "1 software package will be installed" when in fact we will install 20 (because of deps).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@mvollmer mvollmer force-pushed the storage-nfs-on-demand branch 5 times, most recently from aa19d15 to ecd6592 Compare April 4, 2018 12:19
@mvollmer mvollmer changed the title WIP - storage: Install NFS packages on demand storage: Install NFS packages on demand Apr 4, 2018
@garrett
Copy link
Member

garrett commented May 9, 2018

I've updated the mockup:

  1. removed alternate
  2. renumbered the list item as 0., so 1. can be the checking step
  3. added dep checking in-dialog

Here's the relevant excerpt:
packagekit-install-prompt-embedded-nfs-excerpt

Concerns:

  • Dialog will have to be resized to show deps.
  • Screen readers may read things incorrectly. (Related: reading deps would be super-wordy...)

Copy link
Member

@martinpitt martinpitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! I haven't tested it yet, just reviewed the code. I found a few issues.

[ Enum.FILTER_ARCH |
Enum.FILTER_NOT_SOURCE |
Enum.FILTER_NEWEST,
names
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation is off here (I wonder why eslint didn't spot that)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what eslint wants. It rejects the 'correct' indentation. Maybe it doesn't handle this style of multi-line arrays. I have put it all on one line, which fits now.

{
Package: (info, package_id) => {
var parts = package_id.split(";");
var repos = parts[3].split(":");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

varlet in arrow functions or nested scopes please; scoping of var doesn't go well with either. Same problem further down.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. What's wrong with varin arrow functions? It's as broken as with normal functions, but not more broken, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, let has a scope of the enclosing { } block, i. e. what you intuitively expect. var's scope is always the innermost enclosing function (not arrow-closure!), so that if you declare a var in an arrow function, for loop, if block etc., it will still exist outside of that block. I. e. var other than directly in a function is conceptually broken and error prone, and should be avoided.

See https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Statements/let

.then(() => {
for (var i = 0; i < names.length; i++) {
if (!installed_names[names[i]] && data.missing_names.indexOf(names[i]) == -1)
data.unavailable_names.push(names[i]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of accessing names[i] several times, how about names.forEach(name => { ... })?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

#cockpit_modal_dialog .modal-body {
max-height: calc(75vh - 10rem);
overflow: auto;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scrolling the entire dialog body seems a bit odd; don't we want to just scroll the package list?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, @garrett?

@@ -244,7 +244,8 @@ export function cancellableTransaction(method, arglist, progress_cb, signalHandl
// avoid calling progress_cb after ending the transaction, to avoid flickering cancel buttons
ErrorCode: (code, detail) => {
progress_cb = null;
reject({ detail, code: cancelled ? "cancelled" : code });
reject({ detail, code: cancelled ? "cancelled" : code,
toString: () => detail });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a nice trick, thanks!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's obsolete now with the use of TransactionError, right?

},
function () {
return PK.call("/org/freedesktop/PackageKit", "org.freedesktop.DBus.Properties",
"Get", [ "org.freedesktop.PackageKit", "VersionMajor" ]).then(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also a good candidate for moving into lib/packagekit. (Not a blocker for this PR)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has been moved.

if (data.extra_names.length > 0 && data.remove_names.length == 0) {
summary = (
<p>
{ format_to_array(_("$0 will be installed, along with additional packages:"), missing_names) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use ngettext. Depending on if it's one or more packages, translation will be different in non-English languages; e. g. in German, "a wird installiert" vs. "a, b werden installiert". This also occurs a few times below.

# The first simulated install seems to silently not report
# anything on the Debian test images, for unknown reasons. So
# we install a dummy package to warm up all parts of the
# machinery and distribute the fluids evenly.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a stupendously exact technical explanation what's going on 🤣

Could it perhaps be limited to if "debian" in m.image or "ubuntu" in m.image, to ensure we know when it happens elsewhere?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@garrett
Copy link
Member

garrett commented May 9, 2018

So I was thinking about this issue:

  • Dialog will have to be resized to show deps.

Having it jump with that information is not so nice.

Therefore, I figured out how to make it scale with a bit of CSS (and a tiny bit of JS to add a CSS class):

https://garrett.github.io/cockpit-mockweb/dialog-scale/

The idea is the scale would be triggered between step 1 and 2 (and not when clicking a button, like in the demo itself).

@garrett
Copy link
Member

garrett commented May 14, 2018

Oh! Oops; sorry. The modified version is actually in a different file:

PackageKit prompt (with embedded check) for NFS

@mvollmer mvollmer force-pushed the storage-nfs-on-demand branch 4 times, most recently from 6eb7bda to e766996 Compare May 17, 2018 08:28
@mvollmer
Copy link
Member Author

There is now a new cockpit-component-install-dialog that encapsulates all the work.

The OptionalPanel isn't meant to be reusable anymore but I left the API as it is, without putting back the old assumptions of how we store our feature flags in the client. That's a bit more verbose, but the feature detection business can use some cleanup and this could be the first step.

@mvollmer
Copy link
Member Author

@martinpitt, ptal!

Copy link
Member

@martinpitt martinpitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works nicely, and the code now looks by and large good to me, too. Would be nice to fix the (apparently) broken commit message and add a "Closes #" to make this button-mergeable. @andreasn, do you want to review the design, too? You have a pending review.

})
.catch(error => {
dfd.reject(error);
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: should suffice to write just .then(dfd.resolve) and .catch(dfd.reject); but it's fine like that. Does the caller firmly expect a cockpit promise? A standard Promise doesn't work?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not convinced that the two forms are equivalent, because of this. With a call like dfd.resolve(), the resolve method will have this == dfd. With just .then(dfd.resolve), this is likely undefined. This might not matter depending on whether the resolve method actually uses this or not. I think it doesn't, but that's just luck. Am I wrong?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, could be. We use that pattern a lot (git grep fail.*reject) and it seems to work, but indeed it could be that this is by luck.

@@ -0,0 +1,38 @@
.package-list-ct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commit log looks like you got a phone call when starting to type it? :-) ("lib/cockpit-components-install-dialog: New ").

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was on purpose and how we did it back in the days. :-) What else do you expect for a completely new module?

@andreasn
Copy link
Contributor

Do you have a newer video of this? Because I'm having trouble getting it to run :(

@martinpitt
Copy link
Member

@andreasn: The video in the description seems current, that's how it looks like for me.

Copy link
Contributor

@andreasn andreasn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to go!

@martinpitt martinpitt merged commit b529784 into cockpit-project:master May 22, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants