diff --git a/docs/_includes/matrix.html b/docs/_includes/matrix.html index ad17c409b..ea5c265a1 100644 --- a/docs/_includes/matrix.html +++ b/docs/_includes/matrix.html @@ -19,6 +19,7 @@
NAMEDOTCOM
NS1
OCTODNS
+
OPENSRS
OVH
ROUTE53
SOFTLAYER
@@ -70,6 +71,9 @@ + + + @@ -121,6 +125,9 @@ + + + @@ -181,6 +188,9 @@ + + + @@ -213,6 +223,7 @@ + @@ -255,6 +266,7 @@ + @@ -299,6 +311,7 @@ + @@ -349,6 +362,7 @@ + @@ -383,6 +397,7 @@ + @@ -412,6 +427,7 @@ + @@ -434,6 +450,7 @@ + @@ -475,6 +492,7 @@ + @@ -525,6 +543,9 @@ + + + @@ -591,6 +612,9 @@ + + + diff --git a/providers/_all/all.go b/providers/_all/all.go index 9f32c1880..16aa27b8a 100644 --- a/providers/_all/all.go +++ b/providers/_all/all.go @@ -15,6 +15,7 @@ import ( _ "github.com/StackExchange/dnscontrol/providers/namedotcom" _ "github.com/StackExchange/dnscontrol/providers/ns1" _ "github.com/StackExchange/dnscontrol/providers/octodns" + _ "github.com/StackExchange/dnscontrol/providers/opensrs" _ "github.com/StackExchange/dnscontrol/providers/ovh" _ "github.com/StackExchange/dnscontrol/providers/route53" _ "github.com/StackExchange/dnscontrol/providers/softlayer" diff --git a/providers/opensrs/opensrsProvider.go b/providers/opensrs/opensrsProvider.go new file mode 100644 index 000000000..ca0d1ea4f --- /dev/null +++ b/providers/opensrs/opensrsProvider.go @@ -0,0 +1,144 @@ +package opensrs + +import ( + "encoding/json" + "errors" + "fmt" + "sort" + "strings" + + "github.com/StackExchange/dnscontrol/models" + "github.com/StackExchange/dnscontrol/providers" + + opensrs "github.com/philhug/opensrs-go/opensrs" +) + +var docNotes = providers.DocumentationNotes{ + providers.DocCreateDomains: providers.Cannot(), + providers.DocOfficiallySupported: providers.Cannot(), + providers.CanUseTLSA: providers.Cannot(), +} + +func init() { + providers.RegisterRegistrarType("OPENSRS", newReg) +} + +var defaultNameServerNames = []string{ + "ns1.systemdns.com", + "ns2.systemdns.com", + "ns3.systemdns.com", +} + +type OpenSRSApi struct { + UserName string // reseller user name + ApiKey string // API Key + + BaseURL string // An alternate base URI + client *opensrs.Client // Client +} + +func (c *OpenSRSApi) GetNameservers(domainName string) ([]*models.Nameserver, error) { + return models.StringsToNameservers(defaultNameServerNames), nil +} + +func (c *OpenSRSApi) GetRegistrarCorrections(dc *models.DomainConfig) ([]*models.Correction, error) { + corrections := []*models.Correction{} + + nameServers, err := c.getNameservers(dc.Name) + if err != nil { + return nil, err + } + + sort.Strings(nameServers) + actual := strings.Join(nameServers, ",") + + expectedSet := []string{} + for _, ns := range dc.Nameservers { + expectedSet = append(expectedSet, ns.Name) + } + sort.Strings(expectedSet) + expected := strings.Join(expectedSet, ",") + + if actual != expected { + return []*models.Correction{ + { + Msg: fmt.Sprintf("Update nameservers %s -> %s", actual, expected), + F: c.updateNameserversFunc(expectedSet, dc.Name), + }, + }, nil + } + + return corrections, nil +} + +// OpenSRS calls + +func (c *OpenSRSApi) getClient() *opensrs.Client { + return c.client +} + +// Returns the name server names that should be used. If the domain is registered +// then this method will return the delegation name servers. If this domain +// is hosted only, then it will return the default OpenSRS name servers. +func (c *OpenSRSApi) getNameservers(domainName string) ([]string, error) { + client := c.getClient() + + status, err := client.Domains.GetDomain(domainName, "status", 1) + if err != nil { + return nil, err + } + + if status.Attributes.LockState == "0" { + dom, err := client.Domains.GetDomain(domainName, "nameservers", 1) + if err != nil { + return nil, err + } + return dom.Attributes.NameserverList.ToString(), nil + } else { + return nil, errors.New("Domain is locked") + } +} + +// Returns a function that can be invoked to change the delegation of the domain to the given name server names. +func (c *OpenSRSApi) updateNameserversFunc(nameServerNames []string, domainName string) func() error { + return func() error { + client := c.getClient() + + _, err := client.Domains.UpdateDomainNameservers(domainName, nameServerNames) + if err != nil { + return err + } + return nil + } +} + +// constructors + +func newReg(conf map[string]string) (providers.Registrar, error) { + return newProvider(conf, nil) +} + +func newProvider(m map[string]string, metadata json.RawMessage) (*OpenSRSApi, error) { + api := &OpenSRSApi{} + api.ApiKey = m["apikey"] + + if api.ApiKey == "" { + return nil, fmt.Errorf("OpenSRS apikey must be provided.") + } + + api.UserName = m["username"] + if api.UserName == "" { + return nil, fmt.Errorf("OpenSRS username key must be provided.") + } + + if m["baseurl"] != "" { + api.BaseURL = m["baseurl"] + } + + api.client = opensrs.NewClient(opensrs.NewApiKeyMD5Credentials(api.UserName, api.ApiKey)) + if api.BaseURL != "" { + api.client.BaseURL = api.BaseURL + } + + return api, nil +} diff --git a/vendor/github.com/philhug/opensrs-go/LICENSE b/vendor/github.com/philhug/opensrs-go/LICENSE new file mode 100644 index 000000000..88a2305cf --- /dev/null +++ b/vendor/github.com/philhug/opensrs-go/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Philipp Hug + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/philhug/opensrs-go/opensrs/authentication.go b/vendor/github.com/philhug/opensrs-go/opensrs/authentication.go new file mode 100644 index 000000000..eace8c91a --- /dev/null +++ b/vendor/github.com/philhug/opensrs-go/opensrs/authentication.go @@ -0,0 +1,44 @@ +package opensrs + +import ( + "crypto/md5" + "encoding/hex" +) + +const ( + httpHeaderUserName = "X-UserName" + httpHeaderSignature = "X-Signature" +) + +// Provides credentials that can be used for authenticating with OpenSRS. +// +type Credentials interface { + // Returns the HTTP headers that should be set + // to authenticate the HTTP Request. + Headers(xml []byte) map[string]string +} + +// API key MD5 authentication +type apiKeyMD5Credentials struct { + userName string + apiKey string +} + +// NewApiKeyMD5Credentials construct Credentials using the OpenSRS MD5 Api Key method. +func NewApiKeyMD5Credentials(userName string, apiKey string) Credentials { + return &apiKeyMD5Credentials{userName: userName, apiKey: apiKey} +} + +func (c *apiKeyMD5Credentials) Headers(xml []byte) map[string]string { + h := md5.New() + h.Write(xml) + h.Write([]byte(c.apiKey)) + m := hex.EncodeToString(h.Sum(nil)) + + h = md5.New() + h.Write([]byte(m)) + h.Write([]byte(c.apiKey)) + m = hex.EncodeToString(h.Sum(nil)) + + return map[string]string{httpHeaderUserName: c.userName, httpHeaderSignature: m} +} diff --git a/vendor/github.com/philhug/opensrs-go/opensrs/domains.go b/vendor/github.com/philhug/opensrs-go/opensrs/domains.go new file mode 100644 index 000000000..ea073ec41 --- /dev/null +++ b/vendor/github.com/philhug/opensrs-go/opensrs/domains.go @@ -0,0 +1,56 @@ +package opensrs + +import ( + "strconv" +) + +// DomainsService handles communication with the domain related +// methods of the OpenSRS API. +// +type DomainsService struct { + client *Client +} + +// GetDomain fetches a domain. +// +func (s *DomainsService) GetDomain(domainIdentifier string, domainType string, limit int) (*OpsResponse, error) { + opsResponse := OpsResponse{} + opsRequestAttributes := OpsRequestAttributes{Domain: domainIdentifier, Limit: strconv.Itoa(limit), Type: domainType} + + resp, err := s.client.post("GET", "DOMAIN", opsRequestAttributes, &opsResponse) + if err != nil { + return nil, err + } + _ = resp + return &opsResponse, nil +} + +// UpdateDomainNameservers changes domain servers on a domain. +// +func (s *DomainsService) UpdateDomainNameservers(domainIdentifier string, newDs []string) (*OpsResponse, error) { + opsResponse := OpsResponse{} + + opsRequestAttributes := OpsRequestAttributes{Domain: domainIdentifier, AssignNs: newDs, OpType: "assign"} + + resp, err := s.client.post("ADVANCED_UPDATE_NAMESERVERS", "DOMAIN", opsRequestAttributes, &opsResponse) + if err != nil { + return nil, err + } + _ = resp + return &opsResponse, nil +} + +// GetDNSZone fetches zone info for a domain. +// +func (s *DomainsService) GetDNSZone(domainIdentifier string) (*OpsResponse, error) { + opsResponse := OpsResponse{} + opsRequestAttributes := OpsRequestAttributes{Domain: domainIdentifier} + + resp, err := s.client.post("GET_DNS_ZONE", "DOMAIN", opsRequestAttributes, &opsResponse) + if err != nil { + return nil, err + } + _ = resp + return &opsResponse, nil +} + diff --git a/vendor/github.com/philhug/opensrs-go/opensrs/opensrs.go b/vendor/github.com/philhug/opensrs-go/opensrs/opensrs.go new file mode 100644 index 000000000..def3ba761 --- /dev/null +++ b/vendor/github.com/philhug/opensrs-go/opensrs/opensrs.go @@ -0,0 +1,231 @@ +// Package opensrs provides a client for the OpenSRS API. +// In order to use this package you will need a OpenSRS account. +package opensrs + +import ( + "bytes" + "crypto/tls" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" +) + +const ( + // Version identifies the current library version. + // This is a pro-forma convention given that Go dependencies + // tends to be fetched directly from the repo. + // It is also used in the user-agent identify the client. + Version = "0.0.1" + + // defaultBaseURL to the OpenSRS production API. + //defaultBaseURL = "https://rr-n1-tor.opensrs.net:55443" + defaultBaseURL = "https://horizon.opensrs.net:55443" + + // userAgent represents the default user agent used + // when no other user agent is set. + defaultUserAgent = "opensrs-go/" + Version +) + +// Client represents a client to the OpenSRS API. +type Client struct { + // HttpClient is the underlying HTTP client + // used to communicate with the API. + HttpClient *http.Client + + // Credentials used for accessing the OpenSRS API + Credentials Credentials + + // BaseURL for API requests. + // Defaults to the public OpenSRS API, but can be set to a different endpoint (e.g. the sandbox). + BaseURL string + + // UserAgent used when communicating with the OpenSRS API. + UserAgent string + + // Services used for talking to different parts of the OpenSRS API. + Domains *DomainsService + + // Set to true to output debugging logs during API calls + Debug bool +} + +// NewClient returns a new OpenSRS API client using the given credentials. +func NewClient(credentials Credentials) *Client { + proxyUrl, _ := url.Parse("http://127.0.0.1:8080") + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify : true}, + Proxy: http.ProxyURL(proxyUrl), + } + c := &Client{Credentials: credentials, HttpClient: &http.Client{Transport: tr}, BaseURL: defaultBaseURL} + c.Domains = &DomainsService{client: c} + return c +} + +// NewRequest creates an API request. +// The path is expected to be a relative path and will be resolved +// according to the BaseURL of the Client. Paths should always be specified without a preceding slash. +func (c *Client) NewRequest(method, path string, payload interface{}) (*http.Request, error) { + url := c.BaseURL + path + + body := new(bytes.Buffer) + if payload != nil { + xml, err := ToXml(payload) + if err != nil { + return nil, err + } + body = bytes.NewBuffer(xml) + } + + req, err := http.NewRequest(method, url, body) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "text/xml") + req.Header.Add("Accept", "text/xml") + req.Header.Add("User-Agent", formatUserAgent(c.UserAgent)) + for key, value := range c.Credentials.Headers(body.Bytes()) { + req.Header.Add(key, value) + } + + return req, nil +} + +// formatUserAgent builds the final user agent to use for HTTP requests. +// +// If no custom user agent is provided, the default user agent is used. +// +// opensrs-go/1.0 +// +// If a custom user agent is provided, the final user agent is the combination of the custom user agent +// prepended by the default user agent. +// +// opensrs-go/1.0 customAgentFlag +// +func formatUserAgent(customUserAgent string) string { + if customUserAgent == "" { + return defaultUserAgent + } + + return fmt.Sprintf("%s %s", defaultUserAgent, customUserAgent) +} + +func (c *Client) post(action string, object string, attributes OpsRequestAttributes, obj *OpsResponse) (*http.Response, error) { + payload := OpsRequest{Action: action, Object: object, Protocol: "XCP", Attributes: attributes} + req, err := c.NewRequest("POST", "", payload) + if err != nil { + return nil, err + } + + return c.Do(req, obj) +} + +// Do sends an API request and returns the API response. +// +// The API response is JSON decoded and stored in the value pointed by obj, +// or returned as an error if an API error has occurred. +// If obj implements the io.Writer interface, the raw response body will be written to obj, +// without attempting to decode it. +func (c *Client) Do(req *http.Request, obj *OpsResponse) (*http.Response, error) { + if c.Debug { + log.Printf("Executing request (%v): %#v", req.URL, req) + } + + resp, err := c.HttpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if c.Debug { + log.Printf("Response received: %#v", resp) + } + + err = CheckResponse(resp) + if err != nil { + return resp, err + } + + // If obj implements the io.Writer, + // the response body is decoded into v. + if obj != nil { + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + err = FromXml(b, obj) + if err != nil { + return resp, err + } + + err = CheckOpsResponse(resp, obj) + if err != nil { + return resp, err + } + } + + return resp, err +} + +// An ErrorResponse represents an API response that generated an error. +type ErrorResponse struct { + HttpResponse *http.Response + OpsResponse *OpsResponse + + // human-readable message + Message string `json:"message"` +} + +// Error implements the error interface. +func (r *ErrorResponse) Error() string { + s := fmt.Sprintf("%v %v: ", + r.HttpResponse.Request.Method, r.HttpResponse.Request.URL) + if r.OpsResponse != nil { + s = s + fmt.Sprintf("%v %v", + r.OpsResponse.ResponseCode, + r.OpsResponse.ResponseText) + } else { + s = s + fmt.Sprintf("%v %v", + r.HttpResponse.StatusCode, r.Message) + } + return s +} + +// CheckResponse checks the API response for errors, and returns them if present. +// A response is considered an error if the status code is different than 2xx. Specific requests +// may have additional requirements, but this is sufficient in most of the cases. +func CheckResponse(resp *http.Response) error { + if code := resp.StatusCode; 200 <= code && code <= 299 { + return nil + } + + errorResponse := &ErrorResponse{} + errorResponse.HttpResponse = resp + + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + err = FromXml(b, errorResponse) + if err != nil { + return err + } + + return errorResponse +} + +func CheckOpsResponse(resp *http.Response, or *OpsResponse) error { + if or.IsSuccess == "1" { + return nil + } + + errorResponse := &ErrorResponse{} + errorResponse.HttpResponse = resp + errorResponse.OpsResponse = or + + return errorResponse +} diff --git a/vendor/github.com/philhug/opensrs-go/opensrs/structs.go b/vendor/github.com/philhug/opensrs-go/opensrs/structs.go new file mode 100644 index 000000000..9a07f2d08 --- /dev/null +++ b/vendor/github.com/philhug/opensrs-go/opensrs/structs.go @@ -0,0 +1,92 @@ +package opensrs + +type NameserverList []struct { + Name string `json:"name"` + IpAddress string `json:"ipaddress,omitempty"` + Ipv6 string `json:"ipv6,omitempty"` + SortOrder string `json:"sortorder,omitempty"` +} + +type ARecord struct { + IpAddress string `json:"ipaddress,omitempty"` + SubDomain string `json:"subdomain,omitempty"` +} + +type AAAARecord struct { + Ipv6Address string `json:"ipv6_address,omitempty"` + SubDomain string `json:"subdomain,omitempty"` +} + +type CNAMERecord struct { + HostName string `json:"hostname,omitempty"` + SubDomain string `json:"subdomain,omitempty"` +} + +type MXRecord struct { + Priority string `json:"priority,omitempty"` + SubDomain string `json:"subdomain,omitempty"` + HostName string `json:"hostname,omitempty"` +} + +type SRVRecord struct { + Priority string `json:"priority,omitempty"` + Weight string `json:"weight,omitempty"` + SubDomain string `json:"subdomain,omitempty"` + HostName string `json:"hostname,omitempty"` + Port string `json:"port,omitempty"` +} + +type TXTRecord struct { + SubDomain string `json:"subdomain,omitempty"` + Text string `json:"text,omitempty"` +} + +type DnsRecords struct { + A []ARecord `json:"A,omitempty"` + AAAA []AAAARecord `json:"AAAA,omitempty"` + CNAME []CNAMERecord `json:"CNAME,omitempty"` + MX []MXRecord `json:"MX,omitempty"` + SRV []SRVRecord `json:"SRV,omitempty"` + TXT []TXTRecord `json:"TXT,omitempty"` +} + +func (n NameserverList) ToString() []string { + domains := make([]string, len(n)) + for i, ns := range n { + domains[i] = ns.Name + } + return domains +} + +type OpsRequestAttributes struct { + Domain string `json:"domain"` + Limit string `json:"limit,omitempty"` + Type string `json:"type,omitempty"` + Data string `json:"data,omitempty"` + AffectDomains string `json:"affect_domains,omitempty"` + NameserverList NameserverList `json:"nameserver_list,omitempty"` + OpType string `json:"op_type,omitempty"` + AssignNs []string `json:"assign_ns,omitempty"` +} + +type OpsResponse struct { + Action string `json:"action"` + Object string `json:"object"` + Protocol string `json:"protocol"` + IsSuccess string `json:"is_success"` + ResponseCode string `json:"response_code"` + ResponseText string `json:"response_text"` + Attributes struct { + NameserverList NameserverList `json:"nameserver_list,omitempty"` + Type string `json:"type,omitempty"` + LockState string `json:"lock_state,omitempty"` + Records DnsRecords `json:"records,omitempty"` + } `json:"attributes"` +} + +type OpsRequest struct { + Action string `json:"action"` + Object string `json:"object"` + Protocol string `json:"protocol"` + Attributes OpsRequestAttributes `json:"attributes"` +} diff --git a/vendor/github.com/philhug/opensrs-go/opensrs/xml.go b/vendor/github.com/philhug/opensrs-go/opensrs/xml.go new file mode 100644 index 000000000..4613e4d29 --- /dev/null +++ b/vendor/github.com/philhug/opensrs-go/opensrs/xml.go @@ -0,0 +1,176 @@ +package opensrs + +import ( + "encoding/json" + "encoding/xml" + "errors" + "log" + "reflect" + "strconv" + "strings" +) + +type Header struct { + XMLName xml.Name `xml:"header"` + Version string `xml:"version"` +} + +type Item struct { + XMLName xml.Name `xml:"item"` + Key string `xml:"key,attr"` + DtArray *DtArray `xml:"dt_array,omitempty"` + DtAssoc *DtAssoc `xml:"dt_assoc,omitempty"` + Value string `xml:",chardata"` +} + +func (i *Item) decode() interface{} { + if i.DtAssoc != nil { + return i.DtAssoc.decode() + } + if i.DtArray != nil { + return i.DtArray.decode() + } + return i.Value +} + +type DtArray struct { + XMLName xml.Name `xml:"dt_array"` + ItemList []Item `xml:"item,omitempty"` +} + +func (d *DtArray) decode() []interface{} { + m := make([]interface{}, 0) + for _, element := range d.ItemList { + m = append(m, element.decode()) + } + return m +} + +type DtAssoc struct { + XMLName xml.Name `xml:"dt_assoc"` + ItemList []Item `xml:"item,omitempty"` +} + +func (d *DtAssoc) decode() Map { + m := make(Map) + for _, element := range d.ItemList { + m[element.Key] = element.decode() + } + return m +} + +type DataBlock struct { + XMLName xml.Name `xml:"data_block"` + DtAssoc *DtAssoc `xml:"dt_assoc,omitempty"` + //DtArray DtArray `xml:"dt_array,omitempty"` +} + +type Map map[string]interface{} + +func (d *DataBlock) decode() Map { + m := make(Map) + if d.DtAssoc != nil { + return d.DtAssoc.decode() + } + return m +} + +func encodeItem(key string, value reflect.Value) Item { + item := Item{} + item.Key = key + v := internalEncode(value) + s, ok := v.(string) + if ok { + item.Value = s + } + dtass, ok := v.(DtAssoc) + if ok { + item.DtAssoc = &dtass + } + dtarr, ok := v.(DtArray) + if ok { + item.DtArray = &dtarr + } + return item +} + +func internalEncode(v reflect.Value) (p interface{}) { + t := v.Type() + switch t.Kind() { + case reflect.Interface: + return internalEncode(v.Elem()) + case reflect.String: + return v.Interface().(string) + case reflect.Struct: + dt := DtAssoc{} + + for i := 0; i < t.NumField(); i++ { + key := strings.ToLower(t.Field(i).Name) + value := v.Field(i) + item := encodeItem(key, value) + dt.ItemList = append(dt.ItemList, item) + } + return dt + case reflect.Map: // DtAssoc + dt := DtAssoc{} + + for _, k := range v.MapKeys() { + v := v.MapIndex(k) + key := k.String() + item := encodeItem(key, v) + dt.ItemList = append(dt.ItemList, item) + } + return dt + case reflect.Slice: //DtArray + dt := DtArray{} + for i := 0; i < v.Len(); i++ { + key := strconv.Itoa(i) + value := v.Index(i) + item := encodeItem(key, value) + dt.ItemList = append(dt.ItemList, item) + } + return dt + default: + log.Println("FAIL, unknown type", t.Kind()) + } + return nil +} + +type Body struct { + XMLName xml.Name `xml:"body"` + DataBlock DataBlock `xml:"data_block"` +} + +type OPSEnvelope struct { + XMLName xml.Name `xml:"OPS_envelope"` + Header Header `xml:"header"` + Body Body `xml:"body"` +} + +func FromXml(b []byte, v interface{}) error { + var q OPSEnvelope + err := xml.Unmarshal(b, &q) + if err != nil { + return err + } + m := q.Body.DataBlock.decode() + jsonString, _ := json.Marshal(m) + log.Println(string(jsonString)) + return json.Unmarshal(jsonString, &v) + +} + +func ToXml(v interface{}) (b []byte, err error) { + jsonString, _ := json.Marshal(v) + var m interface{} + json.Unmarshal(jsonString, &m) + + q := OPSEnvelope{Header: Header{Version: "0.9"}, Body: Body{}} + dtass, ok := internalEncode(reflect.ValueOf(m)).(DtAssoc) + if ok { + q.Body.DataBlock.DtAssoc = &dtass + } else { + return nil, errors.New("Encoding failed") + } + return xml.MarshalIndent(q, "", " ") +} diff --git a/vendor/vendor.json b/vendor/vendor.json index a9f2bc15f..f20203255 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -330,6 +330,12 @@ "revision": "8e652d1a756f2480f1ad28dc211c77a25264e02d", "revisionTime": "2018-01-09T18:53:19Z" }, + { + "checksumSHA1": "ln/74dXVPX/i7aDah00ZKqbdpw8=", + "path": "github.com/philhug/opensrs-go/opensrs", + "revision": "58112e74137c104eb77bdad4b53c51da22b7b6d5", + "revisionTime": "2017-11-26T23:10:42Z" + }, { "checksumSHA1": "ynJSWoF6v+3zMnh9R0QmmG6iGV8=", "path": "github.com/pkg/errors",