mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2025-02-22 14:43:01 +08:00
refactor into groups (#684)
* Refactor tests into "groups", each with its own filter (not/only/requires) to select which providers are appropriate. * Test driver code is now a lot more simple and clear. * Add support for not(), only(), and requires() as a way to select/reject providers for a test. * Add docs explaining how to add tests * Logging messages are much cleaner now, especially when tests are skipped. * -start and -end now refer to test groups, not individual tests. Log messages list the group numbers clearly. * Add stringer for Capabilities * Change the order of the tests so that simple tests are first * Removed knownFailures from providers.json * fmtjson providers.json Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>
This commit is contained in:
parent
fa160b7202
commit
67e78f7e15
15 changed files with 10344 additions and 9999 deletions
4
go.mod
4
go.mod
|
@ -50,10 +50,10 @@ require (
|
|||
github.com/vultr/govultr v0.2.0
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d // indirect
|
||||
golang.org/x/mod v0.2.0 // indirect
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c // indirect
|
||||
golang.org/x/tools v0.0.0-20200212213243-2ee7536ab1cc // indirect
|
||||
golang.org/x/tools v0.0.0-20200303202040-658b03bcd3d8 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
|
||||
google.golang.org/api v0.17.0
|
||||
google.golang.org/appengine v1.6.5 // indirect
|
||||
|
|
5
go.sum
5
go.sum
|
@ -277,6 +277,8 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjut
|
|||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0 h1:MsuvTghUPjX762sGLnGsxC3HM0B5r83wEtYcYR8/vRs=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -290,6 +292,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -330,6 +333,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
|||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212213243-2ee7536ab1cc h1:AlqcddMxhvJRMNuW1bFwyuNdm+TCkcEGKvw6FVPTuR8=
|
||||
golang.org/x/tools v0.0.0-20200212213243-2ee7536ab1cc/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200303202040-658b03bcd3d8 h1:2oWBslXKHx1vReVc1bA4mflTOcSxgXz536kSRBhwDds=
|
||||
golang.org/x/tools v0.0.0-20200303202040-658b03bcd3d8/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -84,86 +85,168 @@ func getDomainConfigWithNameservers(t *testing.T, prv providers.DNSServiceProvid
|
|||
return dc
|
||||
}
|
||||
|
||||
func runTests(t *testing.T, prv providers.DNSServiceProvider, domainName string, knownFailures map[int]bool, origConfig map[string]string) {
|
||||
dc := getDomainConfigWithNameservers(t, prv, domainName)
|
||||
// run tests one at a time
|
||||
end := *endIdx
|
||||
tests := makeTests(t)
|
||||
if end == 0 || end >= len(tests) {
|
||||
end = len(tests) - 1
|
||||
// testPermitted returns nil if the test is permitted, otherwise an
|
||||
// error explaining why it is not.
|
||||
func testPermitted(t *testing.T, p string, f TestGroup) error {
|
||||
|
||||
// not() and only() can't be mixed.
|
||||
if len(f.only) != 0 && len(f.not) != 0 {
|
||||
return fmt.Errorf("invalid filter: can't mix not() and only()")
|
||||
}
|
||||
for i := *startIdx; i <= end; i++ {
|
||||
tst := tests[i]
|
||||
if t.Failed() {
|
||||
break
|
||||
// TODO(tlim): Have a separate validation pass so that such mistakes
|
||||
// are more visible?
|
||||
|
||||
// If there are any required capabilities, make sure they all exist.
|
||||
if len(f.required) != 0 {
|
||||
for _, c := range f.required {
|
||||
if !providers.ProviderHasCapability(*providerToRun, c) {
|
||||
return fmt.Errorf("%s not supported", c)
|
||||
}
|
||||
}
|
||||
t.Run(fmt.Sprintf("%d: %s", i, tst.Desc), func(t *testing.T) {
|
||||
skipVal := false
|
||||
if knownFailures[i] {
|
||||
t.Log("SKIPPING VALIDATION FOR KNOWN FAILURE CASE")
|
||||
skipVal = true
|
||||
}
|
||||
|
||||
// If there are any "only" items, you must be one of them.
|
||||
if len(f.only) != 0 {
|
||||
for _, provider := range f.only {
|
||||
if p == provider {
|
||||
return nil
|
||||
}
|
||||
dom, _ := dc.Copy()
|
||||
for _, r := range tst.Records {
|
||||
rc := models.RecordConfig(*r)
|
||||
if strings.Contains(rc.GetTargetField(), "**current-domain**") {
|
||||
_ = rc.SetTarget(strings.Replace(rc.GetTargetField(), "**current-domain**", domainName, 1) + ".")
|
||||
}
|
||||
if strings.Contains(rc.GetTargetField(), "**current-domain-no-trailing**") {
|
||||
_ = rc.SetTarget(strings.Replace(rc.GetTargetField(), "**current-domain-no-trailing**", domainName, 1))
|
||||
}
|
||||
if strings.Contains(rc.GetLabelFQDN(), "**current-domain**") {
|
||||
rc.SetLabelFromFQDN(strings.Replace(rc.GetLabelFQDN(), "**current-domain**", domainName, 1), domainName)
|
||||
}
|
||||
if providers.ProviderHasCapability(*providerToRun, providers.CanUseAzureAlias) {
|
||||
if strings.Contains(rc.GetTargetField(), "**subscription-id**") {
|
||||
_ = rc.SetTarget(strings.Replace(rc.GetTargetField(), "**subscription-id**", origConfig["SubscriptionID"], 1))
|
||||
}
|
||||
if strings.Contains(rc.GetTargetField(), "**resource-group**") {
|
||||
_ = rc.SetTarget(strings.Replace(rc.GetTargetField(), "**resource-group**", origConfig["ResourceGroup"], 1))
|
||||
}
|
||||
}
|
||||
dom.Records = append(dom.Records, &rc)
|
||||
}
|
||||
return fmt.Errorf("disabled by only")
|
||||
}
|
||||
|
||||
// If there are any "not" items, you must NOT be one of them.
|
||||
if len(f.not) != 0 {
|
||||
for _, provider := range f.not {
|
||||
if p == provider {
|
||||
return fmt.Errorf("excluded by not(\"%s\")", provider)
|
||||
}
|
||||
dom.IgnoredLabels = tst.IgnoredLabels
|
||||
models.PostProcessRecords(dom.Records)
|
||||
dom2, _ := dom.Copy()
|
||||
// get corrections for first time
|
||||
corrections, err := prv.GetDomainCorrections(dom)
|
||||
if err != nil {
|
||||
t.Fatal(fmt.Errorf("runTests: %w", err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//func makeClearFilter() *TestCase {
|
||||
// tc := tc("Empty")
|
||||
// tc.ChangeFilter = true
|
||||
// return tc
|
||||
//}
|
||||
|
||||
// desc := fmt.Sprintf("%d: %s", i, tst.Desc)
|
||||
// makeChanges runs one set of DNS record tests. Returns true on success.
|
||||
func makeChanges(t *testing.T, prv providers.DNSServiceProvider, dc *models.DomainConfig, tst *TestCase, desc string, expectChanges bool, origConfig map[string]string) bool {
|
||||
domainName := dc.Name
|
||||
|
||||
return t.Run(desc, func(t *testing.T) {
|
||||
dom, _ := dc.Copy()
|
||||
for _, r := range tst.Records {
|
||||
rc := models.RecordConfig(*r)
|
||||
if strings.Contains(rc.GetTargetField(), "**current-domain**") {
|
||||
_ = rc.SetTarget(strings.Replace(rc.GetTargetField(), "**current-domain**", domainName, 1) + ".")
|
||||
}
|
||||
if !skipVal && i != *startIdx && len(corrections) == 0 {
|
||||
if tst.Desc != "Empty" {
|
||||
// There are "no corrections" if the last test was programmatically
|
||||
// skipped. We detect this (possibly inaccurately) by checking to
|
||||
// see if .Desc is "Empty".
|
||||
t.Fatalf("Expect changes for all tests, but got none")
|
||||
}
|
||||
if strings.Contains(rc.GetTargetField(), "**current-domain-no-trailing**") {
|
||||
_ = rc.SetTarget(strings.Replace(rc.GetTargetField(), "**current-domain-no-trailing**", domainName, 1))
|
||||
}
|
||||
for _, c := range corrections {
|
||||
if *verbose {
|
||||
t.Log(c.Msg)
|
||||
}
|
||||
err = c.F()
|
||||
if !skipVal && err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(rc.GetLabelFQDN(), "**current-domain**") {
|
||||
rc.SetLabelFromFQDN(strings.Replace(rc.GetLabelFQDN(), "**current-domain**", domainName, 1), domainName)
|
||||
}
|
||||
// run a second time and expect zero corrections
|
||||
corrections, err = prv.GetDomainCorrections(dom2)
|
||||
//if providers.ProviderHasCapability(*providerToRun, providers.CanUseAzureAlias) {
|
||||
if strings.Contains(rc.GetTargetField(), "**subscription-id**") {
|
||||
_ = rc.SetTarget(strings.Replace(rc.GetTargetField(), "**subscription-id**", origConfig["SubscriptionID"], 1))
|
||||
}
|
||||
if strings.Contains(rc.GetTargetField(), "**resource-group**") {
|
||||
_ = rc.SetTarget(strings.Replace(rc.GetTargetField(), "**resource-group**", origConfig["ResourceGroup"], 1))
|
||||
}
|
||||
//}
|
||||
dom.Records = append(dom.Records, &rc)
|
||||
}
|
||||
dom.IgnoredLabels = tst.IgnoredLabels
|
||||
models.PostProcessRecords(dom.Records)
|
||||
dom2, _ := dom.Copy()
|
||||
|
||||
// get and run corrections for first time
|
||||
corrections, err := prv.GetDomainCorrections(dom)
|
||||
if err != nil {
|
||||
t.Fatal(fmt.Errorf("runTests: %w", err))
|
||||
}
|
||||
if len(corrections) == 0 && expectChanges {
|
||||
t.Fatalf("Expected changes, but got none")
|
||||
}
|
||||
for _, c := range corrections {
|
||||
if *verbose {
|
||||
t.Log(c.Msg)
|
||||
}
|
||||
err = c.F()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !skipVal && len(corrections) != 0 {
|
||||
t.Logf("Expected 0 corrections on second run, but found %d.", len(corrections))
|
||||
for i, c := range corrections {
|
||||
t.Logf("#%d: %s", i, c.Msg)
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// If we just emptied out the zone, no need for a second pass.
|
||||
if len(tst.Records) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// run a second time and expect zero corrections
|
||||
corrections, err = prv.GetDomainCorrections(dom2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(corrections) != 0 {
|
||||
t.Logf("Expected 0 corrections on second run, but found %d.", len(corrections))
|
||||
for i, c := range corrections {
|
||||
t.Logf("#%d: %s", i, c.Msg)
|
||||
}
|
||||
})
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func runTests(t *testing.T, prv providers.DNSServiceProvider, domainName string, knownFailures map[int]bool, origConfig map[string]string) {
|
||||
dc := getDomainConfigWithNameservers(t, prv, domainName)
|
||||
testGroups := makeTests(t)
|
||||
|
||||
firstGroup := *startIdx
|
||||
lastGroup := *endIdx
|
||||
if lastGroup == 0 {
|
||||
lastGroup = len(testGroups)
|
||||
}
|
||||
|
||||
// Start the zone with a clean slate.
|
||||
makeChanges(t, prv, dc, tc("Empty"), "Clean Slate", false, nil)
|
||||
|
||||
curGroup := -1
|
||||
for gIdx, group := range testGroups {
|
||||
|
||||
// Abide by -start -end flags
|
||||
curGroup += 1
|
||||
if curGroup < firstGroup || curGroup > lastGroup {
|
||||
continue
|
||||
}
|
||||
|
||||
// Abide by filter
|
||||
if err := testPermitted(t, *providerToRun, *group); err != nil {
|
||||
//t.Logf("%s: ***SKIPPED(%v)***", group.Desc, err)
|
||||
makeChanges(t, prv, dc, tc("Empty"), fmt.Sprintf("%02d:%s ***SKIPPED(%v)***", gIdx, group.Desc, err), false, origConfig)
|
||||
continue
|
||||
}
|
||||
|
||||
// Run the tests.
|
||||
for _, tst := range group.tests {
|
||||
makeChanges(t, prv, dc, tst, fmt.Sprintf("%02d:%s", gIdx, group.Desc), true, origConfig)
|
||||
if t.Failed() {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all records so next group starts with a clean slate.
|
||||
makeChanges(t, prv, dc, tc("Empty"), "Post cleanup", false, nil)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestDualProviders(t *testing.T) {
|
||||
|
@ -209,6 +292,14 @@ func TestDualProviders(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
type TestGroup struct {
|
||||
Desc string
|
||||
required []providers.Capability
|
||||
only []string
|
||||
not []string
|
||||
tests []*TestCase
|
||||
}
|
||||
|
||||
type TestCase struct {
|
||||
Desc string
|
||||
Records []*rec
|
||||
|
@ -349,6 +440,45 @@ func (r *rec) ttl(t uint32) *rec {
|
|||
return r
|
||||
}
|
||||
|
||||
func manyA(namePattern, target string, n int) []*rec {
|
||||
recs := []*rec{}
|
||||
for i := 0; i < n; i++ {
|
||||
recs = append(recs, makeRec(fmt.Sprintf(namePattern, i), target, "A"))
|
||||
}
|
||||
return recs
|
||||
}
|
||||
|
||||
func testgroup(desc string, items ...interface{}) *TestGroup {
|
||||
group := &TestGroup{Desc: desc}
|
||||
for _, item := range items {
|
||||
switch v := item.(type) {
|
||||
case requiresFilter:
|
||||
if len(group.tests) != 0 {
|
||||
fmt.Printf("ERROR: requires() must be before all tc(): %v\n", desc)
|
||||
os.Exit(1)
|
||||
}
|
||||
group.required = append(group.required, v.caps...)
|
||||
case notFilter:
|
||||
if len(group.tests) != 0 {
|
||||
fmt.Printf("ERROR: not() must be before all tc(): %v\n", desc)
|
||||
os.Exit(1)
|
||||
}
|
||||
group.not = append(group.not, v.names...)
|
||||
case onlyFilter:
|
||||
if len(group.tests) != 0 {
|
||||
fmt.Printf("ERROR: only() must be before all tc(): %v\n", desc)
|
||||
os.Exit(1)
|
||||
}
|
||||
group.only = append(group.only, v.names...)
|
||||
case *TestCase:
|
||||
group.tests = append(group.tests, v)
|
||||
default:
|
||||
fmt.Printf("I don't know about type %T (%v)\n", v, v)
|
||||
}
|
||||
}
|
||||
return group
|
||||
}
|
||||
|
||||
func tc(desc string, recs ...*rec) *TestCase {
|
||||
var records []*rec
|
||||
var ignored []string
|
||||
|
@ -366,90 +496,229 @@ func tc(desc string, recs ...*rec) *TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
func manyA(namePattern, target string, n int) []*rec {
|
||||
recs := []*rec{}
|
||||
for i := 0; i < n; i++ {
|
||||
recs = append(recs, makeRec(fmt.Sprintf(namePattern, i), target, "A"))
|
||||
}
|
||||
return recs
|
||||
func clear(items ...interface{}) *TestCase {
|
||||
return tc("Empty")
|
||||
}
|
||||
|
||||
func makeTests(t *testing.T) []*TestCase {
|
||||
// ALWAYS ADD TO BOTTOM OF LIST. Order and indexes matter.
|
||||
tests := []*TestCase{
|
||||
// A
|
||||
tc("Empty"),
|
||||
tc("Create an A record", a("@", "1.1.1.1")),
|
||||
tc("Change it", a("@", "1.2.3.4")),
|
||||
tc("Add another", a("@", "1.2.3.4"), a("www", "1.2.3.4")),
|
||||
tc("Add another(same name)", a("@", "1.2.3.4"), a("www", "1.2.3.4"), a("www", "5.6.7.8")),
|
||||
tc("Change a ttl", a("@", "1.2.3.4").ttl(1000), a("www", "1.2.3.4"), a("www", "5.6.7.8")),
|
||||
tc("Change single target from set", a("@", "1.2.3.4").ttl(1000), a("www", "2.2.2.2"), a("www", "5.6.7.8")),
|
||||
tc("Change all ttls", a("@", "1.2.3.4").ttl(500), a("www", "2.2.2.2").ttl(400), a("www", "5.6.7.8").ttl(400)),
|
||||
tc("Delete one", a("@", "1.2.3.4").ttl(500), a("www", "5.6.7.8").ttl(400)),
|
||||
tc("Add back and change ttl", a("www", "5.6.7.8").ttl(700), a("www", "1.2.3.4").ttl(700)),
|
||||
tc("Change targets and ttls", a("www", "1.1.1.1"), a("www", "2.2.2.2")),
|
||||
tc("Create wildcard", a("*", "1.2.3.4"), a("www", "1.1.1.1")),
|
||||
tc("Delete wildcard", a("www", "1.1.1.1")),
|
||||
type requiresFilter struct {
|
||||
caps []providers.Capability
|
||||
}
|
||||
|
||||
// CNAMES
|
||||
tc("Empty"),
|
||||
tc("Create a CNAME", cname("foo", "google.com.")),
|
||||
tc("Change it", cname("foo", "google2.com.")),
|
||||
tc("Change to A record", a("foo", "1.2.3.4")),
|
||||
tc("Change back to CNAME", cname("foo", "google.com.")),
|
||||
tc("Record pointing to @", cname("foo", "**current-domain**")),
|
||||
func requires(c ...providers.Capability) requiresFilter {
|
||||
return requiresFilter{caps: c}
|
||||
}
|
||||
|
||||
// NS
|
||||
tc("Empty"),
|
||||
tc("NS for subdomain", ns("xyz", "ns2.foo.com.")),
|
||||
tc("Dual NS for subdomain", ns("xyz", "ns2.foo.com."), ns("xyz", "ns1.foo.com.")),
|
||||
tc("NS Record pointing to @", ns("foo", "**current-domain**")),
|
||||
type notFilter struct {
|
||||
names []string
|
||||
}
|
||||
|
||||
// IDNAs
|
||||
tc("Empty"),
|
||||
tc("Internationalized name", a("ööö", "1.2.3.4")),
|
||||
tc("Change IDN", a("ööö", "2.2.2.2")),
|
||||
tc("Internationalized CNAME Target", cname("a", "ööö.com.")),
|
||||
tc("IDN CNAME AND Target", cname("öoö", "ööö.企业.")),
|
||||
func not(n ...string) notFilter {
|
||||
return notFilter{names: n}
|
||||
}
|
||||
|
||||
// MX
|
||||
tc("Empty"),
|
||||
tc("MX record", mx("@", 5, "foo.com.")),
|
||||
tc("Second MX record, same prio", mx("@", 5, "foo.com."), mx("@", 5, "foo2.com.")),
|
||||
tc("3 MX", mx("@", 5, "foo.com."), mx("@", 5, "foo2.com."), mx("@", 15, "foo3.com.")),
|
||||
tc("Delete one", mx("@", 5, "foo2.com."), mx("@", 15, "foo3.com.")),
|
||||
tc("Change to other name", mx("@", 5, "foo2.com."), mx("mail", 15, "foo3.com.")),
|
||||
tc("Change Preference", mx("@", 7, "foo2.com."), mx("mail", 15, "foo3.com.")),
|
||||
tc("Record pointing to @", mx("foo", 8, "**current-domain**")),
|
||||
}
|
||||
type onlyFilter struct {
|
||||
names []string
|
||||
}
|
||||
|
||||
// PTR
|
||||
if !providers.ProviderHasCapability(*providerToRun, providers.CanUsePTR) {
|
||||
t.Log("Skipping PTR Tests because provider does not support them")
|
||||
} else {
|
||||
tests = append(tests, tc("Empty"),
|
||||
tc("Create PTR record", ptr("4", "foo.com.")),
|
||||
tc("Modify PTR record", ptr("4", "bar.com.")),
|
||||
)
|
||||
}
|
||||
func only(n ...string) onlyFilter {
|
||||
return onlyFilter{names: n}
|
||||
}
|
||||
|
||||
// ALIAS
|
||||
if !providers.ProviderHasCapability(*providerToRun, providers.CanUseAlias) {
|
||||
t.Log("Skipping ALIAS Tests because provider does not support them")
|
||||
} else {
|
||||
tests = append(tests, tc("Empty"),
|
||||
tc("ALIAS at root", alias("@", "foo.com.")),
|
||||
tc("change it", alias("@", "foo2.com.")),
|
||||
tc("ALIAS at subdomain", alias("test", "foo.com.")),
|
||||
)
|
||||
}
|
||||
//
|
||||
|
||||
// NAPTR
|
||||
if !providers.ProviderHasCapability(*providerToRun, providers.CanUseNAPTR) {
|
||||
t.Log("Skipping NAPTR Tests because provider does not support them")
|
||||
} else {
|
||||
tests = append(tests, tc("Empty"),
|
||||
func makeTests(t *testing.T) []*TestGroup {
|
||||
|
||||
sha256hash := strings.Repeat("0123456789abcdef", 4)
|
||||
sha512hash := strings.Repeat("0123456789abcdef", 8)
|
||||
reversedSha512 := strings.Repeat("fedcba9876543210", 8)
|
||||
|
||||
// Each group of tests begins with testgroup("Title").
|
||||
// The system will remove any records so that the tests
|
||||
// begin with a clean slate (i.e. no records).
|
||||
|
||||
// Filters:
|
||||
|
||||
// Only apply to providers that CanUseAlias.
|
||||
// requires(providers.CanUseAlias),
|
||||
// Only apply to ROUTE53 + GANDI_V5:
|
||||
// only("ROUTE53", "GANDI_V5")
|
||||
// Only apply to all providers except ROUTE53 + GANDI_V5:
|
||||
// not("ROUTE53", "GANDI_V5"),
|
||||
// NOTE: You can't mix not() and only()
|
||||
// reset(not("ROUTE53"), only("GCLOUD")), // ERROR!
|
||||
// NOTE: All requires()/not()/only() must appear before any tc().
|
||||
|
||||
// tc()
|
||||
// Each tc() indicates a set of records. The testgroup tries to
|
||||
// migrate from one tc() to the next. For example the first tc()
|
||||
// creates some records. The next tc() might list the same records
|
||||
// but adds 1 new record and omits 1. Therefore migrating to this
|
||||
// second tc() results in 1 record being created and 1 deleted; but
|
||||
// for some providers it may be converting 1 record to another.
|
||||
// Therefore some testgroups are testing the providers ability to
|
||||
// transition between different states. Others are just testing
|
||||
// whether or not a certain kind of record can be created and
|
||||
// deleted.
|
||||
|
||||
// clear() is the same as tc("Empty"). It removes all records. You
|
||||
// can use this to verify a provider can delete all the records in
|
||||
// the last tc(), or to provide a clean slate for the next tc().
|
||||
// Each testgroup() begins and ends with clear(), so you don't have
|
||||
// to list the clear() yourself.
|
||||
|
||||
tests := []*TestGroup{
|
||||
|
||||
//
|
||||
// Basic functionality (add/rename/change/delete).
|
||||
//
|
||||
|
||||
testgroup("A",
|
||||
// These tests aren't specific to "A" records. We're testing
|
||||
// general ability to add/rename/change/delete any record.
|
||||
tc("Create an A record", a("@", "1.1.1.1")),
|
||||
tc("Change it", a("@", "1.2.3.4")),
|
||||
tc("Add another", a("@", "1.2.3.4"), a("www", "1.2.3.4")),
|
||||
tc("Add another(same name)", a("@", "1.2.3.4"), a("www", "1.2.3.4"), a("www", "5.6.7.8")),
|
||||
tc("Change a ttl", a("@", "1.2.3.4").ttl(1000), a("www", "1.2.3.4"), a("www", "5.6.7.8")),
|
||||
tc("Change single target from set", a("@", "1.2.3.4").ttl(1000), a("www", "2.2.2.2"), a("www", "5.6.7.8")),
|
||||
tc("Change all ttls", a("@", "1.2.3.4").ttl(500), a("www", "2.2.2.2").ttl(400), a("www", "5.6.7.8").ttl(400)),
|
||||
tc("Delete one", a("@", "1.2.3.4").ttl(500), a("www", "5.6.7.8").ttl(400)),
|
||||
tc("Add back and change ttl", a("www", "5.6.7.8").ttl(700), a("www", "1.2.3.4").ttl(700)),
|
||||
tc("Change targets and ttls", a("www", "1.1.1.1"), a("www", "2.2.2.2")),
|
||||
tc("Create wildcard", a("*", "1.2.3.4"), a("www", "1.1.1.1")),
|
||||
tc("Delete wildcard", a("www", "1.1.1.1")),
|
||||
),
|
||||
|
||||
testgroup("CNAME",
|
||||
tc("Create a CNAME", cname("foo", "google.com.")),
|
||||
tc("Change CNAME target", cname("foo", "google2.com.")),
|
||||
tc("Change to A record", a("foo", "1.2.3.4")),
|
||||
tc("Change back to CNAME", cname("foo", "google.com.")),
|
||||
clear(),
|
||||
tc("Record pointing to @", cname("foo", "**current-domain**")),
|
||||
),
|
||||
|
||||
testgroup("MX",
|
||||
not("ACTIVEDIRECTORY_PS"), // Not implemented.
|
||||
tc("MX record", mx("@", 5, "foo.com.")),
|
||||
tc("Second MX record, same prio", mx("@", 5, "foo.com."), mx("@", 5, "foo2.com.")),
|
||||
tc("3 MX", mx("@", 5, "foo.com."), mx("@", 5, "foo2.com."), mx("@", 15, "foo3.com.")),
|
||||
tc("Delete one", mx("@", 5, "foo2.com."), mx("@", 15, "foo3.com.")),
|
||||
tc("Change to other name", mx("@", 5, "foo2.com."), mx("mail", 15, "foo3.com.")),
|
||||
tc("Change Preference", mx("@", 7, "foo2.com."), mx("mail", 15, "foo3.com.")),
|
||||
tc("Record pointing to @", mx("foo", 8, "**current-domain**")),
|
||||
),
|
||||
|
||||
testgroup("NS",
|
||||
not("DNSIMPLE", "EXOSCALE"),
|
||||
// DNSIMPLE: Does not support NS records nor subdomains.
|
||||
// EXOSCALE: FILL IN
|
||||
tc("NS for subdomain", ns("xyz", "ns2.foo.com.")),
|
||||
tc("Dual NS for subdomain", ns("xyz", "ns2.foo.com."), ns("xyz", "ns1.foo.com.")),
|
||||
tc("NS Record pointing to @", ns("foo", "**current-domain**")),
|
||||
),
|
||||
|
||||
testgroup("IGNORE function",
|
||||
tc("Create some records", txt("foo", "simple"), a("foo", "1.2.3.4")),
|
||||
tc("Add a new record - ignoring foo", a("bar", "1.2.3.4"), ignore("foo")),
|
||||
clear(),
|
||||
tc("Create some records", txt("bar.foo", "simple"), a("bar.foo", "1.2.3.4")),
|
||||
tc("Add a new record - ignoring *.foo", a("bar", "1.2.3.4"), ignore("*.foo")),
|
||||
),
|
||||
|
||||
testgroup("single TXT",
|
||||
tc("Create a TXT", txt("foo", "simple")),
|
||||
tc("Change a TXT", txt("foo", "changed")),
|
||||
clear(),
|
||||
tc("Create a TXT with spaces", txt("foo", "with spaces")),
|
||||
tc("Change a TXT with spaces", txt("foo", "with whitespace ")),
|
||||
tc("Create 1 TXT as array", txtmulti("foo", []string{"simple"})),
|
||||
clear(),
|
||||
tc("Create a 255-byte TXT", txt("foo", strings.Repeat("A", 255))),
|
||||
),
|
||||
|
||||
testgroup("empty TXT", not("DNSIMPLE", "CLOUDFLAREAPI"),
|
||||
tc("TXT with empty str", txt("foo1", "")),
|
||||
// https://github.com/StackExchange/dnscontrol/issues/598
|
||||
// We decided that permitting the TXT target to be an empty
|
||||
// string is not a requirement (even though RFC1035 permits it).
|
||||
// In the future we might make it "capability" to
|
||||
// indicate which vendors support an empty TXT record.
|
||||
// However at this time there is no pressing need for this
|
||||
// feature.
|
||||
),
|
||||
|
||||
//
|
||||
// Tests that exercise the API protocol and/or code
|
||||
//
|
||||
|
||||
testgroup("Case Sensitivity",
|
||||
// The decoys are required so that there is at least one actual change in each tc.
|
||||
tc("Create CAPS", mx("BAR", 5, "BAR.com.")),
|
||||
tc("Downcase label", mx("bar", 5, "BAR.com."), a("decoy", "1.1.1.1")),
|
||||
tc("Downcase target", mx("bar", 5, "bar.com."), a("decoy", "2.2.2.2")),
|
||||
tc("Upcase both", mx("BAR", 5, "BAR.COM."), a("decoy", "3.3.3.3")),
|
||||
),
|
||||
|
||||
testgroup("IDNA",
|
||||
not("SOFTLAYER"),
|
||||
// SOFTLAYER: fails at direct internationalization, punycode works, of course.
|
||||
tc("Internationalized name", a("ööö", "1.2.3.4")),
|
||||
tc("Change IDN", a("ööö", "2.2.2.2")),
|
||||
tc("Internationalized CNAME Target", cname("a", "ööö.com.")),
|
||||
),
|
||||
testgroup("IDNAs in CNAME targets",
|
||||
not("LINODE"),
|
||||
// LINODE: hostname validation does not allow the target domain TLD
|
||||
tc("IDN CNAME AND Target", cname("öoö", "ööö.企业.")),
|
||||
),
|
||||
|
||||
testgroup("page size",
|
||||
// Tests the paging code of providers. Many providers page at 100.
|
||||
// Notes:
|
||||
// - gandi: page size is 100, therefore we test with 99, 100, and 101
|
||||
// - ns1: free acct only allows 50 records, therefore we skip
|
||||
// - digitalocean: fails due to rate limiting, not page limits.
|
||||
not("NS1"),
|
||||
tc("99 records", manyA("rec%04d", "1.2.3.4", 99)...),
|
||||
tc("100 records", manyA("rec%04d", "1.2.3.4", 100)...),
|
||||
tc("101 records", manyA("rec%04d", "1.2.3.4", 101)...),
|
||||
),
|
||||
|
||||
testgroup("Large updates",
|
||||
// Verify https://github.com/StackExchange/dnscontrol/issues/493
|
||||
only("ROUTE53"),
|
||||
tc("600 records", manyA("rec%04d", "1.2.3.4", 600)...),
|
||||
tc("Update 600 records", manyA("rec%04d", "1.2.3.5", 600)...),
|
||||
tc("Empty"), // Delete them all
|
||||
tc("1200 records", manyA("rec%04d", "1.2.3.4", 1200)...),
|
||||
tc("Update 1200 records", manyA("rec%04d", "1.2.3.5", 1200)...),
|
||||
),
|
||||
|
||||
//
|
||||
// CanUse* types:
|
||||
//
|
||||
|
||||
testgroup("CAA",
|
||||
requires(providers.CanUseCAA),
|
||||
tc("CAA record", caa("@", "issue", 0, "letsencrypt.org")),
|
||||
tc("CAA change tag", caa("@", "issuewild", 0, "letsencrypt.org")),
|
||||
tc("CAA change target", caa("@", "issuewild", 0, "example.com")),
|
||||
tc("CAA change flag", caa("@", "issuewild", 128, "example.com")),
|
||||
tc("CAA many records",
|
||||
caa("@", "issue", 0, "letsencrypt.org"),
|
||||
caa("@", "issuewild", 0, "comodoca.com"),
|
||||
caa("@", "iodef", 128, "mailto:test@example.com")),
|
||||
tc("CAA delete", caa("@", "issue", 0, "letsencrypt.org")),
|
||||
),
|
||||
testgroup("CAA with ;",
|
||||
requires(providers.CanUseCAA), not("DIGITALOCEAN"),
|
||||
// Test support of ";" as a value
|
||||
tc("CAA many records", caa("@", "issuewild", 0, ";")),
|
||||
),
|
||||
|
||||
testgroup("NAPTR",
|
||||
requires(providers.CanUseNAPTR),
|
||||
tc("NAPTR record", naptr("test", 100, 10, "U", "E2U+sip", "!^.*$!sip:customer-service@example.com!", "example.foo.com.")),
|
||||
tc("NAPTR second record", naptr("test", 102, 10, "U", "E2U+email", "!^.*$!mailto:information@example.com!", "example.foo.com.")),
|
||||
tc("NAPTR delete record", naptr("test", 100, 10, "U", "E2U+email", "!^.*$!mailto:information@example.com!", "example.foo.com.")),
|
||||
|
@ -459,14 +728,14 @@ func makeTests(t *testing.T) []*TestCase {
|
|||
tc("NAPTR change flags", naptr("test", 103, 20, "A", "E2U+email", "!^.*$!mailto:information@example.com!", "example2.foo.com.")),
|
||||
tc("NAPTR change service", naptr("test", 103, 20, "A", "E2U+sip", "!^.*$!mailto:information@example.com!", "example2.foo.com.")),
|
||||
tc("NAPTR change regexp", naptr("test", 103, 20, "A", "E2U+sip", "!^.*$!sip:customer-service@example.com!", "example2.foo.com.")),
|
||||
)
|
||||
}
|
||||
),
|
||||
|
||||
// SRV
|
||||
if !providers.ProviderHasCapability(*providerToRun, providers.CanUseSRV) {
|
||||
t.Log("Skipping SRV Tests because provider does not support them")
|
||||
} else {
|
||||
tests = append(tests, tc("Empty"),
|
||||
testgroup("PTR", requires(providers.CanUsePTR), not("ACTIVEDIRECTORY_PS"),
|
||||
tc("Create PTR record", ptr("4", "foo.com.")),
|
||||
tc("Modify PTR record", ptr("4", "bar.com.")),
|
||||
),
|
||||
|
||||
testgroup("SRV", requires(providers.CanUseSRV), not("ACTIVEDIRECTORY_PS", "CLOUDNS"),
|
||||
tc("SRV record", srv("_sip._tcp", 5, 6, 7, "foo.com.")),
|
||||
tc("Second SRV record, same prio", srv("_sip._tcp", 5, 6, 7, "foo.com."), srv("_sip._tcp", 5, 60, 70, "foo2.com.")),
|
||||
tc("3 SRV", srv("_sip._tcp", 5, 6, 7, "foo.com."), srv("_sip._tcp", 5, 60, 70, "foo2.com."), srv("_sip._tcp", 15, 65, 75, "foo3.com.")),
|
||||
|
@ -475,19 +744,13 @@ func makeTests(t *testing.T) []*TestCase {
|
|||
tc("Change Priority", srv("_sip._tcp", 52, 6, 7, "foo.com."), srv("_sip._tcp", 15, 65, 75, "foo4.com.")),
|
||||
tc("Change Weight", srv("_sip._tcp", 52, 62, 7, "foo.com."), srv("_sip._tcp", 15, 65, 75, "foo4.com.")),
|
||||
tc("Change Port", srv("_sip._tcp", 52, 62, 72, "foo.com."), srv("_sip._tcp", 15, 65, 75, "foo4.com.")),
|
||||
)
|
||||
if *providerToRun == "NAMEDOTCOM" || *providerToRun == "HEXONET" || *providerToRun == "EXOSCALE" {
|
||||
t.Log("Skipping SRV Null Target test because provider does not support them")
|
||||
} else {
|
||||
tests = append(tests, tc("Null Target", srv("_sip._tcp", 52, 62, 72, "foo.com."), srv("_sip._tcp", 15, 65, 75, ".")))
|
||||
}
|
||||
}
|
||||
),
|
||||
testgroup("SRV w/ null target", not("NAMEDOTCOM", "HEXONET", "EXOSCALE"),
|
||||
tc("Null Target", srv("_sip._tcp", 52, 62, 72, "foo.com."), srv("_sip._tcp", 15, 65, 75, ".")),
|
||||
),
|
||||
|
||||
// SSHFP
|
||||
if !providers.ProviderHasCapability(*providerToRun, providers.CanUseSSHFP) {
|
||||
t.Log("Skipping SSHFP Tests because provider does not support them")
|
||||
} else {
|
||||
tests = append(tests, tc("Empty"),
|
||||
testgroup("SSHFP",
|
||||
requires(providers.CanUseSSHFP),
|
||||
tc("SSHFP record",
|
||||
sshfp("@", 1, 1, "66c7d5540b7d75a1fb4c84febfa178ad99bdd67c")),
|
||||
tc("SSHFP change algorithm",
|
||||
|
@ -501,105 +764,19 @@ func makeTests(t *testing.T) []*TestCase {
|
|||
sshfp("@", 2, 1, "8888888888888888fb4c84febfa178ad99bdd67c")),
|
||||
tc("SSHFP delete two",
|
||||
sshfp("@", 1, 1, "66666666666d75a1fb4c84febfa178ad99bdd67c")),
|
||||
)
|
||||
}
|
||||
),
|
||||
|
||||
// CAA
|
||||
if !providers.ProviderHasCapability(*providerToRun, providers.CanUseCAA) {
|
||||
t.Log("Skipping CAA Tests because provider does not support them")
|
||||
} else {
|
||||
manyRecordsTc := tc("CAA many records", caa("@", "issue", 0, "letsencrypt.org"), caa("@", "issuewild", 0, ";"), caa("@", "iodef", 128, "mailto:test@example.com"))
|
||||
|
||||
// Digitalocean doesn't support ";" as value for CAA records
|
||||
if *providerToRun == "DIGITALOCEAN" {
|
||||
manyRecordsTc = tc("CAA many records", caa("@", "issue", 0, "letsencrypt.org"), caa("@", "issuewild", 0, "comodoca.com"), caa("@", "iodef", 128, "mailto:test@example.com"))
|
||||
}
|
||||
|
||||
tests = append(tests, tc("Empty"),
|
||||
tc("CAA record", caa("@", "issue", 0, "letsencrypt.org")),
|
||||
tc("CAA change tag", caa("@", "issuewild", 0, "letsencrypt.org")),
|
||||
tc("CAA change target", caa("@", "issuewild", 0, "example.com")),
|
||||
tc("CAA change flag", caa("@", "issuewild", 128, "example.com")),
|
||||
manyRecordsTc,
|
||||
tc("CAA delete", caa("@", "issue", 0, "letsencrypt.org")),
|
||||
)
|
||||
}
|
||||
|
||||
// TLSA
|
||||
if !providers.ProviderHasCapability(*providerToRun, providers.CanUseTLSA) {
|
||||
t.Log("Skipping TLSA Tests because provider does not support them")
|
||||
} else {
|
||||
sha256hash := strings.Repeat("0123456789abcdef", 4)
|
||||
sha512hash := strings.Repeat("0123456789abcdef", 8)
|
||||
reversedSha512 := strings.Repeat("fedcba9876543210", 8)
|
||||
tests = append(tests, tc("Empty"),
|
||||
testgroup("TLSA",
|
||||
requires(providers.CanUseTLSA),
|
||||
tc("TLSA record", tlsa("_443._tcp", 3, 1, 1, sha256hash)),
|
||||
tc("TLSA change usage", tlsa("_443._tcp", 2, 1, 1, sha256hash)),
|
||||
tc("TLSA change selector", tlsa("_443._tcp", 2, 0, 1, sha256hash)),
|
||||
tc("TLSA change matchingtype", tlsa("_443._tcp", 2, 0, 2, sha512hash)),
|
||||
tc("TLSA change certificate", tlsa("_443._tcp", 2, 0, 2, reversedSha512)),
|
||||
)
|
||||
}
|
||||
),
|
||||
|
||||
// Case
|
||||
tests = append(tests, tc("Empty"),
|
||||
tc("Create CAPS", mx("BAR", 5, "BAR.com.")),
|
||||
tc("Downcase label", mx("bar", 5, "BAR.com."), a("decoy", "1.1.1.1")),
|
||||
tc("Downcase target", mx("bar", 5, "bar.com."), a("decoy", "2.2.2.2")),
|
||||
tc("Upcase both", mx("BAR", 5, "BAR.COM."), a("decoy", "3.3.3.3")),
|
||||
// The decoys are required so that there is at least one actual change in each tc.
|
||||
)
|
||||
|
||||
// Test large zonefiles.
|
||||
// Mostly to test paging. Many providers page at 100
|
||||
// Known page sizes:
|
||||
// - gandi: 100
|
||||
skip := map[string]bool{
|
||||
"NS1": true, // ns1 free acct only allows 50 records
|
||||
}
|
||||
if skip[*providerToRun] {
|
||||
t.Log("Skipping Large record count Tests because provider does not support them")
|
||||
} else {
|
||||
tests = append(tests, tc("Empty"),
|
||||
tc("99 records", manyA("rec%04d", "1.2.3.4", 99)...),
|
||||
tc("100 records", manyA("rec%04d", "1.2.3.4", 100)...),
|
||||
tc("101 records", manyA("rec%04d", "1.2.3.4", 101)...),
|
||||
)
|
||||
}
|
||||
|
||||
// NB(tlim): To temporarily skip most of the tests, insert a line like this:
|
||||
//tests = nil
|
||||
|
||||
// TXT (single)
|
||||
tests = append(tests, tc("Empty"),
|
||||
tc("Create a TXT", txt("foo", "simple")),
|
||||
tc("Change a TXT", txt("foo", "changed")),
|
||||
tc("Empty"),
|
||||
tc("Create a TXT with spaces", txt("foo", "with spaces")),
|
||||
tc("Change a TXT with spaces", txt("foo", "with whitespace")),
|
||||
tc("Create 1 TXT as array", txtmulti("foo", []string{"simple"})),
|
||||
tc("Empty"),
|
||||
tc("Create a 255-byte TXT", txt("foo", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")),
|
||||
)
|
||||
|
||||
// FUTURE(tal): https://github.com/StackExchange/dnscontrol/issues/598
|
||||
// We decided that handling an empty TXT string is not a
|
||||
// requirement. In the future we might make it a "capability" to
|
||||
// indicate which vendors fully support RFC 1035, which requires
|
||||
// that a TXT string can be empty.
|
||||
//
|
||||
// // TXT (empty)
|
||||
// if (provider supports empty txt strings) {
|
||||
// tests = append(tests, tc("Empty"),
|
||||
// tc("TXT with empty str", txt("foo1", "")),
|
||||
// )
|
||||
// }
|
||||
|
||||
// TXTMulti
|
||||
if !providers.ProviderHasCapability(*providerToRun, providers.CanUseTXTMulti) {
|
||||
t.Log("Skipping TXTMulti Tests because provider does not support them")
|
||||
} else {
|
||||
tests = append(tests, tc("Empty"),
|
||||
testgroup("TXTMulti",
|
||||
requires(providers.CanUseTXTMulti),
|
||||
tc("Create TXTMulti 1",
|
||||
txtmulti("foo1", []string{"simple"}),
|
||||
),
|
||||
|
@ -622,61 +799,38 @@ func makeTests(t *testing.T) []*TestCase {
|
|||
txtmulti("foo2", []string{"fun", "two"}),
|
||||
txtmulti("foo3", []string{"eh", "bzz", "cee"}),
|
||||
),
|
||||
tc("Empty"),
|
||||
tc("3x255-byte TXTMulti",
|
||||
txtmulti("foo3", []string{"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY", "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"}),
|
||||
),
|
||||
)
|
||||
}
|
||||
txtmulti("foo3", []string{strings.Repeat("X", 255), strings.Repeat("Y", 255), strings.Repeat("Z", 255)})),
|
||||
),
|
||||
|
||||
// ignored records
|
||||
tests = append(tests, tc("Empty"),
|
||||
tc("Create some records", txt("foo", "simple"), a("foo", "1.2.3.4")),
|
||||
tc("Add a new record - ignoring foo", a("bar", "1.2.3.4"), ignore("foo")),
|
||||
)
|
||||
//
|
||||
// Pseudo rtypes:
|
||||
//
|
||||
|
||||
tests = append(tests, tc("Empty"),
|
||||
tc("Create some records", txt("bar.foo", "simple"), a("bar.foo", "1.2.3.4")),
|
||||
tc("Add a new record - ignoring *.foo", a("bar", "1.2.3.4"), ignore("*.foo")),
|
||||
)
|
||||
testgroup("ALIAS",
|
||||
requires(providers.CanUseAlias),
|
||||
tc("ALIAS at root", alias("@", "foo.com.")),
|
||||
tc("change it", alias("@", "foo2.com.")),
|
||||
tc("ALIAS at subdomain", alias("test", "foo.com.")),
|
||||
),
|
||||
|
||||
// R53_ALIAS
|
||||
if !providers.ProviderHasCapability(*providerToRun, providers.CanUseRoute53Alias) {
|
||||
t.Log("Skipping Route53 ALIAS Tests because provider does not support them")
|
||||
} else {
|
||||
tests = append(tests, tc("Empty"),
|
||||
tc("create dependent records", a("foo", "1.2.3.4"), a("quux", "2.3.4.5")),
|
||||
tc("ALIAS to A record in same zone", a("foo", "1.2.3.4"), a("quux", "2.3.4.5"), r53alias("bar", "A", "foo.**current-domain**")),
|
||||
tc("change it", a("foo", "1.2.3.4"), a("quux", "2.3.4.5"), r53alias("bar", "A", "quux.**current-domain**")),
|
||||
)
|
||||
}
|
||||
|
||||
// test r53 for very very large batch sizes
|
||||
if *providerToRun == "ROUTE53" {
|
||||
tests = append(tests, tc("Empty"),
|
||||
tc("600 records", manyA("rec%04d", "1.2.3.4", 600)...),
|
||||
tc("Update 600 records", manyA("rec%04d", "1.2.3.5", 600)...),
|
||||
tc("Empty"),
|
||||
tc("1200 records", manyA("rec%04d", "1.2.3.4", 1200)...),
|
||||
tc("Update 1200 records", manyA("rec%04d", "1.2.3.5", 1200)...),
|
||||
)
|
||||
}
|
||||
// AZURE_ALIAS
|
||||
if !providers.ProviderHasCapability(*providerToRun, providers.CanUseAzureAlias) {
|
||||
t.Log("Skipping AZURE_ALIAS Tests because provider does not support them")
|
||||
} else {
|
||||
t.Log("SubscriptionID: ")
|
||||
tests = append(tests, tc("Empty"),
|
||||
testgroup("AZURE_ALIAS",
|
||||
requires(providers.CanUseAzureAlias),
|
||||
tc("create dependent A records", a("foo.a", "1.2.3.4"), a("quux.a", "2.3.4.5")),
|
||||
tc("ALIAS to A record in same zone", a("foo.a", "1.2.3.4"), a("quux.a", "2.3.4.5"), azureAlias("bar.a", "A", "/subscriptions/**subscription-id**/resourceGroups/**resource-group**/providers/Microsoft.Network/dnszones/**current-domain-no-trailing**/A/foo.a")),
|
||||
tc("change it", a("foo.a", "1.2.3.4"), a("quux.a", "2.3.4.5"), azureAlias("bar.a", "A", "/subscriptions/**subscription-id**/resourceGroups/**resource-group**/providers/Microsoft.Network/dnszones/**current-domain-no-trailing**/A/quux.a")),
|
||||
tc("create dependent CNAME records", cname("foo.cname", "google.com"), cname("quux.cname", "google2.com")),
|
||||
tc("ALIAS to CNAME record in same zone", cname("foo.cname", "google.com"), cname("quux.cname", "google2.com"), azureAlias("bar", "CNAME", "/subscriptions/**subscription-id**/resourceGroups/**resource-group**/providers/Microsoft.Network/dnszones/**current-domain-no-trailing**/CNAME/foo.cname")),
|
||||
tc("change it", cname("foo.cname", "google.com"), cname("quux.cname", "google2.com"), azureAlias("bar.cname", "CNAME", "/subscriptions/**subscription-id**/resourceGroups/**resource-group**/providers/Microsoft.Network/dnszones/**current-domain-no-trailing**/CNAME/quux.cname")),
|
||||
)
|
||||
),
|
||||
|
||||
testgroup("R53_ALIAS",
|
||||
requires(providers.CanUseRoute53Alias),
|
||||
tc("create dependent records", a("foo", "1.2.3.4"), a("quux", "2.3.4.5")),
|
||||
tc("ALIAS to A record in same zone", a("foo", "1.2.3.4"), a("quux", "2.3.4.5"), r53alias("bar", "A", "foo.**current-domain**")),
|
||||
tc("change it", a("foo", "1.2.3.4"), a("quux", "2.3.4.5"), r53alias("bar", "A", "quux.**current-domain**")),
|
||||
),
|
||||
}
|
||||
|
||||
// Empty last
|
||||
tests = append(tests, tc("Empty"))
|
||||
return tests
|
||||
}
|
||||
|
|
|
@ -1,94 +1,87 @@
|
|||
{
|
||||
"ACTIVEDIRECTORY_PS": {
|
||||
"ADServer": "$AD_SERVER",
|
||||
"domain": "$AD_DOMAIN",
|
||||
"knownFailures": "29,30,31,32,33,34,35,38,39,40,41,48,50,51,52"
|
||||
"domain": "$AD_DOMAIN"
|
||||
},
|
||||
"AZURE_DNS": {
|
||||
"SubscriptionID": "$AZURE_SUBSCRIPTION_ID",
|
||||
"ResourceGroup": "$AZURE_RESOURCE_GROUP",
|
||||
"TenantID": "$AZURE_TENANT_ID",
|
||||
"ClientID": "$AZURE_CLIENT_ID",
|
||||
"ClientSecret": "$AZURE_CLIENT_SECRET",
|
||||
"ResourceGroup": "$AZURE_RESOURCE_GROUP",
|
||||
"SubscriptionID": "$AZURE_SUBSCRIPTION_ID",
|
||||
"TenantID": "$AZURE_TENANT_ID",
|
||||
"domain": "$AZURE_DOMAIN"
|
||||
},
|
||||
"BIND": {
|
||||
"domain": "example.com"
|
||||
},
|
||||
"CLOUDNS": {
|
||||
"auth-id": "$CLOUDNS_AUTH_ID",
|
||||
"auth-password": "$CLOUDNS_AUTH_PASSWORD",
|
||||
"domain": "$CLOUDNS_DOMAIN",
|
||||
"knownFailures": "52"
|
||||
"CLOUDFLAREAPI": {
|
||||
"apitoken": "$CF_TOKEN",
|
||||
"domain": "$CF_DOMAIN"
|
||||
},
|
||||
|
||||
"CLOUDFLAREAPI_OLD": {
|
||||
"apikey": "$CF_KEY",
|
||||
"apiuser": "$CF_USER",
|
||||
"domain": "$CF_DOMAIN"
|
||||
},
|
||||
"CLOUDFLAREAPI": {
|
||||
"apitoken": "$CF_TOKEN",
|
||||
"domain": "$CF_DOMAIN"
|
||||
"CLOUDNS": {
|
||||
"auth-id": "$CLOUDNS_AUTH_ID",
|
||||
"auth-password": "$CLOUDNS_AUTH_PASSWORD",
|
||||
"domain": "$CLOUDNS_DOMAIN"
|
||||
},
|
||||
"DIGITALOCEAN": {
|
||||
"token": "$DO_TOKEN",
|
||||
"domain": "$DO_DOMAIN"
|
||||
"domain": "$DO_DOMAIN",
|
||||
"token": "$DO_TOKEN"
|
||||
},
|
||||
"DNSIMPLE": {
|
||||
"COMMENT": "20-22: no ns records managable. Not even for subdomains.",
|
||||
"baseurl": "https://api.sandbox.dnsimple.com",
|
||||
"domain": "$DNSIMPLE_DOMAIN",
|
||||
"knownFailures": "20,21,22",
|
||||
"token": "$DNSIMPLE_TOKEN"
|
||||
},
|
||||
"EXOSCALE": {
|
||||
"dns-endpoint": "https://api.exoscale.ch/dns",
|
||||
"apikey": "$EXOSCALE_API_KEY",
|
||||
"secretkey": "$EXOSCALE_SECRET_KEY",
|
||||
"dns-endpoint": "https://api.exoscale.ch/dns",
|
||||
"domain": "$EXOSCALE_DOMAIN",
|
||||
"knownFailures": "20,21,22"
|
||||
"secretkey": "$EXOSCALE_SECRET_KEY"
|
||||
},
|
||||
"GANDI": {
|
||||
"COMMENT": "5: gandi does not accept TTLs less than 300",
|
||||
"apikey": "$GANDI_KEY",
|
||||
"domain": "$GANDI_DOMAIN",
|
||||
"knownFailures": "5"
|
||||
"domain": "$GANDI_DOMAIN"
|
||||
},
|
||||
"GANDI-LIVEDNS": {
|
||||
"COMMENT": "5: gandi does not accept TTLs less than 300",
|
||||
"apikey": "$GANDILIVE_KEY",
|
||||
"domain": "$GANDILIVE_DOMAIN",
|
||||
"knownFailures": "5"
|
||||
"domain": "$GANDILIVE_DOMAIN"
|
||||
},
|
||||
"GANDI_V5": {
|
||||
"apikey": "$GANDI_V5_APIKEY",
|
||||
"domain": "$GANDI_V5_DOMAIN"
|
||||
},
|
||||
"GCLOUD": {
|
||||
"type": "$GCLOUD_TYPE",
|
||||
"client_email": "$GCLOUD_EMAIL",
|
||||
"domain": "$GCLOUD_DOMAIN",
|
||||
"private_key": "$GCLOUD_PRIVATEKEY",
|
||||
"project_id": "$GCLOUD_PROJECT"
|
||||
"project_id": "$GCLOUD_PROJECT",
|
||||
"type": "$GCLOUD_TYPE"
|
||||
},
|
||||
"HEXONET": {
|
||||
"apientity": "$HEXONET_ENTITY",
|
||||
"apilogin": "$HEXONET_UID",
|
||||
"apipassword": "$HEXONET_PW",
|
||||
"apientity": "$HEXONET_ENTITY",
|
||||
"debugmode": "$HEXONET_DEBUGMODE",
|
||||
"ipaddress": "$HEXONET_IP",
|
||||
"domain": "dnscontrol.com"
|
||||
"domain": "dnscontrol.com",
|
||||
"ipaddress": "$HEXONET_IP"
|
||||
},
|
||||
"LINODE": {
|
||||
"COMMENT": "25: Linode's hostname validation does not allow the target domain TLD",
|
||||
"token": "$LINODE_TOKEN",
|
||||
"domain": "$LINODE_DOMAIN",
|
||||
"knownFailures": "27"
|
||||
"token": "$LINODE_TOKEN"
|
||||
},
|
||||
"NS1": {
|
||||
"domain": "$NS1_DOMAIN",
|
||||
"api_token": "$NS1_TOKEN"
|
||||
"NAMECHEAP": {
|
||||
"apikey": "$NAMECHEAP_KEY",
|
||||
"apiuser": "$NAMECHEAP_USER",
|
||||
"domain": "$NAMECHEAP_DOMAIN"
|
||||
},
|
||||
"NAMEDOTCOM": {
|
||||
"apikey": "$NAMEDOTCOM_KEY",
|
||||
|
@ -96,14 +89,19 @@
|
|||
"apiuser": "$NAMEDOTCOM_USER",
|
||||
"domain": "$NAMEDOTCOM_DOMAIN"
|
||||
},
|
||||
"NAMECHEAP": {
|
||||
"apikey": "$NAMECHEAP_KEY",
|
||||
"apiuser": "$NAMECHEAP_USER",
|
||||
"domain": "$NAMECHEAP_DOMAIN"
|
||||
"NS1": {
|
||||
"api_token": "$NS1_TOKEN",
|
||||
"domain": "$NS1_DOMAIN"
|
||||
},
|
||||
"OCTODNS": {
|
||||
"domain": "example.com",
|
||||
"directory": "config"
|
||||
"directory": "config",
|
||||
"domain": "example.com"
|
||||
},
|
||||
"OVH": {
|
||||
"app-key": "$OVH_APP_KEY",
|
||||
"app-secret-key": "$OVH_APP_SECRET_KEY",
|
||||
"consumer-key": "$OVH_CONSUMER_KEY",
|
||||
"domain": "$OVH_DOMAIN"
|
||||
},
|
||||
"ROUTE53": {
|
||||
"KeyId": "$R53_KEY_ID",
|
||||
|
@ -112,19 +110,12 @@
|
|||
},
|
||||
"SOFTLAYER": {
|
||||
"COMMENT": "22-25 softlayer fails at direct internationalization, puncode works though",
|
||||
"knownFailures": "22,23,24,25",
|
||||
"api_key": "$SL_API_KEY",
|
||||
"domain": "$SL_DOMAIN",
|
||||
"username": "$SL_USERNAME",
|
||||
"api_key": "$SL_API_KEY"
|
||||
"username": "$SL_USERNAME"
|
||||
},
|
||||
"VULTR": {
|
||||
"token": "$VULTR_TOKEN",
|
||||
"domain": "$VULTR_DOMAIN"
|
||||
},
|
||||
"OVH": {
|
||||
"app-key": "$OVH_APP_KEY",
|
||||
"app-secret-key": "$OVH_APP_SECRET_KEY",
|
||||
"consumer-key": "$OVH_CONSUMER_KEY",
|
||||
"domain": "$OVH_DOMAIN"
|
||||
"domain": "$VULTR_DOMAIN",
|
||||
"token": "$VULTR_TOKEN"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//go:generate stringer -type=Capability
|
||||
|
||||
package providers
|
||||
|
||||
import (
|
||||
|
|
38
providers/capability_string.go
Normal file
38
providers/capability_string.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Code generated by "stringer -type=Capability"; DO NOT EDIT.
|
||||
|
||||
package providers
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[CanUseAlias-0]
|
||||
_ = x[CanUseCAA-1]
|
||||
_ = x[CanUsePTR-2]
|
||||
_ = x[CanUseNAPTR-3]
|
||||
_ = x[CanUseSRV-4]
|
||||
_ = x[CanUseSSHFP-5]
|
||||
_ = x[CanUseTLSA-6]
|
||||
_ = x[CanUseTXTMulti-7]
|
||||
_ = x[CanAutoDNSSEC-8]
|
||||
_ = x[CantUseNOPURGE-9]
|
||||
_ = x[DocOfficiallySupported-10]
|
||||
_ = x[DocDualHost-11]
|
||||
_ = x[DocCreateDomains-12]
|
||||
_ = x[CanUseRoute53Alias-13]
|
||||
_ = x[CanGetZones-14]
|
||||
_ = x[CanUseAzureAlias-15]
|
||||
}
|
||||
|
||||
const _Capability_name = "CanUseAliasCanUseCAACanUsePTRCanUseNAPTRCanUseSRVCanUseSSHFPCanUseTLSACanUseTXTMultiCanAutoDNSSECCantUseNOPURGEDocOfficiallySupportedDocDualHostDocCreateDomainsCanUseRoute53AliasCanGetZonesCanUseAzureAlias"
|
||||
|
||||
var _Capability_index = [...]uint8{0, 11, 20, 29, 40, 49, 60, 70, 84, 97, 111, 133, 144, 160, 178, 189, 205}
|
||||
|
||||
func (i Capability) String() string {
|
||||
if i >= Capability(len(_Capability_index)-1) {
|
||||
return "Capability(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Capability_name[_Capability_index[i]:_Capability_index[i+1]]
|
||||
}
|
|
@ -40,7 +40,7 @@ func init() {
|
|||
|
||||
// features declares which features and options are available.
|
||||
var features = providers.DocumentationNotes{
|
||||
providers.CanUseAlias: providers.Can(),
|
||||
providers.CanUseAlias: providers.Can("Only on the bare domain. Otherwise CNAME will be substituted"),
|
||||
providers.CanUseCAA: providers.Can(),
|
||||
providers.CanUsePTR: providers.Can(),
|
||||
providers.CanUseSRV: providers.Can(),
|
||||
|
@ -155,6 +155,11 @@ func PrepDesiredRecords(dc *models.DomainConfig) {
|
|||
|
||||
recordsToKeep := make([]*models.RecordConfig, 0, len(dc.Records))
|
||||
for _, rec := range dc.Records {
|
||||
if rec.Type == "ALIAS" && rec.Name != "@" {
|
||||
// GANDI only permits aliases on a naked domain.
|
||||
// Therefore, we change this to a CNAME.
|
||||
rec.Type = "CNAME"
|
||||
}
|
||||
if rec.TTL < 300 {
|
||||
printer.Warnf("Gandi does not support ttls < 300. Setting %s from %d to 300\n", rec.GetLabelFQDN(), rec.TTL)
|
||||
rec.TTL = 300
|
||||
|
|
19200
vendor/golang.org/x/net/publicsuffix/table.go
generated
vendored
19200
vendor/golang.org/x/net/publicsuffix/table.go
generated
vendored
File diff suppressed because it is too large
Load diff
5
vendor/golang.org/x/tools/imports/forward.go
generated
vendored
5
vendor/golang.org/x/tools/imports/forward.go
generated
vendored
|
@ -4,6 +4,7 @@ package imports // import "golang.org/x/tools/imports"
|
|||
|
||||
import (
|
||||
"go/build"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
intimp "golang.org/x/tools/internal/imports"
|
||||
|
@ -47,7 +48,6 @@ func Process(filename string, src []byte, opt *Options) ([]byte, error) {
|
|||
GO111MODULE: os.Getenv("GO111MODULE"),
|
||||
GOPROXY: os.Getenv("GOPROXY"),
|
||||
GOSUMDB: os.Getenv("GOSUMDB"),
|
||||
Debug: Debug,
|
||||
LocalPrefix: LocalPrefix,
|
||||
},
|
||||
AllErrors: opt.AllErrors,
|
||||
|
@ -57,6 +57,9 @@ func Process(filename string, src []byte, opt *Options) ([]byte, error) {
|
|||
TabIndent: opt.TabIndent,
|
||||
TabWidth: opt.TabWidth,
|
||||
}
|
||||
if Debug {
|
||||
intopt.Env.Logf = log.Printf
|
||||
}
|
||||
return intimp.Process(filename, src, intopt)
|
||||
}
|
||||
|
||||
|
|
5
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_unix.go
generated
vendored
5
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_unix.go
generated
vendored
|
@ -76,8 +76,9 @@ func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) e
|
|||
}
|
||||
|
||||
func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode) {
|
||||
// golang.org/issue/15653
|
||||
dirent := (*syscall.Dirent)(unsafe.Pointer(&buf[0]))
|
||||
// golang.org/issue/37269
|
||||
dirent := &syscall.Dirent{}
|
||||
copy((*[unsafe.Sizeof(syscall.Dirent{})]byte)(unsafe.Pointer(dirent))[:], buf)
|
||||
if v := unsafe.Offsetof(dirent.Reclen) + unsafe.Sizeof(dirent.Reclen); uintptr(len(buf)) < v {
|
||||
panic(fmt.Sprintf("buf size of %d smaller than dirent header size %d", len(buf), v))
|
||||
}
|
||||
|
|
121
vendor/golang.org/x/tools/internal/gocommand/invoke.go
generated
vendored
Normal file
121
vendor/golang.org/x/tools/internal/gocommand/invoke.go
generated
vendored
Normal file
|
@ -0,0 +1,121 @@
|
|||
// Package gocommand is a helper for calling the go command.
|
||||
package gocommand
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// An Invocation represents a call to the go command.
|
||||
type Invocation struct {
|
||||
Verb string
|
||||
Args []string
|
||||
BuildFlags []string
|
||||
Env []string
|
||||
WorkingDir string
|
||||
Logf func(format string, args ...interface{})
|
||||
}
|
||||
|
||||
// Run runs the invocation, returning its stdout and an error suitable for
|
||||
// human consumption, including stderr.
|
||||
func (i *Invocation) Run(ctx context.Context) (*bytes.Buffer, error) {
|
||||
stdout, _, friendly, _ := i.RunRaw(ctx)
|
||||
return stdout, friendly
|
||||
}
|
||||
|
||||
// RunRaw is like Run, but also returns the raw stderr and error for callers
|
||||
// that want to do low-level error handling/recovery.
|
||||
func (i *Invocation) RunRaw(ctx context.Context) (stdout *bytes.Buffer, stderr *bytes.Buffer, friendlyError error, rawError error) {
|
||||
log := i.Logf
|
||||
if log == nil {
|
||||
log = func(string, ...interface{}) {}
|
||||
}
|
||||
|
||||
goArgs := []string{i.Verb}
|
||||
switch i.Verb {
|
||||
case "mod":
|
||||
// mod needs the sub-verb before build flags.
|
||||
goArgs = append(goArgs, i.Args[0])
|
||||
goArgs = append(goArgs, i.BuildFlags...)
|
||||
goArgs = append(goArgs, i.Args[1:]...)
|
||||
case "env":
|
||||
// env doesn't take build flags.
|
||||
goArgs = append(goArgs, i.Args...)
|
||||
default:
|
||||
goArgs = append(goArgs, i.BuildFlags...)
|
||||
goArgs = append(goArgs, i.Args...)
|
||||
}
|
||||
cmd := exec.Command("go", goArgs...)
|
||||
stdout = &bytes.Buffer{}
|
||||
stderr = &bytes.Buffer{}
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
// On darwin the cwd gets resolved to the real path, which breaks anything that
|
||||
// expects the working directory to keep the original path, including the
|
||||
// go command when dealing with modules.
|
||||
// The Go stdlib has a special feature where if the cwd and the PWD are the
|
||||
// same node then it trusts the PWD, so by setting it in the env for the child
|
||||
// process we fix up all the paths returned by the go command.
|
||||
cmd.Env = append(append([]string{}, i.Env...), "PWD="+i.WorkingDir)
|
||||
cmd.Dir = i.WorkingDir
|
||||
|
||||
defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now())
|
||||
|
||||
rawError = runCmdContext(ctx, cmd)
|
||||
friendlyError = rawError
|
||||
if rawError != nil {
|
||||
// Check for 'go' executable not being found.
|
||||
if ee, ok := rawError.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
|
||||
friendlyError = fmt.Errorf("go command required, not found: %v", ee)
|
||||
}
|
||||
if ctx.Err() != nil {
|
||||
friendlyError = ctx.Err()
|
||||
}
|
||||
friendlyError = fmt.Errorf("err: %v: stderr: %s", rawError, stderr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// runCmdContext is like exec.CommandContext except it sends os.Interrupt
|
||||
// before os.Kill.
|
||||
func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
resChan := make(chan error, 1)
|
||||
go func() {
|
||||
resChan <- cmd.Wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-resChan:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
}
|
||||
// Cancelled. Interrupt and see if it ends voluntarily.
|
||||
cmd.Process.Signal(os.Interrupt)
|
||||
select {
|
||||
case err := <-resChan:
|
||||
return err
|
||||
case <-time.After(time.Second):
|
||||
}
|
||||
// Didn't shut down in response to interrupt. Kill it hard.
|
||||
cmd.Process.Kill()
|
||||
return <-resChan
|
||||
}
|
||||
|
||||
func cmdDebugStr(cmd *exec.Cmd) string {
|
||||
env := make(map[string]string)
|
||||
for _, kv := range cmd.Env {
|
||||
split := strings.Split(kv, "=")
|
||||
k, v := split[0], split[1]
|
||||
env[k] = v
|
||||
}
|
||||
|
||||
return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v go %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], cmd.Args)
|
||||
}
|
32
vendor/golang.org/x/tools/internal/gopathwalk/walk.go
generated
vendored
32
vendor/golang.org/x/tools/internal/gopathwalk/walk.go
generated
vendored
|
@ -23,8 +23,10 @@ import (
|
|||
|
||||
// Options controls the behavior of a Walk call.
|
||||
type Options struct {
|
||||
Debug bool // Enable debug logging
|
||||
ModulesEnabled bool // Search module caches. Also disables legacy goimports ignore rules.
|
||||
// If Logf is non-nil, debug logging is enabled through this function.
|
||||
Logf func(format string, args ...interface{})
|
||||
// Search module caches. Also disables legacy goimports ignore rules.
|
||||
ModulesEnabled bool
|
||||
}
|
||||
|
||||
// RootType indicates the type of a Root.
|
||||
|
@ -80,14 +82,14 @@ func WalkSkip(roots []Root, add func(root Root, dir string), skip func(root Root
|
|||
// walkDir creates a walker and starts fastwalk with this walker.
|
||||
func walkDir(root Root, add func(Root, string), skip func(root Root, dir string) bool, opts Options) {
|
||||
if _, err := os.Stat(root.Path); os.IsNotExist(err) {
|
||||
if opts.Debug {
|
||||
log.Printf("skipping nonexistent directory: %v", root.Path)
|
||||
if opts.Logf != nil {
|
||||
opts.Logf("skipping nonexistent directory: %v", root.Path)
|
||||
}
|
||||
return
|
||||
}
|
||||
start := time.Now()
|
||||
if opts.Debug {
|
||||
log.Printf("gopathwalk: scanning %s", root.Path)
|
||||
if opts.Logf != nil {
|
||||
opts.Logf("gopathwalk: scanning %s", root.Path)
|
||||
}
|
||||
w := &walker{
|
||||
root: root,
|
||||
|
@ -100,8 +102,8 @@ func walkDir(root Root, add func(Root, string), skip func(root Root, dir string)
|
|||
log.Printf("gopathwalk: scanning directory %v: %v", root.Path, err)
|
||||
}
|
||||
|
||||
if opts.Debug {
|
||||
log.Printf("gopathwalk: scanned %s in %v", root.Path, time.Since(start))
|
||||
if opts.Logf != nil {
|
||||
opts.Logf("gopathwalk: scanned %s in %v", root.Path, time.Since(start))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,11 +132,11 @@ func (w *walker) init() {
|
|||
full := filepath.Join(w.root.Path, p)
|
||||
if fi, err := os.Stat(full); err == nil {
|
||||
w.ignoredDirs = append(w.ignoredDirs, fi)
|
||||
if w.opts.Debug {
|
||||
log.Printf("Directory added to ignore list: %s", full)
|
||||
if w.opts.Logf != nil {
|
||||
w.opts.Logf("Directory added to ignore list: %s", full)
|
||||
}
|
||||
} else if w.opts.Debug {
|
||||
log.Printf("Error statting ignored directory: %v", err)
|
||||
} else if w.opts.Logf != nil {
|
||||
w.opts.Logf("Error statting ignored directory: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -145,11 +147,11 @@ func (w *walker) init() {
|
|||
func (w *walker) getIgnoredDirs(path string) []string {
|
||||
file := filepath.Join(path, ".goimportsignore")
|
||||
slurp, err := ioutil.ReadFile(file)
|
||||
if w.opts.Debug {
|
||||
if w.opts.Logf != nil {
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
w.opts.Logf("%v", err)
|
||||
} else {
|
||||
log.Printf("Read %s", file)
|
||||
w.opts.Logf("Read %s", file)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
|
|
64
vendor/golang.org/x/tools/internal/imports/fix.go
generated
vendored
64
vendor/golang.org/x/tools/internal/imports/fix.go
generated
vendored
|
@ -14,7 +14,6 @@ import (
|
|||
"go/token"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
|
@ -22,11 +21,11 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
"golang.org/x/tools/internal/gocommand"
|
||||
"golang.org/x/tools/internal/gopathwalk"
|
||||
)
|
||||
|
||||
|
@ -263,7 +262,7 @@ type pass struct {
|
|||
|
||||
// loadPackageNames saves the package names for everything referenced by imports.
|
||||
func (p *pass) loadPackageNames(imports []*ImportInfo) error {
|
||||
if p.env.Debug {
|
||||
if p.env.Logf != nil {
|
||||
p.env.Logf("loading package names for %v packages", len(imports))
|
||||
defer func() {
|
||||
p.env.Logf("done loading package names for %v packages", len(imports))
|
||||
|
@ -335,7 +334,7 @@ func (p *pass) load() ([]*ImportFix, bool) {
|
|||
if p.loadRealPackageNames {
|
||||
err := p.loadPackageNames(append(imports, p.candidates...))
|
||||
if err != nil {
|
||||
if p.env.Debug {
|
||||
if p.env.Logf != nil {
|
||||
p.env.Logf("loading package names: %v", err)
|
||||
}
|
||||
return nil, false
|
||||
|
@ -529,7 +528,7 @@ func getFixes(fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv
|
|||
return nil, err
|
||||
}
|
||||
srcDir := filepath.Dir(abs)
|
||||
if env.Debug {
|
||||
if env.Logf != nil {
|
||||
env.Logf("fixImports(filename=%q), abs=%q, srcDir=%q ...", filename, abs, srcDir)
|
||||
}
|
||||
|
||||
|
@ -747,7 +746,6 @@ func getPackageExports(ctx context.Context, wrapped func(PackageExport), searchP
|
|||
// the go command, the go/build package, etc.
|
||||
type ProcessEnv struct {
|
||||
LocalPrefix string
|
||||
Debug bool
|
||||
|
||||
BuildFlags []string
|
||||
|
||||
|
@ -756,7 +754,7 @@ type ProcessEnv struct {
|
|||
GOPATH, GOROOT, GO111MODULE, GOPROXY, GOFLAGS, GOSUMDB string
|
||||
WorkingDir string
|
||||
|
||||
// Logf is the default logger for the ProcessEnv.
|
||||
// If Logf is non-nil, debug logging is enabled through this function.
|
||||
Logf func(format string, args ...interface{})
|
||||
|
||||
resolver Resolver
|
||||
|
@ -792,7 +790,7 @@ func (e *ProcessEnv) GetResolver() Resolver {
|
|||
if e.resolver != nil {
|
||||
return e.resolver
|
||||
}
|
||||
out, err := e.invokeGo("env", "GOMOD")
|
||||
out, err := e.invokeGo(context.TODO(), "env", "GOMOD")
|
||||
if err != nil || len(bytes.TrimSpace(out.Bytes())) == 0 {
|
||||
e.resolver = newGopathResolver(e)
|
||||
return e.resolver
|
||||
|
@ -823,38 +821,16 @@ func (e *ProcessEnv) buildContext() *build.Context {
|
|||
return &ctx
|
||||
}
|
||||
|
||||
func (e *ProcessEnv) invokeGo(verb string, args ...string) (*bytes.Buffer, error) {
|
||||
goArgs := []string{verb}
|
||||
if verb != "env" {
|
||||
goArgs = append(goArgs, e.BuildFlags...)
|
||||
func (e *ProcessEnv) invokeGo(ctx context.Context, verb string, args ...string) (*bytes.Buffer, error) {
|
||||
inv := gocommand.Invocation{
|
||||
Verb: verb,
|
||||
Args: args,
|
||||
BuildFlags: e.BuildFlags,
|
||||
Env: e.env(),
|
||||
Logf: e.Logf,
|
||||
WorkingDir: e.WorkingDir,
|
||||
}
|
||||
goArgs = append(goArgs, args...)
|
||||
cmd := exec.Command("go", goArgs...)
|
||||
stdout := &bytes.Buffer{}
|
||||
stderr := &bytes.Buffer{}
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
cmd.Env = e.env()
|
||||
cmd.Dir = e.WorkingDir
|
||||
|
||||
if e.Debug {
|
||||
defer func(start time.Time) { e.Logf("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now())
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, fmt.Errorf("running go: %v (stderr:\n%s)", err, stderr)
|
||||
}
|
||||
return stdout, nil
|
||||
}
|
||||
|
||||
func cmdDebugStr(cmd *exec.Cmd) string {
|
||||
env := make(map[string]string)
|
||||
for _, kv := range cmd.Env {
|
||||
split := strings.Split(kv, "=")
|
||||
k, v := split[0], split[1]
|
||||
env[k] = v
|
||||
}
|
||||
|
||||
return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v go %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], cmd.Args)
|
||||
return inv.Run(ctx)
|
||||
}
|
||||
|
||||
func addStdlibCandidates(pass *pass, refs references) {
|
||||
|
@ -1261,7 +1237,7 @@ func (r *gopathResolver) scan(ctx context.Context, callback *scanCallback) error
|
|||
case <-r.scanSema:
|
||||
}
|
||||
defer func() { r.scanSema <- struct{}{} }()
|
||||
gopathwalk.Walk(roots, add, gopathwalk.Options{Debug: r.env.Debug, ModulesEnabled: false})
|
||||
gopathwalk.Walk(roots, add, gopathwalk.Options{Logf: r.env.Logf, ModulesEnabled: false})
|
||||
close(scanDone)
|
||||
}()
|
||||
select {
|
||||
|
@ -1365,7 +1341,7 @@ func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string, incl
|
|||
}
|
||||
}
|
||||
|
||||
if env.Debug {
|
||||
if env.Logf != nil {
|
||||
sortedExports := append([]string(nil), exports...)
|
||||
sort.Strings(sortedExports)
|
||||
env.Logf("loaded exports in dir %v (package %v): %v", dir, pkgName, strings.Join(sortedExports, ", "))
|
||||
|
@ -1381,7 +1357,7 @@ func findImport(ctx context.Context, pass *pass, candidates []pkgDistance, pkgNa
|
|||
// ones. Note that this sorts by the de-vendored name, so
|
||||
// there's no "penalty" for vendoring.
|
||||
sort.Sort(byDistanceOrImportPathShortLength(candidates))
|
||||
if pass.env.Debug {
|
||||
if pass.env.Logf != nil {
|
||||
for i, c := range candidates {
|
||||
pass.env.Logf("%s candidate %d/%d: %v in %v", pkgName, i+1, len(candidates), c.pkg.importPathShort, c.pkg.dir)
|
||||
}
|
||||
|
@ -1419,14 +1395,14 @@ func findImport(ctx context.Context, pass *pass, candidates []pkgDistance, pkgNa
|
|||
wg.Done()
|
||||
}()
|
||||
|
||||
if pass.env.Debug {
|
||||
if pass.env.Logf != nil {
|
||||
pass.env.Logf("loading exports in dir %s (seeking package %s)", c.pkg.dir, pkgName)
|
||||
}
|
||||
// If we're an x_test, load the package under test's test variant.
|
||||
includeTest := strings.HasSuffix(pass.f.Name.Name, "_test") && c.pkg.dir == pass.srcDir
|
||||
_, exports, err := pass.env.GetResolver().loadExports(ctx, c.pkg, includeTest)
|
||||
if err != nil {
|
||||
if pass.env.Debug {
|
||||
if pass.env.Logf != nil {
|
||||
pass.env.Logf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err)
|
||||
}
|
||||
resc <- nil
|
||||
|
|
12
vendor/golang.org/x/tools/internal/imports/mod.go
generated
vendored
12
vendor/golang.org/x/tools/internal/imports/mod.go
generated
vendored
|
@ -146,7 +146,7 @@ func (r *ModuleResolver) init() error {
|
|||
}
|
||||
|
||||
func (r *ModuleResolver) initAllMods() error {
|
||||
stdout, err := r.env.invokeGo("list", "-m", "-json", "...")
|
||||
stdout, err := r.env.invokeGo(context.TODO(), "list", "-m", "-json", "...")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -156,12 +156,14 @@ func (r *ModuleResolver) initAllMods() error {
|
|||
return err
|
||||
}
|
||||
if mod.Dir == "" {
|
||||
if r.env.Debug {
|
||||
if r.env.Logf != nil {
|
||||
r.env.Logf("module %v has not been downloaded and will be ignored", mod.Path)
|
||||
}
|
||||
// Can't do anything with a module that's not downloaded.
|
||||
continue
|
||||
}
|
||||
// golang/go#36193: the go command doesn't always clean paths.
|
||||
mod.Dir = filepath.Clean(mod.Dir)
|
||||
r.modsByModPath = append(r.modsByModPath, mod)
|
||||
r.modsByDir = append(r.modsByDir, mod)
|
||||
if mod.Main {
|
||||
|
@ -468,7 +470,7 @@ func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error
|
|||
if r.scannedRoots[root] {
|
||||
continue
|
||||
}
|
||||
gopathwalk.WalkSkip([]gopathwalk.Root{root}, add, skip, gopathwalk.Options{Debug: r.env.Debug, ModulesEnabled: true})
|
||||
gopathwalk.WalkSkip([]gopathwalk.Root{root}, add, skip, gopathwalk.Options{Logf: r.env.Logf, ModulesEnabled: true})
|
||||
r.scannedRoots[root] = true
|
||||
}
|
||||
close(scanDone)
|
||||
|
@ -581,7 +583,7 @@ func (r *ModuleResolver) scanDirForPackage(root gopathwalk.Root, dir string) dir
|
|||
}
|
||||
modPath, err := module.UnescapePath(filepath.ToSlash(matches[1]))
|
||||
if err != nil {
|
||||
if r.env.Debug {
|
||||
if r.env.Logf != nil {
|
||||
r.env.Logf("decoding module cache path %q: %v", subdir, err)
|
||||
}
|
||||
return directoryPackageInfo{
|
||||
|
@ -697,7 +699,7 @@ func getMainModuleAnd114(env *ProcessEnv) (*ModuleJSON, bool, error) {
|
|||
{{.GoVersion}}
|
||||
{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}
|
||||
`
|
||||
stdout, err := env.invokeGo("list", "-m", "-f", format)
|
||||
stdout, err := env.invokeGo(context.TODO(), "list", "-m", "-f", format)
|
||||
if err != nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
|
5
vendor/modules.txt
vendored
5
vendor/modules.txt
vendored
|
@ -333,7 +333,7 @@ golang.org/x/crypto/pkcs12/internal/rc2
|
|||
## explicit
|
||||
golang.org/x/mod/module
|
||||
golang.org/x/mod/semver
|
||||
# golang.org/x/net v0.0.0-20200222125558-5a598a2470a0
|
||||
# golang.org/x/net v0.0.0-20200226121028-0de0cce0169b
|
||||
## explicit
|
||||
golang.org/x/net/bpf
|
||||
golang.org/x/net/context
|
||||
|
@ -374,11 +374,12 @@ golang.org/x/text/unicode/bidi
|
|||
golang.org/x/text/unicode/norm
|
||||
# golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
|
||||
golang.org/x/time/rate
|
||||
# golang.org/x/tools v0.0.0-20200212213243-2ee7536ab1cc
|
||||
# golang.org/x/tools v0.0.0-20200303202040-658b03bcd3d8
|
||||
## explicit
|
||||
golang.org/x/tools/go/ast/astutil
|
||||
golang.org/x/tools/imports
|
||||
golang.org/x/tools/internal/fastwalk
|
||||
golang.org/x/tools/internal/gocommand
|
||||
golang.org/x/tools/internal/gopathwalk
|
||||
golang.org/x/tools/internal/imports
|
||||
# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||
|
|
Loading…
Reference in a new issue