Compare commits
25 Commits
60da6b0ae7
...
master
Author | SHA1 | Date | |
---|---|---|---|
b6bd431c40 | |||
da1030c960 | |||
209536614e | |||
89da5d6c0d | |||
019261a988 | |||
7d861aad76 | |||
6430dc5bc4 | |||
304451ef7c | |||
ee7a0b7f63 | |||
a1ec2c1602 | |||
71b55d5d17 | |||
30e3d3363c | |||
97ac22e62e | |||
35ef584cd7 | |||
f3599cc4ac | |||
2c0d4cdcae | |||
9ca94d0e05 | |||
cacdf1f5b9 | |||
4ee7337e9d | |||
d37c414466 | |||
e65d84c915 | |||
deb7f4fc5a | |||
c00432b425 | |||
669a371c0d | |||
4ba7eec661 |
@ -4,7 +4,7 @@ FROM python:3.10-slim AS builder
|
||||
# Install Docker
|
||||
RUN apt update
|
||||
RUN apt install curl -y
|
||||
RUN curl -sSL https://s.fascinated.cc/s/install-docker | bash
|
||||
RUN curl -sSL https://get.docker.com/ | CHANNEL=stable bash
|
||||
|
||||
# Step 2: Create the final image
|
||||
FROM python:3.10-slim
|
||||
|
9
LICENSE
Normal file
9
LICENSE
Normal file
@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Fascinated
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -4,7 +4,8 @@ This is a helper container for traefik. It can add, remove and update services.
|
||||
|
||||
## NOTICE
|
||||
|
||||
PLEASE PLEASE PLEASE MAKE SURE YOU HAVE A BACKUP OF YOUR CONFIG FILE BEFORE USING THIS CONTAINER. I AM NOT RESPONSIBLE FOR ANY DATA LOSS.
|
||||
PLEASE PLEASE PLEASE MAKE SURE YOU HAVE A BACKUP OF YOUR CONFIG FILE BEFORE USING THIS CONTAINER. I AM NOT RESPONSIBLE FOR ANY DATA LOSS. </br>
|
||||
This helper also assumes that you have SSL certificates setup for your services.
|
||||
|
||||
## Usage
|
||||
|
||||
@ -22,6 +23,10 @@ traefik-helper
|
||||
|
||||
## Windows Alias
|
||||
|
||||
Ensure you have ran `Set-ExecutionPolicy Unrestricted` in PowerShell as an administrator.
|
||||
|
||||
Create the directory `~/Documents/WindowsPowerShell` and create a file called `Microsoft.PowerShell_profile.ps1` in that directory. Add the following code to the file.
|
||||
|
||||
```bash
|
||||
function Traefik-Helper {
|
||||
$argsAsString = $args -join ' '
|
||||
@ -34,3 +39,5 @@ function Traefik-Helper {
|
||||
# Set the alias
|
||||
Set-Alias -Name "traefik" -Value "Traefik-Helper"
|
||||
```
|
||||
|
||||
This will allow you to run the `traefik` command in PowerShell.
|
||||
|
@ -1,6 +1,10 @@
|
||||
from command.impl.addCommand import AddCommand
|
||||
from command.impl.listCommand import ListCommand
|
||||
from command.impl.removeCommand import RemoveCommand
|
||||
from command.impl.restartCommand import RestartCommand
|
||||
from command.impl.logsCommand import LogsCommand
|
||||
from command.impl.addSubPathCommand import AddSubPathCommand
|
||||
from command.impl.addCatchAllCommand import AddCatchAllCommand
|
||||
|
||||
class CommandManager:
|
||||
|
||||
@ -10,8 +14,13 @@ class CommandManager:
|
||||
self.addCommand(AddCommand())
|
||||
self.addCommand(RemoveCommand())
|
||||
self.addCommand(ListCommand())
|
||||
self.addCommand(RestartCommand())
|
||||
self.addCommand(LogsCommand())
|
||||
self.addCommand(AddSubPathCommand())
|
||||
self.addCommand(AddCatchAllCommand())
|
||||
pass
|
||||
|
||||
|
||||
def addCommand(self, command):
|
||||
self.commands.append(command)
|
||||
pass
|
||||
|
32
src/command/impl/addCatchAllCommand.py
Normal file
32
src/command/impl/addCatchAllCommand.py
Normal file
@ -0,0 +1,32 @@
|
||||
from command.command import Command
|
||||
from traefik.traefikConfig import TraefikConfig
|
||||
from utils.dockerUtils import restartTraefik
|
||||
|
||||
class AddCatchAllCommand(Command):
|
||||
def __init__(self):
|
||||
super().__init__("add-catch-all", "Add a catch all domain", "add-catch-all <name> <domain> <service host>")
|
||||
|
||||
def execute(self, traefikConfig: TraefikConfig, args):
|
||||
if len(args) < 3:
|
||||
self.printUsage()
|
||||
return
|
||||
|
||||
name = args[0]
|
||||
domain = args[1]
|
||||
serviceHost = args[2]
|
||||
|
||||
if traefikConfig.hasRouter(name):
|
||||
print(f"Router \"{name}\" already exists")
|
||||
return
|
||||
|
||||
# Validate if the service host is a valid URL
|
||||
if not serviceHost.startswith("http://") and not serviceHost.startswith("https://"):
|
||||
print(f"Service host \"{serviceHost}\" is not a valid URL")
|
||||
return
|
||||
|
||||
print(f"Adding \"{domain}\" -> \"{serviceHost}\"")
|
||||
|
||||
traefikConfig.addCatchAllRouter(name, domain, serviceHost)
|
||||
traefikConfig.save()
|
||||
|
||||
print(f"Access your service at http://{domain}")
|
@ -6,7 +6,7 @@ class AddCommand(Command):
|
||||
def __init__(self):
|
||||
super().__init__("add", "Add a domain", "add <name> <domain> <service host>")
|
||||
|
||||
def execute(self, traefikConfig:TraefikConfig, args):
|
||||
def execute(self, traefikConfig: TraefikConfig, args):
|
||||
if len(args) < 3:
|
||||
self.printUsage()
|
||||
return
|
||||
@ -19,13 +19,14 @@ class AddCommand(Command):
|
||||
print(f"Router \"{name}\" already exists")
|
||||
return
|
||||
|
||||
# Validate if the service host is a valid URL
|
||||
if not serviceHost.startswith("http://") and not serviceHost.startswith("https://"):
|
||||
print(f"Service host \"{serviceHost}\" is not a valid URL")
|
||||
return
|
||||
|
||||
print(f"Adding \"{domain}\" -> \"{serviceHost}\"")
|
||||
|
||||
traefikConfig.addRouter(name, domain, serviceHost)
|
||||
traefikConfig.addService(name, serviceHost)
|
||||
|
||||
traefikConfig.save()
|
||||
|
||||
restartTraefik()
|
||||
|
||||
print("Done!")
|
||||
print(f"Access your service at http://{domain}")
|
43
src/command/impl/addSubPathCommand.py
Normal file
43
src/command/impl/addSubPathCommand.py
Normal file
@ -0,0 +1,43 @@
|
||||
from command.command import Command
|
||||
from traefik.traefikConfig import TraefikConfig
|
||||
from utils.dockerUtils import restartTraefik
|
||||
|
||||
class AddSubPathCommand(Command):
|
||||
def __init__(self):
|
||||
super().__init__("add-path", "Add sub path to a domain (eg: bob.com/joe)", "add-path <name> <domain> <path> <service host>")
|
||||
|
||||
def execute(self, traefikConfig: TraefikConfig, args):
|
||||
if len(args) < 3:
|
||||
self.printUsage()
|
||||
return
|
||||
|
||||
router = args[0]
|
||||
domain = args[1]
|
||||
path = args[2]
|
||||
# Fix the path
|
||||
if path.startswith("/") == False:
|
||||
path = "/" + path
|
||||
if path.endswith("/") == True:
|
||||
path = path[:-1] # Remove the trailing slash
|
||||
serviceHost = args[3]
|
||||
subPathName = domain + path
|
||||
|
||||
if traefikConfig.hasPathRewrite(subPathName):
|
||||
print(f"Sub path for \"{domain}{path}\" already exists")
|
||||
return
|
||||
|
||||
if traefikConfig.hasRouter(router) == False:
|
||||
print(f"Router \"{router}\" does not exist")
|
||||
return
|
||||
|
||||
# Validate if the service host is a valid URL
|
||||
if not serviceHost.startswith("http://") and not serviceHost.startswith("https://"):
|
||||
print(f"Service host \"{serviceHost}\" is not a valid URL")
|
||||
return
|
||||
|
||||
print(f"Adding \"{domain}{path}\" -> \"{serviceHost}\"")
|
||||
|
||||
traefikConfig.addSubPathRouter(subPathName, domain, path, serviceHost)
|
||||
traefikConfig.save()
|
||||
|
||||
print(f"Access your service at http://{domain}{path}")
|
@ -6,7 +6,7 @@ class ListCommand(Command):
|
||||
def __init__(self):
|
||||
super().__init__("list", "List all services", "list")
|
||||
|
||||
def execute(self, traefikConfig:TraefikConfig, args):
|
||||
def execute(self, traefikConfig: TraefikConfig, args):
|
||||
print("Listing all services:")
|
||||
|
||||
domains = traefikConfig.getAll()
|
||||
|
10
src/command/impl/logsCommand.py
Normal file
10
src/command/impl/logsCommand.py
Normal file
@ -0,0 +1,10 @@
|
||||
from command.command import Command
|
||||
from traefik.traefikConfig import TraefikConfig
|
||||
from utils.dockerUtils import getTraefikLogs
|
||||
|
||||
class LogsCommand(Command):
|
||||
def __init__(self):
|
||||
super().__init__("logs", "Get traefik logs", "logs")
|
||||
|
||||
def execute(self, traefikConfig: TraefikConfig, args):
|
||||
getTraefikLogs()
|
@ -1,13 +1,12 @@
|
||||
from colorama import Fore
|
||||
from command.command import Command
|
||||
from traefik.traefikConfig import TraefikConfig
|
||||
from utils.dockerUtils import restartTraefik
|
||||
|
||||
class RemoveCommand(Command):
|
||||
def __init__(self):
|
||||
super().__init__("remove", "Remove a domain", "remove <name>")
|
||||
|
||||
def execute(self, traefikConfig:TraefikConfig, args):
|
||||
def execute(self, traefikConfig: TraefikConfig, args):
|
||||
if len(args) < 0:
|
||||
self.printUsage()
|
||||
return
|
||||
@ -23,7 +22,3 @@ class RemoveCommand(Command):
|
||||
traefikConfig.removeRouter(name)
|
||||
|
||||
traefikConfig.save()
|
||||
|
||||
restartTraefik()
|
||||
|
||||
print(f"Removed \"{name}\"")
|
27
src/command/impl/removeSubPathCommand.py
Normal file
27
src/command/impl/removeSubPathCommand.py
Normal file
@ -0,0 +1,27 @@
|
||||
from colorama import Fore
|
||||
from command.command import Command
|
||||
from traefik.traefikConfig import TraefikConfig
|
||||
from utils.dockerUtils import restartTraefik
|
||||
|
||||
class RemoveCommand(Command):
|
||||
def __init__(self):
|
||||
super().__init__("remove-sub-path", "Remove sub path from a domain", "remove-sub-path <name> <path>")
|
||||
|
||||
def execute(self, traefikConfig: TraefikConfig, args):
|
||||
if len(args) < 0:
|
||||
self.printUsage()
|
||||
return
|
||||
|
||||
name = args[0]
|
||||
|
||||
if not traefikConfig.hasRouter(name):
|
||||
print(f"Router \"{name}\" does not exist")
|
||||
return
|
||||
|
||||
print(f"Removing \"{name}\"")
|
||||
|
||||
traefikConfig.removeRouter(name)
|
||||
|
||||
traefikConfig.save()
|
||||
|
||||
restartTraefik()
|
10
src/command/impl/restartCommand.py
Normal file
10
src/command/impl/restartCommand.py
Normal file
@ -0,0 +1,10 @@
|
||||
from command.command import Command
|
||||
from traefik.traefikConfig import TraefikConfig
|
||||
from utils.dockerUtils import restartTraefik
|
||||
|
||||
class RestartCommand(Command):
|
||||
def __init__(self):
|
||||
super().__init__("restart", "Restart traefik", "restart")
|
||||
|
||||
def execute(self, traefikConfig: TraefikConfig, args):
|
||||
restartTraefik()
|
@ -38,17 +38,17 @@ class TraefikConfig:
|
||||
}
|
||||
}
|
||||
|
||||
def removeRouter(self, name):
|
||||
# Remove router
|
||||
del self.configYml["http"]["routers"][name]
|
||||
def addCatchAllRouter(self, name, domain, serviceHost):
|
||||
# Add router
|
||||
self.configYml["http"]["routers"][name] = {
|
||||
"entryPoints": ["https"],
|
||||
"rule": "HostRegexp(`{subdomain:[A-Za-z0-9-]+}.%s`)" % domain,
|
||||
"middlewares": ["default-headers", "https-redirectscheme"],
|
||||
"tls": {},
|
||||
"priority": 1,
|
||||
"service": name
|
||||
}
|
||||
|
||||
# Remove service
|
||||
del self.configYml["http"]["services"][name]
|
||||
|
||||
def hasRouter(self, name):
|
||||
return name in self.configYml["http"]["routers"]
|
||||
|
||||
def addService(self, name, serviceHost):
|
||||
# Add service
|
||||
self.configYml["http"]["services"][name] = {
|
||||
"loadBalancer": {
|
||||
@ -60,6 +60,53 @@ class TraefikConfig:
|
||||
}
|
||||
}
|
||||
|
||||
def addSubPathRouter(self, name, domain, path, serviceHost):
|
||||
# Add trailing slashs
|
||||
if not path.endswith("/"):
|
||||
path += "/"
|
||||
if not serviceHost.endswith("/"):
|
||||
serviceHost += "/"
|
||||
|
||||
# Add path stripper middleware
|
||||
self.configYml["http"]["middlewares"][name] = {
|
||||
"stripPrefix": {
|
||||
"prefixes": [path]
|
||||
}
|
||||
}
|
||||
|
||||
# Add router
|
||||
self.configYml["http"]["routers"][name] = {
|
||||
"entryPoints": ["https"],
|
||||
"rule": "Host(`%s`) && PathPrefix(`%s`)" % (domain, path),
|
||||
"middlewares": ["default-headers", "https-redirectscheme", name],
|
||||
"tls": {},
|
||||
"service": name
|
||||
}
|
||||
|
||||
# Add service
|
||||
self.configYml["http"]["services"][name] = {
|
||||
"loadBalancer": {
|
||||
"servers": [
|
||||
{
|
||||
"url": serviceHost
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
def removeRouter(self, name):
|
||||
# Remove router
|
||||
del self.configYml["http"]["routers"][name]
|
||||
|
||||
# Remove service
|
||||
del self.configYml["http"]["services"][name]
|
||||
|
||||
def hasRouter(self, name):
|
||||
return name in self.configYml["http"]["routers"]
|
||||
|
||||
def hasPathRewrite(self, name):
|
||||
return name in self.configYml["http"]["middlewares"]
|
||||
|
||||
def hasService(self, name):
|
||||
return name in self.configYml["http"]["services"]
|
||||
|
||||
|
@ -16,3 +16,7 @@ def restartTraefik():
|
||||
subprocess.run(["docker", "restart", containerName])
|
||||
|
||||
print("Done!")
|
||||
|
||||
def getTraefikLogs():
|
||||
print("Getting Traefik logs, please wait...")
|
||||
subprocess.run(["docker", "logs", containerName, "-f", "--tail=250"])
|
Reference in New Issue
Block a user