k8s_handler.go 20 KB

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