1
0
Fork 0
home-dns/main.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()
}