Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

send User-Agent with platform and system information #214

Merged
merged 5 commits into from
Jan 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
### Added
- A CHANGELOG.md to track important changes
- User-Agent header with APIcast version and system information [PR #214](https://github.com/3scale/apicast/pull/214)

### Changed
- Require openresty 1.11.2 [PR #194](https://github.com/3scale/apicast/pull/194)
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ test-builder-image: builder-image clean ## Smoke test the builder image. Pass an
@echo -e $(SEPARATOR)
$(DOCKER_COMPOSE) run --rm test curl --fail -X POST http://gateway:8090/boot
@echo -e $(SEPARATOR)
$(DOCKER_COMPOSE) run --rm -e THREESCALE_PORTAL_ENDPOINT=https://echo-api.3scale.net gateway /opt/app/libexec/boot | grep lua-resty-http
$(DOCKER_COMPOSE) run --rm -e THREESCALE_PORTAL_ENDPOINT=https://echo-api.3scale.net gateway /opt/app/libexec/boot | grep 'APIcast/'
@echo -e $(SEPARATOR)

test-runtime-image: export IMAGE_NAME = apicast-release-test
Expand Down
18 changes: 15 additions & 3 deletions apicast/conf.d/apicast.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
set_by_lua $deployment 'return os.getenv("THREESCALE_DEPLOYMENT_ENV");';
set_by_lua $user_agent 'return require("user_agent")()';
set_by_lua_block $deployment {
local user_agent = require('user_agent')
return user_agent.platform() .. '+' .. user_agent.deployment()
}

# TODO: enable in the future when we support SSL
# ssl_certificate_by_lua_block { require('module').call() }
Expand All @@ -17,7 +21,8 @@ location = /threescale_authrep {
proxy_http_version 1.1;
proxy_pass $backend_endpoint$path;
proxy_set_header Host "$backend_host";
proxy_set_header X-3scale-User-Agent "nginx$deployment";
proxy_set_header User-Agent "$user_agent";
proxy_set_header X-3scale-User-Agent "$deployment";
proxy_set_header X-3scale-Version "$version";
proxy_set_header Connection "";

Expand Down Expand Up @@ -88,6 +93,9 @@ location = /_threescale/oauth_store_token {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host "$backend_host";
proxy_set_header User-Agent "$user_agent";
proxy_set_header X-3scale-User-Agent "$deployment";
proxy_set_header X-3scale-Version "$version";

proxy_pass $backend_endpoint/services/$service_id/oauth_access_tokens.xml?$backend_authentication_type=$backend_authentication_value;
}
Expand All @@ -98,14 +106,18 @@ location = /_threescale/check_credentials {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host "$backend_host";
proxy_set_header User-Agent "$user_agent";
proxy_set_header X-3scale-User-Agent "$deployment";
proxy_set_header X-3scale-Version "$version";

proxy_pass $backend_endpoint/transactions/oauth_authorize.xml?$backend_authentication_type=$backend_authentication_value&service_id=$service_id&$args;
}

location = /threescale_oauth_authrep {
internal;
proxy_set_header Host "$backend_host";
proxy_set_header X-3scale-User-Agent "nginx$deployment";
proxy_set_header User-Agent "$user_agent";
proxy_set_header X-3scale-User-Agent "$deployment";
proxy_set_header X-3scale-Version "$version";
proxy_set_header X-3scale-OAuth2-Grant-Type "authorization_code";

Expand Down
6 changes: 5 additions & 1 deletion apicast/src/apicast.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ local tonumber = tonumber
local math = math
local getenv = os.getenv
local reload_config = util.env_enabled('APICAST_RELOAD_CONFIG')
local user_agent = require('user_agent')

local _M = {
_VERSION = '0.1'
_VERSION = '2.0',
_NAME = 'APIcast'
}

local missing_configuration = getenv('APICAST_MISSING_CONFIGURATION') or 'log'
Expand All @@ -28,6 +30,8 @@ local function handle_missing_configuration(err)
end

function _M.init()
user_agent.cache()

math.randomseed(ngx.now())
-- First calls to math.random after a randomseed tend to be similar; discard them
for _=1,3 do math.random() end
Expand Down
3 changes: 3 additions & 0 deletions apicast/src/configuration_loader/remote_v1.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ local resty_url = require 'resty.url'
local http = require "resty.http"
local configuration = require 'configuration'
local util = require 'util'
local user_agent = require 'user_agent'

local _M = {
version = '0.1'
Expand Down Expand Up @@ -90,6 +91,8 @@ function _M.download(endpoint, _)
headers['Authorization'] = "Basic " .. ngx.encode_base64(concat({ user or '', pass or '' }, ':'))
end

headers['User-Agent'] = user_agent()

-- TODO: this does not fully implement HTTP spec, it first should send
-- request without Authentication and then send it after gettting 401

Expand Down
23 changes: 23 additions & 0 deletions apicast/src/module.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ function _M.call(self, phase, ...)
end

local cache = {}

function _M.flush()
cache = {}
end

local prequire = function(file)

if cache[file] then
Expand All @@ -60,6 +65,11 @@ local prequire = function(file)
ok, ret = pcall(dofile, file)
end

if type(ret) == 'userdata' then
ngx.log(ngx.WARN, 'cyclic require detected: ', debug.traceback())
return false, ret
end

if ok then
cache[file] = ret
else
Expand Down Expand Up @@ -105,4 +115,17 @@ function _M.load(name, phase)
return nil, 'could not load plugin'
end

function _M:require()
local name = self.name

if not name then
return nil, 'not initialized'
end

local ok, ret = prequire(name)

if ok and ret then return ret
else return ok, ret end
end

return _M
75 changes: 75 additions & 0 deletions apicast/src/user_agent.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
local ffi = require 'ffi'
local module = require 'module'

local setmetatable = setmetatable

local _M = {
_VERSION = '2.0'
}

function _M.deployment()
return _M.threescale_deployment_env or 'unknown'
end

-- User-Agent: <product> / <product-version> <comment>
-- User-Agent: Mozilla/<version> (<system-information>) <platform> (<platform-details>) <extensions>

function _M.call()
return 'APIcast/' .. _M._VERSION .. ' (' .. _M.system_information() .. ') ' .. _M.platform()
end

function _M.system_information()
return ffi.os .. '; ' .. ffi.arch .. '; env:' .. _M.deployment()
end

function _M.platform()
local m = module.new()
local table, err = m:require()
local version = table and table._VERSION
local name = table and table._NAME or m.name

if not name then
return nil, 'missing module name'
end

if not table and err then
return nil, err
end

if version then
return name ..'/'.. version
else
return name
end
end

local mt = {
__call = _M.call,
__tostring = _M.call
}

function _M.reset()
local env = {
threescale_deployment_env = os.getenv('THREESCALE_DEPLOYMENT_ENV')
}

mt.__index = env

_M.env = env

mt.__call = _M.call
mt.__tostring = _M.call
end

function _M.cache()
_M.reset()

local user_agent = _M.call()

mt.__call = function() return user_agent end
mt.__tostring = mt.__call
end

setmetatable(_M, mt)

return _M
12 changes: 1 addition & 11 deletions bin/busted
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
#!/usr/bin/env sh

exec resty -e "$(cat <<LUA
if ngx ~= nil then
ngx.exit = function()end
end

pcall(require, 'luarocks.loader')

-- Busted command-line runner
require 'busted.runner'({ standalone = false })
LUA
)"
exec resty bin/busted.lua "$@"
8 changes: 8 additions & 0 deletions bin/busted.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
if ngx ~= nil then
ngx.exit = function()end
end

pcall(require, 'luarocks.loader')

-- Busted command-line runner
require 'busted.runner'({ standalone = false })
15 changes: 15 additions & 0 deletions spec/module_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ describe('module', function()
end)

describe('.call', function()
after_each(function() module.flush() end)

it('returns', function()
package.loaded['foobar'] = { phase = function() return 'foobar' end }
Expand All @@ -24,4 +25,18 @@ describe('module', function()
end)

end)

describe(':require', function()
it('returns a module', function()
local foobar = { _VERSION = '1.1', _NAME = 'Foo Bar' }
package.loaded['foobar'] = foobar

local m = module.new('foobar')

local mod, err = m:require()

assert.equal(mod, foobar)
assert.falsy(err)
end)
end)
end)
71 changes: 71 additions & 0 deletions spec/user_agent_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
local user_agent = require 'user_agent'
local ffi = require("ffi")

describe('3scale', function()
before_each(function() user_agent.reset() end)

describe('.deployment', function()
it('reads from environment', function()
stub(os, 'getenv').on_call_with('THREESCALE_DEPLOYMENT_ENV').returns('foobar')

user_agent.reset()

assert.same('foobar', user_agent.deployment())
end)

it('uses internal structure', function()
user_agent.env.threescale_deployment_env = 'bar'

assert.same('bar', user_agent.deployment())
end)
end)

describe('.user_agent', function()
-- User-Agent: <product> / <product-version> <comment>
-- User-Agent: Mozilla/<version> (<system-information>) <platform> (<platform-details>) <extensions>

it('matches common format', function()

user_agent.env.threescale_deployment_env = 'production'

assert.match('APIcast/' .. user_agent._VERSION, user_agent.call())
end)

it('includes system information', function()
assert.match('(' .. user_agent.system_information() .. ')', user_agent.call())
end)

it('includes platform information', function()
assert.match(' ' .. user_agent.platform(), user_agent.call())
end)

it('works as tostring', function()
assert.equal(user_agent.call(), tostring(user_agent))
end)

it('works as function', function()
assert.equal(user_agent.call(), user_agent())
end)
end)


describe('.system_information', function()
it('includes os information', function()
assert.match(ffi.os ..'; ' .. ffi.arch, user_agent.system_information())
end)

it('includes deployment information', function()
user_agent.env.threescale_deployment_env = 'foobar'
assert.match(' env:foobar', user_agent.system_information())
end)
end)

describe('.platform', function()
it('includes os information', function()
local apicast = require('apicast')

assert.same('APIcast/' .. apicast._VERSION, user_agent.platform())
end)
end)

end)