Refactoring the access to the Jenkins services through a caching DAO layer

MiniCache is not thread-safe (derp) so smashing a Concurrent::Hash into it
should solve that problem

RUBY AMIRITE
This commit is contained in:
R. Tyler Croy 2017-09-04 09:05:53 -07:00
parent ce0ba7e520
commit 1ed83a6b72
No known key found for this signature in database
GPG Key ID: 1426C7DC3F51E16F
13 changed files with 157 additions and 33 deletions

1
.gitignore vendored
View File

@ -50,3 +50,4 @@ build-iPhoneSimulator/
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc
vendor/
*.html

View File

@ -1,7 +1,10 @@
source 'https://rubygems.org'
gem 'concurrent-ruby'
gem 'faraday'
gem 'faraday_middleware'
gem 'haml'
gem 'httparty'
gem 'mini_cache'
gem 'puma'
gem 'sentry-raven'
gem 'sentry-api'

View File

@ -1,9 +1,12 @@
GEM
remote: https://rubygems.org/
specs:
concurrent-ruby (1.0.5)
diff-lcs (1.3)
faraday (0.13.1)
multipart-post (>= 1.2, < 3)
faraday_middleware (0.12.2)
faraday (>= 0.7.4, < 1.0)
haml (5.0.2)
temple (>= 0.8.0)
tilt
@ -14,6 +17,7 @@ GEM
httparty (0.15.6)
multi_xml (>= 0.5.2)
mimemagic (0.3.2)
mini_cache (1.1.0)
multi_xml (0.6.0)
multipart-post (2.0.0)
mustermann (1.0.1)
@ -50,8 +54,11 @@ PLATFORMS
ruby
DEPENDENCIES
concurrent-ruby
faraday
faraday_middleware
haml
httparty
mini_cache
puma
rspec
sentry-api

3
Jenkinsfile vendored
View File

@ -12,6 +12,9 @@ pipeline {
steps {
sh 'make check'
}
always {
archiveArtifacts 'rspec.html'
}
}
stage('Build container') {
steps {

View File

@ -4,7 +4,10 @@ all: check container
check: spec check-container
spec: depends
./scripts/ruby bundle exec rspec -c
./scripts/ruby bundle exec rspec -c \
--order random \
--format progress \
--format html --out rspec.html
depends: Gemfile
./scripts/ruby bundle install

View File

@ -5,7 +5,7 @@ require 'raven'
require 'sinatra/base'
require 'sentry-api'
require 'canary/jenkins'
require 'canary/dao/jenkins'
module CodeValet
module Canary
@ -35,7 +35,7 @@ module CodeValet
:layout => :_base,
:locals => {
:projects => projects,
:jenkins => Jenkins,
:jenkins => DAO::Jenkins.new,
}
end

30
app/canary/dao.rb Normal file
View File

@ -0,0 +1,30 @@
require 'concurrent/hash'
require 'mini_cache'
module CodeValet
module Canary
# The DAO module contains some data-access objects which are to be used
# from the web tier.
module DAO
# Unless otherwise specified, cached entries should "live" for this
# number of seconds
DEFAULT_CACHE_SECONDS = 120
# Access the caching object
#
# @returs [MiniCache::Store]
def self.cache
return @cache unless @cache.nil?
@cache = MiniCache::Store.new
@cache.instance_variable_set(:@data, Concurrent::Hash.new)
return @cache
end
# Reset the cache, primarily intended for testing
def self.clear_cache
@cache = nil
end
end
end
end

50
app/canary/dao/jenkins.rb Normal file
View File

@ -0,0 +1,50 @@
require 'faraday'
require 'canary/dao'
module CodeValet::Canary::DAO
class Jenkins
# Base URL to contrict
URL_BASE = ENV['JENKINS_URL_BASE'] || 'https://codevalet.io'
CACHE_SECONDS = 300
attr_reader :error
def errored?
return !@error.nil?
end
def rebuiltAlpha
return cache.get_or_set('rebuiltAlpha', :expires_in => CACHE_SECONDS) do
rebuiltFor('codevalet')
end
end
def rebuiltGA
return cache.get_or_set('rebuiltGA', :expires_in => CACHE_SECONDS) do
rebuiltFor('rtyler')
end
end
# Method for directly querying a Jenkins instance to ask when it was last
# rebuilt
#
# @return [String] response from the server if we reached it
# @return [nil] nil on no response, #errored? should be set
def rebuiltFor(user)
# NOTE: worth investigating whether Jenkins will provide the appropriate
# caching headers to
url = "#{URL_BASE}/u/#{user}/userContent/builtOn.txt"
response = Faraday.get(url)
if response.success?
return response.body
end
@error = response.status
return nil
end
private
def cache
return CodeValet::Canary::DAO.cache
end
end
end

View File

@ -1,27 +0,0 @@
require 'httparty'
module CodeValet
module Canary
class Jenkins
URL_BASE = ENV['JENKINS_URL_BASE'] || 'https://codevalet.io'
# https://codevalet.io/u/codevalet/userContent/builtOn.txt
def self.rebuiltAlpha
return self.rebuiltUrlFor('codevalet')
end
def self.rebuiltGa
return self.rebuiltUrlFor('rtyler')
end
def self.rebuiltUrlFor(user)
url = "#{URL_BASE}/u/#{user}/userContent/builtOn.txt"
response = HTTParty.get(url)
if response.ok?
return response.body
end
return 'failed to retrieve last built information..'
end
end
end
end

43
spec/dao/jenkins_spec.rb Normal file
View File

@ -0,0 +1,43 @@
require 'spec_helper'
require 'canary/dao/jenkins'
describe CodeValet::Canary::DAO::Jenkins do
before :each do
CodeValet::Canary::DAO.clear_cache
end
context 'a bare instance' do
it { should_not be_errored }
context 'with a stubbed network call' do
let(:response) { 'stubbed!' }
before :each do
expect(subject).to receive(:rebuiltFor).and_return(response)
end
it 'provides a response on #rebuiltAlpha' do
expect(subject.rebuiltAlpha).to eql(response)
end
it 'provides a response on #rebuiltGA' do
expect(subject.rebuiltGA).to eql(response)
end
it 'should cache subsequent calls on #rebuiltAlpha' do
3.times do
expect(subject.rebuiltAlpha).to eql(response)
end
end
it 'should cache subsequent calls on #rebuiltGA' do
3.times do
expect(subject.rebuiltGA).to eql(response)
end
end
end
end
context 'with network errors' do
end
end

8
spec/dao_spec.rb Normal file
View File

@ -0,0 +1,8 @@
require 'spec_helper'
require 'canary/dao'
describe CodeValet::Canary::DAO do
it { should respond_to :cache }
it { should respond_to :clear_cache }
end

3
spec/spec_helper.rb Normal file
View File

@ -0,0 +1,3 @@
require 'rspec'
$LOAD_PATH.unshift << File.expand_path(File.dirname(__FILE__) + '/../app')

View File

@ -31,7 +31,7 @@
general availability
channel was last rebuilt on:
%code
= jenkins.rebuiltGa
= jenkins.rebuiltGA
.row
.col-md-12