Skip to content

Instantly share code, notes, and snippets.

@samdphillips
Created February 25, 2021 18:33
Show Gist options
  • Save samdphillips/ed1b33be042b46a84a3a0e2ccb69f5f5 to your computer and use it in GitHub Desktop.
Save samdphillips/ed1b33be042b46a84a3a0e2ccb69f5f5 to your computer and use it in GitHub Desktop.
AWS EC2 scripting with Racket

AWS EC2 scripting with Racket

Uses rkshell to run the aws command line tool from Racket.

Example usage (using raco run:

$ raco run control.rkt start
$ raco run control.rkt stop
#lang rkshell/base
(require racket/match
racket/string
racket/system
syntax/parse/define
json
rkshell/private/repl)
(define instance-id "i-abc123")
(define zone-id "/hostedzone/zonezonezon")
(define dns-name "something.example.com.")
(define (raise-error-runner cmd . args)
(define errp (open-output-string))
(define outp (open-output-string))
(define result
(parameterize ([current-error-port errp]
[current-output-port outp]
[current-input-port (open-input-bytes #"")])
(apply system* (rkshell-find-in-path cmd) args)))
(if result
(string->jsexpr (get-output-string outp))
(error (string->symbol cmd)
(string-trim (get-output-string errp)))))
(rkexec-runner raise-error-runner)
(define (make-route53-change ip-address)
(hash
'Changes
(list
(hash 'Action "UPSERT"
'ResourceRecordSet
(hash 'Name dns-name
'Type "A"
'TTL 60
'ResourceRecords
(list (hash 'Value ip-address)))))))
(define-simple-macro (run-async e)
(handle-evt (thread (lambda () e)) void))
(define (notify-every secs msg)
(define (repeat cancel-evt)
(sync (handle-evt (alarm-evt (+ (current-inexact-milliseconds) (* 1000 secs)))
(lambda (e) (displayln msg) (repeat cancel-evt)))
(handle-evt cancel-evt void)))
(nack-guard-evt
(lambda (cancel-evt)
(thread (lambda () (repeat cancel-evt))))))
(module* start #f
(displayln "starting instance")
#{aws ec2 start-instances
--output json
--instance-ids $instance-id}
(sync
(notify-every 2 "waiting for instance to start")
(run-async #{aws ec2 wait instance-running --instance-ids $instance-id}))
(define public-ip-address
#{aws ec2 describe-instances
--query "Reservations[0].Instances[0].PublicIpAddress"
--output json
--instance-ids $instance-id})
(displayln "setting IP address")
(match-define
(hash-table ['ChangeInfo (hash-table ['Id change-id])])
#{aws route53 change-resource-record-sets
--hosted-zone-id $zone-id
--change-batch (jsexpr->string (make-route53-change public-ip-address))})
(sync
(notify-every 2 "waiting for dns to change")
(run-async #{aws route53 wait resource-record-sets-changed --id $change-id})))
(module* stop #f
#{aws ec2 stop-instances --instance-ids $instance-id}
(sync
(notify-every 2 "waiting for instance to stop")
(run-async #{aws ec2 wait instance-stopped --instance-ids $instance-id})))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment