Skip to content

Commit

Permalink
DEV-951 Host Rights API in Kubernetes (#1)
Browse files Browse the repository at this point in the history
* DEV-951 Host Rights API in Kubernetes
- Update to Ruby 3.2
- Add tag and deployment workflows
- Add seeds for rights_log
- Add v1 version to paths
- Add rights_log route
- General refactoring

* Appease standardrb
  • Loading branch information
moseshll authored Oct 19, 2023
1 parent 850db8e commit 2b7a7ff
Show file tree
Hide file tree
Showing 18 changed files with 416 additions and 193 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/build-main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Build latest from main and deploy to staging

on:
workflow_run:
workflows: ['Run Tests']
branches: ['main']
types: [completed]

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- uses: hathitrust/github_actions/build@v1
with:
image: ghcr.io/${{ github.repository }}-unstable
dockerfile: Dockerfile.prod
tag: ${{ github.sha }}
push_latest: true
registry_token: ${{ github.token }}

# TODO: automate deployment w/ argocd
# for now - update image in
# https://github.com/hathitrust/ht_tanka/blob/main/environments/rights_api/staging/main.jsonnet
17 changes: 17 additions & 0 deletions .github/workflows/tag-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
name: Docker Tag Latest Release

on:
release:
types: [released]

jobs:
tag-release:
runs-on: ubuntu-latest
steps:
- uses: hathitrust/github_actions/tag-release@v1
with:
registry_token: ${{ github.token }}
existing_tag: ghcr.io/${{ github.repository }}-unstable:${{ github.sha }}
image: ghcr.io/${{ github.repository }}
new_tag: ${{ github.event.release.tag_name }}
8 changes: 1 addition & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
FROM ruby:3.1
FROM ruby:3.2
ARG UNAME=app
ARG UID=1000
ARG GID=1000

RUN apt-get update -yqq && apt-get install -yqq --no-install-recommends \
libxerces-c-samples \
netcat-traditional

# COPY Gemfile* /usr/src/app/
Expand All @@ -15,8 +14,3 @@ ENV BUNDLE_PATH /gems
RUN gem install bundler

RUN wget -O /usr/local/bin/wait-for https://github.com/eficode/wait-for/releases/download/v2.2.3/wait-for; chmod +x /usr/local/bin/wait-for



#
# COPY . /usr/src/app
18 changes: 18 additions & 0 deletions Dockerfile.prod
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM ruby:3.2
ARG UNAME=app
ARG UID=1000
ARG GID=1000

RUN gem install bundler
RUN groupadd -g $GID -o $UNAME
RUN useradd -m -d /usr/src/app -u $UID -g $GID -o -s /bin/bash $UNAME
RUN mkdir -p /gems && chown $UID:$GID /gems
USER $UNAME
COPY --chown=$UID:$GID Gemfile* /usr/src/app/
WORKDIR /usr/src/app
ENV BUNDLE_PATH /gems
ENV APP_ENV production
RUN bundle install
COPY --chown=$UID:$GID . /usr/src/app

CMD ["bundle", "exec", "ruby", "lib/rights_api/app.rb", "-o", "0.0.0.0"]
24 changes: 12 additions & 12 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ GEM
mysql2 (0.5.5)
nio4r (2.5.9)
parallel (1.23.0)
parser (3.2.2.3)
parser (3.2.2.4)
ast (~> 2.4.1)
racc
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
puma (6.3.1)
puma (6.4.0)
nio4r (~> 2.0)
racc (1.7.1)
rack (2.2.8)
Expand All @@ -41,7 +41,7 @@ GEM
rack-test (2.1.0)
rack (>= 1.3)
rainbow (3.1.1)
regexp_parser (2.8.1)
regexp_parser (2.8.2)
rexml (3.2.6)
rspec (3.12.0)
rspec-core (~> 3.12.0)
Expand All @@ -56,7 +56,7 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-support (3.12.1)
rubocop (1.56.2)
rubocop (1.56.4)
base64 (~> 0.1.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
Expand All @@ -70,12 +70,12 @@ GEM
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.29.0)
parser (>= 3.2.1.0)
rubocop-performance (1.19.0)
rubocop-performance (1.19.1)
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
sequel (5.72.0)
sequel (5.73.0)
bigdecimal
simplecov (0.22.0)
docile (~> 1.1)
Expand All @@ -95,20 +95,20 @@ GEM
rack-protection (= 3.1.0)
sinatra (= 3.1.0)
tilt (~> 2.0)
standard (1.31.0)
standard (1.31.2)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.0)
rubocop (~> 1.56.0)
rubocop (~> 1.56.4)
standard-custom (~> 1.0.0)
standard-performance (~> 1.2)
standard-custom (1.0.2)
lint_roller (~> 1.0)
rubocop (~> 1.50)
standard-performance (1.2.0)
standard-performance (1.2.1)
lint_roller (~> 1.1)
rubocop-performance (~> 1.19.0)
tilt (2.2.0)
unicode-display_width (2.4.2)
rubocop-performance (~> 1.19.1)
tilt (2.3.0)
unicode-display_width (2.5.0)

PLATFORMS
aarch64-linux
Expand Down
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,24 @@
```bash
git clone https://github.com/hathitrust/rights_api
docker-compose build
docker-compose run --rm web bundle install
docker-compose up
```

## Examples

Visit `http://localhost:4567/` for a usage summary.
```
http://localhost:4567/attributes
http://localhost:4567/reasons
http://localhost:4567/rights?htid=test.und_open
http://localhost:4567/v1/attributes
http://localhost:4567/v1/reasons
http://localhost:4567/v1/rights/test.und_open
http://localhost:4567/v1/rights_log/test.und_open
```
See `lib/rights_api/app.rb` for all of the Sinatra routes.

