cluster_handler_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. package api_test
  2. import (
  3. "encoding/json"
  4. "net/http"
  5. "strings"
  6. "testing"
  7. "github.com/porter-dev/porter/internal/kubernetes/fixtures"
  8. "github.com/porter-dev/porter/internal/models/integrations"
  9. "gorm.io/gorm"
  10. "github.com/go-test/deep"
  11. "github.com/porter-dev/porter/internal/forms"
  12. "github.com/porter-dev/porter/internal/models"
  13. )
  14. // ------------------------- TEST TYPES AND MAIN LOOP ------------------------- //
  15. type clusterTest struct {
  16. initializers []func(t *tester)
  17. msg string
  18. method string
  19. endpoint string
  20. body string
  21. expStatus int
  22. expBody string
  23. useCookie bool
  24. validators []func(c *clusterTest, tester *tester, t *testing.T)
  25. }
  26. func testClusterRequests(t *testing.T, tests []*clusterTest, canQuery bool) {
  27. for _, c := range tests {
  28. // create a new tester
  29. tester := newTester(canQuery)
  30. // if there's an initializer, call it
  31. for _, init := range c.initializers {
  32. init(tester)
  33. }
  34. req, err := http.NewRequest(
  35. c.method,
  36. c.endpoint,
  37. strings.NewReader(c.body),
  38. )
  39. tester.req = req
  40. if c.useCookie {
  41. req.AddCookie(tester.cookie)
  42. }
  43. if err != nil {
  44. t.Fatal(err)
  45. }
  46. tester.execute()
  47. rr := tester.rr
  48. // first, check that the status matches
  49. if status := rr.Code; status != c.expStatus {
  50. t.Errorf("%s, handler returned wrong status code: got %v want %v",
  51. c.msg, status, c.expStatus)
  52. }
  53. // if there's a validator, call it
  54. for _, validate := range c.validators {
  55. validate(c, tester, t)
  56. }
  57. }
  58. }
  59. // ------------------------- TEST FIXTURES AND FUNCTIONS ------------------------- //
  60. var createClusterTests = []*clusterTest{
  61. &clusterTest{
  62. initializers: []func(t *tester){
  63. initUserDefault,
  64. initProject,
  65. },
  66. msg: "Create cluster",
  67. method: "POST",
  68. endpoint: "/api/projects/1/clusters",
  69. body: `{"name":"cluster-test","server":"https://10.10.10.10:6443","aws_integration_id":1}`,
  70. expStatus: http.StatusCreated,
  71. expBody: `{"id":1,"project_id":1,"name":"cluster-test","server":"https://10.10.10.10:6443","service":"eks"}`,
  72. useCookie: true,
  73. validators: []func(c *clusterTest, tester *tester, t *testing.T){
  74. projectClusterBodyValidator,
  75. },
  76. },
  77. }
  78. func TestHandleCreateCluster(t *testing.T) {
  79. testRegistryRequests(t, createRegistryTests, true)
  80. }
  81. var readProjectClusterTest = []*clusterTest{
  82. &clusterTest{
  83. initializers: []func(t *tester){
  84. initUserDefault,
  85. initProject,
  86. initProjectClusterDefault,
  87. },
  88. msg: "Read project cluster",
  89. method: "GET",
  90. endpoint: "/api/projects/1/clusters/1",
  91. body: ``,
  92. expStatus: http.StatusOK,
  93. expBody: `{"id":1,"project_id":1,"name":"cluster-test","server":"https://10.10.10.10","service":"kube"}`,
  94. useCookie: true,
  95. validators: []func(c *clusterTest, tester *tester, t *testing.T){
  96. projectClusterBodyValidator,
  97. },
  98. },
  99. }
  100. func TestHandleReadProjectSA(t *testing.T) {
  101. testClusterRequests(t, readProjectClusterTest, true)
  102. }
  103. var listProjectClustersTest = []*clusterTest{
  104. &clusterTest{
  105. initializers: []func(t *tester){
  106. initUserDefault,
  107. initProject,
  108. initProjectClusterDefault,
  109. },
  110. msg: "List project clusters",
  111. method: "GET",
  112. endpoint: "/api/projects/1/clusters",
  113. body: ``,
  114. expStatus: http.StatusOK,
  115. expBody: `[{"id":1,"project_id":1,"name":"cluster-test","server":"https://10.10.10.10","service":"kube"}]`,
  116. useCookie: true,
  117. validators: []func(c *clusterTest, tester *tester, t *testing.T){
  118. projectClustersBodyValidator,
  119. },
  120. },
  121. }
  122. func TestHandleListProjectClusters(t *testing.T) {
  123. testClusterRequests(t, listProjectClustersTest, true)
  124. }
  125. var createProjectClusterCandidatesTests = []*clusterTest{
  126. &clusterTest{
  127. initializers: []func(t *tester){
  128. initUserDefault,
  129. initProject,
  130. },
  131. msg: "Create project cluster candidate w/ no actions -- should create SA by default",
  132. method: "POST",
  133. endpoint: "/api/projects/1/clusters/candidates",
  134. body: `{"kubeconfig":"` + OIDCAuthWithDataForJSON + `"}`,
  135. expStatus: http.StatusCreated,
  136. expBody: `[{"id":1,"resolvers":[],"created_cluster_id":1,"project_id":1,"context_name":"context-test","name":"cluster-test","server":"https://10.10.10.10"}]`,
  137. useCookie: true,
  138. validators: []func(c *clusterTest, tester *tester, t *testing.T){
  139. projectClusterCandidateBodyValidator,
  140. // check that Cluster was created by default
  141. func(c *clusterTest, tester *tester, t *testing.T) {
  142. clusters, err := tester.repo.Cluster.ListClustersByProjectID(1)
  143. if err != nil {
  144. t.Fatalf("%v\n", err)
  145. }
  146. if len(clusters) != 1 {
  147. t.Fatal("Expected cluster to be created by default, but does not exist\n")
  148. }
  149. gotCluster := clusters[0]
  150. gotCluster.Model = gorm.Model{}
  151. expCluster := &models.Cluster{
  152. AuthMechanism: models.OIDC,
  153. ProjectID: 1,
  154. Name: "cluster-test",
  155. Server: "https://10.10.10.10",
  156. OIDCIntegrationID: 1,
  157. TokenCache: integrations.TokenCache{},
  158. CertificateAuthorityData: []byte("-----BEGIN CER"),
  159. }
  160. if diff := deep.Equal(gotCluster, expCluster); diff != nil {
  161. t.Errorf("handler returned wrong body:\n")
  162. t.Error(diff)
  163. }
  164. },
  165. },
  166. },
  167. &clusterTest{
  168. initializers: []func(t *tester){
  169. initUserDefault,
  170. initProject,
  171. },
  172. msg: "Create project SA candidate",
  173. method: "POST",
  174. endpoint: "/api/projects/1/clusters/candidates",
  175. body: `{"kubeconfig":"` + OIDCAuthWithoutDataForJSON + `"}`,
  176. expStatus: http.StatusCreated,
  177. expBody: `[{"id":1,"resolvers":[{"name":"upload-oidc-idp-issuer-ca-data","data":{"filename":"/fake/path/to/ca.pem"},"docs":"https://github.com/porter-dev/porter","resolved":false,"fields":"oidc_idp_issuer_ca_data"}],"created_cluster_id":0,"project_id":1,"context_name":"context-test","name":"cluster-test","server":"https://10.10.10.10"}]`,
  178. useCookie: true,
  179. validators: []func(c *clusterTest, tester *tester, t *testing.T){
  180. projectClusterCandidateBodyValidator,
  181. },
  182. },
  183. }
  184. func TestHandleCreateProjectClusterCandidate(t *testing.T) {
  185. testClusterRequests(t, createProjectClusterCandidatesTests, true)
  186. }
  187. var listProjectClusterCandidatesTests = []*clusterTest{
  188. &clusterTest{
  189. initializers: []func(t *tester){
  190. initUserDefault,
  191. initProject,
  192. initProjectClusterCandidate,
  193. },
  194. msg: "List project cluster candidates",
  195. method: "GET",
  196. endpoint: "/api/projects/1/clusters/candidates",
  197. body: ``,
  198. expStatus: http.StatusOK,
  199. expBody: `[{"id":1,"resolvers":[{"name":"upload-oidc-idp-issuer-ca-data","data":{"filename":"/fake/path/to/ca.pem"},"docs":"https://github.com/porter-dev/porter","resolved":false,"fields":"oidc_idp_issuer_ca_data"}],"created_cluster_id":0,"project_id":1,"context_name":"context-test","name":"cluster-test","server":"https://10.10.10.10"}]`,
  200. useCookie: true,
  201. validators: []func(c *clusterTest, tester *tester, t *testing.T){
  202. projectClusterCandidateBodyValidator,
  203. },
  204. },
  205. }
  206. func TestHandleListProjectClusterCandidates(t *testing.T) {
  207. testClusterRequests(t, listProjectClusterCandidatesTests, true)
  208. }
  209. var resolveProjectClusterCandidatesTests = []*clusterTest{
  210. &clusterTest{
  211. initializers: []func(t *tester){
  212. initUserDefault,
  213. initProject,
  214. initProjectClusterCandidate,
  215. },
  216. msg: "Resolve project cluster candidate",
  217. method: "POST",
  218. endpoint: "/api/projects/1/clusters/candidates/1/resolve",
  219. body: `{"oidc_idp_issuer_ca_data": "LS0tLS1CRUdJTiBDRVJ="}`,
  220. expStatus: http.StatusCreated,
  221. expBody: `{"id":1,"project_id":1,"name":"cluster-test","server":"https://10.10.10.10","service":"kube"}`,
  222. useCookie: true,
  223. validators: []func(c *clusterTest, tester *tester, t *testing.T){
  224. projectClusterBodyValidator,
  225. },
  226. },
  227. }
  228. func TestHandleResolveProjectClusterCandidate(t *testing.T) {
  229. testClusterRequests(t, resolveProjectClusterCandidatesTests, true)
  230. }
  231. // ------------------------- INITIALIZERS AND VALIDATORS ------------------------- //
  232. func initProjectClusterCandidate(tester *tester) {
  233. proj, _ := tester.repo.Project.ReadProject(1)
  234. form := &forms.CreateClusterCandidatesForm{
  235. ProjectID: proj.ID,
  236. Kubeconfig: fixtures.OIDCAuthWithoutData,
  237. }
  238. // convert the form to a ServiceAccountCandidate
  239. ccs, _ := form.ToClusterCandidates(false)
  240. for _, cc := range ccs {
  241. tester.repo.Cluster.CreateClusterCandidate(cc)
  242. }
  243. }
  244. func initProjectClusterDefault(tester *tester) {
  245. proj, _ := tester.repo.Project.ReadProject(1)
  246. form := &forms.CreateClusterCandidatesForm{
  247. ProjectID: proj.ID,
  248. Kubeconfig: fixtures.OIDCAuthWithData,
  249. }
  250. // convert the form to a ServiceAccountCandidate
  251. ccs, _ := form.ToClusterCandidates(false)
  252. for _, cc := range ccs {
  253. tester.repo.Cluster.CreateClusterCandidate(cc)
  254. }
  255. clusterForm := forms.ResolveClusterForm{
  256. Resolver: &models.ClusterResolverAll{},
  257. ClusterCandidateID: 1,
  258. ProjectID: 1,
  259. UserID: 1,
  260. }
  261. clusterForm.ResolveIntegration(*tester.repo)
  262. clusterForm.ResolveCluster(*tester.repo)
  263. }
  264. func projectClusterCandidateBodyValidator(c *clusterTest, tester *tester, t *testing.T) {
  265. gotBody := make([]*models.ClusterCandidateExternal, 0)
  266. expBody := make([]*models.ClusterCandidateExternal, 0)
  267. json.Unmarshal(tester.rr.Body.Bytes(), &gotBody)
  268. json.Unmarshal([]byte(c.expBody), &expBody)
  269. if diff := deep.Equal(gotBody, expBody); diff != nil {
  270. t.Errorf("handler returned wrong body:\n")
  271. t.Error(diff)
  272. }
  273. }
  274. func projectClusterBodyValidator(c *clusterTest, tester *tester, t *testing.T) {
  275. gotBody := &models.ClusterExternal{}
  276. expBody := &models.ClusterExternal{}
  277. json.Unmarshal(tester.rr.Body.Bytes(), gotBody)
  278. json.Unmarshal([]byte(c.expBody), expBody)
  279. if diff := deep.Equal(gotBody, expBody); diff != nil {
  280. t.Errorf("handler returned wrong body:\n")
  281. t.Error(diff)
  282. }
  283. }
  284. func projectClustersBodyValidator(c *clusterTest, tester *tester, t *testing.T) {
  285. gotBody := make([]*models.ClusterExternal, 0)
  286. expBody := make([]*models.ClusterExternal, 0)
  287. json.Unmarshal(tester.rr.Body.Bytes(), &gotBody)
  288. json.Unmarshal([]byte(c.expBody), &expBody)
  289. if diff := deep.Equal(gotBody, expBody); diff != nil {
  290. t.Errorf("handler returned wrong body:\n")
  291. t.Error(diff)
  292. }
  293. }
  294. const OIDCAuthWithDataForJSON string = `apiVersion: v1\nclusters:\n- cluster:\n server: https://10.10.10.10\n certificate-authority-data: LS0tLS1CRUdJTiBDRVJ=\n name: cluster-test\ncontexts:\n- context:\n cluster: cluster-test\n user: test-admin\n name: context-test\ncurrent-context: context-test\nkind: Config\npreferences: {}\nusers:\n- name: test-admin\n user:\n auth-provider:\n config:\n client-id: porter-api\n id-token: token\n idp-issuer-url: https://10.10.10.10\n idp-certificate-authority-data: LS0tLS1CRUdJTiBDRVJ=\n name: oidc`
  295. const OIDCAuthWithoutDataForJSON string = `apiVersion: v1\nclusters:\n- cluster:\n server: https://10.10.10.10\n certificate-authority-data: LS0tLS1CRUdJTiBDRVJ=\n name: cluster-test\ncontexts:\n- context:\n cluster: cluster-test\n user: test-admin\n name: context-test\ncurrent-context: context-test\nkind: Config\npreferences: {}\nusers:\n- name: test-admin\n user:\n auth-provider:\n config:\n client-id: porter-api\n id-token: token\n idp-issuer-url: https://10.10.10.10\n idp-certificate-authority: /fake/path/to/ca.pem\n name: oidc`
  296. const OIDCAuthWithoutData string = `
  297. apiVersion: v1
  298. clusters:
  299. - cluster:
  300. server: https://10.10.10.10
  301. certificate-authority-data: LS0tLS1CRUdJTiBDRVJ=
  302. name: cluster-test
  303. contexts:
  304. - context:
  305. cluster: cluster-test
  306. user: test-admin
  307. name: context-test
  308. current-context: context-test
  309. kind: Config
  310. preferences: {}
  311. users:
  312. - name: test-admin
  313. user:
  314. auth-provider:
  315. config:
  316. client-id: porter-api
  317. id-token: token
  318. idp-issuer-url: https://10.10.10.10
  319. idp-certificate-authority: /fake/path/to/ca.pem
  320. name: oidc
  321. `
  322. const OIDCAuthWithData string = `
  323. apiVersion: v1
  324. clusters:
  325. - cluster:
  326. server: https://10.10.10.10
  327. certificate-authority-data: LS0tLS1CRUdJTiBDRVJ=
  328. name: cluster-test
  329. contexts:
  330. - context:
  331. cluster: cluster-test
  332. user: test-admin
  333. name: context-test
  334. current-context: context-test
  335. kind: Config
  336. preferences: {}
  337. users:
  338. - name: test-admin
  339. user:
  340. auth-provider:
  341. config:
  342. client-id: porter-api
  343. id-token: token
  344. idp-issuer-url: https://10.10.10.10
  345. idp-certificate-authority-data: LS0tLS1CRUdJTiBDRVJ=
  346. name: oidc
  347. `