provision_handler.go 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. package api
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "strconv"
  6. "github.com/go-chi/chi"
  7. "github.com/porter-dev/porter/internal/forms"
  8. "github.com/porter-dev/porter/internal/kubernetes"
  9. "github.com/porter-dev/porter/internal/kubernetes/provisioner"
  10. "github.com/porter-dev/porter/internal/models"
  11. "github.com/porter-dev/porter/internal/adapter"
  12. )
  13. // HandleProvisionTest will create a test resource by deploying a provisioner
  14. // container pod
  15. func (app *App) HandleProvisionTest(w http.ResponseWriter, r *http.Request) {
  16. projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
  17. if err != nil || projID == 0 {
  18. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  19. return
  20. }
  21. // create a new agent
  22. agent, err := kubernetes.GetAgentInClusterConfig()
  23. if err != nil {
  24. app.handleErrorDataRead(err, w)
  25. return
  26. }
  27. _, err = agent.ProvisionTest(
  28. uint(projID),
  29. provisioner.Apply,
  30. &provisioner.PostgresConf{
  31. Host: app.DBConf.Host,
  32. Port: app.DBConf.Port,
  33. },
  34. app.RedisConf,
  35. )
  36. if err != nil {
  37. app.handleErrorInternal(err, w)
  38. return
  39. }
  40. w.WriteHeader(http.StatusOK)
  41. }
  42. // HandleProvisionAWSECRInfra provisions a new aws ECR instance for a project
  43. func (app *App) HandleProvisionAWSECRInfra(w http.ResponseWriter, r *http.Request) {
  44. projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
  45. if err != nil || projID == 0 {
  46. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  47. return
  48. }
  49. form := &forms.CreateECRInfra{
  50. ProjectID: uint(projID),
  51. }
  52. // decode from JSON to form value
  53. if err := json.NewDecoder(r.Body).Decode(form); err != nil {
  54. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  55. return
  56. }
  57. // validate the form
  58. if err := app.validator.Struct(form); err != nil {
  59. app.handleErrorFormValidation(err, ErrProjectValidateFields, w)
  60. return
  61. }
  62. // convert the form to an aws infra instance
  63. infra, err := form.ToAWSInfra()
  64. if err != nil {
  65. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  66. return
  67. }
  68. // handle write to the database
  69. infra, err = app.Repo.AWSInfra.CreateAWSInfra(infra)
  70. if err != nil {
  71. app.handleErrorDataWrite(err, w)
  72. return
  73. }
  74. awsInt, err := app.Repo.AWSIntegration.ReadAWSIntegration(infra.AWSIntegrationID)
  75. if err != nil {
  76. infra.Status = models.StatusError
  77. infra, _ = app.Repo.AWSInfra.UpdateAWSInfra(infra)
  78. app.handleErrorDataRead(err, w)
  79. return
  80. }
  81. // launch provisioning pod
  82. agent, err := kubernetes.GetAgentInClusterConfig()
  83. if err != nil {
  84. infra.Status = models.StatusError
  85. infra, _ = app.Repo.AWSInfra.UpdateAWSInfra(infra)
  86. app.handleErrorDataRead(err, w)
  87. return
  88. }
  89. _, err = agent.ProvisionECR(
  90. uint(projID),
  91. awsInt,
  92. form.ECRName,
  93. infra,
  94. provisioner.Apply,
  95. &provisioner.PostgresConf{
  96. Host: app.DBConf.Host,
  97. Port: app.DBConf.Port,
  98. },
  99. app.RedisConf,
  100. )
  101. if err != nil {
  102. infra.Status = models.StatusError
  103. infra, _ = app.Repo.AWSInfra.UpdateAWSInfra(infra)
  104. app.handleErrorInternal(err, w)
  105. return
  106. }
  107. app.Logger.Info().Msgf("New aws ecr infra created: %d", infra.ID)
  108. w.WriteHeader(http.StatusCreated)
  109. infraExt := infra.Externalize()
  110. if err := json.NewEncoder(w).Encode(infraExt); err != nil {
  111. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  112. return
  113. }
  114. }
  115. // HandleDestroyAWSECRInfra destroys ecr infra
  116. func (app *App) HandleDestroyAWSECRInfra(w http.ResponseWriter, r *http.Request) {
  117. // get path parameters
  118. infraID, err := strconv.ParseUint(chi.URLParam(r, "infra_id"), 10, 64)
  119. if err != nil {
  120. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  121. return
  122. }
  123. // read infra to get id
  124. infra, err := app.Repo.AWSInfra.ReadAWSInfra(uint(infraID))
  125. if err != nil {
  126. app.handleErrorDataRead(err, w)
  127. return
  128. }
  129. awsInt, err := app.Repo.AWSIntegration.ReadAWSIntegration(infra.AWSIntegrationID)
  130. form := &forms.DestroyECRInfra{}
  131. // decode from JSON to form value
  132. if err := json.NewDecoder(r.Body).Decode(form); err != nil {
  133. infra.Status = models.StatusError
  134. infra, _ = app.Repo.AWSInfra.UpdateAWSInfra(infra)
  135. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  136. return
  137. }
  138. // validate the form
  139. if err := app.validator.Struct(form); err != nil {
  140. infra.Status = models.StatusError
  141. infra, _ = app.Repo.AWSInfra.UpdateAWSInfra(infra)
  142. app.handleErrorFormValidation(err, ErrProjectValidateFields, w)
  143. return
  144. }
  145. // launch provisioning destruction pod
  146. agent, err := kubernetes.GetAgentInClusterConfig()
  147. if err != nil {
  148. infra.Status = models.StatusError
  149. infra, _ = app.Repo.AWSInfra.UpdateAWSInfra(infra)
  150. app.handleErrorDataRead(err, w)
  151. return
  152. }
  153. // mark infra for deletion
  154. infra.Status = models.StatusDestroying
  155. infra, err = app.Repo.AWSInfra.UpdateAWSInfra(infra)
  156. if err != nil {
  157. app.handleErrorDataWrite(err, w)
  158. return
  159. }
  160. _, err = agent.ProvisionECR(
  161. infra.ProjectID,
  162. awsInt,
  163. form.ECRName,
  164. infra,
  165. provisioner.Destroy,
  166. &provisioner.PostgresConf{
  167. Host: app.DBConf.Host,
  168. Port: app.DBConf.Port,
  169. },
  170. app.RedisConf,
  171. )
  172. if err != nil {
  173. app.handleErrorInternal(err, w)
  174. return
  175. }
  176. app.Logger.Info().Msgf("AWS ECR infra marked for destruction: %d", infra.ID)
  177. w.WriteHeader(http.StatusOK)
  178. }
  179. // HandleProvisionAWSEKSInfra provisions a new aws EKS instance for a project
  180. func (app *App) HandleProvisionAWSEKSInfra(w http.ResponseWriter, r *http.Request) {
  181. projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
  182. if err != nil || projID == 0 {
  183. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  184. return
  185. }
  186. form := &forms.CreateEKSInfra{
  187. ProjectID: uint(projID),
  188. }
  189. // decode from JSON to form value
  190. if err := json.NewDecoder(r.Body).Decode(form); err != nil {
  191. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  192. return
  193. }
  194. // validate the form
  195. if err := app.validator.Struct(form); err != nil {
  196. app.handleErrorFormValidation(err, ErrProjectValidateFields, w)
  197. return
  198. }
  199. // convert the form to an aws infra instance
  200. infra, err := form.ToAWSInfra()
  201. if err != nil {
  202. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  203. return
  204. }
  205. // handle write to the database
  206. infra, err = app.Repo.AWSInfra.CreateAWSInfra(infra)
  207. if err != nil {
  208. app.handleErrorDataWrite(err, w)
  209. return
  210. }
  211. awsInt, err := app.Repo.AWSIntegration.ReadAWSIntegration(infra.AWSIntegrationID)
  212. if err != nil {
  213. infra.Status = models.StatusError
  214. infra, _ = app.Repo.AWSInfra.UpdateAWSInfra(infra)
  215. app.handleErrorDataRead(err, w)
  216. return
  217. }
  218. // launch provisioning pod
  219. agent, err := kubernetes.GetAgentInClusterConfig()
  220. if err != nil {
  221. infra.Status = models.StatusError
  222. infra, _ = app.Repo.AWSInfra.UpdateAWSInfra(infra)
  223. app.handleErrorDataRead(err, w)
  224. return
  225. }
  226. _, err = agent.ProvisionEKS(
  227. uint(projID),
  228. awsInt,
  229. form.EKSName,
  230. infra,
  231. provisioner.Apply,
  232. &provisioner.PostgresConf{
  233. Host: app.DBConf.Host,
  234. Port: app.DBConf.Port,
  235. },
  236. app.RedisConf,
  237. )
  238. if err != nil {
  239. infra.Status = models.StatusError
  240. infra, _ = app.Repo.AWSInfra.UpdateAWSInfra(infra)
  241. app.handleErrorInternal(err, w)
  242. return
  243. }
  244. app.Logger.Info().Msgf("New aws eks infra created: %d", infra.ID)
  245. w.WriteHeader(http.StatusCreated)
  246. infraExt := infra.Externalize()
  247. if err := json.NewEncoder(w).Encode(infraExt); err != nil {
  248. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  249. return
  250. }
  251. }
  252. // HandleDestroyAWSEKSInfra destroys eks infra
  253. func (app *App) HandleDestroyAWSEKSInfra(w http.ResponseWriter, r *http.Request) {
  254. // get path parameters
  255. infraID, err := strconv.ParseUint(chi.URLParam(r, "infra_id"), 10, 64)
  256. if err != nil {
  257. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  258. return
  259. }
  260. // read infra to get id
  261. infra, err := app.Repo.AWSInfra.ReadAWSInfra(uint(infraID))
  262. if err != nil {
  263. app.handleErrorDataRead(err, w)
  264. return
  265. }
  266. awsInt, err := app.Repo.AWSIntegration.ReadAWSIntegration(infra.AWSIntegrationID)
  267. form := &forms.DestroyEKSInfra{}
  268. // decode from JSON to form value
  269. if err := json.NewDecoder(r.Body).Decode(form); err != nil {
  270. infra.Status = models.StatusError
  271. infra, _ = app.Repo.AWSInfra.UpdateAWSInfra(infra)
  272. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  273. return
  274. }
  275. // validate the form
  276. if err := app.validator.Struct(form); err != nil {
  277. infra.Status = models.StatusError
  278. infra, _ = app.Repo.AWSInfra.UpdateAWSInfra(infra)
  279. app.handleErrorFormValidation(err, ErrProjectValidateFields, w)
  280. return
  281. }
  282. // launch provisioning destruction pod
  283. agent, err := kubernetes.GetAgentInClusterConfig()
  284. if err != nil {
  285. infra.Status = models.StatusError
  286. infra, _ = app.Repo.AWSInfra.UpdateAWSInfra(infra)
  287. app.handleErrorDataRead(err, w)
  288. return
  289. }
  290. // mark infra for deletion
  291. infra.Status = models.StatusDestroying
  292. infra, err = app.Repo.AWSInfra.UpdateAWSInfra(infra)
  293. if err != nil {
  294. app.handleErrorDataWrite(err, w)
  295. return
  296. }
  297. _, err = agent.ProvisionEKS(
  298. infra.ProjectID,
  299. awsInt,
  300. form.EKSName,
  301. infra,
  302. provisioner.Destroy,
  303. &provisioner.PostgresConf{
  304. Host: app.DBConf.Host,
  305. Port: app.DBConf.Port,
  306. },
  307. app.RedisConf,
  308. )
  309. if err != nil {
  310. app.handleErrorInternal(err, w)
  311. return
  312. }
  313. app.Logger.Info().Msgf("AWS EKS infra marked for destruction: %d", infra.ID)
  314. w.WriteHeader(http.StatusOK)
  315. }
  316. // HandleGetProvisioningLogs returns real-time logs of the provisioning process via websockets
  317. func (app *App) HandleGetProvisioningLogs(w http.ResponseWriter, r *http.Request) {
  318. // get path parameters
  319. infraID, err := strconv.ParseUint(chi.URLParam(r, "infra_id"), 10, 64)
  320. if err != nil {
  321. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  322. return
  323. }
  324. // read infra to get id
  325. infra, err := app.Repo.AWSInfra.ReadAWSInfra(uint(infraID))
  326. if err != nil {
  327. app.handleErrorDataRead(err, w)
  328. return
  329. }
  330. streamName := infra.GetID()
  331. upgrader.CheckOrigin = func(r *http.Request) bool { return true }
  332. // upgrade to websocket.
  333. conn, err := upgrader.Upgrade(w, r, nil)
  334. if err != nil {
  335. app.handleErrorUpgradeWebsocket(err, w)
  336. }
  337. client, err := adapter.NewRedisClient(app.RedisConf)
  338. if err != nil {
  339. app.handleErrorInternal(err, w)
  340. return
  341. }
  342. err = provisioner.ResourceStream(client, streamName, conn)
  343. if err != nil {
  344. app.handleErrorWebsocketWrite(err, w)
  345. return
  346. }
  347. }