-
Notifications
You must be signed in to change notification settings - Fork 197
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
compose: Add --ex-lockfile and --ex-write-lockfile-to #1745
Conversation
Can one of the admins verify this patch?
|
Ok, so after some fixups, here is the current state. I generated a version lockfile a week ago.
So it's trying to install a version that's not available anymore in the repos. Which is a good sign here, showing that the lockfile is working :) |
☔ The latest upstream changes (presumably 6b2ac58) made this pull request unmergeable. Please resolve the merge conflicts. |
Rebased on top of master, fixed a memory leak, fixed a double unref and squashed commits. |
src/libpriv/rpmostree-core.c
Outdated
@@ -1954,7 +1956,7 @@ rpmostree_context_prepare (RpmOstreeContext *self, | |||
g_autoptr(GPtrArray) missing_pkgs = NULL; | |||
for (char **it = pkgnames; it && *it; it++) | |||
{ | |||
const char *pkgname = *it; | |||
const char *pkgname = g_hash_table_lookup (self->vlockmap, *it) ?: *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.
Heh, this part is surprisingly simple.
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.
Regarding the lockfile spec itself, I'm not sure if it's worth breaking it down per-repo. (In fact, we're not actually verifying this part when reading it in anyway, right?). In pipeline contexts and even pungi, it's not uncommon to have custom/temporary repos for the build. It also makes it harder when overriding lockfile items, which is something we'll need for FCOS.
Update PR with the suggestions. Dropping WIP. |
return NULL; | ||
} | ||
|
||
g_autoptr(GVariant) value = g_variant_lookup_value (jsonmetav, "packages", G_VARIANT_TYPE ("av")); |
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.
a(ss)
? That should simplify the loop below.
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 tried, it doesn't work. The parsed json is like this
Read: {'packages': <[<[<'acl-2.2.53-2.fc29.x86_64'>, <'sha256:0a7446de7de962fbbfdb204da0a7aef70cc8bd4ee4b2c03b0d730bf62b9a2223'>]>, <[<'atomic-registries-1.22.1-27.gitb507039.fc29.x86_64'>, <'sha256:19fdd5bcb79bc84c4c3d17d63d2ee39173d3d2504ea11ed9f0b7d0d0cf86550c'>]>, <[<'audit-libs-3.0-0.7.20190326git03e7489.fc29.x86_64'>, <'sha256:a907544ffac01209ce49026a0ebedef58d62639aa9d234537577c72d458cc850'>]>, <[<'basesystem-11-6.fc29.noarch'>, <'sha256:e4b86fe4f119873e8705dae47163f4afb6ccf8885426e04d960ff18eed7320bb'>]>, <[<'bash-4.4.23-6.fc29.x86_64'>, <'sha256:1899be2cb4a4793d6a6c62285a6b77f5c22eb5534e76d94d2a5d421cf2aa59ad'>]>,
as you can see, everything is wrapped inside a GVariant. Unless I'm missing something.
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.
Ahh hmm, yeah I see what you mean. Seems like that's just how json_gvariant_deserialize
works. Which makes sense, since it can't know that the JSON is following some particular scheme, so it just wraps all leaf nodes in the JSON into a GVariant. Yuck. So, I do think that parsing with serde would make this much nicer and give us scheme checking as well, but happy to stick with this for now!
{ | ||
/* The manifest might specify not only package names (foo-1.x) but also | ||
* something-that-foo-provides */ | ||
g_autoptr(GPtrArray) alts = rpmostree_get_matching_packages (sack, name); |
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.
Hmm, I know I suggested this, but I'm not completely satisfied either with how "indirect" it all seems. One root issue here of course is #731; libsolv simply doesn't make it easy to provide resolution information. (It's not entirely its fault either; depsolving is a hard problem that isn't exactly disposed to provide clear cut answers to these sorts of questions).
Anyway, good to roll with this for now! Though it might need more thinking and tweaks in the future.
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 couldn't help thinking about this a bit more :)
ISTM like in the lockfile case, maybe we do want to require manifests to not use "provides" but instead purely pkgnames. So e.g. when using --lockfile
or --write-lockfile-to
, we would only search by pkgname rather than the default dnf_context_install()
fuzzy logic and error out if the pkgname isn't found. That would really simplify the semantics of the lockfile.
But yeah, I'm happy getting this in as is for now, and then tweak it in follow-ups!
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 tiny downside of course is that it requires slightly more churn on treefile writers to react to things like Obsoletes
, package splits, etc... Doesn't seem like an unreasonable burden though.
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.
Follow-up to this in #1849.
Updated with requested changes. |
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.
Awesome, this looks good to me now as a first cut! Final tidbit, can you squash the two commits together and change the commit message title to something like: compose: Add --lockfile and --write-lockfile-to
?
return NULL; | ||
} | ||
|
||
g_autoptr(GVariant) value = g_variant_lookup_value (jsonmetav, "packages", G_VARIANT_TYPE ("av")); |
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.
Ahh hmm, yeah I see what you mean. Seems like that's just how json_gvariant_deserialize
works. Which makes sense, since it can't know that the JSON is following some particular scheme, so it just wraps all leaf nodes in the JSON into a GVariant. Yuck. So, I do think that parsing with serde would make this much nicer and give us scheme checking as well, but happy to stick with this for now!
Heh, we're giving him conflicting advice. I feel like what we really need to do is get over the hump and try to have Rust driving things and that would naturally lead to things like this. Possibly even to the point of having the CLI entrypoint be Rust. |
Yeah, all I mean is that rather than reserializing and deserializing to pass to the C side as the first approach did, we could just pass e.g. two
Hehe, I was actually toying with this when I started hacking on |
bot, add author to whitelist |
Awesome, thanks so much for working on this and putting up with all the reviews! 🎉 (BTW, just realized this patch was never tested in CI. I've enabled it now, so if it bounces it should be easier to iterate until we go green.) |
Fixes #1670 This patch introduces a new `compose tree --write-lockfile-to=manifest.lock` argument and a new `compose tree --lockfile=manifest.lock` to read it back for subsequent invocations. Signed-off-by: Rafael Fonseca <[email protected]> Closes: #1745 Approved by: jlebon
Also just noticed I messed up during my recommendation for the commit message and gave you the switch names without the |
💔 Test failed - status-atomicjenkins |
Hmm, looks like
|
for (char **it = pkgnames; it && *it; it++) | ||
{ | ||
const char *pkgname = *it; | ||
g_autoptr(GError) local_error = NULL; | ||
g_autoptr(DnfPackage) pkg = rpmostree_get_locked_package (sack, self->vlockmap, pkgname); |
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.
Either should conditionalize this bit on self->vlockmap != NULL
here or you can also check it in rpmostree_get_locked_package
.
Fixes coreos#1670 This patch introduces a new `compose tree --ex-write-lockfile-to=manifest.lock` argument and a new `compose tree --ex-lockfile=manifest.lock` to read it back for subsequent invocations. Signed-off-by: Rafael Fonseca <[email protected]>
☀️ Test successful - status-atomicjenkins |
Awesome! So an interesting next step here I think would be to change the FCOS pipeline to make use of this? |
right - we're setting up mechanical refs to print out lockfiles and then we'll create new lockfiles (full control of content) to use as input for our dev/prod refs |
nice work @r4f4 |
One problem with how we use lockfiles right now is that we don't enforce them for dependencies. That is, if `foo` requires `bar`, but only `foo` is in the manifest, then while `foo` will be locked`, `bar` will never be checked against the lockfile because it was never explicitly requested. Higher-level though, I don't like how indirect the locking here feels. See some comments about that in: coreos#1745 (comment) coreos#1745 (comment) Essentially, the manifest is an input file of patterns, and all we really know from the lockfile output is that the set of packages in there satisfies this input in some way. But: 1. there are multiple ways to satisfy the same input (hence why hints like `SOLVER_FAVOR` exist) 2. the solution is dependent on how the solver is implemented (i.e. different libsolv versions might yield different solutions) 3. the solution is dependent on flags fed to the solver (i.e. different libdnf versions might yield different solutions) So any attempt at cross-checking between the input file and the lockfile is going to be very hard. Using a stricter mode as I suggested in coreos#1745 of only allowing pure pkgnames or NEVRAs would help, but it wouldn't address the dependency issue. (Though I'm still thinking about possibly doing this anyway.) The solution I propose here is instead to take the nuclear approach: we completely exclude from the sack all packages of the same name as packages in our lockfiles, but which do not match the NEVRA. Therefore, any possible solution has to also satisfy our lockfile (or error out).
One problem with how we use lockfiles right now is that we don't enforce them for dependencies. That is, if `foo` requires `bar`, but only `foo` is in the manifest, then while `foo` will be locked, `bar` will never be checked against the lockfile because it was never explicitly requested. Higher-level though, I don't like how indirect the locking here feels. See some comments about that in: coreos#1745 (comment) coreos#1745 (comment) Essentially, the manifest is an input file of patterns, and all we really know from the lockfile output is that the set of packages in there satisfies this input in some way. But: 1. there are multiple ways to satisfy the same input (hence why hints like `SOLVER_FAVOR` exist) 2. the solution is dependent on how the solver is implemented (i.e. different libsolv versions might yield different solutions) 3. the solution is dependent on flags fed to the solver (i.e. different libdnf versions might yield different solutions) So any attempt at cross-checking between the input file and the lockfile is going to be very hard. Using a stricter mode as I suggested in coreos#1745 of only allowing pure pkgnames or NEVRAs would help, but it wouldn't address the dependency issue. (Though I'm still thinking about possibly doing this anyway.) The solution I propose here is instead to take the nuclear approach: we completely exclude from the sack all packages of the same name as packages in our lockfiles, but which do not match the NEVRA. Therefore, any possible solution has to also satisfy our lockfile (or error out).
One problem with how we use lockfiles right now is that we don't enforce them for dependencies. That is, if `foo` requires `bar`, but only `foo` is in the manifest, then while `foo` will be locked, `bar` will never be checked against the lockfile because it was never explicitly requested. Higher-level though, I don't like how indirect the locking here feels. See some comments about that in: #1745 (comment) #1745 (comment) Essentially, the manifest is an input file of patterns, and all we really know from the lockfile output is that the set of packages in there satisfies this input in some way. But: 1. there are multiple ways to satisfy the same input (hence why hints like `SOLVER_FAVOR` exist) 2. the solution is dependent on how the solver is implemented (i.e. different libsolv versions might yield different solutions) 3. the solution is dependent on flags fed to the solver (i.e. different libdnf versions might yield different solutions) So any attempt at cross-checking between the input file and the lockfile is going to be very hard. Using a stricter mode as I suggested in #1745 of only allowing pure pkgnames or NEVRAs would help, but it wouldn't address the dependency issue. (Though I'm still thinking about possibly doing this anyway.) The solution I propose here is instead to take the nuclear approach: we completely exclude from the sack all packages of the same name as packages in our lockfiles, but which do not match the NEVRA. Therefore, any possible solution has to also satisfy our lockfile (or error out). Closes: #1849 Approved by: cgwalters
One problem with how we use lockfiles right now is that we don't enforce them for dependencies. That is, if `foo` requires `bar`, but only `foo` is in the manifest, then while `foo` will be locked, `bar` will never be checked against the lockfile because it was never explicitly requested. Higher-level though, I don't like how indirect the locking here feels. See some comments about that in: #1745 (comment) #1745 (comment) Essentially, the manifest is an input file of patterns, and all we really know from the lockfile output is that the set of packages in there satisfies this input in some way. But: 1. there are multiple ways to satisfy the same input (hence why hints like `SOLVER_FAVOR` exist) 2. the solution is dependent on how the solver is implemented (i.e. different libsolv versions might yield different solutions) 3. the solution is dependent on flags fed to the solver (i.e. different libdnf versions might yield different solutions) So any attempt at cross-checking between the input file and the lockfile is going to be very hard. Using a stricter mode as I suggested in #1745 of only allowing pure pkgnames or NEVRAs would help, but it wouldn't address the dependency issue. (Though I'm still thinking about possibly doing this anyway.) The solution I propose here is instead to take the nuclear approach: we completely exclude from the sack all packages of the same name as packages in our lockfiles, but which do not match the NEVRA. Therefore, any possible solution has to also satisfy our lockfile (or error out). Closes: #1849 Approved by: cgwalters
One problem with how we use lockfiles right now is that we don't enforce them for dependencies. That is, if `foo` requires `bar`, but only `foo` is in the manifest, then while `foo` will be locked, `bar` will never be checked against the lockfile because it was never explicitly requested. Higher-level though, I don't like how indirect the locking here feels. See some comments about that in: #1745 (comment) #1745 (comment) Essentially, the manifest is an input file of patterns, and all we really know from the lockfile output is that the set of packages in there satisfies this input in some way. But: 1. there are multiple ways to satisfy the same input (hence why hints like `SOLVER_FAVOR` exist) 2. the solution is dependent on how the solver is implemented (i.e. different libsolv versions might yield different solutions) 3. the solution is dependent on flags fed to the solver (i.e. different libdnf versions might yield different solutions) So any attempt at cross-checking between the input file and the lockfile is going to be very hard. Using a stricter mode as I suggested in #1745 of only allowing pure pkgnames or NEVRAs would help, but it wouldn't address the dependency issue. (Though I'm still thinking about possibly doing this anyway.) The solution I propose here is instead to take the nuclear approach: we completely exclude from the sack all packages of the same name as packages in our lockfiles, but which do not match the NEVRA. Therefore, any possible solution has to also satisfy our lockfile (or error out). Closes: #1849 Approved by: cgwalters
This is a skeleton of the solution for issue #1670.
The writing the lockfile part works but reading hasn't been tested yet.
I'm sending this PR so that we can start a discussion on my approach and I'll change things accordingly.
What's known to be missing: