Florian Dambrine
I am a Freelance DevOps Engineer graduated from UTC (University of Technology of Compiègne) in 2014. I am a DevOps enthusiast embracing Cloud computing technologies to build automated infrastructure at large scale.
Knowledge worth sharing
#04
Florian Dambrine - Principal Engineer - @GumGum
***
***
***
Think of a static site generator as a script which takes in data, content and templates, processes them, and outputs a folder full of all the resultant pages and assets.
Content Delivery Network (CDN)
Browser
Browser
Web Server
App Server
Database
Content Delivery Network (CDN)
Navigateur
Optimal performance (CDN)
More Secured (Behind CDN)
Simplified hosting (S3 bucket)
Déploiement atomique (CDN Invalidation)
Git History and atomique rollbacks
CMS for content management
Git Commit
CMS
Automated Deployment
On Save
Hosting (Github Pages, S3)
brew install hugo
hugo new site hugo-tutorial
cd hugo-tutorial
hugo serve
# Visit localhost:1313
git init
git submodule add https://github.com/giraffeacademy/ga-hugo-theme.git themes/ga-hugo-theme
echo 'theme = "ga-hugo-theme"' >> config.toml
Optional
+++
title = "Verity"
description = "Verity Homepage"
date = 2019-09-03T07:43:09Z
weight = 3
chapter = true
pre = "<b><i class='fa fa-caret-square-up'></i> </b>"
draft = false
+++
# Verity
Welcome to the Verity homepage. This documentation aims to deep dive into Verity eco-system.
Verity eco-system is made of **3 major components**:
* `Verity-Api` submit user requests for processing
* CV microservices for images
* NLP for text processing / keywords extraction / threat classification and a lot more
## Global Architecture Diagram
{{< figure src="img/verity-infrastucture-diagram.png" title="Global infrastructure diagram" >}}
{{% children %}}
.
├── archetypes
├── config.toml
├── content
├── data
├── layouts
├── static
└── themes
├── content
├── dir1
├── A1.md
├── A2.md
├── B.md
├── C.md
├── dir2
├── index.md
Page bundle
(list.html)
Leaf bundles
(single.html)
Let's kick off this Hugo-Tutorial website and understand Hugo internals
https://github.com/Lowess/hugo-tutorial
Fork
# Go inside project
cd hugo-tutorial
# Fetch Hugo theme
git submodule update --init --recursive
# Start Hugo server from a terminal
hugo serve
Full series of youtube tutorials with articles and a github project
-- By Mike Dane
Create a new SSH Key and store the public one in Github > https://github.com/settings/keys
# /!\ Be careful to not override your ~/.ssh/id_rsa
$ ssh-keygen -t rsa -b 4096 -C "drone-hugo@ops-talks.com
# Enter file in which to save the key (/Users/florian/.ssh/id_rsa): drone-hugo
# Enter passphrase (empty for no passphrase): (empty)
# Your identification has been saved in drone-hugo.
# Your public key has been saved in drone-hugo.pub.
Login and peer with your Github account https://cloud.drone.io/. Once logged in search for hugo-tutorial and activate the project
Add a your Private SSH key as a Drone Secret. Name it github_ssh_key
git commit --allow-empty -m '🚀 Deploy' && git push
Hugo can output content in multiple formats, including calendar events, e-book formats, Google AMP, and JSON search indexes, or any custom text format.
https://github.com/Lowess/hugo-tutorial
<...>
# JSON templates should go in
# layouts/_default/{pageKind}.{outputFormatName}.{extension}
[outputs]
home = [ "HTML", "JSON" ]
page = ["HTML", "JSON"]
section = ["HTML", "JSON"]
hugo-tutorial/config.toml
layouts/_default/baseof.json.json
{{ define "response" }}
{
"kind" : "{{ .Kind }}",
"count" : "{{ len .Pages }}",
"items" : [
{{ range $i, $e := .Pages -}}
{{ if $i }}, {{ end }}{{ .Render "item" }}
{{ end }}
]
}
{{ end }}
layouts/_default/list.json.json
{
"data": {{ block "response" .}}{{ end }}
}
{{ define "response" }}
{{ .Render "item" }}
{{ end }}
layouts/_default/single.json.json
{
"kind": "{{ .Kind }}",
"title": "{{ .Title }}",
"permalink" : "{{ .Permalink }}index.json",
"length": "{{ len .Content }}"
}
layouts/_default/item.json.json
Go to Forestry.io and sign in using your Github account
Hugo Pipes is Hugo’s asset processing set of functions.
Inspired by https://github.com/netlify-templates/victor-hugo
docker run --rm -it \
-w /app \
-v $(pwd):/app \
lowess/preact-cli \
create Lowess/preact-hugo hugo-js
Let's kick off this Hugo website and bisect its directory layout
docker run --rm \
--entrypoint="" \
-p 3000:3000 \
-e HOST=0.0.0.0 \
-w /app \
-v $(pwd):/app \
lowess/preact-cli npm run start
S3 / CloudFront / Lambda@Edge / R53
Why Lambda@Edge ?
[...] CloudFront does allow you to specify a default root object (index.html), but it only works on the root of the website (such as http://www.example.com > http://www.example.com/index.html). It does not work on any subdirectory (such as http://www.example.com/about/). If you were to attempt to request this URL through CloudFront, CloudFront would do a S3 GetObject API call against a key that does not exist. [...]
CI / CD
# Lambda@edge - subdir-index.js
'use strict';
exports.handler = (event, context, callback) => {
// Extract the request from the CloudFront event that is sent to Lambda@Edge
var request = event.Records[0].cf.request;
// Extract the URI from the request
var olduri = request.uri;
// Match any '/' that occurs at the end of a URI. Replace it with a default index
var newuri = olduri.replace(/\/$/, '\/index.html');
// Log the URI as received by CloudFront and the new URI to be used to fetch from origin
console.log("Old URI: " + olduri + " New URI: " + newuri);
// Replace the received URI with the URI that includes the index page
request.uri = newuri;
// Return to CloudFront
return callback(null, request);
};
Cognito / Google Federation / Lambda
OAuth Flow
PreSignUp Lambda
- Domain validation - (ensure email address is part of whitelisted domains)
OAuth Challenge
Cognito / Google Federation / Lambda
# Authenticate User with Amplify Cognito Federation
useEffect(() => {
Hub.listen("auth", ({ payload: { event, data } }) => {
switch (event) {
case "signIn":
setUser({ user: data })
break;
case "signOut":
setUser({ user: null })
break;
}
})
Auth.currentAuthenticatedUser()
.then(user => {
setUser({ user })
context.login(user)
})
.catch(() => console.log("Not signed in"));
}, []);
# Trigger Cognito OAuth Flow
const SignIn = () => {
return (
<div class="flair-signin mx-auto card">
<div class="card-body">
<h1>Flair</h1>
<h6 class="card-subtitle"><i>Dive into Verity with elegance...</i></h6>
<button
class="btn btn-danger w-100"
onClick={() => Auth.federatedSignIn({provider: 'Google'})}
>
<FaGoogle /> Signin with Google
</button>
<hr />
<button
class="btn btn-default w-100"
onClick={() => Auth.federatedSignIn()}
>
<FaAws /> Sigin with AWS Web UI
</button>
</div>
</div>
);
}
AppSync & Lambda Direct resolvers
AppSync VTL
With Direct Resolver
import { API } from '@aws-amplify/api';
const fetchFromAppSync = async ({graphQLQuery, graphQLVars}) => {
// Run a GraphQL query on AWS AppSync
const { data } = await API.graphql({
query: graphQLQuery,
authMode: 'AMAZON_COGNITO_USER_POOLS',
variables: graphQLVars,
});
// GraphQL results come with the name of the query as a key { "graphQLQuery": {<data>} }
const result = Object.values(data)[0];
console.log(`[fetchFromAppSync] received ${JSON.stringify(result)}`);
}
type Query {
getVerityPageByPageUrl(data_source: String, page_url: String!, env: String): VerityPage
getVerityVideoByUuid(uuid: String!, env: String): VerityVideo
}
type VerityPage {
page_url: String!
...
}
type VerityVideo {
uuid: String!
...
}
(1) Export schema.json
(2) Run aws amplify codegen
AppSync / Lambda
import { API } from '@aws-amplify/api';
const fetchFromAppSync = async (
{graphQLQuery, graphQLVars}
) => {
const { data } = await API.graphql({
query: graphQLQuery,
authMode: 'AMAZON_COGNITO_USER_POOLS',
variables: graphQLVars,
});
}
graphQLQuery = getVerityPageByPageUrl
graphQLVars = { page_url: "http://example.com",
env: "prod" }
AppSync / Lambda
def handler(event, context) -> Dict[Any, Any]:
# Parse event payload
arguments = event.get("arguments", {})
graphql = event.get("info", {})
query = graphql.get("fieldName", None)
response = {}
env = arguments.get("env", "prod")
# Switch case routing based on GraphQL queries
if query == "getVerityPageByPageUrl":
page_response = dynamodb_to_object(
get_page_by_url(page_url=arguments["page_url"], env=env)
)
elif query == "getVerityVideoByUuid":
response = dynamodb_to_object(
get_video_by_uuid(uuid=arguments["uuid"], env=env)
)
return response
By Florian
By Florian Dambrine
OpsTalks #04 - Hugo Static Site Generator & Preact
I am a Freelance DevOps Engineer graduated from UTC (University of Technology of Compiègne) in 2014. I am a DevOps enthusiast embracing Cloud computing technologies to build automated infrastructure at large scale.