*/kube: Add kube.SimpleIngress

Change-Id: Iddcac629b9938f228dd93b32e58bb14606d5c6e5
Reviewed-on: https://gerrit.hackerspace.pl/c/hscloud/+/1745
Reviewed-by: q3k <q3k@hackerspace.pl>
diff --git a/app/covid-formity/prod.jsonnet b/app/covid-formity/prod.jsonnet
index 44f83ef..2ba5adc 100644
--- a/app/covid-formity/prod.jsonnet
+++ b/app/covid-formity/prod.jsonnet
@@ -2,7 +2,7 @@
 # This needs a secret provisioned, create with:
 #    kubectl -n covid-formity create secret generic covid-formity --from-literal=postgres_password=$(pwgen 24 1) --from-literal=secret_key=$(pwgen 24 1) --from-literal=oauth2_secret=...
 
-local kube = import "../../kube/kube.libsonnet";
+local kube = import "../../kube/hscloud.libsonnet";
 local redis = import "../../kube/redis.libsonnet";
 local postgres = import "../../kube/postgres.libsonnet";
 
@@ -89,37 +89,17 @@
         },
     },
 
-    ingress: kube.Ingress("covid-formity") {
+    ingress: kube.SimpleIngress("covid-formity") {
+        hosts:: [cfg.domain] + cfg.altDomains,
+        target_service:: app.svc,
         metadata+: app.metadata("covid-formity") {
             annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                "nginx.ingress.kubernetes.io/proxy-body-size": "0",
                 "nginx.ingress.kubernetes.io/configuration-snippet": "
                     location /qr1 { rewrite ^/qr1(.*)$ https://covid.hackerspace.pl$1 redirect; }
                     location /video { return 302 https://youtu.be/eC19w2NFO0E; }
                     location /manual { return 302 https://wiki.hackerspace.pl/_media/projects:covid-19:przylbica-instrukcja-v1.0.pdf; }
                 ",
-            },
-        },
-        spec+: {
-            tls: [
-                {
-                    hosts: [cfg.domain] + cfg.altDomains,
-                    secretName: "covid-formity-tls",
-                },
-            ],
-            rules: [
-                {
-                    host: dom,
-                    http: {
-                        paths: [
-                            { path: "/", backend: app.svc.name_port },
-                        ]
-                    },
-                }
-                for dom in [cfg.domain] + cfg.altDomains
-            ],
-        },
+            }
+        }
     },
 }
diff --git a/app/inventory/prod.jsonnet b/app/inventory/prod.jsonnet
index f7d4510..7519d9a 100644
--- a/app/inventory/prod.jsonnet
+++ b/app/inventory/prod.jsonnet
@@ -1,4 +1,4 @@
-local kube = import '../../kube/kube.libsonnet';
+local kube = import "../../kube/hscloud.libsonnet";
 local postgres = import '../../kube/postgres_v.libsonnet';
 
 {
@@ -94,26 +94,8 @@
     target_pod:: top.deployment.spec.template,
   },
 
-  ingress: top.ns.Contain(kube.Ingress(cfg.name)) {
-    metadata+: {
-      annotations+: {
-        'kubernetes.io/tls-acme': 'true',
-        'cert-manager.io/cluster-issuer': 'letsencrypt-prod',
-        'nginx.ingress.kubernetes.io/proxy-body-size': '0',
-      },
-    },
-    spec+: {
-      tls: [{ hosts: [cfg.domain], secretName: cfg.name + '-tls' }],
-      rules: [
-        {
-          host: cfg.domain,
-          http: {
-            paths: [
-              { path: '/', backend: top.service.name_port },
-            ],
-          },
-        },
-      ],
-    },
+  ingress: top.ns.Contain(kube.SimpleIngress(cfg.name)) {
+    hosts:: [cfg.domain],
+    target_service:: top.service,
   },
 }
diff --git a/app/mailman-web/kube/mailman.libsonnet b/app/mailman-web/kube/mailman.libsonnet
index c71de4e..f3748a9 100644
--- a/app/mailman-web/kube/mailman.libsonnet
+++ b/app/mailman-web/kube/mailman.libsonnet
@@ -1,4 +1,4 @@
-local kube = import "../../../kube/kube.libsonnet";
+local kube = import "../../../kube/hscloud.libsonnet";
 
 {
     local app = self,
@@ -171,33 +171,9 @@
     },
 
 
