/client.go

https://gitlab.com/cosman2001/shmocklib · Go · 219 lines · 175 code · 18 blank · 26 comment · 41 complexity · f9c9129b98879f4cbc85ff8d48070521 MD5 · raw file

  1. package shmocklib
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io/ioutil"
  7. "log"
  8. "net/http"
  9. "os"
  10. "os/exec"
  11. "path"
  12. "strings"
  13. "syscall"
  14. )
  15. func GetCommandList() []string {
  16. // path is normally reserved for File related things, but since its unix this will also work on url schemes
  17. endpoint := GenerateEndpoint("", "")
  18. jsondata := Get(endpoint)
  19. commands := []string{}
  20. // render json to object
  21. de_err := json.Unmarshal(jsondata, &commands)
  22. check(de_err)
  23. return commands
  24. }
  25. func Get(url string) []byte {
  26. client := &http.Client{}
  27. req, err := http.NewRequest("GET", url, nil)
  28. check(err)
  29. req.Header.Add("X-Shmock-Namespace", namespace)
  30. response, err := client.Do(req)
  31. if err != nil {
  32. log.Fatal(err)
  33. os.Exit(1)
  34. } else {
  35. defer response.Body.Close()
  36. contents, err := ioutil.ReadAll(response.Body)
  37. // Check for 404 error, return command not found
  38. if err != nil {
  39. log.Fatal(err)
  40. }
  41. return contents
  42. }
  43. return nil
  44. }
  45. func DoGet(url string) CommandResponse {
  46. contents := Get(url)
  47. cmd := renderJson(contents)
  48. return cmd
  49. }
  50. func DoPost(url string, json_body []byte, namespace string) {
  51. client := &http.Client{}
  52. req, err := http.NewRequest("POST", url, bytes.NewBuffer(json_body))
  53. if namespace != "" {
  54. req.Header.Set("X-Command-Namespace", namespace)
  55. }
  56. req.Header.Set("Content-Type", "application/json")
  57. resp, err := client.Do(req)
  58. if err != nil {
  59. panic(err)
  60. }
  61. defer resp.Body.Close()
  62. fmt.Println("response Status:", resp.Status)
  63. fmt.Println("response Headers:", resp.Header)
  64. body, _ := ioutil.ReadAll(resp.Body)
  65. fmt.Println("response Body:", string(body))
  66. }
  67. func DoPut(url string) {
  68. client := &http.Client{}
  69. request, err := http.NewRequest("PUT", url, strings.NewReader("<golang>really</golang>"))
  70. request.SetBasicAuth("admin", "admin")
  71. request.ContentLength = 23
  72. response, err := client.Do(request)
  73. if err != nil {
  74. log.Fatal(err)
  75. } else {
  76. defer response.Body.Close()
  77. contents, err := ioutil.ReadAll(response.Body)
  78. if err != nil {
  79. log.Fatal(err)
  80. }
  81. fmt.Println("The calculated length is:", len(string(contents)), "for the url:", url)
  82. fmt.Println(" ", response.StatusCode)
  83. hdr := response.Header
  84. for key, value := range hdr {
  85. fmt.Println(" ", key, ":", value)
  86. }
  87. fmt.Println(contents)
  88. }
  89. }
  90. // generates a valid http endpoint
  91. // uses SHMOCK_SERVER_URL to determine endpoint
  92. // or uses SHMOCK_SERVER_IP and SHMOCK_SERVER_PORT
  93. // or falls back to localhost:3000 as endpoint
  94. func GenerateEndpoint(command_name string, args_hash string) string {
  95. server_url := os.Getenv("SHMOCK_SERVER_URL")
  96. if server_url == "" {
  97. ip := os.Getenv("SHMOCK_SERVER_IP")
  98. port := os.Getenv("SHMOCK_SERVER_PORT")
  99. if ip == "" || port == "" {
  100. server_url = "http://localhost:3000"
  101. }
  102. server_url = fmt.Sprintf("http://%s:%s", ip, port)
  103. }
  104. return fmt.Sprintf("%s/%s", path.Join(server_url, "commands", command_name, args_hash))
  105. }
  106. // write the map output to a file
  107. // if a file already exists with a map inside, read the contents first and then merge the contents and overwrite the file
  108. func MergeToCaptureFile(filepath string, m map[string]CommandResponse) {
  109. if _, err := os.Stat(filepath); os.IsNotExist(err) {
  110. // file does not exist, no need to merge
  111. createTemplatePath(filepath)
  112. } else {
  113. // file already exists we need to read in the data and then merge the two together
  114. prev := GenerateResponseMapFromFile(filepath)
  115. for k, v := range prev {
  116. m[k] = v
  117. }
  118. }
  119. b, err := json.MarshalIndent(m, "", " ")
  120. if err != nil {
  121. log.Fatalf("error encoding command %v", err)
  122. }
  123. err = ioutil.WriteFile(filepath, b, 0644)
  124. check(err)
  125. log.Printf("Wrote to file: %s", filepath)
  126. }
  127. func GetCommand(command_name string, args_hash string) {
  128. // path is normally reserved for File related things, but since its unix this will also work on url schemes
  129. endpoint := GenerateEndpoint(command_name, args_hash)
  130. cmd := DoGet(endpoint)
  131. PrintToConsole(cmd)
  132. }
  133. // run the command on the os and capture the output, return a CommandResponse Object
  134. func CaptureCommand(args []string) CommandResponse {
  135. command := args[0]
  136. args = args[1:]
  137. log.Printf("Command: %s %s", command, args)
  138. var exitcode = 0
  139. cmd := exec.Command(command, args...)
  140. var stdout bytes.Buffer
  141. var stderr bytes.Buffer
  142. cmd.Stdout = &stdout
  143. cmd.Stderr = &stderr
  144. if err := cmd.Start(); err != nil {
  145. log.Fatalf("cmd.Start: %v")
  146. }
  147. if err := cmd.Wait(); err != nil {
  148. if exiterr, ok := err.(*exec.ExitError); ok {
  149. // The program has exited with an exit code != 0
  150. // There is no plattform independent way to retrieve
  151. // the exit code, but the following will work on Unix
  152. if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
  153. // for some odd reason this is output exit status 1
  154. exitcode = status.ExitStatus()
  155. }
  156. } else {
  157. log.Fatalf("cmd.Wait: %v", err)
  158. }
  159. }
  160. //usertime := cmd.ProcessState.UserTime()
  161. //fmt.Printf("Milli [%v]", usertime.Seconds())
  162. log.Printf("Exit Status: %d", exitcode)
  163. // store the original command call in stdin
  164. stdin := append([]string{command}, args...)
  165. command_response := CommandResponse{Stdout: stdout.String(),
  166. Stderr: stderr.String(),
  167. Exitcode: exitcode,
  168. Delay: 0,
  169. Stdin: strings.Join(stdin, " "),
  170. }
  171. return command_response
  172. }
  173. func renderJson(jsondata []byte) CommandResponse {
  174. res := &CommandResponse{}
  175. // render json to object
  176. de_err := json.Unmarshal(jsondata, &res)
  177. if de_err != nil {
  178. //return nil if not found
  179. fmt.Println(string(jsondata))
  180. //panic(de_err)
  181. res.Exitcode = 1
  182. res.Stdout = "Invalid Json in Command Response"
  183. }
  184. return *res
  185. }
  186. // Creates the given path, if path has an extension it gets the base name of the file and generates that path
  187. // The template path is where we store all the shmock response json files
  188. func createTemplatePath(filepath string) {
  189. filepath = path.Dir(filepath)
  190. err := os.MkdirAll(filepath, 0744)
  191. check(err)
  192. }
  193. // dumps the cmd and exit status to the console then exits
  194. func PrintToConsole(command CommandResponse) {
  195. if command.Exitcode == 0 {
  196. fmt.Print(command.Stdout)
  197. } else {
  198. fmt.Println(command.Stderr)
  199. }
  200. os.Exit(command.Exitcode)
  201. }