datacenter.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. // Copyright Joyent, Inc.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. package main
  7. //lint:file-ignore U1000 WIP
  8. import (
  9. "bytes"
  10. "errors"
  11. "fmt"
  12. "net/url"
  13. "strings"
  14. "time"
  15. "github.com/gofrs/uuid"
  16. "github.com/jawher/mow.cli"
  17. "github.com/olekukonko/tablewriter"
  18. )
  19. type Datacenters struct {
  20. *Conch
  21. }
  22. func (c *Conch) Datacenters() *Datacenters {
  23. return &Datacenters{c}
  24. }
  25. type Datacenter struct {
  26. ID uuid.UUID `json:"id"`
  27. Vendor string `json:"vendor"`
  28. VendorName string `json:"vendor_name,omitempty"`
  29. Region string `json:"region"`
  30. Location string `json:"location"`
  31. Created time.Time `json:"created"`
  32. Updated time.Time `json:"updated"`
  33. }
  34. func (d Datacenter) String() string {
  35. if API.JsonOnly {
  36. return API.AsJSON(d)
  37. }
  38. t, err := NewTemplate().Parse(datacenterTemplate)
  39. if err != nil {
  40. panic(err)
  41. }
  42. buf := new(bytes.Buffer)
  43. if err := t.Execute(buf, d); err != nil {
  44. panic(err)
  45. }
  46. return buf.String()
  47. }
  48. type DatacenterList []Datacenter
  49. func (dl DatacenterList) String() string {
  50. if API.JsonOnly {
  51. return API.AsJSON(dl)
  52. }
  53. tableString := &strings.Builder{}
  54. table := tablewriter.NewWriter(tableString)
  55. TableToMarkdown(table)
  56. table.SetHeader([]string{
  57. "ID",
  58. "Vendor",
  59. "Vendor Name",
  60. "Region",
  61. "Location",
  62. })
  63. for _, d := range dl {
  64. table.Append([]string{
  65. CutUUID(d.ID.String()),
  66. d.Vendor,
  67. d.VendorName,
  68. d.Region,
  69. d.Location,
  70. })
  71. }
  72. table.Render()
  73. return tableString.String()
  74. }
  75. /****/
  76. func (d *Datacenters) GetAll() DatacenterList {
  77. dl := make(DatacenterList, 0)
  78. res := d.Do(d.Sling().Get("/dc"))
  79. if ok := res.Parse(&dl); !ok {
  80. panic(res)
  81. }
  82. return dl
  83. }
  84. func (d *Datacenters) FindDatacenterID(id string) (bool, uuid.UUID) {
  85. ids := make([]uuid.UUID, 0)
  86. for _, datacenter := range d.GetAll() {
  87. ids = append(ids, datacenter.ID)
  88. }
  89. return FindUUID(id, ids)
  90. }
  91. func (d *Datacenters) Get(id uuid.UUID) Datacenter {
  92. var dc Datacenter
  93. uri := fmt.Sprintf(
  94. "/dc/%s",
  95. url.PathEscape(id.String()),
  96. )
  97. res := d.Do(d.Sling().Get(uri))
  98. if ok := res.Parse(&dc); !ok {
  99. panic(res)
  100. }
  101. return dc
  102. }
  103. func (d *Datacenters) Update(id uuid.UUID, region string, vendor string, location string, vendorName string) Datacenter {
  104. var dc Datacenter
  105. uri := fmt.Sprintf(
  106. "/dc/%s",
  107. url.PathEscape(id.String()),
  108. )
  109. payload := make(map[string]string)
  110. if region != "" {
  111. payload["region"] = region
  112. }
  113. if vendor != "" {
  114. payload["vendor"] = vendor
  115. }
  116. if vendorName != "" {
  117. payload["vendor_name"] = vendorName
  118. }
  119. if location != "" {
  120. payload["location"] = location
  121. }
  122. if len(payload) == 0 {
  123. panic(errors.New("at least one property must be defined: region, vendor, vendor name, location"))
  124. }
  125. res := d.Do(
  126. d.Sling().New().Post(uri).
  127. Set("Content-Type", "application/json").
  128. BodyJSON(payload),
  129. )
  130. if ok := res.Parse(&dc); !ok {
  131. panic(res)
  132. }
  133. return dc
  134. }
  135. func (d *Datacenters) Create(region string, vendor string, location string, vendorName string) Datacenter {
  136. payload := make(map[string]string)
  137. if vendor == "" {
  138. panic(errors.New("'vendor' cannot be empty"))
  139. }
  140. payload["vendor"] = vendor
  141. if region == "" {
  142. panic(errors.New("'region' cannot be empty"))
  143. }
  144. payload["region"] = region
  145. if location == "" {
  146. panic(errors.New("'location' cannot be empty"))
  147. }
  148. payload["location"] = location
  149. if vendorName != "" {
  150. payload["vendor_name"] = vendorName
  151. }
  152. /**/
  153. var dc Datacenter
  154. // We get a 303 on success
  155. res := d.Do(
  156. d.Sling().New().Post("/dc").
  157. Set("Content-Type", "application/json").
  158. BodyJSON(payload),
  159. )
  160. if ok := res.Parse(&dc); !ok {
  161. panic(res)
  162. }
  163. return dc
  164. }
  165. func (d *Datacenters) Delete(id uuid.UUID) {
  166. uri := fmt.Sprintf(
  167. "/dc/%s",
  168. url.PathEscape(id.String()),
  169. )
  170. res := d.Do(d.Sling().New().Delete(uri))
  171. if res.StatusCode() != 204 {
  172. // I know this is weird. Like in other places, it should be impossible
  173. // to reach here unless the status code is 204. The API returns 204
  174. // (which gets us here) or 409 (which will explode before it gets here).
  175. // If we got here via some other code, then there's some new behavior
  176. // that we need to know about.
  177. panic(res)
  178. }
  179. }
  180. /****/
  181. func (d *Datacenters) GetRooms(id uuid.UUID) RoomList {
  182. rooms := make(RoomList, 0)
  183. uri := fmt.Sprintf(
  184. "/dc/%s/rooms",
  185. url.PathEscape(id.String()),
  186. )
  187. res := d.Do(d.Sling().Get(uri))
  188. if ok := res.Parse(&rooms); !ok {
  189. panic(res)
  190. }
  191. return rooms
  192. }
  193. /****/
  194. func init() {
  195. App.Command("datacenters", "Work with all the datacenters you have access to", func(cmd *cli.Cmd) {
  196. cmd.Before = RequireSysAdmin
  197. cmd.Command("get", "Get a list of all datacenters", func(cmd *cli.Cmd) {
  198. cmd.Action = func() {
  199. fmt.Println(API.Datacenters().GetAll())
  200. }
  201. })
  202. cmd.Command("create", "Create a single datacenter", func(cmd *cli.Cmd) {
  203. var (
  204. vendorOpt = cmd.StringOpt("vendor", "", "Vendor")
  205. regionOpt = cmd.StringOpt("region", "", "Region")
  206. locationOpt = cmd.StringOpt("location", "", "Location")
  207. vendorNameOpt = cmd.StringOpt("vendor-name", "", "Vendor Name")
  208. )
  209. cmd.Spec = "--vendor --region --location [OPTIONS]"
  210. cmd.Action = func() {
  211. // The user can be very silly and supply something like
  212. // '--vendor ""' which will pass the cli lib's requirement
  213. // check but is still crap
  214. if *vendorOpt == "" {
  215. panic(errors.New("--vendor is required"))
  216. }
  217. if *regionOpt == "" {
  218. panic(errors.New("--region is required"))
  219. }
  220. if *locationOpt == "" {
  221. panic(errors.New("--location is required"))
  222. }
  223. fmt.Println(API.Datacenters().Create(
  224. *regionOpt,
  225. *vendorOpt,
  226. *locationOpt,
  227. *vendorNameOpt,
  228. ))
  229. }
  230. })
  231. })
  232. App.Command("datacenter", "Deal with a single datacenter", func(cmd *cli.Cmd) {
  233. var datacenterID uuid.UUID
  234. idArg := cmd.StringArg(
  235. "UUID",
  236. "",
  237. "The UUID of the datacenter. Short UUIDs (first segment) accepted",
  238. )
  239. cmd.Spec = "UUID"
  240. cmd.Before = func() {
  241. RequireSysAdmin()
  242. var ok bool
  243. if ok, datacenterID = API.Datacenters().FindDatacenterID(*idArg); !ok {
  244. panic(errors.New("could not find the datacenter"))
  245. }
  246. }
  247. cmd.Command("get", "Information about a single datacenter", func(cmd *cli.Cmd) {
  248. cmd.Action = func() {
  249. fmt.Println(API.Datacenters().Get(datacenterID))
  250. }
  251. })
  252. cmd.Command("delete", "Delete a single datacenter", func(cmd *cli.Cmd) {
  253. cmd.Action = func() {
  254. // Lower layers panic if there's a problem
  255. API.Datacenters().Delete(datacenterID)
  256. fmt.Println(API.Datacenters().GetAll())
  257. }
  258. })
  259. cmd.Command("update", "Update a single datacenter", func(cmd *cli.Cmd) {
  260. regionOpt := cmd.StringOpt(
  261. "region",
  262. "",
  263. "Region identifier",
  264. )
  265. vendorOpt := cmd.StringOpt(
  266. "vendor",
  267. "",
  268. "Vendor",
  269. )
  270. vendorNameOpt := cmd.StringOpt(
  271. "vendor-name",
  272. "",
  273. "Vendor Name",
  274. )
  275. locationOpt := cmd.StringOpt(
  276. "location",
  277. "",
  278. "Location",
  279. )
  280. cmd.Action = func() {
  281. var count int
  282. if *regionOpt != "" {
  283. count++
  284. }
  285. if *vendorOpt != "" {
  286. count++
  287. }
  288. if *vendorNameOpt != "" {
  289. count++
  290. }
  291. if *locationOpt != "" {
  292. count++
  293. }
  294. if count == 0 {
  295. panic(errors.New("one option must be provided"))
  296. }
  297. fmt.Println(API.Datacenters().Update(
  298. datacenterID,
  299. *regionOpt,
  300. *vendorOpt,
  301. *locationOpt,
  302. *vendorNameOpt,
  303. ))
  304. }
  305. })
  306. cmd.Command("rooms", "Get the room list for a single datacenter", func(cmd *cli.Cmd) {
  307. cmd.Action = func() {
  308. fmt.Println(API.Datacenters().GetRooms(datacenterID))
  309. }
  310. })
  311. })
  312. }