Initial draft of an ohai configuration specification #118

Closed
wants to merge 7 commits into
from

Projects

None yet
@btm
Member
btm commented Apr 23, 2015

No description provided.

@jaymzh jaymzh and 4 others commented on an outdated diff Apr 23, 2015
new/ohai-config.md
+
+For example:
+
+```
+Ohai::Config[:plugin_path] << "/usr/local/lib/ohai/plugins"
+Ohai::Config[:plugin][:hostname][:fqdn_using] = [ :hostname, :nis, :dns ]
+
+# This is ignored:
+node_name "darius"
+```
+
+Using the same ```client.rb``` file used by the chef-client also allows both tools to be configured simultaneously by populating this file during bootstrap with custom local values (when that is supported in the future).
+
+### Namespacing
+
+To reduce the risk of built-in and custom plugins using the same configuration setting for conflicting purposes, plugins must prefix their configuration settings with ```[:plugin]``` and the name of the plugin consuming the setting.
@jaymzh
jaymzh Apr 23, 2015 Contributor

is this the filename or is this the :provides name, or some other name?

@jonlives
jonlives Apr 23, 2015 Contributor

I think it might make sense to use the :provides name here rather than the file name

@jaymzh
jaymzh Apr 23, 2015 Contributor

So if I provides 'foo/bar/baz' does that mean my path is Ohai::Config[:plugin][:foo][:bar][:baz]? What if I provides 'foo', 'bar', 'foo/thing' ?

@lamont-granquist
lamont-granquist Apr 23, 2015 Contributor

Makes more sense to me to use the plugin name. A plugin might not provide a complete hash, but might fill in values inside of an array contained in a hash so it would look more [:foo][:bar][*][:thing]. Just an example to show that basing the config name on the emitted JSON could get really silly. Also the platform plugin fills in platform and platform_family so which one should we use in the config? Do we have to support both? What if there's 10 keys at the same level? etc.

@jaymzh
jaymzh Apr 23, 2015 Contributor

To be clear here when you say "Name" you mean Ohai.plugin(:Name) do :Name, not the filename? So if two plugins have the same name they share the same config space? I'm fine with that.

@mcquin
mcquin Apr 29, 2015

Hm... I can argue a few things, and I think this comes from not fully understanding or knowing what kinds of configuration would be set here.

