Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@
[puppetlabs/kitchensink "3.4.0" :exclusions [cheshire]]
[puppetlabs/i18n]
[nrepl/nrepl]
[io.github.clj-kondo/config-slingshot-slingshot "1.0.0"]]
[io.github.clj-kondo/config-slingshot-slingshot "1.0.0"]

[com.kohlschutter.junixsocket/junixsocket-core "2.10.1" :extension "pom"]]

:deploy-repositories [["clojars" {:url "https://clojars.org/repo"
:username :env/CLOJARS_USERNAME
Expand Down Expand Up @@ -74,7 +76,7 @@
[puppetlabs/i18n "0.9.2"]]

:eastwood {:ignored-faults {:reflection {puppetlabs.trapperkeeper.logging [{:line 92}]
puppetlabs.trapperkeeper.internal [{:line 128}]
puppetlabs.trapperkeeper.internal [{:line 174}]
puppetlabs.trapperkeeper.testutils.logging true
puppetlabs.trapperkeeper.testutils.logging-test true
puppetlabs.trapperkeeper.services.nrepl.nrepl-service-test true
Expand Down
54 changes: 52 additions & 2 deletions src/puppetlabs/trapperkeeper/internal.clj
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
(ns puppetlabs.trapperkeeper.internal
(:import (clojure.lang ExceptionInfo IFn IDeref)
(java.lang ArithmeticException NumberFormatException))
(:import
(clojure.lang ExceptionInfo IFn IDeref)
(java.lang ArithmeticException NumberFormatException)
(java.io File)
(java.net DatagramPacket SocketException)
;; Looks like JDK 16+ support doesn't do SOCK_DGRAM yet
(org.newsclub.net.unix AFUNIXSocketAddress AFUNIXDatagramSocket)
(java.nio.charset StandardCharsets))

(:require [clojure.tools.logging :as log]
[beckon]
[plumbing.graph :as graph]
Expand All @@ -16,6 +23,24 @@
[clojure.core.async.impl.protocols :as async-prot]
[me.raynes.fs :as fs]))

;; helper functions to resolve unknown type references
;; this feels cleaner compared to type hints, but I've no idea what I'm doing
;; - bastelfreak 2025-08-26
(defn utf8-bytes
"Convert a String to a UTF-8 byte array."
[^String s]
(.getBytes s StandardCharsets/UTF_8))

(defn socket-addr
"Convert a String path to a reflection-free AFUNIXSocketAddress."
[^String path]
(AFUNIXSocketAddress/of (File. path)))

(defn send-packet!
"Send a UTF-8 message via a connected AFUNIXDatagramSocket."
[^AFUNIXDatagramSocket s ^String msg]
(let [^bytes buf (utf8-bytes msg)]
(.send s (DatagramPacket. buf (alength buf)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Schemas
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Expand All @@ -28,6 +53,27 @@
;; Private
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Optional systemd support

(def systemd-notify-socket (System/getenv "NOTIFY_SOCKET"))

(defn maybe-notify-systemd
"Send a message to systemd NOTIFY_SOCKET if available."
[^String message]
(when-let [^String socket-path systemd-notify-socket]
(let [^AFUNIXSocketAddress addr (socket-addr socket-path)]
(with-open [^AFUNIXDatagramSocket s (AFUNIXDatagramSocket/newInstance)]
(try
(.connect s addr)
(send-packet! s message)
(catch SocketException _
(log/warn (i18n/trs "Unable to connect to NOTIFY_SOCKET {0}"
(pr-str socket-path)))))))))

(defn notice-service-ready [] (maybe-notify-systemd "READY=1\n"))
(defn notice-service-reloading [] (maybe-notify-systemd "RELOADING=1\n"))
(defn notice-service-stopping [] (maybe-notify-systemd "STOPPING=1\n"))

;; This is (eww) a global variable that holds a reference to all of the running
;; Trapperkeeper apps in the process. It can be used when connecting via nrepl
;; to allow you to do useful things, and also may be used for other things
Expand Down Expand Up @@ -615,11 +661,13 @@
this)
(a/start [this]
(run-lifecycle-fns app-context s/start "start" ordered-services)
(notice-service-ready)
(inc-restart-counter! this)
this)
(a/stop [this]
(a/stop this false))
(a/stop [this throw?]
(notice-service-stopping)
(let [errors (shutdown! app-context)]
(if (and throw? (seq errors))
(let [msg (i18n/trs "Error during app shutdown!")]
Expand All @@ -628,10 +676,12 @@
this)))
(a/restart [this]
(try
(notice-service-reloading)
(run-lifecycle-fns app-context s/stop "stop" (reverse ordered-services))
(doseq [svc-id (keys services-by-id)] (swap! app-context assoc-in [:service-contexts svc-id] {}))
(run-lifecycle-fns app-context s/init "init" ordered-services)
(run-lifecycle-fns app-context s/start "start" ordered-services)
(notice-service-ready)
(inc-restart-counter! this)
this
(catch Throwable t
Expand Down