From d6c1ed91382b733f9f39049ee485e58b27b4a8ee Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Mon, 14 Oct 2024 13:14:05 +0200 Subject: [PATCH] chore: adds a new generator to create `dns_provider.go` (#2305) --- .golangci.yml | 166 ++++++++++-------- .../cli_help => clihelp}/generator.go | 2 +- internal/dns/descriptors/descriptors.go | 76 ++++++++ internal/{dnsdocs => dns/docs}/dns.go.tmpl | 0 internal/{dnsdocs => dns/docs}/dns.md.tmpl | 0 internal/{dnsdocs => dns/docs}/generator.go | 90 +++------- internal/dns/providers/dns_providers.go.tmpl | 27 +++ internal/dns/providers/generator.go | 73 ++++++++ providers/dns/acmedns/acmedns.toml | 1 + providers/dns/domeneshop/domeneshop.toml | 1 + providers/dns/edgedns/edgedns.toml | 1 + providers/dns/linode/linode.toml | 1 + ...s_providers.go => zz_gen_dns_providers.go} | 20 ++- 13 files changed, 306 insertions(+), 152 deletions(-) rename internal/{dnsdocs/cli_help => clihelp}/generator.go (97%) create mode 100644 internal/dns/descriptors/descriptors.go rename internal/{dnsdocs => dns/docs}/dns.go.tmpl (100%) rename internal/{dnsdocs => dns/docs}/dns.md.tmpl (100%) rename internal/{dnsdocs => dns/docs}/generator.go (70%) create mode 100644 internal/dns/providers/dns_providers.go.tmpl create mode 100644 internal/dns/providers/generator.go rename providers/dns/{dns_providers.go => zz_gen_dns_providers.go} (98%) diff --git a/.golangci.yml b/.golangci.yml index c41c3861ab..b280d83bf7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,5 +1,45 @@ -run: - timeout: 10m +linters: + enable-all: true + disable: + - gomnd # deprecated + - cyclop # duplicate of gocyclo + - sqlclosecheck # not relevant (SQL) + - rowserrcheck # not relevant (SQL) + - execinquery # not relevant (SQL) + - lll + - gosec + - dupl # not relevant + - prealloc # too many false-positive + - bodyclose # too many false-positive + - mnd + - testpackage # not relevant + - tparallel # not relevant + - paralleltest # not relevant + - nestif # too many false-positive + - wrapcheck + - err113 # not relevant + - nlreturn # not relevant + - wsl # not relevant + - exhaustive # not relevant + - exhaustruct # not relevant + - makezero # not relevant + - forbidigo + - varnamelen # not relevant + - nilnil # not relevant + - ireturn # not relevant + - contextcheck # too many false-positive + - tenv # we already have a test "framework" to handle env vars + - noctx + - forcetypeassert + - tagliatelle + - errname + - errchkjson + - nonamedreturns + - musttag # false-positive https://github.com/junk1tm/musttag/issues/17 + - gosmopolitan # not relevant + - exportloopref # Useless with go1.22 + - canonicalheader # Can create side effects in the context of API clients + - usestdlibvars # false-positive https://github.com/sashamelentyev/usestdlibvars/issues/96 linters-settings: govet: @@ -96,50 +136,18 @@ linters-settings: sprintf1: true strconcat: false -linters: - enable-all: true - disable: - - gomnd # deprecated - - cyclop # duplicate of gocyclo - - sqlclosecheck # not relevant (SQL) - - rowserrcheck # not relevant (SQL) - - execinquery # not relevant (SQL) - - lll - - gosec - - dupl # not relevant - - prealloc # too many false-positive - - bodyclose # too many false-positive - - mnd - - testpackage # not relevant - - tparallel # not relevant - - paralleltest # not relevant - - nestif # too many false-positive - - wrapcheck - - err113 # not relevant - - nlreturn # not relevant - - wsl # not relevant - - exhaustive # not relevant - - exhaustruct # not relevant - - makezero # not relevant - - forbidigo - - varnamelen # not relevant - - nilnil # not relevant - - ireturn # not relevant - - contextcheck # too many false-positive - - tenv # we already have a test "framework" to handle env vars - - noctx - - forcetypeassert - - tagliatelle - - errname - - errchkjson - - nonamedreturns - - musttag # false-positive https://github.com/junk1tm/musttag/issues/17 - - gosmopolitan # not relevant - - exportloopref # Useless with go1.22 - - canonicalheader # Can create side effects in the context of API clients - - usestdlibvars # false-positive https://github.com/sashamelentyev/usestdlibvars/issues/96 +run: + timeout: 10m + +output: + show-stats: true + sort-results: true + sort-order: + - linter + - file issues: + exclude-generated: strict exclude-use-default: false max-issues-per-linter: 0 max-same-issues: 0 @@ -160,86 +168,96 @@ issues: - path: providers/dns/dns_providers.go linters: - gocyclo - - path: providers/dns/gcloud/googlecloud_test.go - text: 'string `(lego\.wtf|manhattan)` has (\d+) occurrences, make it a constant' - - path: providers/dns/zoneee/zoneee_test.go - text: 'string `(bar|foo)` has (\d+) occurrences, make it a constant' - path: certcrypto/crypto.go text: '(tlsFeatureExtensionOID|ocspMustStapleFeature) is a global variable' + linters: + - gochecknoglobals - path: challenge/dns01/nameserver.go text: '(defaultNameservers|recursiveNameservers|fqdnSoaCache|muFqdnSoaCache) is a global variable' + linters: + - gochecknoglobals - path: challenge/dns01/nameserver_.+.go text: 'dnsTimeout is a global variable' + linters: + - gochecknoglobals - path: challenge/dns01/nameserver_test.go text: 'findXByFqdnTestCases is a global variable' + linters: + - gochecknoglobals - path: challenge/http01/domain_matcher.go text: 'string `Host` has \d occurrences, make it a constant' + linters: + - goconst - path: challenge/http01/domain_matcher.go text: 'cyclomatic complexity \d+ of func `parseForwardedHeader` is high' + linters: + - gocyclo - path: challenge/http01/domain_matcher.go text: "Function 'parseForwardedHeader' has too many statements" + linters: + - funlen - path: challenge/tlsalpn01/tls_alpn_challenge.go text: 'idPeAcmeIdentifierV1 is a global variable' + linters: + - gochecknoglobals - path: log/logger.go text: 'Logger is a global variable' + linters: + - gochecknoglobals - path: 'e2e/(dnschallenge/)?[\d\w]+_test.go' text: load is a global variable + linters: + - gochecknoglobals - path: 'providers/dns/([\d\w]+/)*[\d\w]+_test.go' text: 'envTest is a global variable' + linters: + - gochecknoglobals - path: 'providers/http/([\d\w]+/)*[\d\w]+_test.go' text: 'envTest is a global variable' + linters: + - gochecknoglobals - path: providers/dns/namecheap/namecheap_test.go text: 'testCases is a global variable' + linters: + - gochecknoglobals - path: providers/dns/acmedns/acmedns_test.go text: 'egTestAccount is a global variable' + linters: + - gochecknoglobals - path: providers/http/memcached/memcached_test.go text: 'memcachedHosts is a global variable' - - path: providers/dns/sakuracloud/client_test.go - text: 'cyclomatic complexity 13 of func `(TestDNSProvider_cleanupTXTRecord_concurrent|TestDNSProvider_addTXTRecord_concurrent)` is high' - - path: providers/dns/dns_providers.go - text: "Function 'NewDNSChallengeProviderByName' has too many statements" - - path: cmd/flags.go - text: "Function 'CreateFlags' is too long" - - path: certificate/certificates.go - text: "Function 'GetOCSP' is too long" - - path: providers/dns/otc/client.go - text: "Function 'loginRequest' is too long" - - path: providers/dns/gandi/gandi.go - text: "Function 'Present' is too long" + linters: + - gochecknoglobals - path: cmd/zz_gen_cmd_dnshelp.go linters: - gocyclo - funlen - path: providers/dns/checkdomain/internal/types.go text: '`payed` is a misspelling of `paid`' - - path: providers/dns/namecheap/namecheap_test.go - text: 'cognitive complexity (\d+) of func `TestDNSProvider_getHosts` is high' + linters: + - misspell - path: platform/tester/env_test.go linters: - thelper - path: providers/dns/oraclecloud/oraclecloud_test.go text: 'SA1019: x509.EncryptPEMBlock has been deprecated since Go 1.16' - - path: challenge/http01/domain_matcher.go - text: 'yodaStyleExpr' - - path: providers/dns/dns_providers.go - text: 'Function name: NewDNSChallengeProviderByName,' + linters: + - staticcheck - path: providers/dns/sakuracloud/wrapper.go text: 'mu is a global variable' - - path: providers/dns/hosttech/internal/client_test.go - text: 'Duplicate words \(0\) found' + linters: + - gochecknoglobals - path: cmd/cmd_renew.go text: 'cyclomatic complexity \d+ of func `(renewForDomains|renewForCSR)` is high' + linters: + - gocyclo - path: providers/dns/cpanel/cpanel.go text: 'cyclomatic complexity 13 of func `\(\*DNSProvider\)\.CleanUp` is high' + linters: + - gocyclo # Those elements have been replaced by non-exposed structures. - path: providers/dns/linode/linode_test.go linters: - staticcheck text: "SA1019: linodego\\.(DomainsPagedResponse|DomainRecordsPagedResponse) is deprecated" - -output: - sort-results: true - sort-order: - - linter - - file diff --git a/internal/dnsdocs/cli_help/generator.go b/internal/clihelp/generator.go similarity index 97% rename from internal/dnsdocs/cli_help/generator.go rename to internal/clihelp/generator.go index 4f87ab6546..2d256b4d77 100644 --- a/internal/dnsdocs/cli_help/generator.go +++ b/internal/clihelp/generator.go @@ -14,7 +14,7 @@ import ( "github.com/urfave/cli/v2" ) -const outputFile = "../../../docs/data/zz_cli_help.toml" +const outputFile = "../../docs/data/zz_cli_help.toml" const baseTemplate = `# THIS FILE IS AUTO-GENERATED. PLEASE DO NOT EDIT. diff --git a/internal/dns/descriptors/descriptors.go b/internal/dns/descriptors/descriptors.go new file mode 100644 index 0000000000..838c454a73 --- /dev/null +++ b/internal/dns/descriptors/descriptors.go @@ -0,0 +1,76 @@ +package descriptors + +import ( + "os" + "path/filepath" + + "github.com/BurntSushi/toml" +) + +type Providers struct { + Providers []Provider +} + +type Provider struct { + Name string // Real name of the DNS provider + Code string // DNS code + Aliases []string // DNS code aliases (for compatibility/deprecation) + Since string // First lego version + URL string // DNS provider URL + Description string // Provider summary + Example string // CLI example + Configuration *Configuration // Environment variables + Links *Links // Links + Additional string // Extra documentation + GeneratedFrom string // Source file +} + +type Configuration struct { + Credentials map[string]string + Additional map[string]string +} + +type Links struct { + API string + GoClient string +} + +// GetProviderInformation extract provider information from TOML description files. +func GetProviderInformation(root string) (*Providers, error) { + models := &Providers{} + + err := filepath.Walk(filepath.Join(root, "providers", "dns"), walker(root, models)) + if err != nil { + return nil, err + } + + return models, nil +} + +func walker(root string, prs *Providers) func(string, os.FileInfo, error) error { + return func(path string, _ os.FileInfo, err error) error { + if err != nil { + return err + } + + if filepath.Ext(path) != ".toml" { + return nil + } + + m := Provider{} + + m.GeneratedFrom, err = filepath.Rel(root, path) + if err != nil { + return err + } + + _, err = toml.DecodeFile(path, &m) + if err != nil { + return err + } + + prs.Providers = append(prs.Providers, m) + + return nil + } +} diff --git a/internal/dnsdocs/dns.go.tmpl b/internal/dns/docs/dns.go.tmpl similarity index 100% rename from internal/dnsdocs/dns.go.tmpl rename to internal/dns/docs/dns.go.tmpl diff --git a/internal/dnsdocs/dns.md.tmpl b/internal/dns/docs/dns.md.tmpl similarity index 100% rename from internal/dnsdocs/dns.md.tmpl rename to internal/dns/docs/dns.md.tmpl diff --git a/internal/dnsdocs/generator.go b/internal/dns/docs/generator.go similarity index 70% rename from internal/dnsdocs/generator.go rename to internal/dns/docs/generator.go index c19c370e88..30e543faa3 100644 --- a/internal/dnsdocs/generator.go +++ b/internal/dns/docs/generator.go @@ -16,14 +16,14 @@ import ( "strings" "text/template" - "github.com/BurntSushi/toml" + "github.com/go-acme/lego/v4/internal/dns/descriptors" ) const ( - root = "../../" - dnsPackage = root + "providers/dns" - mdTemplate = root + "internal/dnsdocs/dns.md.tmpl" - cliTemplate = root + "internal/dnsdocs/dns.go.tmpl" + root = "../../../" + + mdTemplate = root + "internal/dns/docs/dns.md.tmpl" + cliTemplate = root + "internal/dns/docs/dns.go.tmpl" cliOutput = root + "cmd/zz_gen_cmd_dnshelp.go" docOutput = root + "docs/content/dns" readmePath = root + "README.md" @@ -34,41 +34,20 @@ const ( endLine = "" ) -type Model struct { - Name string // Real name of the DNS provider - Code string // DNS code - Since string // First lego version - URL string // DNS provider URL - Description string // Provider summary - Example string // CLI example - Configuration *Configuration // Environment variables - Links *Links // Links - Additional string // Extra documentation - GeneratedFrom string // Source file -} - -type Configuration struct { - Credentials map[string]string - Additional map[string]string -} - -type Links struct { - API string - GoClient string -} - -type Providers struct { - Providers []Model -} - func main() { - models := &Providers{} - - err := filepath.Walk(dnsPackage, walker(models)) + models, err := descriptors.GetProviderInformation(root) if err != nil { log.Fatal(err) } + for _, m := range models.Providers { + // generate documentation + err = generateDocumentation(m) + if err != nil { + log.Fatal(err) + } + } + // generate CLI help err = generateCLIHelp(models) if err != nil { @@ -84,36 +63,7 @@ func main() { fmt.Printf("Documentation for %d DNS providers has been generated.\n", len(models.Providers)+1) } -func walker(prs *Providers) func(string, os.FileInfo, error) error { - return func(path string, _ os.FileInfo, err error) error { - if err != nil { - return err - } - - if filepath.Ext(path) == ".toml" { - m := Model{} - - m.GeneratedFrom, err = filepath.Rel(root, path) - if err != nil { - return err - } - - _, err := toml.DecodeFile(path, &m) - if err != nil { - return err - } - - prs.Providers = append(prs.Providers, m) - - // generate documentation - return generateDocumentation(m) - } - - return nil - } -} - -func generateDocumentation(m Model) error { +func generateDocumentation(m descriptors.Provider) error { filename := filepath.Join(docOutput, "zz_gen_"+m.Code+".md") file, err := os.Create(filename) @@ -121,10 +71,12 @@ func generateDocumentation(m Model) error { return err } + defer func() { _ = file.Close() }() + return template.Must(template.ParseFiles(mdTemplate)).Execute(file, m) } -func generateCLIHelp(models *Providers) error { +func generateCLIHelp(models *descriptors.Providers) error { filename := filepath.Clean(cliOutput) file, err := os.Create(filename) @@ -132,6 +84,8 @@ func generateCLIHelp(models *Providers) error { return err } + defer func() { _ = file.Close() }() + tlt := template.New(filepath.Base(cliTemplate)).Funcs(map[string]interface{}{ "safe": func(src string) string { return strings.ReplaceAll(src, "`", "'") @@ -154,7 +108,7 @@ func generateCLIHelp(models *Providers) error { return err } -func generateReadMe(models *Providers) error { +func generateReadMe(models *descriptors.Providers) error { maximum, lines := extractTableData(models) file, err := os.Open(readmePath) @@ -203,7 +157,7 @@ func generateReadMe(models *Providers) error { return os.WriteFile(readmePath, buffer.Bytes(), 0o666) } -func extractTableData(models *Providers) (int, [][]string) { +func extractTableData(models *descriptors.Providers) (int, [][]string) { readmePattern := "[%s](https://go-acme.github.io/lego/dns/%s/)" items := []string{fmt.Sprintf(readmePattern, "Manual", "manual")} diff --git a/internal/dns/providers/dns_providers.go.tmpl b/internal/dns/providers/dns_providers.go.tmpl new file mode 100644 index 0000000000..2030a3ed0d --- /dev/null +++ b/internal/dns/providers/dns_providers.go.tmpl @@ -0,0 +1,27 @@ +// Code generated by 'make generate-dns'; DO NOT EDIT. + +package dns + +import ( + "fmt" + + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/challenge/dns01" +{{- range $provider := .Providers }} + "github.com/go-acme/lego/v4/providers/dns/{{ cleanName $provider.Code }}" +{{- end}} +) + +// NewDNSChallengeProviderByName Factory for DNS providers. +func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) { + switch name { + case "manual": + return dns01.NewDNSProviderManual() +{{- range $provider := .Providers }} + case "{{ $provider.Code }}"{{range $alias := $provider.Aliases }},"{{ $alias }}"{{end}}: + return {{ cleanName $provider.Code }}.NewDNSProvider() +{{- end}} + default: + return nil, fmt.Errorf("unrecognized DNS provider: %s", name) + } +} diff --git a/internal/dns/providers/generator.go b/internal/dns/providers/generator.go new file mode 100644 index 0000000000..98749db470 --- /dev/null +++ b/internal/dns/providers/generator.go @@ -0,0 +1,73 @@ +package main + +//go:generate go run . + +import ( + "bytes" + "fmt" + "go/format" + "log" + "os" + "path/filepath" + "strings" + "text/template" + + "github.com/go-acme/lego/v4/internal/dns/descriptors" +) + +const ( + root = "../../../" + + srcTemplate = "internal/dns/providers/dns_providers.go.tmpl" + outputPath = "providers/dns/zz_gen_dns_providers.go" +) + +func main() { + err := generate() + if err != nil { + log.Fatal(err) + } +} + +func generate() error { + info, err := descriptors.GetProviderInformation(root) + if err != nil { + return err + } + + file, err := os.Create(filepath.Join(root, outputPath)) + if err != nil { + return err + } + + defer func() { _ = file.Close() }() + + tmplFile := filepath.Join(root, srcTemplate) + + tlt := template.New(filepath.Base(tmplFile)).Funcs(map[string]interface{}{ + "cleanName": func(src string) string { + return strings.ReplaceAll(src, "-", "") + }, + }) + + b := &bytes.Buffer{} + err = template.Must(tlt.ParseFiles(tmplFile)).Execute(b, info) + if err != nil { + return err + } + + // gofmt + source, err := format.Source(b.Bytes()) + if err != nil { + return err + } + + _, err = file.Write(source) + if err != nil { + return err + } + + fmt.Printf("Switch mapping for %d DNS providers has been generated.\n", len(info.Providers)+1) + + return nil +} diff --git a/providers/dns/acmedns/acmedns.toml b/providers/dns/acmedns/acmedns.toml index 4e17eb7ee1..098260a375 100644 --- a/providers/dns/acmedns/acmedns.toml +++ b/providers/dns/acmedns/acmedns.toml @@ -2,6 +2,7 @@ Name = "Joohoi's ACME-DNS" Description = '''''' URL = "https://github.com/joohoi/acme-dns" Code = "acme-dns" +Aliases = ["acmedns"] # TODO(ldez): remove "-" in v5 Since = "v1.1.0" Example = ''' diff --git a/providers/dns/domeneshop/domeneshop.toml b/providers/dns/domeneshop/domeneshop.toml index 5dffea7b6d..1bbd1e8589 100644 --- a/providers/dns/domeneshop/domeneshop.toml +++ b/providers/dns/domeneshop/domeneshop.toml @@ -2,6 +2,7 @@ Name = "Domeneshop" Description = '''''' URL = "https://domene.shop" Code = "domeneshop" +Aliases = ["domainnameshop"] Since = "v4.3.0" Example = ''' diff --git a/providers/dns/edgedns/edgedns.toml b/providers/dns/edgedns/edgedns.toml index dffa6e46c5..d543281d6c 100644 --- a/providers/dns/edgedns/edgedns.toml +++ b/providers/dns/edgedns/edgedns.toml @@ -4,6 +4,7 @@ Akamai edgedns supersedes FastDNS; implementing a DNS provider for solving the D ''' URL = "https://www.akamai.com/us/en/products/security/edge-dns.jsp" Code = "edgedns" +Aliases = ["fastdns"] # "fastdns" is for compatibility with v3, must be dropped in v5 Since = "v3.9.0" Example = ''' diff --git a/providers/dns/linode/linode.toml b/providers/dns/linode/linode.toml index 2910916ef9..91fbc783c2 100644 --- a/providers/dns/linode/linode.toml +++ b/providers/dns/linode/linode.toml @@ -2,6 +2,7 @@ Name = "Linode (v4)" Description = '''''' URL = "https://www.linode.com/" Code = "linode" +Aliases = ["linodev4"] # "linodev4" is for compatibility with v3, must be dropped in v5 Since = "v1.1.0" Example = ''' diff --git a/providers/dns/dns_providers.go b/providers/dns/zz_gen_dns_providers.go similarity index 98% rename from providers/dns/dns_providers.go rename to providers/dns/zz_gen_dns_providers.go index cb1fc17bdb..5c805e770d 100644 --- a/providers/dns/dns_providers.go +++ b/providers/dns/zz_gen_dns_providers.go @@ -1,3 +1,5 @@ +// Code generated by 'make generate-dns'; DO NOT EDIT. + package dns import ( @@ -148,7 +150,9 @@ import ( // NewDNSChallengeProviderByName Factory for DNS providers. func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) { switch name { - case "acme-dns": // TODO(ldez): remove "-" in v5 + case "manual": + return dns01.NewDNSProviderManual() + case "acme-dns", "acmedns": return acmedns.NewDNSProvider() case "alidns": return alidns.NewDNSProvider() @@ -156,14 +160,14 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) { return allinkl.NewDNSProvider() case "arvancloud": return arvancloud.NewDNSProvider() - case "azure": - return azure.NewDNSProvider() - case "azuredns": - return azuredns.NewDNSProvider() case "auroradns": return auroradns.NewDNSProvider() case "autodns": return autodns.NewDNSProvider() + case "azure": + return azure.NewDNSProvider() + case "azuredns": + return azuredns.NewDNSProvider() case "bindman": return bindman.NewDNSProvider() case "bluecat": @@ -224,7 +228,7 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) { return dynu.NewDNSProvider() case "easydns": return easydns.NewDNSProvider() - case "edgedns", "fastdns": // "fastdns" is for compatibility with v3, must be dropped in v5 + case "edgedns", "fastdns": return edgedns.NewDNSProvider() case "efficientip": return efficientip.NewDNSProvider() @@ -294,7 +298,7 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) { return lightsail.NewDNSProvider() case "limacity": return limacity.NewDNSProvider() - case "linode", "linodev4": // "linodev4" is for compatibility with v3, must be dropped in v5 + case "linode", "linodev4": return linode.NewDNSProvider() case "liquidweb": return liquidweb.NewDNSProvider() @@ -304,8 +308,6 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) { return luadns.NewDNSProvider() case "mailinabox": return mailinabox.NewDNSProvider() - case "manual": - return dns01.NewDNSProviderManual() case "metaname": return metaname.NewDNSProvider() case "mijnhost":