Repository navigation
feat(p/MISAKA): add Misaka.io DNS provider - #4994
Conversation
Add a DNS provider for Misaka.io Managed DNS (v3 API): record-set corrections via diff2.ByRecordSet, zone listing and creation, apex NS and SOA left to Misaka.io, record sets with unmodelled features (GeoDNS filters, alias, disabled, protected, empty, @ hostnames, non-canonical names) refused on read, TTL audit, golden conversion tests, and a single retry of non-POST requests whose connection is dropped mid-request (observed during integration runs).
TomOnTime
left a comment
There was a problem hiding this comment.
Looks good so far! Just a few requests.
| // Features retains annotations for record types and interface-derived facts. | ||
| Features: providers.DocumentationNotes{ | ||
| providers.CanUseSOA: providers.Cannot("The SOA record is managed by Misaka.io (zone settings)"), | ||
| }, |
There was a problem hiding this comment.
Remove Features. Add to SupportedTypes "SOA:Cannot:Managed by Misaka.io"
| // rejectTTLAboveMax rejects a TTL the API would refuse, instead of quietly | ||
| // lowering it to one the user did not ask for. | ||
| func rejectTTLAboveMax(rc *models.RecordConfig) error { | ||
| if rc.TTL > maxTTL { | ||
| return fmt.Errorf("%s %s: ttl %d is larger than %d, the maximum Misaka.io accepts", rc.GetLabelFQDN(), rc.Type, rc.TTL, maxTTL) | ||
| } | ||
| return nil | ||
| } |
There was a problem hiding this comment.
Instead of rejecting these records, silently reduce the TTL in toNative().
| if rs.Name != strings.ToLower(rs.Name) || strings.HasSuffix(rs.Name, ".") { | ||
| return fail("has a name that is not lowercase or ends with a dot; rename it in the Misaka.io console") | ||
| } |
There was a problem hiding this comment.
Rather than rejecting names that are not lowercase, do one of two things:
- Downcase them. Allow the next
pushto update the record. - Record the original case in RecordConfig.Original for use when the original case is needed.
Resolve the semantic conflicts with DNSControl#4995 and DNSControl#4996: use the renamed CanDualHost and OfficiallySupported fields, and declare SOA as "SOA:Cannot:Managed by Misaka.io" in SupportedTypes instead of the Features note that built-in providers may no longer use (also requested in the review).
…them A TTL above 2147483647 is now lowered to that maximum instead of failing validation. The desired records are adjusted before the diff, as Linode and Domainnameshop do, so the stored TTL matches the desired one and later runs do not offer the same change again.
Misaka.io stores record-set names verbatim and matches them case-sensitively, so a set named "Www" cannot be changed through "www". Instead of refusing such a zone, read the name lowercased as usual, keep the native record set in RecordConfig.Original, and change or delete the set under its original name.
…rigin response A regression run hit a DELETE answered with 502 "The origin web server returned an invalid or incomplete response to Cloudflare". It is the connection drop already handled for "unexpected EOF", seen from the other side of Cloudflare, so it gets the same single retry of non-POST requests. Other 5xx statuses are still not retried.
Under the integration workload the API drops responses more often than first observed: one regression run saw seven drops, including a GET whose retry was dropped as well. A GET, PUT or DELETE is now sent up to three times after a dropped connection or a Cloudflare 502. A POST is still never repeated, because the first one may have been applied; the next push creates whatever is still missing.
|
@TomOnTime Thanks for the review! The new I've resolved the conflicts and made the requested changes to the code. I also re-ran the necessary tests to make sure that the TTL clamping and case conversion both work as expected. providers/misaka/auditrecords.go - "Instead of rejecting these records, silently reduce the TTL in toNative()."I do this in providers/misaka/records.go - "Rather than rejecting names that are not lowercase, do one of two things ..."Went with option 2. Misaka.io matches record-set names case-sensitively: for a set |
|
Thank you for contributing to this new provider, @ZeroClover! A few action items before we can merge this PR:
When I see the Github invite accepted and receive the email, I’ll merge this PR. Thanks again! |
Regenerate the provider page for the renamed "Concurrent fetching" feature label (DNSControl#5002) and let go fix use errors.AsType, so that the "PR: Check git status" job finds nothing to change.
|
Thanks, @TomOnTime Invite accepted and Email sent to Faisal, with you in CC. I'm happy to donate a test account. I have a separate Misaka.io account for integration testing only and sent credentials via SendSafely. Also, I pushed one small follow-up commit (ebcca9b): after #5002 renamed the feature label, |
|
Great! Thanks so much! |
Adds a DNS provider for Misaka.io Managed DNS (
MISAKA), using their v3 API.Please create the GitHub label 'provider-MISAKA'
What it does
diff2.ByRecordSet: the API treats a record set ({name}/{type}) as one unit, so each change is a singlePOST(create),PUT(replace the whole set, including values kept byIGNORE()/NO_PURGE) orDELETE.Basic8,ALIAS,DNAME,PTR,SSHFP,TLSA.get-zones/ListZones, andcreate-domains/ auto-create onpush(EnsureZoneExists).SOAand apexNSare managed by Misaka.io. They are hidden on read. Desired apexNSrecords are dropped, with a warning if they don't match the zone's nameservers (same approach as NexDNS).GetNameserversreads the apexNSset and only warns when the zone doesn't exist yet, sopreviewof a new zone works.filtersaliasenabled: falseprotected@AuditRecordsrejects TTLs above 2147483647, the API's maximum.Rate limits and retries
unexpected EOF, with no HTTP status. This happened in three of the four runs, once each on a GET, a PUT and a DELETE.GET,PUTandDELETEare sent once more after 1 s, with a warning.POSTis never repeated.DELETEcounts as done.Testing
Integration tests
go test -v ./integrationTest -args -verbose -profile MISAKAagainst a dedicated test zone in a separate account: PASS.NS only APEX→not("MISAKA"), because apex NS records are managed by Misaka.io.*_not_supported) oronly(...)groups for other providers.pager101passes.pager601/pager1201are opt-in groups and were not enabled.Result:
PASSin 579.8 s - 252 test steps passed, 0 failed (the remaining groups are skipped as listed above).The connection-drop retry fired once during this run (on a
PUT) and recovered.Full output: https://gist.github.com/ZeroClover/ea219c72183312ddb3e3eec9947ce0a7
TXT procedure (
testing-txt-records.md)Records t0–t3 were created with the exact values a console user would type:
test0,test1,"test2","test3".previewreports changes for t1 and t3. t2 shows no change because Misaka.io publishes the quoted console value"test2"as the unquoted stringtest2, which already matchesTXT("t2", "test2"). This was confirmed withdig TXT t2.<zone> @a.m1ns.net, which returns"test2".After
push,previewis clean, and the authoritative answers equal the strings indnsconfig.js:"test0""\"test1\"""test2""\"test3\""Concurrency
go build -race, thendnscontrol preview --cmode allover six zones, twice. Both runs gathered all zones concurrently with no race report, so the provider declaresCanConcur: providers.Can().Other checks
bin/generate-all.sh: golangci-lint 0 issues, staticcheck clean, no changes to generated files.modernize -fix ./...: no changes.go vet ./...: clean.go test ./...: passes.Notes
MISAKA_DOMAIN,MISAKA_API_TOKEN).