The Puppet Labs Issue Tracker has Moved: https://tickets.puppetlabs.com
https://tickets.puppetlabs.com. See the following page for information on filing tickets with JIRA:
Bootstrapping Puppet in Ruby code has nasty scope cycles...
|Assignee:||Chris Price||% Done:|
|Affected Puppet version:||Branch:||https://github.com/puppetlabs/puppet/pull/571|
Ticket tracking is now hosted in JIRA: https://tickets.puppetlabs.com
So, in debugging an issue using code during early startup of Puppet, I found out that we have some very strange run-scope dependencies in bootstrapping.
Specifically, we depend on runtime code in
Puppet::Application executing before we can safely execute load time code in
This manifests as strange defaults in the configuration, because the run mode is configured in P::A during that run-scope code, but the defaults load in puppet earlier than that.
This also makes it difficult to just
require 'puppet' and have things actually work. Coupled with a desire to be able to change run mode on the fly, and a longer term need for things that Faces abstract over to act as if in a run mode without the current hard-coded dependency, we kind of need to get this resolved.
#2 Updated by Anonymous almost 5 years ago
Can you be a bit more specific about what’s happening that’s a problem?
I can, indeed.
What’s the code in CommandLine and Application that has to run before core is loaded? Is this a fundamental flaw, or a happenstance of implementation?
This is happenstance; I can’t see any compelling reason we must behave the way we currently do, although my gut feeling is that it will be complicated to get to different behaviour. (Also, perhaps a little of this is always unavoidable; something, somewhere will have to bootstrap at some time.)
Further, I think that a lot of this complexity accreted over time. It probably started quite reasonably, years back, and as we added more capabilities, merged to the single binary, and modified our bootstrap process we ended up in this position. It has all the hallmarks of code flow that should have been cleaned up along the way and, for various good reasons like time pressure, just wasn’t.
I can’t give you an assurance that I have a perfect understanding of all the code. Parts of this are a black box, where I understood just enough to know what behaviour was occurring at a very high level, without understanding the ramifications fully. I do know enough to explain the obvious tip of the potential iceberg here. (…or, it could be this shallow. I /think/ it is, but I can’t be sure without a lot more time.)
So, the core problem is that when you
require 'puppet' you
implicitly run the code in
puppet/defaults.rb in the context of
the top level
Puppet module. That, in turn, has dependencies on the
run_mode and/or does setup for the defaults based on the application
that is running.
Puppet::Util::CommandLine we actually load the
application in the
execute instance method. Loading the
application file causes the desired
run_mode to be declared – this
is done at the time the file is loaded. The next thing that the
execute method does is create an instance of the application class.
Puppet::Application initialize method a small set of things
happen: we require the command line file, store that away, then call
set_run_mode with the
run_mode we stashed away earlier, at load
time. (Here, of course, we are in execution time.)
Then, finally, that method will call
require "puppet" after doing
all the rest of the setup. This, in our bootstrap process, is when we
load the top level class, and so evaluate the defaults: triggered at
“load” time, but with that loading deliberately delayed until a bunch
of “execution” time code has run to configure global state.
I introduced the problem by requiring puppet at the time the command line stuff was loaded, long before the code was executed and the objects instantiated that actually configured the global state of the code. This led to invalid values for various settings, breaking startup in limited ways that showed up in our acceptance tests.
To fix this, we should make sure that it is possible to just execute
require "puppet" as the very first line of whatever loads the puppet
code; that, in turn, implies that any library we write can just
require the top level puppet module if they depend on it.
To get there, I can identify that we need to at least disambiguate the
way we handle building the defaults, and the way they interact with
run_mode. On the plus side, this also means that it should be
possible to change
run_mode at runtime without any risk of things
going wrong. :)
#13 Updated by Anonymous almost 4 years ago
For the watchers of this ticket, as part of the work in 7749, the behavior of Telly is that ~/.puppet.conf is merged with /etc/puppet/puppet.conf. We’ve decided this is undesirable and complicates the problem of loading extension code so we’re going to undo this specific behavior change in Telly.