Skip to content

Liara provider - #16

Open
ParsaJR wants to merge 6 commits into
libdns:masterfrom
ParsaJR:master
Open

Liara provider#16
ParsaJR wants to merge 6 commits into
libdns:masterfrom
ParsaJR:master

Conversation

@ParsaJR

@ParsaJRParsaJR commented Aug 20, 2026

Copy link
Copy Markdown

Hello.

I've created a libdns implementation for an Iranian cloud provider named "Liara".

The E2E tests(from libdnstest module) are passed, using my own credentials. It can be tested by anyone who has access to the service. The readme page explains it.

Let me know if it has any issues. Thanks.

@ParsaJR

ParsaJR commented Aug 20, 2026

Copy link
Copy Markdown
Author

I have a question regarding on how does this library can integrate with Caddy web server.

libdns libraries in general have a struct named provider which is the main interface to the library. Example from cloudflare implementation:

typeProviderstruct {
APITokenstring`json:"api_token,omitempty"`ZoneTokenstring`json:"zone_token,omitempty"`
}

Now, It's not clear to me how Caddy can actually inject these values from the Caddyfile?

The related community post provides the following example snippet in Caddyfile:

{
acme_dns cloudflare 1484053787dJQB8vP1q0yc5ZEBnH6JGS4d3mBmvIeMrnnxFi3WtJdF
}

I don’t understand how does this value ends up in the APIToken field of the provider struct. I would expect some key-value configuration for this type of setup in the Caddyfile, but I haven’t been able to find it.

@mholt

mholt commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Thanks for the submission!

To answer your question, there's a thin package at https://github.com/caddy-dns that will translate between Caddyfile and libdns. You can see a template here: https://github.com/caddy-dns/template

(AI is probably good at generating these very simple packages!)

So, when we do admit this to the libdns organization, we'll also give you a caddy-dns repo to maintain.

Did you write this code yourself or use AI?

@ParsaJR

Copy link
Copy Markdown
Author

Hey. Thanks for the explanation.

Yeah, I forgot about that. I think I came across writing custom modules for Caddy while following the book "Network Programming with Go".

I've used AI in the process, but i didn't use it in a "vibe-coded" fashion. I've just got stuck in some places where i didn't familiar with, such as the SRV record types

@gucci-on-fleekgucci-on-fleek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution! I've added a few comments inline.

@@ -0,0 +1,7 @@
# Liara credentials for E2E testing

CLOUDFLARE_API_TOKEN=your-api-token-here

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is CLOUDFLARE_[…] a typo?

Comment threadclient.go
// its methods.
type client struct {
Liara_api_token string
BaseURL string

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if Liara ever adds a v2 API, I would assume that this module would only ever support a single version, so I don't think that there's much point to carrying this around in a struct.

Or alternatively, you could let users configure the API URL themselves, in which case it would make sense to include this in a struct.

Comment threadclient.go
// Client is a struct, that knows how to interact with the Liara's api thorugh
// its methods.
type client struct {
Liara_api_token string

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Go typically uses camelCase rather than snake_case for names. It's fine if you want to go against that convention, but you should at least be consistent within a single struct.

Comment threadclient.go

// Gets all the dns records in a specific zone.
func (c client) APIGetRecords(ctx context.Context, zone string) ([]APIRecord, error) {
endpoint := c.BaseURL + fmt.Sprintf("zones/%s/dns-records", zone)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is assuming that BaseURL will always end in a slash; it would be better to use url.JoinPath instead.

Comment threadclient.go
return nil, err
}

if resp.StatusCode < 200 || resp.StatusCode >= 300 {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should never see a status code in the 3XX range here, because net/http follows redirects by default.

Comment threadprovider.go
}

for _, record := range liaraRecords {
added, err := client.APIPostRecord(ctx, zone, record)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the Liara API allows it, it would be better to POST multiple records in a single request, since that would be much faster over a slow network. But if the API doesn't allow it, then this is totally fine.

Comment threadprovider.go
Comment on lines +118 to +120
existingLiara, yes := APIRecordContains(existingRecords, toRemoveLiara)

if !yes {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can probably write this on a single line: https://go.dev/tour/flowcontrol/6

Comment threadprovider.go
Comment on lines +126 to +131
remaining := RemainingContent(
existingLiara.Contents,
toRemoveLiara.Contents,
)

if len(remaining) == 0 {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's likely a simpler way of writing this (but I'm not familiar with the Liara API so I don't have anything specific to suggest).

Comment threadtypes.go

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could probably make most of the symbols defined in this file private, either by naming them with lowercase letters, or by putting the file in internal/.

Comment threadtypes.go
}

// Checks to see if two APIRecordContent structs are identical.
func (current APIRecordContent) IsEqual(target APIRecordContent) bool {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APIRecordContent only contains strings and uint16s, so I think that you can just use == directly.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@ParsaJR@mholt@gucci-on-fleek