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

Feature Request - config.d style configuration for ziti controller #2012

Open
emoscardini opened this issue May 2, 2024 · 7 comments
Open

Comments

@emoscardini
Copy link
Contributor

Feature request to support conf.d style configuration for the ziti controller.

Adopting a config.d style would include an option to look in a specified directory for multiple configuration directives.

Example of adding a global include key that specifies the directory to search:
ziti controller configuration:

v: 3

include: 
  directory: /opt/openziti/config.d
  extension: yml

events:
  jsonLogger:
    subscriptions:  jsonLogger:
    subscriptions:
      - type: fabric.circuits
     ...
        interval: 25s
    handler:
      type: file
      format: json
      path: /tmp/ziti-25s-events.log

...

The directory /opt/openziti/config.d could contain multiple files all ending in the specified extension.

The content of a file would be the original format & could contain one or more configuration keys:
Example of separated events configuration that will override the default:
/opt/openziti/config.d/01-event-logger.yml

events:
  jsonLogger:
    subscriptions:  jsonLogger:
    subscriptions:
      - type: fabric.circuits
     ...
        interval: 5s
    handler:
      type: file
      format: json
      path: /tmp/ziti-5s-events.log
  amqpLogger:
    subscriptions:
      - type: fabric.usage
      - type: fabric.circuits
     ...
        interval: 5s
    handler:
      type: file
      format: json
      path: /tmp/ziti-events.log

The files would be read/sorted in lexical order. 01,10, 1, 2, 3, a, b, c
Any config key loaded from file would be logged as coming from the specified config.d file.
Any duplicate keys would be logged as unable to import & skipped resulting in only the first config key read to become active.
No merging.
If the include is specified & no files exist or are unable to be read because of permissions, a message would be logged & the process would proceed.

@qrkourier
Copy link
Member

Assuming precedence is by lexical/alnum sort descending, I'd expect highest (last) precedence to win when the same directive is given multiple times, not lowest (first) precedence.

@qrkourier
Copy link
Member

You don't want config merging a la Docker Compose? That was my mental model for this until you say no merging. 🙂

@qrkourier
Copy link
Member

In your model, if first is highest precedence then included configs are always additive, and individual, included directives are ignored unless they're the first occurrence, correct? So, the descending sort also has descending precedence, and last is lowest.

@emoscardini
Copy link
Contributor Author

Merging is hard to handle & I'd rather have a consistent way to load separate distinct overrides than merge multiple ones.

For the sorting the files, I'm following sudoers example:

 Files are parsed in sorted lexical order.  That is, /etc/sudoers.d/01_first will be parsed before /etc/sudoers.d/10_second.

They use lexical order in ascending for sorting the files.

In my model, you could create a default configuration with the include at the top, before any keys & anything loaded from file would override the default values in the configuration.

@qrkourier
Copy link
Member

I like the admin experience you're going for. I'm still fuzzy on precedence. If lower sorted includes can't cancel/override a config property that's already defined then they're lower precedence by definition, i.e., not "overrides." Did I misunderstand?

@emoscardini
Copy link
Contributor Author

Maybe an example would be better.

The default config contains:

v: 3

include: 
  directory: /opt/openziti/config.d
  extension: yml

events:
  jsonLogger:
    subscriptions:  jsonLogger:
    subscriptions:
      - type: fabric.circuits
     ...
        interval: 25s
    handler:
      type: file
      format: json
      path: /tmp/ziti-25s-events.log
...

When the controller starts up, it would find the include key & would read the files in the specified directory. It wouldn't find any files so it would continue to read the default config & load the events key with the 25s interval & something like the following would be in the logs:
[INFO] - found include key but unable to load any files from specified directory.

Someone creates a file called events.yml in the specified directory with the content:

events:
  jsonLogger:
    subscriptions:  jsonLogger:
    subscriptions:
      - type: fabric.circuits
     ...
        interval: 60s
    handler:
      type: file
      format: json
      path: /tmp/ziti-60s-events.log

Sorting in lexical order of files.
events.yml

When the controller restarts, the controller would see the Include before reading events key in the default config & would read the files in the specified directory. It would find events.yml & load the events key with the 60s interval & ignore the default events with the 25s interval & something like the following would be in the logs:
[INFO] - loaded events from /opt/openziti/config.d/events.yml
[ERROR] - found duplicate key events - ignoring

Then someone drops another file called 01-events.yml in the specified directory with the content:

events:
  jsonLogger:
    subscriptions:  jsonLogger:
    subscriptions:
      - type: fabric.circuits
     ...
        interval: 5s
    handler:
      type: file
      format: json
      path: /tmp/ziti-5s-events.log

Sorting in lexical order of files.
01-events.yml
events.yml

When the controller restarted, you would end up with a 5s interval & something like the following would be in the logs:
[INFO] - loaded events from /opt/openziti/config.d/01-events.yml
[ERROR] - found duplicate key events - ignoring
[ERROR] - found duplicate key events - ignoring

@qrkourier
Copy link
Member

I've got it now. The proposed logic is to first seek and find and load any lexically-sorted includes, then load the default configuration. This means the includes have the highest precedence and are always loaded first, and any defaults in the main config.yml are only loaded if not already defined.

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

No branches or pull requests

2 participants