def prepare_router_deployment()

in mysqloperator/controller/innodbcluster/router_objects.py [0:0]


def prepare_router_deployment(cluster: InnoDBCluster, logger, *,
                              init_only: bool = False) -> dict:
    # Start the router deployment with 0 replicas and only set it to the desired
    # value once the cluster is ONLINE, otherwise the router bootstraps could
    # timeout and fail unnecessarily.

    spec = cluster.parsed_spec

    (router_bootstrap_options, router_tls_exists, ca_and_tls) = get_bootstrap_and_tls_options(cluster)
    router_command = ['mysqlrouter', *spec.router.options]
    router_target = fqdn.idc_service_fqdn(cluster, logger)

    tmpl = f"""
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {spec.name}-router
  label:
    tier: mysql
    mysql.oracle.com/cluster: {spec.name}
    app.kubernetes.io/name: mysql-innodbcluster
    app.kubernetes.io/instance: mysql-innodbcluster-{spec.name}-router
    app.kubernetes.io/component: router
    app.kubernetes.io/managed-by: mysql-operator
    app.kubernetes.io/created-by: mysql-operator
spec:
  replicas: {spec.router.instances or 1 if not init_only else 0}
  selector:
    matchLabels:
      component: mysqlrouter
      tier: mysql
      mysql.oracle.com/cluster: {spec.name}
      app.kubernetes.io/name: mysql-router
      app.kubernetes.io/instance: mysql-innodbcluster-{spec.name}-router
      app.kubernetes.io/component: router
      app.kubernetes.io/managed-by: mysql-operator
      app.kubernetes.io/created-by: mysql-operator
  template:
    metadata:
      labels:
        component: mysqlrouter
        tier: mysql
        mysql.oracle.com/cluster: {spec.name}
        app.kubernetes.io/name: mysql-router
        app.kubernetes.io/instance: mysql-innodbcluster-{spec.name}-router
        app.kubernetes.io/component: router
        app.kubernetes.io/managed-by: mysql-operator
        app.kubernetes.io/created-by: mysql-operator
    spec:
      serviceAccountName: {spec.serviceAccountName}
      securityContext:
        runAsUser: 999
        runAsGroup: 999
        fsGroup: 999
        runAsNonRoot: true
      containers:
      - name: router
        image: {spec.router_image}
        imagePullPolicy: {spec.router_image_pull_policy}
        securityContext:
          # These can't go to spec.template.spec.securityContext
          # See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodTemplateSpec / https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodSpec
          # See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#PodSecurityContext - for pods (top level)
          # See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#Container
          # See: https://pkg.go.dev/k8s.io/api@v0.26.1/core/v1#SecurityContext - for containers
          allowPrivilegeEscalation: false
          privileged: false
          readOnlyRootFilesystem: true
          capabilities:
            drop:
            - ALL
        env:
        - name: MYSQL_HOST
          value: {router_target}
        - name: MYSQL_PORT
          value: "3306"
        - name: MYSQL_USER_FILE
          value: /.routeruser
        - name: MYSQL_PASSWORD_FILE
          value: /.routerpw
        - name: MYSQL_CREATE_ROUTER_USER
          value: "0"
        volumeMounts:
        - name: tmpdir
          mountPath: /tmp
        - name: initconfdir
          subPath: router-entrypoint-run.sh.tpl
          mountPath: /run.sh
          readOnly: true
        - name:  routercredentials
          subPath: routerUsername
          mountPath: /.routeruser
          readOnly: true
        - name:  routercredentials
          subPath: routerPassword
          mountPath: /.routerpw
          readOnly: true

{utils.indent(spec.extra_router_volume_mounts if router_tls_exists else spec.extra_router_volume_mounts_no_cert, 8)}
        ports:
        - containerPort: {spec.router_rwport}
          name: mysqlrw
        - containerPort: {spec.router_rwxport}
          name: mysqlxrw
        - containerPort: {spec.router_rwsplitport}
          name: mysqlrwsplit
        - containerPort: {spec.router_roport}
          name: mysqlro
        - containerPort: {spec.router_roxport}
          name: mysqlxro
        - containerPort: {spec.router_httpport}
          name: http
        readinessProbe:
          exec:
            command:
            - cat
            - /tmp/mysqlrouter/mysqlrouter.conf
        livenessProbe:
          failureThreshold: 3
          httpGet:
            path: /api/20190715/swagger.json
            port: http
            scheme: HTTPS
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
      volumes:
      - name: tmpdir
        emptyDir: {{}}
      - name: initconfdir
        configMap:
          name: {spec.name}-initconf
          defaultMode: 0755
      - name: routercredentials
        secret:
          secretName: {spec.name}-router
          # the files are created by root but belonging to mysqlrouter group,
          # thus we need read access for the group
          defaultMode: 0440
{utils.indent(spec.extra_router_volumes if router_tls_exists else spec.extra_router_volumes_no_cert, 6)}
"""
    deployment = yaml.safe_load(tmpl)

    container = deployment["spec"]["template"]["spec"]["containers"][0]

    container["args"] = router_command

    container["env"].append({
        "name": "MYSQL_ROUTER_BOOTSTRAP_EXTRA_OPTIONS",
        "value": router_bootstrap_options
    })

    metadata = {}
    if spec.router.podAnnotations:
        metadata['annotations'] = spec.router.podAnnotations
    if spec.router.podLabels:
        metadata['labels'] = spec.router.podLabels

    if len(metadata):
        utils.merge_patch_object(deployment["spec"]["template"], {"metadata" : metadata })

    if spec.router.podSpec:
        utils.merge_patch_object(deployment["spec"]["template"]["spec"],
                                 spec.router.podSpec, "spec.router.podSpec")

    # Cache the sha256 of the certs and keys we start it. This will prevent that when
    # the sidecar sees the unhandled secrets it will patch the deployment with the same hashes
    # and this won't restart the deployment. If however the TLS data has changed during IC boot
    # the handler will get the new values, hash them and this will trigger the reboot.
    if ca_and_tls:
        # the annotation keys should be the same as in restart_deployment_for_tls()
        tls_hashes_patch = {"spec": { "template": { "metadata": { "annotations": { }}}}}

        ca_pem = ca_and_tls.get(ca_and_tls.get("CA", "ca.pem"))
        ca_pem_sha256 = utils.sha256(ca_pem) if ca_pem else None
        if ca_pem_sha256:
          tls_hashes_patch['spec']['template']['metadata']['annotations']['mysql.oracle.com/ca.pem.sha256'] = ca_pem_sha256

        crl_pem = ca_and_tls.get('crl.pem')
        crl_pem_sha256 = utils.sha256(crl_pem) if crl_pem else None
        if crl_pem_sha256:
          tls_hashes_patch['spec']['template']['metadata']['annotations']['mysql.oracle.com/crl.pem.sha256'] = crl_pem_sha256

        router_tls_crt = ca_and_tls.get('router_tls.crt')
        router_tls_crt_sha256 = utils.sha256(router_tls_crt) if router_tls_crt else None
        if router_tls_crt_sha256:
          tls_hashes_patch['spec']['template']['metadata']['annotations']['mysql.oracle.com/router_tls.crt.sha256'] = router_tls_crt_sha256

        router_tls_key = ca_and_tls.get('router_tls.key')
        router_tls_key_sha256 = utils.sha256(router_tls_key) if router_tls_key else None
        if router_tls_key_sha256:
          tls_hashes_patch['spec']['template']['metadata']['annotations']['mysql.oracle.com/router_tls.key.sha256'] = router_tls_key_sha256

        utils.merge_patch_object(deployment, tls_hashes_patch)

    return deployment