diff options
Diffstat (limited to 'nccli.go')
| -rw-r--r-- | nccli.go | 156 |
1 files changed, 99 insertions, 57 deletions
@@ -1,4 +1,4 @@ -package main +package nccli import ( "bufio" @@ -15,7 +15,6 @@ import ( var ( verbose = false - apiUrl string ) // Based on: https://www.namecheap.com/support/api/methods/domains-dns/set-hosts/ @@ -43,12 +42,12 @@ type GetResponseObj struct { } type Host struct { - XMLName xml.Name `xml:"host"` - Type string `xml:"Type,attr"` - Name string `xml:"Name,attr"` - Address string `xml:"Address,attr"` - MXPref int `xml:"MXPref,attr"` - TTL int `xml:"TTL,attr"` + //XMLName xml.Name `xml:"host"` + Type string `xml:"Type,attr"` + Name string `xml:"Name,attr"` + Address string `xml:"Address,attr"` + MXPref int `xml:"MXPref,attr"` + TTL int `xml:"TTL,attr"` } // Helper functions // @@ -69,12 +68,12 @@ func getEnv(key string) (string, error) { // Load ENV vars into the apiUrl var. // Returns nil if config loaded successfully, error otherwise. -func loadConfig() error { +func loadConfig() (string, error) { keys := [5]string{"API_USER", "API_KEY", "ACCESS_IP", "DOMAIN_MAIN", "DOMAIN_END"} for _, key := range keys { _, err := getEnv(key) if err != nil { - return err + return "", err } } user, _ := getEnv("API_USER") @@ -83,14 +82,14 @@ func loadConfig() error { domainMain, _ := getEnv("DOMAIN_MAIN") domainEnd, _ := getEnv("DOMAIN_END") - apiUrl = "https://api.namecheap.com/xml.response?ApiUser=" + user + + apiUrl := "https://api.namecheap.com/xml.response?ApiUser=" + user + "&ApiKey=" + apiKey + "&UserName=" + user + "&ClientIp=" + accessIP + "&SLD=" + domainMain + "&TLD=" + domainEnd - return nil + return apiUrl, nil } // Print host recrods to stdout. @@ -114,9 +113,9 @@ func logRecords(hosts []Host) { // Args: endPoint - the api method to call. // extraParams (optional) - more params to add to the apiUrl. // Returns the response byte stream. -func makeReq(endPoint string, extraParams ...string) []byte { +func makeReq(baseUrl string, endPoint string, extraParams ...string) ([]byte, error) { - methodUrl := apiUrl + "&Command=namecheap.domains.dns." + endPoint + methodUrl := baseUrl + "&Command=namecheap.domains.dns." + endPoint for _, param := range extraParams { methodUrl += param } @@ -135,18 +134,18 @@ func makeReq(endPoint string, extraParams ...string) []byte { case "sethosts": response, err = http.Post(methodUrl, "application/xml", nil) default: - log.Fatalln("Error: Invalid request type specified.") + return nil, fmt.Errorf("Error: Invalid request type specified.") } if err != nil { - log.Fatalln(err) + return nil, err } responseData, err := ioutil.ReadAll(response.Body) if err != nil { - log.Fatalln(err) + return nil, err } - return responseData + return responseData, nil } // Converts user input strings into ints. Errors if input is wrong. @@ -156,21 +155,19 @@ func makeReq(endPoint string, extraParams ...string) []byte { func normalizeUserChoices(userChoicesStrs []string, maxVal int) ([]int, error) { var chosenInts []int for _, choice := range userChoicesStrs { - text := strings.TrimSpace(choice) + text := strings.TrimSpace(choice) if len(text) == 0 { continue } val, err := strconv.Atoi(text) if err != nil { - //log.Fatalln("Error: Bad user input detected. Please only enter numbers.") - return nil, fmt.Errorf("Error: Bad input detected: %s. Please only enter numbers.", text) + return nil, fmt.Errorf("Error: Bad input detected: %s. Please only enter numbers.", text) } // validate if (val >= 0) && (val < maxVal) { chosenInts = append(chosenInts, val) } else { - //log.Fatalln("Error: Bad user input detected. One of the numbers is not correct.") - return nil, fmt.Errorf("Error: Bad user input detected. The number %d is out of range.", val) + return nil, fmt.Errorf("Error: Bad user input detected. The number %d is out of range.", val) } } return chosenInts, nil @@ -193,24 +190,30 @@ func updateHostRecordSet(hostRecords []Host, newHost Host) { // Performs the API calls that updates Namecheap in any way, // and handles the resulting response validation. // Args: urlEncodedHosts - params consisting of DNS records. -func updateNamecheapRecords(urlEncodedHosts string) { +func updateNamecheapRecords(apiUrl string, urlEncodedHosts string) error { // make the api request to namecheap - responseData := makeReq("sethosts", urlEncodedHosts) + responseData, err := makeReq(apiUrl, "sethosts", urlEncodedHosts) + if err != nil { + return err + } var responseObj SetResponseObj xml.Unmarshal(responseData, &responseObj) // validate - if responseObj.Status != "OK" { - log.Fatalln(string(responseData)) - log.Fatalln("Error: Api request failed. Exiting.") + switch responseObj.Status { + case "OK": + case "ERROR": + return fmt.Errorf("Error: Api request failed.") + default: + return fmt.Errorf("Error while parsing XML response.") } if !responseObj.DNSResultObj.IsSuccess { - log.Fatalln(string(responseData)) - log.Fatalln("Error: Something went wrong updating DNS records for:", responseObj.DNSResultObj.Domain) + return fmt.Errorf("Error: Something went wrong updating DNS records for %s", responseObj.DNSResultObj.Domain) } if verbose { fmt.Println("Success updating DNS records for:", responseObj.DNSResultObj.Domain) } + return nil } // Build a string with all DNS records. @@ -270,17 +273,23 @@ func userInputHandler() []string { // Get all existing DNS records. // Returns a slice of Host corresponding to DNS records. -func getAllRecords() []Host { - responseData := makeReq("gethosts") +func getAllRecords(apiUrl string) ([]Host, error) { + responseData, err := makeReq(apiUrl, "gethosts") + if err != nil { + return nil, err + } var responseObj GetResponseObj xml.Unmarshal(responseData, &responseObj) // validate - if responseObj.Status != "OK" { - log.Fatalln(string(responseData)) - log.Fatalln("Error: Api request failed. Exiting.") + switch responseObj.Status { + case "OK": + return responseObj.Hosts, nil + case "ERROR": + return nil, fmt.Errorf("Error: Api request failed. Exiting.") + default: + return nil, fmt.Errorf("Error while parsing XML response.") } - return responseObj.Hosts } // Add a DNS record. @@ -289,9 +298,12 @@ func getAllRecords() []Host { // rAddress - the value of the DNS record. // rMXPref - the priority of the DNS record. // rTTL - the ttl of the DNS record. -func addRecord(rName string, rType string, rAddress string, rMXPref int, rTTL int) { +func addRecord(apiUrl string, rName string, rType string, rAddress string, rMXPref int, rTTL int) error { // we need to get the records before we can add to it. - var hostRecords = getAllRecords() + hostRecords, err := getAllRecords(apiUrl) + if err != nil { + return err + } if verbose { logRecords(hostRecords) } @@ -303,23 +315,29 @@ func addRecord(rName string, rType string, rAddress string, rMXPref int, rTTL in TTL: rTTL, }) urlEncodedHosts := urlEncodeHosts(hostRecords) - updateNamecheapRecords(urlEncodedHosts) + err = updateNamecheapRecords(apiUrl, urlEncodedHosts) + if err != nil { + return err + } + return nil } // Remove DNS record(s). // Will prompt for user input. -func removeRecord() { +func removeRecord(apiUrl string) error { // we need to get the records before we can remove one - var hostRecords = getAllRecords() + hostRecords, err := getAllRecords(apiUrl) + if err != nil { + return err + } logRecords(hostRecords) // get user input for which ones to delete userChoicesStrs := userInputHandler() userChoicesInts, err := normalizeUserChoices(userChoicesStrs, len(hostRecords)) - if err != nil { - log.Fatalln(err) - } - + if err != nil { + return err + } // build the desired set of host records. var newHostRecords []Host @@ -336,7 +354,11 @@ func removeRecord() { } } urlEncodedHosts := urlEncodeHosts(newHostRecords) - updateNamecheapRecords(urlEncodedHosts) + err = updateNamecheapRecords(apiUrl, urlEncodedHosts) + if err != nil { + return err + } + return nil } // Update DNS record. @@ -345,9 +367,12 @@ func removeRecord() { // rAddress - the value of the DNS record. // rMXPref - the priority of the DNS record. // rTTL - the ttl of the DNS record. -func updateRecord(rName string, rType string, rAddress string, rMXPref int, rTTL int) { +func updateRecord(apiUrl string, rName string, rType string, rAddress string, rMXPref int, rTTL int) error { // we need to get the records before we can update it. - var hostRecords = getAllRecords() + hostRecords, err := getAllRecords(apiUrl) + if err != nil { + return err + } if verbose { logRecords(hostRecords) } @@ -362,7 +387,11 @@ func updateRecord(rName string, rType string, rAddress string, rMXPref int, rTTL }, ) urlEncodedHosts := urlEncodeHosts(hostRecords) - updateNamecheapRecords(urlEncodedHosts) + err = updateNamecheapRecords(apiUrl, urlEncodedHosts) + if err != nil { + return err + } + return nil } func main() { @@ -398,38 +427,51 @@ func main() { switch os.Args[1] { case "list": listCmd.Parse(os.Args[2:]) - err := loadConfig() + apiUrl, err := loadConfig() if err != nil { log.Fatalln(err) } - logRecords(getAllRecords()) + hostRecords, err := getAllRecords(apiUrl) + if err != nil { + log.Fatalln(err) + } + logRecords(hostRecords) case "add": addCmd.Parse(os.Args[2:]) if *addName == "" || *addType == "" || *addAddress == "" { log.Fatalln(addUpdateErrorMsg) } - err := loadConfig() + apiUrl, err := loadConfig() + if err != nil { + log.Fatalln(err) + } + err = addRecord(apiUrl, *addName, *addType, *addAddress, *addMXPref, *addTTL) if err != nil { log.Fatalln(err) } - addRecord(*addName, *addType, *addAddress, *addMXPref, *addTTL) case "remove": removeCmd.Parse(os.Args[2:]) - err := loadConfig() + apiUrl, err := loadConfig() + if err != nil { + log.Fatalln(err) + } + err = removeRecord(apiUrl) if err != nil { log.Fatalln(err) } - removeRecord() case "update": updateCmd.Parse(os.Args[2:]) if *updateName == "" || *updateType == "" || *updateAddress == "" { log.Fatalln(addUpdateErrorMsg) } - err := loadConfig() + apiUrl, err := loadConfig() + if err != nil { + log.Fatalln(err) + } + err = updateRecord(apiUrl, *updateName, *updateType, *updateAddress, *updateMXPref, *updateTTL) if err != nil { log.Fatalln(err) } - updateRecord(*updateName, *updateType, *updateAddress, *updateMXPref, *updateTTL) default: log.Fatalln(subCmdErrorMsg) } |
