Documentation ¶
Overview ¶
Package hoverdnsapi offers some API actions to control your Hover/TuCOWS DNS entries programmatically. This isn't a supported utility form Hover, it's just a thing I wrote because I needed it at the time.
Package hoverdnsapi offers some API actions to control your Hover/TuCOWS DNS entries programmatically. This isn't a supported utility form Hover, it's just a thing I wrote because I needed it at the time.
Index ¶
- Variables
- func APIURL(resource string) string
- func APIURLDNS(domainID string) string
- type Action
- type Address
- type Client
- func (c *Client) Delete(fqdn, domain string) error
- func (c *Client) DoActions(actions ...Action) (err error)
- func (c *Client) ExistingTXTRecords(fqdn string) error
- func (c *Client) FillDomains() error
- func (c *Client) GetAuth() (string, error)
- func (c *Client) GetCookie(key string) (value string, ok bool)
- func (c *Client) GetDomainByName(domainname string) (*Domain, bool)
- func (c *Client) GetDomainEntries(domain string) error
- func (c *Client) HTTPDelete(url string) (err error)
- func (c *Client) Upsert(fqdn, domain, value string, ttl uint) error
- type ContactBlock
- type Domain
- type DomainList
- type Entry
- type HoverAct
- type NopLogger
- type PlaintextAuth
- type User
- type YALI
Constants ¶
This section is empty.
Variables ¶
var ( // HoverAddress is a constant-ish var that I use to ensure that within my domains, the ones // I expect to have Hovers contact info (their default) do. For example, Tech Contacts // where I don't want to be that guy (for managed domains, they should be the tech // contact). Of course, if the values in this constant are incorrect, TuCows is the // authority, but please PR me a correction to help me maintain accuracy. HoverAddress = Address{ Status: "active", OrganizationName: "Hover, a service of Tucows.com Co", FirstName: "Support", LastName: "Contact", Address1: "96 Mowat Ave.", City: "Toronto", State: "ON", Zip: "M6K 3M1", Country: "CA", Phone: "+1.8667316556", Email: "[email protected]", } )
Functions ¶
Types ¶
type Action ¶
type Action struct {
// contains filtered or unexported fields
}
Action is a single action (Add, Update, Delete) to complete in a DoActions() call
type Address ¶
type Address struct { Status string `json:"status"` // Status seems to be "active" in all my zones OrganizationName string `json:"org_name"` // Name of Organization FirstName string `json:"first_name"` // First naem seems to be given non-family name, not positional LastName string `json:"last_name"` // Last Name seems to be family name, not positional Address1 string `json:"address1"` Address2 string `json:"address2"` Address3 string `json:"address3"` City string `json:"city"` State string `json:"state"` // State seems to be the US state or the Canadian province Zip string `json:"zip"` // 5-digit US (ie 10001) or 6-char slammed Canadian (V0H1X0 no space) Country string `json:"country"` // 2-leter state code; this seems to match the second (non-country) of a ISO-3166-2 code Phone string `json:"phone"` // phone format all over the map, but thy seem to write it as a ITU E164, but a "." separating country code and subscriber number Facsimile string `json:"fax"` // same format as phone Email string `json:"email"` // rfc2822 format email address such as rfc2822 para 3.4.1 }
Address holds an address used for admin, billing, or tech contact. Empirically, it seems at least US and Canada formats are squeezed into a US format. Please PR if you discover additional formats.
type Client ¶
type Client struct { HTTPClient *http.Client Username string Password string // contains filtered or unexported fields }
Client is the client context for communicating with Hover DNS API; should only need one of these but keeping state isolated to instances rather than global where possible.
func NewClient ¶
func NewClient(username, password, filename string, timeout time.Duration, opt ...interface{}) *Client
NewClient Creates a Hover client using plaintext passwords against plain username. Consider the risk of where the text is stored.
func (*Client) DoActions ¶
DoActions is a way to burn down an accumulated list of actions. Mostly, this stack will be one or two deep, but this offers the chance to go grab a GetAuth() (for authentication cookie) if needed, or a detailed DNS list if needed, in a sort of lazy-evaluation logic that avoid these actions if not needed.
func (*Client) ExistingTXTRecords ¶
ExistingTXTRecords checks whether the given TXT record exists; err != nil if not found
func (*Client) FillDomains ¶
FillDomains fills the list of domains allocated to the usernamr and password to the Domains structure. It will use GetAuth() to perform a login if necessary.
func (*Client) GetAuth ¶
GetAuth returns the authentication key for the username and password, performing a login if the key is not already known from a previous login.
func (*Client) GetCookie ¶
GetCookie searches existing cookies from a login to Hover's API to find the given cookie.
func (*Client) GetDomainByName ¶
GetDomainByName searches iteratively and returns the Domain record that has the given name
func (*Client) GetDomainEntries ¶
GetDomainEntries gets the entries for a specific domain -- essentially the zone records
func (*Client) HTTPDelete ¶
HTTPDelete actually does an HTTP call with the DELETE method. BOG-standard Go only offers GET and POST.
TODO: move to a separate file as a layer onto net/http
type ContactBlock ¶
type ContactBlock struct { Admin Address `json:"admin"` Billing Address `json:"billing"` Tech Address `json:"tech"` Owner Address `json:"owner"` }
ContactBlock is merely the four contact addresses that Hover uses, but it's easier to work with a defined type in static constants during testing
type Domain ¶
type Domain struct { ID string `json:"id"` // A unique opaque identifier defined by Hover DomainName string `json:"domain_name"` // the actual domain name. ie: "example.com" NumEmails int `json:"num_emails,omitempty"` // This appears to be the number of email accounts either permitted or defined for the domain RenewalDate string `json:"renewal_date,omitempty"` // This renewal date appears to be the first day of non-service after a purchased year of valid service: the first day offline if you don't renew. RFC3339/ISO8601 -formatted yyyy-mm-dd. DisplayDate string `json:"display_date"` // Display Date seems to be the same as Renewal Date but perhaps can allow for odd display corner-cases such as leap-years, leap-seconds, or timezones oddities. RFC3339/ISO8601 to granularity of day as well. RegisteredDate string `json:"registered_date,omitempty"` // Date the domain was first registered, which is likely also the first day of service (or partial-day, technically) RFC3339/ISO8601 to granularity of day as well. Active bool `json:"active,omitempty"` // Domain Entries also show which zones are active Contacts ContactBlock `json:"contacts"` Entries []Entry `json:"entries,omitempty"` // entries in a zone, if expanded HoverUser User `json:"hover_user,omitempty"` Glue struct{} `json:"glue,omitempty"` // I'm not sure how Hover records Glue Records here, or whether they're still used. Please PR a suggested format! NameServers []string `json:"nameservers,omitempty"` Locked bool `json:"locked,omitempty"` Renewable bool `json:"renewable,omitempty"` AutoRenew bool `json:"auto_renew,omitempty"` Status string `json:"status,omitempty"` // Status seems to be "active" in all my zones WhoisPrivacy bool `json:"whois_privacy,omitempty"` // boolean as lower-case string: keep your real address out of whois? }
Domain structure describes the config for an entire domain within Hover: the dates involved, contact addresses, nameservers, etc: it seems to cover everything about the domain in one structure, which is convenient when you want to compare data across many domains.
func (Domain) GetEntryByFQDN ¶
GetEntryByFQDN attempts to find a single Entry in the Domain, returning a non-nil result if found. "ok" is manipulated so that an "if" can be used to check whether it was found without having to rely on sentinel or implicit values of the returned (ie a nil Entry might not always mean "not found", but it does today)
type DomainList ¶
DomainList is a structure mapping the json response to a request for a list of domains. It tends to be a very rich response including an array of full Domain instances.
type Entry ¶
type Entry struct { CanRevert bool `json:"can_revert"` Content string `json:"content"` // free-form text of verbatim value to store (ie "192.168.0.1" for A-rec) ID string `json:"id"` // A unique opaque identifier defined by Hover Default bool `json:"is_default"` // seems to track the default @ or "*" record Name string `json:"name"` // entry name, or "*" for default TTL int `json:"ttl"` // TimeToLive, seconds Type string `json:"type"` // record type: A, MX, PTR, TXT, etc }
Entry is a single DNS record, such as a single NS, TXT, A, PTR, AAAA record within a zone.
type HoverAct ¶
type HoverAct int
HoverAct is simply an enum type to typecheck various actions we can perform in a queue
const ( // Error is the zero-value, catch a fencepost error Error HoverAct = iota // Add a domain record Add // Delete of a record will require the list of records Delete // Update will also need the list of records and IDs Update // Expand is an internal state that will expand a domain to include entries Expand )
type NopLogger ¶
NopLogger reduces spin while not logging https://gist.github.com/Avinash-Bhat/48c4f06b0cc840d9fd6c#file-log_test-go
Intended to be a compatible implementation for YALI for low-cost log discarding
type PlaintextAuth ¶
type PlaintextAuth struct { Username string `json:"username"` // username such as 'chickenandpork', exactly as typed in the login form PlaintextPassword string `json:"plaintextpassword"` // password, in plaintext, for login, exactly as typed in the login form }
PlaintextAuth is a structure into which the username and password are read from a plaintext file. This is necessary because when this code is written, Hover offers no API, so raw logins are mimicked as clients. This has risks, of course. The trade-off is that plaintext risk versus no functionality means we have no alternative. For this reason, reading the auth from a file on disk means it cannot be offered in-code during integration tests, and similarly, can be provided by a configmap or similar during a production deployment.
For versatility, the intent is to accept JSON, YAML, and even XML if it's trivial to do.
func ReadConfigFile ¶
func ReadConfigFile(filename string) (*PlaintextAuth, error)
ReadConfigFile reads a Plaintext Password struct from a JSON file. This is loosely named because it's intended to eventually try different formats on parse failure (ie try JSON, fallback to YAML, fallback to XML, shoot: fallback to CSV if we need to provide the most options with sufficient return on investment.
type User ¶
type User struct { Billing struct { Description string `json:"description,omitempty"` // This seems to be a description of my card, such as "Visa ending 1234" PayMode string `json:"pay_mode,omitempty"` // some reference to how payments are processed: mine all say "apple_pay", and they're in my Apple } `json:"billing,omitempty"` Email string `json:"email,omitempty"` EmailSecondary string `json:"email_secondary,omitempty"` }
The User record in a Domain seems to record additional contact information that augments the Billing Contact with the credit card used and some metadata around it.