Skip to content

Commit

Permalink
Merge pull request kubernetes-sigs#2338 from adityask2/metric-addition
Browse files Browse the repository at this point in the history
Changes to add 2 metrics to external-dns.
  • Loading branch information
k8s-ci-robot committed Oct 28, 2021
2 parents 0fa419a + 39d5b39 commit 4c66715
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 1 deletion.
38 changes: 37 additions & 1 deletion controller/controller.go
Expand Up @@ -94,6 +94,14 @@ var (
Help: "Number of Source errors.",
},
)
verifiedARecords = prometheus.NewGauge(
prometheus.GaugeOpts{
Namespace: "external_dns",
Subsystem: "controller",
Name: "verified_a_records",
Help: "Number of DNS A-records that exists both in source and registry.",
},
)
)

func init() {
Expand All @@ -105,6 +113,7 @@ func init() {
prometheus.MustRegister(deprecatedRegistryErrors)
prometheus.MustRegister(deprecatedSourceErrors)
prometheus.MustRegister(controllerNoChangesTotal)
prometheus.MustRegister(verifiedARecords)
}

// Controller is responsible for orchestrating the different components.
Expand Down Expand Up @@ -151,7 +160,8 @@ func (c *Controller) RunOnce(ctx context.Context) error {
return err
}
sourceEndpointsTotal.Set(float64(len(endpoints)))

vRecords := fetchMatchingARecords(endpoints, records)
verifiedARecords.Set(float64(len(vRecords)))
endpoints = c.Registry.AdjustEndpoints(endpoints)

plan := &plan.Plan{
Expand Down Expand Up @@ -181,6 +191,32 @@ func (c *Controller) RunOnce(ctx context.Context) error {
return nil
}

// Checks and returns the intersection of A records in endpoint and registry.
func fetchMatchingARecords(endpoints []*endpoint.Endpoint, registryRecords []*endpoint.Endpoint) []string {
aRecords := filterARecords(endpoints)
recordsMap := make(map[string]struct{})
for _, regRecord := range registryRecords {
recordsMap[regRecord.DNSName] = struct{}{}
}
var cm []string
for _, sourceRecord := range aRecords {
if _, found := recordsMap[sourceRecord]; found {
cm = append(cm, sourceRecord)
}
}
return cm
}

func filterARecords(endpoints []*endpoint.Endpoint) []string {
var aRecords []string
for _, endPoint := range endpoints {
if endPoint.RecordType == endpoint.RecordTypeA {
aRecords = append(aRecords, endPoint.DNSName)
}
}
return aRecords
}

// ScheduleRunOnce makes sure execution happens at most once per interval.
func (c *Controller) ScheduleRunOnce(now time.Time) {
c.nextRunAtMux.Lock()
Expand Down
94 changes: 94 additions & 0 deletions controller/controller_test.go
Expand Up @@ -19,6 +19,8 @@ package controller
import (
"context"
"errors"
"github.com/prometheus/client_golang/prometheus"
"math"
"reflect"
"testing"
"time"
Expand Down Expand Up @@ -49,6 +51,10 @@ type filteredMockProvider struct {
ApplyChangesCalls []*plan.Changes
}

type errorMockProvider struct {
mockProvider
}

func (p *filteredMockProvider) GetDomainFilter() endpoint.DomainFilterInterface {
return p.domainFilter
}
Expand All @@ -70,6 +76,10 @@ func (p *mockProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error
return p.RecordsStore, nil
}

func (p *errorMockProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
return nil, errors.New("error for testing")
}

// ApplyChanges validates that the passed in changes satisfy the assumptions.
func (p *mockProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
if len(changes.Create) != len(p.ExpectChanges.Create) {
Expand Down Expand Up @@ -180,6 +190,13 @@ func TestRunOnce(t *testing.T) {

// Validate that the mock source was called.
source.AssertExpectations(t)
// check the verified records
assert.Equal(t, math.Float64bits(1), valueFromMetric(verifiedARecords))
}

func valueFromMetric(metric prometheus.Gauge) uint64 {
ref := reflect.ValueOf(metric)
return reflect.Indirect(ref).FieldByName("valBits").Uint()
}

func TestShouldRunOnce(t *testing.T) {
Expand Down Expand Up @@ -376,3 +393,80 @@ func TestWhenMultipleControllerConsidersAllFilteredComain(t *testing.T) {
},
)
}

func TestVerifyARecords(t *testing.T) {
testControllerFiltersDomains(
t,
[]*endpoint.Endpoint{
{
DNSName: "create-record.used.tld",
RecordType: endpoint.RecordTypeA,
Targets: endpoint.Targets{"1.2.3.4"},
},
{
DNSName: "some-record.used.tld",
RecordType: endpoint.RecordTypeA,
Targets: endpoint.Targets{"8.8.8.8"},
},
},
endpoint.NewDomainFilter([]string{"used.tld"}),
[]*endpoint.Endpoint{
{
DNSName: "some-record.used.tld",
RecordType: endpoint.RecordTypeA,
Targets: endpoint.Targets{"8.8.8.8"},
},
{
DNSName: "create-record.used.tld",
RecordType: endpoint.RecordTypeA,
Targets: endpoint.Targets{"1.2.3.4"},
},
},
[]*plan.Changes{},
)
assert.Equal(t, math.Float64bits(2), valueFromMetric(verifiedARecords))

testControllerFiltersDomains(
t,
[]*endpoint.Endpoint{
{
DNSName: "some-record.1.used.tld",
RecordType: endpoint.RecordTypeA,
Targets: endpoint.Targets{"1.2.3.4"},
},
{
DNSName: "some-record.2.used.tld",
RecordType: endpoint.RecordTypeA,
Targets: endpoint.Targets{"8.8.8.8"},
},
{
DNSName: "some-record.3.used.tld",
RecordType: endpoint.RecordTypeA,
Targets: endpoint.Targets{"24.24.24.24"},
},
},
endpoint.NewDomainFilter([]string{"used.tld"}),
[]*endpoint.Endpoint{
{
DNSName: "some-record.1.used.tld",
RecordType: endpoint.RecordTypeA,
Targets: endpoint.Targets{"1.2.3.4"},
},
{
DNSName: "some-record.2.used.tld",
RecordType: endpoint.RecordTypeA,
Targets: endpoint.Targets{"8.8.8.8"},
},
},
[]*plan.Changes{{
Create: []*endpoint.Endpoint{
{
DNSName: "some-record.3.used.tld",
RecordType: endpoint.RecordTypeA,
Targets: endpoint.Targets{"24.24.24.24"},
},
},
}},
)
assert.Equal(t, math.Float64bits(2), valueFromMetric(verifiedARecords))
}
2 changes: 2 additions & 0 deletions docs/faq.md
Expand Up @@ -185,6 +185,8 @@ Here is the full list of available metrics provided by ExternalDNS:
| external_dns_registry_errors_total | Number of Registry errors | Counter |
| external_dns_source_endpoints_total | Number of Endpoints in the registry | Gauge |
| external_dns_source_errors_total | Number of Source errors | Counter |
| external_dns_controller_verified_records | Number of DNS A-records that exists both in | Gauge |
| | source & registry | |

### How can I run ExternalDNS under a specific GCP Service Account, e.g. to access DNS records in other projects?

Expand Down

0 comments on commit 4c66715

Please sign in to comment.