Configuring Services with Deploy-Time or Run-Time Arguments

v0-6
todo
proposal

#1

There are two types of configurations related to configuring services.

1- Configuring service tasks/events, it’s public ports, data volumes etc. from the mesg.yml. (we have this feature).

2- Configuring service’s program with its own runtime cli flags. (we do this within the Dockerfile of service while running it.)

Currently, these two types of configurations can be only done by changing the source code of services. And we don’t support ability of somehow setting cli flags of service’s program within the mesg.yml file. Since mesg.yml is the place where we describe and configure services, I think, we should also support configuring runtime of service’s program inside of it too.

Purpose if this proposal is to discuss both to support configuring services dynamically without changing their source code by using some kind of runtime or build-time arguments for MESG services. If we have this feature, as a second, we can support providing configurations to runtime of service’s program, this way program can be dynamically configured as well.

Real world case about why this is needed:

As a user, let’s say that I have a workflow file and I want to use service-http-server for serving a vuejs app and a rest api but from different ports. To do that, service-http-server should be configurable to set a port address without a need of changing its source code because, I don’t want to fork it and create two different branches to setup different ports. I want to have the full power of reusable services by passing configurations dynamically.

So, for this case, I need to be able to both configure this part in the yml file and this flag in the Dockerfile dynamically.

Check the sample mesg.yml and Dockerfile below for see this proposal in actual code:
- Note that, to configure service’s program dynamically from the mesg.yml, I followed the pattern of using build args of Docker. Every arg provided while deploying/starting MESG services also directly mapped to Docker’s build time args

command:

$ mesg-core service deploy --arg serverAddr=2300 https://github.com/ilgooz/service-http-server
// outputs a SERVICE_ID

// or

$ mesg-core service start --arg serverAddr=2300 SERVICE_ID
// outputs an ANOTHER_SERVICE_ID

mesg.yml:

...
name: ...
description: ...
env:
  # we can also support env vars that will be accessed by service's program on runtime
  # as next to configuring service's program with Docker's build time args.
  AN_ENV_VAR: 123
configuration:
  ports:
    - $serverAddr
...

Dockerfile:

CMD website --serverAddr $serverAddr

We discussed on Discord and kinda agreed on providing runtime arguments to MESG services with the service start/deploy command and they can be accessed with $ sign in the yml file. Every argument should be also mapped to Docker’s build time arguments. But of course, every change on args must cause service to get a different service id.

By having this configuration features, we can also support giving configurations for services in the workflow files easily like this:
sample workflow.yml:

...
services:
  website:
    id: https://github.com/ilgooz/service-http-server/
    args:
      serverAddr: 2300

  restapi:
    repo: https://github.com/ilgooz/service-http-server/
    args:
      serverAddr: 3300

when:
  ...

Introducing Workflow Files
Milestone v0.6
#2

I really like the idea, just to have something else to thing about why not using the structure of the yaml as key.
In you case it would be something like

mesg-core service deploy --configuration.ports.0 2300 https://....

We could like that override any configuration of a service and not only the one that the developer allowed and because it will be a new hash we shouldn’t have any issue because that the responsibility of the user who configures it.
This could help for many things like using a different alias for a service that we deploy like mesg-core service deploy --alias xxx ...

It’s not really sexy interface when you have to go really nested but that might be really flexible


#3

Good summary of the problem @ilgooz :+1:

My suggestion on this system are:

  • Add a new key to the mesg.yml to explicitly define the name and default value of the configuration that the service is compatible with. Because the key configuration is already used for configuring the service’s container, with need another name (or change it to container?). It could be “variable”, “userConfig”, “argument” (suggestion accepted :wink: ).
    It should look like:
name: ...
description: ...
variable:
  myVariable: 123
# variable name: default value

With this knowledge, the core could check that the a user’s config is defined by the service.

  • Make mesg.yml configurable by using yaml variable if possible, otherwise we can use a template system like $variable (@ilgooz suggestion) or {{variable}}.
configuration:
  ports:
    - *myVariable
    - $myVariable
    - {{myVariable}}
  • Inject config as env variable in the service (or use Docker’s secret or config) so the service’s app can also access the config

  • Do not inject config in Dockerfile. The Dockerfile should be as generic as possible.

  • Accept user’s config only in the deploy command. As the user’s config must change the service ID, deploy command is the right place.


#4

I don’t think it’s a good idea.

We don’t want users to be able to re-configure everything in the mesg.yml. I think it will allow malicious use of a service and it doesn’t solve the problem of passing configuration to the service’s application.
If users want to change so much a mesg.yml, they can download the source and change it themself.

But I suggest to allow basic override of some variables, like the future alias, maybe even name and description, but that’s it. Only “cosmetic” stuff should be customizable without the approval of the service developer.


#5

Also, if the mesg.yml is compatible with a template system, we could imagine future advance configuration of a service.
For example, the service-ethereum has 2 version of the mesg.yml:
Master’s version:


