-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Welcome
- Yes, I've searched similar issues on GitHub and didn't find any.
How do you use lego?
Library
Detailed Description
vancluever/terraform-provider-acme#235 describes an issue we're seeing in the Terraform ACME provider when multiple certificates that may share different configurations for the same DNS provider are created or renewed in parallel. The main issue lies in the fact that Terraform providers do not launch multiple processes for each resource being executed in a provider (if it did, this could scale to hundreds or even thousands of processes depending on the provider being utilized, which obviously would be bad). This means that multiple resources are going to clobber each other's DNS configurations via resetting of environment variables.
The current pattern for loading providers is using NewDNSProvider
in each provider package (see the factory in dns_providers.go
). The proposal is to provide another pattern side-by-side that will allow library consumers to supply a configuration to the constructor.
Proposal
The proposal is to provide a new function that would perform similar to NewDNSProvider
, but take a configuration:
func NewDNSProviderWithConfig(config map[string]string)
This would process the configuration and hand-off initialization as appropriate in each provider (example: via the NewDNSProviderConfig
function available to some providers).
Helpers
Helpers would be used to ensure that provider authors and maintainers do not need to maintain configuration reading. These would go in the platform/config
package tree.
Configuration reader
A configuration reader would take a struct, reflect on its annotated values, and either set them from the passed in map[string]string
, or read them from the environment.
An example configuration that the reader can take is below (an annotated version of the route53
provider):
type Route53Config struct {
AccessKeyId string `lego:"AWS_ACCESS_KEY_ID"`
SecretAccessKey string `lego:"AWS_SECRET_ACCESS_KEY"`
Region string `lego:"AWS_REGION"`
HostedZoneId string `lego:"AWS_HOSTED_ZONE_ID"`
MaxRetries int `lego:"AWS_MAX_RETRIES"`
TTL int `lego:"AWS_TTL"`
PropagationTimeout time.Duration `lego:"AWS_PROPAGATION_TIMEOUT"`
PollingInterval time.Duration `lego:"AWS_POLLING_INTERVAL"`
}
The reader would fall back to the existing EnvOrDefault
and _FILE
functionality to ensure backwards compatibility.
Capability identification
To identify that a provider package is capable of the new configuration structure, an assignable value will be placed in the same package as the rest of the helpers. The provider can then alias to this value:
const HasWithConfig = config.HasWithConfig
Provider factory generators can then use this value to generate factories with the new provider constructor:
// Original
case "route53":
return route53.NewDNSProvider()
// New (after checking for presence of route53.HasWithConfig)
case "route53":
return route53.NewDNSProviderWithConfig(configMap)
Abandoned ideas
I was originally thinking that we'd need another interface for this, but given that the existing API seems to be pattern-based, I think we can just get away with simply extending the pattern as outlined above and continuing to rely on code generation and factories to handle any differences between provider capabilities (if there ends up being any after implementation).