149 lines
3.1 KiB
Go
149 lines
3.1 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"io/ioutil"
|
||
|
"log"
|
||
|
"net/http"
|
||
|
"os"
|
||
|
"os/signal"
|
||
|
|
||
|
"github.com/aws/aws-sdk-go/aws"
|
||
|
"github.com/aws/aws-sdk-go/aws/session"
|
||
|
"github.com/aws/aws-sdk-go/service/route53"
|
||
|
|
||
|
"gopkg.in/robfig/cron.v2"
|
||
|
yaml "gopkg.in/yaml.v2"
|
||
|
)
|
||
|
|
||
|
type Record struct {
|
||
|
Cron string
|
||
|
Name string
|
||
|
TTL int
|
||
|
Zone string
|
||
|
}
|
||
|
|
||
|
type Config struct {
|
||
|
Records []Record
|
||
|
Source string
|
||
|
}
|
||
|
|
||
|
func findPublicAddress(url string) []string {
|
||
|
ipResp, ipError := http.Get(url)
|
||
|
if ipError != nil {
|
||
|
log.Printf("error getting public address: %s", ipError.Error())
|
||
|
}
|
||
|
defer ipResp.Body.Close()
|
||
|
body, bodyError := ioutil.ReadAll(ipResp.Body)
|
||
|
if bodyError != nil {
|
||
|
log.Printf("error getting response body: %s", bodyError.Error())
|
||
|
}
|
||
|
|
||
|
return []string{
|
||
|
string(body),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func loadConfig(path string) (*Config, error) {
|
||
|
data, readError := ioutil.ReadFile(path)
|
||
|
if readError != nil {
|
||
|
|
||
|
}
|
||
|
|
||
|
dest := &Config{}
|
||
|
yamlError := yaml.Unmarshal(data, dest)
|
||
|
if yamlError != nil {
|
||
|
log.Printf("error parsing config: %s", yamlError.Error())
|
||
|
return nil, yamlError
|
||
|
}
|
||
|
|
||
|
return dest, nil
|
||
|
}
|
||
|
|
||
|
func mapResourceValues(values []string) []*route53.ResourceRecord {
|
||
|
records := make([]*route53.ResourceRecord, len(values))
|
||
|
for i, v := range values {
|
||
|
records[i] = &route53.ResourceRecord{
|
||
|
Value: aws.String(v),
|
||
|
}
|
||
|
}
|
||
|
return records
|
||
|
}
|
||
|
|
||
|
func updateRecord(conf *Config, r Record) {
|
||
|
// create a session
|
||
|
sess, sessError := session.NewSession()
|
||
|
if sessError != nil {
|
||
|
log.Printf("error creating aws session: %s", sessError.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// get current ip
|
||
|
values := findPublicAddress(conf.Source)
|
||
|
log.Printf("updating '%s' to '%s'", r.Name, values)
|
||
|
|
||
|
// prepare the update
|
||
|
updates := &route53.ChangeResourceRecordSetsInput{
|
||
|
ChangeBatch: &route53.ChangeBatch{
|
||
|
Changes: []*route53.Change{
|
||
|
{
|
||
|
Action: aws.String("UPSERT"),
|
||
|
ResourceRecordSet: &route53.ResourceRecordSet{
|
||
|
Name: aws.String(r.Name),
|
||
|
TTL: aws.Int64(300),
|
||
|
Type: aws.String("A"),
|
||
|
ResourceRecords: mapResourceValues(values),
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
HostedZoneId: aws.String(r.Zone),
|
||
|
}
|
||
|
|
||
|
// apply
|
||
|
svc := route53.New(sess)
|
||
|
updateOutput, updateError := svc.ChangeResourceRecordSets(updates)
|
||
|
if updateError != nil {
|
||
|
log.Printf("error updating route53 records: %s", updateError.Error())
|
||
|
}
|
||
|
|
||
|
log.Printf("updated route53 records: %v", updateOutput)
|
||
|
}
|
||
|
|
||
|
func scheduleJob(conf *Config, c *cron.Cron, r Record) {
|
||
|
log.Printf("scheduling cron job for %s", r.Name)
|
||
|
c.AddFunc(r.Cron, func() {
|
||
|
log.Printf("executing cron job for %s", r.Name)
|
||
|
updateRecord(conf, r)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
if len(os.Args) < 2 {
|
||
|
log.Printf("not enough arguments: ./home-dns config.yml")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
confPath := os.Args[1]
|
||
|
log.Printf("loading config from '%s'", confPath)
|
||
|
conf, confError := loadConfig(confPath)
|
||
|
if confError != nil {
|
||
|
log.Printf("error loading config: %s", confError.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// open a session (first, to fail early)
|
||
|
|
||
|
// schedule cron jobs
|
||
|
c := cron.New()
|
||
|
for _, r := range conf.Records {
|
||
|
scheduleJob(conf, c, r)
|
||
|
}
|
||
|
c.Start()
|
||
|
|
||
|
stop := make(chan os.Signal)
|
||
|
signal.Notify(stop, os.Interrupt, os.Kill)
|
||
|
<-stop
|
||
|
|
||
|
c.Stop()
|
||
|
}
|