-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathCaddyfile.generate
executable file
·142 lines (121 loc) · 4.14 KB
/
Caddyfile.generate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#! /usr/bin/env python3
# Copyright (C) 2018 Sebastian Pipping <sebastian@pipping.org>
# Licensed under GNU Affero GPL v3 or later
import subprocess
import sys
from argparse import ArgumentParser
from collections import namedtuple
from configparser import ConfigParser, NoOptionError
from contextlib import suppress
from tempfile import NamedTemporaryFile
from textwrap import dedent
class CaddyfileGenerator:
Site = namedtuple(
"Site",
[
"alias_domains",
"backend_authority",
"domain",
],
)
def __init__(self):
self._redir_target_of = {}
self._backend_of = {}
def add(self, site):
self._backend_of[site.domain] = site.backend_authority
for domain in site.alias_domains:
self._redir_target_of[domain] = site.domain
def write_to(self, fp):
print(
dedent("""\
# NOTE: This file has been generated, do not edit
(common) {
encode zstd gzip
log {
output stdout
}
}"""),
file=fp,
)
for domain, backend_authority in sorted(self._backend_of.items()):
if backend_authority is None:
continue
print(
dedent("""
%s {
import common
reverse_proxy %s {
header_down +Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
}
}""") # noqa: E501
% (domain, backend_authority),
file=fp,
)
for source_domain, target_domain in sorted(self._redir_target_of.items()):
print(
dedent("""
%s {
import common
redir https://%s{uri}
}""")
% (source_domain, target_domain),
file=fp,
)
def run(options):
config = ConfigParser()
with open(options.config_filename) as fin:
config.read_file(fin, options.config_filename)
caddyfile = CaddyfileGenerator()
for domain in config.sections():
try:
alias_domains = config.get(domain, "aliases").split()
except NoOptionError:
alias_domains = []
try:
backend_authority = config.get(domain, "backend")
except NoOptionError:
backend_authority = None
site = CaddyfileGenerator.Site(alias_domains, backend_authority, domain)
caddyfile.add(site)
with NamedTemporaryFile() as temp_file:
# The idea is to diff against previous content or against
# empty file when there is no previos content
with suppress(OSError): # noqa: SIM117
with open(options.output_filename, "r+b") as fin:
temp_file.file.write(fin.read())
temp_file.file.flush()
with open(options.output_filename, "w") as fout:
caddyfile.write_to(fout)
exit_code = subprocess.call( # noqa: S603
["diff", "-u", temp_file.name, options.output_filename],
)
# Write stateful output in the format expected by SaltStack
if exit_code and options.saltstack:
print()
print("changed=yes comment='Caddyfile changed'")
if __name__ == "__main__":
parser = ArgumentParser()
parser.add_argument(
"--config",
dest="config_filename",
metavar="FILENAME",
default="sites.cfg",
help="Path to config file to read (default: %(default)s)",
)
parser.add_argument(
"--output",
dest="output_filename",
metavar="FILENAME",
default="Caddyfile",
help="Path to write Caddyfile to (default: %(default)s)",
)
parser.add_argument(
"--saltstack",
action="store_true",
help="Add lines signaling changes to SaltStack (default: omitted)",
)
options = parser.parse_args()
try:
run(options)
except OSError as e:
sys.exit(e)