provision_handler.go 9.5 KB

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