Add rake task to populate Consul from dynamic_settings.yml
Fixes: CNVS-39293 Since we eliminated the pre-population functionality from our Consul wrapper we needed something to conveniently populate the KV store. Test Plan: - Start a Consul server - Run `bin/rake canvas:seed_consul` - Verify that values were written to the KV store. Change-Id: I340011b7d00ed4e3dd2918e3f101f6377fc72d7e Reviewed-on: https://gerrit.instructure.com/126574 Reviewed-by: Cody Cutrer <cody@instructure.com> Product-Review: Tyler Pickett <tpickett@instructure.com> QA-Review: Cody Cutrer <cody@instructure.com> Tested-by: Jenkins
This commit is contained in:
parent
7e932a4498
commit
099365aec1
|
@ -114,7 +114,7 @@ gem 'canvas_statsd', '2.0.4'
|
|||
gem 'statsd-ruby', '1.4.0', require: false
|
||||
gem 'aroi', '0.0.5', require: false
|
||||
gem 'gepub', '0.7.0beta4'
|
||||
gem 'imperium', '0.1.3', require: false
|
||||
gem 'imperium', '0.2.1', require: false
|
||||
gem 'academic_benchmarks', '0.0.10', require: false
|
||||
|
||||
gem 'graphql', '1.6.7'
|
||||
|
|
|
@ -5,33 +5,45 @@
|
|||
# shaped like the example below, one key for the related set of data,
|
||||
# and a hash of key/value pairs (no nesting)
|
||||
development:
|
||||
address-book:
|
||||
app-host: "http://address-book.docker"
|
||||
secret: "opensesame"
|
||||
canvas:
|
||||
encryption-secret: "astringthatisactually32byteslong"
|
||||
signing-secret: "astringthatisactually32byteslong"
|
||||
ha_cache: |
|
||||
cache_store: ha_store
|
||||
servers:
|
||||
- redis://localhost/2
|
||||
# keep stale data for up to 1 week in the cache
|
||||
race_condition_ttl: 604800
|
||||
# how long it might take to recompute a cache value
|
||||
# before the lock times out and another process is
|
||||
# allowed to write it
|
||||
lock_timeout: 5
|
||||
# how long before a cache entry is considered stale
|
||||
expires_in: 300
|
||||
live-events:
|
||||
aws_endpoint: http://kinesis.canvaslms.docker
|
||||
kinesis_stream_name: live-events
|
||||
live-events-subscription-service:
|
||||
app-host: "http://les.docker"
|
||||
sad-panda: null
|
||||
math-man:
|
||||
base_url: 'http://mathman.docker'
|
||||
use_for_svg: 'false'
|
||||
use_for_mml: 'false'
|
||||
rich-content-service:
|
||||
app-host: "rce.docker"
|
||||
# tree
|
||||
config:
|
||||
# service
|
||||
canvas:
|
||||
# environment
|
||||
address-book:
|
||||
app-host: "http://address-book.docker"
|
||||
secret: "opensesame"
|
||||
canvas:
|
||||
encryption-secret: "astringthatisactually32byteslong"
|
||||
signing-secret: "astringthatisactually32byteslong"
|
||||
live-events:
|
||||
aws_endpoint: http://kinesis.canvaslms.docker
|
||||
kinesis_stream_name: live-events
|
||||
live-events-subscription-service:
|
||||
app-host: "http://les.docker"
|
||||
sad-panda: null
|
||||
math-man:
|
||||
base_url: 'http://mathman.docker'
|
||||
use_for_svg: 'false'
|
||||
use_for_mml: 'false'
|
||||
rich-content-service:
|
||||
app-host: "rce.docker"
|
||||
# another service
|
||||
inst-fs:
|
||||
app-host: "http://inst-fs.docker"
|
||||
secret: "super-sekret-value"
|
||||
|
||||
private:
|
||||
canvas:
|
||||
ha_cache.yml: |
|
||||
cache_store: ha_store
|
||||
servers:
|
||||
- redis://localhost/2
|
||||
# keep stale data for up to 1 week in the cache
|
||||
race_condition_ttl: 604800
|
||||
# how long it might take to recompute a cache value
|
||||
# before the lock times out and another process is
|
||||
# allowed to write it
|
||||
lock_timeout: 5
|
||||
# how long before a cache entry is considered stale
|
||||
expires_in: 300
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# Consul
|
||||
|
||||
[Consul](https://www.consul.io/) is a service discovery and configuration
|
||||
management system from Hashicorp. Canvas currently only uses the configuration
|
||||
management k/v store. Generally speaking you won't need to run Consul during
|
||||
development (or even small production deployments) because we have full
|
||||
configuration support through `config/dynamic_settings.yml`.
|
||||
|
||||
For those who need to run Consul in development we've provided a docker-compose
|
||||
override file to start up a Consul server and a rake task to pre-populate the
|
||||
KV store from the values in `config/dynamic_settings.yml`. This rake task
|
||||
(`canvas:seed_consul`) will traverse the tree found in the config file and write
|
||||
the values found to the KV store, if a value already exists it will not be
|
||||
overwritten. Due to the change in population mechanisms we have also enabled
|
||||
persistence in the Consul container so users don't have to constantly refresh
|
||||
the values in the KV store.
|
||||
|
||||
## Enabling Consul
|
||||
To enable use of Consul with Docker there are three things that you need to do.
|
||||
|
||||
1. Add `docker-compose/consul.override.yml` to your `COMPOSE_FILE` env var.
|
||||
2. Un-comment the development consul configuration in `config/consul.yml`
|
||||
3. Run the rake task to pre-populate the config values in consul: `docker-compose run --rm web bin/rake canvas:seed_consul`
|
|
@ -5,21 +5,45 @@
|
|||
# shaped like the example below, one key for the related set of data,
|
||||
# and a hash of key/value pairs (no nesting)
|
||||
development:
|
||||
address-book:
|
||||
app-host: "http://address-book.docker"
|
||||
secret: "opensesame"
|
||||
live-events-subscription-service:
|
||||
app-host: "http://les.docker"
|
||||
sad-panda: null
|
||||
rich-content-service:
|
||||
app-host: "rce.docker"
|
||||
canvas:
|
||||
encryption-secret: "astringthatisactually32byteslong"
|
||||
signing-secret: "astringthatisactually32byteslong"
|
||||
live-events:
|
||||
aws_endpoint: http://kinesis.canvaslms.docker
|
||||
kinesis_stream_name: live-events
|
||||
math-man:
|
||||
base_url: 'http://mathman.docker'
|
||||
use_for_svg: 'false'
|
||||
use_for_mml: 'false'
|
||||
# tree
|
||||
config:
|
||||
# service
|
||||
canvas:
|
||||
# prefix
|
||||
address-book:
|
||||
app-host: "http://address-book.docker"
|
||||
secret: "opensesame"
|
||||
canvas:
|
||||
encryption-secret: "astringthatisactually32byteslong"
|
||||
signing-secret: "astringthatisactually32byteslong"
|
||||
live-events:
|
||||
aws_endpoint: http://kinesis.canvaslms.docker
|
||||
kinesis_stream_name: live-events
|
||||
live-events-subscription-service:
|
||||
app-host: "http://les.docker"
|
||||
sad-panda: null
|
||||
math-man:
|
||||
base_url: 'http://mathman.docker'
|
||||
use_for_svg: 'false'
|
||||
use_for_mml: 'false'
|
||||
rich-content-service:
|
||||
app-host: "rce.docker"
|
||||
# another service
|
||||
inst-fs:
|
||||
app-host: "http://inst-fs.docker"
|
||||
secret: "super-sekret-value"
|
||||
|
||||
private:
|
||||
canvas:
|
||||
ha_cache.yml: |
|
||||
cache_store: ha_store
|
||||
servers:
|
||||
- redis://redis/2
|
||||
# keep stale data for up to 1 week in the cache
|
||||
race_condition_ttl: 604800
|
||||
# how long it might take to recompute a cache value
|
||||
# before the lock times out and another process is
|
||||
# allowed to write it
|
||||
lock_timeout: 5
|
||||
# how long before a cache entry is considered stale
|
||||
expires_in: 300
|
||||
|
|
|
@ -8,8 +8,13 @@ services:
|
|||
- consul
|
||||
|
||||
consul:
|
||||
image: consul:0.7.2
|
||||
command: agent -dev -client 0.0.0.0 -datacenter canvas-dev -node canvas-consul -bootstrap
|
||||
image: consul:0.7.5
|
||||
command: agent -server -client 0.0.0.0 -datacenter canvas-dev -node canvas-consul -bootstrap -ui
|
||||
environment:
|
||||
GOMAXPROCS: "2"
|
||||
VIRTUAL_PORT: 8500
|
||||
volumes:
|
||||
- consul_data:/consul/data
|
||||
|
||||
volumes:
|
||||
consul_data: {}
|
||||
|
|
|
@ -101,7 +101,8 @@ module Canvas
|
|||
kv_client: kv_client)
|
||||
else
|
||||
proxy = root_fallback_proxy
|
||||
proxy = proxy.for_prefix(service) if service && service != :canvas
|
||||
proxy = proxy.for_prefix(tree)
|
||||
proxy = proxy.for_prefix(service)
|
||||
proxy = proxy.for_prefix(prefix) if prefix
|
||||
proxy
|
||||
end
|
||||
|
|
|
@ -119,6 +119,22 @@ namespace :canvas do
|
|||
ENV["COMPILE_ASSETS_API_DOCS"] = "0"
|
||||
Rake::Task['canvas:compile_assets'].invoke
|
||||
end
|
||||
|
||||
desc "Load config/dynamic_settings.yml into the configured consul cluster"
|
||||
task :seed_consul => [:environment] do
|
||||
def load_tree(root, tree)
|
||||
tree.each do |node, subtree|
|
||||
key = [root, node].compact.join('/')
|
||||
if Hash === subtree
|
||||
load_tree(key, subtree)
|
||||
else
|
||||
Imperium::KV.put(key, subtree, cas: 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
load_tree(nil, ConfigFile.load('dynamic_settings'))
|
||||
end
|
||||
end
|
||||
|
||||
namespace :lint do
|
||||
|
|
|
@ -106,8 +106,25 @@ module Canvas
|
|||
end
|
||||
|
||||
context "when consul is not configured" do
|
||||
let(:data) {
|
||||
{
|
||||
config: {
|
||||
canvas: {foo: {bar: 'baz'}},
|
||||
frobozz: {some: {thing: 'magic'}}
|
||||
},
|
||||
private: {
|
||||
canvas: {zab: {rab: 'oof'}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
before do
|
||||
DynamicSettings.config = nil
|
||||
DynamicSettings.fallback_data = data
|
||||
end
|
||||
|
||||
after do
|
||||
DynamicSettings.fallback_data = nil
|
||||
end
|
||||
|
||||
it 'must return an empty FallbackProxy when fallback data is also unconfigured' do
|
||||
|
@ -117,37 +134,34 @@ module Canvas
|
|||
end
|
||||
|
||||
it 'must return a FallbackProxy with configured fallback data' do
|
||||
DynamicSettings.fallback_data = {'foo' => {bar: 'baz'}}
|
||||
proxy = DynamicSettings.find('foo')
|
||||
proxy = DynamicSettings.find('foo', tree: 'config', service: 'canvas')
|
||||
expect(proxy).to be_a(DynamicSettings::FallbackProxy)
|
||||
expect(proxy[:bar]).to eq 'baz'
|
||||
end
|
||||
|
||||
it 'must treat a non-canvas service as a prefix' do
|
||||
DynamicSettings.fallback_data = {'service' => {'foo' => {bar: 'baz'}}}
|
||||
proxy = DynamicSettings.find('foo', service: 'service')
|
||||
expect(proxy[:bar]).to eq 'baz'
|
||||
it 'must default the the config tree' do
|
||||
proxy = DynamicSettings.find('foo', service: 'canvas')
|
||||
expect(proxy['bar']).to eq 'baz'
|
||||
end
|
||||
|
||||
it 'must ignore a nil service' do
|
||||
DynamicSettings.fallback_data = {'foo' => {bar: 'baz'}}
|
||||
proxy = DynamicSettings.find('foo', service: nil)
|
||||
expect(proxy[:bar]).to eq 'baz'
|
||||
it 'must handle an alternate tree' do
|
||||
proxy = DynamicSettings.find('zab', tree: 'private', service: 'canvas')
|
||||
expect(proxy['rab']).to eq 'oof'
|
||||
end
|
||||
|
||||
it 'must not treat explicit canvas service as a prefix' do
|
||||
DynamicSettings.fallback_data = {
|
||||
'foo' => {bar: 'baz'},
|
||||
'canvas' => {'foo' => {bar: 'quux'}}
|
||||
}
|
||||
proxy = DynamicSettings.find('foo', service: :canvas)
|
||||
expect(proxy[:bar]).to eq 'baz'
|
||||
it 'must default to the canvas service' do
|
||||
proxy = DynamicSettings.find('foo', tree: 'config')
|
||||
expect(proxy['bar']).to eq 'baz'
|
||||
end
|
||||
|
||||
it 'must accept an alternate service' do
|
||||
proxy = DynamicSettings.find('some', tree: 'config', service: 'frobozz')
|
||||
expect(proxy['thing']).to eq 'magic'
|
||||
end
|
||||
|
||||
it 'must ignore a nil prefix' do
|
||||
DynamicSettings.fallback_data = {'service' => {'foo' => 'bar'}}
|
||||
proxy = DynamicSettings.find(service: 'service')
|
||||
expect(proxy['foo']).to eq 'bar'
|
||||
proxy = DynamicSettings.find(tree: 'config', service: 'canvas')
|
||||
expect(proxy.for_prefix('foo')['bar']).to eq 'baz'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,8 +29,12 @@ describe MathMan do
|
|||
before do
|
||||
@original_fallback = Canvas::DynamicSettings.fallback_data
|
||||
Canvas::DynamicSettings.fallback_data = {
|
||||
'math-man': {
|
||||
base_url: service_url,
|
||||
config: {
|
||||
canvas: {
|
||||
'math-man': {
|
||||
base_url: service_url,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PluginSetting.create!(
|
||||
|
|
|
@ -115,8 +115,12 @@ describe "RequestContextGenerator" do
|
|||
Thread.current[:context] = nil
|
||||
Canvas::DynamicSettings.reset_cache!
|
||||
Canvas::DynamicSettings.fallback_data = {
|
||||
canvas: {
|
||||
'signing-secret' => shared_secret
|
||||
config: {
|
||||
canvas: {
|
||||
canvas: {
|
||||
'signing-secret' => shared_secret
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
env['HTTP_X_REQUEST_CONTEXT_ID'] = Canvas::Security.base64_encode(remote_request_context_id)
|
||||
|
|
Loading…
Reference in New Issue