Commit 7c149875 authored by Jan Včelák's avatar Jan Včelák 🚀
Browse files

import cleaned-up application

parent 1a5961da
PassengerFriendlyErrorPages off
if ENV.include? 'OPENSHIFT_APP_UUID'
source 'http://mirror1.ops.rhcloud.com/mirror/ruby/'
else
source 'https://rubygems.org'
end
gem 'jenkins_api_client'
gem 'mysql2'
gem 'sequel'
gem 'sinatra'
gem 'haml'
GEM
remote: https://rubygems.org/
specs:
haml (4.0.4)
tilt
jenkins_api_client (0.14.1)
json
mixlib-shellout (>= 1.1.0)
nokogiri (~> 1.5.0)
terminal-table (>= 1.4.0)
thor (>= 0.16.0)
json (1.8.1)
mixlib-shellout (1.2.0)
mysql2 (0.3.14)
nokogiri (1.5.10)
rack (1.5.2)
rack-protection (1.5.1)
rack
sequel (4.4.0)
sinatra (1.4.4)
rack (~> 1.4)
rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4)
terminal-table (1.4.5)
thor (0.18.1)
tilt (1.4.1)
PLATFORMS
ruby
DEPENDENCIES
haml
jenkins_api_client
mysql2
sequel
sinatra
updater: bundle exec ./update_builds.rb loop
The OpenShift `ruby` cartridge documentation can be found at:
https://github.com/openshift/origin-server/tree/master/cartridges/openshift-origin-cartridge-ruby/README.md
#!/usr/bin/env ruby
def usage
STDERR.puts "usage: %s <display-name> <jenkins-name> <jenkins-token>" % $PROGRAM_NAME
end
if ARGV.count != 3
usage
exit 1
end
require 'rubygems'
require 'securerandom'
require_relative './config'
new_project = CONFIG.db.job.new
new_project.title = ARGV.shift
new_project.name = ARGV.shift
new_project.jenkins_token = ARGV.shift
new_project.token = SecureRandom.urlsafe_base64(32)
new_project.save
puts "Project: %s" % new_project.title
puts "Jenkins: %s (token %s)" % [new_project.name, new_project.jenkins_token]
puts "Token: %s" % new_project.token
require_relative 'lib/app_config'
if ENV.include? 'OPENSHIFT_APP_UUID'
db_url = ENV["OPENSHIFT_MYSQL_DB_URL"].sub(/^mysql:/, "mysql2:")
db_url = File.join db_url, ENV["OPENSHIFT_APP_NAME"]
else
db_url = "mysql2://gitlab2jenkins:password@localhost/gitlab2jenkins"
end
CONFIG = AppConfig.new(
"https://gitlab.labs.nic.cz",
"https://jenkins.labs.nic.cz",
db_url
)
require 'rack/lobster'
require 'rubygems'
require 'bundler'
Bundler.require
map '/health' do
health = proc do |env|
[200, { "Content-Type" => "text/html" }, ["1"]]
end
run health
end
map '/lobster' do
run Rack::Lobster.new
end
map '/' do
welcome = proc do |env|
[200, { "Content-Type" => "text/html" }, [<<WELCOME_CONTENTS
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Welcome to OpenShift</title>
<style>
html {
background: black;
}
body {
background: #333;
background: -webkit-linear-gradient(top, black, #666);
background: -o-linear-gradient(top, black, #666);
background: -moz-linear-gradient(top, black, #666);
background: linear-gradient(top, black, #666);
color: white;
font-family: "Helvetica Neue",Helvetica,"Liberation Sans",Arial,sans-serif;
width: 40em;
margin: 0 auto;
padding: 3em;
}
a {
color: white;
}
h1 {
text-transform: capitalize;
-moz-text-shadow: -1px -1px 0 black;
-webkit-text-shadow: 2px 2px 2px black;
text-shadow: -1px -1px 0 black;
box-shadow: 1px 2px 2px rgba(0, 0, 0, 0.5);
background: #CC0000;
width: 22.5em;
margin: 1em -2em;
padding: .3em 0 .3em 1.5em;
position: relative;
}
h1:before {
content: '';
width: 0;
height: 0;
border: .5em solid #91010B;
border-left-color: transparent;
border-bottom-color: transparent;
position: absolute;
bottom: -1em;
left: 0;
z-index: -1000;
}
h1:after {
content: '';
width: 0;
height: 0;
border: .5em solid #91010B;
border-right-color: transparent;
border-bottom-color: transparent;
position: absolute;
bottom: -1em;
right: 0;
z-index: -1000;
}
h2 {
margin: 2em 0 .5em;
border-bottom: 1px solid #999;
}
pre {
background: black;
padding: 1em 0 0;
-webkit-border-radius: 1em;
-moz-border-radius: 1em;
border-radius: 1em;
color: #9cf;
}
ul {
margin: 0;
padding: 0;
}
li {
list-style-type: none;
padding: .5em 0;
}
.brand {
display: block;
text-decoration: none;
}
.brand .brand-image {
float: left;
border:none;
}
.brand .brand-text {
float: left;
font-size: 24px;
line-height: 24px;
padding: 4px 0;
color: white;
text-transform: uppercase;
}
.brand:hover,
.brand:active {
text-decoration: underline;
}
.brand:before,
.brand:after {
content: ' ';
display: table;
}
.brand:after {
clear: both;
}
</style>
</head>
<body>
<a href="http://openshift.com" class="brand">
<img class="brand-image"
alt="OpenShift logo"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAgCAYAAABU1PscAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAARHgAAER4B27UUrQAABUhJREFUWMPFWFlsVGUU/s5/70zbaSltA7RQpJ2lC9CFkQkWIgSJxkAhRA0JCYFq4hPG6JsoGKNCtPigxqhvGlPAuGIaE4igNaElbIW2yNL2tkOtTYGWCqWF2e79fCh7p1Bmpnge/3vuOef7z/nPJiTxMHS6pMRuu6YqFNTTAJYSyAU4GZB0AH2AGCANAfc5Qrba6T3HrmECScYLwCioSIcV2AjidQDZ45Q/LJRaWrLV03X89P8GwHB5XwG4DcDkGPWEBKimNrzN094efGQAzjm9GWHFr4R4LiHKgFaSL3r8zYcmHEBbkW+KFo7UEyhKsNeHlMgyV8eJo4kQpqId9ub6HCoc+XWcxl8lcBTATwDax8GfZtHa054/f/bNg8ZcnyOhHjBc834E8MJ9/vML8aYZQX1hd1PP3WFXkhMRfYkIlpOoGomc0WRRTnch+XAQWG2KTNJNLbuy68C/cQMwXOWrAKkdgz8A8kMdg9X5fn/gQcI7POXLaMk3AGbe/P8SbF0D1KcGRGXpIJJpIQkWBHhnsf/Ie3GF0DmnMxmQT8bg7RellXr8ze+Ox3gAcBvNf+iUUhH5FODLSvScAerDGpiVxTAyGUYKzICA34nCwbhDyHB7N4L8PAofhVzh9jfvjffR/ZZTnupIsR8G0C9EjW7Tfnii/dBgrPL0u83kmjHy33Z3Z/zG97uKi7xpWA8GHZpE1mcZRne8MvXblfbxqQAWR+Fp+mdW5hZPjAqu5JVlhrTwOgrXi2ABbjjchF4FYGvi0qhprgagjYod4OeldXWRWBUEtdBjEH4mwIJ7vF2V4Dqgot0+NEFdPAqmdZ5tAXA8Slx6LrpKsxMHQJge5ft1v0oe2OOu+PZ39+LCOFqImqiXo8JzAeBkXlnmnoKK9LgACJl2R9gELsHW1saUwKCpnbIoa8UMTokVgGXJmSjHkfNWUlWDy9d6USVdyoiEF8b1iElxQKHuPG1D/bCtVEBhCiykMQQFgCK2mN2sSx+tkdcbhGq7wKSkK9RnmsCG2xVSLsflAR1S6eloWhawtF8yGJGskSJDBdQR8pIjZMXcfFmm1gOg2lRaSRdT1AD1PBPQbCAyxcRMifCpc41HEtILNbh9s8SSvYTUmBp2LDGOdCOB1OD0XbeByWliwY5bugc9nU2T4wqhCx7PNAV9bSGwARp3TzVaP0j09GQUzJubLUgefY3SEHMh63MVr4FIlYL+7C1AlCwAmxM+/plYy6hhgN2xp1HBawAr72krnH3uoicTaXyHx7uIwKZoT0QhUhszAAI7x7ivL0a60/jp77yyTFrWt6N6rxE99c7OkxdiBhC2y/cAorXHpama/aNG8dkOO32b6p3zTzXmeysfPu4LkkKafA3IrGjfCfPtuGfiPlfx+xBsuWtwpa3zIuy2YaoZ5o0eSQc5TVnb53aeeAuk9eBtRvkqUH0MoTsqA7nL429eFzeA3lyfQ08eaiNgCrjTYNozQ1S+WyUfQCosTLqZ+oiDUNwhggPujpZTuCMXGwUV6cJgKYnNIJffR3df2NLLZ5871puQrUR//pzpU7rOnAfJP53eDELrsoPpk4RIGRn5xqIBAAdBOCAoBjBjPJsJUdZSt9HSOGFrld5cn2M4KbwfkIUJzqYhQlYWdJ7YN2FrFQCY3nPsmk61AuSuRNYyUdaiRBk/7tViR37Zcir1JYC8WNshgjWWPfhq0dmzVx/5bhQAWnLKU1Md8gZHOsjxAgmD2GEKq4s6m1sxASQPu16HiBh53goqPg9ac0TEcwNQEOBlQAZEcMgC94dDZt2c7r8GMIH0H43ZRDC51RVCAAAAAElFTkSuQmCC">
<div class="brand-text"><strong>Open</strong>Shift</div>
</a>
<h1>
Welcome to OpenShift
</h1>
<p>
Place your application here
</p>
<p>
In order to commit to your new project, go to your projects git repo (created with the rhc app create command). Make your changes, then run:
</p>
<pre>
git commit -a -m 'Some commit message'
git push
</pre>
<p>
Then reload this page.
</p>
<h2>
What's next?
</h2>
<ul>
<li>
Why not visit us at <a href="http://www.openshift.com">http://www.openshift.com</a>, or
</li>
<li>
You could get help in the <a href="http://www.openshift.com/forums/openshift">OpenShift forums</a>, or
</li>
<li>
You're welcome to come chat with us in our IRC channel at #openshift on freenode.net
</li>
</ul>
</body>
</html>
WELCOME_CONTENTS
]]
end
run welcome
end
require './web.rb'
run Sinatra::Application
jenkins:
url: https://jenkins.labs.nic.cz
require_relative 'builds_database'
require_relative 'jenkins_connector'
class AppConfig
attr_reader :gitlab_url, :jenkins_url, :db_url
attr_reader :db, :jenkins
def initialize(gitlab_url, jenkins_url, db_url)
@gitlab_url = gitlab_url
@jenkins_url = jenkins_url
@db_url = db_url
@db_connection = Sequel.connect db_url
@db = BuildsDatabase.new @db_connection
@jenkins = JenkinsConnector.new jenkins_url
end
end
require 'sequel'
class BuildsDatabase
attr_reader :job, :build
def initialize(db_connection)
@db = db_connection
create_schema
require_relative "builds_model"
@job = Job
@build = Build
end
def create_schema
create_jobs_table unless @db.table_exists?(:jobs)
create_builds_table unless @db.table_exists?(:builds)
end
def create_jobs_table
@db.create_table(:jobs) do
column :id, :integer, :unsigned => true, :primary_key => true, :auto_increment => true
column :name, :varchar, :length => 128, :null => false, :unique => true
column :title, :varchar, :length => 128, :null => false
column :token, :varchar, :length => 128, :null => false
column :jenkins_token, :varchar, :length => 128, :null => false
end
end
def create_builds_table
@db.create_table(:builds) do
column :id, :integer, :unsigned => true, :primary_key => true, :auto_increment => true
foreign_key :job_id, :jobs, :type => :integer, :unsigned => true, :null => false, :on_delete => :restrict, :on_update => :cascade
column :commit, :varchar, :length => 40, :null => false
column :build, :integer, :null => true
column :status, :varchar, :length => 128, :null => true
column :created_at, :timestamp, :null => false, :default => Sequel::CURRENT_TIMESTAMP
column :branch, :varchar, :length => 128, :null => true
end
end
end
class BuildsDatabase
private
class Job < Sequel::Model
one_to_many :builds, :order => Sequel.desc(:created_at)
def self.get_authenticated(id, token)
first id: id, token: token
end
end
class Build < Sequel::Model
many_to_one :job
def is_success?
status == "success"
end
end
end
require "jenkins_api_client"
module JenkinsApi
class Client
class Job
def build_with_token(job_name, token, params={})
msg = "Building job '#{job_name}' with token authentication"
msg << " and parameters: #{params.inspect}" unless params.empty?
@logger.info msg
build_endpoint = params.empty? ? "build" : "buildWithParameters"
raw_response = false
@client.api_post_request(
"/job/#{job_name}/#{build_endpoint}?token=#{token}",
params,
raw_response
)
end
end
end
end
require "jenkins_api_client"
require_relative "jenkins_api_extension"
class JenkinsConnector
def initialize(server_url)
@jenkins = JenkinsApi::Client.new server_url: server_url
end
def submit_build(job, commit, branch)
@jenkins.job.build_with_token(job.name, job.jenkins_token, { :commit => commit, :branch => branch })
end
def refresh_builds(job)
builds = @jenkins.job.get_builds job.name
builds.each do |build|
refresh_build job, build["number"]
end
end
private
def refresh_build(job, build_number)
build = job.builds_dataset.first(build: build_number)
# nothing new to update
return if build and final_status?(build.status)
# download build details
build_details = @jenkins.job.get_build_details(job.name, build_number)
build_info = extract_build_info(build_details)
# commit number not specified or not in complete form
return if build_info[:commit].nil?
# try to find submitted build without build number assigned
if build.nil?
build = job.builds_dataset.first(commit: build_info[:commit], build: nil)
end
if build.nil?
create_new_build job, build_info
elsif not final_status?(build.status)
update_existing_build job, build_info, build
end
end
def create_new_build(job, info)
build = job.add_build(info)
build.save
end
def update_existing_build(job, info, build)
build.build = info[:build]
build.status = info[:status]
build.branch = info[:branch] if build.branch.nil?
build.save
end
def extract_build_info(data)
result = {
:build => data["number"],
:status => build_status_from_api(data["result"]),
:created_at => nil,
:commit => nil,
:branch => nil
}
if not data["timestamp"].nil?
result[:created_at] = Time.at(data["timestamp"] / 1000)
end
fallback = {:commit => nil, :branch => nil}
data["actions"].each do |action|
if action.has_key?("lastBuiltRevision")
last_built_revision = action["lastBuiltRevision"]
result[:commit] = last_built_revision["SHA1"]
last_built_revision["branch"].each do |branch_info|
if branch_info["SHA1"] == result[:commit]
result[:branch] = branch_info["name"] unless branch_info["name"] == "detached"
break
end
end
break
elsif action.has_key?("parameters")
action["parameters"].each do |parameter|
fallback[:commit] = parameter["value"] if parameter["name"] == "commit"
fallback[:branch] = parameter["value"] if parameter["name"] == "branch"
end
end
end
if result[:commit].nil? and fallback[:commit] =~ /^[0-9a-f]{40}$/
result[:commit] = fallback[:commit]
end
if result[:branch].nil? and not fallback[:branch].nil?
result[:branch] = fallback[:branch]
end
result
end
def build_status_from_api(api_string)
if api_string.nil?
nil
else
case api_string.downcase
when "success" then "success"
when "aborted" then "canceled"
when "running" then "running"
else "failed"
end
end
end
def final_status?(status)
["success", "canceled", "failed"].include? status
end
end
#!/usr/bin/env ruby
if ARGV.count != 1 or not ["once", "loop"].include?(ARGV[0])
STDERR.puts "usage: %s (once|loop)" % $PROGRAM_NAME
exit 1
end
require_relative './config'
POLL_TIMEOUT = 30
ERROR_TIMEOUT = 600
infinite = ARGV[0] == "loop"
while true do
error = false
CONFIG.db.job.each do |job|
begin
CONFIG.jenkins.refresh_builds(job)
rescue Exception => e
error = true
STDERR.puts "Exception: #{e.to_s}"
end
end
break unless infinite
if error
sleep ERROR_TIMEOUT
else
sleep POLL_TIMEOUT
end
end
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment