diff --git a/lib/kamal/configuration.rb b/lib/kamal/configuration.rb index 5f1743bdd..8d989464b 100644 --- a/lib/kamal/configuration.rb +++ b/lib/kamal/configuration.rb @@ -47,7 +47,7 @@ def initialize(raw_config, destination: nil, version: nil, validate: true) @destination = destination @declared_version = version - validate! raw_config, example: validation_yml.symbolize_keys, context: "" + validate! raw_config, example: validation_yml.symbolize_keys, context: "", with: Kamal::Configuration::Validator::Configuration # Eager load config to validate it, these are first as they have dependencies later on @servers = Servers.new(config: self) diff --git a/lib/kamal/configuration/docs/configuration.yml b/lib/kamal/configuration/docs/configuration.yml index 895d2f847..fc9245c5a 100644 --- a/lib/kamal/configuration/docs/configuration.yml +++ b/lib/kamal/configuration/docs/configuration.yml @@ -2,13 +2,24 @@ # # Configuration is read from the `config/deploy.yml` # + +# Destinations +# # When running commands, you can specify a destination with the `-d` flag, # e.g. `kamal deploy -d staging` # # In this case the configuration will also be read from `config/deploy.staging.yml` # and merged with the base configuration. + +# Extensions +# +# Kamal will not accept unrecognized keys in the configuration file. +# +# However, you might want to declare a configuration block using YAML anchors +# and aliases to avoid repetition. # -# The available configuration options are explained below. +# You can use prefix a configuration section with `x-` to indicate that it is an +# extension. Kamal will ignore the extension and not raise an error. # The service name # This is a required value. It is used as the container name prefix. diff --git a/lib/kamal/configuration/validator.rb b/lib/kamal/configuration/validator.rb index bc443229c..c7ba0f727 100644 --- a/lib/kamal/configuration/validator.rb +++ b/lib/kamal/configuration/validator.rb @@ -15,11 +15,10 @@ def validate! def validate_against_example!(validation_config, example) validate_type! validation_config, Hash - if (unknown_keys = validation_config.keys - example.keys).any? - unknown_keys_error unknown_keys - end + check_unknown_keys! validation_config, example validation_config.each do |key, value| + next if extension?(key) with_context(key) do example_value = example[key] @@ -137,4 +136,18 @@ def with_context(context) ensure @context = old_context end + + def allow_extensions? + false + end + + def extension?(key) + key.to_s.start_with?("x-") + end + + def check_unknown_keys!(config, example) + unknown_keys = config.keys - example.keys + unknown_keys.reject! { |key| extension?(key) } if allow_extensions? + unknown_keys_error unknown_keys if unknown_keys.present? + end end diff --git a/lib/kamal/configuration/validator/configuration.rb b/lib/kamal/configuration/validator/configuration.rb new file mode 100644 index 000000000..102f2e5bb --- /dev/null +++ b/lib/kamal/configuration/validator/configuration.rb @@ -0,0 +1,6 @@ +class Kamal::Configuration::Validator::Configuration < Kamal::Configuration::Validator + private + def allow_extensions? + true + end +end diff --git a/test/configuration_test.rb b/test/configuration_test.rb index 6e83054b4..043a4c8cb 100644 --- a/test/configuration_test.rb +++ b/test/configuration_test.rb @@ -344,4 +344,12 @@ class ConfigurationTest < ActiveSupport::TestCase assert_raises(Kamal::ConfigurationError) { Kamal::Configuration.new(@deploy_with_roles.merge(retain_containers: 0)) } end + + test "extensions" do + dest_config_file = Pathname.new(File.expand_path("fixtures/deploy_with_extensions.yml", __dir__)) + + config = Kamal::Configuration.create_from config_file: dest_config_file + assert_equal config.role(:web_tokyo).running_traefik?, true + assert_equal config.role(:web_chicago).running_traefik?, true + end end diff --git a/test/fixtures/deploy_with_extensions.yml b/test/fixtures/deploy_with_extensions.yml new file mode 100644 index 000000000..7d6a9db27 --- /dev/null +++ b/test/fixtures/deploy_with_extensions.yml @@ -0,0 +1,24 @@ + +x-web: &web + traefik: true + +service: app +image: dhh/app +servers: + web_chicago: + <<: *web + hosts: + - 1.1.1.1 + - 1.1.1.2 + web_tokyo: + <<: *web + hosts: + - 1.1.1.3 + - 1.1.1.4 +env: + REDIS_URL: redis://x/y +registry: + server: registry.digitalocean.com + username: user + password: pw +primary_role: web_tokyo