diff options
-rwxr-xr-x | bin/install_script.sh | 10 | ||||
-rw-r--r-- | python/get_initial_configuration_hetzner_server.py | 21 | ||||
-rw-r--r-- | python/hetzner_helper.py | 59 | ||||
-rw-r--r-- | python/list_servers.py | 6 | ||||
-rw-r--r-- | python/reboot_hetzner_server.py | 18 | ||||
-rwxr-xr-x | scripts/hetzner_server/arch_chroot_script.sh | 18 | ||||
-rwxr-xr-x | scripts/hetzner_server/arch_host_puppet_configuration_script.sh | 13 | ||||
-rwxr-xr-x | scripts/hetzner_server/arch_host_script.sh | 49 |
8 files changed, 194 insertions, 0 deletions
diff --git a/bin/install_script.sh b/bin/install_script.sh index 36cc594..322a1ed 100755 --- a/bin/install_script.sh +++ b/bin/install_script.sh | |||
@@ -11,6 +11,7 @@ $(basename $0) [options] | |||
11 | One of the following options is necessary: | 11 | One of the following options is necessary: |
12 | --instance-id id Id of the cloud instance | 12 | --instance-id id Id of the cloud instance |
13 | --vps-id id Id of the vps | 13 | --vps-id id Id of the vps |
14 | --hetzner-id id Id of the Hetzner server | ||
14 | 15 | ||
15 | Optional arguments: | 16 | Optional arguments: |
16 | --password password Password of the host (only useful in case of no reboot and vps) | 17 | --password password Password of the host (only useful in case of no reboot and vps) |
@@ -53,6 +54,15 @@ while [ -n "$1" ]; do | |||
53 | T="ovh_vps_ssd" | 54 | T="ovh_vps_ssd" |
54 | shift | 55 | shift |
55 | ;; | 56 | ;; |
57 | --hetzner-id) | ||
58 | host_id="$2" | ||
59 | if [ -z "$host_user" ]; then | ||
60 | host_user="root" | ||
61 | fi | ||
62 | [ -n "$T" ] && usage && exit 1 | ||
63 | T="hetzner_server" | ||
64 | shift | ||
65 | ;; | ||
56 | --password) | 66 | --password) |
57 | password="$2" | 67 | password="$2" |
58 | shift | 68 | shift |
diff --git a/python/get_initial_configuration_hetzner_server.py b/python/get_initial_configuration_hetzner_server.py new file mode 100644 index 0000000..71583ff --- /dev/null +++ b/python/get_initial_configuration_hetzner_server.py | |||
@@ -0,0 +1,21 @@ | |||
1 | import sys | ||
2 | import json | ||
3 | import hetzner_helper | ||
4 | |||
5 | instance = sys.argv[-1] | ||
6 | instance = hetzner_helper.get("servers/{}".format(instance))[1]["server"] | ||
7 | |||
8 | infos = {} | ||
9 | infos["ips"] = { | ||
10 | "v4": { | ||
11 | "ipAddress": instance["public_net"]["ipv4"]["ip"], | ||
12 | "gateway": "172.31.1.1", | ||
13 | }, | ||
14 | "v6": { | ||
15 | "ipAddress": instance["public_net"]["ipv6"]["ip"].split("/")[0], | ||
16 | "gateway": "fe80::1", | ||
17 | "mask": instance["public_net"]["ipv6"]["ip"].split("/")[1], | ||
18 | } | ||
19 | } | ||
20 | |||
21 | print(json.dumps(infos)) | ||
diff --git a/python/hetzner_helper.py b/python/hetzner_helper.py new file mode 100644 index 0000000..496e14d --- /dev/null +++ b/python/hetzner_helper.py | |||
@@ -0,0 +1,59 @@ | |||
1 | import json | ||
2 | import requests | ||
3 | import os | ||
4 | |||
5 | from configparser import RawConfigParser, NoSectionError, NoOptionError | ||
6 | |||
7 | class AuthenticationException(Exception): | ||
8 | pass | ||
9 | |||
10 | class RateLimitExceeded(Exception): | ||
11 | pass | ||
12 | |||
13 | class InternalServer(Exception): | ||
14 | pass | ||
15 | |||
16 | class HetznerConfig: | ||
17 | def __init__(self): | ||
18 | config = RawConfigParser() | ||
19 | config.read([os.path.expanduser('~/.hetzner.conf')]) | ||
20 | |||
21 | self.api_key = config.get("default", "api_key") | ||
22 | |||
23 | config = HetznerConfig() | ||
24 | |||
25 | def call(endpoint, url_params=None, body=None, method="GET"): | ||
26 | api = "https://api.hetzner.cloud/v1/{}".format(endpoint) | ||
27 | headers = {"Authorization": "Bearer {}".format(config.api_key)} | ||
28 | data = json.dumps(body) if body is not None else None | ||
29 | |||
30 | if method == "GET": | ||
31 | request = requests.get(api, headers=headers, params=url_params) | ||
32 | elif method == "POST" or (method == "GET" and body is not None): | ||
33 | request = requests.post(api, data=data, headers=headers, params=url_params) | ||
34 | elif method == "DELETE": | ||
35 | request = requests.delete(api, headers=headers) | ||
36 | elif method == "PUT": | ||
37 | request = requests.put(api, headers=headers, data=data) | ||
38 | |||
39 | if request.status_code == 401 or request.status_code == 403: | ||
40 | raise AuthenticationException() | ||
41 | |||
42 | if request.status_code == 429: | ||
43 | raise RateLimitExceeded() | ||
44 | |||
45 | if request.status_code == 500: | ||
46 | raise InternalServer(request.text) | ||
47 | |||
48 | if not request.text: | ||
49 | return request.status_code, "" | ||
50 | |||
51 | js = request.json() | ||
52 | |||
53 | return request.status_code, js | ||
54 | |||
55 | def get(*args, **kwargs): | ||
56 | return call(*args, method="GET", **kwargs) | ||
57 | |||
58 | def post(*args, **kwargs): | ||
59 | return call(*args, method="POST", **kwargs) | ||
diff --git a/python/list_servers.py b/python/list_servers.py index e7bd2af..6d22a56 100644 --- a/python/list_servers.py +++ b/python/list_servers.py | |||
@@ -21,3 +21,9 @@ print("OVH VPS SSD servers:") | |||
21 | for vps in vps_list: | 21 | for vps in vps_list: |
22 | print("\t{}".format(vps)) | 22 | print("\t{}".format(vps)) |
23 | 23 | ||
24 | import hetzner_helper | ||
25 | |||
26 | print("Hetzner VPS servers:") | ||
27 | return_code, json = hetzner_helper.call("servers") | ||
28 | for server in json["servers"]: | ||
29 | print("\t{}: {}".format(server["name"], server["id"])) | ||
diff --git a/python/reboot_hetzner_server.py b/python/reboot_hetzner_server.py new file mode 100644 index 0000000..7452afe --- /dev/null +++ b/python/reboot_hetzner_server.py | |||
@@ -0,0 +1,18 @@ | |||
1 | import sys | ||
2 | import hetzner_helper | ||
3 | |||
4 | instance = sys.argv[-1] | ||
5 | actions = [] | ||
6 | if "--rescue" in sys.argv: | ||
7 | actions.append("enable_rescue") | ||
8 | elif "--local" in sys.argv: | ||
9 | actions.append("disable_rescue") | ||
10 | |||
11 | if "--hard" in sys.argv: | ||
12 | actions.append("reset") | ||
13 | else: | ||
14 | actions.append("reboot") | ||
15 | |||
16 | for action in actions: | ||
17 | result = hetzner_helper.post("servers/{}/actions/{}".format(instance, action)) | ||
18 | print(result) | ||
diff --git a/scripts/hetzner_server/arch_chroot_script.sh b/scripts/hetzner_server/arch_chroot_script.sh new file mode 100755 index 0000000..afc78e2 --- /dev/null +++ b/scripts/hetzner_server/arch_chroot_script.sh | |||
@@ -0,0 +1,18 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | pacman-key --init | ||
4 | pacman-key --populate archlinux | ||
5 | |||
6 | UUID=$(cat /device_uuid) | ||
7 | PART="/dev/disk/by-uuid/$UUID" | ||
8 | DEVICE=$(realpath "$PART") | ||
9 | |||
10 | mkfs.ext4 -F -U "$UUID" "$DEVICE" | ||
11 | mount "$DEVICE" /mnt | ||
12 | |||
13 | pacstrap -G /mnt base git puppet | ||
14 | |||
15 | echo "$PART / auto defaults 0 1" > /mnt/etc/fstab | ||
16 | |||
17 | umount /mnt | ||
18 | |||
diff --git a/scripts/hetzner_server/arch_host_puppet_configuration_script.sh b/scripts/hetzner_server/arch_host_puppet_configuration_script.sh new file mode 100755 index 0000000..3ca2b51 --- /dev/null +++ b/scripts/hetzner_server/arch_host_puppet_configuration_script.sh | |||
@@ -0,0 +1,13 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | git_branch="$1" | ||
4 | environment="$2" | ||
5 | |||
6 | DEVICE="/dev/sda1" | ||
7 | MOUNTPOINT="/mnt" | ||
8 | |||
9 | cp /tmp/arch_puppet_configuration_script.sh "$MOUNTPOINT/root/" | ||
10 | |||
11 | /tmp/root.x86_64/bin/arch-chroot "$MOUNTPOINT" /root/arch_puppet_configuration_script.sh "$git_branch" "$environment" | ||
12 | |||
13 | umount "$MOUNTPOINT" | ||
diff --git a/scripts/hetzner_server/arch_host_script.sh b/scripts/hetzner_server/arch_host_script.sh new file mode 100755 index 0000000..23fbc32 --- /dev/null +++ b/scripts/hetzner_server/arch_host_script.sh | |||
@@ -0,0 +1,49 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | set -e | ||
4 | |||
5 | git_branch="$1" | ||
6 | environment="$2" | ||
7 | |||
8 | # Randomizer | ||
9 | haveged & | ||
10 | # /Randomizer | ||
11 | |||
12 | # Prepare an arch chroot | ||
13 | cd /tmp | ||
14 | |||
15 | LATEST=$(curl -L https://mirrors.kernel.org/archlinux/iso/latest/sha1sums.txt | grep "bootstrap" | head -n1) | ||
16 | SHA1=$(echo "$LATEST" | cut -d' ' -f1) | ||
17 | NAME=$(echo "$LATEST" | cut -d' ' -f3) | ||
18 | |||
19 | curl -L -O "https://mirrors.kernel.org/archlinux/iso/latest/$NAME" | ||
20 | |||
21 | tar -xzf "$NAME" | ||
22 | |||
23 | echo 'Server = http://archlinux.mirrors.ovh.net/archlinux/$repo/os/$arch' > /tmp/root.x86_64/etc/pacman.d/mirrorlist | ||
24 | # /Prepare an arch chroot | ||
25 | |||
26 | # Prepare device information (not available in chroot) | ||
27 | DEVICE="/dev/sda1" | ||
28 | MOUNTPOINT="/mnt" | ||
29 | |||
30 | UUID=$(lsblk -rno UUID "$DEVICE") | ||
31 | |||
32 | echo "$UUID" > /tmp/root.x86_64/device_uuid | ||
33 | # /Prepare device information | ||
34 | |||
35 | # Install very basic system via chroot (base git puppet) | ||
36 | cp /tmp/arch_chroot_script.sh /tmp/root.x86_64/ | ||
37 | |||
38 | /tmp/root.x86_64/bin/arch-chroot /tmp/root.x86_64/ /arch_chroot_script.sh | ||
39 | # /Install very basic system via chroot | ||
40 | |||
41 | # Mount and install rest of system (via puppet) | ||
42 | mount "$DEVICE" "$MOUNTPOINT" | ||
43 | |||
44 | cp /tmp/arch_install_script.sh "$MOUNTPOINT/root/" | ||
45 | cp /tmp/puppet_variables.json "$MOUNTPOINT/root/" | ||
46 | |||
47 | /tmp/root.x86_64/bin/arch-chroot "$MOUNTPOINT" /root/arch_install_script.sh "$git_branch" "$environment" | ||
48 | # /Mount and install rest of system | ||
49 | |||