architect-team/architect-cli

Support for config files

Closed this issue · 7 comments

There are a number of use-cases for allowing services to be configured via config or properties files instead of via direct environment variables. Sometimes this is because the configuration options are too substantial or complex, and other times it's due to the preferences of the service creator. In either case, configuration via files instead of direct parameters is something we'd like to support.

Proposal

There are two proposals for this syntax that we're currently contemplating: the first of which is a bit easier on the service creator, the second of which is a bit more explicit. We're currently seeking input on which of these two syntaxes would be preferred by developers.

Option 1 - File type parameters

The first option is to expose a new type option for parameters so they can forcibly represent files instead of direct values. This assertion would both indicate the service's readiness to look for the contents on the filesystem as well as instruct our platform to mount the file at the specified path.

# Service config
name: architect/api
parameters:
  PROPERTIES_FILE:
    type: file
    mount_path: /properties/default.properties
# Environment config
services:
  architect/api:
    ref: latest
    parameters:
      PROPERTIES_FILE: file:./config.properties

In the above example, the contents of ./config.properties would be stored in string value, but would be mounted to the path specified by the service upon deployment. An example of such a mount can be found in the way kubernetes mounts config maps to volumes.

Options 2 - Populate volumes from parameters

Option 2 is a bit more verbose and less magical. Instead of creating the volume for all parameters/credentials on behalf of the service, the service can mount any individual parameter to volumes they define explicitly. This would not require a parameter to indicate any type, but would require a service to define a volume explicitly as well as mount parameters to it:

# Service config
name: architect/api
parameters:
  PROPERTIES_FILE:
volumes:
  credentials:
    mount_path: /properties
    parameters:
      - parameter: PROPERTIES_FILE
        mount_path: default.properties
# Environment config
services:
  architect/api:
    ref: latest
    parameters:
      PROPERTIES_FILE: file:./config.properties

In the above example, the parameter properties file contents would be passed into the service as a string in the associated environment parameter AND would be mounted to the specified volume.

I don't like option1 because it overloads parameters when we already have a place for mounting to the filesystem in volumes. Option 2 is my vote because it'll be more familiar for docker/kubernetes users.

# Service config
name: architect/api
parameters:
  GCP_CREDENTIALS_FILE:
volumes:
  credentials:
    mount_path: /creds
    parameters:
      - parameter: GCP_CREDENTIALS_FILE
        mount_path: gcp-credentials.json  # mount_path might be confusing; maybe just path?

Might be better to keep it leaner and do the following:

# Service config
name: architect/api
parameters:
  GCP_CREDENTIALS_FILE:
volumes:
  credentials:
    mount_path: /creds/gcp-credentials.json
    parameter: GCP_CREDENTIALS_FILE

I'm a fan of 2 over 1 as well for the same reason as TJ. Mounting in parameters when we have volumes for that already seems out of place.

@davidthor Just want to clarify that in your examples PROPERTIES_FILE should be GCP_CREDENTIALS_FILE?

Assuming that, I'm generally in agreement with the other two. I think parameterizing files as volumes is closer to what a user would expect. However, in we seem to be using parameters as the primary reference to the string that will end up as the contents of the file. This took a bit of time for me to digest and I'm not sure it is the most intuitive. To clarify we might rename one of the keys below:

# Service config
name: architect/api
parameters:
  GCP_CREDENTIALS_FILE:
    description: ...
    required: true
volumes:
  credentials:
    mount_path: /creds/gcp-credentials.json
    contents_from: GCP_CREDENTIALS_FILE // <-- contents_from feels more clear than parameter

Further clarifying, an operator might choose to take the contents of a file OR just use an inline string, correct? To them, this parameter is no different from any other parameter... they don't care that it will later be written to a file, correct?

# One environment config
services:
  architect/api:
    ref: latest
    parameters:
      GCP_CREDENTIALS_FILE: file:./config.properties

# Some alternative config
services:
  architect/api:
    ref: latest
    parameters:
      GCP_CREDENTIALS_FILE: some-inline-string

Assuming the above, the service creator declares that their Service requires a parameter and also specifies that the contents of that parameter will be written to a file of their choosing. At that point, why are they not just writing an environment variable to a file in their service under the hood? Why does the service spec need to know about it at all?

Thanks for the feedback guys! Some of this has inspired some separate conversations about volumes which I'll be writing up, but it seems to the consensus is that folks prefer explicitness and are comfortable with having to declare a volume to fill.

There's more to digest in the comments above, but I want to focus on @dan-barrett's last comment specifically regarding the need (or lackthereof) for a feature like this. The reason I generally find option 1 favorable is because this file doesn't require persistence, but @dan-barrett rightly points out that developers are free to perform this write directly into the container filesystem at bootup to yield the same effect as writing it to a mounted volume. With that in mind, do we need to do anything at all to support config files? Here's an example of what a service creator could do TODAY:

# Service config
name: architect/api
parameters:
  PROPERTIES_FILE:
command: echo $PROPERTIES_FILE > /properties/default.properties && npm start

With the above in mind, it really seems like the only optimization we could provide is with knowing that the contents are intended to be written to a file so that we do NOT put it in an env parameter. That being said, this also seems like an extremely minor value-add in this regard.

How does everyone feel about the above compared to the prior options?

-- Edit --

I did just realize that the above would come with some redundancy when there are different commands used for local dev (e.g. hot reloading). I'd revise the above to the following in that case:

# Service config
name: architect/api
parameters:
  PROPERTIES_FILE:
command: echo $PROPERTIES_FILE > /properties/default.properties && npm start
debug:
  command: echo $PROPERTIES_FILE > /properties/default.properties && npm run dev

in the case of NPM or other similar tools, this logic could also be directly in the two start commands for simplicity.

They could define an entrypoint that does the above as to not conflict with the command. I'm fine with punting if you're happy with this objection handling.

It's hard for me to think otherwise given how lean that is to implement. I think we're better off documenting this example and moving on for now to focus on bigger issues.

Just opened an issue to create such a sample (#195). Closing.