From caf65fcaaf78173b4a55274d2e9041fd5939af6a Mon Sep 17 00:00:00 2001 From: Radek Pietruszewski Date: Sat, 28 Oct 2023 23:58:45 +0200 Subject: [PATCH] *: Kill frab, smsgw, toot, covid-formity, voucherchecker Change-Id: I763c758994008db38b47a7e61d3f1b503685aba6 Reviewed-on: https://gerrit.hackerspace.pl/c/hscloud/+/1750 Reviewed-by: q3k --- CEMETERY.md | 11 + README.md | 2 +- app/covid-formity/prod.jsonnet | 105 -------- app/toot/prod.jsonnet | 43 ---- cluster/kube/k0.libsonnet | 3 - hswaw/kube/frab.libsonnet | 103 -------- hswaw/kube/hswaw.jsonnet | 20 -- .../secrets/cipher/prod-capacifier-password | 40 --- .../secrets/cipher/prod-frab-secret-key-base | 40 --- .../secrets/cipher/prod-frab-smtp-password | 40 --- hswaw/kube/secrets/cipher/prod-twilio-token | 40 --- hswaw/kube/smsgw.libsonnet | 81 ------ hswaw/smsgw/BUILD.bazel | 57 ----- hswaw/smsgw/dispatcher.go | 110 -------- hswaw/smsgw/dispatcher_test.go | 206 --------------- hswaw/smsgw/main.go | 226 ---------------- hswaw/smsgw/proto/BUILD.bazel | 24 -- .../proto/gomod-generated-placeholder.go | 1 - hswaw/smsgw/proto/smsgw.proto | 17 -- hswaw/smsgw/twilio.go | 79 ------ hswaw/voucherchecker/BUILD.bazel | 41 --- hswaw/voucherchecker/main.go | 241 ------------------ 22 files changed, 12 insertions(+), 1518 deletions(-) create mode 100644 CEMETERY.md delete mode 100644 app/covid-formity/prod.jsonnet delete mode 100644 app/toot/prod.jsonnet delete mode 100644 hswaw/kube/frab.libsonnet delete mode 100644 hswaw/kube/secrets/cipher/prod-capacifier-password delete mode 100644 hswaw/kube/secrets/cipher/prod-frab-secret-key-base delete mode 100644 hswaw/kube/secrets/cipher/prod-frab-smtp-password delete mode 100644 hswaw/kube/secrets/cipher/prod-twilio-token delete mode 100644 hswaw/kube/smsgw.libsonnet delete mode 100644 hswaw/smsgw/BUILD.bazel delete mode 100644 hswaw/smsgw/dispatcher.go delete mode 100644 hswaw/smsgw/dispatcher_test.go delete mode 100644 hswaw/smsgw/main.go delete mode 100644 hswaw/smsgw/proto/BUILD.bazel delete mode 100644 hswaw/smsgw/proto/gomod-generated-placeholder.go delete mode 100644 hswaw/smsgw/proto/smsgw.proto delete mode 100644 hswaw/smsgw/twilio.go delete mode 100644 hswaw/voucherchecker/BUILD.bazel delete mode 100644 hswaw/voucherchecker/main.go diff --git a/CEMETERY.md b/CEMETERY.md new file mode 100644 index 00000000..4fc836f6 --- /dev/null +++ b/CEMETERY.md @@ -0,0 +1,11 @@ +# hscloud cemetery + +Here's a list of projects and services that are no longer with us (and the last commit that contained them): + +- covid-formity - 633fb2 +- toot - 633fb2 +- frab - 633fb2 +- smsgw - 633fb2 +- voucherchecker - 633fb2 + +RIP. diff --git a/README.md b/README.md index 8ba66e54..1c01ca8c 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Directory Structure Directories you should care about: - - **app**: external services that we host that are somewhat universal: matrix, covid-formity, etc. + - **app**: external services that we host that are somewhat universal: matrix, mastodon, etc. - **bgpwtf**: code related to our little ISP - **cluster**: code related to our Kubernetes cluster (`k0.hswaw.net`) - **dc**: code related to datacenter automation diff --git a/app/covid-formity/prod.jsonnet b/app/covid-formity/prod.jsonnet deleted file mode 100644 index 2ba5adc4..00000000 --- a/app/covid-formity/prod.jsonnet +++ /dev/null @@ -1,105 +0,0 @@ -# covid19.hackerspace.pl, a covid-formity instance. -# 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/hscloud.libsonnet"; -local redis = import "../../kube/redis.libsonnet"; -local postgres = import "../../kube/postgres.libsonnet"; - -{ - local app = self, - local cfg = app.cfg, - cfg:: { - namespace: "covid-formity", - image: "registry.k0.hswaw.net/informatic/covid-formity@sha256:53c5fb0dbc4a6660ab47e39869a516f1e3f833dee5a03867386771bd9ffaf7b8", - domain: "covid19.hackerspace.pl", - altDomains: ["covid.hackerspace.pl", "www.covid.hackerspace.pl"], - }, - - metadata(component):: { - namespace: app.cfg.namespace, - labels: { - "app.kubernetes.io/name": "covid-formity", - "app.kubernetes.io/managed-by": "kubecfg", - "app.kubernetes.io/component": component, - }, - }, - - namespace: kube.Namespace(app.cfg.namespace), - - postgres: postgres { - cfg+: { - namespace: cfg.namespace, - appName: "covid-formity", - database: "covid-formity", - username: "covid-formity", - password: { secretKeyRef: { name: "covid-formity", key: "postgres_password" } }, - }, - }, - - redis: redis { - cfg+: { - namespace: cfg.namespace, - appName: "covid-formity", - password: { secretKeyRef: { name: "covid-formity", key: "redis_password" } }, - storageClassName: app.postgres.cfg.storageClassName, - }, - }, - - deployment: kube.Deployment("covid-formity") { - metadata+: app.metadata("covid-formity"), - spec+: { - replicas: 1, - template+: { - spec+: { - containers_: { - web: kube.Container("covid-formity") { - image: cfg.image, - ports_: { - http: { containerPort: 5000 }, - }, - env_: { - DATABASE_HOSTNAME: "postgres", - DATABASE_USERNAME: app.postgres.cfg.username, - DATABASE_PASSWORD: app.postgres.cfg.password, - CACHE_REDIS_PASSWORD: app.redis.cfg.password, - CACHE_REDIS_URL: "redis://default:$(CACHE_REDIS_PASSWORD)@redis", - DATABASE_NAME: app.postgres.cfg.appName, - SPACEAUTH_CONSUMER_KEY: "covid-formity", - SPACEAUTH_CONSUMER_SECRET: { secretKeyRef: { name: "covid-formity", key: "oauth2_secret" } }, - SECRET_KEY: { secretKeyRef: { name: "covid-formity", key: "secret_key" } }, - SHIPPING_KURJERZY_EMAIL: "qrde@hackerspace.pl", - SHIPPING_KURJERZY_PASSWORD: { secretKeyRef: { name: "covid-formity-shipping", key: "kurjerzy_password" } }, - }, - }, - }, - }, - }, - }, - }, - - svc: kube.Service("covid-formity") { - metadata+: app.metadata("covid-formity"), - target_pod:: app.deployment.spec.template, - spec+: { - ports: [ - { name: "http", port: 5000, targetPort: 5000, protocol: "TCP" }, - ], - type: "ClusterIP", - }, - }, - - ingress: kube.SimpleIngress("covid-formity") { - hosts:: [cfg.domain] + cfg.altDomains, - target_service:: app.svc, - metadata+: app.metadata("covid-formity") { - annotations+: { - "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; } - ", - } - } - }, -} diff --git a/app/toot/prod.jsonnet b/app/toot/prod.jsonnet deleted file mode 100644 index c823be21..00000000 --- a/app/toot/prod.jsonnet +++ /dev/null @@ -1,43 +0,0 @@ -# toot.hackerspace.pl, a Mastodon instance. -# This needs a secret provisioned, create with: -# kubectl -n toot create secret generic mastodon --from-literal=postgres_password=$(pwgen 24 1) - -local kube = import "../../kube/kube.libsonnet"; -local postgres = import "../../kube/postgres.libsonnet"; -local redis = import "../../kube/redis.libsonnet"; - -{ - local app = self, - local cfg = app.cfg, - cfg:: { - namespace: "toot", - }, - - metadata(component):: { - namespace: app.cfg.namespace, - labels: { - "app.kubernetes.io/name": "toot", - "app.kubernetes.io/managed-by": "kubecfg", - "app.kubernetes.io/component": component, - }, - }, - - namespace: kube.Namespace(app.cfg.namespace), - - postgres: postgres { - cfg+: { - namespace: cfg.namespace, - appName: "toot", - database: "mastodon", - username: "mastodon", - password: { secretKeyRef: { name: "mastodon", key: "postgres_password" } }, - }, - }, - - redis: redis { - cfg+: { - namespace: cfg.namespace, - appName: "toot", - }, - }, -} diff --git a/cluster/kube/k0.libsonnet b/cluster/kube/k0.libsonnet index 648ed3fa..e6079e3a 100644 --- a/cluster/kube/k0.libsonnet +++ b/cluster/kube/k0.libsonnet @@ -327,9 +327,6 @@ local admins = import "lib/admins.libsonnet"; // hijacked by other cluster users, you should also state // it here (either as a wildcard, or unary domains). allow_domain: [ - { namespace: "covid-formity", dns: "covid19.hackerspace.pl" }, - { namespace: "covid-formity", dns: "covid.hackerspace.pl" }, - { namespace: "covid-formity", dns: "www.covid.hackerspace.pl" }, { namespace: "inventory", dns: "inventory.hackerspace.pl" }, { namespace: "capacifier", dns: "capacifier.hackerspace.pl" }, { namespace: "ldapweb", dns: "profile.hackerspace.pl" }, diff --git a/hswaw/kube/frab.libsonnet b/hswaw/kube/frab.libsonnet deleted file mode 100644 index 25083e37..00000000 --- a/hswaw/kube/frab.libsonnet +++ /dev/null @@ -1,103 +0,0 @@ -local mirko = import "../../kube/mirko.libsonnet"; -local kube = import "../../kube/kube.libsonnet"; -local postgres = import "../../kube/postgres.libsonnet"; - -{ - local cfg = self.cfg, - cfg:: { - image: "frab/frab@sha256:30051f5153c4f02a8a1bee4b306bd696e2b018f2b13d16bd9c681fc1d633de3e", - storageClassName: error "storageClassName must be set!", - webFQDN: error "webFQDN must be set!", - - secret: { - secretKeyBase: error "secretKeyBase must be set!", - smtpPassword: error "smtpPassword must be set!", - }, - - smtp: { - server: "mail.hackerspace.pl", - from: "frab@hackerspace.pl", - username: "frab", - }, - }, - - component(cfg, env): mirko.Component(env, "frab") { - local frab = self, - cfg+: { - image: cfg.image, - volumes+: { - public: kube.PersistentVolumeClaimVolume(frab.volumePublic), - }, - - pgpass:: { secretKeyRef: { name: frab.makeName("-postgres"), key: "postgres_password", } }, - - container: frab.Container("main") { - volumeMounts_+: { - public: { mountPath: "/home/frab/app/public", }, - }, - // order matters (for POSTGRES_PASS substitution), we don't use env_ - env: [ - { name: "TZ", value: "Europe/Warsaw" }, - { name: "POSTGRES_PASS", valueFrom: frab.cfg.pgpass }, - { name: "DATABASE_URL", value: "postgresql://frab:$(POSTGRES_PASS)@%s/frab" % [frab.postgres.svc.host] }, - { name: "SECRET_KEY_BASE", valueFrom: kube.SecretKeyRef(frab.secret, "secretKeyBase") }, - { name: "FROM_EMAIL", value: cfg.smtp.from }, - { name: "SMTP_ADDRESS", value: cfg.smtp.server }, - { name: "SMTP_USERNAME", value: cfg.smtp.username }, - { name: "SMTP_PASSWORD", valueFrom: kube.SecretKeyRef(frab.secret, "smtpPassword") }, - { name: "SMTP_PORT", value: "587" }, - { name: "SMTP_NOTLS", value: "false" }, - ], - resources: { - // thicc RoR - requests: { - cpu: "100m", - memory: "512Mi", - }, - limits: { - cpu: "1", - memory: "1Gi", - }, - }, - }, - ports+: { - publicHTTP: { - web: { - port: 3000, - dns: cfg.webFQDN, - }, - }, - }, - }, - - secret: kube.Secret(frab.makeName("-secret")) { - metadata+: frab.metadata, - data: cfg.secret, - }, - - postgres: postgres { - cfg+: { - namespace: frab.metadata.namespace, - appName: "frab", - storageClassName: cfg.storageClassName, - prefix: frab.makeName("-postgres") + "-", - database: "frab", - username: "frab", - password: frab.cfg.pgpass, - }, - }, - - volumePublic: kube.PersistentVolumeClaim(frab.makeName("-public")) { - metadata+: frab.metadata, - spec+: { - storageClassName: cfg.storageClassName, - accessModes: ["ReadWriteOnce"], - resources: { - requests: { - storage: "5Gi", - }, - }, - }, - }, - }, -} diff --git a/hswaw/kube/hswaw.jsonnet b/hswaw/kube/hswaw.jsonnet index 172cda3a..4bf81215 100644 --- a/hswaw/kube/hswaw.jsonnet +++ b/hswaw/kube/hswaw.jsonnet @@ -1,9 +1,7 @@ local mirko = import "../../kube/mirko.libsonnet"; local kube = import "../../kube/kube.libsonnet"; -local smsgw = import "smsgw.libsonnet"; local teleimg = import "teleimg.libsonnet"; -local frab = import "frab.libsonnet"; local pretalx = import "pretalx.libsonnet"; local cebulacamp = import "cebulacamp.libsonnet"; @@ -13,18 +11,14 @@ local cebulacamp = import "cebulacamp.libsonnet"; local cfg = self.cfg, cfg+: { - smsgw: smsgw.cfg, teleimg: teleimg.cfg, - frab: frab.cfg, pretalx: pretalx.cfg, cebulacamp: cebulacamp.cfg, }, components: { - smsgw: smsgw.component(cfg.smsgw, env), teleimg: teleimg.teleimg(cfg.teleimg, env), lelegram: teleimg.lelegram(cfg.teleimg, env), - frab: frab.component(cfg.frab, env), pretalx: pretalx.component(cfg.pretalx, env) { cronjob: null, }, @@ -34,26 +28,12 @@ local cebulacamp = import "cebulacamp.libsonnet"; prod: self.hswaw("hswaw-prod") { cfg+: { - smsgw+: { - secret+: { - twilio_token: std.base64(std.split(importstr "secrets/plain/prod-twilio-token", "\n")[0]), - }, - webhookFQDN: "smsgw-webhook-prod.hswaw.net", - }, teleimg+: { webFQDN: "teleimg.hswaw.net", secret+: { telegram_token: std.base64(std.split(importstr "secrets/plain/prod-telegram-token", "\n")[0]), }, }, - frab+: { - storageClassName: "waw-hdd-redundant-3", - webFQDN: "frab.hackerspace.pl", - secret+: { - secretKeyBase: std.base64(std.split(importstr "secrets/plain/prod-frab-smtp-password", "\n")[0]), - smtpPassword: std.base64(std.split(importstr "secrets/plain/prod-frab-secret-key-base", "\n")[0]), - }, - }, pretalx+: { storageClassName: "waw-hdd-redundant-3", webFQDN: "cfp.cebula.camp", diff --git a/hswaw/kube/secrets/cipher/prod-capacifier-password b/hswaw/kube/secrets/cipher/prod-capacifier-password deleted file mode 100644 index ec666cdc..00000000 --- a/hswaw/kube/secrets/cipher/prod-capacifier-password +++ /dev/null @@ -1,40 +0,0 @@ ------BEGIN PGP MESSAGE----- - -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 -=SBGB ------END PGP MESSAGE----- diff --git a/hswaw/kube/secrets/cipher/prod-frab-secret-key-base b/hswaw/kube/secrets/cipher/prod-frab-secret-key-base deleted file mode 100644 index e91bcf2a..00000000 --- a/hswaw/kube/secrets/cipher/prod-frab-secret-key-base +++ /dev/null @@ -1,40 +0,0 @@ ------BEGIN PGP MESSAGE----- - -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 -=hseZ ------END PGP MESSAGE----- diff --git a/hswaw/kube/secrets/cipher/prod-frab-smtp-password b/hswaw/kube/secrets/cipher/prod-frab-smtp-password deleted file mode 100644 index b0deb6c3..00000000 --- a/hswaw/kube/secrets/cipher/prod-frab-smtp-password +++ /dev/null @@ -1,40 +0,0 @@ ------BEGIN PGP MESSAGE----- - -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 -=bRrv ------END PGP MESSAGE----- diff --git a/hswaw/kube/secrets/cipher/prod-twilio-token b/hswaw/kube/secrets/cipher/prod-twilio-token deleted file mode 100644 index d65b61ca..00000000 --- a/hswaw/kube/secrets/cipher/prod-twilio-token +++ /dev/null @@ -1,40 +0,0 @@ ------BEGIN PGP MESSAGE----- - -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 -WtNaMpCfU92nkD2ZwTp96oiLUOVbWEaGGByh -=IR9z ------END PGP MESSAGE----- diff --git a/hswaw/kube/smsgw.libsonnet b/hswaw/kube/smsgw.libsonnet deleted file mode 100644 index a35b8921..00000000 --- a/hswaw/kube/smsgw.libsonnet +++ /dev/null @@ -1,81 +0,0 @@ -local mirko = import "../../kube/mirko.libsonnet"; -local kube = import "../../kube/kube.libsonnet"; - -{ - cfg:: { - secret: { - twilio_token: error "twilio_token must be set", - }, - image: "registry.k0.hswaw.net/q3k/smsgs:1570049853-05c5b491c45de6d960979d4aee8635768f3178e9", - webhookFQDN: error "webhookFQDN must be set", - }, - - component(cfg, env):: mirko.Component(env, "smsgw") { - local smsgw = self, - cfg+: { - image: cfg.image, - container: smsgw.GoContainer("main", "/smsgw/smsgw") { - env_: { - TWILIO_TOKEN: kube.SecretKeyRef(smsgw.secret, "twilio_token"), - }, - command+: [ - "-twilio_friendly_phone", "48732168371", - "-twilio_sid", "AC806ed4bf4b6c80c8f8ea686379b69518", - "-twilio_token", "$(TWILIO_TOKEN)", - "-webhook_listen", "0.0.0.0:5000", - "-webhook_public", "https://%s/" % [ cfg.webhookFQDN ], - ], - }, - ports+: { - publicHTTP: { - webhook: { - port: 5000, - dns: cfg.webhookFQDN, - } - }, - }, - }, - - secret: kube.Secret("smsgw") { - metadata+: smsgw.metadata, - data: cfg.secret, - }, - - // Temporary machinery to access gRPC from outsite. - // In the future, this will be handled by a proxy/API gateway. - // For now, we need this running. - // TODO(q3k): remove this when we have an API GW or proxy. - stopgap: { - local stopgap = self, - - rpcLB: kube.Service("smsgw-tcp-rpc") { - metadata+: smsgw.metadata, - target_pod: smsgw.deployment.spec.template, - spec+: { - type: "LoadBalancer", - ports: [ - { name: "grpc-external", port: 443, targetPort: 4200 }, - ], - }, - }, - - mkClientCert(name, cn):: kube.Certificate(name) { - metadata+: smsgw.metadata, - spec: { - secretName: name, - duration: "35040h0m0s", // 4 years - issuerRef: { - // Contract with cluster/lib/pki.libsonnet. - // Copied over. - name: "pki-ca", - kind: "ClusterIssuer", - }, - commonName: cn, - }, - }, - - kasownikCert: stopgap.mkClientCert("smsgw-tcp-rpc-consumer", "kasownik.external.hswaw.net"), - piorekfCert: stopgap.mkClientCert("smsgw-tcp-rpc-piorekf", "piorekf.person.hswaw.net"), - } - }, -} diff --git a/hswaw/smsgw/BUILD.bazel b/hswaw/smsgw/BUILD.bazel deleted file mode 100644 index 000f20c9..00000000 --- a/hswaw/smsgw/BUILD.bazel +++ /dev/null @@ -1,57 +0,0 @@ -load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_layer", "container_push") -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") - -go_library( - name = "smsgw_lib", - srcs = [ - "dispatcher.go", - "main.go", - "twilio.go", - ], - importpath = "code.hackerspace.pl/hscloud/hswaw/smsgw", - visibility = ["//visibility:private"], - deps = [ - "//go/mirko", - "//hswaw/smsgw/proto", - "@com_github_golang_glog//:glog", - "@org_golang_google_grpc//codes", - "@org_golang_google_grpc//status", - ], -) - -go_binary( - name = "smsgw", - embed = [":smsgw_lib"], - visibility = ["//visibility:public"], -) - -go_test( - name = "smsgw_test", - srcs = ["dispatcher_test.go"], - embed = [":smsgw_lib"], -) - -container_layer( - name = "layer_bin", - directory = "/smsgw/", - files = [ - ":smsgw", - ], -) - -container_image( - name = "runtime", - base = "@prodimage-bionic//image", - layers = [ - ":layer_bin", - ], -) - -container_push( - name = "push", - format = "Docker", - image = ":runtime", - registry = "registry.k0.hswaw.net", - repository = "q3k/smsgs", - tag = "{BUILD_TIMESTAMP}-{STABLE_GIT_COMMIT}", -) diff --git a/hswaw/smsgw/dispatcher.go b/hswaw/smsgw/dispatcher.go deleted file mode 100644 index 60fba323..00000000 --- a/hswaw/smsgw/dispatcher.go +++ /dev/null @@ -1,110 +0,0 @@ -package main - -import ( - "context" - "regexp" - "time" - - "github.com/golang/glog" -) - -// dispatcher is responsible for dispatching incoming SMS messages to subscribers -// that have chosen to receive them, filtering accordingly. -type dispatcher struct { - // New SMS messages to be dispatched. - incoming chan *sms - // New subscribers to send messages to. - subscribers chan *subscriber -} - -// newDispatcher creates a new dispatcher. -func newDispatcher() *dispatcher { - return &dispatcher{ - incoming: make(chan *sms), - subscribers: make(chan *subscriber), - } -} - -// sms received from the upstream provider. -type sms struct { - from string - body string - timestamp time.Time -} - -// subscriber that wants to receive messages with a given body filter. -type subscriber struct { - // regexp to filter message body by - re *regexp.Regexp - // channel to which messages will be sent, must be emptied regularly by the - // subscriber. - data chan *sms - // channel that needs to be closed when the subscriber doesn't want to receive - // any more messages. - cancel chan struct{} -} - -func (p *dispatcher) publish(msg *sms) { - p.incoming <- msg -} - -func (p *dispatcher) subscribe(sub *subscriber) { - p.subscribers <- sub -} - -func (p *dispatcher) run(ctx context.Context) { - // Map of internal IDs to subscribers. Internal IDs are used to remove - // canceled subscribers easily. - subscriberMap := make(map[int64]*subscriber) - // Internal channel that will emit SIDs of subscribers that needs to be - // removed. - subscriberCancel := make(chan int64) - - for { - select { - - // Should the processor close? - case <-ctx.Done(): - return - - // Do we need to remove a given subscriber? - case sid := <-subscriberCancel: - delete(subscriberMap, sid) - - // Do we have a new subscriber? - case sub := <-p.subscribers: - // Generate a SID. A UNIX nanosecond timestamp is enough, since - // we're not running in parallel. - sid := time.Now().UnixNano() - glog.V(5).Infof("New subscriber %x, regexp %v", sid, sub.re) - - // Add to subscriber map. - subscriberMap[sid] = sub - - // On sub.cancel closed, emit info that we need to delete that - // subscriber. - go func() { - _, _ = <-sub.cancel - subscriberCancel <- sid - }() - - // Do we have a new message to dispatch? - case in := <-p.incoming: - for sid, s := range subscriberMap { - glog.V(10).Infof("Considering %x", sid) - // If this subscriber doesn't care, ignore. - if !s.re.MatchString(in.body) { - continue - } - - // Send, non-blocking, to subscriber. This ensures that we - // don't get stuck if a subscriber doesn't drain fast enough. - go func(to *subscriber, sid int64) { - glog.V(10).Infof("Dispatching to %x, %v", sid, to.data) - to.data <- in - glog.V(10).Infof("Dispatched to %x", sid) - }(s, sid) - } - } - } -} diff --git a/hswaw/smsgw/dispatcher_test.go b/hswaw/smsgw/dispatcher_test.go deleted file mode 100644 index a5f39777..00000000 --- a/hswaw/smsgw/dispatcher_test.go +++ /dev/null @@ -1,206 +0,0 @@ -package main - -import ( - "context" - "regexp" - "testing" - "time" -) - -func makeDut() (*dispatcher, context.CancelFunc, context.Context) { - dut := newDispatcher() - - ctx := context.Background() - ctxC, cancelCtx := context.WithCancel(ctx) - go dut.run(ctxC) - - return dut, cancelCtx, ctx -} - -func expectReceived(t *testing.T, s *sms, data chan *sms) { - ticker := time.NewTicker(100 * time.Millisecond) - defer ticker.Stop() - select { - case d := <-data: - if d.from != s.from { - t.Errorf("Received SMS from %q, wanted %q", d.from, s.from) - } - if d.body != s.body { - t.Errorf("Received SMS body %q, wanted %q", d.body, s.body) - } - if d.timestamp != s.timestamp { - t.Errorf("Received SMS timestamp %v, wanted %v", d.timestamp, s.timestamp) - } - case <-ticker.C: - t.Fatalf("Timed out waiting for message") - } -} - -func expectEmpty(t *testing.T, data chan *sms) { - ticker := time.NewTicker(1 * time.Millisecond) - defer ticker.Stop() - select { - case <-data: - t.Fatalf("Received unwanted message") - case <-ticker.C: - } -} - -func TestDispatcher(t *testing.T) { - dut, cancelDut, _ := makeDut() - defer cancelDut() - - data := make(chan *sms) - cancel := make(chan struct{}) - - dut.subscribe(&subscriber{ - re: regexp.MustCompile(".*"), - data: data, - cancel: cancel, - }) - - in := &sms{ - from: "+4821372137", - body: "foo", - timestamp: time.Now(), - } - dut.publish(in) - - // Make sure we ge the message. - expectReceived(t, in, data) - - // Make sure we don't receive the message again. - expectEmpty(t, data) - - // Publish a new message, but this time close our subscriber. - close(cancel) - // Hack: yield. - time.Sleep(1 * time.Millisecond) - - dut.publish(in) - expectEmpty(t, data) -} - -type testSubscriber struct { - re *regexp.Regexp - data chan *sms - cancel chan struct{} -} - -func TestDispatcherFilters(t *testing.T) { - dut, cancelDut, _ := makeDut() - defer cancelDut() - - subscribers := []*testSubscriber{ - {re: regexp.MustCompile(".*")}, - {re: regexp.MustCompile("foo")}, - {re: regexp.MustCompile("bar")}, - } - - for _, s := range subscribers { - s.data = make(chan *sms) - s.cancel = make(chan struct{}) - dut.subscribe(&subscriber{ - re: s.re, - data: s.data, - cancel: s.cancel, - }) - defer func(c chan struct{}) { - close(c) - }(s.cancel) - } - - in := &sms{ - from: "+4821372137", - body: "foo", - timestamp: time.Now(), - } - dut.publish(in) - expectReceived(t, in, subscribers[0].data) - expectReceived(t, in, subscribers[1].data) - expectEmpty(t, subscribers[2].data) - - in = &sms{ - from: "+4821372137", - body: "bar", - timestamp: time.Now(), - } - dut.publish(in) - expectReceived(t, in, subscribers[0].data) - expectEmpty(t, subscribers[1].data) - expectReceived(t, in, subscribers[2].data) - - in = &sms{ - from: "+4821372137", - body: "foobar", - timestamp: time.Now(), - } - dut.publish(in) - expectReceived(t, in, subscribers[0].data) - expectReceived(t, in, subscribers[1].data) - expectReceived(t, in, subscribers[2].data) -} - -func TestDispatcherMany(t *testing.T) { - dut, cancelDut, _ := makeDut() - defer cancelDut() - - subscribers := make([]*testSubscriber, 10000) - - for i, _ := range subscribers { - s := &testSubscriber{ - re: regexp.MustCompile(".*"), - data: make(chan *sms), - cancel: make(chan struct{}), - } - subscribers[i] = s - dut.subscribe(&subscriber{ - re: s.re, - data: s.data, - cancel: s.cancel, - }) - defer func(c chan struct{}) { - close(c) - }(s.cancel) - } - - in := &sms{ - from: "+4821372137", - body: "foo", - timestamp: time.Now(), - } - dut.publish(in) - - for _, s := range subscribers { - expectReceived(t, in, s.data) - } -} - -func TestDispatcherHammer(t *testing.T) { - dut, cancelDut, _ := makeDut() - defer cancelDut() - - for i := 0; i < 1000000; i += 1 { - s := &testSubscriber{ - re: regexp.MustCompile(".*"), - data: make(chan *sms), - cancel: make(chan struct{}), - } - - dut.subscribe(&subscriber{ - re: s.re, - data: s.data, - cancel: s.cancel, - }) - - in := &sms{ - from: "+4821372137", - body: "foo", - timestamp: time.Now(), - } - dut.publish(in) - expectReceived(t, in, s.data) - - close(s.cancel) - } -} diff --git a/hswaw/smsgw/main.go b/hswaw/smsgw/main.go deleted file mode 100644 index 3095c002..00000000 --- a/hswaw/smsgw/main.go +++ /dev/null @@ -1,226 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "net/http" - "regexp" - "strings" - "time" - - "code.hackerspace.pl/hscloud/go/mirko" - "github.com/golang/glog" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - pb "code.hackerspace.pl/hscloud/hswaw/smsgw/proto" -) - -var ( - flagTwilioSID string - flagTwilioToken string - flagTwilioFriendlyPhone string - - flagWebhookListen string - flagWebhookPublic string -) - -func init() { - flag.Set("logtostderr", "true") -} - -type server struct { - dispatcher *dispatcher -} - -func ourPhoneNumber(ctx context.Context, t *twilio, friendly string) (*incomingPhoneNumber, error) { - ipn, err := t.getIncomingPhoneNumbers(ctx) - if err != nil { - return nil, err - } - - for _, pn := range ipn { - if pn.FriendlyName == friendly { - return &pn, nil - } - } - - return nil, fmt.Errorf("requested phone number %q not in list", friendly) -} - -func ensureWebhook(ctx context.Context, t *twilio) { - pn, err := ourPhoneNumber(ctx, t, flagTwilioFriendlyPhone) - if err != nil { - glog.Exitf("could not get our phone number: %v", err) - } - - url := fmt.Sprintf("%ssms", flagWebhookPublic) - - // first setup. - if pn.SMSMethod != "POST" || pn.SMSURL != url { - glog.Infof("Updating webhook (is %s %q, want %s %q)", pn.SMSMethod, pn.SMSURL, "POST", url) - if err := t.updateIncomingPhoneNumberSMSWebhook(ctx, pn.SID, "POST", url); err != nil { - glog.Exitf("could not set webhook: %v") - } - - // try again to check that it's actually set - for { - pn, err = ourPhoneNumber(ctx, t, flagTwilioFriendlyPhone) - if err != nil { - glog.Exitf("could not get our phone number: %v", err) - } - if pn.SMSMethod == "POST" || pn.SMSURL == url { - break - } - glog.Infof("Webhook not yet ready, currently %s %q", pn.SMSMethod, pn.SMSURL) - time.Sleep(5 * time.Second) - } - glog.Infof("Webhook verified") - } else { - glog.Infof("Webhook up to date") - } - - // now keep checking to make sure that nobody takes over our webhook - tick := time.NewTicker(30 * time.Second) - for { - select { - case <-ctx.Done(): - return - case <-tick.C: - pn, err = ourPhoneNumber(ctx, t, flagTwilioFriendlyPhone) - if err != nil { - glog.Exitf("could not get our phone number: %v", err) - } - if pn.SMSMethod != "POST" || pn.SMSURL != url { - glog.Exitf("Webhook got deconfigured, not %s %q", pn.SMSMethod, pn.SMSURL) - } - } - } -} - -func (s *server) webhookHandler(w http.ResponseWriter, r *http.Request) { - if err := r.ParseForm(); err != nil { - glog.Errorf("webhook body parse error: %v", err) - return - } - - accountSID := r.PostForm.Get("AccountSid") - if accountSID != flagTwilioSID { - glog.Errorf("webhook got wrong account sid, got %q, wanted %q", accountSID, flagTwilioSID) - return - } - - body := r.PostForm.Get("Body") - if body == "" { - return - } - - from := r.PostForm.Get("From") - - glog.Infof("Got SMS from %q, body %q", from, body) - - s.dispatcher.publish(&sms{ - from: from, - body: body, - timestamp: time.Now(), - }) - - w.WriteHeader(200) -} - -func main() { - flag.StringVar(&flagTwilioSID, "twilio_sid", "", "Twilio account SID") - flag.StringVar(&flagTwilioToken, "twilio_token", "", "Twilio auth token") - flag.StringVar(&flagTwilioFriendlyPhone, "twilio_friendly_phone", "", "Twilio friendly phone number") - - flag.StringVar(&flagWebhookListen, "webhook_listen", "127.0.0.1:5000", "Listen address for webhook handler") - flag.StringVar(&flagWebhookPublic, "webhook_public", "", "Public address for webhook handler (wg. http://proxy.q3k.org/smsgw/)") - flag.Parse() - - if flagTwilioSID == "" || flagTwilioToken == "" { - glog.Exitf("twilio_sid and twilio_token must be set") - } - - if flagTwilioFriendlyPhone == "" { - glog.Exitf("twilio_friendly_phone must be set") - } - - if flagWebhookPublic == "" { - glog.Exitf("webhook_public must be set") - } - - if !strings.HasSuffix(flagWebhookPublic, "/") { - flagWebhookPublic += "/" - } - - s := &server{ - dispatcher: newDispatcher(), - } - - m := mirko.New() - if err := m.Listen(); err != nil { - glog.Exitf("Listen(): %v", err) - } - - webhookMux := http.NewServeMux() - webhookMux.HandleFunc("/sms", s.webhookHandler) - webhookSrv := http.Server{ - Addr: flagWebhookListen, - Handler: webhookMux, - } - go func() { - if err := webhookSrv.ListenAndServe(); err != nil { - glog.Exitf("webhook ListenAndServe: %v", err) - } - }() - - t := &twilio{ - accountSID: flagTwilioSID, - accountToken: flagTwilioToken, - } - go ensureWebhook(m.Context(), t) - go s.dispatcher.run(m.Context()) - - pb.RegisterSMSGatewayServer(m.GRPC(), s) - - if err := m.Serve(); err != nil { - glog.Exitf("Serve(): %v", err) - } - - <-m.Done() -} - -func (s *server) Messages(req *pb.MessagesRequest, stream pb.SMSGateway_MessagesServer) error { - re := regexp.MustCompile(".*") - if req.FilterBody != "" { - var err error - re, err = regexp.Compile(req.FilterBody) - if err != nil { - return status.Errorf(codes.InvalidArgument, "filter regexp error: %v", err) - } - } - - data := make(chan *sms) - cancel := make(chan struct{}) - defer func() { - close(cancel) - close(data) - }() - - s.dispatcher.subscribe(&subscriber{ - re: re, - data: data, - cancel: cancel, - }) - - for d := range data { - stream.Send(&pb.MessagesResponse{ - Sender: d.from, - Body: d.body, - Timestamp: d.timestamp.UnixNano(), - }) - } - - return nil -} diff --git a/hswaw/smsgw/proto/BUILD.bazel b/hswaw/smsgw/proto/BUILD.bazel deleted file mode 100644 index 0aca681d..00000000 --- a/hswaw/smsgw/proto/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@rules_proto//proto:defs.bzl", "proto_library") -load("@io_bazel_rules_go//go:def.bzl", "go_library") -load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") - -proto_library( - name = "proto_proto", - srcs = ["smsgw.proto"], - visibility = ["//visibility:public"], -) - -go_proto_library( - name = "proto_go_proto", - compilers = ["@io_bazel_rules_go//proto:go_grpc"], - importpath = "code.hackerspace.pl/hscloud/hswaw/smsgw/proto", - proto = ":proto_proto", - visibility = ["//visibility:public"], -) - -go_library( - name = "proto", - embed = [":proto_go_proto"], - importpath = "code.hackerspace.pl/hscloud/hswaw/smsgw/proto", - visibility = ["//visibility:public"], -) diff --git a/hswaw/smsgw/proto/gomod-generated-placeholder.go b/hswaw/smsgw/proto/gomod-generated-placeholder.go deleted file mode 100644 index 92256db4..00000000 --- a/hswaw/smsgw/proto/gomod-generated-placeholder.go +++ /dev/null @@ -1 +0,0 @@ -package proto diff --git a/hswaw/smsgw/proto/smsgw.proto b/hswaw/smsgw/proto/smsgw.proto deleted file mode 100644 index 2a95308b..00000000 --- a/hswaw/smsgw/proto/smsgw.proto +++ /dev/null @@ -1,17 +0,0 @@ -syntax = "proto3"; -package proto; -option go_package = "code.hackerspace.pl/hscloud/hswaw/smsgw/proto"; - -message MessagesRequest { - string filter_body = 1; -} - -message MessagesResponse { - string sender = 1; - string body = 3; - int64 timestamp = 4; -} - -service SMSGateway { - rpc Messages(MessagesRequest) returns (stream MessagesResponse); -} diff --git a/hswaw/smsgw/twilio.go b/hswaw/smsgw/twilio.go deleted file mode 100644 index cdc02553..00000000 --- a/hswaw/smsgw/twilio.go +++ /dev/null @@ -1,79 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "net/url" - "strings" -) - -type twilio struct { - accountSID string - accountToken string -} - -type incomingPhoneNumber struct { - FriendlyName string `json:"friendly_name"` - SMSMethod string `json:"sms_method"` - SMSURL string `json:"sms_url"` - SID string `json:"sid"` -} - -func (t *twilio) getIncomingPhoneNumbers(ctx context.Context) ([]incomingPhoneNumber, error) { - url := fmt.Sprintf("https://api.twilio.com/2010-04-01/Accounts/%s/IncomingPhoneNumbers.json", t.accountSID) - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return nil, err - } - req = req.WithContext(ctx) - req.SetBasicAuth(t.accountSID, t.accountToken) - res, err := http.DefaultClient.Do(req) - if err != nil { - return nil, err - } - defer res.Body.Close() - - result := struct { - Message string `json:"message"` - Status int64 `json:"status"` - IPN []incomingPhoneNumber `json:"incoming_phone_numbers"` - }{} - - if err := json.NewDecoder(res.Body).Decode(&result); err != nil { - return nil, err - } - - if result.Message != "" { - return nil, fmt.Errorf("REST response error, status: %v, message: %q", result.Status, result.Message) - } - - return result.IPN, nil -} - -func (t *twilio) updateIncomingPhoneNumberSMSWebhook(ctx context.Context, sid, method, whurl string) error { - turl := fmt.Sprintf("https://api.twilio.com/2010-04-01/Accounts/%s/IncomingPhoneNumbers/%s.json", t.accountSID, sid) - - data := url.Values{} - data.Set("SmsMethod", method) - data.Set("SmsUrl", whurl) - - req, err := http.NewRequest("POST", turl, strings.NewReader(data.Encode())) - if err != nil { - return err - } - req = req.WithContext(ctx) - req.SetBasicAuth(t.accountSID, t.accountToken) - req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - res, err := http.DefaultClient.Do(req) - if err != nil { - return err - } - defer res.Body.Close() - - if res.StatusCode != 200 { - return fmt.Errorf("status code: %v", res.StatusCode) - } - return nil -} diff --git a/hswaw/voucherchecker/BUILD.bazel b/hswaw/voucherchecker/BUILD.bazel deleted file mode 100644 index f9e84060..00000000 --- a/hswaw/voucherchecker/BUILD.bazel +++ /dev/null @@ -1,41 +0,0 @@ -load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_layer", "container_push") -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") - -go_library( - name = "voucherchecker_lib", - srcs = ["main.go"], - importpath = "code.hackerspace.pl/hscloud/hswaw/voucherchecker", - visibility = ["//visibility:private"], - deps = ["@com_github_golang_glog//:glog"], -) - -go_binary( - name = "voucherchecker", - embed = [":voucherchecker_lib"], - visibility = ["//visibility:public"], -) - -container_layer( - name = "layer_bin", - directory = "/voucherchecker/", - files = [ - ":voucherchecker", - ], -) - -container_image( - name = "runtime", - base = "@prodimage-bionic//image", - layers = [ - ":layer_bin", - ], -) - -container_push( - name = "push", - format = "Docker", - image = ":runtime", - registry = "registry.k0.hswaw.net", - repository = "q3k/voucherchecker", - tag = "{BUILD_TIMESTAMP}-{STABLE_GIT_COMMIT}", -) diff --git a/hswaw/voucherchecker/main.go b/hswaw/voucherchecker/main.go deleted file mode 100644 index e3678877..00000000 --- a/hswaw/voucherchecker/main.go +++ /dev/null @@ -1,241 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "flag" - "fmt" - "io/ioutil" - "net/http" - "net/http/cookiejar" - "regexp" - "strings" - "time" - - "github.com/golang/glog" -) - -func init() { - flag.Set("logtostderr", "true") -} - -var ( - flagListen string - - reVoucher = regexp.MustCompile("[A-Z0-9]+") -) - -type voucherstatus int - -const ( - statusUnknown voucherstatus = iota - statusInvalid - statusUnused - statusUsed - statusCart -) - -func (v voucherstatus) String() string { - switch v { - case statusInvalid: - return "INVALID" - case statusUnused: - return "UNUSED" - case statusUsed: - return "USED" - case statusCart: - return "INCART" - } - return "UNKNOWN" -} - -type vouchercache struct { - status voucherstatus - expires time.Time -} - -func (c *vouchercache) fresh() bool { - if c.status == statusUsed { - return true - } - if c.expires.Before(time.Now()) { - return false - } - return true -} - -type statusReq struct { - voucher string - res chan voucherstatus -} - -type refreshRes struct { - voucher string - status voucherstatus -} - -type service struct { - statusReq chan *statusReq - - pretixSem chan struct{} -} - -func newService() *service { - return &service{ - statusReq: make(chan *statusReq), - pretixSem: make(chan struct{}, 3), - } -} - -func (s *service) worker(ctx context.Context) error { - cache := make(map[string]*vouchercache) - waiters := make(map[string][]chan voucherstatus) - refreshes := make(chan *refreshRes) - - for { - select { - case <-ctx.Done(): - return ctx.Err() - - // is there a refresh pending? - case ref := <-refreshes: - glog.Infof("cache feed: %v is %v", ref.voucher, ref.status) - expires := 30 * time.Minute - if ref.status == statusInvalid { - expires = 48 * time.Hour - } - cache[ref.voucher] = &vouchercache{ - status: ref.status, - expires: time.Now().Add(expires), - } - for _, w := range waiters[ref.voucher] { - w := w - go func() { - w <- ref.status - }() - } - delete(waiters, ref.voucher) - - // is there a new request? - case req := <-s.statusReq: - // return cache if fresh - if el, ok := cache[req.voucher]; ok && el.fresh() { - go func() { - glog.Infof("cache hit: %v is %v", req.voucher, el.status) - req.res <- el.status - }() - continue - } - // is someone waiting for a refresh already? - if _, ok := waiters[req.voucher]; ok { - glog.Infof("cache miss, secondary: %v", req.voucher) - waiters[req.voucher] = append(waiters[req.voucher], req.res) - continue - } - // request refresh - glog.Infof("cache miss, primary: %v", req.voucher) - waiters[req.voucher] = []chan voucherstatus{req.res} - go func() { - s := s.getStatus(ctx, req.voucher) - refreshes <- &refreshRes{ - voucher: req.voucher, - status: s, - } - }() - } - } -} - -func (s *service) run() { - mux := http.NewServeMux() - mux.HandleFunc("/status", s.handlerStatus) - - ctx := context.Background() - go s.worker(ctx) - - glog.Infof("Listening on %s...", flagListen) - if err := http.ListenAndServe(flagListen, mux); err != nil { - glog.Exitf("could not listen: %v", err) - } -} - -func (s *service) handlerStatus(w http.ResponseWriter, r *http.Request) { - status := statusUnknown - defer func() { - e := json.NewEncoder(w) - e.Encode(struct { - Status string - }{ - Status: status.String(), - }) - }() - - voucher := r.URL.Query().Get("voucher") - if voucher == "" || !strings.HasPrefix(voucher, "CHAOS") { - status = statusInvalid - return - } - if !reVoucher.MatchString(voucher) { - status = statusInvalid - return - } - - resC := make(chan voucherstatus) - s.statusReq <- &statusReq{ - voucher: voucher, - res: resC, - } - status = <-resC -} - -func (s *service) getStatus(ctx context.Context, voucher string) voucherstatus { - s.pretixSem <- struct{}{} - defer func() { - <-s.pretixSem - }() - - cookieJar, _ := cookiejar.New(nil) - client := &http.Client{ - Jar: cookieJar, - } - - res, err := client.Get(fmt.Sprintf("https://tickets.events.ccc.de/36c3/redeem/?voucher=%s&subevent=&hello=this-is-q3k-at-hackerspace-pl-we-use-this-for-voucher-distribution", voucher)) - if err != nil { - glog.Errorf("Getting main page: %v", err) - return statusUnknown - } - defer res.Body.Close() - - data, err := ioutil.ReadAll(res.Body) - if err != nil { - glog.Errorf("Reading result page: %v", err) - return statusUnknown - } - - if strings.Contains(string(data), "not known") { - return statusInvalid - } - if strings.Contains(string(data), "already been") { - return statusUsed - } - if strings.Contains(string(data), "You entered a voucher code that allows you ") { - return statusUnused - } - if strings.Contains(string(data), "voucher code is currently locked") { - return statusCart - } - - glog.Errorf("Unexpected result for %s", voucher) - glog.Infof("%s", data) - status := statusUnknown - - return status -} - -func main() { - flag.StringVar(&flagListen, "listen", ":8081", "Listen address") - flag.Parse() - - s := newService() - s.run() -}