aboutsummaryrefslogtreecommitdiff
path: root/nccli.go
diff options
context:
space:
mode:
authorKumar Damani <me@kumardamani.net>2022-06-08 02:01:19 +0000
committerKumar Damani <me@kumardamani.net>2022-06-08 02:01:19 +0000
commit3fe5f2789f0b2e7066184194a3ac3ced02353667 (patch)
tree91e97ab057d34364f1abb7e81f89e1f804ee8aad /nccli.go
parent4ec6622f7bb891fa68f8bf45f5b8d84dcc46dae7 (diff)
more unit tests.
Diffstat (limited to 'nccli.go')
-rw-r--r--nccli.go156
1 files changed, 99 insertions, 57 deletions
diff --git a/nccli.go b/nccli.go
index fec69c3..73a8de8 100644
--- a/nccli.go
+++ b/nccli.go
@@ -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)
}