Create levant tf module
Also a template service Nomad job that can be used for some straighforward services
This commit is contained in:
parent
54f98e740f
commit
726b634092
19
.terraform.lock.hcl
generated
19
.terraform.lock.hcl
generated
@ -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" {
|
provider "registry.terraform.io/hashicorp/nomad" {
|
||||||
version = "1.4.16"
|
version = "1.4.16"
|
||||||
hashes = [
|
hashes = [
|
||||||
|
40
levant/.terraform.lock.hcl
generated
Normal file
40
levant/.terraform.lock.hcl
generated
Normal 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
levant/README.md
Normal file
7
levant/README.md
Normal 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
levant/levant.py
Executable file
60
levant/levant.py
Executable 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
levant/main.tf
Normal file
37
levant/main.tf
Normal 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
levant/test.nomad
Normal file
2
levant/test.nomad
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
job {
|
||||||
|
}
|
145
service.nomad
Normal file
145
service.nomad
Normal 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 ]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user