package release import ( "context" "fmt" "net/http" semver "github.com/Masterminds/semver/v3" "github.com/porter-dev/porter/api/server/authz" "github.com/porter-dev/porter/api/server/handlers" "github.com/porter-dev/porter/api/server/shared" "github.com/porter-dev/porter/api/server/shared/apierrors" "github.com/porter-dev/porter/api/server/shared/config" "github.com/porter-dev/porter/api/types" "github.com/porter-dev/porter/internal/models" "github.com/stefanmcshane/helm/pkg/release" ) type RollbackReleaseHandler struct { handlers.PorterHandlerReadWriter authz.KubernetesAgentGetter } func NewRollbackReleaseHandler( config *config.Config, decoderValidator shared.RequestDecoderValidator, writer shared.ResultWriter, ) *RollbackReleaseHandler { return &RollbackReleaseHandler{ PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer), KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config), } } func (c *RollbackReleaseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { user, _ := r.Context().Value(types.UserScope).(*models.User) cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster) helmRelease, _ := r.Context().Value(types.ReleaseScope).(*release.Release) helmAgent, err := c.GetHelmAgent(r.Context(), r, cluster, "") if err != nil { c.HandleAPIError(w, r, apierrors.NewErrInternal(err)) return } request := &types.RollbackReleaseRequest{} if ok := c.DecodeAndValidate(w, r, request); !ok { return } err = helmAgent.RollbackRelease(context.Background(), helmRelease.Name, request.Revision) if err != nil { c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient( fmt.Errorf("error rolling back release: %s", err.Error()), http.StatusBadRequest, )) return } // update the github actions env if the release exists and is built from source if cName := helmRelease.Chart.Metadata.Name; cName == "job" || cName == "web" || cName == "worker" { rel, err := c.Repo().Release().ReadRelease(cluster.ID, helmRelease.Name, helmRelease.Namespace) if err == nil && rel != nil { err = UpdateReleaseRepo(c.Config(), rel, helmRelease) if err != nil { c.HandleAPIError(w, r, apierrors.NewErrInternal(err)) return } gitAction := rel.GitActionConfig if gitAction != nil && gitAction.ID != 0 && gitAction.GitlabIntegrationID == 0 { gaRunner, err := GetGARunner( r.Context(), c.Config(), user.ID, cluster.ProjectID, cluster.ID, rel.GitActionConfig, helmRelease.Name, helmRelease.Namespace, rel, helmRelease, ) if err != nil { c.HandleAPIError(w, r, apierrors.NewErrInternal(err)) return } actionVersion, err := semver.NewVersion(gaRunner.Version) if err != nil { c.HandleAPIError(w, r, apierrors.NewErrInternal(err)) return } if createEnvSecretConstraint.Check(actionVersion) { if err := gaRunner.CreateEnvSecret(); err != nil { c.HandleAPIError(w, r, apierrors.NewErrInternal(err)) return } } } } } } func UpdateReleaseRepo(config *config.Config, release *models.Release, helmRelease *release.Release) error { repository := helmRelease.Config["image"].(map[string]interface{})["repository"] repoStr, ok := repository.(string) if !ok { return fmt.Errorf("Could not find field repository in config") } if repoStr != release.ImageRepoURI && repoStr != "public.ecr.aws/o1j4x7p4/hello-porter" && repoStr != "public.ecr.aws/o1j4x7p4/hello-porter-job" { release.ImageRepoURI = repoStr _, err := config.Repo.Release().UpdateRelease(release) if err != nil { return err } // determine if the git action config is set, and propagate update to that as well if release.GitActionConfig != nil && release.GitActionConfig.ID != 0 { gitActionConfig, err := config.Repo.GitActionConfig().ReadGitActionConfig(release.GitActionConfig.ID) if err != nil { return err } gitActionConfig.ImageRepoURI = repoStr err = config.Repo.GitActionConfig().UpdateGitActionConfig(gitActionConfig) if err != nil { return err } } } return nil }