cluster_handler.go 10 KB

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