### Using the Example Provider Source: https://github.com/libdns/libdns/blob/master/libdnstest/README.md Instantiate and use the example provider, which implements all libdns interfaces using in-memory storage. This provider is useful for ensuring that the tests pass against a known-good implementation. ```go import "github.com/libdns/libdns/libdnstest/example" provider := example.New("example.com.") records, err := provider.GetRecords(ctx, "example.com.") ``` -------------------------------- ### SetRecords Usage Example Source: https://github.com/libdns/libdns/blob/master/_autodocs/01-interfaces.md Example demonstrating how to use SetRecords to ensure specific records exist for a zone. ```go // Ensure only 192.0.2.1 is an A record for example.com records := []libdns.Record{ libdns.Address{ Name: "@", TTL: 3600 * time.Second, IP: netip.MustParseAddr("192.0.2.1"), }, } set, err := provider.SetRecords(ctx, "example.com.", records) if err != nil { log.Fatal(err) } // Any other A records for @ are now removed; TXT and other types unaffected ``` -------------------------------- ### Create DNS records Source: https://github.com/libdns/libdns/blob/master/_autodocs/08-quick-reference.md Examples for initializing various DNS record structures supported by libdns. ```go // Address (A or AAAA) libdns.Address{ Name: "www", TTL: 3600 * time.Second, IP: netip.MustParseAddr("192.0.2.1"), } // CNAME libdns.CNAME{ Name: "alias", TTL: 3600 * time.Second, Target: "www.example.com.", } // TXT libdns.TXT{ Name: "_acme-challenge", TTL: 300 * time.Second, Text: "validation-string", } // MX libdns.MX{ Name: "@", TTL: 3600 * time.Second, Preference: 10, Target: "mail.example.com.", } // NS libdns.NS{ Name: "@", TTL: 3600 * time.Second, Target: "ns1.example.com.", } // SRV libdns.SRV{ Service: "xmpp", Transport: "tcp", Name: "@", TTL: 3600 * time.Second, Priority: 10, Weight: 20, Port: 5222, Target: "chat.example.com.", } // CAA libdns.CAA{ Name: "@", TTL: 3600 * time.Second, Flags: 0, Tag: "issue", Value: "letsencrypt.org", } // ServiceBinding (HTTPS) libdns.ServiceBinding{ Name: "@", TTL: 3600 * time.Second, Scheme: "https", Priority: 1, Target: "cdn.example.com.", Params: libdns.SvcParams{ "alpn": {"h2", "h3"}, }, } ``` -------------------------------- ### ServiceBinding Port Configuration Source: https://github.com/libdns/libdns/blob/master/_autodocs/07-special-cases.md Examples showing how to configure ports via URLSchemePort, SvcParams, or both simultaneously. ```go // https://example.com:2443/ // Client looks up: _2443._https.example.com. sb := libdns.ServiceBinding{ Scheme: "https", URLSchemePort: 2443, // Port in the URL Name: "example.com.", Priority: 1, Target: "cdn.example.com.", } // Name becomes: _2443._https.example.com. ``` ```go sb := libdns.ServiceBinding{ Scheme: "https", Name: "example.com.", Priority: 1, Target: "cdn.example.com.", Params: libdns.SvcParams{ "port": {"8443"}, // Connect to 8443, not 443 }, } // This changes what port the client connects to // But the URL may still use default or specified port ``` ```go // URL: https://example.com:8888/ // Client looks up: _8888._https.example.com. // Then connects to port 9999 sb := libdns.ServiceBinding{ Scheme: "https", URLSchemePort: 8888, // Port in URL (must match) Name: "example.com.", Priority: 1, Target: "cdn.example.com.", Params: libdns.SvcParams{ "port": {"9999"}, // Connection port (if different from URLSchemePort) }, } ``` -------------------------------- ### Basic Test Suite Setup Source: https://github.com/libdns/libdns/blob/master/libdnstest/README.md Initialize the test suite with a provider and a domain. Run all defined tests against the provider. ```go import "github.com/libdns/libdns/libdnstest" suite := libdnstest.NewTestSuite(provider, "example.com.") suite.RunTests(t) ``` -------------------------------- ### Create Address Records Source: https://github.com/libdns/libdns/blob/master/_autodocs/02-record-types.md Example of initializing a slice of Address records and appending them via a provider. ```go import ( "net/netip" "time" "github.com/libdns/libdns" ) records := []libdns.Record{ libdns.Address{ Name: "www", TTL: 3600 * time.Second, IP: netip.MustParseAddr("192.0.2.1"), }, libdns.Address{ Name: "api", TTL: 3600 * time.Second, IP: netip.MustParseAddr("2001:db8::1"), }, } created, _ := provider.AppendRecords(ctx, "example.com.", records) ``` -------------------------------- ### Implement RecordGetter usage Source: https://github.com/libdns/libdns/blob/master/_autodocs/01-interfaces.md Example demonstrating how to retrieve and iterate over DNS records using the RecordGetter interface. ```go import ( "context" "github.com/libdns/libdns" ) provider := yourProvider() ctx := context.Background() records, err := provider.GetRecords(ctx, "example.com.") if err != nil { log.Fatal(err) } for _, record := range records { rr := record.RR() fmt.Printf("%s %d %s %s\n", rr.Name, rr.TTL, rr.Type, rr.Data) } ``` -------------------------------- ### Create TXT Records Source: https://github.com/libdns/libdns/blob/master/_autodocs/02-record-types.md Example of creating and appending TXT records using the libdns provider interface. ```go records := []libdns.Record{ libdns.TXT{ Name: "v=spf1", TTL: 3600 * time.Second, Text: "v=spf1 include:_spf.example.com ~all", }, libdns.TXT{ Name: "_acme-challenge", TTL: 300 * time.Second, Text: "validation-token-here", }, } created, _ := provider.AppendRecords(ctx, "example.com.", records) ``` -------------------------------- ### Run Provider Tests Source: https://github.com/libdns/libdns/blob/master/_autodocs/04-testing.md Example of initializing a test suite and running tests, including wrapping a provider that lacks ZoneLister support. ```go import ( "testing" "github.com/libdns/libdns/libdnstest" ) func TestMyProvider(t *testing.T) { provider := &MyProvider{} // If MyProvider doesn't implement ZoneLister, wrap it: wrappedProvider := libdnstest.WrapNoZoneLister(provider) suite := libdnstest.NewTestSuite(wrappedProvider, "example.com.") suite.RunTests(t) // ZoneLister test will be skipped } ``` -------------------------------- ### Implement RecordAppender usage Source: https://github.com/libdns/libdns/blob/master/_autodocs/01-interfaces.md Example demonstrating how to append new records to a DNS zone using the RecordAppender interface. ```go records := []libdns.Record{ libdns.Address{ Name: "www", TTL: 3600 * time.Second, IP: netip.MustParseAddr("192.0.2.1"), }, libdns.TXT{ Name: "_acme-challenge", TTL: 300 * time.Second, Text: "validation-string", }, } created, err := provider.AppendRecords(ctx, "example.com.", records) if err != nil { log.Fatal(err) } for _, r := range created { fmt.Printf("Created: %s\n", r.RR().Name) } ``` -------------------------------- ### Implement CustomRecord for testing Source: https://github.com/libdns/libdns/blob/master/_autodocs/04-testing.md Example of defining a custom record type and configuring the test suite to use it. ```go type CustomRecord struct { libdns.RR ProviderID string } func (c CustomRecord) RR() libdns.RR { return c.RR } suite := libdnstest.NewTestSuite(provider, "example.com.") suite.AppendRecordFunc = func(record libdns.Record) libdns.Record { return CustomRecord{ RR: record.RR(), ProviderID: "custom-id", } } suite.RunTests(t) ``` -------------------------------- ### Set MX Records Source: https://github.com/libdns/libdns/blob/master/_autodocs/02-record-types.md Example of creating a slice of MX records and applying them via a provider. ```go records := []libdns.Record{ libdns.MX{ Name: "@", TTL: 3600 * time.Second, Preference: 10, Target: "mail1.example.com.", }, libdns.MX{ Name: "@", TTL: 3600 * time.Second, Preference: 20, Target: "mail2.example.com.", }, } created, _ := provider.SetRecords(ctx, "example.com.", records) ``` -------------------------------- ### AbsoluteName Usage Examples Source: https://github.com/libdns/libdns/blob/master/_autodocs/03-utility-functions.md Demonstrates how to convert relative names to FQDNs, including handling of the zone root and existing FQDNs. ```go import "github.com/libdns/libdns" fqdn := libdns.AbsoluteName("www", "example.com.") // Returns: "www.example.com." fqdn = libdns.AbsoluteName("@", "example.com") // Returns: "example.com." fqdn = libdns.AbsoluteName("", "example.com.") // Returns: "example.com." (empty string treated as "@") fqdn = libdns.AbsoluteName("sub.example.com.", "ignored.com.") // Returns: "sub.example.com." (already FQDN, zone ignored) fqdn = libdns.AbsoluteName("sub", "example.com") // Returns: "sub.example.com." (zone trailing dot added automatically) ``` -------------------------------- ### Invoke ListZones Method Source: https://github.com/libdns/libdns/blob/master/_autodocs/01-interfaces.md Example usage of the ListZones method to retrieve and iterate over available DNS zones. ```go zones, err := provider.ListZones(ctx) if err != nil { log.Fatal(err) } for _, zone := range zones { fmt.Println(zone.Name) } ``` -------------------------------- ### Relative Name Examples Source: https://github.com/libdns/libdns/blob/master/_autodocs/09-architecture.md Examples of relative DNS names used within libdns, where names are relative to the zone root. ```go // Input/output: relative names "www" // Relative to zone "@" // Zone root "_acme-challenge" // Relative to zone ``` -------------------------------- ### Invoke AttemptZoneCleanup Source: https://github.com/libdns/libdns/blob/master/_autodocs/04-testing.md Example of calling the cleanup method within a test suite. ```go suite := libdnstest.NewTestSuite(provider, "example.com.") if err := suite.AttemptZoneCleanup(); err != nil { t.Fatalf("Cleanup failed: %v", err) } ``` -------------------------------- ### Get All Records Source: https://github.com/libdns/libdns/blob/master/_autodocs/08-quick-reference.md Retrieves all records for a specified zone. ```go records, err := provider.GetRecords(ctx, "example.com.") ``` -------------------------------- ### RelativeName Usage Examples Source: https://github.com/libdns/libdns/blob/master/_autodocs/03-utility-functions.md Demonstrates how to convert FQDNs to relative names, handling trailing dots and zone root cases. ```go import "github.com/libdns/libdns" name := libdns.RelativeName("sub.example.com", "example.com.") // Returns: "sub" name = libdns.RelativeName("example.com.", "example.com.") // Returns: "@" name = libdns.RelativeName("example.net", "example.com.") // Returns: "example.net" (cannot be expressed relative) name = libdns.RelativeName("sub.example.com.", "example.com") // Returns: "sub" (trailing dots handled liberally) ``` -------------------------------- ### Delete DNS Records Source: https://github.com/libdns/libdns/blob/master/_autodocs/01-interfaces.md Examples showing how to delete specific records and how to use wildcard matching by leaving fields empty. ```go // Delete a specific record records := []libdns.Record{ libdns.TXT{ Name: "_acme-challenge", TTL: 300 * time.Second, Text: "validation-string", }, } deleted, err := provider.DeleteRecords(ctx, "example.com.", records) if err != nil { log.Fatal(err) } // Delete any TXT records for a name (TTL and Text unspecified) records = []libdns.Record{ libdns.TXT{ Name: "old-record", TTL: 0, Text: "", }, } deleted, err = provider.DeleteRecords(ctx, "example.com.", records) ``` -------------------------------- ### Relative Record Name Examples Source: https://github.com/libdns/libdns/blob/master/_autodocs/05-implementation-guide.md Demonstrates the relative naming convention used by libdns for record inputs and outputs. ```text // Input/output naming convention "www" // → www.example.com. "@" // → example.com. "_acme-challenge" // → _acme-challenge.example.com. "sub.domain" // → sub.domain.example.com. ``` -------------------------------- ### Configure TestSuite Options Source: https://github.com/libdns/libdns/blob/master/_autodocs/04-testing.md Example of configuring test suite options such as skipping specific record types before execution. ```go suite := libdnstest.NewTestSuite(provider, "test.example.com.") suite.SkipRRTypes = map[string]bool{"SRV": true} suite.RunTests(t) ``` -------------------------------- ### Interact with DNS Records using libdns and Cloudflare Provider Source: https://github.com/libdns/libdns/blob/master/README.md This example demonstrates how to use the libdns library with a specific provider (Cloudflare) to list, create/update, and delete DNS records. Ensure you have the appropriate provider package imported and configured. ```go import ( "github.com/libdns/cloudflare" "github.com/libdns/libdns" ) ctx := context.TODO() zone := "example.com." // configure the DNS provider (choose any from github.com/libdns) provider := cloudflare.Provider{APIToken: "topsecret"} // list records recs, err := provider.GetRecords(ctx, zone) // create records (AppendRecords is similar, with different semantics) newRecs, err := provider.SetRecords(ctx, zone, []libdns.Record{ libdns.Address{ Name: "@", Value: netip.MustParseAddr("1.2.3.4"), }, }) // delete records deletedRecs, err := provider.DeleteRecords(ctx, zone, []libdns.Record{ libdns.TXT{ Name: "subdomain", Text: "txt value I want to delete" }, }) // no matter which provider you use, the code stays the same! // (some providers have caveats; see their package documentation) ``` -------------------------------- ### Create CNAME Record Source: https://github.com/libdns/libdns/blob/master/_autodocs/02-record-types.md Example of creating a CNAME record. Note that using CNAME with AppendRecords can be problematic if other records exist at the same name. ```go record := libdns.CNAME{ Name: "alias", TTL: 3600 * time.Second, Target: "www.example.com.", } created, _ := provider.AppendRecords(ctx, "example.com.", []libdns.Record{record}) ``` -------------------------------- ### Document DNSSEC Record Behavior Source: https://github.com/libdns/libdns/blob/master/_autodocs/05-implementation-guide.md Example of documenting whether a provider includes DNSSEC records in GetRecords results. ```go // Provider documentation /* MyProvider GetRecords behavior: - Includes standard RRs (A, AAAA, CNAME, etc.) - Excludes DNSSEC-related RRs (RRSIG, DNSKEY, DS) - DNSSEC can be managed separately via provider-specific APIs */ ``` -------------------------------- ### Create SRV Record Source: https://github.com/libdns/libdns/blob/master/_autodocs/02-record-types.md Example of creating an SRV record. The Target field must include a trailing dot. ```go record := libdns.SRV{ Service: "xmpp", Transport: "tcp", Name: "@", TTL: 3600 * time.Second, Priority: 10, Weight: 20, Port: 5222, Target: "chat.example.com.", } created, _ := provider.AppendRecords(ctx, "example.com.", []libdns.Record{record}) ``` -------------------------------- ### Handle RFC 9460 Escape Sequences Source: https://github.com/libdns/libdns/blob/master/_autodocs/07-special-cases.md Examples of valid and invalid escape sequences for SvcParams parsing. ```go input := `key="value\\nescaped"` // Literal backslash and 'n' input2 := `key="value\052comma"` // Escaped comma (octal 052 = comma) input3 := `key="value\"quote"` // Escaped quote ``` ```go // These will fail to parse input1 := `key="unescaped)paren"` // Illegal unescaped character input2 := `key="incomplete\"` // Incomplete escape sequence input3 := `key="bad\256octal"` // Octal value out of range ``` -------------------------------- ### Respect context cancellation in providers Source: https://github.com/libdns/libdns/blob/master/_autodocs/09-architecture.md Providers must check for context cancellation before starting and during iterative operations to ensure responsiveness. ```go func (p *Provider) AppendRecords(ctx context.Context, zone string, recs []Record) ([]Record, error) { // Check before starting select { case <-ctx.Done(): return nil, ctx.Err() default: } for _, rec := range recs { // Check during iteration select { case <-ctx.Done(): return nil, ctx.Err() default: } p.apiCreateRecord(ctx, zone, rec) } return recs, nil } ``` -------------------------------- ### Define NS Records Source: https://github.com/libdns/libdns/blob/master/_autodocs/02-record-types.md Example of defining a slice of NS records for a zone. ```go records := []libdns.Record{ libdns.NS{ Name: "@", TTL: 3600 * time.Second, Target: "ns1.example.com.", }, libdns.NS{ Name: "@", TTL: 3600 * time.Second, Target: "ns2.example.com.", }, } ``` -------------------------------- ### Initialize and Run Test Suite Source: https://github.com/libdns/libdns/blob/master/_autodocs/08-quick-reference.md Use the libdnstest suite to validate provider implementations. Wrap providers lacking ZoneLister functionality using WrapNoZoneLister. ```go import "github.com/libdns/libdns/libdnstest" // Create test suite suite := libdnstest.NewTestSuite(provider, "example.com.") // Configure suite.Timeout = 60 * time.Second suite.SkipRRTypes = map[string]bool{"SRV": true} suite.ExpectEmptyZone = true // Run all tests suite.RunTests(t) // For providers without ZoneLister wrapped := libdnstest.WrapNoZoneLister(provider) suite := libdnstest.NewTestSuite(wrapped, "example.com.") ``` -------------------------------- ### Create ServiceBinding Record Source: https://github.com/libdns/libdns/blob/master/_autodocs/02-record-types.md Demonstrates initializing a ServiceBinding record with specific parameters and appending it via a provider. ```go record := libdns.ServiceBinding{ Name: "@", TTL: 3600 * time.Second, Scheme: "https", Priority: 1, Target: "cdn.example.com.", Params: libdns.SvcParams{ "alpn": {"h2", "h3"}, "ipv4hint": {"192.0.2.1", "192.0.2.2"}, "ipv6hint": {"2001:db8::1"}, }, } created, _ := provider.AppendRecords(ctx, "example.com.", []libdns.Record{record}) ``` -------------------------------- ### Manage context for libdns operations Source: https://github.com/libdns/libdns/blob/master/_autodocs/08-quick-reference.md Demonstrates how to apply timeouts, cancellation, and deadlines to provider operations. ```go // With timeout ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() records, _ := provider.GetRecords(ctx, zone) // Cancellable ctx, cancel := context.WithCancel(context.Background()) defer cancel() records, _ := provider.GetRecords(ctx, zone) // With deadline deadline := time.Now().Add(5 * time.Second) ctx, cancel := context.WithDeadline(context.Background(), deadline) defer cancel() ``` -------------------------------- ### Initialize and Run TestSuite Source: https://github.com/libdns/libdns/blob/master/_autodocs/04-testing.md Standard implementation of a provider test function using NewTestSuite and RunTests. ```go import ( "testing" "github.com/libdns/libdns/libdnstest" ) func TestMyProvider(t *testing.T) { provider := &MyProvider{...} suite := libdnstest.NewTestSuite(provider, "example.com.") suite.RunTests(t) } ``` -------------------------------- ### Implement context-aware operations Source: https://github.com/libdns/libdns/blob/master/_autodocs/09-architecture.md Demonstrates how to use context.Context to manage cancellation and deadlines for long-running operations. ```go // Long-running operation can be cancelled ctx, cancel := context.WithCancel(context.Background()) go func() { time.Sleep(5 * time.Second) cancel() // Cancel after 5 seconds }() records, err := provider.GetRecords(ctx, zone) ``` -------------------------------- ### Test Suite Initialization and Execution Source: https://github.com/libdns/libdns/blob/master/_autodocs/00-index.md Utilities for running provider tests within the libdnstest package. ```go type TestSuite struct func NewTestSuite(provider Provider, zone string) *TestSuite func (ts *TestSuite) RunTests(t *testing.T) func (ts *TestSuite) AttemptZoneCleanup() error ``` -------------------------------- ### Extending libdns with new record types Source: https://github.com/libdns/libdns/blob/master/_autodocs/09-architecture.md Demonstrates how to implement a new record type while maintaining compatibility with existing interfaces. ```go // Future DNS record type added type TLSA struct { Name string TTL time.Duration // ... fields } func (t TLSA) RR() RR { ... } // Existing code still works: record.RR() // Still works record.RR().Parse() // Parses to new TLSA type ``` -------------------------------- ### View project file organization Source: https://github.com/libdns/libdns/blob/master/_autodocs/README.md A directory structure overview of the libdns documentation files. ```text 00-index.md Overview & navigation 01-interfaces.md Core interfaces 02-record-types.md DNS record types 03-utility-functions.md Helper functions 04-testing.md Testing framework 05-implementation-guide.md Provider implementation 06-common-patterns.md Usage examples 07-special-cases.md Edge cases 08-quick-reference.md Condensed reference 09-architecture.md Design & architecture README.md This file ``` -------------------------------- ### Go Provider Documentation Template Source: https://github.com/libdns/libdns/blob/master/_autodocs/05-implementation-guide.md A standard Go package documentation template for libdns providers, detailing usage, atomicity, DNSSEC, rate limiting, and concurrency. ```go /* Package mypackage implements the libdns interfaces for MyDNS provider. # Usage provider := &Provider{ APIKey: "your-api-key", } records, err := provider.GetRecords(ctx, "example.com.") # Atomicity SetRecords operations are atomic; if an error occurs, the zone is unchanged. # DNSSEC GetRecords does not include DNSSEC-related records. These can be managed via the provider's web interface. # Rate Limiting The provider implements automatic retry with exponential backoff for 429 and 5xx errors. Retries are limited to 3 attempts. # Concurrency All methods are safe for concurrent use. */ package mypackage ``` -------------------------------- ### AttemptZoneCleanup() Source: https://github.com/libdns/libdns/blob/master/_autodocs/04-testing.md Deletes all test records (names starting with 'test-') from the zone. This method is called automatically before tests and can be invoked manually. ```APIDOC ## AttemptZoneCleanup() ### Description Deletes all test records (names starting with "test-") from the zone. This method is called automatically before tests and can be invoked manually. ### Signature `func (ts *TestSuite) AttemptZoneCleanup() error` ### Returns - **error** (error) - Non-nil if cleanup fails. ### Cleanup Rules - Deletes records with names starting with "test-". - For SRV/SVCB records, strips up to two `_xxx.` prefixes before checking name. - Respects SkipRRTypes configuration. - Returns nil if no test records found. ``` -------------------------------- ### Module Structure Overview Source: https://github.com/libdns/libdns/blob/master/_autodocs/00-index.md Visual representation of the libdns repository layout. ```text github.com/libdns/libdns/ ├── libdns.go # Core interfaces and utilities ├── record.go # Record interface and RR type ├── rrtypes.go # Specific record types (Address, CNAME, etc.) ├── libdnstest/ │ └── libdnstest.go # Testing utilities └── go.mod # Module declaration (Go 1.23+) ``` -------------------------------- ### Implement Custom Provider Source: https://github.com/libdns/libdns/blob/master/_autodocs/00-index.md Define a struct and implement the required interface methods to create a custom DNS provider. Ensure all necessary record manipulation methods are defined. ```go type MyProvider struct { apiKey string } func (p *MyProvider) GetRecords(ctx context.Context, zone string) ([]libdns.Record, error) { // Call API, convert to libdns types return records, nil } // Implement AppendRecords, SetRecords, DeleteRecords, optionally ListZones ``` -------------------------------- ### Import libdnstest Source: https://github.com/libdns/libdns/blob/master/_autodocs/04-testing.md Import the libdnstest package to access testing utilities. ```go import "github.com/libdns/libdns/libdnstest" ``` -------------------------------- ### Construct SRV Record Names Source: https://github.com/libdns/libdns/blob/master/_autodocs/05-implementation-guide.md Demonstrates the mapping between libdns.SRV struct fields and the resulting absolute DNS name format. ```go // Input: libdns.SRV srv := libdns.SRV{ Service: "xmpp", Transport: "tcp", Name: "example.com.", Priority: 10, Weight: 20, Port: 5222, Target: "chat.example.com.", } // Converted to absolute name: // _xmpp._tcp.example.com. // When parsing from provider, extract components: // _xmpp._tcp.example.com. → // Service: "xmpp" (underscore removed) // Transport: "tcp" (underscore removed) // Name: "example.com." ``` -------------------------------- ### Convert DNS Names Source: https://github.com/libdns/libdns/blob/master/_autodocs/06-common-patterns.md Demonstrates converting between relative and absolute DNS names, including handling of zone roots. ```go func nameConversionExamples(zone string) { // Convert to absolute names for external systems rel := "www" abs := libdns.AbsoluteName(rel, zone) fmt.Printf("Relative '%s' → Absolute '%s'\n", rel, abs) // Convert from absolute back to relative back := libdns.RelativeName(abs, zone) fmt.Printf("Absolute '%s' → Relative '%s'\n", abs, back) // Zone root handling root := libdns.AbsoluteName("@", zone) fmt.Printf("Zone root: %s\n", root) // Empty string equals "@" also_root := libdns.AbsoluteName("", zone) fmt.Printf("Empty string as root: %s\n", also_root) } ``` -------------------------------- ### Project File Organization Source: https://github.com/libdns/libdns/blob/master/_autodocs/MANIFEST.md Displays the directory structure of the generated documentation output. ```text /workspace/home/output/ ├── 00-index.md (Start here) ├── 01-interfaces.md (Core API) ├── 02-record-types.md (Record types) ├── 03-utility-functions.md (Helpers) ├── 04-testing.md (Test framework) ├── 05-implementation-guide.md (Provider implementation) ├── 06-common-patterns.md (Usage patterns) ├── 07-special-cases.md (Edge cases) ├── 08-quick-reference.md (Quick lookup) ├── 09-architecture.md (Design & philosophy) ├── README.md (Overview) ├── COMPLETION_SUMMARY.txt (Project report) └── MANIFEST.md (This file) ``` -------------------------------- ### Initialize and Validate a libdns Provider Source: https://github.com/libdns/libdns/blob/master/_autodocs/06-common-patterns.md Initializes a provider using an environment variable and performs a connection test with a timeout. Ensure the DNS_API_TOKEN environment variable is set before calling this function. ```go func initializeProvider() (libdns.RecordGetter, error) { apiToken := os.Getenv("DNS_API_TOKEN") if apiToken == "" { return nil, fmt.Errorf("DNS_API_TOKEN environment variable not set") } provider := &mydns.Provider{ APIToken: apiToken, } // Test connection ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if _, err := provider.GetRecords(ctx, "example.com."); err != nil { return nil, fmt.Errorf("failed to connect to DNS provider: %w", err) } return provider, nil } ``` -------------------------------- ### Implement a Custom DNS Provider Source: https://github.com/libdns/libdns/blob/master/_autodocs/00-index.md Boilerplate for creating a new provider by implementing the required interfaces. ```go import "github.com/libdns/libdns" type MyProvider struct { // Your provider fields } func (p *MyProvider) GetRecords(ctx context.Context, zone string) ([]libdns.Record, error) { // Implementation } // Implement other interfaces as needed ``` -------------------------------- ### Define Alias and Non-Alias ServiceBinding Records Source: https://github.com/libdns/libdns/blob/master/_autodocs/07-special-cases.md Demonstrates the structural difference between Alias mode (Priority 0) and standard ServiceBinding records. ```go // Alias record (like CNAME but allowed at zone root) sb := libdns.ServiceBinding{ Scheme: "https", Name: "@", // Allowed at zone root! Priority: 0, // Indicates Alias mode Target: "example.com.", // Points to another name Params: libdns.SvcParams{}, // Must be empty in Alias mode } // Non-alias record sb2 := libdns.ServiceBinding{ Scheme: "https", Name: "api", Priority: 1, // Non-zero = not Alias mode Target: "cdn.example.com.", Params: libdns.SvcParams{"alpn": {"h2", "h3"}}, } ``` -------------------------------- ### Parse Methods for Records and Parameters Source: https://github.com/libdns/libdns/blob/master/_autodocs/00-index.md Methods for parsing resource records and service parameters. ```go func (r RR) RR() RR func (r RR) Parse() (Record, error) func (params SvcParams) String() string func ParseSvcParams(input string) (SvcParams, error) ``` -------------------------------- ### Manage SvcParams Source: https://github.com/libdns/libdns/blob/master/_autodocs/08-quick-reference.md Create, serialize, and parse SvcParams for DNS service records. ```go // Create params params := libdns.SvcParams{ "alpn": {"h2", "h3"}, "ipv4hint": {"192.0.2.1"}, } // Serialize formatted := params.String() // Output: "alpn=h2,h3 ipv4hint=192.0.2.1" // Parse parsed, _ := libdns.ParseSvcParams(formatted) ``` -------------------------------- ### Run Provider Tests Source: https://github.com/libdns/libdns/blob/master/_autodocs/00-index.md Utilize the libdnstest suite to validate provider implementations. ```go import "github.com/libdns/libdns/libdnstest" suite := libdnstest.NewTestSuite(myProvider, "test.example.com.") suite.RunTests(t) ``` -------------------------------- ### Parse ServiceBinding Name Fields in Go Source: https://github.com/libdns/libdns/blob/master/_autodocs/03-utility-functions.md Demonstrates how ServiceBinding records are structured after parsing port and scheme prefixes from the name field. ```go // Parsed from: _443._https.api.example.com sb := libdns.ServiceBinding{ Name: "api.example.com.", URLSchemePort: 443, Scheme: "https", Priority: 1, Target: "cdn.example.com.", } // Parsed from: _dns.recursive.example.com sb = libdns.ServiceBinding{ Name: "recursive.example.com.", Scheme: "dns", Priority: 1, Target: "ns.example.com.", } ``` -------------------------------- ### List DNS zones with ZoneLister Source: https://github.com/libdns/libdns/blob/master/_autodocs/00-index.md Use this optional interface to retrieve a list of available DNS zones. ```go ListZones(ctx context.Context) ([]Zone, error) ``` -------------------------------- ### Create CAA Records Source: https://github.com/libdns/libdns/blob/master/_autodocs/02-record-types.md Demonstrates initializing a slice of CAA records and appending them using a provider. ```go records := []libdns.Record{ libdns.CAA{ Name: "@", TTL: 3600 * time.Second, Flags: 0, Tag: "issue", Value: "letsencrypt.org", }, libdns.CAA{ Name: "@", TTL: 3600 * time.Second, Flags: 0, Tag: "iodef", Value: "mailto:security@example.com", }, } created, _ := provider.AppendRecords(ctx, "example.com.", records) ``` -------------------------------- ### Create CNAME Record in Go Source: https://github.com/libdns/libdns/blob/master/_autodocs/06-common-patterns.md Creates a single CNAME record pointing a subdomain to a target host. ```go func createCNAME(ctx context.Context, provider libdns.RecordSetter, zone string, name string, target string) error { record := libdns.CNAME{ Name: name, TTL: 3600 * time.Second, Target: target, } _, err := provider.SetRecords(ctx, zone, []libdns.Record{record}) return err } ``` -------------------------------- ### Provider Interfaces Source: https://github.com/libdns/libdns/blob/master/_autodocs/00-index.md Definitions for the core provider interfaces. ```go type Provider interface // All 5 interfaces + ZoneLister type RecordProvider interface // 4 record interfaces (no ZoneLister) ``` -------------------------------- ### Implement Context with Timeout Source: https://github.com/libdns/libdns/blob/master/_autodocs/06-common-patterns.md Wrap provider operations with a context timeout to prevent hanging requests. ```go func operationWithTimeout(provider libdns.RecordGetter, zone string, timeout time.Duration) ([]libdns.Record, error) { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() return provider.GetRecords(ctx, zone) } // Usage: records, err := operationWithTimeout(provider, "example.com.", 10*time.Second) if err != nil { log.Printf("Operation timed out or failed: %v", err) } ``` -------------------------------- ### Implement a DNS provider Source: https://github.com/libdns/libdns/blob/master/_autodocs/README.md Define a struct that implements the required interface methods, such as GetRecords, to integrate a new DNS provider. ```go type MyProvider struct { /* ... */ } func (p *MyProvider) GetRecords(ctx context.Context, zone string) ([]libdns.Record, error) { // Implementation } ``` -------------------------------- ### Test a real DNS provider with libdnstest Source: https://github.com/libdns/libdns/blob/master/_autodocs/04-testing.md Uses a dedicated test zone and requires environment variables for API authentication. Ensure tests run sequentially and clean up records after execution. ```go func TestCloudflareProvider(t *testing.T) { if testing.Short() { t.Skip("Skipping integration test") } provider := &cloudflare.Provider{ APIToken: os.Getenv("CLOUDFLARE_API_TOKEN"), } suite := libdnstest.NewTestSuite(provider, "test.example.com.") suite.ExpectEmptyZone = true suite.RunTests(t) } ``` -------------------------------- ### RunTests Source: https://github.com/libdns/libdns/blob/master/_autodocs/04-testing.md Executes the full suite of tests against the configured provider. ```APIDOC ## RunTests(t *testing.T) ### Description Executes all tests against the provider sequentially. It validates interfaces including ListZones, GetRecords, AppendRecords, SetRecords, and DeleteRecords. ### Parameters - **t** (*testing.T) - Required - The standard Go testing context. ``` -------------------------------- ### Record Deletion Methods Source: https://github.com/libdns/libdns/blob/master/_autodocs/07-special-cases.md Demonstrates the difference between using generic RR types and structured, type-safe address types for record deletion. ```go // Using RR (generic) deleteRec := libdns.RR{ Name: "test", Type: "A", TTL: 0, // Wildcard Data: "", // Wildcard } // Using structured type (type-safe) deleteAddr := libdns.Address{ Name: "test", TTL: 0, IP: netip.Addr{}, // Zero value; some treat as wildcard } ``` -------------------------------- ### DNSSEC Provider Implementation Notes Source: https://github.com/libdns/libdns/blob/master/_autodocs/07-special-cases.md Providers handle DNSSEC records inconsistently; always consult specific provider documentation for support details. ```go // A provider might: // 1. Never include DNSSEC in GetRecords // 2. Always include DNSSEC in GetRecords // 3. Include DNSSEC in GetRecords but not support setting it // The decision for SetRecords is independent: // - Some providers reject DNSSEC records in SetRecords // - Some providers silently ignore them // - Some manage DNSSEC separately // Always check provider documentation ``` -------------------------------- ### Set HTTPS Service Binding Records in Go Source: https://github.com/libdns/libdns/blob/master/_autodocs/06-common-patterns.md Configures HTTPS service binding records with specific parameters like ALPN and IP hints. ```go func setupHTTPSRecords(ctx context.Context, provider libdns.RecordSetter, zone string) error { records := []libdns.Record{ libdns.ServiceBinding{ Name: "@", TTL: 3600 * time.Second, Scheme: "https", Priority: 1, Target: "cdn.example.com.", Params: libdns.SvcParams{ "alpn": {"h2", "h3"}, "ipv4hint": {"192.0.2.1", "192.0.2.2"}, "ipv6hint": {"2001:db8::1"}, }, }, } _, err := provider.SetRecords(ctx, zone, records) return err } ``` -------------------------------- ### NewTestSuite Source: https://github.com/libdns/libdns/blob/master/_autodocs/04-testing.md Creates a new test suite instance for a specific libdns provider and zone. ```APIDOC ## NewTestSuite(provider Provider, zone string) *TestSuite ### Description Initializes a new test suite for a libdns provider. The suite is configured with a default timeout of 30 seconds. ### Parameters - **provider** (Provider) - Required - Implementation of all libdns interfaces to test. - **zone** (string) - Required - Test zone name with trailing dot (e.g., "example.com."). ### Returns - **suite** (*TestSuite) - Configured test suite instance. ``` -------------------------------- ### CNAME AppendRecords Usage Source: https://github.com/libdns/libdns/blob/master/_autodocs/07-special-cases.md Compares the problematic use of AppendRecords with CNAMEs against the recommended SetRecords approach. ```go // ✗ Not recommended with AppendRecords // If no "www" exists: creates CNAME (works like SetRecords) // If "www" exists with other records: fails or leaves zone invalid records := []libdns.Record{ libdns.CNAME{Name: "www", Target: "example.com."}, } _, err := provider.AppendRecords(ctx, zone, records) ``` ```go // ✓ Correct approach records := []libdns.Record{ libdns.CNAME{Name: "www", Target: "example.com."}, } _, err := provider.SetRecords(ctx, zone, records) // All other records at "www" are removed before CNAME is set ``` -------------------------------- ### Test an in-memory provider with libdnstest Source: https://github.com/libdns/libdns/blob/master/_autodocs/04-testing.md Suitable for parallel execution as it lacks external dependencies and rate limits. ```go func TestMemoryProvider(t *testing.T) { provider := memory.NewProvider() // In-memory provider suite := libdnstest.NewTestSuite(provider, "example.com.") suite.ExpectEmptyZone = true suite.RunTests(t) } ``` -------------------------------- ### Provider Name Conversion Source: https://github.com/libdns/libdns/blob/master/_autodocs/09-architecture.md Demonstrates how a provider implementation converts relative names to absolute names before interacting with an external API. ```go // Inside provider implementation func (p *Provider) AppendRecords(ctx context.Context, zone string, recs []Record) ([]Record, error) { for _, rec := range recs { rr := rec.RR() // Convert relative to absolute for API absoluteName := libdns.AbsoluteName(rr.Name, zone) // Call provider API with absolute name p.apiCreateRecord(ctx, absoluteName, rr.Type, rr.Data) } } ``` -------------------------------- ### Enable ExpectEmptyZone Source: https://github.com/libdns/libdns/blob/master/_autodocs/04-testing.md Configures the suite to verify that only default records remain in the zone after tests finish. ```go suite.ExpectEmptyZone = true ``` -------------------------------- ### Create SRV Records in Go Source: https://github.com/libdns/libdns/blob/master/_autodocs/06-common-patterns.md Demonstrates appending an SRV record for a service using the RecordAppender interface. ```go func createSRVRecord(ctx context.Context, provider libdns.RecordAppender, zone string) error { record := libdns.SRV{ Service: "xmpp", Transport: "tcp", Name: "@", TTL: 3600 * time.Second, Priority: 10, Weight: 20, Port: 5222, Target: "chat.example.com.", } _, err := provider.AppendRecords(ctx, zone, []libdns.Record{record}) return err } ``` -------------------------------- ### Import libdns packages Source: https://github.com/libdns/libdns/blob/master/_autodocs/08-quick-reference.md Required imports for utilizing the libdns library and testing utilities. ```go import ( "github.com/libdns/libdns" "github.com/libdns/libdns/libdnstest" ) ``` -------------------------------- ### Set Email Security Records in Go Source: https://github.com/libdns/libdns/blob/master/_autodocs/06-common-patterns.md Configures SPF, DMARC, and DKIM TXT records for a domain using the libdns.RecordSetter interface. ```go func setupEmailRecords(ctx context.Context, provider libdns.RecordSetter, zone string) error { records := []libdns.Record{ libdns.TXT{ Name: "@", TTL: 3600 * time.Second, Text: "v=spf1 include:_spf.google.com ~all", }, libdns.TXT{ Name: "_dmarc", TTL: 3600 * time.Second, Text: "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com", }, libdns.TXT{ Name: "default._domainkey", TTL: 3600 * time.Second, Text: "v=DKIM1; k=rsa; p=MIGfMA0BgkqhkiG...", }, } _, err := provider.SetRecords(ctx, zone, records) return err } ``` -------------------------------- ### Define Interface Segregation Source: https://github.com/libdns/libdns/blob/master/_autodocs/09-architecture.md Demonstrates the use of small, focused interfaces to allow providers to implement only the operations they support. ```go // ✓ libdns approach: Small, focused interfaces type RecordGetter interface { GetRecords(ctx context.Context, zone string) ([]Record, error) } ``` -------------------------------- ### Implement SetRecords logic Source: https://github.com/libdns/libdns/blob/master/_autodocs/09-architecture.md Provides a reference implementation for the SetRecords method, ensuring only input records exist for specific name/type pairs. ```go func SetRecords(ctx context.Context, zone string, recs []Record) ([]Record, error) { // 1. Identify input (name, type) pairs inputPairs := extractPairs(recs) // 2. Get existing records existing, _ := GetRecords(ctx, zone) // 3. Find records to delete toDelete := findRecordsMatching(existing, inputPairs) // 4. Delete old, append new (atomic if possible) DeleteRecords(ctx, zone, toDelete) return AppendRecords(ctx, zone, recs) } ``` -------------------------------- ### Implement Context Cancellation in Provider Methods Source: https://github.com/libdns/libdns/blob/master/_autodocs/05-implementation-guide.md Ensure methods honor context cancellation by checking ctx.Done() before and between operations. ```go func (p *MyProvider) GetRecords(ctx context.Context, zone string) ([]libdns.Record, error) { // Check context before making API calls select { case <-ctx.Done(): return nil, ctx.Err() default: } // Perform operation // Check context between operations select { case <-ctx.Done(): return nil, ctx.Err() default: } return records, nil } ``` -------------------------------- ### Set MX Records in Go Source: https://github.com/libdns/libdns/blob/master/_autodocs/06-common-patterns.md Configures multiple MX records with different preferences for mail routing. ```go func setupMXRecords(ctx context.Context, provider libdns.RecordSetter, zone string) error { records := []libdns.Record{ libdns.MX{ Name: "@", TTL: 3600 * time.Second, Preference: 10, Target: "mail.example.com.", }, libdns.MX{ Name: "@", TTL: 3600 * time.Second, Preference: 20, Target: "mail2.example.com.", }, } _, err := provider.SetRecords(ctx, zone, records) return err } ``` -------------------------------- ### Name Conversion Source: https://github.com/libdns/libdns/blob/master/_autodocs/08-quick-reference.md Utilities for converting between relative and absolute domain names. ```go // Relative to Absolute absolute := libdns.AbsoluteName("www", "example.com.") // Returns: "www.example.com." // Absolute to Relative relative := libdns.RelativeName("www.example.com.", "example.com.") // Returns: "www" // Zone root root := libdns.AbsoluteName("@", "example.com.") // Returns: "example.com." ``` -------------------------------- ### Construct ServiceBinding Record Names Source: https://github.com/libdns/libdns/blob/master/_autodocs/05-implementation-guide.md Shows how Scheme and URLSchemePort fields influence the generated record name for ServiceBinding records. ```go // HTTPS record (default scheme) sb := libdns.ServiceBinding{ Scheme: "https", URLSchemePort: 0, // Not needed for default port Name: "example.com.", Priority: 1, Target: "cdn.example.com.", } // Converts to: example.com. (no prefix for default HTTPS) // HTTPS on non-default port sb = libdns.ServiceBinding{ Scheme: "https", URLSchemePort: 2443, Name: "example.com.", Priority: 1, Target: "cdn.example.com.", } // Converts to: _2443._https.example.com. // SVCB record with scheme sb = libdns.ServiceBinding{ Scheme: "dns", Name: "example.com.", Priority: 1, Target: "dns.example.com.", } // Converts to: _dns.example.com. ``` -------------------------------- ### Implement provider-specific error handling Source: https://github.com/libdns/libdns/blob/master/_autodocs/05-implementation-guide.md Provide descriptive errors for API-specific failures such as missing zones or rate limiting. ```go func (p *MyProvider) AppendRecords(ctx context.Context, zone string, recs []libdns.Record) ([]libdns.Record, error) { // Check if zone exists if exists, err := p.apiZoneExists(ctx, zone); err != nil { return nil, fmt.Errorf("failed to verify zone: %w", err) } else if !exists { return nil, fmt.Errorf("zone %q not found in account", zone) } // Check API rate limits if limited, retryAfter := p.apiIsRateLimited(); limited { return nil, fmt.Errorf("rate limited; retry after %v", retryAfter) } // ... rest of implementation } ``` -------------------------------- ### Retrieve DNS records as an application developer Source: https://github.com/libdns/libdns/blob/master/_autodocs/README.md Use the GetRecords method to fetch DNS records for a specific zone. Ensure the provider is initialized before calling this method. ```go import "github.com/libdns/libdns" provider := yourDNSProvider() records, err := provider.GetRecords(ctx, "example.com.") ``` -------------------------------- ### Serialize SvcParams Source: https://github.com/libdns/libdns/blob/master/_autodocs/02-record-types.md Converts SvcParams into zone presentation format. ```go params := libdns.SvcParams{ "alpn": {"h2", "h3"}, "ipv4hint": {"192.0.2.1"}, } formatted := params.String() // Output: alpn=h2,h3 ipv4hint=192.0.2.1 ``` -------------------------------- ### TestSuite Definition Source: https://github.com/libdns/libdns/blob/master/_autodocs/04-testing.md Structure definition and constructor for the libdns provider test suite. ```go type TestSuite struct { provider Provider zone string Timeout time.Duration AppendRecordFunc func(record libdns.Record) libdns.Record SkipRRTypes map[string]bool ExpectEmptyZone bool } func NewTestSuite(provider Provider, zone string) *TestSuite func (ts *TestSuite) RunTests(t *testing.T) ```