provision_handler.go 9.6 KB

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