Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
GrooveCS committed May 11, 2021
0 parents commit 327258e
Show file tree
Hide file tree
Showing 17 changed files with 457 additions and 0 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
10 changes: 10 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Tells the .editorconfg plugin to stop searching once it finds this file
root = true

[*]
indent_size = 2
indent_style = space
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
42 changes: 42 additions & 0 deletions .github/workflows/build-and-push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# This is a basic workflow to help you get started with Actions

name: Build and Push to Quay

on:
push:
branches: [ master ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Source to Image Build
uses: redhat-actions/s2i-build@v2
id: build_image
with:
builder_image: registry.access.redhat.com/ubi8/nodejs-12
image: openshift-s2i-typescript-example
# The tags of the image to build, separated by a space
tags: latest ${{ github.sha }}
# List of environment variable key-value pairs to pass to the s2i builder context
env_vars: HUSKY_SKIP_INSTALL=1

- name: Push To Quay Action
uses: redhat-actions/push-to-registry@v2
with:
image: ${{ steps.build_image.outputs.image }}
tags: ${{ steps.build_image.outputs.tags }}
registry: quay.io/evanshortiss
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
node_modules
package-lock.json
.DS_Store

# These are generated - safe to ignore
*.js
*.js.map
*.d.ts

# Nodeshift and other temp files
tmp
openshift.local.clusterup
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v12.16.1
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM node:12-alpine

WORKDIR /usr/app
98 changes: 98 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# TypeScript Sample Application

This repository provides a simple starting point for running TypeScript
applications on OpenShift. It can also be applied to applications that use
Babel or other transpilers.

A blogpost that runs through the details of this repository and OpenShift can
be found [here](http://evanshortiss.com/development/openshift/javascript/typescript/2018/02/15/ts-on-openshift.html).

You can use this repository as a template, just click the green "Use this
template" button at the top of this page on GitHub.

## Running on OpenShift via NodeShift
To use this method of deployment you'll need:

* Node.js v12 or later
* OpenShift 4.x (Run OpenShift 4.x locally using [CodeReady Containers](https://developers.redhat.com/products/codeready-containers/overview))

NodeShift is a neat CLI that simplifies deployment of Node.js applications on
OpenShift. This project incldues NodeShift in `devDependencies`.

You can run the following to deploy it on an OpenShift instance:

```
$ git clone [email protected]:evanshortiss/openshift-typescript-example.git ts-openshift
$ cd ts-openshift
# Ensure you are logged into your openshift instance
$ oc login
# Choose the project you'd like to deploy this applicaion into
$ oc new-project ts-example
# Build, deploy, and expose an endpoint for the service
$ npm run nodeshift -- --expose
```

If you're deploying on a locally running instance of OpenShift you might need
to do the following to bypass the self-signed certificate issues:

```
$ npm run nodeshift -- --expose --strictSSL=false
```

## Run Locally without Docker
To run this application locally you'll need:

* Node.js v12 or later
* npm v6 or later
* Git

Execute the following commands to start the program locally:

```
git clone [email protected]:evanshortiss/openshift-typescript-example.git ts-openshift
cd ts-openshift
npm instal
npm run build
npm start
```

If you're developing locally, start a live reload server like so:

```
npm run start-dev
```

## Build Locally using Source-to-Image (s2i)
To perform the following steps you'll need:

* [Docker](https://docs.docker.com/release-notes/) (v19.x tested)
* [s2i](https://github.com/openshift/source-to-image/releases) (v1.1.13 tested)

With both tools installed, execute the following commands to run your
application locally. This will create a container that matches the one created
using an OpenShift Build.

```bash
# Run the s2i build script
./scripts/s2i.sh

# Run the container image
docker run -p 8080:8080 quay.io/evanshortiss/openshift-s2i-typescript-example
```

## Running Local Dev Mode using Docker Compose
To perform the following steps you'll need:

* [Docker](https://docs.docker.com/release-notes/) (v19.x tested)
* [Docker Compose](https://docs.docker.com/compose/install/)

Run the `docker-compose up` command from the root of the repository to start
Node.js and Redis containers.

The application will be available on port 8080.
22 changes: 22 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: '3'

services:
redis:
container_name: redis
image: redis:6.2-alpine
command: redis-server --requirepass redispass
ts-node:
depends_on:
- redis
container_name: ts-node
build: .
command: sh -c "npm i && npm run start-dev"
ports:
- 8080:8080/tcp
environment:
HUSKY_SKIP_INSTALL: 1
REDIS_SERVICE_PASS: redispass
REDIS_SERVICE_HOST: redis
volumes:
- '.:/usr/app/'
restart: unless-stopped
54 changes: 54 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "openshift-ts",
"version": "4.0.0",
"description": "A sample TypeScript application that can be deployed on OpenShift",
"main": "src/server.js",
"scripts": {
"build": "tsc",
"nodeshift": "nodeshift --expose",
"postinstall": "npm run build",
"format": "prettier --write --single-quote src/**/*.ts",
"start": "node src/server.js",
"start-dev": "nodemon --legacy-watch -e ts -x ts-node src/server.ts | pino-pretty -t",
"test": "echo \"Error: no test specified\" && exit 1"
},
"husky": {
"hooks": {
"pre-commit": "npm run format && git add ."
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/evanshortiss/openshift-typescript-example.git"
},
"author": "Evan Shortiss <[email protected]> (https://evanshortiss.com/)",
"license": "MIT",
"bugs": {
"url": "https://github.com/evanshortiss/openshift-typescript-example/issues"
},
"homepage": "https://github.com/evanshortiss/openshift-typescript-example#readme",
"dependencies": {
"env-var": "~6.3.0",
"express": "~4.17.1",
"express-handlebars": "^5.3.2",
"helmet": "~4.1.0",
"kube-probe": "~0.5.0",
"morgan": "~1.10.0",
"pino": "~6.5.1",
"redis": "^3.1.2"
},
"devDependencies": {
"@types/express": "~4.17.8",
"@types/express-handlebars": "^3.1.0",
"@types/node": "~12.12.54",
"@types/pino": "~6.3.0",
"@types/redis": "^2.8.28",
"husky": "~4.2.5",
"nodemon": "~2.0.7",
"nodeshift": "^8.2.0",
"pino-pretty": "~4.1.0",
"prettier": "~2.1.1",
"ts-node": "~9.1.1",
"typescript": "~4.0.2"
}
}
13 changes: 13 additions & 0 deletions scripts/s2i.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash

IMAGE_REPOSITORY=${IMAGE_REPOSITORY:-quay.io/evanshortiss/openshift-s2i-typescript-example:latest}

# Don't want to copy in local node_modules to the build
rm -rf node_modules/

# Build local codebase using source-to-image. Skip the husky hooks
# installation using env vars. Use red hat ubi8 nodejs 12 as the base
# image and tag using the given tag name
s2i build -c . \
-e HUSKY_SKIP_INSTALL=1 \
registry.access.redhat.com/ubi8/nodejs-12 ${IMAGE_REPOSITORY}
18 changes: 18 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { from, logger } from 'env-var';
import * as pino from 'pino';

const { get } = from(process.env, {}, logger);

const config = {
PORT: get('PORT').default('8080').asPortNumber(),

LOG_LEVEL: get('LOG_LEVEL')
.default('debug')
.asEnum(Object.keys(pino.levels.values)),

REDIS_SERVICE_HOST: get('REDIS_SERVICE_HOST').asString(),
REDIS_SERVICE_PORT: get('REDIS_SERVICE_PORT').default(6379).asPortNumber(),
REDIS_SERVICE_PASS: get('REDIS_SERVICE_PASS').asString(),
};

export = config;
6 changes: 6 additions & 0 deletions src/log.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import pino from 'pino';
import { LOG_LEVEL } from './config';

export default pino({
level: LOG_LEVEL,
});
37 changes: 37 additions & 0 deletions src/redis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import redis from 'redis';
import {
REDIS_SERVICE_HOST,
REDIS_SERVICE_PORT,
REDIS_SERVICE_PASS,
} from './config';
import log from './log';

let client!: redis.RedisClient;

if (REDIS_SERVICE_HOST) {
log.info(
`connecting redis client to: ${REDIS_SERVICE_HOST}:${REDIS_SERVICE_PORT}`
);
client = redis.createClient({
host: REDIS_SERVICE_HOST,
port: REDIS_SERVICE_PORT,
password: REDIS_SERVICE_PASS,
});
}

export function getViewCount(): Promise<void | number> {
return new Promise((resolve, reject) => {
if (!client) {
log.warn('no redis client. cannot return view count');
resolve();
} else {
client.incr('views', (err, n) => {
if (err) {
reject(err);
} else {
resolve(n);
}
});
}
});
}
45 changes: 45 additions & 0 deletions src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import express from 'express';
import { PORT } from './config';
import { getViewCount } from './redis';
import log from './log';
import exphbs from 'express-handlebars';

const app = express();
const hbs = exphbs();

// Add kubernetes liveness and readiness probes at
// /api/health/readiness and /api/health/liveness
require('kube-probe')(app);

// Configure handlebars rendering
app.engine('handlebars', hbs);
app.set('view engine', 'handlebars');

// Include sensible security headers by default
app.use(require('helmet')());
// Log incoming requests
app.use(require('morgan')('combined'));

// Respond with an index.html file for the default route
app.get('/', async (req: express.Request, res: express.Response) => {
// Include a view count header if Redis is connected
const viewCount = await getViewCount();

res.render('index', { viewCount });
});

// Our "Hello, World" endpoint. Can be passed a querystring "name" parameter
app.get('/api/hello', (req: express.Request, res: express.Response) => {
const name = req.query.name || 'World';
const message = `Hello, ${name}!`;

log.debug(`returning message: "${message}"`);

res.json({
message,
});
});

app.listen(PORT, () => {
log.info(`🚀 server listening on port ${PORT}`);
});
Loading

0 comments on commit 327258e

Please sign in to comment.