create.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. package porter_app
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "errors"
  6. "fmt"
  7. "net/http"
  8. "strings"
  9. "github.com/google/uuid"
  10. "github.com/porter-dev/porter/internal/kubernetes"
  11. "github.com/porter-dev/porter/internal/kubernetes/envgroup"
  12. "github.com/porter-dev/porter/internal/telemetry"
  13. "github.com/porter-dev/porter/api/server/authz"
  14. "github.com/porter-dev/porter/api/server/handlers"
  15. "github.com/porter-dev/porter/api/server/shared"
  16. "github.com/porter-dev/porter/api/server/shared/apierrors"
  17. "github.com/porter-dev/porter/api/server/shared/config"
  18. "github.com/porter-dev/porter/api/server/shared/requestutils"
  19. "github.com/porter-dev/porter/api/types"
  20. "github.com/porter-dev/porter/internal/helm"
  21. "github.com/porter-dev/porter/internal/helm/loader"
  22. "github.com/porter-dev/porter/internal/models"
  23. "github.com/porter-dev/porter/internal/repository"
  24. "github.com/stefanmcshane/helm/pkg/chart"
  25. )
  26. type CreatePorterAppHandler struct {
  27. handlers.PorterHandlerReadWriter
  28. authz.KubernetesAgentGetter
  29. }
  30. func NewCreatePorterAppHandler(
  31. config *config.Config,
  32. decoderValidator shared.RequestDecoderValidator,
  33. writer shared.ResultWriter,
  34. ) *CreatePorterAppHandler {
  35. return &CreatePorterAppHandler{
  36. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  37. KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config),
  38. }
  39. }
  40. func (c *CreatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  41. ctx := r.Context()
  42. project, _ := ctx.Value(types.ProjectScope).(*models.Project)
  43. cluster, _ := ctx.Value(types.ClusterScope).(*models.Cluster)
  44. ctx, span := telemetry.NewSpan(r.Context(), "serve-create-porter-app")
  45. defer span.End()
  46. request := &types.CreatePorterAppRequest{}
  47. if ok := c.DecodeAndValidate(w, r, request); !ok {
  48. err := telemetry.Error(ctx, span, nil, "error decoding request")
  49. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  50. return
  51. }
  52. stackName, reqErr := requestutils.GetURLParamString(r, types.URLParamStackName)
  53. if reqErr != nil {
  54. err := telemetry.Error(ctx, span, reqErr, "error getting stack name from url")
  55. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  56. return
  57. }
  58. namespace := fmt.Sprintf("porter-stack-%s", stackName)
  59. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "application-name", Value: stackName})
  60. helmAgent, err := c.GetHelmAgent(ctx, r, cluster, namespace)
  61. if err != nil {
  62. err = telemetry.Error(ctx, span, err, "error getting helm agent")
  63. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  64. return
  65. }
  66. k8sAgent, err := c.GetAgent(r, cluster, namespace)
  67. if err != nil {
  68. err = telemetry.Error(ctx, span, err, "error getting k8s agent")
  69. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  70. return
  71. }
  72. helmRelease, err := helmAgent.GetRelease(ctx, stackName, 0, false)
  73. shouldCreate := err != nil
  74. porterYamlBase64 := request.PorterYAMLBase64
  75. porterYaml, err := base64.StdEncoding.DecodeString(porterYamlBase64)
  76. if err != nil {
  77. err = telemetry.Error(ctx, span, err, "error decoding porter yaml")
  78. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  79. return
  80. }
  81. imageInfo := request.ImageInfo
  82. registries, err := c.Repo().Registry().ListRegistriesByProjectID(cluster.ProjectID)
  83. if err != nil {
  84. err = telemetry.Error(ctx, span, err, "error listing registries")
  85. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  86. return
  87. }
  88. var releaseValues map[string]interface{}
  89. var releaseDependencies []*chart.Dependency
  90. if shouldCreate || request.OverrideRelease {
  91. releaseValues = nil
  92. releaseDependencies = nil
  93. // this is required because when the front-end sends an update request with overrideRelease=true, it is unable to
  94. // get the image info from the release. unless it is explicitly provided in the request, we avoid overwriting it
  95. // by attempting to get the image info from the release
  96. if helmRelease != nil && (imageInfo.Repository == "" || imageInfo.Tag == "") {
  97. imageInfo = attemptToGetImageInfoFromRelease(helmRelease.Config)
  98. }
  99. } else {
  100. releaseValues = helmRelease.Config
  101. releaseDependencies = helmRelease.Chart.Metadata.Dependencies
  102. }
  103. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "image-repo", Value: imageInfo.Repository}, telemetry.AttributeKV{Key: "image-tag", Value: imageInfo.Tag})
  104. if request.Builder == "" {
  105. // attempt to get builder from db
  106. app, err := c.Repo().PorterApp().ReadPorterAppByName(cluster.ID, stackName)
  107. if err == nil {
  108. request.Builder = app.Builder
  109. }
  110. }
  111. injectLauncher := strings.Contains(request.Builder, "heroku") ||
  112. strings.Contains(request.Builder, "paketo")
  113. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "builder", Value: request.Builder})
  114. if shouldCreate {
  115. // create the namespace if it does not exist already
  116. _, err = k8sAgent.CreateNamespace(namespace, nil)
  117. if err != nil {
  118. err = telemetry.Error(ctx, span, err, "error creating namespace")
  119. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  120. return
  121. }
  122. cloneEnvGroup(c, w, r, k8sAgent, request.EnvGroups, namespace)
  123. }
  124. chart, values, releaseJobValues, err := parse(
  125. porterYaml,
  126. imageInfo,
  127. c.Config(),
  128. cluster.ProjectID,
  129. request.UserUpdate,
  130. request.EnvGroups,
  131. namespace,
  132. releaseValues,
  133. releaseDependencies,
  134. SubdomainCreateOpts{
  135. k8sAgent: k8sAgent,
  136. dnsRepo: c.Repo().DNSRecord(),
  137. powerDnsClient: c.Config().PowerDNSClient,
  138. appRootDomain: c.Config().ServerConf.AppRootDomain,
  139. stackName: stackName,
  140. },
  141. injectLauncher,
  142. shouldCreate,
  143. )
  144. if err != nil {
  145. err = telemetry.Error(ctx, span, err, "error parsing porter yaml into chart and values")
  146. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  147. return
  148. }
  149. if shouldCreate {
  150. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "installing-application", Value: true})
  151. // create the release job chart if it does not exist (only done by front-end currently, where we set overrideRelease=true)
  152. if request.OverrideRelease && releaseJobValues != nil {
  153. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "installing-pre-deploy-job", Value: true})
  154. conf, err := createReleaseJobChart(
  155. ctx,
  156. stackName,
  157. releaseJobValues,
  158. c.Config().ServerConf.DefaultApplicationHelmRepoURL,
  159. registries,
  160. cluster,
  161. c.Repo(),
  162. )
  163. if err != nil {
  164. err = telemetry.Error(ctx, span, err, "error making config for pre-deploy job chart")
  165. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  166. return
  167. }
  168. _, err = helmAgent.InstallChart(ctx, conf, c.Config().DOConf, c.Config().ServerConf.DisablePullSecretsInjection)
  169. if err != nil {
  170. err = telemetry.Error(ctx, span, err, "error installing pre-deploy job chart")
  171. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "install-pre-deploy-job-error", Value: err})
  172. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  173. _, uninstallChartErr := helmAgent.UninstallChart(ctx, fmt.Sprintf("%s-r", stackName))
  174. if uninstallChartErr != nil {
  175. uninstallChartErr = telemetry.Error(ctx, span, err, "error uninstalling pre-deploy job chart after failed install")
  176. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(uninstallChartErr, http.StatusInternalServerError))
  177. }
  178. return
  179. }
  180. }
  181. conf := &helm.InstallChartConfig{
  182. Chart: chart,
  183. Name: stackName,
  184. Namespace: namespace,
  185. Values: values,
  186. Cluster: cluster,
  187. Repo: c.Repo(),
  188. Registries: registries,
  189. }
  190. // create the app chart
  191. _, err = helmAgent.InstallChart(ctx, conf, c.Config().DOConf, c.Config().ServerConf.DisablePullSecretsInjection)
  192. if err != nil {
  193. err = telemetry.Error(ctx, span, err, "error installing app chart")
  194. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  195. _, err = helmAgent.UninstallChart(ctx, stackName)
  196. if err != nil {
  197. err = telemetry.Error(ctx, span, err, "error uninstalling app chart after failed install")
  198. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  199. }
  200. return
  201. }
  202. existing, err := c.Repo().PorterApp().ReadPorterAppByName(cluster.ID, stackName)
  203. if err != nil {
  204. err = telemetry.Error(ctx, span, err, "error reading app from DB")
  205. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  206. return
  207. } else if existing.Name != "" {
  208. err = telemetry.Error(ctx, span, err, "app with name already exists in project")
  209. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusForbidden))
  210. return
  211. }
  212. app := &models.PorterApp{
  213. Name: stackName,
  214. ClusterID: cluster.ID,
  215. ProjectID: project.ID,
  216. RepoName: request.RepoName,
  217. GitRepoID: request.GitRepoID,
  218. GitBranch: request.GitBranch,
  219. BuildContext: request.BuildContext,
  220. Builder: request.Builder,
  221. Buildpacks: request.Buildpacks,
  222. Dockerfile: request.Dockerfile,
  223. ImageRepoURI: request.ImageRepoURI,
  224. PullRequestURL: request.PullRequestURL,
  225. PorterYamlPath: request.PorterYamlPath,
  226. }
  227. // create the db entry
  228. porterApp, err := c.Repo().PorterApp().UpdatePorterApp(app)
  229. if err != nil {
  230. err = telemetry.Error(ctx, span, err, "error writing app to DB")
  231. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  232. return
  233. }
  234. _, err = createPorterAppEvent(ctx, "SUCCESS", porterApp.ID, 1, imageInfo.Tag, c.Repo().PorterAppEvent())
  235. if err != nil {
  236. err = telemetry.Error(ctx, span, err, "error creating porter app event")
  237. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  238. return
  239. }
  240. c.WriteResult(w, r, porterApp.ToPorterAppType())
  241. } else {
  242. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "upgrading-application", Value: true})
  243. // create/update the release job chart
  244. if request.OverrideRelease {
  245. if releaseJobValues == nil {
  246. releaseJobName := fmt.Sprintf("%s-r", stackName)
  247. _, err := helmAgent.GetRelease(ctx, releaseJobName, 0, false)
  248. if err == nil {
  249. // handle exception where the user has chosen to delete the release job
  250. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "deleting-pre-deploy-job", Value: true})
  251. _, err = helmAgent.UninstallChart(ctx, releaseJobName)
  252. if err != nil {
  253. err = telemetry.Error(ctx, span, err, "error uninstalling pre-deploy job chart")
  254. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  255. return
  256. }
  257. }
  258. } else {
  259. releaseJobName := fmt.Sprintf("%s-r", stackName)
  260. helmRelease, err := helmAgent.GetRelease(ctx, releaseJobName, 0, false)
  261. if err != nil {
  262. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "creating-pre-deploy-job", Value: true})
  263. conf, err := createReleaseJobChart(
  264. ctx,
  265. stackName,
  266. releaseJobValues,
  267. c.Config().ServerConf.DefaultApplicationHelmRepoURL,
  268. registries,
  269. cluster,
  270. c.Repo(),
  271. )
  272. if err != nil {
  273. err = telemetry.Error(ctx, span, err, "error making config for pre-deploy job chart")
  274. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  275. return
  276. }
  277. _, err = helmAgent.InstallChart(ctx, conf, c.Config().DOConf, c.Config().ServerConf.DisablePullSecretsInjection)
  278. if err != nil {
  279. err = telemetry.Error(ctx, span, err, "error installing pre-deploy job chart")
  280. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "install-pre-deploy-job-error", Value: err})
  281. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  282. _, uninstallChartErr := helmAgent.UninstallChart(ctx, fmt.Sprintf("%s-r", stackName))
  283. if uninstallChartErr != nil {
  284. uninstallChartErr = telemetry.Error(ctx, span, err, "error uninstalling pre-deploy job chart after failed install")
  285. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(uninstallChartErr, http.StatusInternalServerError))
  286. }
  287. return
  288. }
  289. } else {
  290. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "updating-pre-deploy-job", Value: true})
  291. chart, err := loader.LoadChartPublic(ctx, c.Config().Metadata.DefaultAppHelmRepoURL, "job", "")
  292. if err != nil {
  293. err = telemetry.Error(ctx, span, err, "error loading latest job chart")
  294. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  295. return
  296. }
  297. conf := &helm.UpgradeReleaseConfig{
  298. Name: helmRelease.Name,
  299. Cluster: cluster,
  300. Repo: c.Repo(),
  301. Registries: registries,
  302. Values: releaseJobValues,
  303. Chart: chart,
  304. }
  305. _, err = helmAgent.UpgradeReleaseByValues(ctx, conf, c.Config().DOConf, c.Config().ServerConf.DisablePullSecretsInjection, false)
  306. if err != nil {
  307. err = telemetry.Error(ctx, span, err, "error upgrading pre-deploy job chart")
  308. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  309. return
  310. }
  311. }
  312. }
  313. }
  314. // update the app chart
  315. conf := &helm.InstallChartConfig{
  316. Chart: chart,
  317. Name: stackName,
  318. Namespace: namespace,
  319. Values: values,
  320. Cluster: cluster,
  321. Repo: c.Repo(),
  322. Registries: registries,
  323. }
  324. // update the chart
  325. _, err = helmAgent.UpgradeInstallChart(ctx, conf, c.Config().DOConf, c.Config().ServerConf.DisablePullSecretsInjection)
  326. if err != nil {
  327. err = telemetry.Error(ctx, span, err, "error upgrading application")
  328. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  329. return
  330. }
  331. // update the DB entry
  332. app, err := c.Repo().PorterApp().ReadPorterAppByName(cluster.ID, stackName)
  333. if err != nil {
  334. err = telemetry.Error(ctx, span, err, "error reading app from DB")
  335. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  336. return
  337. }
  338. if app == nil {
  339. err = telemetry.Error(ctx, span, nil, "app with name does not exist in project")
  340. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusForbidden))
  341. return
  342. }
  343. if request.RepoName != "" {
  344. app.RepoName = request.RepoName
  345. }
  346. if request.GitBranch != "" {
  347. app.GitBranch = request.GitBranch
  348. }
  349. if request.BuildContext != "" {
  350. app.BuildContext = request.BuildContext
  351. }
  352. // handles deletion of builder,buildpacks, and dockerfile path
  353. if request.Builder != "" {
  354. if request.Builder == "null" {
  355. app.Builder = ""
  356. } else {
  357. app.Builder = request.Builder
  358. }
  359. }
  360. if request.Buildpacks != "" {
  361. if request.Buildpacks == "null" {
  362. app.Buildpacks = ""
  363. } else {
  364. app.Buildpacks = request.Buildpacks
  365. }
  366. }
  367. if request.Dockerfile != "" {
  368. if request.Dockerfile == "null" {
  369. app.Dockerfile = ""
  370. } else {
  371. app.Dockerfile = request.Dockerfile
  372. }
  373. }
  374. if request.ImageRepoURI != "" {
  375. app.ImageRepoURI = request.ImageRepoURI
  376. }
  377. if request.PullRequestURL != "" {
  378. app.PullRequestURL = request.PullRequestURL
  379. }
  380. telemetry.WithAttributes(
  381. span,
  382. telemetry.AttributeKV{Key: "updated-repo-name", Value: app.RepoName},
  383. telemetry.AttributeKV{Key: "updated-git-branch", Value: app.GitBranch},
  384. telemetry.AttributeKV{Key: "updated-build-context", Value: app.BuildContext},
  385. telemetry.AttributeKV{Key: "updated-builder", Value: app.Builder},
  386. telemetry.AttributeKV{Key: "updated-buildpacks", Value: app.Buildpacks},
  387. telemetry.AttributeKV{Key: "updated-dockerfile", Value: app.Dockerfile},
  388. telemetry.AttributeKV{Key: "updated-image-repo-uri", Value: app.ImageRepoURI},
  389. telemetry.AttributeKV{Key: "updated-pull-request-url", Value: app.PullRequestURL},
  390. )
  391. updatedPorterApp, err := c.Repo().PorterApp().UpdatePorterApp(app)
  392. if err != nil {
  393. err = telemetry.Error(ctx, span, err, "error writing updated app to DB")
  394. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  395. return
  396. }
  397. _, err = createPorterAppEvent(ctx, "SUCCESS", updatedPorterApp.ID, helmRelease.Version+1, imageInfo.Tag, c.Repo().PorterAppEvent())
  398. if err != nil {
  399. err = telemetry.Error(ctx, span, err, "error creating porter app event")
  400. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  401. return
  402. }
  403. c.WriteResult(w, r, updatedPorterApp.ToPorterAppType())
  404. }
  405. }
  406. // createPorterAppEvent creates an event for use in the activity feed
  407. func createPorterAppEvent(ctx context.Context, status string, appID uint, revision int, tag string, repo repository.PorterAppEventRepository) (*models.PorterAppEvent, error) {
  408. event := models.PorterAppEvent{
  409. ID: uuid.New(),
  410. Status: status,
  411. Type: "DEPLOY",
  412. TypeExternalSource: "KUBERNETES",
  413. PorterAppID: appID,
  414. Metadata: map[string]any{
  415. "revision": revision,
  416. "image_tag": tag,
  417. },
  418. }
  419. err := repo.CreateEvent(ctx, &event)
  420. if err != nil {
  421. return nil, err
  422. }
  423. if event.ID == uuid.Nil {
  424. return nil, err
  425. }
  426. return &event, nil
  427. }
  428. func createReleaseJobChart(
  429. ctx context.Context,
  430. stackName string,
  431. values map[string]interface{},
  432. repoUrl string,
  433. registries []*models.Registry,
  434. cluster *models.Cluster,
  435. repo repository.Repository,
  436. ) (*helm.InstallChartConfig, error) {
  437. chart, err := loader.LoadChartPublic(ctx, repoUrl, "job", "")
  438. if err != nil {
  439. return nil, err
  440. }
  441. releaseName := fmt.Sprintf("%s-r", stackName)
  442. namespace := fmt.Sprintf("porter-stack-%s", stackName)
  443. return &helm.InstallChartConfig{
  444. Chart: chart,
  445. Name: releaseName,
  446. Namespace: namespace,
  447. Values: values,
  448. Cluster: cluster,
  449. Repo: repo,
  450. Registries: registries,
  451. }, nil
  452. }
  453. func cloneEnvGroup(c *CreatePorterAppHandler, w http.ResponseWriter, r *http.Request, agent *kubernetes.Agent, envGroups []string, namespace string) {
  454. for _, envGroupName := range envGroups {
  455. cm, _, err := agent.GetLatestVersionedConfigMap(envGroupName, "default")
  456. if err != nil {
  457. if errors.Is(err, kubernetes.IsNotFoundError) {
  458. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
  459. fmt.Errorf("error cloning env group: envgroup %s in namespace %s not found", envGroupName, "default"), http.StatusNotFound,
  460. "no config map found for envgroup",
  461. ))
  462. return
  463. }
  464. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  465. return
  466. }
  467. secret, _, err := agent.GetLatestVersionedSecret(envGroupName, "default")
  468. if err != nil {
  469. if errors.Is(err, kubernetes.IsNotFoundError) {
  470. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
  471. fmt.Errorf("error cloning env group: envgroup %s in namespace %s not found", envGroupName, "default"), http.StatusNotFound,
  472. "no k8s secret found for envgroup",
  473. ))
  474. return
  475. }
  476. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  477. return
  478. }
  479. vars := make(map[string]string)
  480. secretVars := make(map[string]string)
  481. for key, val := range cm.Data {
  482. if !strings.Contains(val, "PORTERSECRET") {
  483. vars[key] = val
  484. }
  485. }
  486. for key, val := range secret.Data {
  487. secretVars[key] = string(val)
  488. }
  489. configMap, err := envgroup.CreateEnvGroup(agent, types.ConfigMapInput{
  490. Name: envGroupName,
  491. Namespace: namespace,
  492. Variables: vars,
  493. SecretVariables: secretVars,
  494. })
  495. if err != nil {
  496. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  497. return
  498. }
  499. _, err = envgroup.ToEnvGroup(configMap)
  500. if err != nil {
  501. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  502. return
  503. }
  504. }
  505. }