-    #ingress: ns.Contain(kube.Ingress("mailman")) {
-    #    metadata+: {
-    #        annotations+: {
-    #            "kubernetes.io/tls-acme": "true",
-    #            "certmanager.k8s.io/cluster-issuer": "letsencrypt-prod",
-    #            "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-    #        },
-    #    },
-    #    spec+: {
-    #        tls: [
-    #            {
-    #                hosts: [cfg.webDomain],
-    #                secretName: "mailman-ingress-tls",
-    #            },
-    #        ],
-    #        rules: [
-    #            {
-    #                host: cfg.webDomain,
-    #                http: {
-    #                    paths: [
-    #                        { path: "/", backend: app.svcWeb.name_port },
-    #                        //{ path: "/static/", backend: app.svcStatic.name_port },
-    #                    ],
-    #                },
-    #            },
-    #        ],
-    #    },
+    #ingress: ns.Contain(kube.SimpleIngress("mailman")) {
+    #    hosts:: [cfg.webDomain],
+    #    target_service:: app.svcWeb,
     #},
 
     config: ns.Contain(kube.Secret("config")) {
diff --git a/app/matrix/lib/matrix.libsonnet b/app/matrix/lib/matrix.libsonnet
index 1990358..2b95345 100644
--- a/app/matrix/lib/matrix.libsonnet
+++ b/app/matrix/lib/matrix.libsonnet
@@ -28,7 +28,7 @@
 # For appservice-telegram instances, you can use this oneliner magic:
 #    kubectl -n matrix create secret generic appservice-telegram-prod-registration --from-file=registration.yaml=<(kubectl -n matrix logs job/appservice-telegram-prod-bootstrap | grep -A 100 SNIPSNIP | grep -v SNIPSNIP)
 
-local kube = import "../../../kube/kube.libsonnet";
+local kube = import "../../../kube/hscloud.libsonnet";
 local postgres = import "../../../kube/postgres.libsonnet";
 
 {
@@ -393,37 +393,16 @@
     // until it spits you a registration YAML and you feed that to a secret.
     appservices: {},
 
-    ingress: kube.Ingress("matrix") {
-        metadata+: app.metadata("matrix") {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-            },
-        },
-        spec+: {
-            tls: [
-                {
-                    hosts: [cfg.webDomain],
-                    secretName: "synapse-tls",
-                },
-            ],
-            rules: [
-                {
-                    host: cfg.webDomain,
-                    http: {
-                        paths: [
-                            { path: "/", backend: app.riotSvc.name_port },
-                            { path: "/_matrix", backend: app.synapseSvc.name_port },
-                        ] + (if cfg.cas.enable then [
-                            { path: "/_cas", backend: app.casSvc.name_port },
-                        ] else []) + (if cfg.wellKnown then [
-                            { path: "/.well-known/matrix", backend: app.wellKnown.svc.name_port },
-                        ] else [])
-                    },
-                }
-            ],
-        },
+    ingress: kube.SimpleIngress("matrix") {
+        hosts:: [cfg.webDomain],
+        target_service:: app.riotSvc,
+        metadata+: app.metadata("matrix"),
+        extra_paths:: [
+            { path: "/_matrix", backend: app.synapseSvc.name_port },
+        ] + (if cfg.cas.enable then [
+            { path: "/_cas", backend: app.casSvc.name_port },
+        ] else []) + (if cfg.wellKnown then [
+            { path: "/.well-known/matrix", backend: app.wellKnown.svc.name_port },
+        ] else [])
     },
-
 }
diff --git a/app/onlyoffice/prod.jsonnet b/app/onlyoffice/prod.jsonnet
index ea9958d..28fcb23 100644
--- a/app/onlyoffice/prod.jsonnet
+++ b/app/onlyoffice/prod.jsonnet
@@ -2,7 +2,7 @@
 // JWT secret needs to be generated as follows per environment:
 //     kubectl -n onlyoffice-prod create secret generic documentserver-jwt --from-literal=jwt=$(pwgen 32 1)
 
-local kube = import "../../kube/kube.libsonnet";
+local kube = import "../../kube/hscloud.libsonnet";
 local policies = import "../../kube/policies.libsonnet";
 
 {
@@ -51,7 +51,7 @@
                                 local make(sp, p) = { name: "data", mountPath: p, subPath: sp },
                                 volumeMounts: [
                                     // Per upstream Dockerfile:
-                                    // VOLUME /var/log/$COMPANY_NAME /var/lib/$COMPANY_NAME 
+                                    // VOLUME /var/log/$COMPANY_NAME /var/lib/$COMPANY_NAME
                                     //        /var/www/$COMPANY_NAME/Data /var/lib/postgresql
                                     //        /var/lib/rabbitmq /var/lib/redis
                                     //        /usr/share/fonts/truetype/custom
@@ -75,27 +75,10 @@
         svc: oo.ns.Contain(kube.Service("documentserver")) {
             target_pod:: oo.deploy.spec.template,
         },
-        
-        ingress: oo.ns.Contain(kube.Ingress("office")) {
-            metadata+: {
-                annotations+: {
-                    "kubernetes.io/tls-acme": "true",
-                    "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                },
-            },
-            spec+: {
-                tls: [{ hosts: [cfg.domain], secretName: "office-tls" }],
-                rules: [
-                    {
-                        host: cfg.domain,
-                        http: {
-                            paths: [
-                                { path: "/", backend: oo.svc.name_port, },
-                            ],
-                        },
-                    },
-                ],
-            },
+
+        ingress: oo.ns.Contain(kube.SimpleIngress("office")) {
+            hosts:: [cfg.domain],
+            target_service:: oo.svc,
         },
 
         // Needed because the documentserver runs its own supervisor, and:
diff --git a/bgpwtf/cccampix/kube/ix.libsonnet b/bgpwtf/cccampix/kube/ix.libsonnet
index a3521f8..92e1eca 100644
--- a/bgpwtf/cccampix/kube/ix.libsonnet
+++ b/bgpwtf/cccampix/kube/ix.libsonnet
@@ -1,4 +1,4 @@
-local kube = import "../../../kube/kube.libsonnet";
+local kube = import "../../../kube/hscloud.libsonnet";
 
 {
     IX: {
@@ -425,81 +425,28 @@
             },
         },
 
-        ingress: kube.Ingress("ingress") {
-            metadata+: ix.metadata("public") {
-                annotations+: {
-                    "kubernetes.io/tls-acme": "true",
-                    "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                    "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-                },
-            },
-            spec+: {
-                tls: [
-                    { hosts: [cfg.frontend.domain], secretName: "public-tls"}
-                ],
-                rules: [
-                    {
-                        host: cfg.frontend.domain,
-                        http: {
-                            paths: [
-                                { path: "/", backend: ix.frontend.svc.name_port },
-                            ],
-                        },
-                    },
-                ],
-            },
+        ingress: kube.SimpleIngress("ingress") {
+            hosts:: [cfg.frontend.domain],
+            target_service:: ix.frontend.svc,
+            metadata+: ix.metadata("public"),
         },
 
-        aliceIngress: kube.Ingress("alice") {
+        aliceIngress: kube.SimpleIngress("alice") {
+            hosts:: [cfg.alice.domain],
+            target_service:: ix.alice.svc,
+            metadata+: ix.metadata("alice"),
+        },
+
+        grpcIngress: kube.SimpleIngress("grpc") {
+            hosts:: [cfg.verifier.domain],
+            target_service:: ix.verifier.svc,
             metadata+: ix.metadata("alice") {
                 annotations+: {
-                    "kubernetes.io/tls-acme": "true",
-                    "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                    "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-                },
-            },
-            spec+: {
-                tls: [
-                    { hosts: [cfg.alice.domain], secretName: "alice-tls"}
-                ],
-                rules: [
-                    {
-                        host: cfg.alice.domain,
-                        http: {
-                            paths: [
-                                { path: "/", backend: ix.alice.svc.name_port },
-                            ],
-                        },
-                    },
-                ],
-            },
-        },
-
-        grpcIngress: kube.Ingress("grpc") {
-            metadata+: ix.metadata("grpc") {
-                annotations+: {
-                    "kubernetes.io/tls-acme": "true",
-                    "cert-manager.io/cluster-issuer": "letsencrypt-prod",
                     "kubernetes.io/ingress.class": "nginx",
                     "nginx.ingress.kubernetes.io/ssl-redirect": "true",
                     "nginx.ingress.kubernetes.io/backend-protocol": "GRPC",
                 },
             },
-            spec+: {
-                tls: [
-                    { hosts: [cfg.verifier.domain], secretName: "grpc-tls"}
-                ],
-                rules: [
-                    {
-                        host: cfg.verifier.domain,
-                        http: {
-                            paths: [
-                                { path: "/", backend: ix.verifier.svc.name_port },
-                            ],
-                        },
-                    },
-                ],
-            },
         },
     },
 }
diff --git a/bgpwtf/internet/kube/prod.jsonnet b/bgpwtf/internet/kube/prod.jsonnet
index 0644ab3..e905e67 100644
--- a/bgpwtf/internet/kube/prod.jsonnet
+++ b/bgpwtf/internet/kube/prod.jsonnet
@@ -1,4 +1,4 @@
-local kube = import '../../../kube/kube.libsonnet';
+local kube = import '../../../kube/hscloud.libsonnet';
 
 {
     local internet = self,
@@ -59,27 +59,9 @@
         target_pod:: internet.deployment.spec.template,
     },
 
-    ingress: kube.Ingress("frontend") {
-        metadata+: internet.metadata("frontend") {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-            },
-        },
-        spec+: {
-            tls: [
-                { hosts: [cfg.domain], secretName: "frontend-tls"}
-            ],
-            rules: [
-                {
-                    host: cfg.domain,
-                    http: {
-                        paths: [
-                            { path: "/", backend: internet.svc.name_port },
-                        ],
-                    },
-                },
-            ],
-        },
+    ingress: kube.SimpleIngress("frontend") {
+        hosts:: [cfg.domain],
+        target_service:: internet.svc,
+        metadata+: internet.metadata("frontend"),
     },
 }
diff --git a/bgpwtf/speedtest/kube/prod.jsonnet b/bgpwtf/speedtest/kube/prod.jsonnet
index 6b57ce2..e9b1d59 100644
--- a/bgpwtf/speedtest/kube/prod.jsonnet
+++ b/bgpwtf/speedtest/kube/prod.jsonnet
@@ -1,4 +1,4 @@
-local kube = import '../../../kube/kube.libsonnet';
+local kube = import '../../../kube/hscloud.libsonnet';
 
 {
     local speedtest = self,
@@ -58,28 +58,9 @@
         target_pod:: speedtest.deployment.spec.template,
     },
 
-    ingress: kube.Ingress("public") {
-        metadata+: speedtest.metadata("public") {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-            },
-        },
-        spec+: {
-            tls: [
-                { hosts: [cfg.domain], secretName: "public-tls"}
-            ],
-            rules: [
-                {
-                    host: cfg.domain,
-                    http: {
-                        paths: [
-                            { path: "/", backend: speedtest.svc.name_port },
-                        ],
-                    },
-                },
-            ],
-        },
+    ingress: kube.SimpleIngress("public") {
+        hosts:: [cfg.domain],
+        target_service:: speedtest.svc,
+        metadata+: speedtest.metadata("public"),
     },
 }
diff --git a/cluster/kube/lib/rook.libsonnet b/cluster/kube/lib/rook.libsonnet
index dba8234..0392eda 100644
--- a/cluster/kube/lib/rook.libsonnet
+++ b/cluster/kube/lib/rook.libsonnet
@@ -1,6 +1,6 @@
 # Deploy Rook/Ceph Operator
 
-local kube = import "../../../kube/kube.libsonnet";
+local kube = import "../../../kube/hscloud.libsonnet";
 local policies = import "../../../kube/policies.libsonnet";
 
 local oa = kube.OpenAPI;
@@ -753,31 +753,9 @@
             },
         },
 
-        dashboardIngress: kube.Ingress(cluster.name("dashboard")) {
-            metadata+: cluster.metadata {
-                annotations+: {
-                    "kubernetes.io/tls-acme": "true",
-                    "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                },
-            },
-            spec+: {
-                tls: [
-                    {
-                        hosts: ["%s.hswaw.net" % name],
-                        secretName: cluster.name("dashboard"),
-                    },
-                ],
-                rules: [
-                    {
-                        host: "%s.hswaw.net" % name,
-                        http: {
-                            paths: [
-                                { path: "/", backend: cluster.dashboardService.name_port },
-                            ]
-                        },
-                    }
-                ],
-            },
+        dashboardIngress: kube.SimpleIngress(cluster.name("dashboard")) {
+            hosts:: ["%s.hswaw.net" % name],
+            target_service:: cluster.dashboardService,
         },
 
         # Benji is a backup tool, external to rook, that we use for backing up
diff --git a/devtools/gerrit/kube/gerrit.libsonnet b/devtools/gerrit/kube/gerrit.libsonnet
index 420ad63..dd34762 100644
--- a/devtools/gerrit/kube/gerrit.libsonnet
+++ b/devtools/gerrit/kube/gerrit.libsonnet
@@ -1,4 +1,4 @@
-local kube = import "../../../kube/kube.libsonnet";
+local kube = import "../../../kube/hscloud.libsonnet";
 
 {
     local gerrit = self,
@@ -225,28 +225,9 @@
         },
     },
 
-    ingress: kube.Ingress(gerrit.name("gerrit")) {
-        metadata+: gerrit.metadata("ingress") {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-            },
-        },
-        spec+: {
-            tls: [
-                { hosts: [cfg.domain], secretName: gerrit.name("acme") },
-            ],
-            rules: [
-                {
-                    host: cfg.domain, 
-                    http: {
-                        paths: [
-                            { path: "/", backend: gerrit.svc.name_port },
-                        ],
-                    },
-                }
-            ],
-        },
+    ingress: kube.SimpleIngress(gerrit.name("gerrit")) {
+        hosts:: [cfg.domain],
+        target_service:: gerrit.svc,
+        metadata+: gerrit.metadata("ingress"),
     },
 }
diff --git a/devtools/issues/redmine.libsonnet b/devtools/issues/redmine.libsonnet
index 5e656db..e558d2b 100644
--- a/devtools/issues/redmine.libsonnet
+++ b/devtools/issues/redmine.libsonnet
@@ -1,4 +1,4 @@
-local kube = import "../../kube/kube.libsonnet";
+local kube = import "../../kube/hscloud.libsonnet";
 local postgres = import "../../kube/postgres.libsonnet";
 
 {
@@ -130,32 +130,9 @@
         target_pod:: app.deployment.spec.template,
     },
 
-    ingress: app.ns.Contain(kube.Ingress("redmine")) {
-        metadata+: {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-            },
-        },
-        spec+: {
-            tls: [
-                {
-                    hosts: [cfg.domain],
-                    secretName: "redmine-tls",
-                },
-            ],
-            rules: [
-                {
-                    host: cfg.domain,
-                    http: {
-                        paths: [
-                            { path: "/", backend: app.svc.name_port },
-                        ]
-                    },
-                }
-            ],
-        },
+    ingress: app.ns.Contain(kube.SimpleIngress("redmine")) {
+        hosts:: [cfg.domain],
+        target_service:: app.svc,
     },
 
     b: (if std.length(cfg.b.domains) > 0 then {
@@ -182,34 +159,10 @@
         svc: app.ns.Contain(kube.Service("b")) {
             target_pod:: app.b.deployment.spec.template,
         },
-        ingress: app.ns.Contain(kube.Ingress("b")) {
-            metadata+: {
-                annotations+: {
-                    "kubernetes.io/tls-acme": "true",
-                    "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                    "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-                },
-            },
-            spec+: {
-                tls: [
-                    {
-                        hosts: cfg.b.domains,
-                        secretName: "b-tls",
-                    },
-                ],
-                rules: [
-                    {
-                        host: domain,
-                        http: {
-                            paths: [
-                                { path: "/", backend: app.b.svc.name_port },
-                            ]
-                        },
-                    }
-                    for domain in cfg.b.domains
-                ],
-            },
-        }
+        ingress: app.ns.Contain(kube.SimpleIngress("b")) {
+            hosts:: cfg.b.domains,
+            target_service:: app.b.svc,
+        },
     } else {}),
 
 }
