porter_app.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. package client
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/porter-dev/porter/api/server/handlers/porter_app"
  6. "github.com/porter-dev/porter/internal/models"
  7. "github.com/porter-dev/porter/api/types"
  8. )
  9. func (c *Client) NewGetPorterApp(
  10. ctx context.Context,
  11. projectID, clusterID uint,
  12. appName string,
  13. ) (*types.PorterApp, error) {
  14. resp := &types.PorterApp{}
  15. err := c.getRequest(
  16. fmt.Sprintf(
  17. "/projects/%d/clusters/%d/applications/%s",
  18. projectID, clusterID, appName,
  19. ),
  20. nil,
  21. resp,
  22. )
  23. return resp, err
  24. }
  25. func (c *Client) NewCreatePorterApp(
  26. ctx context.Context,
  27. projectID, clusterID uint,
  28. appName string,
  29. req *types.CreatePorterAppRequest,
  30. ) (*types.PorterApp, error) {
  31. resp := &types.PorterApp{}
  32. err := c.postRequest(
  33. fmt.Sprintf(
  34. "/projects/%d/clusters/%d/applications/%s",
  35. projectID, clusterID, appName,
  36. ),
  37. req,
  38. resp,
  39. )
  40. return resp, err
  41. }
  42. // NewCreateOrUpdatePorterAppEvent will create a porter app event if one does not exist, or else it will update the existing one if an ID is passed in the object
  43. func (c *Client) NewCreateOrUpdatePorterAppEvent(
  44. ctx context.Context,
  45. projectID, clusterID uint,
  46. appName string,
  47. req *types.CreateOrUpdatePorterAppEventRequest,
  48. ) (types.PorterAppEvent, error) {
  49. resp := &types.PorterAppEvent{}
  50. err := c.postRequest(
  51. fmt.Sprintf(
  52. "/projects/%d/clusters/%d/applications/%s/events",
  53. projectID, clusterID, appName,
  54. ),
  55. req,
  56. resp,
  57. )
  58. return *resp, err
  59. }
  60. // TODO: remove these functions once they are no longer called (check telemetry)
  61. func (c *Client) GetPorterApp(
  62. ctx context.Context,
  63. projectID, clusterID uint,
  64. stackName string,
  65. ) (*types.PorterApp, error) {
  66. resp := &types.PorterApp{}
  67. err := c.getRequest(
  68. fmt.Sprintf(
  69. "/projects/%d/clusters/%d/stacks/%s",
  70. projectID, clusterID, stackName,
  71. ),
  72. nil,
  73. resp,
  74. )
  75. return resp, err
  76. }
  77. func (c *Client) CreatePorterApp(
  78. ctx context.Context,
  79. projectID, clusterID uint,
  80. name string,
  81. req *types.CreatePorterAppRequest,
  82. ) (*types.PorterApp, error) {
  83. resp := &types.PorterApp{}
  84. err := c.postRequest(
  85. fmt.Sprintf(
  86. "/projects/%d/clusters/%d/stacks/%s",
  87. projectID, clusterID, name,
  88. ),
  89. req,
  90. resp,
  91. )
  92. return resp, err
  93. }
  94. // CreateOrUpdatePorterAppEvent will create a porter app event if one does not exist, or else it will update the existing one if an ID is passed in the object
  95. func (c *Client) CreateOrUpdatePorterAppEvent(
  96. ctx context.Context,
  97. projectID, clusterID uint,
  98. name string,
  99. req *types.CreateOrUpdatePorterAppEventRequest,
  100. ) (types.PorterAppEvent, error) {
  101. resp := &types.PorterAppEvent{}
  102. err := c.postRequest(
  103. fmt.Sprintf(
  104. "/projects/%d/clusters/%d/stacks/%s/events",
  105. projectID, clusterID, name,
  106. ),
  107. req,
  108. resp,
  109. )
  110. return *resp, err
  111. }
  112. // ListEnvGroups (List all Env Groups for a given cluster)
  113. func (c *Client) ListEnvGroups(
  114. ctx context.Context,
  115. projectID, clusterID uint,
  116. ) (types.ListEnvironmentGroupsResponse, error) {
  117. resp := &types.ListEnvironmentGroupsResponse{}
  118. err := c.getRequest(
  119. fmt.Sprintf(
  120. "/projects/%d/clusters/%d/environment-groups",
  121. projectID, clusterID,
  122. ),
  123. nil,
  124. resp,
  125. )
  126. return *resp, err
  127. }
  128. // ParseYAML takes in a base64 encoded porter yaml and returns an app proto
  129. func (c *Client) ParseYAML(
  130. ctx context.Context,
  131. projectID, clusterID uint,
  132. b64Yaml string,
  133. appName string,
  134. ) (*porter_app.ParsePorterYAMLToProtoResponse, error) {
  135. resp := &porter_app.ParsePorterYAMLToProtoResponse{}
  136. req := &porter_app.ParsePorterYAMLToProtoRequest{
  137. B64Yaml: b64Yaml,
  138. AppName: appName,
  139. }
  140. err := c.postRequest(
  141. fmt.Sprintf(
  142. "/projects/%d/clusters/%d/apps/parse",
  143. projectID, clusterID,
  144. ),
  145. req,
  146. resp,
  147. )
  148. return resp, err
  149. }
  150. // ValidatePorterAppInput is the input struct to ValidatePorterApp
  151. type ValidatePorterAppInput struct {
  152. ProjectID uint
  153. ClusterID uint
  154. AppName string
  155. Base64AppProto string
  156. Base64AppOverrides string
  157. DeploymentTarget string
  158. CommitSHA string
  159. }
  160. // ValidatePorterApp takes in a base64 encoded app definition that is potentially partial and returns a complete definition
  161. // using any previous app revisions and defaults
  162. func (c *Client) ValidatePorterApp(
  163. ctx context.Context,
  164. inp ValidatePorterAppInput,
  165. ) (*porter_app.ValidatePorterAppResponse, error) {
  166. resp := &porter_app.ValidatePorterAppResponse{}
  167. req := &porter_app.ValidatePorterAppRequest{
  168. AppName: inp.AppName,
  169. Base64AppProto: inp.Base64AppProto,
  170. Base64AppOverrides: inp.Base64AppOverrides,
  171. DeploymentTargetId: inp.DeploymentTarget,
  172. CommitSHA: inp.CommitSHA,
  173. }
  174. err := c.postRequest(
  175. fmt.Sprintf(
  176. "/projects/%d/clusters/%d/apps/validate",
  177. inp.ProjectID, inp.ClusterID,
  178. ),
  179. req,
  180. resp,
  181. )
  182. return resp, err
  183. }
  184. // ApplyPorterApp takes in a base64 encoded app definition and applies it to the cluster
  185. func (c *Client) ApplyPorterApp(
  186. ctx context.Context,
  187. projectID, clusterID uint,
  188. base64AppProto string,
  189. deploymentTarget string,
  190. appRevisionID string,
  191. forceBuild bool,
  192. ) (*porter_app.ApplyPorterAppResponse, error) {
  193. resp := &porter_app.ApplyPorterAppResponse{}
  194. req := &porter_app.ApplyPorterAppRequest{
  195. Base64AppProto: base64AppProto,
  196. DeploymentTargetId: deploymentTarget,
  197. AppRevisionID: appRevisionID,
  198. ForceBuild: forceBuild,
  199. }
  200. err := c.postRequest(
  201. fmt.Sprintf(
  202. "/projects/%d/clusters/%d/apps/apply",
  203. projectID, clusterID,
  204. ),
  205. req,
  206. resp,
  207. )
  208. return resp, err
  209. }
  210. // DefaultDeploymentTarget returns the default deployment target for a given project and cluster
  211. func (c *Client) DefaultDeploymentTarget(
  212. ctx context.Context,
  213. projectID, clusterID uint,
  214. ) (*porter_app.DefaultDeploymentTargetResponse, error) {
  215. resp := &porter_app.DefaultDeploymentTargetResponse{}
  216. req := &porter_app.DefaultDeploymentTargetRequest{}
  217. err := c.getRequest(
  218. fmt.Sprintf(
  219. "/projects/%d/clusters/%d/default-deployment-target",
  220. projectID, clusterID,
  221. ),
  222. req,
  223. resp,
  224. )
  225. return resp, err
  226. }
  227. // CurrentAppRevision returns the currently deployed app revision for a given project, app name and deployment target
  228. func (c *Client) CurrentAppRevision(
  229. ctx context.Context,
  230. projectID uint, clusterID uint,
  231. appName string, deploymentTarget string,
  232. ) (*porter_app.LatestAppRevisionResponse, error) {
  233. resp := &porter_app.LatestAppRevisionResponse{}
  234. req := &porter_app.LatestAppRevisionRequest{
  235. DeploymentTargetID: deploymentTarget,
  236. }
  237. err := c.getRequest(
  238. fmt.Sprintf(
  239. "/projects/%d/clusters/%d/apps/%s/latest",
  240. projectID, clusterID, appName,
  241. ),
  242. req,
  243. resp,
  244. )
  245. return resp, err
  246. }
  247. // CreatePorterAppDBEntryInput is the input struct to CreatePorterAppDBEntry
  248. type CreatePorterAppDBEntryInput struct {
  249. AppName string
  250. GitRepoName string
  251. GitRepoID uint
  252. GitBranch string
  253. ImageRepository string
  254. PorterYamlPath string
  255. ImageTag string
  256. Local bool
  257. }
  258. // CreatePorterAppDBEntry creates an entry in the porter app
  259. func (c *Client) CreatePorterAppDBEntry(
  260. ctx context.Context,
  261. projectID uint, clusterID uint,
  262. inp CreatePorterAppDBEntryInput,
  263. ) error {
  264. var sourceType porter_app.SourceType
  265. var image *porter_app.Image
  266. if inp.Local {
  267. sourceType = porter_app.SourceType_Local
  268. }
  269. if inp.GitRepoName != "" {
  270. sourceType = porter_app.SourceType_Github
  271. }
  272. if inp.ImageRepository != "" {
  273. sourceType = porter_app.SourceType_DockerRegistry
  274. image = &porter_app.Image{
  275. Repository: inp.ImageRepository,
  276. Tag: inp.ImageTag,
  277. }
  278. }
  279. req := &porter_app.CreateAppRequest{
  280. Name: inp.AppName,
  281. SourceType: sourceType,
  282. GitBranch: inp.GitBranch,
  283. GitRepoName: inp.GitRepoName,
  284. GitRepoID: inp.GitRepoID,
  285. PorterYamlPath: inp.PorterYamlPath,
  286. Image: image,
  287. }
  288. err := c.postRequest(
  289. fmt.Sprintf(
  290. "/projects/%d/clusters/%d/apps/create",
  291. projectID, clusterID,
  292. ),
  293. req,
  294. &types.PorterApp{},
  295. )
  296. return err
  297. }
  298. // CreateSubdomain returns a subdomain for a given service that point to the ingress-nginx service in the cluster
  299. func (c *Client) CreateSubdomain(
  300. ctx context.Context,
  301. projectID uint, clusterID uint,
  302. appName string, serviceName string,
  303. ) (*porter_app.CreateSubdomainResponse, error) {
  304. resp := &porter_app.CreateSubdomainResponse{}
  305. req := &porter_app.CreateSubdomainRequest{
  306. ServiceName: serviceName,
  307. }
  308. err := c.postRequest(
  309. fmt.Sprintf(
  310. "/projects/%d/clusters/%d/apps/%s/subdomain",
  311. projectID, clusterID, appName,
  312. ),
  313. req,
  314. resp,
  315. )
  316. return resp, err
  317. }
  318. // PredeployStatus checks the current status of a predeploy job for an app revision
  319. func (c *Client) PredeployStatus(
  320. ctx context.Context,
  321. projectID uint, clusterID uint,
  322. appName string, appRevisionId string,
  323. ) (*porter_app.PredeployStatusResponse, error) {
  324. resp := &porter_app.PredeployStatusResponse{}
  325. err := c.getRequest(
  326. fmt.Sprintf(
  327. "/projects/%d/clusters/%d/apps/%s/%s/predeploy-status",
  328. projectID, clusterID, appName, appRevisionId,
  329. ),
  330. nil,
  331. resp,
  332. )
  333. if resp.Status == "" {
  334. return nil, fmt.Errorf("no predeploy status found")
  335. }
  336. return resp, err
  337. }
  338. // UpdateRevisionStatus updates the status of an app revision
  339. func (c *Client) UpdateRevisionStatus(
  340. ctx context.Context,
  341. projectID uint, clusterID uint,
  342. appName string, appRevisionId string,
  343. status models.AppRevisionStatus,
  344. ) (*porter_app.UpdateAppRevisionStatusResponse, error) {
  345. resp := &porter_app.UpdateAppRevisionStatusResponse{}
  346. req := &porter_app.UpdateAppRevisionStatusRequest{
  347. Status: status,
  348. }
  349. err := c.postRequest(
  350. fmt.Sprintf(
  351. "/projects/%d/clusters/%d/apps/%s/revisions/%s",
  352. projectID, clusterID, appName, appRevisionId,
  353. ),
  354. req,
  355. resp,
  356. )
  357. return resp, err
  358. }
  359. // GetBuildEnv returns the build environment for a given app proto
  360. func (c *Client) GetBuildEnv(
  361. ctx context.Context,
  362. projectID uint, clusterID uint,
  363. appName string, appRevisionId string,
  364. ) (*porter_app.GetBuildEnvResponse, error) {
  365. resp := &porter_app.GetBuildEnvResponse{}
  366. err := c.getRequest(
  367. fmt.Sprintf(
  368. "/projects/%d/clusters/%d/apps/%s/revisions/%s/build-env",
  369. projectID, clusterID, appName, appRevisionId,
  370. ),
  371. nil,
  372. resp,
  373. )
  374. return resp, err
  375. }
  376. // ReportRevisionStatusInput is the input struct to ReportRevisionStatus
  377. type ReportRevisionStatusInput struct {
  378. ProjectID uint
  379. ClusterID uint
  380. AppName string
  381. AppRevisionID string
  382. PRNumber int
  383. CommitSHA string
  384. }
  385. // ReportRevisionStatus reports the status of an app revision to external services
  386. func (c *Client) ReportRevisionStatus(
  387. ctx context.Context,
  388. inp ReportRevisionStatusInput,
  389. ) (*porter_app.ReportRevisionStatusResponse, error) {
  390. resp := &porter_app.ReportRevisionStatusResponse{}
  391. req := &porter_app.ReportRevisionStatusRequest{
  392. PRNumber: inp.PRNumber,
  393. CommitSHA: inp.CommitSHA,
  394. }
  395. err := c.postRequest(
  396. fmt.Sprintf(
  397. "/projects/%d/clusters/%d/apps/%s/revisions/%s/status",
  398. inp.ProjectID, inp.ClusterID, inp.AppName, inp.AppRevisionID,
  399. ),
  400. req,
  401. resp,
  402. )
  403. return resp, err
  404. }
  405. // CreateOrUpdateAppEnvironment updates the app environment group and creates it if it doesn't exist
  406. func (c *Client) CreateOrUpdateAppEnvironment(
  407. ctx context.Context,
  408. projectID uint, clusterID uint,
  409. appName string,
  410. deploymentTargetID string,
  411. variables map[string]string,
  412. secrets map[string]string,
  413. Base64AppProto string,
  414. ) (*porter_app.UpdateAppEnvironmentResponse, error) {
  415. resp := &porter_app.UpdateAppEnvironmentResponse{}
  416. req := &porter_app.UpdateAppEnvironmentRequest{
  417. DeploymentTargetID: deploymentTargetID,
  418. Variables: variables,
  419. Secrets: secrets,
  420. HardUpdate: false,
  421. Base64AppProto: Base64AppProto,
  422. }
  423. err := c.postRequest(
  424. fmt.Sprintf(
  425. "/projects/%d/clusters/%d/apps/%s/update-environment",
  426. projectID, clusterID, appName,
  427. ),
  428. req,
  429. resp,
  430. )
  431. return resp, err
  432. }
  433. // PorterYamlV2Pods gets all pods for a given deployment target id and app name
  434. func (c *Client) PorterYamlV2Pods(
  435. ctx context.Context,
  436. projectID, clusterID uint,
  437. porterAppName string,
  438. req *types.PorterYamlV2PodsRequest,
  439. ) (*types.GetReleaseAllPodsResponse, error) {
  440. resp := &types.GetReleaseAllPodsResponse{}
  441. err := c.getRequest(
  442. fmt.Sprintf(
  443. "/projects/%d/clusters/%d/apps/%s/pods",
  444. projectID, clusterID,
  445. porterAppName,
  446. ),
  447. req,
  448. resp,
  449. )
  450. return resp, err
  451. }
  452. // UpdateImage updates the image for a porter app (porter yaml v2 only)
  453. func (c *Client) UpdateImage(
  454. ctx context.Context,
  455. projectID, clusterID uint,
  456. appName, deploymentTargetId, tag string,
  457. ) (*porter_app.UpdateImageResponse, error) {
  458. req := &porter_app.UpdateImageRequest{
  459. Tag: tag,
  460. DeploymentTargetId: deploymentTargetId,
  461. }
  462. resp := &porter_app.UpdateImageResponse{}
  463. err := c.postRequest(
  464. fmt.Sprintf(
  465. "/projects/%d/clusters/%d/apps/%s/update-image",
  466. projectID, clusterID, appName,
  467. ),
  468. &req,
  469. resp,
  470. )
  471. return resp, err
  472. }
  473. // ListAppRevisions lists the last ten app revisions for a given app
  474. func (c *Client) ListAppRevisions(
  475. ctx context.Context,
  476. projectID, clusterID uint,
  477. appName string,
  478. deploymentTargetID string,
  479. ) (*porter_app.ListAppRevisionsResponse, error) {
  480. resp := &porter_app.ListAppRevisionsResponse{}
  481. req := &porter_app.ListAppRevisionsRequest{
  482. DeploymentTargetID: deploymentTargetID,
  483. }
  484. err := c.getRequest(
  485. fmt.Sprintf(
  486. "/projects/%d/clusters/%d/apps/%s/revisions",
  487. projectID, clusterID,
  488. appName,
  489. ),
  490. req,
  491. resp,
  492. )
  493. return resp, err
  494. }
  495. // RollbackRevision reverts an app to a previous revision
  496. func (c *Client) RollbackRevision(
  497. ctx context.Context,
  498. projectID, clusterID uint,
  499. appName string,
  500. deploymentTargetID string,
  501. ) (*porter_app.RollbackAppRevisionResponse, error) {
  502. resp := &porter_app.RollbackAppRevisionResponse{}
  503. req := &porter_app.RollbackAppRevisionRequest{
  504. DeploymentTargetID: deploymentTargetID,
  505. }
  506. err := c.postRequest(
  507. fmt.Sprintf(
  508. "/projects/%d/clusters/%d/apps/%s/rollback",
  509. projectID, clusterID,
  510. appName,
  511. ),
  512. req,
  513. resp,
  514. )
  515. return resp, err
  516. }