k8s_handler.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
  1. package api
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "net/url"
  7. "github.com/go-chi/chi"
  8. "github.com/gorilla/schema"
  9. "github.com/gorilla/websocket"
  10. "github.com/porter-dev/porter/internal/forms"
  11. "github.com/porter-dev/porter/internal/kubernetes"
  12. "github.com/porter-dev/porter/internal/kubernetes/prometheus"
  13. v1 "k8s.io/api/core/v1"
  14. "k8s.io/client-go/tools/clientcmd"
  15. )
  16. // Enumeration of k8s API error codes, represented as int64
  17. const (
  18. ErrK8sDecode ErrorCode = iota + 600
  19. ErrK8sValidate
  20. ErrEnvDecode
  21. )
  22. var upgrader = websocket.Upgrader{
  23. ReadBufferSize: 1024,
  24. WriteBufferSize: 1024,
  25. }
  26. // HandleListNamespaces retrieves a list of namespaces
  27. func (app *App) HandleListNamespaces(w http.ResponseWriter, r *http.Request) {
  28. vals, err := url.ParseQuery(r.URL.RawQuery)
  29. if err != nil {
  30. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  31. return
  32. }
  33. // get the filter options
  34. form := &forms.K8sForm{
  35. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  36. Repo: app.Repo,
  37. DigitalOceanOAuth: app.DOConf,
  38. },
  39. }
  40. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  41. // validate the form
  42. if err := app.validator.Struct(form); err != nil {
  43. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  44. return
  45. }
  46. // create a new agent
  47. var agent *kubernetes.Agent
  48. if app.ServerConf.IsTesting {
  49. agent = app.TestAgents.K8sAgent
  50. } else {
  51. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  52. }
  53. namespaces, err := agent.ListNamespaces()
  54. if err != nil {
  55. app.handleErrorDataRead(err, w)
  56. return
  57. }
  58. if err := json.NewEncoder(w).Encode(namespaces); err != nil {
  59. app.handleErrorFormDecoding(err, ErrK8sDecode, w)
  60. return
  61. }
  62. }
  63. // HandleCreateConfigMap deletes the pod given the name and namespace.
  64. func (app *App) HandleCreateConfigMap(w http.ResponseWriter, r *http.Request) {
  65. fmt.Println("CREATING CONFGIMAP")
  66. vals, err := url.ParseQuery(r.URL.RawQuery)
  67. if err != nil {
  68. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  69. return
  70. }
  71. // get the filter options
  72. form := &forms.K8sForm{
  73. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  74. Repo: app.Repo,
  75. DigitalOceanOAuth: app.DOConf,
  76. },
  77. }
  78. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  79. // validate the form
  80. if err := app.validator.Struct(form); err != nil {
  81. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  82. return
  83. }
  84. // create a new agent
  85. var agent *kubernetes.Agent
  86. if app.ServerConf.IsTesting {
  87. agent = app.TestAgents.K8sAgent
  88. } else {
  89. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  90. }
  91. configMap := &forms.ConfigMapForm{}
  92. if err := json.NewDecoder(r.Body).Decode(configMap); err != nil {
  93. app.handleErrorFormDecoding(err, ErrUserDecode, w)
  94. return
  95. }
  96. _, err = agent.CreateConfigMap(configMap.Name, configMap.Namespace, configMap.EnvVariables)
  97. if err != nil {
  98. app.handleErrorInternal(err, w)
  99. return
  100. }
  101. if err := json.NewEncoder(w).Encode(configMap); err != nil {
  102. app.handleErrorFormDecoding(err, ErrEnvDecode, w)
  103. return
  104. }
  105. w.WriteHeader(http.StatusOK)
  106. return
  107. }
  108. // HandleListConfigMaps lists all configmaps in a namespace.
  109. func (app *App) HandleListConfigMaps(w http.ResponseWriter, r *http.Request) {
  110. vals, err := url.ParseQuery(r.URL.RawQuery)
  111. if err != nil {
  112. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  113. return
  114. }
  115. // get the filter options
  116. form := &forms.K8sForm{
  117. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  118. Repo: app.Repo,
  119. DigitalOceanOAuth: app.DOConf,
  120. },
  121. }
  122. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  123. // validate the form
  124. if err := app.validator.Struct(form); err != nil {
  125. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  126. return
  127. }
  128. // create a new agent
  129. var agent *kubernetes.Agent
  130. if app.ServerConf.IsTesting {
  131. agent = app.TestAgents.K8sAgent
  132. } else {
  133. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  134. }
  135. configMaps, err := agent.ListConfigMaps(vals["namespace"][0])
  136. if err != nil {
  137. app.handleErrorInternal(err, w)
  138. return
  139. }
  140. if err := json.NewEncoder(w).Encode(configMaps); err != nil {
  141. app.handleErrorFormDecoding(err, ErrEnvDecode, w)
  142. return
  143. }
  144. w.WriteHeader(http.StatusOK)
  145. return
  146. }
  147. // HandleGetConfigMap retreives the configmap given the name and namespace.
  148. func (app *App) HandleGetConfigMap(w http.ResponseWriter, r *http.Request) {
  149. vals, err := url.ParseQuery(r.URL.RawQuery)
  150. if err != nil {
  151. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  152. return
  153. }
  154. // get the filter options
  155. form := &forms.K8sForm{
  156. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  157. Repo: app.Repo,
  158. DigitalOceanOAuth: app.DOConf,
  159. },
  160. }
  161. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  162. // validate the form
  163. if err := app.validator.Struct(form); err != nil {
  164. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  165. return
  166. }
  167. // create a new agent
  168. var agent *kubernetes.Agent
  169. if app.ServerConf.IsTesting {
  170. agent = app.TestAgents.K8sAgent
  171. } else {
  172. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  173. }
  174. configMap, err := agent.GetConfigMap(vals["name"][0], vals["namespace"][0])
  175. if err != nil {
  176. app.handleErrorInternal(err, w)
  177. return
  178. }
  179. if err := json.NewEncoder(w).Encode(configMap); err != nil {
  180. app.handleErrorFormDecoding(err, ErrEnvDecode, w)
  181. return
  182. }
  183. w.WriteHeader(http.StatusOK)
  184. return
  185. }
  186. // HandleDeleteConfigMap deletes the pod given the name and namespace.
  187. func (app *App) HandleDeleteConfigMap(w http.ResponseWriter, r *http.Request) {
  188. vals, err := url.ParseQuery(r.URL.RawQuery)
  189. if err != nil {
  190. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  191. return
  192. }
  193. // get the filter options
  194. form := &forms.K8sForm{
  195. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  196. Repo: app.Repo,
  197. DigitalOceanOAuth: app.DOConf,
  198. },
  199. }
  200. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  201. // validate the form
  202. if err := app.validator.Struct(form); err != nil {
  203. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  204. return
  205. }
  206. // create a new agent
  207. var agent *kubernetes.Agent
  208. if app.ServerConf.IsTesting {
  209. agent = app.TestAgents.K8sAgent
  210. } else {
  211. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  212. }
  213. err = agent.DeleteConfigMap(vals["name"][0], vals["namespace"][0])
  214. if err != nil {
  215. app.handleErrorInternal(err, w)
  216. return
  217. }
  218. w.WriteHeader(http.StatusOK)
  219. return
  220. }
  221. // HandleUpdateConfigMap deletes the pod given the name and namespace.
  222. func (app *App) HandleUpdateConfigMap(w http.ResponseWriter, r *http.Request) {
  223. vals, err := url.ParseQuery(r.URL.RawQuery)
  224. if err != nil {
  225. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  226. return
  227. }
  228. // get the filter options
  229. form := &forms.K8sForm{
  230. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  231. Repo: app.Repo,
  232. DigitalOceanOAuth: app.DOConf,
  233. },
  234. }
  235. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  236. // validate the form
  237. if err := app.validator.Struct(form); err != nil {
  238. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  239. return
  240. }
  241. // create a new agent
  242. var agent *kubernetes.Agent
  243. if app.ServerConf.IsTesting {
  244. agent = app.TestAgents.K8sAgent
  245. } else {
  246. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  247. }
  248. configMap := &forms.ConfigMapForm{}
  249. if err := json.NewDecoder(r.Body).Decode(configMap); err != nil {
  250. app.handleErrorFormDecoding(err, ErrEnvDecode, w)
  251. return
  252. }
  253. _, err = agent.UpdateConfigMap(configMap.Name, configMap.Namespace, configMap.EnvVariables)
  254. if err != nil {
  255. app.handleErrorInternal(err, w)
  256. return
  257. }
  258. if err := json.NewEncoder(w).Encode(configMap); err != nil {
  259. app.handleErrorFormDecoding(err, ErrEnvDecode, w)
  260. return
  261. }
  262. w.WriteHeader(http.StatusOK)
  263. return
  264. }
  265. // HandleGetPodLogs returns real-time logs of the pod via websockets
  266. // TODO: Refactor repeated calls.
  267. func (app *App) HandleGetPodLogs(w http.ResponseWriter, r *http.Request) {
  268. // get session to retrieve correct kubeconfig
  269. _, err := app.Store.Get(r, app.ServerConf.CookieName)
  270. // get path parameters
  271. namespace := chi.URLParam(r, "namespace")
  272. podName := chi.URLParam(r, "name")
  273. if err != nil {
  274. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  275. return
  276. }
  277. vals, err := url.ParseQuery(r.URL.RawQuery)
  278. if err != nil {
  279. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  280. return
  281. }
  282. // get the filter options
  283. form := &forms.K8sForm{
  284. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  285. Repo: app.Repo,
  286. DigitalOceanOAuth: app.DOConf,
  287. },
  288. }
  289. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  290. // validate the form
  291. if err := app.validator.Struct(form); err != nil {
  292. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  293. return
  294. }
  295. // create a new agent
  296. var agent *kubernetes.Agent
  297. if app.ServerConf.IsTesting {
  298. agent = app.TestAgents.K8sAgent
  299. } else {
  300. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  301. }
  302. upgrader.CheckOrigin = func(r *http.Request) bool { return true }
  303. // upgrade to websocket.
  304. conn, err := upgrader.Upgrade(w, r, nil)
  305. if err != nil {
  306. app.handleErrorUpgradeWebsocket(err, w)
  307. }
  308. err = agent.GetPodLogs(namespace, podName, conn)
  309. if err != nil {
  310. app.handleErrorWebsocketWrite(err, w)
  311. return
  312. }
  313. }
  314. // HandleDeletePod deletes the pod given the name and namespace.
  315. func (app *App) HandleDeletePod(w http.ResponseWriter, r *http.Request) {
  316. // get path parameters
  317. namespace := chi.URLParam(r, "namespace")
  318. name := chi.URLParam(r, "name")
  319. vals, err := url.ParseQuery(r.URL.RawQuery)
  320. if err != nil {
  321. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  322. return
  323. }
  324. // get the filter options
  325. form := &forms.K8sForm{
  326. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  327. Repo: app.Repo,
  328. DigitalOceanOAuth: app.DOConf,
  329. },
  330. }
  331. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  332. // validate the form
  333. if err := app.validator.Struct(form); err != nil {
  334. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  335. return
  336. }
  337. // create a new agent
  338. var agent *kubernetes.Agent
  339. if app.ServerConf.IsTesting {
  340. agent = app.TestAgents.K8sAgent
  341. } else {
  342. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  343. }
  344. err = agent.DeletePod(namespace, name)
  345. if err != nil {
  346. app.handleErrorInternal(err, w)
  347. return
  348. }
  349. w.WriteHeader(http.StatusOK)
  350. return
  351. }
  352. // HandleGetIngress returns the ingress object given the name and namespace.
  353. func (app *App) HandleGetIngress(w http.ResponseWriter, r *http.Request) {
  354. // get session to retrieve correct kubeconfig
  355. _, err := app.Store.Get(r, app.ServerConf.CookieName)
  356. // get path parameters
  357. namespace := chi.URLParam(r, "namespace")
  358. name := chi.URLParam(r, "name")
  359. if err != nil {
  360. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  361. return
  362. }
  363. vals, err := url.ParseQuery(r.URL.RawQuery)
  364. if err != nil {
  365. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  366. return
  367. }
  368. // get the filter options
  369. form := &forms.K8sForm{
  370. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  371. Repo: app.Repo,
  372. DigitalOceanOAuth: app.DOConf,
  373. },
  374. }
  375. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  376. // validate the form
  377. if err := app.validator.Struct(form); err != nil {
  378. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  379. return
  380. }
  381. // create a new agent
  382. var agent *kubernetes.Agent
  383. if app.ServerConf.IsTesting {
  384. agent = app.TestAgents.K8sAgent
  385. } else {
  386. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  387. }
  388. ingress, err := agent.GetIngress(namespace, name)
  389. if err != nil {
  390. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  391. return
  392. }
  393. if err := json.NewEncoder(w).Encode(ingress); err != nil {
  394. app.handleErrorFormDecoding(err, ErrK8sDecode, w)
  395. return
  396. }
  397. }
  398. // HandleListPods returns all pods that match the given selectors
  399. // TODO: Refactor repeated calls.
  400. func (app *App) HandleListPods(w http.ResponseWriter, r *http.Request) {
  401. // get session to retrieve correct kubeconfig
  402. _, err := app.Store.Get(r, app.ServerConf.CookieName)
  403. if err != nil {
  404. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  405. return
  406. }
  407. vals, err := url.ParseQuery(r.URL.RawQuery)
  408. if err != nil {
  409. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  410. return
  411. }
  412. // get the filter options
  413. form := &forms.K8sForm{
  414. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  415. Repo: app.Repo,
  416. DigitalOceanOAuth: app.DOConf,
  417. },
  418. }
  419. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  420. // validate the form
  421. if err := app.validator.Struct(form); err != nil {
  422. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  423. return
  424. }
  425. // create a new agent
  426. var agent *kubernetes.Agent
  427. if app.ServerConf.IsTesting {
  428. agent = app.TestAgents.K8sAgent
  429. } else {
  430. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  431. }
  432. pods := []v1.Pod{}
  433. for _, selector := range vals["selectors"] {
  434. podsList, err := agent.GetPodsByLabel(selector)
  435. if err != nil {
  436. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  437. return
  438. }
  439. for _, pod := range podsList.Items {
  440. pods = append(pods, pod)
  441. }
  442. }
  443. if err := json.NewEncoder(w).Encode(pods); err != nil {
  444. app.handleErrorFormDecoding(err, ErrK8sDecode, w)
  445. return
  446. }
  447. }
  448. // HandleListJobsByChart lists all jobs belonging to a specific Helm chart
  449. func (app *App) HandleListJobsByChart(w http.ResponseWriter, r *http.Request) {
  450. // get path parameters
  451. namespace := chi.URLParam(r, "namespace")
  452. chart := chi.URLParam(r, "chart")
  453. releaseName := chi.URLParam(r, "release_name")
  454. vals, err := url.ParseQuery(r.URL.RawQuery)
  455. if err != nil {
  456. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  457. return
  458. }
  459. // get the filter options
  460. form := &forms.K8sForm{
  461. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  462. Repo: app.Repo,
  463. DigitalOceanOAuth: app.DOConf,
  464. },
  465. }
  466. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  467. // validate the form
  468. if err := app.validator.Struct(form); err != nil {
  469. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  470. return
  471. }
  472. // create a new agent
  473. var agent *kubernetes.Agent
  474. if app.ServerConf.IsTesting {
  475. agent = app.TestAgents.K8sAgent
  476. } else {
  477. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  478. }
  479. jobs, err := agent.ListJobsByLabel(namespace, kubernetes.Label{
  480. Key: "helm.sh/chart",
  481. Val: chart,
  482. }, kubernetes.Label{
  483. Key: "meta.helm.sh/release-name",
  484. Val: releaseName,
  485. })
  486. if err != nil {
  487. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  488. return
  489. }
  490. if err := json.NewEncoder(w).Encode(jobs); err != nil {
  491. app.handleErrorFormDecoding(err, ErrK8sDecode, w)
  492. return
  493. }
  494. }
  495. // HandleListJobPods lists all pods belonging to a specific job
  496. func (app *App) HandleListJobPods(w http.ResponseWriter, r *http.Request) {
  497. // get path parameters
  498. namespace := chi.URLParam(r, "namespace")
  499. name := chi.URLParam(r, "name")
  500. vals, err := url.ParseQuery(r.URL.RawQuery)
  501. if err != nil {
  502. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  503. return
  504. }
  505. // get the filter options
  506. form := &forms.K8sForm{
  507. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  508. Repo: app.Repo,
  509. DigitalOceanOAuth: app.DOConf,
  510. },
  511. }
  512. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  513. // validate the form
  514. if err := app.validator.Struct(form); err != nil {
  515. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  516. return
  517. }
  518. // create a new agent
  519. var agent *kubernetes.Agent
  520. if app.ServerConf.IsTesting {
  521. agent = app.TestAgents.K8sAgent
  522. } else {
  523. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  524. }
  525. pods, err := agent.GetJobPods(namespace, name)
  526. if err != nil {
  527. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  528. return
  529. }
  530. if err := json.NewEncoder(w).Encode(pods); err != nil {
  531. app.handleErrorFormDecoding(err, ErrK8sDecode, w)
  532. return
  533. }
  534. }
  535. // HandleStreamControllerStatus test calls
  536. // TODO: Refactor repeated calls.
  537. func (app *App) HandleStreamControllerStatus(w http.ResponseWriter, r *http.Request) {
  538. // get session to retrieve correct kubeconfig
  539. _, err := app.Store.Get(r, app.ServerConf.CookieName)
  540. if err != nil {
  541. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  542. return
  543. }
  544. vals, err := url.ParseQuery(r.URL.RawQuery)
  545. if err != nil {
  546. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  547. return
  548. }
  549. // get the filter options
  550. form := &forms.K8sForm{
  551. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  552. Repo: app.Repo,
  553. DigitalOceanOAuth: app.DOConf,
  554. },
  555. }
  556. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  557. // validate the form
  558. if err := app.validator.Struct(form); err != nil {
  559. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  560. return
  561. }
  562. // create a new agent
  563. var agent *kubernetes.Agent
  564. if app.ServerConf.IsTesting {
  565. agent = app.TestAgents.K8sAgent
  566. } else {
  567. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  568. }
  569. upgrader.CheckOrigin = func(r *http.Request) bool { return true }
  570. // upgrade to websocket.
  571. conn, err := upgrader.Upgrade(w, r, nil)
  572. if err != nil {
  573. app.handleErrorUpgradeWebsocket(err, w)
  574. }
  575. // get path parameters
  576. kind := chi.URLParam(r, "kind")
  577. err = agent.StreamControllerStatus(conn, kind)
  578. if err != nil {
  579. app.handleErrorWebsocketWrite(err, w)
  580. return
  581. }
  582. }
  583. // HandleDetectPrometheusInstalled detects a prometheus installation in the target cluster
  584. func (app *App) HandleDetectPrometheusInstalled(w http.ResponseWriter, r *http.Request) {
  585. vals, err := url.ParseQuery(r.URL.RawQuery)
  586. if err != nil {
  587. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  588. return
  589. }
  590. // get the filter options
  591. form := &forms.K8sForm{
  592. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  593. Repo: app.Repo,
  594. DigitalOceanOAuth: app.DOConf,
  595. },
  596. }
  597. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  598. // validate the form
  599. if err := app.validator.Struct(form); err != nil {
  600. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  601. return
  602. }
  603. // create a new agent
  604. var agent *kubernetes.Agent
  605. if app.ServerConf.IsTesting {
  606. agent = app.TestAgents.K8sAgent
  607. } else {
  608. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  609. }
  610. // detect prometheus service
  611. _, found, err := prometheus.GetPrometheusService(agent.Clientset)
  612. if !found {
  613. http.NotFound(w, r)
  614. return
  615. }
  616. w.WriteHeader(http.StatusOK)
  617. return
  618. }
  619. // HandleListNGINXIngresses lists all NGINX ingresses in a target cluster
  620. func (app *App) HandleListNGINXIngresses(w http.ResponseWriter, r *http.Request) {
  621. vals, err := url.ParseQuery(r.URL.RawQuery)
  622. if err != nil {
  623. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  624. return
  625. }
  626. // get the filter options
  627. form := &forms.K8sForm{
  628. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  629. Repo: app.Repo,
  630. DigitalOceanOAuth: app.DOConf,
  631. },
  632. }
  633. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  634. // validate the form
  635. if err := app.validator.Struct(form); err != nil {
  636. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  637. return
  638. }
  639. // create a new agent
  640. var agent *kubernetes.Agent
  641. if app.ServerConf.IsTesting {
  642. agent = app.TestAgents.K8sAgent
  643. } else {
  644. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  645. }
  646. res, err := prometheus.GetIngressesWithNGINXAnnotation(agent.Clientset)
  647. if err != nil {
  648. app.handleErrorInternal(err, w)
  649. return
  650. }
  651. w.WriteHeader(http.StatusOK)
  652. if err := json.NewEncoder(w).Encode(res); err != nil {
  653. app.handleErrorFormDecoding(err, ErrK8sDecode, w)
  654. return
  655. }
  656. }
  657. func (app *App) HandleGetPodMetrics(w http.ResponseWriter, r *http.Request) {
  658. vals, err := url.ParseQuery(r.URL.RawQuery)
  659. if err != nil {
  660. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  661. return
  662. }
  663. // get the filter options
  664. form := &forms.MetricsQueryForm{
  665. K8sForm: &forms.K8sForm{
  666. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  667. Repo: app.Repo,
  668. DigitalOceanOAuth: app.DOConf,
  669. },
  670. },
  671. QueryOpts: &prometheus.QueryOpts{},
  672. }
  673. form.K8sForm.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  674. // decode from JSON to form value
  675. decoder := schema.NewDecoder()
  676. decoder.IgnoreUnknownKeys(true)
  677. if err := decoder.Decode(form.QueryOpts, vals); err != nil {
  678. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  679. return
  680. }
  681. // validate the form
  682. if err := app.validator.Struct(form); err != nil {
  683. app.handleErrorFormValidation(err, ErrK8sValidate, w)
  684. return
  685. }
  686. // create a new agent
  687. var agent *kubernetes.Agent
  688. if app.ServerConf.IsTesting {
  689. agent = app.TestAgents.K8sAgent
  690. } else {
  691. agent, err = kubernetes.GetAgentOutOfClusterConfig(form.OutOfClusterConfig)
  692. }
  693. // get prometheus service
  694. promSvc, found, err := prometheus.GetPrometheusService(agent.Clientset)
  695. if err != nil {
  696. app.handleErrorInternal(err, w)
  697. return
  698. }
  699. if !found {
  700. app.handleErrorInternal(err, w)
  701. return
  702. }
  703. rawQuery, err := prometheus.QueryPrometheus(agent.Clientset, promSvc, form.QueryOpts)
  704. if err != nil {
  705. app.handleErrorInternal(err, w)
  706. return
  707. }
  708. fmt.Fprint(w, string(rawQuery))
  709. }
  710. type KubeconfigResponse struct {
  711. Kubeconfig []byte `json:"kubeconfig"`
  712. }
  713. func (app *App) HandleGetTemporaryKubeconfig(w http.ResponseWriter, r *http.Request) {
  714. vals, err := url.ParseQuery(r.URL.RawQuery)
  715. if err != nil {
  716. app.handleErrorFormDecoding(err, ErrReleaseDecode, w)
  717. return
  718. }
  719. // get the filter options
  720. form := &forms.K8sForm{
  721. OutOfClusterConfig: &kubernetes.OutOfClusterConfig{
  722. Repo: app.Repo,
  723. DigitalOceanOAuth: app.DOConf,
  724. },
  725. }
  726. form.PopulateK8sOptionsFromQueryParams(vals, app.Repo.Cluster)
  727. // get the API config
  728. apiConf, err := form.OutOfClusterConfig.CreateRawConfigFromCluster()
  729. if err != nil {
  730. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  731. return
  732. }
  733. bytes, err := clientcmd.Write(*apiConf)
  734. res := &KubeconfigResponse{
  735. Kubeconfig: bytes,
  736. }
  737. if err := json.NewEncoder(w).Encode(res); err != nil {
  738. app.handleErrorFormDecoding(err, ErrK8sDecode, w)
  739. return
  740. }
  741. }