-
-
Notifications
You must be signed in to change notification settings - Fork 74
All-new Record abstraction and exported APIs #153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Initial draft for v1.0 pre-releases. Follow-up from #152.
I've been updating the Cloudflare provider in a branch that uses this branch. I'm testing with a SRV record currently, since it's a more complicated record type. So far, the effects of this refactor look promising. Except... something that doesn't really have much to do with this PR... Since the Cloudflare documentation shows a "content" field and optional "data" components, I figured I can just pass in the content of the record as a single string and call it a day. (That's easy now with the But, unfortunately, either their docs are wrong or their API is wrong: https://community.cloudflare.com/t/creating-srv-record-with-content-string-instead-of-individual-component-fields/781178?u=mholt -- I am getting errors when omitting the individual data components, despite the docs saying they are optional. So, I'm hoping their API is just buggy and the docs are right, since otherwise I have to type-assert every single record type to use their API. Doable, and inevitably some providers will require this, but kinda lame. |
This comment was marked as resolved.
This comment was marked as resolved.
Co-authored-by: Max Chernoff <git@maxchernoff.ca>
@gucci-on-fleek Thanks for the thorough review! I believe I have addressed (pun intended?) all the comments, including combining A and AAAA into an Address struct, except for the matter of SVCB records. Real quick: the two downsides to combining into the Address type, that I can think of, are:
However, given that users don't really care what the size of the address is in Go code, I think at least that second downside is acceptable. The code automatically assigns the correct RR-type either way. As for SVCB records, I'm inclined to keep them separate since they have slightly different semantics around the name, compared to HTTPS records. Do you agree? |
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
This comment was marked as resolved.
This comment was marked as resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Matt & gucci-on-fleek, thanks for the awesome work! The Record/RR approach looks good.
I looked at the changes I need to make in dnsclay to work with this update, and it's very limited.
I added a few comments/questions in the PR, hope they help.
What about adding some test helpers to libdns/libdns, or subpackage, or other testing-module, that provider implementers can run on their provider? I think it would be like what is suggested in issue #44. I can write some of these tests. I imagine the tests would be run with a provider impelmentation, zone name, and "subzone" name, and the tests would get/add/set/delete records and see the changes are reflected properly, and check the names are well formed (relative names, "@" instead of empty name, nothing outside the zone). We could add an Options struct to influence behaviour to check for, such as skipping checks for whether exact TTLs are set (some DNS providers appear to only allow setting a small set of TTLs). The options can be expanded later on. We could check context cancellations, do concurrent requests and test with the race checker on. We would take care not to touch anything outside the subzone, but of course bugs in provider implementations could do the wrong thing... Hopefully implementers are in a position to create an isolated account/zone. |
Since a Record type MUST be able to make itself into an RR by definition in the documentation, doesn't really make sense if it can error when doing so.
@gucci-on-fleek Awesome, thanks for trying out the new APIs!
Oof, you're right, that does look a bit tedious. Is that kind of enumeration only going to happen in tests? If so, I think I'm okay with it. If it's going to be in production code, I wonder if we could assist with a common method that could be used with a type assertion, so it works with all those types with targets.
Yeah... also, equality is not obvious for structures like that. Hence why Go doesn't just support it already. So it's probably best if users implement their own equality logic. There are many ways to do it.
Rock on, good luck!! Thank you again -- looking forward to wrapping it up :) @mjl- Thanks for your feedback, and for testing out the new APIs!
Ah yeah, this -- and the rest of your comment -- would be great to discuss in either that issue or a new one, to give it all the attention it deserves. How does that sound? We could look at it after this PR is merged (although, I will be having my hands full getting all the providers notified and to try to update, etc, so it might be more you and other collaborators working on it). |
I'd certainly be in favour of that. I've added a few tests at libdns/rfc2136@4d49bd3, but nothing too extensive yet. I'd recommend looking at DNSControl's tests for inspiration since they've thought of most of the weird edge cases there already. (And if either of you want the credentials to a test zone on my DNS server, just email me and I'll happily send them over)
I think so? I mean, I guess if you were writing a “DNS Target Verifier” program that makes sure that all the targets in your zone point at valid domains this might come up, but that seems like a fairly contrived example.
The runtime panic might catch people by surprise though, especially since every other package main
import (
"fmt"
"github.com/libdns/libdns"
)
func main() {
var a libdns.Record = libdns.HTTPS{
Name: "test",
Value: libdns.SvcParams{
"key": []string{"value1", "value2"},
},
}
b := a
if a == b {
fmt.Println("equal")
} else {
fmt.Println("not equal")
}
} So maybe it might be better to instead define type HTTPS struct {
Name string
TTL time.Duration
Priority uint16
Target string
Value *SvcParams
} That should stop it from panicking, but I don't know enough about Go to know the greater implications to making a struct field a pointer.
Depending on the timeline of all of this, it might be good to have the tests ready before we notify all of the provider maintainers. Having tests would probably make it easier to port all the providers open, and it would be mildly frustrating to finish porting a provider and then see that all the tests are failing after. But porting |
Ok, I've pushed support for |
Working on updating https://github.com/libdns/domainnameshop for this and made good headway. There is a need for a unique identifier that can be kept track of, In the case of Domanname.shop the API returns an ID when you create a record and you need an ID to remove a record. Without an ID field in the |
See #153 (comment)
Yes, that's the only way, here's an example. |
That's a bit of hassle, but I've made it work and updated my module, got it working with Caddy which is my main focus. |
@JMyklebust Thanks for the feedback / update! Yeah, the ID field was a tricky one... At first it seemed obvious we should have it, but then there's some very subtle and potentially harmful outcomes of doing that, considering cross-zone and cross-provider compatibility... anyway you probably read the discussion and get the gist. Thanks for maintaining your package -- changes look good, at a quick glance 👍 See also #119 (comment) |
Initial draft for v1.0 pre-releases.
Follow-up from #152.
This is a breaking change, but most provider implementations will remain mostly the same. Most will need to search-replace some identifiers and, with time, conform to the clarified semantics in some cases.
This change is crucial to solidify APIs for a permanent v1 release in the future, and to reduce the likelihood of breaking changes after that.
/cc @gucci-on-fleek -- thanks for spearheading this. I've tried to maintain the clarified semantics from the earlier PR.
The main change is:
libdns.Record
is now an interface that simply returns anRR
.What used to be
Record
is nowRR
, and it is strictly a DNS record's main fields: Name, Type, TTL, and Data.There are also type-specific structs such as
A
,AAAA
,CNAME
,SRV
, etc., that implementlibdns.Record
.To convert from
RR
to a specific type, there'sRR.Parse()
. To go the other way (from specific type toRR
), there's theRR()
method on every type (implementing theRecord
interface).Since the main APIs (Get/Set/etc...) take the
Record
type, users have the option of using either RRs, or type-specific structs, in their application code, and provider packages can also choose which they prefer on their end.Once we finalize these changes, we can attempt to notify provider package maintainers of the upcoming changes in the hopes for a stable API with 1.0 on the horizon. Since most packages are small, this shouldn't be too difficult. Also, many of the semantics are quite nuanced and some only pertain in edge cases, so it's okay if the semantics are implemented by each provider package gradually over time.
The main uses of this package -- that I know of -- are appending and deleting TXT records, updating A/AAAA records, and setting HTTPS records. I think they're mostly straightforward use cases that shouldn't surprise most users and package maintainers, except it might expose bugs that were previously less obvious.
Hopefully this is in the right direction! Let me know what you think.