And, RSK version:

As you see, the block dependencies is only present in the RSK version. If we have a template system, we could have only 1 version of the mesg.yml that render different version based on config:

name: "Ethereum"
description: "Ethereum Service to interact with any Smart Contract"
dependencies:
{{ if myVariable == "rsk" }}
  rskclient:
    image: regtest
    volumes:
      - /var/lib/rsk
    ports:
      - 4444:4444
      - 30305:30305
{{ endif }}

Again, this is not something to implement in priority, let’s just keep it in mind what a template system could do on mesg.yml.


#6

2- Configuring service’s program with its own runtime cli flags. (we do this within the Dockerfile of service while running it.)

I don’t like it as cli is not user-friendly for complex types like hash map etc

1- Configuring service tasks/events, it’s public ports, data volumes etc. from the mesg.yml . (we have this feature).

I like it more but it will be hard to manage,even with template system.

Imagine someone has configs for dev, staging, preprod, prod, test and it rather standard environment and there are more complex ones. In that case, mesg.yml will become one big template. Also sometimes one doesn’t want to show prod config for devs…

So I like the idea of having config separated from mesg.yml. In that case you create for example
config.dev, config.staging, config.prod, config etc…


#7

I think, having a template system built-in introduces too much complexity and final yaml file becomes to invalid which causes to a poor UX when yaml editors are used.

But users still can use a template engine to dynamically create mesg.yml files depending on their logic. And they can have multiple yml files in their repo with different names. We just need to provide the possibility of accepting custom names for yml files with a flag in the deploy command. e.g.:

mesg-core service deploy --definition mesg-dev.yml /path/to/service

#8

Just got some random thoughts around that.

I like the idea of having having multiple mesg.yml, I would suggest to get some inspiration from docker-compose where by default it’s docker-compose.yml but you can override by giving another file. So for the case of the service ethereum (which is one really complex one) we can have a “basic” mesg.yml file and one just for the dependencies and running it like that
mesg-core service deploy --file mesg.yml --file testnet.yml ...

Also passing arguments is a good solution, for smaller needs, and this would be similar to the docker build arguments (and could use the docker build arguments) and would be used like that
mesg-core service deploy --args argX=1234 ...
This argument would be passed to the docker build and also replaced in the mesg.yml.

Of course this can be combined with
mesg-core service deploy --file mesg.yml --file testnet.yml --args privateKey=xxx ...

I think args are more important because they can cover way more features (eg: the image for the ethereum node or the volume etc…) so this might be enough and also the good point is that these arguments can also be injected in the docker build.


#9

Summary of first iteration

Allow user to pass user configuration to the service.

Update mesg.yml

Add environment to configuration in mesg.yml. (So environment will also be available in dependencies).

configuration:
  env:
    - key: AN_ENV_VAR_FOR_SERVICE
      name: "Set the endpoint of blablabla...." // user friendly name. Optional
      value: 123 // Developer defined value, eg: default value.
dependencies:
  depA:
    env:
      - key: AN_OTHER_ENV_VAR_FOR_DEP_A
        name: "Set the endpoint of blablabla...." // user friendly name. Optional
        value: 123 // Developer defined value, eg: default value.

Env can be a new struct (it’s too different from the parameter struct).

Pass flag to deploy command

Allow user to pass env flag to override environment (only for configuration, NOT for dependencies).

mesg-core service deploy SERVER_URL/PATH --env AN_ENV_VAR=yolo

Save env variable

The modification of ENV must change the service’s hash. So adding them to the service structure will at the same time both save them in the db and change the service hash.
Also, I think it’s best to not override the value in the configuration.env struct. @krhubert implement it by adding a EnvValue in the dependency struct (like the env def). I’m fine with this.

Inject it in the service’s container

Inject the env as env in the container. Make sure that “MESG” env override developer env.
Only the service dependency should accept the user’s env, not any other dependencies.


Env definition as object
assigned krhubert #10

#11

I suggested in the pull request something more traditional

env:
  - XXX=YYY

docker compose style https://docs.docker.com/compose/environment-variables/

This is kind of traditional and also really easy to use a file based on that and include variables and stuff like that.


#12

As said by @Anthony, we could start by a very simple definition of env in the mesg.yml, and in a future PR, improving the system by allowing both key=value and full struct (for advance use) in the mesg.yml.

@krhubert what do you think?


#13

I’ve created a tutorial (in draft) about this feature, we can publish it after a new release! https://medium.com/@ilgooz/configuring-mesg-services-in-the-deploy-time-b61b6d56eb2f


#14

Very good initiative :slight_smile: :+1:


#15

@Nicolas suggested on the related PR:
To do in other PRs:


#16

Done in https://github.com/mesg-foundation/core/pull/666


#17

Topic solved by PR https://github.com/mesg-foundation/core/pull/641


unassigned krhubert #18