wip
This commit is contained in:
commit
572e29e723
22 changed files with 453 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.secrets.cfg*
|
0
bundles/gitea/files/app.ini
Normal file
0
bundles/gitea/files/app.ini
Normal file
55
bundles/gitea/items.py
Normal file
55
bundles/gitea/items.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
downloads = {
|
||||
'/usr/local/bin/gitea': {
|
||||
'url': 'https://dl.gitea.io/gitea/{version}/gitea-{version}-linux-amd64'.format(version=node.metadata['gitea']['version']),
|
||||
'sha256': node.metadata['gitea']['sha256'],
|
||||
'triggers': {
|
||||
'svc_systemd:gitea:restart',
|
||||
},
|
||||
'preceded_by': {
|
||||
'action:stop_gitea',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
users = {
|
||||
'git': {},
|
||||
}
|
||||
|
||||
directories = {
|
||||
'/home/git': {
|
||||
'mode': '0755',
|
||||
'owner': 'git',
|
||||
'group': 'git',
|
||||
},
|
||||
'/var/lib/gitea': {
|
||||
'owner': 'git',
|
||||
'mode': '0700',
|
||||
'triggers': {
|
||||
'svc_systemd:gitea:restart',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
actions = {
|
||||
'chmod_gitea': {
|
||||
'command': 'chmod a+x /usr/local/bin/gitea',
|
||||
'unless': 'test -x /usr/local/bin/gitea',
|
||||
'needs': {
|
||||
'download:/usr/local/bin/gitea',
|
||||
},
|
||||
},
|
||||
'stop_gitea': {
|
||||
'command': 'systemctl stop gitea',
|
||||
'triggered': True,
|
||||
},
|
||||
}
|
||||
|
||||
files = {
|
||||
'/etc/gitea/app.ini': {
|
||||
'content_type': 'mako',
|
||||
'context': node.metadata['gitea'],
|
||||
'triggers': {
|
||||
'svc_systemd:gitea:restart',
|
||||
},
|
||||
},
|
||||
}
|
102
bundles/gitea/metadata.py
Normal file
102
bundles/gitea/metadata.py
Normal file
|
@ -0,0 +1,102 @@
|
|||
defaults = {
|
||||
'gitea': {
|
||||
'database': {
|
||||
'username': 'gitea',
|
||||
'password': repo.vault.password_for('{} postgresql gitea'.format(node.name)),
|
||||
'database': 'gitea',
|
||||
},
|
||||
'app_name': 'Gitea',
|
||||
'lfs_secret_key': repo.vault.password_for('{} gitea lfs_secret_key'.format(node.name)),
|
||||
'security_secret_key': repo.vault.password_for('{} gitea security_secret_key'.format(node.name)),
|
||||
'oauth_secret_key': repo.vault.password_for('{} gitea oauth_secret_key'.format(node.name)),
|
||||
'internal_token': repo.vault.password_for('{} gitea internal_token'.format(node.name)),
|
||||
},
|
||||
'postgresql': {
|
||||
'roles': {
|
||||
'gitea': {
|
||||
'password': repo.vault.password_for('{} postgresql gitea'.format(node.name)),
|
||||
},
|
||||
},
|
||||
'databases': {
|
||||
'gitea': {
|
||||
'owner': 'gitea',
|
||||
},
|
||||
},
|
||||
},
|
||||
'systemd': {
|
||||
'services': {
|
||||
'gitea': {
|
||||
'content': {
|
||||
'Unit': {
|
||||
'Description': 'gitea',
|
||||
'After': 'syslog.target',
|
||||
'After': 'network.target',
|
||||
'Requires': 'postgresql.service',
|
||||
},
|
||||
'Service': {
|
||||
'RestartSec': '2s',
|
||||
'Type': 'simple',
|
||||
'User': 'git',
|
||||
'Group': 'git',
|
||||
'WorkingDirectory': '/var/lib/gitea/',
|
||||
'ExecStart': '/usr/local/bin/gitea web -c /etc/gitea/app.ini',
|
||||
'Restart': 'always',
|
||||
'Environment': 'USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea',
|
||||
},
|
||||
'Install': {
|
||||
'WantedBy': 'multi-user.target',
|
||||
},
|
||||
},
|
||||
'needs': {
|
||||
'action:chmod_gitea',
|
||||
'download:/usr/local/bin/gitea',
|
||||
'file:/etc/systemd/system/gitea.service',
|
||||
'file:/etc/gitea/app.ini',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@metadata_reactor.provides(
|
||||
'nginx/vhosts',
|
||||
)
|
||||
def nginx(metadata):
|
||||
if not node.has_bundle('nginx'):
|
||||
raise DoNotRunAgain
|
||||
|
||||
return {
|
||||
'nginx': {
|
||||
'vhosts': {
|
||||
metadata.get('gitea/domain'): {
|
||||
'proxy': {
|
||||
'/': {
|
||||
'target': 'http://127.0.0.1:22000',
|
||||
},
|
||||
},
|
||||
'website_check_path': '/user/login',
|
||||
'website_check_string': 'Sign In',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@metadata_reactor.provides(
|
||||
'icinga2_api/gitea/services',
|
||||
)
|
||||
def icinga_check_for_new_release(metadata):
|
||||
return {
|
||||
'icinga2_api': {
|
||||
'gitea': {
|
||||
'services': {
|
||||
'GITEA UPDATE': {
|
||||
'command_on_monitored_host': '/usr/local/share/icinga/plugins/check_github_for_new_release go-gitea/gitea v{}'.format(metadata.get('gitea/version')),
|
||||
'vars.notification.mail': True,
|
||||
'check_interval': '60m',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
13
bundles/l4d2/files/l4d2.service
Normal file
13
bundles/l4d2/files/l4d2.service
Normal file
|
@ -0,0 +1,13 @@
|
|||
GNU nano 4.8 /etc/systemd/system/l4d2-server-a.service
|
||||
[Unit]
|
||||
Description=l4d2 Server A
|
||||
After=network.target steam-update.service
|
||||
|
||||
[Service]
|
||||
User=steam
|
||||
WorkingDirectory=/home/steam/steam/l4d2
|
||||
ExecStart=/home/steam/steam/l4d2/srcds_run -port 27001 -secure +exec server_a.cfg
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
4
bundles/l4d2/files/update_addons
Normal file
4
bundles/l4d2/files/update_addons
Normal file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/bash
|
||||
|
||||
/home/steam/steam_workshop_downloader/workshop.py -o /home/steam/steam/l4d2/left4dead2/addons 2283884609
|
||||
chown -R steam:steam /home/steam/steam/l4d2/left4dead2/addons
|
7
bundles/l4d2/metadata.py
Normal file
7
bundles/l4d2/metadata.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
@metadata_reactor.provides()
|
||||
def steam(metadata):
|
||||
return {
|
||||
'steam': {
|
||||
222860: 'l4d2',
|
||||
},
|
||||
}
|
15
bundles/steam/files/steam-update.service
Normal file
15
bundles/steam/files/steam-update.service
Normal file
|
@ -0,0 +1,15 @@
|
|||
GNU nano 4.8 /etc/systemd/system/l4d2-update.service
|
||||
[Unit]
|
||||
Description=l4d2 update
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=steam
|
||||
Group=steam
|
||||
WorkingDirectory=/home/steam/steam
|
||||
ExecStart=/home/steam/steam_workshop_downloader/workshop.py -o /home/steam/steam/l4d2/left4dead2/addons/workshop 2283884609
|
||||
ExecStart=/home/steam/steam/steamcmd.sh +login anonymous +force_install_dir ./l4d2/ +app_update 222860 validate +quit
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
4
bundles/steam/items.py
Normal file
4
bundles/steam/items.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
for id, name in node.metadata.get('steam'):
|
||||
pass
|
||||
|
||||
# sudo -Hiu steam bash -c '~/steam/steamcmd.sh +login anonymous +force_install_dir ./l4d2/ +app_update 222860 validate +quit'
|
19
bundles/systemd/files/unitfile
Normal file
19
bundles/systemd/files/unitfile
Normal file
|
@ -0,0 +1,19 @@
|
|||
% for i, (segment, options) in enumerate(data.items()):
|
||||
% if i > 0:
|
||||
|
||||
% endif
|
||||
[${segment}]
|
||||
% for option, value in options.items():
|
||||
% if isinstance(value, dict):
|
||||
% for k, v in value.items():
|
||||
${option}=${k}=${v}
|
||||
% endfor
|
||||
% elif isinstance(value, (list, set, tuple)):
|
||||
% for item in sorted(value):
|
||||
${option}=${item}
|
||||
% endfor
|
||||
% elif isinstance(value, str):
|
||||
${option}=${value}
|
||||
% endif
|
||||
% endfor
|
||||
% endfor
|
52
bundles/systemd/items.py
Normal file
52
bundles/systemd/items.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
timezone = node.metadata.get('timezone', 'UTC')
|
||||
keymap = node.metadata.get('keymap', 'de')
|
||||
|
||||
actions = {
|
||||
'systemd-reload': {
|
||||
'command': 'systemctl daemon-reload',
|
||||
'cascade_skip': False,
|
||||
'triggered': True,
|
||||
'needed_by': {
|
||||
'svc_systemd:',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, service in node.metadata.get('systemd', {}).get('services', {}).items():
|
||||
# use set() in metadata
|
||||
for enumerator in [
|
||||
'preceded_by', 'needs', 'needed_by', 'triggers', 'triggered_by'
|
||||
]:
|
||||
assert isinstance(service.get(enumerator, set()), set)
|
||||
|
||||
# dont call a service 'service' explicitly
|
||||
if name.endswith('.service'):
|
||||
raise Exception(name)
|
||||
|
||||
# split unit file content data from item data
|
||||
content_data = service.pop('content')
|
||||
|
||||
# default WantedBy=multi-user.target
|
||||
content_data\
|
||||
.setdefault('Install', {})\
|
||||
.setdefault('WantedBy', {'multi-user.target'})
|
||||
|
||||
# create unit file
|
||||
unit_path = f'/etc/systemd/system/{name}.service'
|
||||
files[unit_path] = {
|
||||
'source': 'unitfile',
|
||||
'content_type': 'mako',
|
||||
'context': {
|
||||
'data': content_data,
|
||||
},
|
||||
'triggers': [
|
||||
'action:systemd-reload',
|
||||
f'svc_systemd:{name}:restart',
|
||||
],
|
||||
}
|
||||
|
||||
# service depends on unit file
|
||||
service.setdefault('needs', set()).add(f'file:{unit_path}')
|
||||
|
||||
# service
|
||||
svc_systemd[name] = service
|
9
groups.py
Normal file
9
groups.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from os import walk
|
||||
from os.path import join, basename, splitext
|
||||
|
||||
for root, dirs, files in walk(join(repo_path, "groups")):
|
||||
for filename in files:
|
||||
if filename.endswith(".py"):
|
||||
group = join(root, filename)
|
||||
with open(group, 'r', encoding='utf-8') as f:
|
||||
groups[splitext(basename(filename))[0]] = eval(f.read())
|
6
groups/os/debian-10.py
Normal file
6
groups/os/debian-10.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
'supergroups': [
|
||||
'debian',
|
||||
],
|
||||
'os_version': (10,)
|
||||
}
|
11
groups/os/debian.py
Normal file
11
groups/os/debian.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
'supergroups': [
|
||||
'linux',
|
||||
],
|
||||
'bundles': [
|
||||
'apt',
|
||||
'systemd',
|
||||
],
|
||||
'os': 'debian',
|
||||
'pip_command': 'pip3',
|
||||
}
|
1
groups/os/linux.py
Normal file
1
groups/os/linux.py
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
120
items/download.py
Normal file
120
items/download.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
from bundlewrap.items import Item, ItemStatus
|
||||
from bundlewrap.exceptions import BundleError
|
||||
from bundlewrap.utils.text import force_text, mark_for_translation as _
|
||||
from bundlewrap.utils.remote import PathInfo
|
||||
import types
|
||||
from pipes import quote
|
||||
|
||||
# Downloaded from https://github.com/bundlewrap/plugins/blob/master/item_download/items/download.py
|
||||
# No, we can't use plugins here, because bw4 won't support them anymore.
|
||||
|
||||
class Download(Item):
|
||||
"""
|
||||
Download a file and verify its Hash.
|
||||
"""
|
||||
BUNDLE_ATTRIBUTE_NAME = "downloads"
|
||||
NEEDS_STATIC = [
|
||||
"pkg_apt:",
|
||||
"pkg_pacman:",
|
||||
"pkg_yum:",
|
||||
"pkg_zypper:",
|
||||
]
|
||||
ITEM_ATTRIBUTES = {
|
||||
'url': "",
|
||||
'sha256': "",
|
||||
'verifySSL': True,
|
||||
'decompress': None,
|
||||
}
|
||||
ITEM_TYPE_NAME = "download"
|
||||
REQUIRED_ATTRIBUTES = []
|
||||
|
||||
def __repr__(self):
|
||||
return "<Download name:{}>".format(self.name)
|
||||
|
||||
def __hash_remote_file(self, filename):
|
||||
path_info = PathInfo(self.node, filename)
|
||||
if not path_info.is_file:
|
||||
return None
|
||||
|
||||
if hasattr(path_info, 'sha256'):
|
||||
return path_info.sha256
|
||||
else:
|
||||
""""pending pr so do it manualy"""
|
||||
if self.node.os == 'macos':
|
||||
result = self.node.run("shasum -a 256 -- {}".format(quote(filename)))
|
||||
elif self.node.os in self.node.OS_FAMILY_BSD:
|
||||
result = self.node.run("sha256 -q -- {}".format(quote(filename)))
|
||||
else:
|
||||
result = self.node.run("sha256sum -- {}".format(quote(filename)))
|
||||
return force_text(result.stdout).strip().split()[0]
|
||||
|
||||
def fix(self, status):
|
||||
if status.must_be_deleted:
|
||||
# Not possible
|
||||
pass
|
||||
else:
|
||||
decompress = self.attributes.get('decompress')
|
||||
# download file
|
||||
self.node.run("curl -L {verify}-s -- {url}{pipe} > {file}".format(
|
||||
pipe = ' | ' + decompress if decompress else '',
|
||||
verify="" if self.attributes.get('verifySSL', True) else "-k ",
|
||||
file=quote(self.name),
|
||||
url=quote(self.attributes['url'])
|
||||
))
|
||||
|
||||
# check hash
|
||||
sha256 = self.__hash_remote_file(self.name)
|
||||
|
||||
if sha256 != self.attributes['sha256']:
|
||||
# unlink file
|
||||
self.node.run("rm -rf -- {}".format(quote(self.name)))
|
||||
|
||||
return False
|
||||
|
||||
def cdict(self):
|
||||
"""This is how the world should be"""
|
||||
cdict = {
|
||||
'type': 'download',
|
||||
'sha256': self.attributes['sha256'],
|
||||
}
|
||||
|
||||
return cdict
|
||||
|
||||
def sdict(self):
|
||||
"""This is how the world is right now"""
|
||||
path_info = PathInfo(self.node, self.name)
|
||||
if not path_info.exists:
|
||||
return None
|
||||
else:
|
||||
sdict = {
|
||||
'type': 'download',
|
||||
'sha256': self.__hash_remote_file(self.name)
|
||||
}
|
||||
|
||||
return sdict
|
||||
|
||||
@classmethod
|
||||
def validate_attributes(cls, bundle, item_id, attributes):
|
||||
if 'sha256' not in attributes:
|
||||
raise BundleError(_(
|
||||
"at least one hash must be set on {item} in bundle '{bundle}'"
|
||||
).format(
|
||||
bundle=bundle.name,
|
||||
item=item_id,
|
||||
))
|
||||
|
||||
if 'url' not in attributes:
|
||||
raise BundleError(_(
|
||||
"you need to specify the url on {item} in bundle '{bundle}'"
|
||||
).format(
|
||||
bundle=bundle.name,
|
||||
item=item_id,
|
||||
))
|
||||
|
||||
def get_auto_deps(self, items):
|
||||
deps = []
|
||||
for item in items:
|
||||
# debian TODO: add other package manager
|
||||
if item.ITEM_TYPE_NAME == 'pkg_apt' and item.name == 'curl':
|
||||
deps.append(item.id)
|
||||
return deps
|
9
nodes.py
Normal file
9
nodes.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from os import walk
|
||||
from os.path import join, basename, splitext
|
||||
|
||||
for root, dirs, files in walk(join(repo_path, "nodes")):
|
||||
for filename in files:
|
||||
if filename.endswith(".py"):
|
||||
node = join(root, filename)
|
||||
with open(node, 'r', encoding='utf-8') as f:
|
||||
nodes[splitext(basename(filename))[0]] = eval(f.read())
|
1
nodes/backupserver.py
Normal file
1
nodes/backupserver.py
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
6
nodes/gameserver.py
Normal file
6
nodes/gameserver.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
'bundles': [
|
||||
'steam',
|
||||
'l4d2',
|
||||
],
|
||||
}
|
16
nodes/homeserver.py
Normal file
16
nodes/homeserver.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
'hostname': '10.0.0.2',
|
||||
'bundles': [
|
||||
'gitea',
|
||||
],
|
||||
'groups': [
|
||||
'debian-10',
|
||||
],
|
||||
'metadata': {
|
||||
'gitea': {
|
||||
'version': '1.14.2',
|
||||
'sha256': '0d11d87ce60d5d98e22fc52f2c8c6ba2b54b14f9c26c767a46bf102c381ad128',
|
||||
'domain': 'git.sublimity.de',
|
||||
},
|
||||
},
|
||||
}
|
1
nodes/mailserver.py
Normal file
1
nodes/mailserver.py
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
|
@ -0,0 +1 @@
|
|||
bundlewrap>=4.4.2
|
Loading…
Reference in a new issue