## TODO
- `rights_log` queries (requires changes to the rights_database gem)
- `db-image` seeds for `rights_log` (currently empty)
- Remove last vestiges of rights_database gem
- `/rights?...` query parameters other than HTID (dates, for example)
- `/rights_log?...` query parameters other than HTID (dates, for example)
- Results paging
- Add versioning to API routes
- Think carefully about exposing `rights_current.user` and `rights_current.note` in a public API

2 changes: 2 additions & 0 deletions config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require "./lib/rights_api/app"
run RightsAPI::App
13 changes: 3 additions & 10 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ services:
volumes:
- .:/usr/src/app
- gem_cache:/gems
command: bundle exec ruby rights_api.rb -o 0.0.0.0
command: bundle exec rackup --host 0.0.0.0 -p 4567
environment:
# SOLR_URL: http://solr-sdr-catalog:9033/solr/catalog
RIGHTS_DATABASE_CONNECTION_STRING: "mysql2://ht_rights:ht_rights@mariadb/ht"
depends_on:
# - solr-sdr-catalog
- mariadb

test:
Expand All @@ -25,19 +23,14 @@ services:
- gem_cache:/gems
command: bash -c "/usr/local/bin/wait-for mariadb:3306 && bundle exec rspec"
environment:
# SOLR_URL: http://solr-sdr-catalog:9033/solr/catalog
RIGHTS_DATABASE_CONNECTION_STRING: "mysql2://ht_rights:ht_rights@mariadb/ht"
depends_on:
# - solr-sdr-catalog
- mariadb

# solr-sdr-catalog:
# image: ghcr.io/hathitrust/catalog-solr-sample
# ports:
# - "9033:9033"

mariadb:
image: ghcr.io/hathitrust/db-image
volumes:
- ./sql/100_rights_log.sql:/docker-entrypoint-initdb.d/100_rights_log.sql
restart: always
environment:
MYSQL_RANDOM_ROOT_PASSWORD: 1
Expand Down
67 changes: 67 additions & 0 deletions lib/rights_api/app.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

require "sinatra"
require "sinatra/json"
require "sinatra/reloader" if development?

require_relative "query"

module RightsAPI
class App < Sinatra::Base
USAGE = <<~END_USAGE
API_URL => this usage summary
API_URL/access_profiles => contents of the access_profiles table
API_URL/access_profiles/1 => access_profile entry with id=1
API_URL/access_statements => contents of the access_stmts table
API_URL/access_statements/pd => access_stmts entry with stmt_key=pd
API_URL/attributes => contents of the attributes table
API_URL/attributes/1 => attributes entry with id=1
API_URL/reasons => contents of the reasons table
API_URL/reasons/1 => reasons entry with id=1
API_URL/rights/HTID => query rights_current for current rights on HTID
API_URL/rights_log/HTID => query rights_current for rights history on HTID
END_USAGE

STANDARD_TABLES = %w[attributes access_profiles access_statements reasons sources]

# Redirect to the current version
get "/" do
redirect(request.url + "v1/")
end

get "/v1/?" do
json({usage: USAGE})
end

get "/v1/rights/:htid" do |htid|
json RightsAPI.rights(htid)
end

get "/v1/rights_log/:htid" do |htid|
json RightsAPI.rights_log(htid)
end

# The "all" queries for most tables
STANDARD_TABLES.each do |name|
get "/v1/#{name}/?" do
json(RightsAPI.send(name.to_sym))
end
end

# The "by id" queries for most tables
STANDARD_TABLES.each do |name|
get "/v1/#{name}/:id" do |id|
data = (RightsAPI.send name.to_sym)[hash_key(name: name, key: id)]
data = {} if data.nil?
json data
end
end

private

# Most tables have integer primary keys, but access_stmts is indexed by string
def hash_key(name:, key:)
(name == "access_statements") ? key.to_s : key.to_i
end
end
end
68 changes: 68 additions & 0 deletions lib/rights_api/query.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# frozen_string_literal: true

require_relative "services"

module RightsAPI
def access_statements
@access_statements ||= db_connection[:access_stmts].to_hash(:stmt_key)
end

def access_profiles
@access_profiles ||= db_connection[:access_profiles].to_hash(:id)
end

def attributes
@attributes ||= db_connection[:attributes].to_hash(:id)
end

def reasons
@reasons ||= db_connection[:reasons].to_hash(:id)
end

def rights(htid)
rights_query(htid: htid, table: :rights_current)
end

def rights_log(htid)
rights_query(htid: htid, table: :rights_log)
end

def sources
@sources ||= db_connection[:sources].to_hash(:id)
end

module_function :access_profiles, :access_statements, :attributes, :reasons,
:rights, :rights_log, :sources

private

def db_connection
Services[:rights_database].db
end

# Common code for querying rights or rights_log and returning results
def rights_query(htid:, table:)
namespace, id = htid.split(".", 2)
result = []
db_connection[table]
.where(:namespace => namespace, Sequel.qualify(table, :id) => id)
.order(:time)
.each do |entry|
result <<
{
htid: entry[:namespace] + "." + entry[:id],
namespace: entry[:namespace],
id: entry[:id],
attribute: entry[:attr],
reason: entry[:reason],
source: entry[:source],
access_profile: entry[:access_profile],
time: entry[:time]
}
# Keep note and user out of structure for public API
end
result
end

module_function :db_connection, :rights_query
end
6 changes: 0 additions & 6 deletions lib/rights_api/rights_api.rb

This file was deleted.

Loading

0 comments on commit 2b7a7ff

Please sign in to comment.