diff --git a/README.md b/README.md index b088da0..63b19b0 100644 --- a/README.md +++ b/README.md @@ -43,10 +43,11 @@ docker run kutzilla/hetzner-ddns example.com my-secret-api-token A ## Optional Parameters -* `-e RECORD_NAME` - The name of the DNS-record that DDNS updates should be applied to. This could be `sub` if you like to update the subdomain `sub.example.com` of `example.com`. The default value is `@` If you want to update multiple Records you can use a pattern. The pattern which can be used is `RECORD_NAME_` e.g. `RECORD_NAME_EXAMPLE`, `RECORD_NAME` will be ignored the multi domain approach is used. +* `-e RECORD_NAME` - The name of the DNS-record that DDNS updates should be applied to. This could be `sub` if you like to update the subdomain `sub.example.com` of `example.com`. The default value is `@` If you want to update multiple Records you can use a pattern. This functionality only works if the `MULTIPLE_DOMAIN_MODE` is set to `true`. * `-e CRON_EXPRESSION` - The cron expression of the DDNS update interval. The default is every 5 minutes - `*/5 * * * *`. * `-e TTL` - The TTL (Time To Live) value specifies how long the record is valid before the nameservers are prompted to reload the zone file. The default is `86400`. * `-e RECORD_NAME__TTL` - In case you have multiple records, you can specify the TTL per record referenced by `` for example `RECORD_NAME_EXAMPLE_TTL`, if no TTL is specified the TTL will be `86400`. The `TTL` argument will be ignored if this parameter is used. +* `-e MULTIPLE_DOMAIN_MODE` - Sets the mode to update a single record or multiple records at once. ## Build diff --git a/pkg/conf/conf.go b/pkg/conf/conf.go index 932dfd1..bb175f7 100644 --- a/pkg/conf/conf.go +++ b/pkg/conf/conf.go @@ -10,36 +10,42 @@ import ( ) const ( - EnvZoneName = "ZONE_NAME" - EnvApiToken = "API_TOKEN" - EnvRecordType = "RECORD_TYPE" - EnvRecordName = "RECORD_NAME" - EnvCronExpression = "CRON_EXPRESSION" - EnvTimeToLive = "TTL" - - DescZoneName = "The DNS zone that DDNS updates should be applied to." - DescApiToken = "Your Hetzner API token." - DescRecordType = "The record type of your zone. If your zone uses an IPv4 address use `A`. Use `AAAA` if it uses an IPv6 address." - DescRecordName = "The name of the DNS-record that DDNS updates should be applied to. This could be `sub` if you like to update the subdomain `sub.example.com` of `example.com`. The default value is `@`" - DescCronExpression = "The cron expression of the DDNS update interval. The default is every 5 minutes - `*/5 * * * *`" - DescTimeToLive = "Time to live of the record" - - DefaultRecordKey = "default" - DefaultRecordName = "@" - DefaultRecordType = "A" - DefaultCronExpression = "*/5 * * * *" - DefaultTimeToLive = 86400 + EnvZoneName = "ZONE_NAME" + EnvApiToken = "API_TOKEN" + EnvRecordType = "RECORD_TYPE" + EnvRecordName = "RECORD_NAME" + EnvCronExpression = "CRON_EXPRESSION" + EnvTimeToLive = "TTL" + EnvMultipleDomainMode = "MULTIPLE_DOMAIN_MODE" + + DescZoneName = "The DNS zone that DDNS updates should be applied to." + DescApiToken = "Your Hetzner API token." + DescRecordType = "The record type of your zone. If your zone uses an IPv4 address use `A`. Use `AAAA` if it uses an IPv6 address." + DescRecordName = "The name of the DNS-record that DDNS updates should be applied to. This could be `sub` if you like to update the subdomain `sub.example.com` of `example.com`. The default value is `@`" + DescCronExpression = "The cron expression of the DDNS update interval. The default is every 5 minutes - `*/5 * * * *`" + DescTimeToLive = "Time to live of the record" + DescMultipleDomainMode = "Sets the mode to update a single record or multiple records at once" + + DefaultRecordKey = "default" + DefaultRecordName = "@" + DefaultRecordType = "A" + DefaultCronExpression = "*/5 * * * *" + DefaultTimeToLive = 86400 + DefaultMultipleDomainMode = false IPv4 = "IPv4" IPv6 = "IPv6" IPv6RecordType = "AAAA" ) -type RecordConfig map[string]*RecordConf +type RecordConfig struct { + mode bool + records map[string]*RecordConf +} type DynDnsConf struct { DnsConf DnsConf - RecordConf map[string]*RecordConf + RecordConf RecordConfig ProviderConf ProviderConf CronConf CronConf } @@ -71,45 +77,46 @@ func (e *ArgumentMissingError) Error() string { return "The mandatory argument " + e.argumentName + " is missing" } -func setupRecordConfig(records RecordConfig) { - useDefaultConfig := true - envPrefix := fmt.Sprintf("%s_", EnvRecordName) +func setupRecordConfig(recordConf RecordConfig) { + flag.BoolVar(&recordConf.mode, EnvMultipleDomainMode, DefaultMultipleDomainMode, DescMultipleDomainMode) - for _, envRecord := range os.Environ() { - if strings.HasPrefix(envRecord, envPrefix) { - useDefaultConfig = false - envKey := strings.Split(envRecord, "=")[0] + if recordConf.mode { + envPrefix := fmt.Sprintf("%s_", EnvRecordName) + for _, envRecord := range os.Environ() { + if strings.HasPrefix(envRecord, envPrefix) { + envKey := strings.Split(envRecord, "=")[0] - if strings.HasSuffix(envKey, "_TTL") { - continue - } + if strings.HasSuffix(envKey, "_TTL") { + continue + } - if _, exists := records[envKey]; exists { - continue - } + if _, exists := recordConf.records[envKey]; exists { + continue + } - var record = &RecordConf{ - RecordType: DefaultRecordType, // assume its ipv4, fix later after arg parse if not - } - records[envKey] = record + var record = &RecordConf{ + RecordType: DefaultRecordType, // assume its ipv4, fix later after arg parse if not + } + recordConf.records[envKey] = record - flag.StringVar(&record.RecordName, envKey, DefaultRecordName, DescRecordName) - flag.IntVar(&record.TTL, fmt.Sprintf("%s_TTL", envKey), DefaultTimeToLive, DescTimeToLive) + flag.StringVar(&record.RecordName, envKey, DefaultRecordName, DescRecordName) + flag.IntVar(&record.TTL, fmt.Sprintf("%s_TTL", envKey), DefaultTimeToLive, DescTimeToLive) + } } } - if useDefaultConfig { + if !recordConf.mode { var record = &RecordConf{ RecordType: DefaultRecordType, } flag.StringVar(&record.RecordName, EnvRecordName, DefaultRecordName, DescRecordName) flag.IntVar(&record.TTL, EnvTimeToLive, DefaultTimeToLive, DescTimeToLive) - records[DefaultRecordKey] = record + recordConf.records[DefaultRecordKey] = record } } -func setRecordType(records RecordConfig, recordType string) { - for _, record := range records { +func setRecordType(recordConf RecordConfig, recordType string) { + for _, record := range recordConf.records { record.RecordType = recordType } } @@ -121,7 +128,10 @@ func Read() DynDnsConf { flag.StringVar(&apiToken, EnvApiToken, apiToken, DescApiToken) flag.StringVar(&recordType, EnvRecordType, recordType, DescRecordType) - records := make(map[string]*RecordConf) + records := RecordConfig{ + mode: DefaultMultipleDomainMode, + records: make(map[string]*RecordConf), + } setupRecordConfig(records) var cronExpression = DefaultCronExpression @@ -175,7 +185,7 @@ func validate(dynDnsConf DynDnsConf) (DynDnsConf, error) { } // Check record type - for _, record := range dynDnsConf.RecordConf { + for _, record := range dynDnsConf.RecordConf.records { if record.RecordType == "" { return dynDnsConf, &ArgumentMissingError{ argumentName: EnvRecordType, diff --git a/pkg/conf/conf_test.go b/pkg/conf/conf_test.go index 0bff16b..bd84581 100644 --- a/pkg/conf/conf_test.go +++ b/pkg/conf/conf_test.go @@ -7,9 +7,24 @@ import ( "github.com/stretchr/testify/assert" ) +func TestSingleDomain(t *testing.T) { + assert := assert.New(t) + + os.Setenv(EnvZoneName, "example.com") + os.Setenv(EnvApiToken, "abcdefghi1234567890") + os.Setenv(EnvRecordType, "A") + + dynDnsConf := Read() + + assert.Equal("@", dynDnsConf.RecordConf.records["default"].RecordName) + assert.Equal("A", dynDnsConf.RecordConf.records["default"].RecordType) +} + func TestReadMultipleDomains(t *testing.T) { assert := assert.New(t) + os.Setenv(EnvMultipleDomainMode, "true") + os.Setenv(EnvZoneName, "example.com") os.Setenv(EnvApiToken, "abcdefghi1234567890") os.Setenv(EnvRecordType, "A") @@ -23,18 +38,21 @@ func TestReadMultipleDomains(t *testing.T) { os.Setenv(EnvCronExpression, "*/10 * * * *") dynDnsConf := Read() + + assert.Equal(true, dynDnsConf.RecordConf.mode) + // Check optional args - assert.NotEqual(DefaultRecordName, dynDnsConf.RecordConf["RECORD_NAME_TEST1"].RecordName) - assert.Equal("test1", dynDnsConf.RecordConf["RECORD_NAME_TEST1"].RecordName) + assert.NotEqual(DefaultRecordName, dynDnsConf.RecordConf.records["RECORD_NAME_TEST1"].RecordName) + assert.Equal("test1", dynDnsConf.RecordConf.records["RECORD_NAME_TEST1"].RecordName) - assert.NotEqual(DefaultTimeToLive, dynDnsConf.RecordConf["RECORD_NAME_TEST1"].TTL) - assert.Equal(4711, dynDnsConf.RecordConf["RECORD_NAME_TEST1"].TTL) + assert.NotEqual(DefaultTimeToLive, dynDnsConf.RecordConf.records["RECORD_NAME_TEST1"].TTL) + assert.Equal(4711, dynDnsConf.RecordConf.records["RECORD_NAME_TEST1"].TTL) - assert.NotEqual(DefaultRecordName, dynDnsConf.RecordConf["RECORD_NAME_TEST2"].RecordName) - assert.Equal("test2", dynDnsConf.RecordConf["RECORD_NAME_TEST2"].RecordName) + assert.NotEqual(DefaultRecordName, dynDnsConf.RecordConf.records["RECORD_NAME_TEST2"].RecordName) + assert.Equal("test2", dynDnsConf.RecordConf.records["RECORD_NAME_TEST2"].RecordName) - assert.NotEqual(DefaultTimeToLive, dynDnsConf.RecordConf["RECORD_NAME_TEST2"].TTL) - assert.Equal(1337, dynDnsConf.RecordConf["RECORD_NAME_TEST2"].TTL) + assert.NotEqual(DefaultTimeToLive, dynDnsConf.RecordConf.records["RECORD_NAME_TEST2"].TTL) + assert.Equal(1337, dynDnsConf.RecordConf.records["RECORD_NAME_TEST2"].TTL) assert.NotEqual(DefaultCronExpression, dynDnsConf.CronConf.CronExpression) assert.Equal("*/10 * * * *", dynDnsConf.CronConf.CronExpression)