Skip to content
Snippets Groups Projects
Commit 4629ca85 authored by Dmytro Bogatov's avatar Dmytro Bogatov :two_hearts:
Browse files

Add kubectl apply, fix shevastream.

parent a7d9951d
No related tags found
No related merge requests found
...@@ -9,10 +9,12 @@ import requests ...@@ -9,10 +9,12 @@ import requests
import time import time
import sys import sys
import os import os
from kubernetes import client, config from kubernetes import client, config, utils
from kubernetes.client.rest import ApiException from kubernetes.client.rest import ApiException
import base64 import base64
from enum import Enum, auto from enum import Enum, auto
import subprocess
from jinja2 import Template, Environment, FileSystemLoader
cwd = Path(os.path.dirname(os.path.realpath(__file__))) cwd = Path(os.path.dirname(os.path.realpath(__file__)))
...@@ -23,6 +25,33 @@ def deploy(): ...@@ -23,6 +25,33 @@ def deploy():
bootstrapCluster() bootstrapCluster()
def apply(resource, message=None):
if len(resource.splitlines()) == 1:
argument = resource if "https://" in resource else (cwd / resource).as_posix()
kubectl = f"kubectl --kubeconfig={(cwd / 'kubeconfig.yaml').as_posix()} apply -f {argument}"
output, error = subprocess.Popen(kubectl.split(), stdout=subprocess.PIPE).communicate()
else:
kubectl = f"kubectl --kubeconfig={(cwd / 'kubeconfig.yaml').as_posix()} apply -f -"
result = subprocess.run(
kubectl.split(),
stdout=subprocess.PIPE,
input=resource,
encoding='ascii'
)
output = result.stdout.encode()
error = result.stderr
if error is None:
_logger.debug(f"{kubectl}\n{output.decode()}")
else:
if message is not None:
_logger.critical(f"{message} provision failed")
_logger.error(error)
raise Exception(f"kubectl failed : {kubectl}")
if message is not None:
_logger.info(message)
def bootstrapCluster(): def bootstrapCluster():
class SecretType(Enum): class SecretType(Enum):
...@@ -30,6 +59,9 @@ def bootstrapCluster(): ...@@ -30,6 +59,9 @@ def bootstrapCluster():
BASICAUTH = auto() BASICAUTH = auto()
REGSITRY = auto() REGSITRY = auto()
DASHBOARD = auto() DASHBOARD = auto()
DO = auto()
STATUSSITE = auto()
SHEVASTREAM = auto()
def createNamespace(namespace): def createNamespace(namespace):
try: try:
...@@ -41,19 +73,27 @@ def bootstrapCluster(): ...@@ -41,19 +73,27 @@ def bootstrapCluster():
else: else:
raise e raise e
def createTlsSecret(namespace, type): def createSecret(namespace, type):
try: try:
if type == SecretType.TLS: data = None
stringData = None
if type == SecretType.TLS or type == SecretType.DASHBOARD:
with open(_secretsPath / "certificate.key", "r") as key: with open(_secretsPath / "certificate.key", "r") as key:
with open(_secretsPath / "certificate.crt", "r") as certificate: with open(_secretsPath / "certificate.crt", "r") as certificate:
data = {'tls.crt': base64.b64encode(certificate.read().encode()).decode(), 'tls.key': base64.b64encode(key.read().encode()).decode()} if type == SecretType.TLS:
secType = 'kubernetes.io/tls' data = {"tls.crt": base64.b64encode(certificate.read().encode()).decode(), "tls.key": base64.b64encode(key.read().encode()).decode()}
metadata = {'name': 'lets-encrypt'} secType = "kubernetes.io/tls"
metadata = {"name": "lets-encrypt"}
else:
data = {"certificate.crt": base64.b64encode(certificate.read().encode()).decode(), "certificate.key": base64.b64encode(key.read().encode()).decode()}
secType = "Opaque"
metadata = {"name": "kubernetes-dashboard-certs"}
elif type == SecretType.BASICAUTH: elif type == SecretType.BASICAUTH:
with open(_secretsPath / "auth", "r") as auth: with open(_secretsPath / "auth", "r") as auth:
data = {'auth': base64.b64encode(auth.read().encode()).decode()} data = {"auth": base64.b64encode(auth.read().encode()).decode()}
secType = 'Opaque' secType = "Opaque"
metadata = {'name': 'basic-auth'} metadata = {"name": "basic-auth"}
elif type == SecretType.REGSITRY: elif type == SecretType.REGSITRY:
data = { data = {
".dockerconfigjson": base64.b64encode( ".dockerconfigjson": base64.b64encode(
...@@ -69,10 +109,24 @@ def bootstrapCluster(): ...@@ -69,10 +109,24 @@ def bootstrapCluster():
}).encode() }).encode()
).decode() ).decode()
} }
secType = 'kubernetes.io/dockerconfigjson' secType = "kubernetes.io/dockerconfigjson"
metadata = {'name': 'regsecret'} metadata = {"name": "regsecret"}
elif type == SecretType.DO:
api.create_namespaced_secret(namespace, client.V1Secret(data=data, metadata=metadata, api_version="v1", type=secType)) stringData = {"access-token": _doToken}
secType = "Opaque"
metadata = {"name": "digitalocean"}
elif type == SecretType.STATUSSITE:
with open(_secretsPath / "appsettings.production.yml", "r") as config:
data = {"appsettings.production.yml": base64.b64encode(config.read().encode()).decode()}
secType = "Opaque"
metadata = {"name": "appsettings.production.yml"}
elif type == SecretType.SHEVASTREAM:
with open(cwd / "sources" / "shevastream" / "appsettings.json", "r") as config:
data = {"appsettings.json": base64.b64encode(config.read().encode()).decode()}
secType = "Opaque"
metadata = {"name": "shevastream-appsettings"}
api.create_namespaced_secret(namespace, client.V1Secret(data=data, string_data=stringData, metadata=metadata, api_version="v1", type=secType))
_logger.info(f"Secret {type} added to {namespace}") _logger.info(f"Secret {type} added to {namespace}")
except ApiException as e: except ApiException as e:
if "already exists" in e.body: if "already exists" in e.body:
...@@ -80,6 +134,12 @@ def bootstrapCluster(): ...@@ -80,6 +134,12 @@ def bootstrapCluster():
else: else:
raise e raise e
def createDashboardIngress():
templates = Environment(loader=FileSystemLoader(searchpath=str(cwd / "sources" / "dashboard")))
config = templates.get_template("ingress.yaml").render(data={"token": "TODO"})
print(config)
apply(config)
if not (cwd / "kubeconfig.yaml").is_file(): if not (cwd / "kubeconfig.yaml").is_file():
raise "Bootstrap called with no Kubeconfig file" raise "Bootstrap called with no Kubeconfig file"
...@@ -89,9 +149,20 @@ def bootstrapCluster(): ...@@ -89,9 +149,20 @@ def bootstrapCluster():
for namespace in namespaces: for namespace in namespaces:
createNamespace(namespace) createNamespace(namespace)
createTlsSecret(namespace, SecretType.TLS) createSecret(namespace, SecretType.TLS)
createTlsSecret(namespace, SecretType.BASICAUTH) createSecret(namespace, SecretType.BASICAUTH)
createTlsSecret(namespace, SecretType.REGSITRY) createSecret(namespace, SecretType.REGSITRY)
createSecret("kube-system", SecretType.DASHBOARD)
createSecret("kube-system", SecretType.DO)
createSecret("status-site", SecretType.STATUSSITE)
createSecret("websites", SecretType.SHEVASTREAM)
apply("sources/dashboard/all.yaml", "Dashboard")
apply("sources/nginx/mandatory.yaml", "NGINX ingress controller")
apply("https://raw.githubusercontent.com/digitalocean/csi-digitalocean/master/deploy/kubernetes/releases/csi-digitalocean-v0.3.1.yaml", "DO Volume provisioner")
createDashboardIngress()
def provisionCluster(): def provisionCluster():
...@@ -134,9 +205,6 @@ def generateServices(): ...@@ -134,9 +205,6 @@ def generateServices():
def generateService(name, image, replicated=True, auth=False, rps=10): def generateService(name, image, replicated=True, auth=False, rps=10):
import subprocess
from jinja2 import Template, Environment, FileSystemLoader
_logger.info(f"Generating {name} service") _logger.info(f"Generating {name} service")
if name in domainUrlExceptions: if name in domainUrlExceptions:
...@@ -168,7 +236,8 @@ def generateService(name, image, replicated=True, auth=False, rps=10): ...@@ -168,7 +236,8 @@ def generateService(name, image, replicated=True, auth=False, rps=10):
"replicated": replicated, "replicated": replicated,
"rps": rps, "rps": rps,
"auth": auth, "auth": auth,
"hosts": hosts "hosts": hosts,
"secret": None if "shevastream" not in name else ("appsettings", "/run/secrets/settings/", "shevastream-appsettings", "appsettings", "appsettings.production.json")
} }
) )
...@@ -196,7 +265,7 @@ def kubeconfig(): ...@@ -196,7 +265,7 @@ def kubeconfig():
if response.status_code == 200: if response.status_code == 200:
try: try:
response = response.content.decode('utf-8') response = response.content.decode("utf-8")
_logger.debug(f"Kubeconfig \n{response}") _logger.debug(f"Kubeconfig \n{response}")
with open(cwd / "kubeconfig.yaml", "w") as kubeconfigFile: with open(cwd / "kubeconfig.yaml", "w") as kubeconfigFile:
kubeconfigFile.write(response) kubeconfigFile.write(response)
...@@ -222,14 +291,14 @@ def setup_logger(level): ...@@ -222,14 +291,14 @@ def setup_logger(level):
datefmt=None, datefmt=None,
reset=True, reset=True,
log_colors={ log_colors={
'DEBUG': 'cyan', "DEBUG": "cyan",
'INFO': 'green', "INFO": "green",
'WARNING': 'yellow', "WARNING": "yellow",
'ERROR': 'red', "ERROR": "red",
'CRITICAL': 'red', "CRITICAL": "red",
} }
) )
logger = logging.getLogger('example') logger = logging.getLogger("example")
handler = logging.StreamHandler() handler = logging.StreamHandler()
handler.setFormatter(formatter) handler.setFormatter(formatter)
logger.addHandler(handler) logger.addHandler(handler)
...@@ -267,7 +336,7 @@ def main(): ...@@ -267,7 +336,7 @@ def main():
parser = argparse.ArgumentParser(description="Deploy K8S to Digital Ocean.") parser = argparse.ArgumentParser(description="Deploy K8S to Digital Ocean.")
parser.add_argument('--verbose', '-v', dest="verbose", action='count', default=4, help="Log level: CRITICAL, ERROR, WARNING, INFO, DEBUG") parser.add_argument("--verbose", "-v", dest="verbose", action="count", default=4, help="Log level: CRITICAL, ERROR, WARNING, INFO, DEBUG")
subparsers = parser.add_subparsers(title="commands", dest="command", required=True) subparsers = parser.add_subparsers(title="commands", dest="command", required=True)
...@@ -287,6 +356,7 @@ def main(): ...@@ -287,6 +356,7 @@ def main():
bootstrapParser = subparsers.add_parser("bootstrap") bootstrapParser = subparsers.add_parser("bootstrap")
addSecretsPathArgument(bootstrapParser) addSecretsPathArgument(bootstrapParser)
addDockerPassArgument(bootstrapParser) addDockerPassArgument(bootstrapParser)
addDOTokenArgument(bootstrapParser)
args = parser.parse_args() args = parser.parse_args()
...@@ -297,10 +367,10 @@ def main(): ...@@ -297,10 +367,10 @@ def main():
global _secretsPath global _secretsPath
_logger = setup_logger(60 - args.verbose * 10) _logger = setup_logger(60 - args.verbose * 10)
_doToken = args.doToken if 'doToken' in args else "" _doToken = args.doToken if "doToken" in args else ""
_name = args.name if 'name' in args else "" _name = args.name if "name" in args else ""
_dockerPass = args.dockerPass if 'dockerPass' in args else "" _dockerPass = args.dockerPass if "dockerPass" in args else ""
_secretsPath = args.secretsPath if 'secretsPath' in args else "" _secretsPath = args.secretsPath if "secretsPath" in args else ""
if args.command == "deploy": if args.command == "deploy":
deploy() deploy()
...@@ -394,5 +464,5 @@ domainUrlExceptions = { ...@@ -394,5 +464,5 @@ domainUrlExceptions = {
namespaces = ["websites", "monitoring", "ingress", "status-site", "kube-system", "gitlab", "review"] namespaces = ["websites", "monitoring", "ingress", "status-site", "kube-system", "gitlab", "review"]
if __name__ == '__main__': if __name__ == "__main__":
main() main()
...@@ -8,7 +8,7 @@ metadata: ...@@ -8,7 +8,7 @@ metadata:
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required!" nginx.ingress.kubernetes.io/auth-realm: "Authentication Required!"
nginx.ingress.kubernetes.io/auth-type: basic nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/configuration-snippet: | nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header Authorization "Bearer __DASHBOARD_TOKEN__"; proxy_set_header Authorization "Bearer {{ data.token }}";
name: dashboard name: dashboard
namespace: kube-system namespace: kube-system
spec: spec:
......
...@@ -49,6 +49,19 @@ spec: ...@@ -49,6 +49,19 @@ spec:
- name: {{ data.name }} - name: {{ data.name }}
image: {{ data.image }} image: {{ data.image }}
imagePullPolicy: Always imagePullPolicy: Always
{% if data.secret is not none %}
volumeMounts:
- name: {{ data.secret[0] }}
mountPath: {{ data.secret[1] }}
volumes:
- name: {{ data.secret[0] }}
secret:
secretName: {{ data.secret[2] }}
items:
- key: {{ data.secret[3] }}
path: {{ data.secret[4] }}
{% endif %}
{% else %} {% else %}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment