danyspin97 / tt

A init/rc manager inspired by s6 and 66. Discontinued in favor of https://github.com/rinit-org/rinit
GNU General Public License v3.0
17 stars 3 forks source link

Providers in services #9

Open danyspin97 opened 4 years ago

danyspin97 commented 4 years ago

Some services need a feature that can be provided by one or more services but they only depend on one (otherwise optional dependencies would have sufficed).

Example

mysql.service

[main]
name = mysql
type = longrun

[run]
build = auto
execute = "mysqld"

mywebapp.system:

[main]
name = mywebapp
type = longrun

[options]
depends#sql = ( mysql postgresql )

When enabling mywebapp, tt will look for both mysql.system and postgresql.system files. In this case the service postgresql cannot be found so mywebapp will depend on mysql service.

Overriding default decision

The default decision can be overriden by creating a file /etc/tt/conf/mywebapp.depends (the exact name/location needs to be finalized) with the following contents:

sql = postgresql

This is also needed when both services are found on the system.

CLI

In addition, the following arguments will be added:

# All providers are set to postgresql (when it is a possible choice)
$ tt enable mywebapp --depends-on postgresql
# depends#sql will use postgresql
$ tt enable mywebapp --depends-on sql#postgresql
# depends#sql in service mywebapp will use postgresql 
$ tt enable mywebapp myotherwebapp --depends-on mywebapp:sql#postgresql

Use cases

Possibilities

Providers chosen for the system in a /etc/tt/depends file and overridden by myservice.depends files.

Cogitri commented 4 years ago

What's the default importance ordering for providers?In depends#sql=( mysql postgresql ) mysql is used if both mysql and pgsql ate available?

danyspin97 commented 4 years ago

If both are provided, the file /etc/tt/conf/mywebapp.depends is needed to choose which one is used. The global system file /etc/tt/depends set defaults without needing a per-service .depends file. For example NetworkManager can be set as network handler and iptables as firewall at a distribution level.

wozeparrot commented 4 years ago

Will tt throw an error when updating the service database and no option is chosen?

danyspin97 commented 4 years ago

Will tt throw an error when updating the service database and no option is chosen?

Following the above example of mywebapp, it could happen:

When, instead of adding a service, the database is updated and a provider has been added to the services, tt still follow the three situations above.

danyspin97 commented 3 years ago

New proposal with virtuals

Another possibility would be to use providers and virtuals together. Example with a user audio setup:

Virtual audio service audio.user:

[main]
name = audio
description = Provides an audio server
type = provider

[options]
providers = [ alsa jack pulseaudio pipewire-pulse ]

Pulseaudio service pulseaudio.user:

[main]
name = pulseaudio
description = Pulseaudio server
type = longrun

[run]
build = path
execute = pulseaudio

Mpd service mpd.user:

[main]
name = mpd
description = MPD server
type = longrun

[run]
build = path
execute = mpd --no-daemon

[options]
dependencies =[ audio ]

This new proposal would require no additional syntax (as oppesed to the initial proposal).

How does it work?

When enabling mpd:

File settings

The file /home/user/.config/tt/virtuals/audio.conf might contain the system setting for that provider:

pulseaudio

The file /home/user/.config/tt/providers/mpd.conf might contain the service setting for any provider:

audio =[ pulseaudio ]
# more providers here

The service setting has priority over the system setting. The latter has priority over automatic choice of providers.

Multiple providers for one virtual

Optionally, multiple providers can be chosen via both system and service setting. For example a web service would require multiple database to work. However, udev and mdev could not work together, so there should be an option allow-multiple-providers (true by default).

Cogitri commented 3 years ago

The new proposal sounds better to me since we won't have to declare what services are providers of a certain functionality multiple times (for different services that need that functionality).

danyspin97 commented 3 years ago

The new proposal sounds better to me since we won't have to declare what services are providers of a certain functionality multiple times (for different services that need that functionality).

Yea, that's better. Just modifying a virtual would add a new provider.

One small change over the new proposal is to split dependencies and providers so that it would be simpler to implement (and easier to read too). requires key can be used for providers, for example.

danyspin97 commented 3 years ago

While implementing providers I've found a few things to better define and I'd like to write them down here first:

My idea to fix the third point would be to have differente keys for every require:

Each key corresponds to a different mapping in the config file; a single key/value format will be applied to requires-one and an array format will be applied to requires-multiple.

Regarding the second point, I haven't put much thought in it yet.

Cogitri commented 3 years ago

Not quite sure if I understand the need for requires-one and requires-multiple. At what point would I want to enforce in a service that only one of the providers is up? E.g. if I have a dependency on sql I wouldn't care if postgres and mariadb are both up and I don't really see when you would care.

danyspin97 commented 3 years ago

E.g. if I have a dependency on sql I wouldn't care if postgres and mariadb are both up and I don't really see when you would care.

It would be important for service restarting. If a service has only postgres as sql provider, I don't want to restart it when mariadb goes down.

requires-one and requires-multiple could be used to enforce a service policy. I.e. a service can only depend on one audio provider (requires-one) but a web app could depend on one or more sql databases (requires-multiple).

The user choose his own providers by writing to .config/tt/providers/<service>. A virtual added in requires-multiple would always read this config file as an array, while a virtual added in requires-single would be read a single value there.

By using requires, this file would always be treated as an array, leading to really strange (and mostly wrong) configurations. It would be easier to use tt, but it could not enforce this rule. I think the two configurations are equals in flexibility.

We could also use requires for the time being and add the distinction later, if we think it could be beneficial to services. (After all providers is the main blocker for adding services in tt-services).

EDIT 1:

While writing the implementation, I encountered another issue with requires-single and requires-multiple: they don't work in global settings. This means that the global setting .config/tt/virtual/<virtual_name> can be either: