Skip to content

Commit

Permalink
[standalone] introduce standalone policy
Browse files Browse the repository at this point in the history
  • Loading branch information
mikz committed Dec 5, 2018
1 parent 626579a commit 27b9c0e
Show file tree
Hide file tree
Showing 9 changed files with 770 additions and 0 deletions.
171 changes: 171 additions & 0 deletions examples/configuration/standalone.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
global:
log_level: debug
error_log: stderr
access_log: stdout
opentracing_tracer: jaeger
upstream:
load_balancer: least_conn
retry: 5xx
retry_times: 3

# we can pretend this would be nested inside `servers` block in the future
server:
# nginx config options like client_body_timeout or send_timeout
listen:
- port: 8090
name: management
- port: 8081
name: echo # and also fake backend
- port: 8080
name: default # default would be the default name
- port: 8089
name: default # default would be the default name
proxy_protocol: true
- port: 8443
# name: default # several listen could have the same name
protocol: http2 # | spdy | http
tls: true
- port: 8444
name: default
protocol: http2 # | spdy | http
proxy_protocol: true
tls:
protocols: TLSv1.3
# those two could be the defaults as policies have ssl_certificate phase
certificate: /var/run/secret/apicast/certificate.crt
certificate_key: /var/run/secret/apicast/certificate.key
ciphers: "HIGH:!aNULL:!MD5"
- port: 9421
name: prometheus
tls:
protocols: TLSv1.3
# those two could be the defaults as policies have ssl_certificate phase
certificate: /var/run/secret/apicast/certificate.crt
certificate_key: /var/run/secret/apicast/certificate.key
ciphers: "HIGH:!aNULL:!MD5"

routes:
- # Route object
name: management
match:
# Condition DSL to be defined by Rate limit policy and Conditional policy evaluation
server_port: management # otherwise would match the default
destination: # Destination DSL, AB testing, traffic split, etc. to be extended in the future
service: management
policy_chain: management
upstream: management

- name: echo
match:
server_port: echo # otherwise would match the default
destination:
service: echo

- match:
server_port: prometheus
routes:
- match:
uri_path: '/metrics'
http_method: 'GET'
destination:
service: prometheus

- match:
http_method: 'POST'
destination:
http_response: 405

# I'd like to treat this as a route tree.
# If it matches all conditions of this rule then we can route it deeper.
# But if it does not match the child rules we should reject the request with 404 (or with some other policy).
destination:
service: not_found

- match:
http_host: auth.example.com
destination:
service: auth-server

- match:
server_port: default # could be ommited, default would be the default
destination:
service: 3scale

- match:
always: true
destination:
service: not_found

internal: # TODO: if we can figure out better name than "service" we would make our life much easier, vhost?

- name: auth-server
policy_chain:
- policy: example.authentication.server
configuration:
redis: external://redis

- name: 3scale
policy_chain:
- policy: apicast.policy.load_configuration
- policy: apicast.policy.find_service
- policy: apicast.policy.local_chain

- name: simple
policy_chain:
- policy: example.authentication.client
configuration:
server: internal://auth-server

upstream: http://echo-api.3scale.net

- name: echo
policy_chain:
- logging
# - echo
# upstream: http://echo-api.3scale.net
upstream: external://echo

- name: backend # this is fake backend
policy_chain:
- echo

- name: management
policy_chain:
- policy: apicast.policy.management
configuration:
mode: debug

- name: prometheus
policy_chain:
- policy: apicast.policy.prometheus

- name: echo
policy_chain:
- policy: apicast.policy.cors
configuration:
allow_methods: GET
allow_origin: '*'
- policy: apicast.policy.echo

- name: logging
policy_chain:
- policy: log
configuration:
url: syslog://localhost
fields: url, path, client_ip

external: # kind of like egress, but it could also be an internal service
# an abstraction for stubbing out external services with policies (fake backend vs external service)
- name: backend
server: https://su1.3scale.net
load_balancer: least_conn

- name: echo
server: https://echo-api.3scale.net
load_balancer: least_conn

- name: redis
server: tcp://localhost:6879
load_balancer: least_conn
retries: 3

34 changes: 34 additions & 0 deletions gateway/config/standalone.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
local PolicyChain = require('apicast.policy_chain')
local resty_url = require('resty.url')
local format = string.format

local function to_url(uri)
local url = resty_url.parse(uri)

if url then
return uri
elseif uri then
return format('file:%s', uri)
end
end

local standalone = assert(PolicyChain.load_policy(
'apicast.policy.standalone',
'builtin',
{ url = to_url(context.configuration) }))

if arg then -- running CLI to generate nginx config
return {
template = 'http.d/standalone.conf.liquid',
standalone = standalone:load_configuration(),
configuration = standalone.url,
}

else -- booting APIcast
return {
policy_chain = PolicyChain.new{
standalone,
},
configuration = standalone.url,
}
end
58 changes: 58 additions & 0 deletions gateway/http.d/standalone.conf.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{% for server in standalone.server.listen %}
server {
listen {{ server.port }} default_server
{%- if server.tls %} ssl {% endif -%}
{%- case server.protocol -%}
{%- when 'http2' %}{{- server.protocol -}}
{%- when 'spdy' %}{{- server.protocol -}}
{%- endcase -%}
{%- if server.proxy_protocol %} proxy_protocol {% endif -%}
;

{% if server.tls.protocols -%}
ssl_protocols {{ server.tls.protocols | join: " " }};
{%- endif %}

{%- capture server_name -%}{{ server.name | default: 'default' }}{%- endcapture %}
set $port_name {{ server_name }};
server_name {{ server_name }};

set $ctx_ref -1;

location / {
rewrite_by_lua_block {
require('resty.ctx').stash()
require('apicast.executor'):rewrite()
}
access_by_lua_block { require('apicast.executor'):access() }
content_by_lua_block { require('apicast.executor'):content() }
post_action @post_action;
}

body_filter_by_lua_block { require('apicast.executor'):body_filter() }
header_filter_by_lua_block { require('apicast.executor'):header_filter() }

location @post_action {
internal;

proxy_pass_request_headers off;

rewrite_by_lua_block { require('resty.ctx').apply() }
content_by_lua_block { require('apicast.executor'):post_action() }
log_by_lua_block { require('apicast.executor'):log() }
}
}
{% endfor %}

{% for upstream in standalone.external %}
upstream {{ upstream.name }} {
server 0.0.0.1:1;

balancer_by_lua_block {
require('apicast.executor'):balancer('{{ upstream.load_balancer }}')
}

keepalive {{ upstream.keepalive | default: 64 }};
}

{% endfor %}
75 changes: 75 additions & 0 deletions gateway/src/apicast/policy/standalone/configuration.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
local resty_url = require('resty.url')

local setmetatable = setmetatable

local _M = {

}
local mt = { __index = _M }

local allowed_schemes = {
file = true,
-- TODO: support Data URI
}


function _M.new(uri)
local url, err = resty_url.parse(uri)

if not url then return nil, err end

if not allowed_schemes[url.scheme] then
return nil, 'scheme not supported'
end

return setmetatable({ url = url }, mt)
end

do
local loaders = { }
local path = require('pl.path')
local file = require('pl.file')

local YAML = require('lyaml')

local decoders = {
['.yml'] = YAML.load,
['.yaml'] = YAML.load,
}

local function decode(fmt, contents)
local decoder = decoders[fmt]

if not decoder then return nil, 'unsupported format' end
return decoder(contents)
end

function loaders.file(uri)
local filename = uri.opaque
if not filename then return nil, 'invalid file url' end

local ext = path.extension(filename)
local contents = file.read(filename)

if contents then
return decode(ext, contents)
else
return nil, 'no such file'
end
end

function _M:load()
local url = self and self.url
if not url then return nil, 'not initialized' end

local load = loaders[url.scheme]
if not load then return nil, 'cannot load scheme' end


return load(url)
end

end


return _M
1 change: 1 addition & 0 deletions gateway/src/apicast/policy/standalone/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
return require('standalone')
Loading

0 comments on commit 27b9c0e

Please sign in to comment.