Arguments for namespacing with the plugin's :Name:

  1. specific application of configuration settings to one plugin won't affect others which provide overlapping attributes
  2. potentially replace hints files
  3. write less, configure more (Lamont's last point)

Argument for namespacing with the attribute's [:possibly][:chained][:name]:
A. configure attributes provided across plugins
B. explicitly set attribute value (not sure if this would be for pre-population or values added after ohai runs)
C. no need to know which plugin provides which attributes

I think C is pretty important because Ohai 7 kind of did away with the relationship between a plugin and an attribute. Unless you're a plugin author or don't mind searching through ohai source code, you probably don't know or care which plugin provides which attribute.

[Redacted because I'm bad at reading]

@mcquin
mcquin Apr 29, 2015

What exactly does "plugin configuration" mean?

@btm
btm Apr 30, 2015 Member

@mcquin "plugin configuration" isn't in the document, so I'd just reckon that it means passing optional configuration values to modify a plugins behavior.

I don't expect a ton of configuration being exposed for ohai plugins by plugin authors. I prefer namespacing on plugin name because:

  1. its short
  2. who knows if another plugin that provides the same attributes is going to care about or parse that configuration data in the same way. That's where namespacing is important, now you potentially have multiple plugins written by different people who might need to make agreements as to what the configuration values are and mean.

@mcquin Regarding C in your list, I don't think many people are going to go searching in source code for configuration values, it's going to be documentation that says "If you want to do blah, put Ohai::Config[:plugin][:hostname][:fqdn_using] = [ :hostname, :nis, :dns ]" in your config. The namespacing choice is for our benefit as developers more than the users.

@mcquin
mcquin Apr 30, 2015

These options aren't for setting attribute values before ohai runs? That's behavior I was assuming. In that case, using the plugin's name makes the most sense.

@btm
btm Apr 30, 2015 Member

Yes, these options are for modifying the behavior of an Ohai plugin by giving it a configuration value before it runs.

These are actual use cases we're solving for here:

#108: Ohai::Config[:plugin][:hostname][:fqdn_using] = [ :hostname, :nis, :dns ]
#109: Ohai::Config[:plugin][:platform][:amazon_is_amazon] = true

@jonlives
Contributor

Thanks for this @btm! At first glance, this looks pretty good to me. ๐Ÿ‘

@adamedx adamedx and 3 others commented on an outdated diff Apr 23, 2015
new/ohai-config.md
+
+ As a Chef user,
+ I want to easily configure Ohai,
+ so that it behaves optimally on my infrastructure.
+
+ As an Ohai user,
+ I want Ohai to load configuration settings from my client.rb,
+ so that it behaves the same as it does during a chef-client run.
+
+## Specification
+
+### Configuration File
+
+Previously Ohai has not had a configuration file. A small number of Ohai configuration settings do exist that can be specified in the chef-client ```client.rb``` file, which are then passed to Ohai during a chef-client run. For example, additional locations on the file system can be searched for plugins by adding paths to the ```Ohai::Config[:plugin_path]``` array. This file is only loaded by Chef and does not affect Ohai when being run on the command line nor any other programs loading Ohai as a library.
+
+When run from the command line, Ohai should load the ```client.rb``` file from the appropriate platform specific path unless an alternate configuration file is provided as a command line argument. To preserve compatibility with the existing parsing of the ```client.rb```, the Ohai configuration file syntax will be variables set on the ```Ohai::Config``` class,, and barewords will be ignored using method_missing.
@adamedx
adamedx Apr 23, 2015 Contributor

Don't we also now support config.rb in addition to client.rb?

@miketheman
miketheman Apr 29, 2015 Contributor

Re: config file, it's nice to be able to run ohai without escalating privileges - i.e. any logged-in user can run ohai cpu/total to get a number if they need it. Adding a read dependency on client.rb may have security implications - the running user may not have read access to said file.

@mcquin
mcquin Apr 29, 2015

Maybe the behavior for running ohai from the command line could be to look for an /etc/chef/client.rb and if it doesn't exist or isn't readable then print a warning and continue without it.

@miketheman
miketheman Apr 29, 2015 Contributor

That's one approach. We actually discussed that internally today, both for this, as well as for a chef-shell default mode - most people in our team either forget about -z or just want it to work.
When using a cli tool, adding more details to the output is a bit annoying - case and point:
ffi-yajl/json_gem is deprecated, these monkeypatches will be dropped shortly
being everywhere now.

@btm
btm Apr 30, 2015 Member

@adamedx chef/chef#1964 implemented a config.rb for knife last year, yes. It still also loads client.rb. I'll add a comment to the RFC about that.

@miketheman we already warn if we cannot find a configuration file. I'd be fine if while searching multiple file names for configuration we just print debug messages when we cannot find or open individual ones. running ohai without escalating almost always gives you different data anyway.

@danielsdeleo
Member

One thing brought up in the IRC meeting is that you'll have to be careful when handling bare words, e.g., you'll encounter things like knife[:foo] = "value" or the slightly more complicated random_thing = {}; random_thing[:bar] = "value". I think a null object pattern could handle that.

@danielsdeleo
Member

I think this can be made a lot more elegant if we instead do:

This gets us:

  • nicer config syntax for ohai, e.g., you can do ohai.config_setting = "value"
  • Don't need config parsing hacks
  • One place where we can configure default ohai config if we need ohai feature flags or whatever to maintain compat within a chef release cycle
  • 3rd parties can load chef config without resorting to config parsing hacks.
@jaymzh
Contributor
jaymzh commented Apr 23, 2015

Hmmm, @danielsdeleo that got me thinking.

Definitely not a blocker for this, but something related to bring up... if we do all of that extraction work, can we also unify the Log stuff. At the moment if I do Ohai::Log.info() it's only logged if I call ohai from the command line, but NOT if it's called from Chef. If I do Chef::Log.info() it's logged if ohai is called from Chef, but NOT if it's called from the command line. This is... painful. :(

@lamont-granquist
Contributor

I'm ๐Ÿ‘ on a separate chef-config gem, but since this is blocking other RFCs, we need an interim solution and either this RFC is the interim solution and there's another chef-config gem RFC, or we need to call out the two phases in this RFC.

@danielsdeleo
Member

Here's a pull request with an incomplete implementation of extracting a chef-config gem: chef/chef#3270 I hope it gives us a concrete way to think about the actual work required to create a chef-config gem and exposes some of the difficulties and decisions we'd face along the way.

@mcquin
mcquin commented Apr 29, 2015

+1 to #118 (comment). Ohai definitely needs its own config context.

@mcquin mcquin and 3 others commented on an outdated diff Apr 30, 2015
new/ohai-config.md
+# This is ignored:
+node_name "darius"
+```
+
+Using the same ```client.rb``` file used by the chef-client also allows both tools to be configured simultaneously by populating this file during bootstrap with custom local values (when that is supported in the future).
+
+### Namespacing
+
+To reduce the risk of built-in and custom plugins using the same configuration setting for conflicting purposes, plugins must prefix their configuration settings with ```[:plugin]``` and the name of the plugin consuming the setting.
+
+For example:
+
+```
+Ohai::Config[:plugin][:dmi][:all_ids] = true
+Ohai::Config[:plugin][:ec2][:silly_magic_arp] = "de:ad:de:ad:de:ad"
+Ohai::Config[:plugin][:platform][:amazon_is_amazon] = true
@mcquin
mcquin Apr 30, 2015

Plugin names start with capital letters. Disabled plugins are specified by the exact plugin (symbolized) name. These should be:

Ohai::Config[:plugin][:DMI][:all_ids] = true
Ohai::Config[:plugin][:EC2][:silly_magic_arp] = "de:ad:de:ad:de:ad"
Ohai::Config[:plugin][:Platform][:amazon_is_amazon] = true
@juliandunn
juliandunn May 1, 2015 Contributor

Ouch, that's very difficult to use ๐Ÿ˜ฆ

@btm
btm May 1, 2015 Member

Yeah. Mixlib-config is case sensitive. I would guess it's technically possibly to have a ec2 and an EC2 named Ohai plugin, but that would be maddening. I'd support codifying in this RFC that all key names should be lower-case.

There's a fine point here as to if we're using the filename of the plugin or the plugin name. The passwd.rb plugin contains this line Ohai.plugin(:Passwd) do, which is the plugin name as @mcquin points out.

There are nine :Filesystem plugins in different paths like windows/filesystem.rb and aix/filesystem.rb. In that cause you'd have this:

Ohai::Config[:plugin][:windows][:filesystem][:ignore_drives] = [ "z", "x" ]
Ohai::Config[:plugin][:aix][:filesystem][:whatever_aix_does] = "banana ice cream"

Here's the thing, we're not enforcing this with code. We're not going to have mixlib-config prohibit you from setting values in a portion of the config that's different than your plugin name or plugin path. I think we're only looking for something that's 1) simple and 2) likely to reduce name collisions.

More often than not the name of the file on disk is the name of the plugin. Right now I'm planning on changing my proposal to say you should name space as the lowercase version of the plugin name. People should generally get that right. In places where there's a problem like darwin/system_profiler.rb is SystemProfile, the correct namespace would be Ohai::Config[:plugin][:systemprofile]. We can catch people choosing the wrong one when we review code, but it's not a catastrophe if they don't

@jaymzh
jaymzh May 1, 2015 Contributor

plugin names have to start with a capital in Ohai 7/8, so you can't, but you can probably have an Ec2 and an EC2 ;)
@btm sounds like we're just coming up with a guideline here... so I'd agree pluginname.downcase is what space we should use (the actual name, not the path), using the path means that I have to configure my AIX and Windows implementations of some plugin separately... and that sucks (though the implementations of that plugin could expose a way to do that underneath it's namespace.

@danielsdeleo danielsdeleo referenced this pull request in chef/chef May 13, 2015
Merged

Pull Config from external Gem #3270

@btm
Member
btm commented Jun 4, 2015

Updated for ChefConfig, ya'll have like a few seconds to review it and drink coffee before the meeting. :)

@danielsdeleo danielsdeleo commented on an outdated diff Jun 4, 2015
new/ohai-config.md
+chef_server_url "https://api.chef.io/organizations/oneofus"
+ohai.plugin_path = "/etc/chef/ohai/plugins.local"
+ohai.plugin[:hostname][:fqdn_using] = [ :hostname, :nis, :dns ]
+```
+
+Because at the top level (outside of a config_context) Ohai::Config will be the same as ChefConfig and Chef::Config, existing top level configuration options like Ohai::Config[:disabled_plugins] will be deprecated in favor of new settings within the config_context, i.e. Ohai::Config.ohai.disabled_plugins. Until support for those top-level settings is removed, their values will be set inside the config_context.
+
+For example:
+
+```
+# Old Syntax
+Ohai::Config[:plugin_path] << "/usr/local/lib/ohai/plugins"
+
+# New Syntax
+Ohai::Config.ohai.plugin_path
+=> ["/opt/chefdk/embedded/apps/ohai/lib/ohai/plugins", "/usr/local/lib/ohai/plugins"]
@danielsdeleo
danielsdeleo Jun 4, 2015 Member

[nitpick] These do different things which muddies the example a little.

@danielsdeleo danielsdeleo commented on an outdated diff Jun 4, 2015
new/ohai-config.md
+```
+
+Because at the top level (outside of a config_context) Ohai::Config will be the same as ChefConfig and Chef::Config, existing top level configuration options like Ohai::Config[:disabled_plugins] will be deprecated in favor of new settings within the config_context, i.e. Ohai::Config.ohai.disabled_plugins. Until support for those top-level settings is removed, their values will be set inside the config_context.
+
+For example:
+
+```
+# Old Syntax
+Ohai::Config[:plugin_path] << "/usr/local/lib/ohai/plugins"
+
+# New Syntax
+Ohai::Config.ohai.plugin_path
+=> ["/opt/chefdk/embedded/apps/ohai/lib/ohai/plugins", "/usr/local/lib/ohai/plugins"]
+```
+
+For convenience, a config method will be added to the Ohai class to access Ohai::Config.ohai, allowing the use of Ohai.config[:plugin_path] instead of Ohai::Config.ohai[:plugin_path] throughout the code. All existing uses of Ohai::Config in Ohai will need to be updated accordingly.
@danielsdeleo
danielsdeleo Jun 4, 2015 Member

Probably want to backtick the code things so it reads a little clearer

@danielsdeleo danielsdeleo and 1 other commented on an outdated diff Jun 4, 2015
new/ohai-config.md
+For example:
+
+```
+# Old Syntax
+Ohai::Config[:plugin_path] << "/usr/local/lib/ohai/plugins"
+
+# New Syntax
+Ohai::Config.ohai.plugin_path
+=> ["/opt/chefdk/embedded/apps/ohai/lib/ohai/plugins", "/usr/local/lib/ohai/plugins"]
+```
+
+For convenience, a config method will be added to the Ohai class to access Ohai::Config.ohai, allowing the use of Ohai.config[:plugin_path] instead of Ohai::Config.ohai[:plugin_path] throughout the code. All existing uses of Ohai::Config in Ohai will need to be updated accordingly.
+
+### Configuration File
+
+When run from the command line, Ohai should load the config.rb and then the client.rb files from the appropriate platform specific path, unless an alternate configuration file is provided as a command line argument. This provides similar behavior to knife which loads config.rb and then knife.rb. This facilitates reducing the number of separate configuration files to maintain for command line behavior.
@danielsdeleo
danielsdeleo Jun 4, 2015 Member

Do you mean that it should read, e.g., ~/.chef/config.rb (i.e., the "workstation" path)? Or did we have a plan to make chef-client and chef-solo just read /etc/chef/config.rb (I'd like that but I can't recall if we have a plan for it).

@btm
btm Jun 4, 2015 Member

I think we just load the workstation path for now, but moving toward just a ~/.chef/config.rb and /etc/chef/config.rb file does sound nice too.

@danielsdeleo danielsdeleo commented on an outdated diff Jun 4, 2015
new/ohai-config.md
+
+When run from the command line, Ohai should load the config.rb and then the client.rb files from the appropriate platform specific path, unless an alternate configuration file is provided as a command line argument. This provides similar behavior to knife which loads config.rb and then knife.rb. This facilitates reducing the number of separate configuration files to maintain for command line behavior.
+
+When loaded as a library, Ohai must not load a configuration file and will expect to be provided any necessary configuration options. For example, when loaded by the Chef client, configuration values will be located in the client.rb file, or the file passed to the Chef client as the configuration file. Using the same file for configuration simplifies configuration file creation during bootstrap for the client.
+
+### Namespacing
+
+To reduce the risk of built-in and custom plugins using the same configuration setting for conflicting purposes, it is recommended that all plugins prefix their configuration settings with [:plugin] and the snake-case name of the plugin consuming the setting, as set in the plugin itself. The exposed overlap is intended, to facilitate passing a configuration option to the same plugin for multiple platforms but only specifying it once.
+
+For example:
+
+```
+Ohai::Config.ohai[:plugin][:memory][:unit] = "mb"
+Ohai::Config.ohai[:plugin][:dmi][:all_ids] = true
+Ohai::Config.ohai[:plugin][:ec2][:silly_magic_arp] = "de:ad:de:ad:de:ad"
+Ohai::Config.ohai[:plugin][:platform][:amazon_is_amazon] = true
@danielsdeleo
danielsdeleo Jun 4, 2015 Member

Might be worth showing this as the syntax users would put in their config file? Or showing both how you'd configure it as a user and also how you'd access as a plugin developer?

@danielsdeleo
Member

๐Ÿ‘ overall, just some possible cleanup on the language and format of examples.

@thommay
Contributor
thommay commented Jun 4, 2015

LGTM ๐Ÿ‘

@coderanger
Contributor

We should address what to do with the existing hints system. I would prefer to see it deprecated in favor of the new config system if we move forward with this. Having two places to put config data for plugins is overly confusing, especially to new users. The case I really want to avoid is knife bootstrap --ohai-config ipaddress.rb --hint ec2 --hint iam .... There doesn't seem to be an obvious divide between the config system and hints as it stands, so we should either merge them or figure out a good justification for having two separate systems.

@danielsdeleo danielsdeleo commented on the diff Jun 4, 2015
new/ohai-config.md
+For convenience, a config method will be added to the `Ohai` class to access `Ohai::Config.ohai`, allowing the use of `Ohai.config[:plugin_path]` instead of `Ohai::Config.ohai[:plugin_path]` throughout the code. All existing uses of `Ohai::Config` in Ohai will need to be updated accordingly.
+
+### Configuration File
+
+When run from the command line, Ohai should load the workstation 'config.rb' and then the 'client.rb' files from the appropriate platform specific path, unless an alternate configuration file is provided as a command line argument. This provides similar behavior to knife which loads 'config.rb' and then 'knife.rb'. This facilitates reducing the number of separate configuration files to maintain for command line behavior.
+
+When loaded as a library, Ohai must not load a configuration file and will expect to be provided any necessary configuration options. For example, when loaded by the Chef client, configuration values will be located in the 'client.rb' file, or the file passed to the Chef client as the configuration file. Using the same file for configuration simplifies configuration file creation during bootstrap for the client.
+
+### Namespacing
+
+To reduce the risk of built-in and custom plugins using the same configuration setting for conflicting purposes, it is recommended that all plugins prefix their configuration settings with `[:plugin]` and the snake-case name of the plugin consuming the setting, as set in the plugin itself. The exposed overlap is intended, to facilitate passing a configuration option to the same plugin for multiple platforms but only specifying it once.
+
+For example, configuration file settings would look like:
+
+```
+ohai[:plugin][:memory][:unit] = "mb"
@danielsdeleo
danielsdeleo Jun 4, 2015 Member

From a technical perspective, this is a bit tricky. Allowing arbitrary nesting here means that we have to make ohai[:plugin] an auto-vivifying Hash or Mash. I dislike that because you have to fix the behavior of every single method on Hash to do the right thing (e.g., rarely used stuff like Hash#replace). Though this will be less of a problem if we limit the nesting level we allow for plugin configuration to a single level.

@stevendanna
stevendanna Jun 5, 2015 Member

What if we added some Ohai Plugin DSL syntax for the declaration of configurable? We could then pre-vivify those paths and also catch problems. I'm not sure how it would work as there is a bit of a chicken-and-egg problem since the load path is part of configuration. Perhaps if the per-plugin settings looked more like:

ohai[:plugin][:PLUGIN_NAME] do
  nested_setting1 = foo
  nested_setting2 = bar
end

or something ilke that which would allow per-plugin configuration to be deferred until after that plugin was loaded.

@btm
Member
btm commented Jun 4, 2015

@coderanger The hints system functions well as an API for provisioning tools to provide information about the node to Ohai. I'm reluctant to replace it with a configuration system meant for human consumption and then try to make the existing tools use that effectively.

Hints was designed to serve two purposes:

  • Tell Ohai something about the system it couldn't easily discover e.g. you are in EC2.
  • Pass Ohai attributes it couldn't easily discover, e.g. you are in datacenter region delta.

knife-ec2, knife-rackspace, and knife-softlayer are all examples of plugins that tell Ohai they're in a cloud system, and then their respective plugins can go out and collect additional metadata. Other provisioning tools like kitchen-ec2 and chef-provisioning also use this API.

knife-azure and knife-linode are examples of tools that pass metadata into Ohai which becomes attributes, because that data cannot be gathered from the node.

In all of these examples, the user doesn't specify "--hint" on bootstrap, the provisioning tool uses the hints system automatically to pass the necessary information. I don't recall why we needed "--hint" initially. Perhaps so we could "solve the problem" in Ohai and update the external tools later. Over time, because Ohai doesn't have a configuration system currently, people have submitted patches to use the hint system to configure plugins and we've tried to prevent that from happening as we knew we needed a real configuration file some day for other features.

To replace the hints system with this proposed configuration system, we would have to add support for a .d folder for tools to write to. We know from experience trying to manage a configuration file with Chef::Util::FileEdit that is only partially automated is very difficult. Each tool would create it's own configuration file, probably namespaced by tool name, and create the user configuration values like:

ohai.plugin[:azure][:attribute][:vm_name] = "a_tiny_cloud_nodule"
ohai.plugin[:azure][:attribute][:public_fqdn] = "x.azure.the.tubes"
ohai.plugin[:azure][:attribute][:public_ssh_port] = 22
ohai.plugin[:azure][:attribute][:public_winrm_port] = 5985

Then I think that we'd create a helper method that each plugin could use to parse attributes out of its own configuration settings and set those as attributes.

So I think it's doable, but I think it's a worse API for software. I'd rather put notifications around that say hints are just an API for data integration from provisioning software and the configuration file is what humans should be using.

It's hard finding people who've really used hints [as intended?], tagging some of them in case they have thoughts: @stevendanna, @stormsilver, @lndbrg, @jblancett, @wingrunr21

@coderanger
Contributor

So the counter argument is then why would ohai.plugin[:hostname][:fqdn_using] = [ :hostname, :nis, :dns ] not be using a hint file for the hostname plugin? plugin_path I get as being a global-level Ohai config, but a lot of the other examples given seem to be plugin-level and thus there is no clear reason why they should be configs vs. hints. I don't really think "as intended" matters when this is going to be exposed all the way out to the knife bootstrap UX.

@btm
Member
btm commented Jun 4, 2015

hints are for data, config files are for settings.

/etc/chef/ohai/hints/linode.json is data passed from another tool to facilitate the collection of attributes and provide additional attributes that Ohai would be unable to collect.

ohai.plugin[:hostname][:fqdn_using] = [ :hostname, :nis, :dns ] is a user preference that specifies the preferred behavior of the plugin.

The interface of hints is designed for other tools, not for humans. Hint files are a terrible interface for user configuration because they're a bunch of individual JSON data files spread out that are unique to a node.

Similarly, the user configuration file is a pretty bad interface for other pieces of software to integrate with, for reasons I described in my last comment. You've got to manipulate the data into a custom file format meant for humans, some of which is potentially data and some potentially settings.

The user configuration file should be a single file that contains the settings for your infrastructure. If you have differing infrastructures you could write some code or maintain separate files. We want a user configuration interface that can use existing workflows, e.g. copy config.rb to your home .chef directory, client.rb to your server, and go.

@coderanger
Contributor

I really don't see how that separation is clear enough to be practical. I understand that the EC2 plugin is technically always activated and the hint is "I am actually on an EC2 machine" but in practice the UX is "How do I enable the EC2 plugin?" and that UX needs a clear answer that doesn't surprise users.

@coderanger
Contributor

And if the answer to make "How do I enable the EC2 plugin?" is "Make a hint file." and then "How do I change the hostname plugin? Make a config file.", the next question to any user that doesn't have a deep understanding of the internals is "Why are these in two different places?". We've all seen this with software before, two settings that seem like basically the same kind of thing but are in different places due to the internal implementation. And that's a bad UX and we shouldn't do that.

@btm
Member
btm commented Jun 4, 2015

To the user, the plugins are currently all enabled by default. If you want to disable the ec2 plugin, you use Ohai::Config[:disabled_plugins] via the configuration file. To the user, the answer is always use the configuration file.

I'm not convinced there's enough confusion there to warrant forcing all the other tools in the ecosystem into switching to an inferior API.

@coderanger
Contributor

I can flatly say that is not how people think about it. The EC2 plugin (and several other cloud plugins) do not work at all unless the hint file exists for 99% of people. Also stuff like the the EC2 plugin's IAM hint is basically config. I understand you are trying to adhere to technical correctness and I am saying that is going to result in a confusing UX by having two different says to set things for Ohai plugins. We need to address that before this moves forward.

@juliandunn
Contributor

hints are for data, config files are for settings

Users don't understand that distinction and they shouldn't have to reason about the difference. This has been borne out in a number of conversations with users along the lines of "why do I need to create a JSON file with '{}' in it just so I can collect EC2 metadata?" I talked about this some time ago at chef/ohai#422

@btm
Member
btm commented Jun 5, 2015

@juliandunn You would have to have to create a configuration file that says ohai.plugins[:ec2][:something] = true just so you can collect EC2 metadata either way. The JSON structure of hints is intended to make it easy for tools to do that for you, which many tools do, so the user doesn't have to know about it.

@btm
Member
btm commented Jun 5, 2015

@coderanger I need you to propose an alternative.

@coderanger
Contributor

So an alternative would be to merge the two systems. The old hint JSON would be a JSON form of config while this new API would be the .rb form, and define some loading rules so that hints/foo.json becomes ohai[:foo]. The hints/ folder would be basically equivalent to a .d style loading system for the same config data, and modify hint? to read from the config data in memory instead of trying to load hint files. This would still be somewhat imperfect as the tooling would be unequal, but it at least puts us on a path towards unifying things. To carry the example forward, knife bootstrap --hint 'hostname={"fqdn_using": ["hostname", "dns"]}' would work just fine. Having multiple formats has proven to be not entirely clear for things like roles and environments, but this has been management and allowed both formats to do things the other cannot so it would be a starting point.

@juliandunn
Contributor

You would have to have to create a configuration file that says ohai.plugins[:ec2][:something] = true just so you can collect EC2 metadata either way. The JSON structure of hints is intended to make it easy for tools to do that for you, which many tools do, so the user doesn't have to know about it.

That "configuration file" mechanism is still easier to grok than the JSON hint structure. In practice, especially on EC2, many users don't fire up machines using knife ec2 or something that creates the hint for them; they use a CMP, they create the boxes manually, they use autoscaling groups, whatever... and then they're left to scratch their heads about our hint system. I like @coderanger's alternative of having one system.

@stormsilver

Whoa, this is a huge blast from the past. TBH I'm surprised the hints system is still around.

When the hints system came into being, Ohai was using various forms of MAC address matching to figure out which cloud provider it was running on. This proved to be quite brittle and eventually broke. We needed a way to tell Ohai which cloud it was running on - something that provisioning tools always knew. Aha! We'll just drop a file in a certain place, call it /etc/ohai/which_cloud_am_i_running_on and we'll shove a string in there ec2 and boom! Detection done.

Then someone was like "Bro you could make it a file named for the cloud and make it JSON and then we could also put other things in there too, if we needed". Okay, sure. At the time there was no use for the JSON inside the file, but it was easy and "forward thinking" so great.

The key for me at the time was that I could just stuff a line in my bootstrap template touch /etc/ohai/hints/ec2.json and that was all I needed to do to cause Ohai to know it was running on EC2 or Rackspace or whatever. The reason it was called "hints" was because we were thinking "sometimes Ohai just needs a little help doing all its detection" and we could pipe data into it that we already knew from some other source.

I don't really know how the hints system has evolved since then. I think if you're going to replace it with something better/more extensible/easier to understand then that's a great thing. To me being able to do something simple like touching a file without needing to go through special tooling is quite useful and I would try to figure out a way to persist that ability through to the new design.

But there are a lot of ways to do that, right? I mean arguably a lot of people are already writing these types of things into their chef.rb config files (something like "where's the chef server?" - easy for provisioning to know, hard for chef to figure out on its own). So moving over to a similar config file ain't the end of the world. It's not hard to cause provisioning tools to be able to write a ruby config file.

You could also consider the "hints files" to be like Chef's first_boot.json. Ohai could read its hints on first boot based on some kind of condition, transfer its contents into config, and then never look at it again?

@btm
Member
btm commented Jun 5, 2015

@juliandunn I'm going to interpret your last comment as liking the @coderanger's notion of a single system, but presume you didn't read his alternative in the comment right before yours because its effectively using the existing JSON based hints system for configuration and you disapprove of the use of JSON.

I think on the surface everyone will agree it's preferable to have less options.

@stormsilver hey man! yeah, it was hard to look back and believe it's been three years. hints really hasn't changed at all, except that more provisioning tools take advantage of it these days. Most the interest in it has been of the sort, "Ohai doesn't have a configuration file? huh." GCE provided a DMI string for detection, which was awesome, although unfortunately we removed that functionality from Ohai when it changed formats rather than updating it. :( Most other cloud providers haven't caught on yet that you shouldn't need a network call to figure out where you are. A couple tools are using the JSON structure to pass metadata on creation now where there isn't a metadata service to query after creation. We've still been toeing the line of trying to avoid network calls that timeout, but they're sneaking in. Maybe we can find a compromise on that once we have a configuration file.

@btm
Member
btm commented Jun 5, 2015

@coderanger @juliandunn I've added a paragraph about the hints system to the "current state" section of the RFC. Obviously it's common people don't understand the difference between hints and a config file. I agree with you that we need to approach that issue with some thought. However, we've had many features we've wanted to write in the past, and a couple right now, that are blocked by not having a configuration system. We should move ahead with getting a configuration system written and then iterate on improvements that can leverage it.

I think significant value exists in sharing a configuration file format and configuration file with Chef through the use of ChefConfig which makes it a clear winner over a solution that builds on the hints systems JSON format.

I've discussed the issues related to autovivifying deep hashes with @danielsdeleo and @jkeiser, and I'm going to work on integrating those conversations in next.

@thommay
Contributor
thommay commented Jun 5, 2015

Completely agree with @btm here; if we want to remove or change the hints system (and I'm still unconvinced we do), that should occur in a separate RFC.

@coderanger
Contributor

100% disagree. This UX schism will be directly exposed to end users and so it is a place where I think tech debt is especially objectionable. I agree there is a lot of value in the config system, I also think we need to resolve this issue before we implement it. Iteration doesn't work when a bad UX will be the first thing users learn of the new system, we need to nail that down first and then implement as needed to make the UX delightful. I would love to see a counter-example for how to do a knife bootstrap with whatever options are needed to (effectively) enable the EC2 plugin and to reconfigure the hostname plugin in a way that is clear and concise to end users.

@jkeiser
Member
jkeiser commented Jun 6, 2015

It sounds to me like if we had a configuration system, we would very quickly add configuration toggle to enable EC2, so users didn't have to specify weird hint files. Am I off base here? Is there some reason hints are the right way to do it, or did we just do that because we had no other way to configure ohai?

The question of how to accept hints data from external systems seems orthogonal to me. If you have a good configuration system, you add the configuration toggles users need.

@cheeseplus

@jkeiser I think this is totally on point

Ohai hints are not exactly well documented and very confusing to new users as to why or when they need them. If there was a configuration system there would be no reason to have them around any longer that I can see and would go a good ways to demystifying Ohai behaviour for new users.

@coderanger
Contributor

We can't remove hint? until there is a normal deprecation cycle, and beyond that we need a UX flow for knife bootstrap and friends. I'm totally okay with saying knife should write out a config file for ohai during bootstrap but no one has put forward an example of what that should look like as a user.

@coderanger
Contributor

(also even though we can't remove hint? I think it would be fine to make that read from the config system as described above)

@thommay
Contributor
thommay commented Jun 6, 2015

Given that we can't remove hints for a complete release cycle - until Chef 13 at the earliest - I think it's much more valuable to get the config system merged and working so that we can see exactly how it feels and iterate from there. And note that it's not just the code that's being deprecated; we have to keep the current behaviour of files in /etc/chef/ohai/hints.

@jkeiser
Member
jkeiser commented Jun 6, 2015

Yeah, it feels like we should introduce the config system, so that we can put in the configuration variables we want and start using the hint system exclusively for what it was designed for--"push" input from external systems--rather than the configuration it's currently being used for.

I can't say whether removing hints is right or not; I don't know enough about the different hints to say whether dumping them in with the user-driven configuration file is a good idea or not. (If the hints are really pushed by external systems, it feels like any configuration system that is centered around a single file would be the wrong place to put it--you need multiple files to avoid people overwriting each other.)

I don't think it's necessary to fold the hint system into configuration right now. @coderanger has a valid point that we need knife bootstrap ui to set up ohai configuration, and we should be doing RFCs from a UX perspective first. I'd imagine we'd make that interface similar to how we set up chef configuration. We'll also need the ability to set ohai configuration in Provisioning.

This has gone on for a while. My suggestion for next steps:

  • Add command line / provisioning examples to the RFC for configuring the ohai configuration.
  • Add an example of "enable EC2" to this RFC, so we have a POC for moving config from hints into the new system. (NOTE: this is predicated on my understanding that "enable EC2" is generally user-driven configuration, which in my experience is true.)
  • Punt "merge / deprecate / destroy / mutate the hint system" to a separate RFC.

This sound like it addresses most concerns?

@btm
Member
btm commented Jun 6, 2015

@jkeiser @cheeseplus Once we have a configuration system, one of the next things to do add an attribute (plugin) whitelist mode. This was part of the Ohai 7 design that didn't get done. The plan back then was to add a run_mode option that could take :all_plugins (default) or :required_plugins. Also added would be required_attributes and disabled_attributes options, the latter being like disabled_plugins but written for the new (and completed) Ohai 7 DSL. That was two years ago though.

Obviously we need to go back through the design cycle for that now. But once you had that, I think you could argue it's okay to switch the cloud plugins back to looking for metadata servers on the network by default. Anyone who didn't want that could migrate to using the whitelist, which would negate the original need for hints.

The second feature of hints, ingesting static data, could easily be deprecated into some kind of --ingest-json-attrs flag that pretty much kept doing the same thing it does now.

@coderanger This is a potential roadmap to a place that I think we all want, but I think is out of scope for this design:

knife currently creates the client.rb from specific configuration settings/command line arguments. First, we need to give ChefConfig conf.d support. Next, add a flag+config to knife for specifying a config file on the workstation that gets copied to the client on bootstrap. Which is where you'd configure Ohai plugins as proposed here, and would also help existing people who are hacking their bootstrap templates to set Ohai::Config[:disabled_plugins] today. As well as anything else you want in your client.rb but knife doesn't currently support, which currently puts you in a crappy bootstrap, then chef-client cookbook, then a second run pattern. Then Ohai attribute/plugin whitelisting, then open the network timeout faucet on all the cloud plugins, then deprecate/rename hints for attribute ingestion.

Given that,

I would love to see a counter-example for how to do a knife bootstrap with whatever options are needed to (effectively) enable the EC2 plugin and to reconfigure the hostname plugin in a way that is clear and concise to end users.

  1. Get an Ohai config (this RFC)
  • Use the chef-client cookbook to configure the ohai plugins.
  • knife-ec2 creates hint files for you, our you could place them in your AMI for knife bootstrap

knife ec2 server create or knife bootstrap --hint ec2

  1. ChefConfig .d support + knife bootstrap config file support
  • Use a new --copy-config option or configuration setting to copy a config file to configure ohai plugins
  • knife-ec2 creates hint files for you, our you could place them in your AMI for knife bootstrap

knife ec2 server create or knife bootstrap --hint ec2

  1. Ohai plugin whitelisting
  • Use a new --copy-config option or configuration setting to copy a config file to configure ohai plugins + use whitelisting if you don't want cloud plugins
  • plugins/tools that use hints for attribute inject keep doing so, but cloud plugins are now gratuitous on the network

knife ec2 server create or knife bootstrap

  1. Ohai hints -> attribute injection
  • Use a new --copy-config option or configuration setting to copy a config file to configure ohai plugins
  • cloud plugins are still gratuitious on the network, but you're whitelisting now anyway.
  • old --hint attribute injection is deprecated to some other name like --ingest-json-attrs

knife ec2 server create or knife bootstrap

@lamont-granquist
Contributor

FWIW, my vote is that Ohai config and a config.d style directory is what should ultimately replace hints completely, but that replacing hints is definitely out of scope for this RFC.

I definitely do not want to see Ohai config modelled as hints just because we have hints and we can only have one thing or nothing at all. Since people are confused by hints that just winds us up in absolutely the wrong place.

@adamhjk adamhjk and 1 other commented on an outdated diff Jun 11, 2015
new/ohai-config.md
+
+The lack of a configuration file has been a major impediment to many improvements in Ohai and plugin behavior.
+
+Previously Ohai has not had a configuration file. A small number of Ohai configuration settings do exist that can be specified in the Chef client 'client.rb' file which is parsed by `Mixlib::Config`. These settings are then passed to Ohai during a client run. For example, additional locations on the file system can be searched for plugins by adding paths to the `Ohai::Config[:plugin_path]` array. However, the 'client.rb' file is only loaded by the Chef client and does not affect Ohai when being run on the command line nor any other programs loading Ohai as a library, causing inconsistent Ohai results.
+
+### Hints
+
+The existing hints system was designed to provide facts about a system that Ohai would be unable to or have difficulty to determine on its own. The most common use case is informing a knife plugin that the system is in a particular cloud environment, enabling the plugin to collect appropriate metadata. Sometimes the plugin also passes metadata to Ohai to become attributes that Ohai would otherwise be unable to collect. For this purpose it was decided to use JSON as a data interchange format.
+
+When a 'hint_name.json' file exists in the directory specified by `Ohai::Config[:hints_path]`, `Ohai::Hints.hint?(hint_name)` will return non-nil. If the file contains JSON data, it will be returned as a hash. If the file is empty, an empty hash will be returned.
+
+The hints system should only be used by other tools to assist Ohai in collecting data. It should not otherwise be used to configure the behavior or Ohai or its plugins. That is the purpose of the new configuration system. The hints system may be combined with the configuration system in the future and deprecated for general ease of use of Ohai.
+
+## Specification
+
+The `Ohai::Config` Ruby class will use the new ChefConfig library that is bundled with the Chef client. Configuration will be set in a configuration file using a `Mixlib::Config` config_context named 'ohai'. Here is an example 'client.rb' file that would configure both the client and Ohai.
@adamhjk
adamhjk Jun 11, 2015 Contributor

Just for clarity, this doesn't imply that ohai has a dependency on chef, just on the chefconfig gem?

@danielsdeleo
danielsdeleo Jun 11, 2015 Member

Correct, that's why we split it out. I'll leave to @btm to update if this should be more clear.

@coderanger
Contributor

So let's rewind a bunch and look at this from the very outermost level. The core desire is to have a way to configure ohai so that things like knife bootstrap can leave the system set up so that ohai run directly and ohai run from Chef will behave the same way. Let's build out that UX first and then use that to drive how we implement things.

To work out the UX we need to define some more concrete use cases than just "I want to easily configure Ohai". From our discussions above I think the most illustrative use cases so far:

  1. I want to disable an ohai plugin,
  2. I want to set the ohai plugin path.
  3. I want to change the source of data for the hostname plugin.
  4. I want to retrieve EC2 metadata in the ec2 plugin.

Does that seem like a starting point on use cases? I'll keep using those for the remainder of this wall o text, but this is a decision point so it could affect everything after here.

I would suggest the following as a UX example to cover these cases:

  1. knife bootstrap --ohai-config "disabled_plugins=['one', 'two']"
  2. knife bootstrap --ohai-config "plugin_path='/tmp/plugins'"
  3. knife bootstrap --ohai-config "hostname.fqdn_using=['hostname', 'dns']"
  4. knife bootstrap --ohai-config ec2

These could all be composed by giving the --ohai-config option more than once:

knife bootstrap --ohai-config "hostname.fqdn_using=['hostname', 'dns']" --ohai-config ec2

What do people think about that as a UX? Once we agree on a UX we can figure out how best to implement that.

@btm
Member
btm commented Jun 17, 2015

@coderanger, I don't think that UX is core to the problem we're trying to solve, nor that UX is the appropriate starting point. By starting with that particular UX, you're focusing the discussion on creating a command line interface. But we want to use CLI for options the user might flip regularly. We already have mixlib-cli for that. We are building the config system here for the system wide options that are unique to an environment that are unlikely to change for the user on a system by system basis. In this case, the mixlib-config / chef-config UX is more relevant.

@coderanger
Contributor

If the use case here is being driven by users needing to do something, then user experience should be part of the driving force of the design. If this is not a user facing feature then that use case should be removed, though then I'm unsure what problem it is solving. We should map out every step of this UX, that means CLI options -> what files those modify -> how that affects ohai. UX is literally at the core of every problem and always will be if you want to make usable software.

@adamhjk
Contributor
adamhjk commented Jun 18, 2015

This RFC is approved. The goal is iteration towards a configuration design that can lead to an improved UX. @thommay is drafting another RFC that unifies the UX for all these use cases, and will take @coderanger's points into account.

Go go gadget @chef/rfc-editors

@thommay
Contributor
thommay commented Jun 19, 2015

Merged, thanks everyone.

@thommay thommay closed this Jun 19, 2015
@jaymzh
Contributor
jaymzh commented Oct 21, 2016

did this ever get actually implemented. I had thought it was... but ohai.config[:plugin] doesn't work, nor does ohai.plugin.

I ended up having to Ohai::Config[:plugin] = {} unless Ohai::Config[:plugin]; Ohai::Config[:plugin][:foo] = {...}`

is there a plan to implement this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment