porter_app.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  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. // GetAppManifests returns the manifests for a given app based on the latest successful app revision
  151. func (c *Client) GetAppManifests(
  152. ctx context.Context,
  153. projectID, clusterID uint,
  154. appName string,
  155. ) (*porter_app.AppManifestsResponse, error) {
  156. resp := &porter_app.AppManifestsResponse{}
  157. err := c.getRequest(
  158. fmt.Sprintf(
  159. "/projects/%d/clusters/%d/apps/%s/manifests",
  160. projectID, clusterID, appName,
  161. ),
  162. nil,
  163. resp,
  164. )
  165. return resp, err
  166. }
  167. // UpdateAppInput is the input struct to UpdateApp
  168. type UpdateAppInput struct {
  169. ProjectID uint
  170. ClusterID uint
  171. Name string
  172. ImageTagOverride string
  173. GitSource porter_app.GitSource
  174. DeploymentTargetId string
  175. CommitSHA string
  176. AppRevisionID string
  177. Base64AppProto string
  178. Base64PorterYAML string
  179. IsEnvOverride bool
  180. WithPredeploy bool
  181. Exact bool
  182. }
  183. // UpdateApp updates a porter app
  184. func (c *Client) UpdateApp(
  185. ctx context.Context,
  186. inp UpdateAppInput,
  187. ) (*porter_app.UpdateAppResponse, error) {
  188. resp := &porter_app.UpdateAppResponse{}
  189. req := &porter_app.UpdateAppRequest{
  190. Name: inp.Name,
  191. GitSource: inp.GitSource,
  192. DeploymentTargetId: inp.DeploymentTargetId,
  193. CommitSHA: inp.CommitSHA,
  194. ImageTagOverride: inp.ImageTagOverride,
  195. AppRevisionID: inp.AppRevisionID,
  196. Base64AppProto: inp.Base64AppProto,
  197. Base64PorterYAML: inp.Base64PorterYAML,
  198. IsEnvOverride: inp.IsEnvOverride,
  199. WithPredeploy: inp.WithPredeploy,
  200. Exact: inp.Exact,
  201. }
  202. err := c.postRequest(
  203. fmt.Sprintf(
  204. "/projects/%d/clusters/%d/apps/update",
  205. inp.ProjectID, inp.ClusterID,
  206. ),
  207. req,
  208. resp,
  209. )
  210. return resp, err
  211. }
  212. // DefaultDeploymentTarget returns the default deployment target for a given project and cluster
  213. func (c *Client) DefaultDeploymentTarget(
  214. ctx context.Context,
  215. projectID, clusterID uint,
  216. ) (*porter_app.DefaultDeploymentTargetResponse, error) {
  217. resp := &porter_app.DefaultDeploymentTargetResponse{}
  218. req := &porter_app.DefaultDeploymentTargetRequest{}
  219. err := c.getRequest(
  220. fmt.Sprintf(
  221. "/projects/%d/clusters/%d/default-deployment-target",
  222. projectID, clusterID,
  223. ),
  224. req,
  225. resp,
  226. )
  227. return resp, err
  228. }
  229. // CurrentAppRevisionInput is the input struct to CurrentAppRevision
  230. type CurrentAppRevisionInput struct {
  231. ProjectID uint
  232. ClusterID uint
  233. AppName string
  234. // DeploymentTargetName is the name of the deployment target to get the current app revision for. One of this or DeploymentTargetID must be set.
  235. DeploymentTargetName string
  236. // DeploymentTargetID is the id of the deployment target to get the current app revision for. One of this or DeploymentTargetName must be set.
  237. DeploymentTargetID string
  238. }
  239. // CurrentAppRevision returns the currently deployed app revision for a given project, app name and deployment target
  240. func (c *Client) CurrentAppRevision(
  241. ctx context.Context,
  242. input CurrentAppRevisionInput,
  243. ) (*porter_app.LatestAppRevisionResponse, error) {
  244. resp := &porter_app.LatestAppRevisionResponse{}
  245. req := &porter_app.LatestAppRevisionRequest{
  246. DeploymentTargetName: input.DeploymentTargetName,
  247. DeploymentTargetID: input.DeploymentTargetID,
  248. }
  249. err := c.getRequest(
  250. fmt.Sprintf(
  251. "/projects/%d/clusters/%d/apps/%s/latest",
  252. input.ProjectID, input.ClusterID, input.AppName,
  253. ),
  254. req,
  255. resp,
  256. )
  257. return resp, err
  258. }
  259. // CreatePorterAppDBEntryInput is the input struct to CreatePorterAppDBEntry
  260. type CreatePorterAppDBEntryInput struct {
  261. AppName string
  262. GitRepoName string
  263. GitRepoID uint
  264. GitBranch string
  265. ImageRepository string
  266. PorterYamlPath string
  267. ImageTag string
  268. Local bool
  269. DeploymentTargetID string
  270. }
  271. // CreateSubdomain returns a subdomain for a given service that point to the ingress-nginx service in the cluster
  272. func (c *Client) CreateSubdomain(
  273. ctx context.Context,
  274. projectID uint, clusterID uint,
  275. appName string, serviceName string,
  276. ) (*porter_app.CreateSubdomainResponse, error) {
  277. resp := &porter_app.CreateSubdomainResponse{}
  278. req := &porter_app.CreateSubdomainRequest{
  279. ServiceName: serviceName,
  280. }
  281. err := c.postRequest(
  282. fmt.Sprintf(
  283. "/projects/%d/clusters/%d/apps/%s/subdomain",
  284. projectID, clusterID, appName,
  285. ),
  286. req,
  287. resp,
  288. )
  289. return resp, err
  290. }
  291. // PredeployStatus checks the current status of a predeploy job for an app revision
  292. func (c *Client) PredeployStatus(
  293. ctx context.Context,
  294. projectID uint, clusterID uint,
  295. appName string, appRevisionId string,
  296. ) (*porter_app.PredeployStatusResponse, error) {
  297. resp := &porter_app.PredeployStatusResponse{}
  298. err := c.getRequest(
  299. fmt.Sprintf(
  300. "/projects/%d/clusters/%d/apps/%s/%s/predeploy-status",
  301. projectID, clusterID, appName, appRevisionId,
  302. ),
  303. nil,
  304. resp,
  305. )
  306. if resp.Status == "" {
  307. return nil, fmt.Errorf("no predeploy status found")
  308. }
  309. return resp, err
  310. }
  311. // GetRevision returns an app revision
  312. func (c *Client) GetRevision(
  313. ctx context.Context,
  314. projectID uint, clusterID uint,
  315. appName string, appRevisionId string,
  316. ) (*porter_app.GetAppRevisionResponse, error) {
  317. resp := &porter_app.GetAppRevisionResponse{}
  318. err := c.getRequest(
  319. fmt.Sprintf(
  320. "/projects/%d/clusters/%d/apps/%s/revisions/%s",
  321. projectID, clusterID, appName, appRevisionId,
  322. ),
  323. nil,
  324. resp,
  325. )
  326. return resp, err
  327. }
  328. // GetRevisionStatus returns the status of an app revision
  329. func (c *Client) GetRevisionStatus(
  330. ctx context.Context,
  331. projectID uint, clusterID uint,
  332. appName string, appRevisionId string,
  333. ) (*porter_app.GetAppRevisionStatusResponse, error) {
  334. resp := &porter_app.GetAppRevisionStatusResponse{}
  335. err := c.getRequest(
  336. fmt.Sprintf(
  337. "/projects/%d/clusters/%d/apps/%s/revisions/%s/status",
  338. projectID, clusterID, appName, appRevisionId,
  339. ),
  340. nil,
  341. resp,
  342. )
  343. return resp, err
  344. }
  345. // UpdateRevisionStatus updates the status of an app revision
  346. func (c *Client) UpdateRevisionStatus(
  347. ctx context.Context,
  348. projectID uint, clusterID uint,
  349. appName string, appRevisionId string,
  350. status models.AppRevisionStatus,
  351. ) (*porter_app.UpdateAppRevisionStatusResponse, error) {
  352. resp := &porter_app.UpdateAppRevisionStatusResponse{}
  353. req := &porter_app.UpdateAppRevisionStatusRequest{
  354. Status: status,
  355. }
  356. err := c.postRequest(
  357. fmt.Sprintf(
  358. "/projects/%d/clusters/%d/apps/%s/revisions/%s",
  359. projectID, clusterID, appName, appRevisionId,
  360. ),
  361. req,
  362. resp,
  363. )
  364. return resp, err
  365. }
  366. // GetBuildEnv returns the build environment for a given app proto
  367. func (c *Client) GetBuildEnv(
  368. ctx context.Context,
  369. projectID uint, clusterID uint,
  370. appName string, appRevisionId string,
  371. ) (*porter_app.GetBuildEnvResponse, error) {
  372. resp := &porter_app.GetBuildEnvResponse{}
  373. err := c.getRequest(
  374. fmt.Sprintf(
  375. "/projects/%d/clusters/%d/apps/%s/revisions/%s/build-env",
  376. projectID, clusterID, appName, appRevisionId,
  377. ),
  378. nil,
  379. resp,
  380. )
  381. return resp, err
  382. }
  383. // GetAppEnvVariables returns all env variables for a given app
  384. func (c *Client) GetAppEnvVariables(
  385. ctx context.Context,
  386. projectID uint, clusterID uint,
  387. appName string,
  388. deploymentTargetName string,
  389. ) (*porter_app.AppEnvVariablesResponse, error) {
  390. resp := &porter_app.AppEnvVariablesResponse{}
  391. req := &porter_app.AppEnvVariablesRequest{
  392. DeploymentTargetName: deploymentTargetName,
  393. }
  394. err := c.getRequest(
  395. fmt.Sprintf(
  396. "/projects/%d/clusters/%d/apps/%s/env-variables",
  397. projectID, clusterID, appName,
  398. ),
  399. req,
  400. resp,
  401. )
  402. return resp, err
  403. }
  404. // GetBuildFromRevision returns the build environment for a given app proto
  405. func (c *Client) GetBuildFromRevision(
  406. ctx context.Context,
  407. projectID uint, clusterID uint,
  408. appName string, appRevisionId string,
  409. ) (*porter_app.GetBuildFromRevisionResponse, error) {
  410. resp := &porter_app.GetBuildFromRevisionResponse{}
  411. err := c.getRequest(
  412. fmt.Sprintf(
  413. "/projects/%d/clusters/%d/apps/%s/revisions/%s/build",
  414. projectID, clusterID, appName, appRevisionId,
  415. ),
  416. nil,
  417. resp,
  418. )
  419. return resp, err
  420. }
  421. // ReportRevisionStatusInput is the input struct to ReportRevisionStatus
  422. type ReportRevisionStatusInput struct {
  423. ProjectID uint
  424. ClusterID uint
  425. AppName string
  426. AppRevisionID string
  427. PRNumber int
  428. CommitSHA string
  429. }
  430. // ReportRevisionStatus reports the status of an app revision to external services
  431. func (c *Client) ReportRevisionStatus(
  432. ctx context.Context,
  433. inp ReportRevisionStatusInput,
  434. ) (*porter_app.ReportRevisionStatusResponse, error) {
  435. resp := &porter_app.ReportRevisionStatusResponse{}
  436. req := &porter_app.ReportRevisionStatusRequest{
  437. PRNumber: inp.PRNumber,
  438. CommitSHA: inp.CommitSHA,
  439. }
  440. err := c.postRequest(
  441. fmt.Sprintf(
  442. "/projects/%d/clusters/%d/apps/%s/revisions/%s/status",
  443. inp.ProjectID, inp.ClusterID, inp.AppName, inp.AppRevisionID,
  444. ),
  445. req,
  446. resp,
  447. )
  448. return resp, err
  449. }
  450. // CreateOrUpdateAppEnvironment updates the app environment group and creates it if it doesn't exist
  451. func (c *Client) CreateOrUpdateAppEnvironment(
  452. ctx context.Context,
  453. projectID uint, clusterID uint,
  454. appName string,
  455. deploymentTargetID string,
  456. variables map[string]string,
  457. secrets map[string]string,
  458. Base64AppProto string,
  459. ) (*porter_app.UpdateAppEnvironmentResponse, error) {
  460. resp := &porter_app.UpdateAppEnvironmentResponse{}
  461. req := &porter_app.UpdateAppEnvironmentRequest{
  462. DeploymentTargetID: deploymentTargetID,
  463. Variables: variables,
  464. Secrets: secrets,
  465. HardUpdate: false,
  466. Base64AppProto: Base64AppProto,
  467. }
  468. err := c.postRequest(
  469. fmt.Sprintf(
  470. "/projects/%d/clusters/%d/apps/%s/update-environment",
  471. projectID, clusterID, appName,
  472. ),
  473. req,
  474. resp,
  475. )
  476. return resp, err
  477. }
  478. // PorterYamlV2Pods gets all pods for a given deployment target id and app name
  479. func (c *Client) PorterYamlV2Pods(
  480. ctx context.Context,
  481. projectID, clusterID uint,
  482. porterAppName string,
  483. deploymentTargetName string,
  484. ) (*types.GetReleaseAllPodsResponse, error) {
  485. req := &porter_app.PodStatusRequest{
  486. DeploymentTargetName: deploymentTargetName,
  487. }
  488. resp := &types.GetReleaseAllPodsResponse{}
  489. err := c.getRequest(
  490. fmt.Sprintf(
  491. "/projects/%d/clusters/%d/apps/%s/pods",
  492. projectID, clusterID,
  493. porterAppName,
  494. ),
  495. req,
  496. resp,
  497. )
  498. return resp, err
  499. }
  500. // UpdateImage updates the image for a porter app (porter yaml v2 only)
  501. func (c *Client) UpdateImage(
  502. ctx context.Context,
  503. projectID, clusterID uint,
  504. appName, deploymentTargetName, tag string,
  505. ) (*porter_app.UpdateImageResponse, error) {
  506. req := &porter_app.UpdateImageRequest{
  507. Tag: tag,
  508. DeploymentTargetName: deploymentTargetName,
  509. }
  510. resp := &porter_app.UpdateImageResponse{}
  511. err := c.postRequest(
  512. fmt.Sprintf(
  513. "/projects/%d/clusters/%d/apps/%s/update-image",
  514. projectID, clusterID, appName,
  515. ),
  516. &req,
  517. resp,
  518. )
  519. return resp, err
  520. }
  521. // ListAppRevisions lists the last ten app revisions for a given app
  522. func (c *Client) ListAppRevisions(
  523. ctx context.Context,
  524. projectID, clusterID uint,
  525. appName string,
  526. deploymentTargetID string,
  527. ) (*porter_app.ListAppRevisionsResponse, error) {
  528. resp := &porter_app.ListAppRevisionsResponse{}
  529. req := &porter_app.ListAppRevisionsRequest{
  530. DeploymentTargetID: deploymentTargetID,
  531. }
  532. err := c.getRequest(
  533. fmt.Sprintf(
  534. "/projects/%d/clusters/%d/apps/%s/revisions",
  535. projectID, clusterID,
  536. appName,
  537. ),
  538. req,
  539. resp,
  540. )
  541. return resp, err
  542. }
  543. // RollbackRevision reverts an app to a previous revision
  544. func (c *Client) RollbackRevision(
  545. ctx context.Context,
  546. projectID, clusterID uint,
  547. appName string,
  548. deploymentTargetName string,
  549. ) (*porter_app.RollbackAppRevisionResponse, error) {
  550. resp := &porter_app.RollbackAppRevisionResponse{}
  551. req := &porter_app.RollbackAppRevisionRequest{
  552. DeploymentTargetName: deploymentTargetName,
  553. }
  554. err := c.postRequest(
  555. fmt.Sprintf(
  556. "/projects/%d/clusters/%d/apps/%s/rollback",
  557. projectID, clusterID,
  558. appName,
  559. ),
  560. req,
  561. resp,
  562. )
  563. return resp, err
  564. }
  565. // RunAppJob runs a job for an app
  566. func (c *Client) RunAppJob(
  567. ctx context.Context,
  568. projectID, clusterID uint,
  569. appName string, jobName string,
  570. deploymentTargetName string,
  571. ) (*porter_app.RunAppJobResponse, error) {
  572. resp := &porter_app.RunAppJobResponse{}
  573. req := &porter_app.RunAppJobRequest{
  574. ServiceName: jobName,
  575. DeploymentTargetName: deploymentTargetName,
  576. }
  577. err := c.postRequest(
  578. fmt.Sprintf(
  579. "/projects/%d/clusters/%d/apps/%s/run",
  580. projectID, clusterID,
  581. appName,
  582. ),
  583. req,
  584. resp,
  585. )
  586. return resp, err
  587. }
  588. // RunAppJobStatusInput contains all the information necessary to check the status of a job
  589. type RunAppJobStatusInput struct {
  590. // AppName is the name of the app associated with the job
  591. AppName string
  592. // Cluster is the id of the cluster against which to retrieve a helm agent for
  593. ClusterID uint
  594. // DeploymentTargetName is the id of the deployment target the job was run against
  595. DeploymentTargetName string
  596. // ServiceName is the name of the app service that was triggered
  597. ServiceName string
  598. // JobRunID is the UID returned from the /apps/{porter_app_name}/run endpoint
  599. JobRunID string
  600. // ProjectID is the project in which the cluster exists
  601. ProjectID uint
  602. }
  603. // RunAppJobStatus gets the status for a job app run
  604. func (c *Client) RunAppJobStatus(
  605. ctx context.Context,
  606. input RunAppJobStatusInput,
  607. ) (*porter_app.AppJobRunStatusResponse, error) {
  608. resp := &porter_app.AppJobRunStatusResponse{}
  609. req := &porter_app.AppJobRunStatusRequest{
  610. DeploymentTargetName: input.DeploymentTargetName,
  611. JobRunID: input.JobRunID,
  612. ServiceName: input.ServiceName,
  613. }
  614. err := c.getRequest(
  615. fmt.Sprintf(
  616. "/projects/%d/clusters/%d/apps/%s/run-status",
  617. input.ProjectID, input.ClusterID,
  618. input.AppName,
  619. ),
  620. req,
  621. resp,
  622. )
  623. return resp, err
  624. }