fastdns: Do not overwrite existing TXT records (#774)

This commit is contained in:
Ludovic Fernandez 2019-02-04 22:12:03 +01:00 committed by GitHub
parent 0e6e4807b1
commit c4730a6ec3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 186 additions and 160 deletions

12
Gopkg.lock generated
View file

@ -55,7 +55,7 @@
revision = "a3fa4a771d87bda2514a90a157e1fed1b6897d2e"
[[projects]]
digest = "1:d677d9bb0dfb2cf2ec6ab5ff1d2deb08ef88f4b255c9f201f01100933ef001a2"
digest = "1:47071ecf8d840dd357ede1b2aed46576bdd0a866adecef3c9e85a00db9672202"
name = "github.com/akamai/AkamaiOPEN-edgegrid-golang"
packages = [
"client-v1",
@ -64,8 +64,8 @@
"jsonhooks-v1",
]
pruneopts = "NUT"
revision = "196fbdab2db66d0e231cc363efb63454f55fed1c"
version = "v0.6.2"
revision = "1471ce9c14c6d8c007516e129262962a628fecdf"
version = "v0.7.3"
[[projects]]
digest = "1:823e87ae25170339e2bfd1d6f7c2e27554c6bb5655f91c67b37bd5be45bb6b32"
@ -232,12 +232,12 @@
revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a"
[[projects]]
digest = "1:a1578f7323eca2b88021fdc9a79a99833d40b12c32a5ea4f284e2fad19ea2657"
digest = "1:1bb197a3b5db4e06e00b7560f8e89836c486627f2a0338332ed37daa003d259e"
name = "github.com/google/uuid"
packages = ["."]
pruneopts = "NUT"
revision = "d460ce9f8df2e77fb1ba55ca87fafed96c607494"
version = "v1.0.0"
revision = "064e2069ce9c359c118179501254f67d7d37ba24"
version = "0.2"
[[projects]]
branch = "master"

View file

@ -96,3 +96,7 @@
[[constraint]]
version = "v1.1.0"
name = "github.com/miekg/dns"
[[constraint]]
version = "v0.7.3"
name = "github.com/akamai/AkamaiOPEN-edgegrid-golang"

View file

@ -89,16 +89,10 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
_ = record.SetField("target", value)
_ = record.SetField("active", true)
existingRecord := d.findExistingRecord(zone, recordName)
if existingRecord != nil {
if reflect.DeepEqual(existingRecord.ToMap(), record.ToMap()) {
for _, r := range zone.Zone.Txt {
if r != nil && reflect.DeepEqual(r.ToMap(), record.ToMap()) {
return nil
}
err = zone.RemoveRecord(existingRecord)
if err != nil {
return fmt.Errorf("fastdns: %v", err)
}
}
return d.createRecord(zone, record)
@ -119,13 +113,17 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
return fmt.Errorf("fastdns: %v", err)
}
existingRecord := d.findExistingRecord(zone, recordName)
if existingRecord != nil {
err := zone.RemoveRecord(existingRecord)
if err != nil {
var removed bool
for _, r := range zone.Zone.Txt {
if r != nil && r.Name == recordName {
if zone.RemoveRecord(r) != nil {
return fmt.Errorf("fastdns: %v", err)
}
removed = true
}
}
if removed {
return zone.Save()
}
@ -150,16 +148,6 @@ func (d *DNSProvider) findZoneAndRecordName(fqdn, domain string) (string, string
return zone, name, nil
}
func (d *DNSProvider) findExistingRecord(zone *configdns.Zone, recordName string) *configdns.TxtRecord {
for _, r := range zone.Zone.Txt {
if r.Name == recordName {
return r
}
}
return nil
}
func (d *DNSProvider) createRecord(zone *configdns.Zone, record *configdns.TxtRecord) error {
err := zone.AddRecord(record)
if err != nil {

View file

@ -6,8 +6,11 @@ import (
"errors"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"os"
"path/filepath"
"runtime"
"strings"
@ -61,13 +64,21 @@ func NewRequest(config edgegrid.Config, method, path string, body io.Reader) (*h
// NewJSONRequest creates an HTTP request that can be sent to the Akamai APIs with a JSON body
// The JSON body is encoded and the Content-Type/Accept headers are set automatically.
func NewJSONRequest(config edgegrid.Config, method, path string, body interface{}) (*http.Request, error) {
var req *http.Request
var err error
if body != nil {
jsonBody, err := jsonhooks.Marshal(body)
if err != nil {
return nil, err
}
buf := bytes.NewReader(jsonBody)
req, err := NewRequest(config, method, path, buf)
req, err = NewRequest(config, method, path, buf)
} else {
req, err = NewRequest(config, method, path, nil)
}
if err != nil {
return nil, err
}
@ -78,6 +89,36 @@ func NewJSONRequest(config edgegrid.Config, method, path string, body interface{
return req, nil
}
// NewMultiPartFormDataRequest creates an HTTP request that uploads a file to the Akamai API
func NewMultiPartFormDataRequest(config edgegrid.Config, uriPath, filePath string, otherFormParams map[string]string) (*http.Request, error) {
file, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer file.Close()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
// TODO: make this field name configurable
part, err := writer.CreateFormFile("importFile", filepath.Base(filePath))
if err != nil {
return nil, err
}
_, err = io.Copy(part, file)
for key, val := range otherFormParams {
_ = writer.WriteField(key, val)
}
err = writer.Close()
if err != nil {
return nil, err
}
req, err := NewRequest(config, "POST", uriPath, body)
req.Header.Set("Content-Type", writer.FormDataContentType())
return req, err
}
// Do performs a given HTTP Request, signed with the Akamai OPEN Edgegrid
// Authorization header. An edgegrid.Response or an error is returned.
func Do(config edgegrid.Config, req *http.Request) (*http.Response, error) {

View file

@ -16,6 +16,8 @@ type APIError struct {
Title string `json:"title"`
Status int `json:"status"`
Detail string `json:"detail"`
Errors []APIErrorDetail `json:"errors"`
Problems []APIErrorDetail `json:"problems"`
Instance string `json:"instance"`
Method string `json:"method"`
ServerIP string `json:"serverIp"`
@ -26,8 +28,26 @@ type APIError struct {
RawBody string `json:"-"`
}
type APIErrorDetail struct {
Type string `json:"type"`
Title string `json:"title"`
Detail string `json:"detail"`
RejectedValue string `json:"rejectedValue"`
}
func (error APIError) Error() string {
return strings.TrimSpace(fmt.Sprintf("API Error: %d %s %s More Info %s", error.Status, error.Title, error.Detail, error.Type))
var errorDetails string
if len(error.Errors) > 0 {
for _, e := range error.Errors {
errorDetails = fmt.Sprintf("%s \n %s", errorDetails, e)
}
}
if len(error.Problems) > 0 {
for _, e := range error.Problems {
errorDetails = fmt.Sprintf("%s \n %s", errorDetails, e)
}
}
return strings.TrimSpace(fmt.Sprintf("API Error: %d %s %s More Info %s\n %s", error.Status, error.Title, error.Detail, error.Type, errorDetails))
}
// NewAPIError creates a new API error based on a Response,
@ -45,7 +65,6 @@ func NewAPIError(response *http.Response) APIError {
// other purposes.
func NewAPIErrorFromBody(response *http.Response, body []byte) APIError {
error := APIError{}
if err := jsonhooks.Unmarshal(body, &error); err == nil {
error.Status = response.StatusCode
error.Title = response.Status

View file

@ -1324,6 +1324,7 @@ func (record *RrsigRecord) ToMap() map[string]interface{} {
type SoaRecord struct {
fieldMap []string `json:"-"`
originalSerial uint `json:"-"`
TTL int `json:"ttl,omitempty"`
Originserver string `json:"originserver,omitempty"`
Contact string `json:"contact,omitempty"`

View file

@ -82,7 +82,7 @@ func GetZone(hostname string) (*Zone, error) {
} else if res.StatusCode == 404 {
return nil, &ZoneError{zoneName: hostname}
} else {
err = client.BodyJSON(res, &zone)
err = client.BodyJSON(res, zone)
if err != nil {
return nil, err
}
@ -762,11 +762,18 @@ func (zone *Zone) removeTxtRecord(record *TxtRecord) error {
return errors.New("Txt Record not found")
}
func (zone *Zone) PreMarshalJSON() error {
func (zone *Zone) PostUnmarshalJSON() error {
if zone.Zone.Soa.Serial > 0 {
zone.Zone.Soa.Serial = zone.Zone.Soa.Serial + 1
} else {
zone.Zone.Soa.originalSerial = zone.Zone.Soa.Serial
}
return nil
}
func (zone *Zone) PreMarshalJSON() error {
if zone.Zone.Soa.Serial == 0 {
zone.Zone.Soa.Serial = uint(time.Now().Unix())
} else if zone.Zone.Soa.Serial == zone.Zone.Soa.originalSerial {
zone.Zone.Soa.Serial = zone.Zone.Soa.Serial + 1
}
return nil
}
@ -786,21 +793,24 @@ func (zone *Zone) validateCnames() (bool, []name) {
}
func (zone *Zone) removeCnameName(host string) {
for i, v := range cnameNames {
if v.name == host {
r := cnameNames[:i]
cnameNames = append(r, cnameNames[i+1:]...)
var ncn []name
for _, v := range cnameNames {
if v.name != host {
ncn =append(ncn, v)
}
}
cnameNames = ncn
}
func (zone *Zone) removeNonCnameName(host string) {
for i, v := range nonCnameNames {
if v.name == host {
r := nonCnameNames[:i]
nonCnameNames = append(r, nonCnameNames[i+1:]...)
var ncn []name
for _, v := range nonCnameNames {
if v.name != host {
ncn =append(ncn, v)
}
}
nonCnameNames = ncn
}
func (zone *Zone) FindRecords(recordType string, options map[string]interface{}) []DNSRecord {

View file

@ -44,7 +44,7 @@ type PreJSONMarshaler interface {
// ImplementsPreJSONMarshaler checks for support for the PreMarshalJSON pre-hook
func ImplementsPreJSONMarshaler(v interface{}) bool {
value := reflect.ValueOf(v)
if value.Kind() == reflect.Ptr && value.IsNil() {
if !value.IsValid() {
return false
}

View file

@ -42,7 +42,7 @@ func NewDCESecurity(domain Domain, id uint32) (UUID, error) {
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
// domain with the id returned by os.Getuid.
//
// NewDCESecurity(Person, uint32(os.Getuid()))
// NewDCEPerson(Person, uint32(os.Getuid()))
func NewDCEPerson() (UUID, error) {
return NewDCESecurity(Person, uint32(os.Getuid()))
}
@ -50,7 +50,7 @@ func NewDCEPerson() (UUID, error) {
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
// domain with the id returned by os.Getgid.
//
// NewDCESecurity(Group, uint32(os.Getgid()))
// NewDCEGroup(Group, uint32(os.Getgid()))
func NewDCEGroup() (UUID, error) {
return NewDCESecurity(Group, uint32(os.Getgid()))
}

View file

@ -27,7 +27,7 @@ var (
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
h.Reset()
h.Write(space[:])
h.Write(data)
h.Write([]byte(data))
s := h.Sum(nil)
var uuid UUID
copy(uuid[:], s)

View file

@ -15,6 +15,8 @@ func (uuid UUID) MarshalText() ([]byte, error) {
// UnmarshalText implements encoding.TextUnmarshaler.
func (uuid *UUID) UnmarshalText(data []byte) error {
// See comment in ParseBytes why we do this.
// id, err := ParseBytes(data)
id, err := ParseBytes(data)
if err == nil {
*uuid = id

View file

@ -5,11 +5,13 @@
package uuid
import (
"net"
"sync"
)
var (
nodeMu sync.Mutex
interfaces []net.Interface // cached list of interfaces
ifname string // name of interface being used
nodeID [6]byte // hardware for version 1 UUIDs
zeroID [6]byte // nodeID with only 0's
@ -37,12 +39,21 @@ func SetNodeInterface(name string) bool {
}
func setNodeInterface(name string) bool {
iname, addr := getHardwareInterface(name) // null implementation for js
if iname != "" && addr != nil {
ifname = iname
copy(nodeID[:], addr)
if interfaces == nil {
var err error
interfaces, err = net.Interfaces()
if err != nil && name != "" {
return false
}
}
for _, ifs := range interfaces {
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
copy(nodeID[:], ifs.HardwareAddr)
ifname = ifs.Name
return true
}
}
// We found no interfaces with a valid hardware address. If name
// does not specify a specific interface generate a random Node ID
@ -83,6 +94,9 @@ func SetNodeID(id []byte) bool {
// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
func (uuid UUID) NodeID() []byte {
if len(uuid) != 16 {
return nil
}
var node [6]byte
copy(node[:], uuid[10:])
return node[:]

View file

@ -1,12 +0,0 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build js
package uuid
// getHardwareInterface returns nil values for the JS version of the code.
// This remvoves the "net" dependency, because it is not used in the browser.
// Using the "net" library inflates the size of the transpiled JS code by 673k bytes.
func getHardwareInterface(name string) (string, []byte) { return "", nil }

View file

@ -1,33 +0,0 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !js
package uuid
import "net"
var interfaces []net.Interface // cached list of interfaces
// getHardwareInterface returns the name and hardware address of interface name.
// If name is "" then the name and hardware address of one of the system's
// interfaces is returned. If no interfaces are found (name does not exist or
// there are no interfaces) then "", nil is returned.
//
// Only addresses of at least 6 bytes are returned.
func getHardwareInterface(name string) (string, []byte) {
if interfaces == nil {
var err error
interfaces, err = net.Interfaces()
if err != nil {
return "", nil
}
}
for _, ifs := range interfaces {
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
return ifs.Name, ifs.HardwareAddr
}
}
return "", nil
}

21
vendor/github.com/google/uuid/sql.go generated vendored
View file

@ -13,36 +13,35 @@ import (
// Currently, database types that map to string and []byte are supported. Please
// consult database-specific driver documentation for matching types.
func (uuid *UUID) Scan(src interface{}) error {
switch src := src.(type) {
case nil:
return nil
switch src.(type) {
case string:
// if an empty UUID comes from a table, we return a null UUID
if src == "" {
if src.(string) == "" {
return nil
}
// see Parse for required string format
u, err := Parse(src)
u, err := Parse(src.(string))
if err != nil {
return fmt.Errorf("Scan: %v", err)
}
*uuid = u
case []byte:
b := src.([]byte)
// if an empty UUID comes from a table, we return a null UUID
if len(src) == 0 {
if len(b) == 0 {
return nil
}
// assumes a simple slice of bytes if 16 bytes
// otherwise attempts to parse
if len(src) != 16 {
return uuid.Scan(string(src))
if len(b) != 16 {
return uuid.Scan(string(b))
}
copy((*uuid)[:], src)
copy((*uuid)[:], b)
default:
return fmt.Errorf("Scan: unable to scan type %T into UUID", src)

View file

@ -86,7 +86,7 @@ func clockSequence() int {
return int(clockSeq & 0x3fff)
}
// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to
// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to
// -1 causes a new sequence to be generated.
func SetClockSequence(seq int) {
defer timeMu.Unlock()
@ -100,9 +100,9 @@ func setClockSequence(seq int) {
randomBits(b[:]) // clock sequence
seq = int(b[0])<<8 | int(b[1])
}
oldSeq := clockSeq
old_seq := clockSeq
clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant
if oldSeq != clockSeq {
if old_seq != clockSeq {
lasttime = 0
}
}

View file

@ -58,12 +58,12 @@ func Parse(s string) (UUID, error) {
14, 16,
19, 21,
24, 26, 28, 30, 32, 34} {
v, ok := xtob(s[x], s[x+1])
if !ok {
if v, ok := xtob(s[x], s[x+1]); !ok {
return uuid, errors.New("invalid UUID format")
}
} else {
uuid[i] = v
}
}
return uuid, nil
}
@ -88,22 +88,15 @@ func ParseBytes(b []byte) (UUID, error) {
14, 16,
19, 21,
24, 26, 28, 30, 32, 34} {
v, ok := xtob(b[x], b[x+1])
if !ok {
if v, ok := xtob(b[x], b[x+1]); !ok {
return uuid, errors.New("invalid UUID format")
}
} else {
uuid[i] = v
}
}
return uuid, nil
}
// FromBytes creates a new UUID from a byte slice. Returns an error if the slice
// does not have a length of 16. The bytes are copied from the slice.
func FromBytes(b []byte) (uuid UUID, err error) {
err = uuid.UnmarshalBinary(b)
return uuid, err
}
// Must returns uuid if err is nil and panics otherwise.
func Must(uuid UUID, err error) UUID {
if err != nil {
@ -183,7 +176,7 @@ func (v Variant) String() string {
return fmt.Sprintf("BadVariant%d", int(v))
}
// SetRand sets the random number generator to r, which implements io.Reader.
// SetRand sets the random number generator to r, which implents io.Reader.
// If r.Read returns an error when the package requests random data then
// a panic will be issued.
//

View file

@ -13,7 +13,7 @@ import (
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
// be set NewUUID returns nil. If clock sequence has not been set by
// SetClockSequence then it will be set automatically. If GetTime fails to
// return the current NewUUID returns nil and an error.
// return the current NewUUID returns Nil and an error.
//
// In most cases, New should be used.
func NewUUID() (UUID, error) {

View file

@ -6,7 +6,7 @@ package uuid
import "io"
// New creates a new random UUID or panics. New is equivalent to
// New is creates a new random UUID or panics. New is equivalent to
// the expression
//
// uuid.Must(uuid.NewRandom())
@ -14,12 +14,12 @@ func New() UUID {
return Must(NewRandom())
}
// NewRandom returns a Random (Version 4) UUID.
// NewRandom returns a Random (Version 4) UUID or panics.
//
// The strength of the UUIDs is based on the strength of the crypto/rand
// package.
//
// A note about uniqueness derived from the UUID Wikipedia entry:
// A note about uniqueness derived from from the UUID Wikipedia entry:
//
// Randomly generated UUIDs have 122 random bits. One's annual risk of being
// hit by a meteorite is estimated to be one chance in 17 billion, that