hswaw: add kasownik
Change-Id: I48739f9d4ecb8244a2baff5d38a308f7612940eb
Reviewed-on: https://gerrit.hackerspace.pl/c/hscloud/+/1990
Reviewed-by: informatic <informatic@hackerspace.pl>
diff --git a/cluster/kube/k0.libsonnet b/cluster/kube/k0.libsonnet
index 40d582d..c7c7c21 100644
--- a/cluster/kube/k0.libsonnet
+++ b/cluster/kube/k0.libsonnet
@@ -385,6 +385,7 @@
{ namespace: "cebulacamp", dns: "cebula.camp" },
{ namespace: "engelsystem-prod", dns: "engelsystem.hackerspace.pl" },
{ namespace: "invoicer", dns: "invoicer.hackerspace.pl" },
+ { namespace: "kasownik", dns: "kasownik.hackerspace.pl" },
{ namespace: "labelmaker", dns: "label.hackerspace.pl" },
{ namespace: "labelmaker", dns: "*.label.hackerspace.pl" },
{ namespace: "ldapweb", dns: "profile.hackerspace.pl" },
@@ -484,6 +485,9 @@
"arsenicum",
"radex",
],
+ "kasownik": [
+ "radex",
+ ],
"labelmaker": [
"radex",
],
diff --git a/hswaw/kasownik/OWNERS b/hswaw/kasownik/OWNERS
new file mode 100644
index 0000000..27671b7
--- /dev/null
+++ b/hswaw/kasownik/OWNERS
@@ -0,0 +1,3 @@
+owners:
+ - informatic
+ - radex
diff --git a/hswaw/kasownik/README.md b/hswaw/kasownik/README.md
new file mode 100644
index 0000000..a1dc1fa
--- /dev/null
+++ b/hswaw/kasownik/README.md
@@ -0,0 +1,5 @@
+# kasownik
+
+Warsaw Hackerspace Membership Management System
+
+Source and docs: https://code.hackerspace.pl/hswaw/kasownik
diff --git a/hswaw/kasownik/clear_lockfile.jsonnet b/hswaw/kasownik/clear_lockfile.jsonnet
new file mode 100644
index 0000000..f254486
--- /dev/null
+++ b/hswaw/kasownik/clear_lockfile.jsonnet
@@ -0,0 +1,42 @@
+local kube = import "../../kube/hscloud.libsonnet";
+
+// NOTE: Run `kubectl -n kasownik delete job/kasownik-clear-lockfile; kubecfg update hswaw/kasownik/clear_lockfile.jsonnet` to clear lockfile
+// This will not work if cronjob pod is still running (volume won't be mounted)
+{
+ local top = self,
+ local cfg = self.cfg,
+
+ cfg:: {
+ name: 'kasownik',
+ namespace: 'kasownik',
+ },
+
+ local ns = kube.Namespace(cfg.namespace),
+
+ clear_lockfile: ns.Contain(kube.Job(cfg.name + '-clear-lockfile')) {
+ spec+: {
+ ttlSecondsAfterFinished: 10, // NOTE: does not work, requires k8s 1.23
+ template+: {
+ spec+: {
+ containers_: {
+ default: kube.Container("default") {
+ image: 'alpine:3.20.1',
+ volumeMounts_: {
+ data: { mountPath: '/data' },
+ },
+ command: ["sh", "-c", |||
+ set -e -x
+ rm /data/kasownik.lock
+ |||]
+ }
+ },
+ volumes_: {
+ data: {
+ persistentVolumeClaim: { claimName: cfg.name + '-fetch-data' },
+ },
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/hswaw/kasownik/prod.jsonnet b/hswaw/kasownik/prod.jsonnet
new file mode 100644
index 0000000..41c654c
--- /dev/null
+++ b/hswaw/kasownik/prod.jsonnet
@@ -0,0 +1,183 @@
+local kube = import "../../kube/hscloud.libsonnet";
+local postgres = import "../../kube/postgres.libsonnet";
+
+// Setup:
+// - create `kasownik` and `kasownik-fetch` secrets
+// - run `./manage.py syncdb` to create db
+// - create `cache` folder in the fetch_data PVC
+{
+ local top = self,
+ local cfg = self.cfg,
+
+ cfg:: {
+ name: 'kasownik',
+ namespace: 'kasownik',
+ domain: 'kasownik.hackerspace.pl',
+
+ image: 'registry.k0.hswaw.net/radex/kasownik:20240710124006',
+
+ oauthClientId: 'd5770622-a661-45d1-9356-b8cb77de708c',
+ storageClassName: "waw-hdd-redundant-3",
+ },
+
+ secretRefs:: {
+ oauth: { secretKeyRef: { name: cfg.name, key: 'oauth_secret' } },
+ secret_key: { secretKeyRef: { name: cfg.name, key: 'secret_key' } },
+ ldap: { secretKeyRef: { name: cfg.name, key: 'ldap_password' } },
+ postgres: { secretKeyRef: { name: cfg.name, key: 'postgres_password' } },
+ smtp: { secretKeyRef: { name: cfg.name, key: 'smtp_password' } },
+ fetch: {
+ local name = cfg.name + '-fetch',
+ tdid: { secretKeyRef: { name: name, key: 'tdid' } },
+ alias: { secretKeyRef: { name: name, key: 'alias' } },
+ password: { secretKeyRef: { name: name, key: 'password' } },
+ user_agent: { secretKeyRef: { name: name, key: 'user-agent' } },
+ }
+ },
+
+ local ns = kube.Namespace(cfg.namespace),
+
+ env:: {
+ // Quirk: Must be alphabetically before SQLALCHEMY_DATABASE_URI
+ POSTGRES_PASSWORD: top.secretRefs.postgres,
+ SQLALCHEMY_DATABASE_URI: 'postgresql+psycopg2://%s:%s@%s:%s/%s' % [
+ top.postgres.cfg.username,
+ '$(POSTGRES_PASSWORD)',
+ top.postgres.svc.host,
+ top.postgres.svc.port,
+ top.postgres.cfg.database,
+ ],
+ SPACEAUTH_CONSUMER_KEY: cfg.oauthClientId,
+ SPACEAUTH_CONSUMER_SECRET: top.secretRefs.oauth,
+ SECRET_KEY: top.secretRefs.secret_key,
+ DISABLE_LDAP: 'false',
+ LDAP_URI: 'ldap://ldap.hackerspace.pl',
+ LDAP_BIND_DN: 'cn=kasownik,ou=Services,dc=hackerspace,dc=pl',
+ LDAP_BIND_PASSWORD: top.secretRefs.ldap,
+ SMTP_USER: 'kasownik',
+ SMTP_PASSWORD: top.secretRefs.smtp,
+ MEMBERSHIP_FEES: std.manifestJson({
+ starving: 75,
+ fatty: 150,
+ }),
+ },
+
+ deployment: ns.Contain(kube.Deployment(cfg.name)) {
+ spec+: {
+ replicas: 1,
+ template+: {
+ spec+: {
+ containers_: {
+ default: kube.Container("default") {
+ image: cfg.image,
+ ports_: {
+ http: { containerPort: 5000 },
+ },
+ env_: top.env,
+ livenessProbe: {
+ httpGet: { path: '/', port: 5000 },
+ initialDelaySeconds: 20,
+ periodSeconds: 5 * 60,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+
+ cronjob: ns.Contain(kube.CronJob(cfg.name + '-cronjob')) {
+ spec+: {
+ schedule: "05 11,15,18 * * *", // after Elixir sessions
+ jobTemplate+: {
+ spec+: {
+ template+: {
+ spec+: {
+ containers_: {
+ default: kube.Container("default") {
+ image: cfg.image,
+ volumeMounts_: {
+ data: { mountPath: '/data' },
+ config: { mountPath: '/config' },
+ },
+ command: ["sh", "-c", |||
+ set -e -x
+ python /usr/src/fetch/banking-pekaobiznes.py --config /config/config.ini
+ ./manage.py automatch
+ ./manage.py ldapsync --dry-run
+ |||],
+ env_: top.env + {
+ SCRAPER_TDID: top.secretRefs.fetch.tdid,
+ SCRAPER_ALIAS: top.secretRefs.fetch.alias,
+ SCRAPER_PASSWORD: top.secretRefs.fetch.password,
+ SCRAPER_USER_AGENT: top.secretRefs.fetch.user_agent,
+ },
+ },
+ },
+ volumes_: {
+ data: top.fetch_data.volume,
+ config: top.fetch_config.volume,
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ fetch_config: ns.Contain(kube.ConfigMap(cfg.name + '-fetch-config')) {
+ data: {
+ "config.ini": std.manifestIni({
+ sections: {
+ general: {
+ cache_dir: '/data/cache',
+ lockfile: '/data/kasownik.lock',
+ },
+ database: {
+ uri: '${SQLALCHEMY_DATABASE_URI}'
+ },
+ scraper: {
+ tdid: '${SCRAPER_TDID}',
+ alias: '${SCRAPER_ALIAS}',
+ password: '${SCRAPER_PASSWORD}',
+ user_agent: '${SCRAPER_USER_AGENT}',
+ },
+ logging: {
+ level: 'DEBUG',
+ }
+ }
+ })
+ },
+ },
+
+ // used for lockfile and fetch cache
+ fetch_data: ns.Contain(kube.PersistentVolumeClaim(cfg.name + '-fetch-data')) {
+ storage:: "1Gi",
+ storageClass:: cfg.storageClassName,
+ spec+: {
+ accessModes: ['ReadWriteMany']
+ }
+ },
+
+ postgres: ns.Contain(postgres) {
+ cfg+: {
+ prefix: cfg.name + '-',
+ appName: cfg.name,
+ storageClassName: cfg.storageClassName,
+ version: '15.4',
+
+ database: 'kasownik',
+ username: 'kasownik',
+ password: top.secretRefs.postgres,
+ }
+ },
+
+ service: ns.Contain(kube.Service(cfg.name)) {
+ target:: top.deployment,
+ },
+
+ ingress: ns.Contain(kube.SimpleIngress(cfg.name)) {
+ hosts:: [cfg.domain],
+ target:: top.service,
+ },
+}