Create levant tf module

Also a template service Nomad job that can be used for some straighforward services
This commit is contained in:
IamTheFij 2022-07-08 16:24:03 -07:00
parent 11f5c10f83
commit 02b448e363
7 changed files with 310 additions and 0 deletions

View File

@ -19,6 +19,25 @@ provider "registry.terraform.io/hashicorp/consul" {
]
}
provider "registry.terraform.io/hashicorp/external" {
version = "2.2.2"
hashes = [
"h1:BKQ5f5ijzeyBSnUr+j0wUi+bYv6KBQVQNDXNRVEcfJE=",
"zh:0b84ab0af2e28606e9c0c1289343949339221c3ab126616b831ddb5aaef5f5ca",
"zh:10cf5c9b9524ca2e4302bf02368dc6aac29fb50aeaa6f7758cce9aa36ae87a28",
"zh:56a016ee871c8501acb3f2ee3b51592ad7c3871a1757b098838349b17762ba6b",
"zh:719d6ef39c50e4cffc67aa67d74d195adaf42afcf62beab132dafdb500347d39",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:7fbfc4d37435ac2f717b0316f872f558f608596b389b895fcb549f118462d327",
"zh:8ac71408204db606ce63fe8f9aeaf1ddc7751d57d586ec421e62d440c402e955",
"zh:a4cacdb06f114454b6ed0033add28006afa3f65a0ea7a43befe45fc82e6809fb",
"zh:bb5ce3132b52ae32b6cc005bc9f7627b95259b9ffe556de4dad60d47d47f21f0",
"zh:bb60d2976f125ffd232a7ccb4b3f81e7109578b23c9c6179f13a11d125dca82a",
"zh:f9540ecd2e056d6e71b9ea5f5a5cf8f63dd5c25394b9db831083a9d4ea99b372",
"zh:ffd998b55b8a64d4335a090b6956b4bf8855b290f7554dd38db3302de9c41809",
]
}
provider "registry.terraform.io/hashicorp/nomad" {
version = "1.4.16"
hashes = [

40
nomad/levant/.terraform.lock.hcl generated Normal file
View File

@ -0,0 +1,40 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/external" {
version = "2.2.2"
hashes = [
"h1:BKQ5f5ijzeyBSnUr+j0wUi+bYv6KBQVQNDXNRVEcfJE=",
"zh:0b84ab0af2e28606e9c0c1289343949339221c3ab126616b831ddb5aaef5f5ca",
"zh:10cf5c9b9524ca2e4302bf02368dc6aac29fb50aeaa6f7758cce9aa36ae87a28",
"zh:56a016ee871c8501acb3f2ee3b51592ad7c3871a1757b098838349b17762ba6b",
"zh:719d6ef39c50e4cffc67aa67d74d195adaf42afcf62beab132dafdb500347d39",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:7fbfc4d37435ac2f717b0316f872f558f608596b389b895fcb549f118462d327",
"zh:8ac71408204db606ce63fe8f9aeaf1ddc7751d57d586ec421e62d440c402e955",
"zh:a4cacdb06f114454b6ed0033add28006afa3f65a0ea7a43befe45fc82e6809fb",
"zh:bb5ce3132b52ae32b6cc005bc9f7627b95259b9ffe556de4dad60d47d47f21f0",
"zh:bb60d2976f125ffd232a7ccb4b3f81e7109578b23c9c6179f13a11d125dca82a",
"zh:f9540ecd2e056d6e71b9ea5f5a5cf8f63dd5c25394b9db831083a9d4ea99b372",
"zh:ffd998b55b8a64d4335a090b6956b4bf8855b290f7554dd38db3302de9c41809",
]
}
provider "registry.terraform.io/hashicorp/nomad" {
version = "1.4.17"
hashes = [
"h1:oWV3VXZhqPZ8Ia07nlIZLeXDBqVULMg9lP3dVMczDCo=",
"zh:146f97eacd9a0c78b357a6cfd2cb12765d4b18e9660a75500ee3e748c6eba41a",
"zh:2eb89a6e5cee9aea03a96ea9f141096fe3baf219b2700ce30229d2d882f5015f",
"zh:3d0f971f79b615c1014c75e2f99f34bd4b4da542ca9f31d5ea7fadc4e9de39c1",
"zh:46099a750c752ce05aa14d663a86478a5ad66d95aff3d69367f1d3628aac7792",
"zh:71e56006b013dcfe1e4e059b2b07148b44fcd79351ae2c357e0d97e27ae0d916",
"zh:74febd25d776688f0558178c2f5a0e6818bbf4cdaa2e160d7049da04103940f0",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:af18c064a5f0dd5422d6771939274841f635b619ab392c73d5bf9720945fdb85",
"zh:c133d7a862079da9f06e301c530eacbd70e9288fa2276ec0704df907270ee328",
"zh:c894cf98d239b9f5a4b7cde9f5c836face0b5b93099048ee817b0380ea439c65",
"zh:c918642870f0cafdbe4d7dd07c909701fc3ddb47cac8357bdcde1327bf78c11d",
"zh:f8f5655099a57b4b9c0018a2d49133771e24c7ff8262efb1ceb140fd224aa9b6",
]
}

7
nomad/levant/README.md Normal file
View File

@ -0,0 +1,7 @@
# Terraform Levant
This module renders a levant template and then creates a Nomad job based on that template.
It only covers a subset of levant capabilities because much else can be done with Terraform already.
required:

60
nomad/levant/levant.py Executable file
View File

@ -0,0 +1,60 @@
#! /usr/bin/env python3
import json
import sys
from subprocess import check_output
from typing import Optional
from typing import overload
from typing import TypeVar
T = TypeVar("T")
@overload
def get_json(d: dict[str, str], key: str, default: None = None) -> None:
...
@overload
def get_json(d: dict[str, str], key: str, default: T = None) -> T:
...
def get_json(d: dict[str, str], key: str, default: Optional[T] = None) -> Optional[T]:
if key not in d:
return default
return json.loads(d[key])
query = json.load(sys.stdin)
# Required
template_path = query["template_path"]
# Optional
consul_address = query.get("consul_address")
# Need to parse JSON back
variables = [
f'--var={key}={value}' for key, value in get_json(query, "variables", {}).items()
]
variable_files = [
f'--var-file={value}' for value in get_json(query, "var_files", [])
]
args: list[str] = list(
filter(
None,
["levant", "render", consul_address]
+ variables
+ variable_files
+ [template_path],
)
)
# print(" ".join(args), file=sys.stderr)
# exit(1)
template = check_output(args, stderr=sys.stderr)
print(json.dumps({"template": template.decode()}))

37
nomad/levant/main.tf Normal file
View File

@ -0,0 +1,37 @@
variable "template_path" {
type = string
nullable = false
}
variable "consul_address" {
type = string
default = null
nullable = true
description = "Consul host and port for making KeyValue lookups"
}
variable "variables" {
type = map(string)
description = "Variables to be passed into nomad-pack with values in JSON form"
default = {}
}
variable "var_files" {
type = list(string)
description = "HCL files containing variables to be used by nomad-pack"
default = []
}
data "external" "levant" {
program = ["${path.module}/levant.py"]
query = {
template_path = var.template_path
consul_address = var.consul_address
variables = jsonencode(var.variables)
var_files = jsonencode(var.var_files)
}
}
resource "nomad_job" "levant" {
jobspec = data.external.levant.result.template
}

2
nomad/levant/test.nomad Normal file
View File

@ -0,0 +1,2 @@
job {
}

145
nomad/service.nomad Normal file
View File

@ -0,0 +1,145 @@
# Vars
# name = string*
# image = string*
# service_port = int
# ingress = bool
# args = json(list[str])
# resources = dict(cpu = int, mem = int)
# templates = json(list(dict(
# data = str,
# dest = str,
# change_mode = str,
# change_signal = str,
# left_delimiter = str,
# right_delimiter = str,
# )))
# healthcheck = "/"
# mysql = bool
# redis = bool
job "[[.name]]" {
region = "global"
datacenters = ["dc1"]
type = "service"
group "[[.name]]" {
[[ with .count ]]count = [[ . ]][[end]]
network {
mode = "bridge"
[[ if not (empty .service_port) ]]
port "main" {
[[ if default false .ingress ]]
host_network = "loopback"
[[ end ]]
to = [[.service_port]]
}
[[ end ]]
}
[[ if not (empty .service_port) ]]
service {
name = "[[.name | replace "_" "-"]]"
port = "main"
[[ if default false .ingress ]]
connect {
sidecar_service {
proxy {
local_service_port = [[.service_port]]
[[ if default false .mysql ]]
upstreams {
destination_name = "mysql-server"
local_bind_port = 4040
}
[[ end -]]
[[ if default false .redis ]]
upstreams {
destination_name = "redis"
local_bind_port = 6379
}
[[ end ]]
}
}
sidecar_task {
resources {
cpu = 50
memory = 50
}
}
}
[[ end ]]
check {
type = "http"
path = "[[ or .healthcheck "/" ]]"
port = "main"
interval = "10s"
timeout = "10s"
}
tags = [
[[ if default false .ingress -]]
"traefik.enable=true",
"traefik.http.routers.[[.name]].entryPoints=websecure",
[[ end -]]
]
}
[[ end ]]
task "[[.name]]" {
driver = "docker"
config {
image = "[[.image]]"
[[ if not (empty .service_port) -]]
ports = ["main"]
[[- end ]]
[[ if not (empty .args) -]]
args = ["[[ .args | parseJSON | join `", "` ]]"]
[[- end ]]
[[ with .templates]]
[[ range $t := . | parseJSON ]]
mount {
type = "bind"
target = "[[ $t.dest ]]"
source = "local/[[ $t.dest ]]"
}
[[ end ]]
[[ end ]]
}
[[ with .env -]]
env = {
[[- range $k, $v := . ]]
"[[$k]]" = "[[$v]]"
[[- end ]]
}
[[ end ]]
[[ with .templates ]]
[[ range $t := . | parseJSON ]]
template {
data = <<EOF
[[ $t.data ]]
EOF
destination = "local/[[ $t.dest ]]"
[[ with $t.left_delimiter ]]left_delimiter = "[[ . ]]"[[ end -]]
[[ with $t.right_delimiter ]]right_delimiter = "[[ . ]]"[[ end -]]
[[ with $t.change_mode ]]change_mode = "[[ . ]]"[[ end -]]
[[ with $t.change_signal ]]change_signal = "[[ . ]]"[[ end -]]
[[ with $t.env ]]env = [[ . ]][[ end ]]
}
[[ end ]]
[[ end ]]
[[ with .resources ]]
resources {
cpu = [[ .cpu ]]
memory = [[ .memory ]]
}
[[ end ]]
}
}
}