mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 09:36:58 +02:00
Update all the test in the civo provider
Signed-off-by: Alejandro J. Nuñez Madrazo <alejandro@civo.com>
This commit is contained in:
parent
90f3bccc74
commit
d5afada5c2
6
go.mod
6
go.mod
@ -14,6 +14,7 @@ require (
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1483
|
||||
github.com/aws/aws-sdk-go v1.42.52
|
||||
github.com/bodgit/tsig v0.0.2
|
||||
github.com/civo/civogo v0.2.90
|
||||
github.com/cloudflare/cloudflare-go v0.25.0
|
||||
github.com/cloudfoundry-community/go-cfclient v0.0.0-20190201205600-f136f9222381
|
||||
github.com/datawire/ambassador v1.6.0
|
||||
@ -52,7 +53,7 @@ require (
|
||||
go.etcd.io/etcd/api/v3 v3.5.0
|
||||
go.etcd.io/etcd/client/v3 v3.5.0
|
||||
go.uber.org/ratelimit v0.2.0
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
google.golang.org/api v0.66.0
|
||||
@ -81,7 +82,6 @@ require (
|
||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||
github.com/civo/civogo v0.2.66 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
@ -140,7 +140,7 @@ require (
|
||||
go.uber.org/zap v1.19.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
|
17
go.sum
17
go.sum
@ -236,8 +236,10 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
|
||||
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/civo/civogo v0.2.66 h1:4TXj2DmTg0mhKdRkxy3tbDTiAP+EqPZ50v3iuB58pDE=
|
||||
github.com/civo/civogo v0.2.66/go.mod h1:SR0ZOhABfQHjgNQE3UyfX4gaYsrfslkPFRFMx5P29rg=
|
||||
github.com/civo/civogo v0.2.89 h1:+7LZoOafk5qRHlKxKJR6GSmODj6AEgGjOljmrP2+YsQ=
|
||||
github.com/civo/civogo v0.2.89/go.mod h1:7+GeeFwc4AYTULaEshpT2vIcl3Qq8HPoxA17viX3l6g=
|
||||
github.com/civo/civogo v0.2.90 h1:S5jOTAonqeG0p3mqXxbKRUefxHgfM9VYOIcfyVOItcE=
|
||||
github.com/civo/civogo v0.2.90/go.mod h1:7+GeeFwc4AYTULaEshpT2vIcl3Qq8HPoxA17viX3l6g=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/cloudflare-go v0.20.0/go.mod h1:sPWL/lIC6biLEdyGZwBQ1rGQKF1FhM7N60fuNiFdYTI=
|
||||
@ -969,6 +971,7 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
@ -978,8 +981,9 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
|
||||
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
|
||||
github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
|
||||
github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
|
||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
@ -1440,8 +1444,9 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM=
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -1573,13 +1578,15 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -40,15 +40,15 @@ type CivoProvider struct {
|
||||
|
||||
// CivoChanges All API calls calculated from the plan
|
||||
type CivoChanges struct {
|
||||
Creates []CivoChangeCreate
|
||||
Deletes []CivoChangeDelete
|
||||
Updates []CivoChangeUpdate
|
||||
Creates []*CivoChangeCreate
|
||||
Deletes []*CivoChangeDelete
|
||||
Updates []*CivoChangeUpdate
|
||||
}
|
||||
|
||||
// CivoChangeCreate Civo Domain Record Creates
|
||||
type CivoChangeCreate struct {
|
||||
Domain civogo.DNSDomain
|
||||
Options civogo.DNSRecordConfig
|
||||
Options *civogo.DNSRecordConfig
|
||||
}
|
||||
|
||||
// CivoChangeUpdate Civo Domain Record Updates
|
||||
@ -171,7 +171,7 @@ func (p *CivoProvider) submitChanges(ctx context.Context, changes CivoChanges) e
|
||||
|
||||
if p.DryRun {
|
||||
log.WithFields(logFields).Info("Would create record.")
|
||||
} else if _, err := p.Client.CreateDNSRecord(change.Domain.ID, &change.Options); err != nil {
|
||||
} else if _, err := p.Client.CreateDNSRecord(change.Domain.ID, change.Options); err != nil {
|
||||
log.WithFields(logFields).Errorf(
|
||||
"Failed to Create record: %v",
|
||||
err,
|
||||
@ -226,8 +226,205 @@ func (p *CivoProvider) submitChanges(ctx context.Context, changes CivoChanges) e
|
||||
return nil
|
||||
}
|
||||
|
||||
// processCreateActions return a list of changes to create records.
|
||||
func processCreateActions(zonesByID map[string]civogo.DNSDomain, recordsByZoneID map[string][]civogo.DNSRecord, createsByZone map[string][]*endpoint.Endpoint, civoChange *CivoChanges) error {
|
||||
for zoneID, creates := range createsByZone {
|
||||
zone := zonesByID[zoneID]
|
||||
|
||||
if len(creates) == 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"zoneName": zone.Name,
|
||||
}).Debug("Skipping Zone, no creates found.")
|
||||
continue
|
||||
}
|
||||
|
||||
records := recordsByZoneID[zoneID]
|
||||
|
||||
// Generate Create
|
||||
for _, ep := range creates {
|
||||
matchedRecords := getRecordID(records, zone, *ep)
|
||||
|
||||
if len(matchedRecords) != 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"zoneName": zone.Name,
|
||||
"dnsName": ep.DNSName,
|
||||
"recordType": ep.RecordType,
|
||||
}).Warn("Records found which should not exist")
|
||||
}
|
||||
|
||||
recordType, err := convertRecordType(ep.RecordType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, target := range ep.Targets {
|
||||
civoChange.Creates = append(civoChange.Creates, &CivoChangeCreate{
|
||||
Domain: zone,
|
||||
Options: &civogo.DNSRecordConfig{
|
||||
Value: target,
|
||||
Name: getStrippedRecordName(zone, *ep),
|
||||
Type: recordType,
|
||||
Priority: 0,
|
||||
TTL: int(ep.RecordTTL),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processUpdateActions return a list of changes to update records.
|
||||
func processUpdateActions(zonesByID map[string]civogo.DNSDomain, recordsByZoneID map[string][]civogo.DNSRecord, updatesByZone map[string][]*endpoint.Endpoint, civoChange *CivoChanges) error {
|
||||
for zoneID, updates := range updatesByZone {
|
||||
zone := zonesByID[zoneID]
|
||||
|
||||
if len(updates) == 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"zoneName": zone.Name,
|
||||
}).Debug("Skipping Zone, no updates found.")
|
||||
continue
|
||||
}
|
||||
|
||||
records := recordsByZoneID[zoneID]
|
||||
|
||||
for _, ep := range updates {
|
||||
matchedRecords := getRecordID(records, zone, *ep)
|
||||
|
||||
if len(matchedRecords) == 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"dnsName": ep.DNSName,
|
||||
"zoneName": zone.Name,
|
||||
"recordType": ep.RecordType,
|
||||
}).Warn("Update Records not found.")
|
||||
}
|
||||
|
||||
recordType, err := convertRecordType(ep.RecordType)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
matchedRecordsByTarget := make(map[string]civogo.DNSRecord)
|
||||
|
||||
for _, record := range matchedRecords {
|
||||
matchedRecordsByTarget[record.Value] = record
|
||||
}
|
||||
|
||||
for _, target := range ep.Targets {
|
||||
if record, ok := matchedRecordsByTarget[target]; ok {
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"dnsName": ep.DNSName,
|
||||
"zoneName": zone.Name,
|
||||
"recordType": ep.RecordType,
|
||||
"target": target,
|
||||
}).Warn("Updating Existing Target")
|
||||
|
||||
civoChange.Updates = append(civoChange.Updates, &CivoChangeUpdate{
|
||||
Domain: zone,
|
||||
DomainRecord: record,
|
||||
Options: civogo.DNSRecordConfig{
|
||||
Value: target,
|
||||
Name: getStrippedRecordName(zone, *ep),
|
||||
Type: recordType,
|
||||
Priority: 0,
|
||||
TTL: int(ep.RecordTTL),
|
||||
},
|
||||
})
|
||||
|
||||
delete(matchedRecordsByTarget, target)
|
||||
} else {
|
||||
// Record did not previously exist, create new 'target'
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"dnsName": ep.DNSName,
|
||||
"zoneName": zone.Name,
|
||||
"recordType": ep.RecordType,
|
||||
"target": target,
|
||||
}).Warn("Creating New Target")
|
||||
|
||||
civoChange.Creates = append(civoChange.Creates, &CivoChangeCreate{
|
||||
Domain: zone,
|
||||
Options: &civogo.DNSRecordConfig{
|
||||
Value: target,
|
||||
Name: getStrippedRecordName(zone, *ep),
|
||||
Type: recordType,
|
||||
Priority: 0,
|
||||
TTL: int(ep.RecordTTL),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Any remaining records have been removed, delete them
|
||||
for _, record := range matchedRecordsByTarget {
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"dnsName": ep.DNSName,
|
||||
"recordType": ep.RecordType,
|
||||
"target": record.Value,
|
||||
}).Warn("Deleting target")
|
||||
|
||||
civoChange.Deletes = append(civoChange.Deletes, &CivoChangeDelete{
|
||||
Domain: zone,
|
||||
DomainRecord: record,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processDeleteActions return a list of changes to delete records.
|
||||
func processDeleteActions(zonesByID map[string]civogo.DNSDomain, recordsByZoneID map[string][]civogo.DNSRecord, deletesByZone map[string][]*endpoint.Endpoint, civoChange *CivoChanges) error {
|
||||
for zoneID, deletes := range deletesByZone {
|
||||
zone := zonesByID[zoneID]
|
||||
|
||||
if len(deletes) == 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"zoneName": zone.Name,
|
||||
}).Debug("Skipping Zone, no deletes found.")
|
||||
continue
|
||||
}
|
||||
|
||||
records := recordsByZoneID[zoneID]
|
||||
|
||||
for _, ep := range deletes {
|
||||
matchedRecords := getRecordID(records, zone, *ep)
|
||||
|
||||
if len(matchedRecords) == 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"dnsName": ep.DNSName,
|
||||
"zoneName": zone.Name,
|
||||
"recordType": ep.RecordType,
|
||||
}).Warn("Records to Delete not found.")
|
||||
}
|
||||
|
||||
for _, record := range matchedRecords {
|
||||
civoChange.Deletes = append(civoChange.Deletes, &CivoChangeDelete{
|
||||
Domain: zone,
|
||||
DomainRecord: record,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// ApplyChanges applies a given set of changes in a given zone.
|
||||
func (p *CivoProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
||||
var civoChange CivoChanges
|
||||
recordsByZoneID := make(map[string][]civogo.DNSRecord)
|
||||
|
||||
zones, err := p.fetchZones(ctx)
|
||||
@ -260,187 +457,29 @@ func (p *CivoProvider) ApplyChanges(ctx context.Context, changes *plan.Changes)
|
||||
updatesByZone := endpointsByZone(zoneNameIDMapper, changes.UpdateNew)
|
||||
deletesByZone := endpointsByZone(zoneNameIDMapper, changes.Delete)
|
||||
|
||||
var civoCreates []CivoChangeCreate
|
||||
var civoUpdates []CivoChangeUpdate
|
||||
var civoDeletes []CivoChangeDelete
|
||||
|
||||
// Generate Creates
|
||||
for zoneID, creates := range createsByZone {
|
||||
zone := zonesByID[zoneID]
|
||||
|
||||
if len(creates) == 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"zoneName": zone.Name,
|
||||
}).Debug("Skipping Zone, no creates found.")
|
||||
continue
|
||||
}
|
||||
|
||||
records := recordsByZoneID[zoneID]
|
||||
|
||||
// Generate Create
|
||||
for _, ep := range creates {
|
||||
matchedRecords := getRecordID(records, zone, ep)
|
||||
|
||||
if len(matchedRecords) != 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"zoneName": zone.Name,
|
||||
"dnsName": ep.DNSName,
|
||||
"recordType": ep.RecordType,
|
||||
}).Warn("Records found which should not exist")
|
||||
}
|
||||
|
||||
recordType, err := convertRecordType(ep.RecordType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, target := range ep.Targets {
|
||||
civoCreates = append(civoCreates, CivoChangeCreate{
|
||||
Domain: zone,
|
||||
Options: civogo.DNSRecordConfig{
|
||||
Value: target,
|
||||
Name: getStrippedRecordName(zone, ep),
|
||||
Type: recordType,
|
||||
Priority: 0,
|
||||
TTL: int(ep.RecordTTL),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
err = processCreateActions(zonesByID, recordsByZoneID, createsByZone, &civoChange)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate Updates
|
||||
for zoneID, updates := range updatesByZone {
|
||||
zone := zonesByID[zoneID]
|
||||
|
||||
if len(updates) == 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"zoneName": zone.Name,
|
||||
}).Debug("Skipping Zone, no updates found.")
|
||||
continue
|
||||
}
|
||||
|
||||
records := recordsByZoneID[zoneID]
|
||||
|
||||
for _, ep := range updates {
|
||||
matchedRecords := getRecordID(records, zone, ep)
|
||||
|
||||
if len(matchedRecords) == 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"dnsName": ep.DNSName,
|
||||
"zoneName": zone.Name,
|
||||
"recordType": ep.RecordType,
|
||||
}).Warn("Update Records not found.")
|
||||
}
|
||||
|
||||
recordType, err := convertRecordType(ep.RecordType)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
matchedRecordsByTarget := make(map[string]civogo.DNSRecord)
|
||||
|
||||
for _, record := range matchedRecords {
|
||||
matchedRecordsByTarget[record.DNSDomainID] = record
|
||||
}
|
||||
|
||||
for _, target := range ep.Targets {
|
||||
if record, ok := matchedRecordsByTarget[target]; ok {
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"dnsName": ep.DNSName,
|
||||
"zoneName": zone.Name,
|
||||
"recordType": ep.RecordType,
|
||||
"target": target,
|
||||
}).Warn("Updating Existing Target")
|
||||
|
||||
civoUpdates = append(civoUpdates, CivoChangeUpdate{
|
||||
Domain: zone,
|
||||
DomainRecord: record,
|
||||
Options: civogo.DNSRecordConfig{
|
||||
Value: target,
|
||||
Name: getStrippedRecordName(zone, ep),
|
||||
Type: recordType,
|
||||
Priority: 0,
|
||||
TTL: int(ep.RecordTTL),
|
||||
},
|
||||
})
|
||||
|
||||
delete(matchedRecordsByTarget, target)
|
||||
} else {
|
||||
// Record did not previously exist, create new 'target'
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"dnsName": ep.DNSName,
|
||||
"zoneName": zone.Name,
|
||||
"recordType": ep.RecordType,
|
||||
"target": target,
|
||||
}).Warn("Creating New Target")
|
||||
|
||||
civoCreates = append(civoCreates, CivoChangeCreate{
|
||||
Domain: zone,
|
||||
Options: civogo.DNSRecordConfig{
|
||||
Value: target,
|
||||
Name: getStrippedRecordName(zone, ep),
|
||||
Type: recordType,
|
||||
Priority: 0,
|
||||
TTL: int(ep.RecordTTL),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
err = processUpdateActions(zonesByID, recordsByZoneID, updatesByZone, &civoChange)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
// Generate Deletes
|
||||
for zoneID, deletes := range deletesByZone {
|
||||
zone := zonesByID[zoneID]
|
||||
err = processDeleteActions(zonesByID, recordsByZoneID, deletesByZone, &civoChange)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(deletes) == 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"zoneName": zone.Name,
|
||||
}).Debug("Skipping Zone, no deletes found.")
|
||||
continue
|
||||
}
|
||||
|
||||
records := recordsByZoneID[zoneID]
|
||||
|
||||
for _, ep := range deletes {
|
||||
matchedRecords := getRecordID(records, zone, ep)
|
||||
|
||||
if len(matchedRecords) == 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"zoneID": zoneID,
|
||||
"dnsName": ep.DNSName,
|
||||
"zoneName": zone.Name,
|
||||
"recordType": ep.RecordType,
|
||||
}).Warn("Records to Delete not found.")
|
||||
}
|
||||
|
||||
for _, record := range matchedRecords {
|
||||
civoDeletes = append(civoDeletes, CivoChangeDelete{
|
||||
Domain: zone,
|
||||
DomainRecord: record,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return p.submitChanges(ctx, CivoChanges{
|
||||
Creates: civoCreates,
|
||||
Deletes: civoDeletes,
|
||||
Updates: civoUpdates,
|
||||
})
|
||||
return p.submitChanges(ctx, civoChange)
|
||||
}
|
||||
|
||||
func endpointsByZone(zoneNameIDMapper provider.ZoneIDName, endpoints []*endpoint.Endpoint) map[string][]endpoint.Endpoint {
|
||||
endpointsByZone := make(map[string][]endpoint.Endpoint)
|
||||
func endpointsByZone(zoneNameIDMapper provider.ZoneIDName, endpoints []*endpoint.Endpoint) map[string][]*endpoint.Endpoint {
|
||||
endpointsByZone := make(map[string][]*endpoint.Endpoint)
|
||||
|
||||
for _, ep := range endpoints {
|
||||
zoneID, _ := zoneNameIDMapper.FindZone(ep.DNSName)
|
||||
@ -448,7 +487,7 @@ func endpointsByZone(zoneNameIDMapper provider.ZoneIDName, endpoints []*endpoint
|
||||
log.Debugf("Skipping record %s because no hosted zone matching record DNS Name was detected", ep.DNSName)
|
||||
continue
|
||||
}
|
||||
endpointsByZone[zoneID] = append(endpointsByZone[zoneID], *ep)
|
||||
endpointsByZone[zoneID] = append(endpointsByZone[zoneID], ep)
|
||||
}
|
||||
|
||||
return endpointsByZone
|
||||
|
@ -18,18 +18,22 @@ package civo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/civo/civogo"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gotest.tools/assert"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
)
|
||||
|
||||
type mockCivoClient struct{}
|
||||
|
||||
func TestNewCivoProvider(t *testing.T) {
|
||||
_ = os.Setenv("CIVO_TOKEN", "xxxxxxxxxxxxxxx")
|
||||
_, err := NewCivoProvider(endpoint.NewDomainFilter([]string{"test.civo.com"}), true)
|
||||
@ -98,34 +102,295 @@ func TestCivoProviderRecords(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestCivoProviderApplyChanges(t *testing.T) {
|
||||
changes := &plan.Changes{}
|
||||
client, server, _ := civogo.NewClientForTesting(map[string]string{
|
||||
"/v2/dns/12345/records": `[
|
||||
{"id": "1", "domain_id":"12345", "account_id": "1", "name": "www", "type": "A", "value": "10.0.0.0", "ttl": 600},
|
||||
{"id": "2", "account_id": "1", "domain_id":"12345", "name": "mail", "type": "A", "value": "10.0.0.1", "ttl": 600}
|
||||
]`,
|
||||
"/v2/dns": `[
|
||||
{"id": "12345", "account_id": "1", "name": "example.com"},
|
||||
{"id": "12346", "account_id": "1", "name": "example.net"}
|
||||
]`,
|
||||
func TestCivoProcessCreateActions(t *testing.T) {
|
||||
zoneByID := map[string]civogo.DNSDomain{
|
||||
"example.com": {
|
||||
ID: "1",
|
||||
AccountID: "1",
|
||||
Name: "example.com",
|
||||
},
|
||||
}
|
||||
|
||||
recordsByZoneID := map[string][]civogo.DNSRecord{
|
||||
"example.com": {
|
||||
{
|
||||
ID: "1",
|
||||
AccountID: "1",
|
||||
DNSDomainID: "1",
|
||||
Name: "txt",
|
||||
Value: "12.12.12.1",
|
||||
Type: "A",
|
||||
TTL: 600,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
createsByDomain := map[string][]*endpoint.Endpoint{
|
||||
"example.com": {
|
||||
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.4"),
|
||||
endpoint.NewEndpoint("txt.example.com", endpoint.RecordTypeCNAME, "foo.example.com"),
|
||||
},
|
||||
}
|
||||
|
||||
var changes CivoChanges
|
||||
err := processCreateActions(zoneByID, recordsByZoneID, createsByDomain, &changes)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 2, len(changes.Creates))
|
||||
assert.Equal(t, 0, len(changes.Updates))
|
||||
assert.Equal(t, 0, len(changes.Deletes))
|
||||
|
||||
expectedCreates := []*CivoChangeCreate{
|
||||
{
|
||||
Domain: civogo.DNSDomain{
|
||||
ID: "1",
|
||||
AccountID: "1",
|
||||
Name: "example.com",
|
||||
},
|
||||
Options: &civogo.DNSRecordConfig{
|
||||
Type: "A",
|
||||
Name: "foo",
|
||||
Value: "1.2.3.4",
|
||||
},
|
||||
},
|
||||
{
|
||||
Domain: civogo.DNSDomain{
|
||||
ID: "1",
|
||||
AccountID: "1",
|
||||
Name: "example.com",
|
||||
},
|
||||
Options: &civogo.DNSRecordConfig{
|
||||
Type: "CNAME",
|
||||
Name: "txt",
|
||||
Value: "foo.example.com",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if !elementsMatch(t, expectedCreates, changes.Creates) {
|
||||
assert.Failf(t, "diff: %s", cmp.Diff(expectedCreates, changes.Creates))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCivoProcessUpdateActions(t *testing.T) {
|
||||
zoneByID := map[string]civogo.DNSDomain{
|
||||
"example.com": {
|
||||
ID: "1",
|
||||
AccountID: "1",
|
||||
Name: "example.com",
|
||||
},
|
||||
}
|
||||
|
||||
recordsByZoneID := map[string][]civogo.DNSRecord{
|
||||
"example.com": {
|
||||
{
|
||||
ID: "1",
|
||||
AccountID: "1",
|
||||
DNSDomainID: "1",
|
||||
Name: "txt",
|
||||
Value: "1.2.3.4",
|
||||
Type: "A",
|
||||
TTL: 600,
|
||||
},
|
||||
{
|
||||
ID: "2",
|
||||
AccountID: "1",
|
||||
DNSDomainID: "1",
|
||||
Name: "foo",
|
||||
Value: "foo.example.com",
|
||||
Type: "CNAME",
|
||||
TTL: 600,
|
||||
},
|
||||
{
|
||||
ID: "3",
|
||||
AccountID: "1",
|
||||
DNSDomainID: "1",
|
||||
Name: "bar",
|
||||
Value: "10.10.10.1",
|
||||
Type: "A",
|
||||
TTL: 600,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
updatesByDomain := map[string][]*endpoint.Endpoint{
|
||||
"example.com": {
|
||||
endpoint.NewEndpoint("txt.example.com", endpoint.RecordTypeA, "10.20.30.40"),
|
||||
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeCNAME, "bar.example.com"),
|
||||
},
|
||||
}
|
||||
|
||||
var changes CivoChanges
|
||||
err := processUpdateActions(zoneByID, recordsByZoneID, updatesByDomain, &changes)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 2, len(changes.Creates))
|
||||
assert.Equal(t, 0, len(changes.Updates))
|
||||
assert.Equal(t, 2, len(changes.Deletes))
|
||||
|
||||
expectedUpdate := []*CivoChangeCreate{
|
||||
{
|
||||
Domain: civogo.DNSDomain{
|
||||
ID: "1",
|
||||
AccountID: "1",
|
||||
Name: "example.com",
|
||||
},
|
||||
Options: &civogo.DNSRecordConfig{
|
||||
Type: "A",
|
||||
Name: "txt",
|
||||
Value: "10.20.30.40",
|
||||
},
|
||||
},
|
||||
{
|
||||
Domain: civogo.DNSDomain{
|
||||
ID: "1",
|
||||
AccountID: "1",
|
||||
Name: "example.com",
|
||||
},
|
||||
Options: &civogo.DNSRecordConfig{
|
||||
Type: "CNAME",
|
||||
Name: "foo",
|
||||
Value: "bar.example.com",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if !elementsMatch(t, expectedUpdate, changes.Creates) {
|
||||
assert.Failf(t, "diff: %s", cmp.Diff(expectedUpdate, changes.Creates))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCivoProcessDeleteAction(t *testing.T) {
|
||||
zoneByID := map[string]civogo.DNSDomain{
|
||||
"example.com": {
|
||||
ID: "1",
|
||||
AccountID: "1",
|
||||
Name: "example.com",
|
||||
},
|
||||
}
|
||||
|
||||
recordsByZoneID := map[string][]civogo.DNSRecord{
|
||||
"example.com": {
|
||||
{
|
||||
ID: "1",
|
||||
AccountID: "1",
|
||||
DNSDomainID: "1",
|
||||
Name: "txt",
|
||||
Value: "1.2.3.4",
|
||||
Type: "A",
|
||||
TTL: 600,
|
||||
},
|
||||
{
|
||||
ID: "2",
|
||||
AccountID: "1",
|
||||
DNSDomainID: "1",
|
||||
Name: "foo",
|
||||
Value: "5.6.7.8",
|
||||
Type: "A",
|
||||
TTL: 600,
|
||||
},
|
||||
{
|
||||
ID: "3",
|
||||
AccountID: "1",
|
||||
DNSDomainID: "1",
|
||||
Name: "bar",
|
||||
Value: "10.10.10.1",
|
||||
Type: "A",
|
||||
TTL: 600,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
deleteByDomain := map[string][]*endpoint.Endpoint{
|
||||
"example.com": {
|
||||
endpoint.NewEndpoint("txt.example.com", endpoint.RecordTypeA, "1.2.3.4"),
|
||||
endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "5.6.7.8"),
|
||||
},
|
||||
}
|
||||
|
||||
var changes CivoChanges
|
||||
err := processDeleteActions(zoneByID, recordsByZoneID, deleteByDomain, &changes)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, 0, len(changes.Creates))
|
||||
assert.Equal(t, 0, len(changes.Updates))
|
||||
assert.Equal(t, 2, len(changes.Deletes))
|
||||
|
||||
expectedDelete := []*CivoChangeDelete{
|
||||
{
|
||||
Domain: civogo.DNSDomain{
|
||||
ID: "1",
|
||||
AccountID: "1",
|
||||
Name: "example.com",
|
||||
},
|
||||
DomainRecord: civogo.DNSRecord{
|
||||
ID: "1",
|
||||
AccountID: "1",
|
||||
DNSDomainID: "1",
|
||||
Name: "txt",
|
||||
Value: "1.2.3.4",
|
||||
Type: "A",
|
||||
TTL: 600,
|
||||
},
|
||||
},
|
||||
{
|
||||
Domain: civogo.DNSDomain{
|
||||
ID: "1",
|
||||
AccountID: "1",
|
||||
Name: "example.com",
|
||||
},
|
||||
DomainRecord: civogo.DNSRecord{
|
||||
ID: "2",
|
||||
AccountID: "1",
|
||||
DNSDomainID: "1",
|
||||
Type: "A",
|
||||
Name: "foo",
|
||||
Value: "5.6.7.8",
|
||||
TTL: 600,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if !elementsMatch(t, expectedDelete, changes.Deletes) {
|
||||
assert.Failf(t, "diff: %s", cmp.Diff(expectedDelete, changes.Deletes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCivoApplyChanges(t *testing.T) {
|
||||
client, server, _ := civogo.NewAdvancedClientForTesting([]civogo.ConfigAdvanceClientForTesting{
|
||||
{
|
||||
Method: "GET",
|
||||
Value: []civogo.ValueAdvanceClientForTesting{
|
||||
{
|
||||
RequestBody: "",
|
||||
URL: "/v2/dns",
|
||||
ResponseBody: `[{"id": "12345", "account_id": "1", "name": "example.com"}]`,
|
||||
},
|
||||
{
|
||||
RequestBody: "",
|
||||
URL: "/v2/dns/12345/records",
|
||||
ResponseBody: `[]`,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
changes := &plan.Changes{}
|
||||
provider := &CivoProvider{
|
||||
Client: *client,
|
||||
}
|
||||
|
||||
changes.Create = []*endpoint.Endpoint{
|
||||
{DNSName: "test.com", Targets: endpoint.Targets{"target"}},
|
||||
{DNSName: "ttl.test.com", Targets: endpoint.Targets{"target"}, RecordTTL: 600},
|
||||
{DNSName: "new.ext-dns-test.example.com", Targets: endpoint.Targets{"target"}, RecordType: endpoint.RecordTypeA},
|
||||
{DNSName: "new.ext-dns-test-with-ttl.example.com", Targets: endpoint.Targets{"target"}, RecordType: endpoint.RecordTypeA, RecordTTL: 100},
|
||||
}
|
||||
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "test.test.com", Targets: endpoint.Targets{"target-new"}, RecordType: "A", RecordTTL: 600}}
|
||||
changes.Delete = []*endpoint.Endpoint{{DNSName: "test.test.com", Targets: endpoint.Targets{"target"}, RecordType: "A"}}
|
||||
changes.Delete = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"target"}}}
|
||||
changes.UpdateOld = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.example.de", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"target-old"}}}
|
||||
changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.foo.com", Targets: endpoint.Targets{"target-new"}, RecordType:endpoint.RecordTypeCNAME, RecordTTL: 100}}
|
||||
err := provider.ApplyChanges(context.Background(), changes)
|
||||
if err != nil {
|
||||
t.Errorf("should not fail, %s", err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestCivoProviderFetchZones(t *testing.T) {
|
||||
@ -148,7 +413,7 @@ func TestCivoProviderFetchZones(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.DeepEqual(t, zones, expected)
|
||||
assert.ElementsMatch(t, zones, expected)
|
||||
}
|
||||
func TestCivoProviderFetchZonesWithFilter(t *testing.T) {
|
||||
client, server, _ := civogo.NewClientForTesting(map[string]string{
|
||||
@ -171,7 +436,7 @@ func TestCivoProviderFetchZonesWithFilter(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.DeepEqual(t, expected, actual)
|
||||
assert.ElementsMatch(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestCivoProviderFetchRecords(t *testing.T) {
|
||||
@ -194,7 +459,7 @@ func TestCivoProviderFetchRecords(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.DeepEqual(t, expected, actual)
|
||||
assert.ElementsMatch(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestCivoGetStrippedRecordName(t *testing.T) {
|
||||
@ -263,3 +528,67 @@ func TestCivoProviderGetRecordID(t *testing.T) {
|
||||
|
||||
assert.Equal(t, id[0].ID, record[0].ID)
|
||||
}
|
||||
|
||||
// This function is an adapted copy of the testify package's ElementsMatch function with the
|
||||
// call to ObjectsAreEqual replaced with cmp.Equal which better handles struct's with pointers to
|
||||
// other structs. It also ignores ordering when comparing unlike cmp.Equal.
|
||||
func elementsMatch(t *testing.T, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) {
|
||||
if listA == nil && listB == nil {
|
||||
return true
|
||||
} else if listA == nil {
|
||||
return isEmpty(listB)
|
||||
} else if listB == nil {
|
||||
return isEmpty(listA)
|
||||
}
|
||||
|
||||
aKind := reflect.TypeOf(listA).Kind()
|
||||
bKind := reflect.TypeOf(listB).Kind()
|
||||
|
||||
if aKind != reflect.Array && aKind != reflect.Slice {
|
||||
return assert.Fail(t, fmt.Sprintf("%q has an unsupported type %s", listA, aKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
if bKind != reflect.Array && bKind != reflect.Slice {
|
||||
return assert.Fail(t, fmt.Sprintf("%q has an unsupported type %s", listB, bKind), msgAndArgs...)
|
||||
}
|
||||
|
||||
aValue := reflect.ValueOf(listA)
|
||||
bValue := reflect.ValueOf(listB)
|
||||
|
||||
aLen := aValue.Len()
|
||||
bLen := bValue.Len()
|
||||
|
||||
if aLen != bLen {
|
||||
return assert.Fail(t, fmt.Sprintf("lengths don't match: %d != %d", aLen, bLen), msgAndArgs...)
|
||||
}
|
||||
|
||||
// Mark indexes in bValue that we already used
|
||||
visited := make([]bool, bLen)
|
||||
for i := 0; i < aLen; i++ {
|
||||
element := aValue.Index(i).Interface()
|
||||
found := false
|
||||
for j := 0; j < bLen; j++ {
|
||||
if visited[j] {
|
||||
continue
|
||||
}
|
||||
if cmp.Equal(bValue.Index(j).Interface(), element) {
|
||||
visited[j] = true
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return assert.Fail(t, fmt.Sprintf("element %s appears more times in %s than in %s", element, aValue, bValue), msgAndArgs...)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func isEmpty(xs interface{}) bool {
|
||||
if xs != nil {
|
||||
objValue := reflect.ValueOf(xs)
|
||||
return objValue.Len() == 0
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user