AmmarFahmy
adding all files
105b369
from typing import Optional, Dict, List, Any
from phi.k8s.app.base import (
K8sApp,
AppVolumeType, # noqa: F401
ContainerContext, # noqa: F401
ServiceType,
RestartPolicy, # noqa: F401
ImagePullPolicy, # noqa: F401
LoadBalancerProvider, # noqa: F401
)
from phi.k8s.app.traefik.crds import ingressroute_crd, middleware_crd
from phi.utils.log import logger
class TraefikRouter(K8sApp):
# -*- App Name
name: str = "traefik"
# -*- Image Configuration
image_name: str = "traefik"
image_tag: str = "v2.10"
# -*- RBAC Configuration
# Create a ServiceAccount, ClusterRole, and ClusterRoleBinding
create_rbac: bool = True
# -*- Install traefik CRDs
# See: https://doc.traefik.io/traefik/providers/kubernetes-crd/#configuration-requirements
install_crds: bool = False
# -*- Traefik Configuration
domain_name: Optional[str] = None
# Enable Access Logs
access_logs: bool = True
# Traefik config file on the host
traefik_config_file: Optional[str] = None
# Traefik config file on the container
traefik_config_file_container_path: str = "/etc/traefik/traefik.yaml"
# -*- HTTP Configuration
http_enabled: bool = False
http_routes: Optional[List[dict]] = None
http_container_port: int = 80
http_service_port: int = 80
http_node_port: Optional[int] = None
http_key: str = "http"
http_ingress_name: str = "http-ingress"
forward_http_to_https: bool = False
enable_http_proxy_protocol: bool = False
enable_http_forward_headers: bool = False
# -*- HTTPS Configuration
https_enabled: bool = False
https_routes: Optional[List[dict]] = None
https_container_port: int = 443
https_service_port: int = 443
https_node_port: Optional[int] = None
https_key: str = "https"
https_ingress_name: str = "https-ingress"
enable_https_proxy_protocol: bool = False
enable_https_forward_headers: bool = False
add_headers: Optional[Dict[str, dict]] = None
# -*- Dashboard Configuration
dashboard_enabled: bool = False
dashboard_routes: Optional[List[dict]] = None
dashboard_container_port: int = 8080
dashboard_service_port: int = 8080
dashboard_node_port: Optional[int] = None
dashboard_key: str = "dashboard"
dashboard_ingress_name: str = "dashboard-ingress"
# The dashboard is gated behind a user:password, which is generated using
# htpasswd -nb user password
# You can provide the "users:password" list as a dashboard_auth_users param
# or as DASHBOARD_AUTH_USERS in the secrets_file
# Using the secrets_file is recommended
dashboard_auth_users: Optional[str] = None
insecure_api_access: bool = False
# -*- Service Configuration
create_service: bool = True
def get_dashboard_auth_users(self) -> Optional[str]:
return self.dashboard_auth_users or self.get_secret_from_file("DASHBOARD_AUTH_USERS")
def get_ingress_rules(self) -> List[Any]:
from kubernetes.client.models.v1_ingress_rule import V1IngressRule
from kubernetes.client.models.v1_ingress_backend import V1IngressBackend
from kubernetes.client.models.v1_ingress_service_backend import V1IngressServiceBackend
from kubernetes.client.models.v1_http_ingress_path import V1HTTPIngressPath
from kubernetes.client.models.v1_http_ingress_rule_value import V1HTTPIngressRuleValue
from kubernetes.client.models.v1_service_port import V1ServicePort
ingress_rules = [
V1IngressRule(
http=V1HTTPIngressRuleValue(
paths=[
V1HTTPIngressPath(
path="/",
path_type="Prefix",
backend=V1IngressBackend(
service=V1IngressServiceBackend(
name=self.get_service_name(),
port=V1ServicePort(
name=self.https_key if self.https_enabled else self.http_key,
port=self.https_service_port if self.https_enabled else self.http_service_port,
),
)
),
),
]
),
)
]
if self.dashboard_enabled:
ingress_rules[0].http.paths.append(
V1HTTPIngressPath(
path="/",
path_type="Prefix",
backend=V1IngressBackend(
service=V1IngressServiceBackend(
name=self.get_service_name(),
port=V1ServicePort(
name=self.dashboard_key,
port=self.dashboard_service_port,
),
)
),
)
)
return ingress_rules
def get_cr_policy_rules(self) -> List[Any]:
from phi.k8s.create.rbac_authorization_k8s_io.v1.cluster_role import (
PolicyRule,
)
return [
PolicyRule(
api_groups=[""],
resources=["services", "endpoints", "secrets"],
verbs=["get", "list", "watch"],
),
PolicyRule(
api_groups=["extensions", "networking.k8s.io"],
resources=["ingresses", "ingressclasses"],
verbs=["get", "list", "watch"],
),
PolicyRule(
api_groups=["extensions", "networking.k8s.io"],
resources=["ingresses/status"],
verbs=["update"],
),
PolicyRule(
api_groups=["traefik.io", "traefik.containo.us"],
resources=[
"middlewares",
"middlewaretcps",
"ingressroutes",
"traefikservices",
"ingressroutetcps",
"ingressrouteudps",
"tlsoptions",
"tlsstores",
"serverstransports",
],
verbs=["get", "list", "watch"],
),
]
def get_container_args(self) -> Optional[List[str]]:
if self.command is not None:
if isinstance(self.command, str):
return self.command.strip().split(" ")
return self.command
container_args = ["--providers.kubernetescrd"]
if self.access_logs:
container_args.append("--accesslog")
if self.http_enabled:
container_args.append(f"--entrypoints.{self.http_key}.Address=:{self.http_service_port}")
if self.enable_http_proxy_protocol:
container_args.append(f"--entrypoints.{self.http_key}.proxyProtocol.insecure=true")
if self.enable_http_forward_headers:
container_args.append(f"--entrypoints.{self.http_key}.forwardedHeaders.insecure=true")
if self.https_enabled:
container_args.append(f"--entrypoints.{self.https_key}.Address=:{self.https_service_port}")
if self.enable_https_proxy_protocol:
container_args.append(f"--entrypoints.{self.https_key}.proxyProtocol.insecure=true")
if self.enable_https_forward_headers:
container_args.append(f"--entrypoints.{self.https_key}.forwardedHeaders.insecure=true")
if self.forward_http_to_https:
container_args.extend(
[
f"--entrypoints.{self.http_key}.http.redirections.entryPoint.to={self.https_key}",
f"--entrypoints.{self.http_key}.http.redirections.entryPoint.scheme=https",
]
)
if self.dashboard_enabled:
container_args.append("--api=true")
container_args.append("--api.dashboard=true")
if self.insecure_api_access:
container_args.append("--api.insecure")
return container_args
def get_secrets(self) -> List[Any]:
return self.add_secrets or []
def get_ports(self) -> List[Any]:
from phi.k8s.create.common.port import CreatePort
ports: List[CreatePort] = self.add_ports or []
if self.http_enabled:
web_port = CreatePort(
name=self.http_key,
container_port=self.http_container_port,
service_port=self.http_service_port,
target_port=self.http_key,
)
if (
self.service_type in (ServiceType.NODE_PORT, ServiceType.LOAD_BALANCER)
and self.http_node_port is not None
):
web_port.node_port = self.http_node_port
ports.append(web_port)
if self.https_enabled:
websecure_port = CreatePort(
name=self.https_key,
container_port=self.https_container_port,
service_port=self.https_service_port,
target_port=self.https_key,
)
if (
self.service_type in (ServiceType.NODE_PORT, ServiceType.LOAD_BALANCER)
and self.https_node_port is not None
):
websecure_port.node_port = self.https_node_port
ports.append(websecure_port)
if self.dashboard_enabled:
dashboard_port = CreatePort(
name=self.dashboard_key,
container_port=self.dashboard_container_port,
service_port=self.dashboard_service_port,
target_port=self.dashboard_key,
)
if (
self.service_type in (ServiceType.NODE_PORT, ServiceType.LOAD_BALANCER)
and self.dashboard_node_port is not None
):
dashboard_port.node_port = self.dashboard_node_port
ports.append(dashboard_port)
return ports
def add_app_resources(self, namespace: str, service_account_name: Optional[str]) -> List[Any]:
from phi.k8s.create.apiextensions_k8s_io.v1.custom_object import CreateCustomObject
app_resources = self.add_resources or []
if self.http_enabled:
http_ingressroute = CreateCustomObject(
name=self.http_ingress_name,
crd=ingressroute_crd,
spec={
"entryPoints": [self.http_key],
"routes": self.http_routes,
},
app_name=self.get_app_name(),
namespace=namespace,
)
app_resources.append(http_ingressroute)
logger.debug(f"Added IngressRoute: {http_ingressroute.name}")
if self.https_enabled:
https_ingressroute = CreateCustomObject(
name=self.https_ingress_name,
crd=ingressroute_crd,
spec={
"entryPoints": [self.https_key],
"routes": self.https_routes,
},
app_name=self.get_app_name(),
namespace=namespace,
)
app_resources.append(https_ingressroute)
logger.debug(f"Added IngressRoute: {https_ingressroute.name}")
if self.add_headers:
headers_middleware = CreateCustomObject(
name="header-middleware",
crd=middleware_crd,
spec={
"headers": self.add_headers,
},
app_name=self.get_app_name(),
namespace=namespace,
)
app_resources.append(headers_middleware)
logger.debug(f"Added Middleware: {headers_middleware.name}")
if self.dashboard_enabled:
# create dashboard_auth_middleware if auth provided
# ref: https://doc.traefik.io/traefik/operations/api/#configuration
dashboard_auth_middleware = None
dashboard_auth_users = self.get_dashboard_auth_users()
if dashboard_auth_users is not None:
from phi.k8s.create.core.v1.secret import CreateSecret
dashboard_auth_secret = CreateSecret(
secret_name="dashboard-auth-secret",
app_name=self.get_app_name(),
namespace=namespace,
string_data={"users": dashboard_auth_users},
)
app_resources.append(dashboard_auth_secret)
logger.debug(f"Added Secret: {dashboard_auth_secret.secret_name}")
dashboard_auth_middleware = CreateCustomObject(
name="dashboard-auth-middleware",
crd=middleware_crd,
spec={"basicAuth": {"secret": dashboard_auth_secret.secret_name}},
app_name=self.get_app_name(),
namespace=namespace,
)
app_resources.append(dashboard_auth_middleware)
logger.debug(f"Added Middleware: {dashboard_auth_middleware.name}")
dashboard_routes = self.dashboard_routes
# use default dashboard routes
if dashboard_routes is None:
# domain must be provided
if self.domain_name is not None:
dashboard_routes = [
{
"kind": "Rule",
"match": f"Host(`traefik.{self.domain_name}`)",
"middlewares": [
{
"name": dashboard_auth_middleware.name,
"namespace": namespace,
},
]
if dashboard_auth_middleware is not None
else [],
"services": [
{
"kind": "TraefikService",
"name": "api@internal",
}
],
},
]
dashboard_ingressroute = CreateCustomObject(
name=self.dashboard_ingress_name,
crd=ingressroute_crd,
spec={
"routes": dashboard_routes,
},
app_name=self.get_app_name(),
namespace=namespace,
)
app_resources.append(dashboard_ingressroute)
logger.debug(f"Added IngressRoute: {dashboard_ingressroute.name}")
if self.install_crds:
from phi.k8s.resource.yaml import YamlResource
if self.yaml_resources is None:
self.yaml_resources = []
self.yaml_resources.append(
YamlResource(
name="traefik-crds",
url="https://raw.githubusercontent.com/traefik/traefik/v2.10/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml",
)
)
logger.debug("Added CRD yaml")
return app_resources