diff --git a/hswaw/capacifier/prod.jsonnet b/hswaw/capacifier/prod.jsonnet
index 1f4af02..fa508bb 100644
--- a/hswaw/capacifier/prod.jsonnet
+++ b/hswaw/capacifier/prod.jsonnet
@@ -1,4 +1,4 @@
-local kube = import "../../kube/kube.libsonnet";
+local kube = import "../../kube/hscloud.libsonnet";
 
 {
     local top = self,
@@ -50,26 +50,8 @@
         target_pod:: top.deployment.spec.template,
     },
 
-    ingress: top.ns.Contain(kube.Ingress(cfg.name)) {
-        metadata+: {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-            },
-        },
-        spec+: {
-            tls: [ { hosts: [ cfg.domain ], secretName: cfg.name + "-tls" } ],
-            rules: [
-                {
-                    host: cfg.domain,
-                    http: {
-                        paths:  [
-                            { path: "/", backend: top.service.name_port },
-                        ],
-                    },
-                },
-            ],
-        },
+    ingress: top.ns.Contain(kube.SimpleIngress(cfg.name)) {
+        hosts:: [cfg.domain],
+        target_service:: top.service,
     },
 }
diff --git a/hswaw/ldapweb/prod.jsonnet b/hswaw/ldapweb/prod.jsonnet
index 589df76..aebd110 100644
--- a/hswaw/ldapweb/prod.jsonnet
+++ b/hswaw/ldapweb/prod.jsonnet
@@ -1,4 +1,4 @@
-local kube = import "../../kube/kube.libsonnet";
+local kube = import "../../kube/hscloud.libsonnet";
 
 {
     local top = self,
@@ -50,26 +50,8 @@
         target_pod:: top.deployment.spec.template,
     },
 
-    ingress: top.ns.Contain(kube.Ingress(cfg.name)) {
-        metadata+: {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-            },
-        },
-        spec+: {
-            tls: [ { hosts: [ cfg.domain ], secretName: cfg.name + "-tls" } ],
-            rules: [
-                {
-                    host: cfg.domain,
-                    http: {
-                        paths:  [
-                            { path: "/", backend: top.service.name_port },
-                        ],
-                    },
-                },
-            ],
-        },
+    ingress: top.ns.Contain(kube.SimpleIngress(cfg.name)) {
+        hosts:: [cfg.domain],
+        target_service:: top.service,
     },
 }
diff --git a/hswaw/oodviewer/prod.jsonnet b/hswaw/oodviewer/prod.jsonnet
index e785808..e7fab3d 100644
--- a/hswaw/oodviewer/prod.jsonnet
+++ b/hswaw/oodviewer/prod.jsonnet
@@ -2,7 +2,7 @@
 //
 // See README.md for more information.
 
-local kube = import "../../kube/kube.libsonnet";
+local kube = import "../../kube/hscloud.libsonnet";
 
 {
     local top = self,
@@ -60,26 +60,8 @@
         target_pod:: top.deploy.spec.template,
     },
 
-    ingress: top.ns.Contain(kube.Ingress("oodviewer")) {
-        metadata+: {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-            },
-        },
-        spec+: {
-            tls: [ { hosts: [ cfg.domain ], secretName: "oodviewer-tls" } ],
-            rules: [
-                {
-                    host: cfg.domain,
-                    http: {
-                        paths:  [
-                            { path: "/", backend: top.service.name_port },
-                        ],
-                    },
-                },
-            ],
-        },
-    }
+    ingress: top.ns.Contain(kube.SimpleIngress("oodviewer")) {
+        hosts:: [cfg.domain],
+        target_service:: top.service,
+    },
 }
diff --git a/hswaw/paperless/paperless.libsonnet b/hswaw/paperless/paperless.libsonnet
index 8008a69..1d5965f 100644
--- a/hswaw/paperless/paperless.libsonnet
+++ b/hswaw/paperless/paperless.libsonnet
@@ -6,7 +6,7 @@
 # first user as such:
 #   kubectl -n paperless exec -it deploy/paperless -c paperless -- python ./manage.py shell -c "from django.contrib.auth.models import User; u = User.objects.get_by_natural_key('informatic'); u.is_superuser = True; u.is_staff = True; u.save()"
 
-local kube = import "../../kube/kube.libsonnet";
+local kube = import "../../kube/hscloud.libsonnet";
 local postgres = import "../../kube/postgres.libsonnet";
 local redis = import "../../kube/redis.libsonnet";
 
@@ -171,26 +171,8 @@
         target_pod:: app.deploy.spec.template,
     },
 
-    ingress: app.ns.Contain(kube.Ingress("paperless")) {
-        metadata+: {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-            },
-        },
-        spec+: {
-            tls: [ { hosts: [ cfg.domain ], secretName: "paperless-tls" } ],
-            rules: [
-                {
-                    host: cfg.domain,
-                    http: {
-                        paths:  [
-                            { path: "/", backend: app.service.name_port },
-                        ],
-                    },
-                },
-            ],
-        },
-    }
+    ingress: app.ns.Contain(kube.SimpleIngress("paperless")) {
+        hosts:: [cfg.domain],
+        target_service:: app.service,
+    },
 }
diff --git a/hswaw/site/prod.jsonnet b/hswaw/site/prod.jsonnet
index be5be7e..252187b 100644
--- a/hswaw/site/prod.jsonnet
+++ b/hswaw/site/prod.jsonnet
@@ -1,4 +1,4 @@
-local kube = import "../../kube/kube.libsonnet";
+local kube = import "../../kube/hscloud.libsonnet";
 
 {
     local top = self,
@@ -45,27 +45,8 @@
         target_pod:: top.deployment.spec.template,
     },
 
-    ingress: top.ns.Contain(kube.Ingress(cfg.name)) {
-        metadata+: {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-            },
-        },
-        spec+: {
-            tls: [ { hosts: cfg.domains, secretName: cfg.name + "-tls" } ],
-            rules: [
-                {
-                    host: domain,
-                    http: {
-                        paths:  [
-                            { path: "/", backend: top.service.name_port },
-                        ],
-                    },
-                }
-                for domain in cfg.domains
-            ],
-        },
+    ingress: top.ns.Contain(kube.SimpleIngress(cfg.name)) {
+        hosts:: cfg.domains,
+        target_service:: top.service,
     },
 }
diff --git a/kube/hscloud.libsonnet b/kube/hscloud.libsonnet
new file mode 100644
index 0000000..bbfa07a
--- /dev/null
+++ b/kube/hscloud.libsonnet
@@ -0,0 +1,34 @@
+// HSWAW / hscloud / k0-specific extensions to kube.libsonnet
+
+local kube = import "kube.libsonnet";
+
+kube {
+    // Basic Ingress config pointing `hosts` to `target_service`, with HTTPS set up
+    SimpleIngress(name): kube.Ingress(name) {
+        local ingress = self,
+        hosts:: error "hosts must be defined",
+        target_service:: error "target_service must be defined",
+        extra_paths:: [],
+        metadata+: {
+            annotations+: {
+                'kubernetes.io/tls-acme': 'true',
+                'cert-manager.io/cluster-issuer': 'letsencrypt-prod',
+                'nginx.ingress.kubernetes.io/proxy-body-size': '0',
+            },
+        },
+        spec+: {
+            tls: [{ hosts: ingress.hosts, secretName: name + '-tls' }],
+            rules: [
+                {
+                    host: host,
+                    http: {
+                        paths: [
+                            { path: '/', backend: ingress.target_service.name_port },
+                        ] + ingress.extra_paths,
+                    },
+                }
+                for host in ingress.hosts
+            ],
+        },
+    },
+}
diff --git a/ops/monitoring/lib/global.libsonnet b/ops/monitoring/lib/global.libsonnet
index bd6bee3..b3a5eb7 100644
--- a/ops/monitoring/lib/global.libsonnet
+++ b/ops/monitoring/lib/global.libsonnet
@@ -1,4 +1,4 @@
-local kube = import "../../../kube/kube.libsonnet";
+local kube = import "../../../kube/hscloud.libsonnet";
 
 {
     // Global sets up a global tier instance of the hscloud monitoring infrastructure.
@@ -134,26 +134,9 @@
                 },
             },
 
-            ingressAPI: ns.Contain(kube.Ingress("victoria-api")) {
-                metadata+: {
-                    annotations+: {
-                        "kubernetes.io/tls-acme": "true",
-                        "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                    },
-                },
-                spec+: {
-                    tls: [
-                        { hosts: [cfg.hosts.globalAPI], secretName: "ingress-tls" },
-                    ],
-                    rules: [
-                        {
-                            host: cfg.hosts.globalAPI,
-                            http: {
-                                paths: [ { path: "/", backend: { serviceName: victoria.serviceAPI.metadata.name, servicePort: 8427 } }, ],
-                            },
-                        }
-                    ],
-                },
+            ingressAPI: ns.Contain(kube.SimpleIngress("victoria-api")) {
+                hosts:: [cfg.hosts.globalAPI],
+                target_service:: victoria.serviceAPI,
             },
         },
 
@@ -279,26 +262,9 @@
                 },
             },
 
-            ingress: ns.Contain(kube.Ingress("grafana-public")) {
-                metadata+: {
-                    annotations+: {
-                        "kubernetes.io/tls-acme": "true",
-                        "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                    },
-                },
-                spec+: {
-                    tls: [
-                        { hosts: [cfg.hosts.globalDashboard], secretName: "ingress-grafana-tls" },
-                    ],
-                    rules: [
-                        {
-                            host: cfg.hosts.globalDashboard,
-                            http: {
-                                paths: [ { path: "/", backend: { serviceName: grafana.service.metadata.name, servicePort: 3000 } }, ],
-                            },
-                        }
-                    ],
-                },
+            ingress: ns.Contain(kube.SimpleIngress("grafana-public")) {
+                hosts:: [cfg.hosts.globalDashboard],
+                target_service:: grafana.service,
             },
         },
     }
diff --git a/ops/sso/kube/sso.libsonnet b/ops/sso/kube/sso.libsonnet
index 7b51c72..58e3f8b 100644
--- a/ops/sso/kube/sso.libsonnet
+++ b/ops/sso/kube/sso.libsonnet
@@ -1,6 +1,6 @@
 #    kubectl create secret generic sso --from-literal=secret_key=$(pwgen 24 1) --from-literal=ldap_bind_password=...
 
-local kube = import "../../../kube/kube.libsonnet";
+local kube = import "../../../kube/hscloud.libsonnet";
 
 {
     local app = self,
@@ -104,31 +104,8 @@
         },
     },
 
-    ingress: app.ns.Contain(kube.Ingress("sso")) {
-        metadata+: {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-            },
-        },
-        spec+: {
-            tls: [
-                {
-                    hosts: [cfg.domain],
-                    secretName: "sso-tls",
-                },
-            ],
-            rules: [
-                {
-                    host: cfg.domain,
-                    http: {
-                        paths: [
-                            { path: "/", backend: app.svc.name_port },
-                        ]
-                    },
-                }
-            ],
-        },
+    ingress: app.ns.Contain(kube.SimpleIngress("sso")) {
+        hosts:: [cfg.domain],
+        target_service:: app.svc,
     },
 }
diff --git a/personal/q3k/annoyatron/prod.jsonnet b/personal/q3k/annoyatron/prod.jsonnet
index c693cfd..84f2f6e 100644
--- a/personal/q3k/annoyatron/prod.jsonnet
+++ b/personal/q3k/annoyatron/prod.jsonnet
@@ -1,4 +1,4 @@
-local kube = import '../../../kube/kube.libsonnet';
+local kube = import '../../../kube/hscloud.libsonnet';
 {
     local annoyatron = self,
     local cfg = self.cfg,
@@ -46,28 +46,11 @@
             ],
         },
     },
-    ingress: kube.Ingress("annoyatron") {
+    ingress: kube.SimpleIngress("annoyatron") {
         metadata+: {
             namespace: "q3k",
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-            },
         },
-        spec+: {
-            tls: [
-                { hosts: [cfg.domain], secretName: "annoyatron-tls" },
-            ],
-            rules: [
-                {
-                    host: cfg.domain,
-                    http: {
-                        paths: [
-                            { path: "/", backend: annoyatron.svc.name_port },
-                        ],
-                    },
-                }
-            ],
-        },
+        hosts:: [cfg.domain],
+        target_service:: annoyatron.svc,
     },
 }
diff --git a/personal/q3k/ppsa.jsonnet b/personal/q3k/ppsa.jsonnet
index 60651c7..c5bf90a 100644
--- a/personal/q3k/ppsa.jsonnet
+++ b/personal/q3k/ppsa.jsonnet
@@ -1,4 +1,4 @@
-local kube = import "../../kube/kube.libsonnet";
+local kube = import "../../kube/hscloud.libsonnet";
 
 {
     local top = self,
@@ -33,29 +33,8 @@
     svc: top.ns.Contain(kube.Service("ppsa-jsonapi")) {
         target_pod:: top.deploy.spec.template,
     },
-    ingress: top.ns.Contain(kube.Ingress("ppsa-jsonapi")) {
-        metadata+: {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-            },
-        },
-        spec+: {
-            tls: [
-                { hosts: [ "ppsa.app.q3k.org"], secretName: "ppsa-jsonapi-tls", },
-            ],
-            rules: [
-                {
-                    host: "ppsa.app.q3k.org",
-                    http: {
-                        paths: [
-                            { path: "/", backend: top.svc.name_port },
-                        ],
-                    },
-                },
-            ],
-        },
+    ingress: top.ns.Contain(kube.SimpleIngress("ppsa-jsonapi")) {
+        hosts:: ["ppsa.app.q3k.org"],
+        target_service:: top.svc,
     },
-
 }
diff --git a/personal/q3k/rc3.jsonnet b/personal/q3k/rc3.jsonnet
index 91f3969..b4112f2 100644
--- a/personal/q3k/rc3.jsonnet
+++ b/personal/q3k/rc3.jsonnet
@@ -1,4 +1,4 @@
-local kube = import "../../kube/kube.libsonnet";
+local kube = import "../../kube/hscloud.libsonnet";
 
 {
     local rc3 = self,
@@ -32,29 +32,11 @@
         },
         target_pod:: rc3.deploy.spec.template,
     },
-    ingress: kube.Ingress("rc3-data") {
+    ingress: kube.SimpleIngress("rc3-data") {
         metadata+: {
             namespace: "personal-q3k",
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-            },
         },
-        spec+: {
-            tls: [
-                { hosts: [ "rc3-data.q3k.org"], secretName: "rc3-data-tls", },
-            ],
-            rules: [
-                {
-                    host: "rc3-data.q3k.org",
-                    http: {
-                        paths: [
-                            { path: "/", backend: rc3.svc.name_port },
-                        ],
-                    },
-                },
-            ],
-        },
+        hosts:: ["rc3-data.q3k.org"],
+        target_service:: rc3.svc,
     },
 }
diff --git a/personal/q3k/wow/lib.libsonnet b/personal/q3k/wow/lib.libsonnet
index d9ddc3d..03127e9 100644
--- a/personal/q3k/wow/lib.libsonnet
+++ b/personal/q3k/wow/lib.libsonnet
@@ -1,4 +1,4 @@
-local kube = import "../../../kube/kube.libsonnet";
+local kube = import "../../../kube/hscloud.libsonnet";
 
 {
     local wow = self,
@@ -272,30 +272,8 @@
             ],
         },
     },
-    panelIngress: ns.Contain(kube.Ingress(cfg.prefix + "panel")) {
-        metadata+: {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-            },
-        },
-        spec+: {
-            tls: [
-                {
-                    hosts: [cfg.panel.domain],
-                    secretName: cfg.prefix + "panel-tls",
-                },
-            ],
-            rules: [
-                {
-                    host: cfg.panel.domain,
-                    http: {
-                        paths: [
-                            { path: "/", backend: wow.panelSvc.name_port },
-                        ],
-                    },
-                }
-            ],
-        },
+    panelIngress: ns.Contain(kube.SimpleIngress(cfg.prefix + "panel")) {
+        hosts:: [cfg.panel.domain],
+        target_service:: wow.panelSvc,
     },
 }
diff --git a/personal/radex/demo-next/prod.jsonnet b/personal/radex/demo-next/prod.jsonnet
index dce74b9..c5abec7 100644
--- a/personal/radex/demo-next/prod.jsonnet
+++ b/personal/radex/demo-next/prod.jsonnet
@@ -1,4 +1,4 @@
-local kube = import "../../../kube/kube.libsonnet";
+local kube = import "../../../kube/hscloud.libsonnet";
 
 {
     local top = self,
@@ -58,26 +58,8 @@
         target_pod:: top.deployment.spec.template,
     },
 
-    ingress: top.ns.Contain(kube.Ingress(cfg.name)) {
-        metadata+: {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-            },
-        },
-        spec+: {
-            tls: [ { hosts: [ cfg.domain ], secretName: cfg.name + "-tls" } ],
-            rules: [
-                {
-                    host: cfg.domain,
-                    http: {
-                        paths:  [
-                            { path: "/", backend: top.service.name_port },
-                        ],
-                    },
-                },
-            ],
-        },
-    }
+    ingress: top.ns.Contain(kube.SimpleIngress(cfg.name)) {
+        hosts:: [cfg.domain],
+        target_service:: top.service,
+    },
 }
diff --git a/personal/radex/demo/prod.jsonnet b/personal/radex/demo/prod.jsonnet
index 23cc20f..a5f7678 100644
--- a/personal/radex/demo/prod.jsonnet
+++ b/personal/radex/demo/prod.jsonnet
@@ -1,4 +1,4 @@
-local kube = import "../../../kube/kube.libsonnet";
+local kube = import "../../../kube/hscloud.libsonnet";
 
 {
     local top = self,
@@ -38,26 +38,8 @@
         target_pod:: top.deployment.spec.template,
     },
 
-    ingress: top.ns.Contain(kube.Ingress(cfg.name)) {
-        metadata+: {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-                "nginx.ingress.kubernetes.io/proxy-body-size": "0",
-            },
-        },
-        spec+: {
-            tls: [ { hosts: [ cfg.domain ], secretName: cfg.name + "-tls" } ],
-            rules: [
-                {
-                    host: cfg.domain,
-                    http: {
-                        paths:  [
-                            { path: "/", backend: top.service.name_port },
-                        ],
-                    },
-                },
-            ],
-        },
-    }
+    ingress: top.ns.Contain(kube.SimpleIngress(cfg.name)) {
+        hosts:: [cfg.domain],
+        target_service:: top.service,
+    },
 }
diff --git a/personal/vuko/shells/prod.jsonnet b/personal/vuko/shells/prod.jsonnet
index ed04483..bc654e6 100644
--- a/personal/vuko/shells/prod.jsonnet
+++ b/personal/vuko/shells/prod.jsonnet
@@ -1,5 +1,5 @@
 # this is libjsonnet library for kubernetes related things
-local kube = import '../../../kube/kube.libsonnet';
+local kube = import '../../../kube/hscloud.libsonnet';
 
 {
     local shells = self,
@@ -137,27 +137,9 @@
 
     # ingress creates VirtualHost on ingress.k0.hswaw.net forwaring http(s)
     # requests to your domain to specified Pod/container
-    ingress: kube.Ingress("frontend") {
-        metadata+: shells.metadata("frontend") {
-            annotations+: {
-                "kubernetes.io/tls-acme": "true",
-                "cert-manager.io/cluster-issuer": "letsencrypt-prod",
-            },
-        },
-        spec+: {
-            tls: [
-                { hosts: [cfg.domain], secretName: "shells-frontend-tls"}
-            ],
-            rules: [
-                {
-                    host: cfg.domain,
-                    http: {
-                        paths: [
-                            { path: "/", backend: shells.svc.name_port },
-                        ],
-                    },
-                },
-            ],
-        },
+    ingress: kube.SimpleIngress("frontend") {
+        hosts:: [cfg.domain],
+        target_service:: shells.svc,
+        metadata+: shells.metadata("frontend"),
     },
 }