lego/providers/dns/vkcloud/internal/client.go
Andrew Kluev 8fe27e0cc3
Add DNS provider for VK Cloud (#1706)
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
2022-09-02 22:56:10 +02:00

160 lines
3.6 KiB
Go

package internal
import (
"errors"
"fmt"
"net/http"
"net/url"
"path"
"strings"
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack"
)
// Client VK client.
type Client struct {
baseURL *url.URL
openstack *gophercloud.ProviderClient
authOpts gophercloud.AuthOptions
authenticated bool
}
// NewClient creates a Client.
func NewClient(endpoint string, authOpts gophercloud.AuthOptions) (*Client, error) {
err := validateAuthOptions(authOpts)
if err != nil {
return nil, err
}
openstackClient, err := openstack.NewClient(authOpts.IdentityEndpoint)
if err != nil {
return nil, fmt.Errorf("new client: %w", err)
}
baseURL, err := url.Parse(endpoint)
if err != nil {
return nil, fmt.Errorf("parse URL: %w", err)
}
return &Client{
baseURL: baseURL,
openstack: openstackClient,
authOpts: authOpts,
}, nil
}
func (c *Client) ListZones() ([]DNSZone, error) {
var zones []DNSZone
opts := &gophercloud.RequestOpts{JSONResponse: &zones}
// TODO(ldez): go1.19 => c.baseURL.JoinPath("/")
endpoint := joinPath(c.baseURL, "/")
err := c.request(http.MethodGet, endpoint, opts)
if err != nil {
return nil, err
}
return zones, nil
}
func (c *Client) ListTXTRecords(zoneUUID string) ([]DNSTXTRecord, error) {
var records []DNSTXTRecord
opts := &gophercloud.RequestOpts{JSONResponse: &records}
// TODO(ldez): go1.19 => c.baseURL.JoinPath(zoneUUID, "txt", "/")
endpoint := joinPath(c.baseURL, zoneUUID, "txt", "/")
err := c.request(http.MethodGet, endpoint, opts)
if err != nil {
return nil, err
}
return records, nil
}
func (c *Client) CreateTXTRecord(zoneUUID string, record *DNSTXTRecord) error {
opts := &gophercloud.RequestOpts{
JSONBody: record,
JSONResponse: record,
}
// TODO(ldez): go1.19 => c.baseURL.JoinPath(zoneUUID, "txt", "/")
endpoint := joinPath(c.baseURL, zoneUUID, "txt", "/")
return c.request(http.MethodPost, endpoint, opts)
}
func (c *Client) DeleteTXTRecord(zoneUUID, recordUUID string) error {
// TODO(ldez): go1.19 => c.baseURL.JoinPath(zoneUUID, "txt", recordUUID)
endpoint := joinPath(c.baseURL, zoneUUID, "txt", recordUUID)
return c.request(http.MethodDelete, endpoint, &gophercloud.RequestOpts{})
}
func (c *Client) request(method string, endpoint *url.URL, options *gophercloud.RequestOpts) error {
if err := c.lazyAuth(); err != nil {
return fmt.Errorf("auth: %w", err)
}
_, err := c.openstack.Request(method, endpoint.String(), options)
if err != nil {
return fmt.Errorf("request: %w", err)
}
return nil
}
func (c *Client) lazyAuth() error {
if c.authenticated {
return nil
}
err := openstack.Authenticate(c.openstack, c.authOpts)
if err != nil {
return err
}
c.authenticated = true
return nil
}
func validateAuthOptions(opts gophercloud.AuthOptions) error {
if opts.TenantID == "" {
return errors.New("project id is missing in credentials information")
}
if opts.Username == "" {
return errors.New("username is missing in credentials information")
}
if opts.Password == "" {
return errors.New("password is missing in credentials information")
}
if opts.IdentityEndpoint == "" {
return errors.New("identity endpoint is missing in config")
}
if opts.DomainName == "" {
return errors.New("domain name is missing in config")
}
return nil
}
// light version of go1.19 url.URL.JoinPath.
// TODO(ldez): must be remove when we will update to go1.19.
func joinPath(uri *url.URL, elem ...string) *url.URL {
result := path.Join(elem...)
result = path.Join(uri.Path, result)
if len(elem) > 0 && strings.HasSuffix(elem[len(elem)-1], "/") {
result += "/"
}
parse, _ := uri.Parse(result)
return parse
}