Skip to content

Instantly share code, notes, and snippets.

@njbartlett
Last active February 24, 2018 17:30
Show Gist options
  • Save njbartlett/5e79d4ad69eda7719305171e801ec2a9 to your computer and use it in GitHub Desktop.
Save njbartlett/5e79d4ad69eda7719305171e801ec2a9 to your computer and use it in GitHub Desktop.
Docker Container Reaper in Ponylang (a learning exercise)
use "collections"
use "json"
use "net"
use "net/http"
use "time"
actor Main
"""
This is my learning exercise for the Pony language (ponylang.org). It is a
port of a Java application that:
* Maintains a Map of ID to Timestamp.
* Periodically scans the entire Map and kills the Docker containers
corresponding to those IDs with a Timestamp that is too old.
* Clients can query the content of the Map via an HTTP GET request.
* Clients can add/update an ID in the Map with an HTTP PUT request. The ID
should be atomically inserted or updated in the Map with the current time.
"""
let defaultHost: String = "0.0.0.0"
let defaultPort: String = "8080"
let defaultLimit: USize = 100
new create(env: Env) =>
serve(env, env.root)
fun serve(env: Env, rootAuth: AmbientAuth) =>
let manager : ManagedIds = ManagedIds
Server(
where auth = NetAuth.create(rootAuth)
, host = try env.args(1) else defaultHost end
, service = try env.args(1) else defaultPort end
, limit = try env.args(2).usize() else defaultLimit end
, handler = HttpHandler(manager)
, notify = InfoListener(env)
, logger = CommonLog(env.out)
)
fun serve(env: Env, rootAuth: None) =>
env.err.print("No authority to run")
actor ManagedIds
"""
This actor owns the Map and runs the periodic scan-and-kill (not yet
implemented).
"""
let _map: Map[String, Timestamp] = _map.create()
.add("foo", Timestamp.now())
.add("bar", Timestamp.now())
be list(fn: {ref(String)} iso) =>
var rows: List[String] = List[String]
for (key, ts) in _map.pairs() do
var date = ts.formatISO8601()
var row = JsonObject.from_map(Map[String, JsonType]
.add("id", key)
.add("expiry", date)
).string(where pretty_print=true)
rows.push(row)
end
(consume fn)("blah") // TODO: haven't worked out the string join yet
class HttpHandler
let _manager: ManagedIds
new val create(manager: ManagedIds) =>
_manager = manager
fun apply(request: Payload) if request.method == "GET" =>
"""
Handle GET requests
"""
_manager.list(
object iso
var _request: Payload = consume request
fun ref apply(rows: String) =>
let response: Payload = Payload.response()
response.add_chunk(rows)
response.add_chunk("\n")
let r: Payload = _request = Payload.request()
(consume r).respond(consume response)
end)
/*
fun apply(request: Payload) if request.method == "PUT" =>
"""
Handle PUT requests
"""
//_managedIDs.insert(request.url.path, Timestamp.now())
//_managedIDs = _managedIDs.add(request.url.path, Timestamp.now())
let response = Payload.response(StatusOK)
(consume request).respond(consume response)
*/
fun apply(request: Payload) =>
"""
Handle all other HTTP methods and return 405 Not Allowed
"""
let response: Payload = Payload.response(StatusMethodNotAllowed)
(consume request).respond(consume response)
class InfoListener
let _env: Env
new iso create(env: Env) =>
_env = env
fun ref listening(server: Server ref) =>
try
(let host, let service) = server.local_address().name()
_env.out.print("Listening on " + host + ":" + service)
else
_env.out.print("Couldn't get local address.")
server.dispose()
end
fun ref not_listening(server: Server ref) =>
_env.out.print("Failed to listen.")
fun ref closed(server: Server ref) =>
_env.out.print("Shutdown.")
class val Timestamp
let _seconds: I64
let _nanos: I64
new val now() =>
(_seconds, _nanos) = Time.now()
fun formatISO8601(): String val =>
Date(_seconds, _nanos).format("%FT%TZ")
@njbartlett
Copy link
Author

@jemc: Thanks, I'm glad it's not just me!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment