cluster_handler.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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/models"
  9. )
  10. // HandleCreateProjectCluster creates a new cluster
  11. func (app *App) HandleCreateProjectCluster(w http.ResponseWriter, r *http.Request) {
  12. projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
  13. if err != nil || projID == 0 {
  14. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  15. return
  16. }
  17. form := &forms.CreateClusterForm{
  18. ProjectID: uint(projID),
  19. }
  20. // decode from JSON to form value
  21. if err := json.NewDecoder(r.Body).Decode(form); err != nil {
  22. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  23. return
  24. }
  25. // validate the form
  26. if err := app.validator.Struct(form); err != nil {
  27. app.handleErrorFormValidation(err, ErrProjectValidateFields, w)
  28. return
  29. }
  30. // convert the form to a registry
  31. cluster, err := form.ToCluster()
  32. if err != nil {
  33. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  34. return
  35. }
  36. // handle write to the database
  37. cluster, err = app.Repo.Cluster.CreateCluster(cluster)
  38. if err != nil {
  39. app.handleErrorDataWrite(err, w)
  40. return
  41. }
  42. app.Logger.Info().Msgf("New cluster created: %d", cluster.ID)
  43. w.WriteHeader(http.StatusCreated)
  44. clusterExt := cluster.Externalize()
  45. if err := json.NewEncoder(w).Encode(clusterExt); err != nil {
  46. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  47. return
  48. }
  49. }
  50. // HandleReadProjectCluster reads a cluster by id
  51. func (app *App) HandleReadProjectCluster(w http.ResponseWriter, r *http.Request) {
  52. id, err := strconv.ParseUint(chi.URLParam(r, "cluster_id"), 0, 64)
  53. if err != nil || id == 0 {
  54. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  55. return
  56. }
  57. cluster, err := app.Repo.Cluster.ReadCluster(uint(id))
  58. if err != nil {
  59. app.handleErrorRead(err, ErrProjectDataRead, w)
  60. return
  61. }
  62. clusterExt := cluster.Externalize()
  63. w.WriteHeader(http.StatusOK)
  64. if err := json.NewEncoder(w).Encode(clusterExt); err != nil {
  65. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  66. return
  67. }
  68. }
  69. // HandleListProjectClusters returns a list of clusters that have linked Integrations.
  70. func (app *App) HandleListProjectClusters(w http.ResponseWriter, r *http.Request) {
  71. projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
  72. if err != nil || projID == 0 {
  73. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  74. return
  75. }
  76. clusters, err := app.Repo.Cluster.ListClustersByProjectID(uint(projID))
  77. if err != nil {
  78. app.handleErrorRead(err, ErrProjectDataRead, w)
  79. return
  80. }
  81. extClusters := make([]*models.ClusterExternal, 0)
  82. for _, cluster := range clusters {
  83. extClusters = append(extClusters, cluster.Externalize())
  84. }
  85. w.WriteHeader(http.StatusOK)
  86. if err := json.NewEncoder(w).Encode(extClusters); err != nil {
  87. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  88. return
  89. }
  90. }
  91. // HandleUpdateProjectCluster updates a project's cluster
  92. func (app *App) HandleUpdateProjectCluster(w http.ResponseWriter, r *http.Request) {
  93. projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
  94. if err != nil || projID == 0 {
  95. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  96. return
  97. }
  98. clusterID, err := strconv.ParseUint(chi.URLParam(r, "cluster_id"), 0, 64)
  99. if err != nil || clusterID == 0 {
  100. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  101. return
  102. }
  103. form := &forms.UpdateClusterForm{
  104. ID: uint(clusterID),
  105. }
  106. // decode from JSON to form value
  107. if err := json.NewDecoder(r.Body).Decode(form); err != nil {
  108. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  109. return
  110. }
  111. // validate the form
  112. if err := app.validator.Struct(form); err != nil {
  113. app.handleErrorFormValidation(err, ErrProjectValidateFields, w)
  114. return
  115. }
  116. // convert the form to a registry
  117. cluster, err := form.ToCluster(app.Repo.Cluster)
  118. if err != nil {
  119. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  120. return
  121. }
  122. // handle write to the database
  123. cluster, err = app.Repo.Cluster.UpdateCluster(cluster)
  124. if err != nil {
  125. app.handleErrorDataWrite(err, w)
  126. return
  127. }
  128. w.WriteHeader(http.StatusOK)
  129. clusterExt := cluster.Externalize()
  130. if err := json.NewEncoder(w).Encode(clusterExt); err != nil {
  131. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  132. return
  133. }
  134. }
  135. // HandleDeleteProjectCluster handles the deletion of a Cluster via the cluster ID
  136. func (app *App) HandleDeleteProjectCluster(w http.ResponseWriter, r *http.Request) {
  137. id, err := strconv.ParseUint(chi.URLParam(r, "cluster_id"), 0, 64)
  138. if err != nil || id == 0 {
  139. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  140. return
  141. }
  142. cluster, err := app.Repo.Cluster.ReadCluster(uint(id))
  143. if err != nil {
  144. app.handleErrorRead(err, ErrProjectDataRead, w)
  145. return
  146. }
  147. err = app.Repo.Cluster.DeleteCluster(cluster)
  148. if err != nil {
  149. app.handleErrorRead(err, ErrProjectDataRead, w)
  150. return
  151. }
  152. w.WriteHeader(http.StatusOK)
  153. }
  154. // HandleCreateProjectClusterCandidates handles the creation of ClusterCandidates using
  155. // a kubeconfig and a project id
  156. func (app *App) HandleCreateProjectClusterCandidates(w http.ResponseWriter, r *http.Request) {
  157. projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
  158. if err != nil || projID == 0 {
  159. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  160. return
  161. }
  162. form := &forms.CreateClusterCandidatesForm{
  163. ProjectID: uint(projID),
  164. }
  165. // decode from JSON to form value
  166. if err := json.NewDecoder(r.Body).Decode(form); err != nil {
  167. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  168. return
  169. }
  170. // validate the form
  171. if err := app.validator.Struct(form); err != nil {
  172. app.handleErrorFormValidation(err, ErrProjectValidateFields, w)
  173. return
  174. }
  175. // convert the form to a ClusterCandidate
  176. ccs, err := form.ToClusterCandidates(app.ServerConf.IsLocal)
  177. if err != nil {
  178. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  179. return
  180. }
  181. extClusters := make([]*models.ClusterCandidateExternal, 0)
  182. session, err := app.Store.Get(r, app.ServerConf.CookieName)
  183. if err != nil {
  184. http.Error(w, err.Error(), http.StatusInternalServerError)
  185. return
  186. }
  187. userID, _ := session.Values["user_id"].(uint)
  188. for _, cc := range ccs {
  189. // handle write to the database
  190. cc, err = app.Repo.Cluster.CreateClusterCandidate(cc)
  191. if err != nil {
  192. app.handleErrorDataWrite(err, w)
  193. return
  194. }
  195. app.Logger.Info().Msgf("New cluster candidate created: %d", cc.ID)
  196. // if the ClusterCandidate does not have any actions to perform, create the Cluster
  197. // automatically
  198. if len(cc.Resolvers) == 0 {
  199. // we query the repo again to get the decrypted version of the cluster candidate
  200. cc, err = app.Repo.Cluster.ReadClusterCandidate(cc.ID)
  201. if err != nil {
  202. app.handleErrorDataRead(err, w)
  203. return
  204. }
  205. clusterForm := &forms.ResolveClusterForm{
  206. Resolver: &models.ClusterResolverAll{},
  207. ClusterCandidateID: cc.ID,
  208. ProjectID: uint(projID),
  209. UserID: userID,
  210. }
  211. err := clusterForm.ResolveIntegration(*app.Repo)
  212. if err != nil {
  213. app.handleErrorDataWrite(err, w)
  214. return
  215. }
  216. cluster, err := clusterForm.ResolveCluster(*app.Repo)
  217. if err != nil {
  218. app.handleErrorDataWrite(err, w)
  219. return
  220. }
  221. cc, err = app.Repo.Cluster.UpdateClusterCandidateCreatedClusterID(cc.ID, cluster.ID)
  222. if err != nil {
  223. app.handleErrorDataWrite(err, w)
  224. return
  225. }
  226. app.Logger.Info().Msgf("New cluster created: %d", cluster.ID)
  227. }
  228. extClusters = append(extClusters, cc.Externalize())
  229. }
  230. w.WriteHeader(http.StatusCreated)
  231. if err := json.NewEncoder(w).Encode(extClusters); err != nil {
  232. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  233. return
  234. }
  235. }
  236. // HandleListProjectClusterCandidates returns a list of externalized ClusterCandidates
  237. // ([]models.ClusterCandidateExternal) based on a project ID
  238. func (app *App) HandleListProjectClusterCandidates(w http.ResponseWriter, r *http.Request) {
  239. projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
  240. if err != nil || projID == 0 {
  241. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  242. return
  243. }
  244. ccs, err := app.Repo.Cluster.ListClusterCandidatesByProjectID(uint(projID))
  245. if err != nil {
  246. app.handleErrorRead(err, ErrProjectDataRead, w)
  247. return
  248. }
  249. extCCs := make([]*models.ClusterCandidateExternal, 0)
  250. for _, cc := range ccs {
  251. extCCs = append(extCCs, cc.Externalize())
  252. }
  253. w.WriteHeader(http.StatusOK)
  254. if err := json.NewEncoder(w).Encode(extCCs); err != nil {
  255. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  256. return
  257. }
  258. }
  259. // HandleResolveClusterCandidate accepts a list of resolving objects (ClusterResolver)
  260. // for a given ClusterCandidate, which "resolves" that ClusterCandidate and creates a
  261. // Cluster for a specific project
  262. func (app *App) HandleResolveClusterCandidate(w http.ResponseWriter, r *http.Request) {
  263. projID, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
  264. if err != nil || projID == 0 {
  265. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  266. return
  267. }
  268. candID, err := strconv.ParseUint(chi.URLParam(r, "candidate_id"), 0, 64)
  269. if err != nil || projID == 0 {
  270. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  271. return
  272. }
  273. session, err := app.Store.Get(r, app.ServerConf.CookieName)
  274. if err != nil {
  275. http.Error(w, err.Error(), http.StatusInternalServerError)
  276. return
  277. }
  278. userID, _ := session.Values["user_id"].(uint)
  279. // decode actions from request
  280. resolver := &models.ClusterResolverAll{}
  281. if err := json.NewDecoder(r.Body).Decode(resolver); err != nil {
  282. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  283. return
  284. }
  285. clusterResolver := &forms.ResolveClusterForm{
  286. Resolver: resolver,
  287. ClusterCandidateID: uint(candID),
  288. ProjectID: uint(projID),
  289. UserID: userID,
  290. }
  291. err = clusterResolver.ResolveIntegration(*app.Repo)
  292. if err != nil {
  293. app.handleErrorDataWrite(err, w)
  294. return
  295. }
  296. cluster, err := clusterResolver.ResolveCluster(*app.Repo)
  297. if err != nil {
  298. app.handleErrorDataWrite(err, w)
  299. return
  300. }
  301. _, err = app.Repo.Cluster.UpdateClusterCandidateCreatedClusterID(uint(candID), cluster.ID)
  302. if err != nil {
  303. app.handleErrorDataWrite(err, w)
  304. return
  305. }
  306. app.Logger.Info().Msgf("New cluster created: %d", cluster.ID)
  307. clusterExt := cluster.Externalize()
  308. w.WriteHeader(http.StatusCreated)
  309. if err := json.NewEncoder(w).Encode(clusterExt); err != nil {
  310. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  311. return
  312. }
  313. }