Tool to help you manage your Grafana dashboards using Git.

dashboards.go 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package grafana
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "grafana/helpers"
  6. )
  7. // dbSearchResponse represents an element of the response to a dashboard search
  8. // query
  9. type dbSearchResponse struct {
  10. ID int `json:"id"`
  11. Title string `json:"title"`
  12. URI string `json:"uri"`
  13. Type string `json:"type"`
  14. Tags []string `json:"tags"`
  15. Starred bool `json:"isStarred"`
  16. }
  17. // dbCreateOrUpdateRequest represents the request sent to create or update a
  18. // dashboard
  19. type dbCreateOrUpdateRequest struct {
  20. Dashboard rawJSON `json:"dashboard"`
  21. Overwrite bool `json:"overwrite"`
  22. }
  23. // dbCreateOrUpdateResponse represents the response sent by the Grafana API to
  24. // a dashboard creation or update. All fields described from the Grafana
  25. // documentation aren't located in this structure because there are some we
  26. // don't need.
  27. type dbCreateOrUpdateResponse struct {
  28. Status string `json:"success"`
  29. Version int `json:"version,omitempty"`
  30. Message string `json:"message,omitempty"`
  31. }
  32. // Dashboard represents a Grafana dashboard, with its JSON definition, slug and
  33. // current version.
  34. type Dashboard struct {
  35. RawJSON []byte
  36. Name string
  37. Slug string
  38. Version int
  39. }
  40. // UnmarshalJSON tells the JSON parser how to unmarshal JSON data into an
  41. // instance of the Dashboard structure.
  42. // Returns an error if there was an issue unmarshalling the JSON.
  43. func (d *Dashboard) UnmarshalJSON(b []byte) (err error) {
  44. // Define the structure of what we want to parse
  45. var body struct {
  46. Dashboard rawJSON `json:"dashboard"`
  47. Meta struct {
  48. Slug string `json:"slug"`
  49. Version int `json:"version"`
  50. } `json:"meta"`
  51. }
  52. // Unmarshal the JSON into the newly defined structure
  53. if err = json.Unmarshal(b, &body); err != nil {
  54. return
  55. }
  56. // Define all fields with their corresponding value.
  57. d.Slug = body.Meta.Slug
  58. d.Version = body.Meta.Version
  59. d.RawJSON = body.Dashboard
  60. // Define the dashboard's name from the previously extracted JSON description
  61. err = d.setDashboardNameFromRawJSON()
  62. return
  63. }
  64. // setDashboardNameFromJSON finds a dashboard's name from the content of its
  65. // RawJSON field
  66. func (d *Dashboard) setDashboardNameFromRawJSON() (err error) {
  67. // Define the necessary structure to catch the dashboard's name
  68. var dashboard struct {
  69. Name string `json:"title"`
  70. }
  71. // Unmarshal the JSON content into the structure and set the dashboard's
  72. // name
  73. err = json.Unmarshal(d.RawJSON, &dashboard)
  74. d.Name = dashboard.Name
  75. return
  76. }
  77. // GetDashboardsURIs requests the Grafana API for the list of all dashboards,
  78. // then returns the dashboards' URIs. An URI will look like "db/[dashboard slug]".
  79. // Returns an error if there was an issue requesting the URIs or parsing the
  80. // response body.
  81. func (c *Client) GetDashboardsURIs() (URIs []string, err error) {
  82. resp, err := c.request("GET", "search", nil)
  83. if err != nil {
  84. return
  85. }
  86. var respBody []dbSearchResponse
  87. if err = json.Unmarshal(resp, &respBody); err != nil {
  88. return
  89. }
  90. URIs = make([]string, 0)
  91. for _, db := range respBody {
  92. URIs = append(URIs, db.URI)
  93. }
  94. return
  95. }
  96. // GetDashboard requests the Grafana API for a dashboard identified by a given
  97. // URI (using the same format as GetDashboardsURIs).
  98. // Returns the dashboard as an instance of the Dashboard structure.
  99. // Returns an error if there was an issue requesting the dashboard or parsing
  100. // the response body.
  101. func (c *Client) GetDashboard(URI string) (db *Dashboard, err error) {
  102. body, err := c.request("GET", "dashboards/"+URI, nil)
  103. if err != nil {
  104. return
  105. }
  106. db = new(Dashboard)
  107. err = json.Unmarshal(body, db)
  108. return
  109. }
  110. // CreateOrUpdateDashboard takes a given JSON content (as []byte) and create the
  111. // dashboard if it doesn't exist on the Grafana instance, else updates the
  112. // existing one. The Grafana API decides whether to create or update based on the
  113. // "id" attribute in the dashboard's JSON: If it's unkown or null, it's a
  114. // creation, else it's an update.
  115. // Returns an error if there was an issue generating the request body, performing
  116. // the request or decoding the response's body.
  117. func (c *Client) CreateOrUpdateDashboard(contentJSON []byte) (err error) {
  118. reqBody := dbCreateOrUpdateRequest{
  119. Dashboard: rawJSON(contentJSON),
  120. Overwrite: true,
  121. }
  122. // Generate the request body's JSON
  123. reqBodyJSON, err := json.Marshal(reqBody)
  124. if err != nil {
  125. return
  126. }
  127. var httpError *httpUnkownError
  128. var isHttpUnknownError bool
  129. // Send the request
  130. respBodyJSON, err := c.request("POST", "dashboards/db", reqBodyJSON)
  131. if err != nil {
  132. // Check the error against the httpUnkownError type in order to decide
  133. // how to process the error
  134. httpError, isHttpUnknownError = err.(*httpUnkownError)
  135. // We process httpUnkownError errors below, after we decoded the body
  136. if !isHttpUnknownError {
  137. return
  138. }
  139. }
  140. // Decode the response body
  141. var respBody dbCreateOrUpdateResponse
  142. if err = json.Unmarshal(respBodyJSON, &respBody); err != nil {
  143. return
  144. }
  145. if respBody.Status != "success" && isHttpUnknownError {
  146. // Get the dashboard's slug for logging
  147. var slug string
  148. slug, err = helpers.GetDashboardSlug(contentJSON)
  149. if err != nil {
  150. return
  151. }
  152. return fmt.Errorf(
  153. "Failed to update dashboard %s (%d %s): %s",
  154. slug, httpError.StatusCode, respBody.Status, respBody.Message,
  155. )
  156. }
  157. return
  158. }
  159. // DeleteDashboard deletes the dashboard identified by a given slug on the
  160. // Grafana API.
  161. // Returns an error if the process failed.
  162. func (c *Client) DeleteDashboard(slug string) (err error) {
  163. _, err = c.request("DELETE", "dashboards/db/"+slug, nil)
  164. return
  165. }