Alexander Belanger 4 лет назад
Родитель
Сommit
3da89141bd

+ 165 - 0
api/server/handlers/release/upgrade_webhook.go

@@ -0,0 +1,165 @@
+package release
+
+import (
+	"fmt"
+	"net/http"
+	"net/url"
+
+	"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/server/shared/requestutils"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/helm"
+	"github.com/porter-dev/porter/internal/integrations/slack"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+type WebhookHandler struct {
+	handlers.PorterHandlerReadWriter
+	authz.KubernetesAgentGetter
+}
+
+func NewWebhookHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *WebhookHandler {
+	return &WebhookHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+		KubernetesAgentGetter:   authz.NewOutOfClusterAgentGetter(config),
+	}
+}
+
+func (c *WebhookHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	token, _ := requestutils.GetURLParamString(r, types.URLParamToken)
+
+	// retrieve release by token
+	release, err := c.Repo().Release().ReadReleaseByWebhookToken(token)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
+			fmt.Errorf("release not found with given webhook"),
+			http.StatusBadRequest,
+		))
+
+		return
+	}
+
+	cluster, err := c.Repo().Cluster().ReadCluster(release.ProjectID, release.ClusterID)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	helmAgent, err := c.GetHelmAgent(r, cluster)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	request := &types.WebhookRequest{}
+
+	if ok := c.DecodeAndValidate(w, r, request); !ok {
+		return
+	}
+
+	rel, err := helmAgent.GetRelease(release.Name, 0)
+
+	// repository is set to current repository by default
+	repository := rel.Config["image"].(map[string]interface{})["repository"]
+
+	gitAction := release.GitActionConfig
+
+	if gitAction != nil && gitAction.ID != 0 && (repository == "porterdev/hello-porter" || repository == "public.ecr.aws/o1j4x7p4/hello-porter") {
+		repository = gitAction.ImageRepoURI
+	} else if gitAction != nil && gitAction.ID != 0 && (repository == "porterdev/hello-porter-job" || repository == "public.ecr.aws/o1j4x7p4/hello-porter-job") {
+		repository = gitAction.ImageRepoURI
+	}
+
+	image := map[string]interface{}{}
+	image["repository"] = repository
+	image["tag"] = request.Commit
+	rel.Config["image"] = image
+
+	if rel.Config["auto_deploy"] == false {
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
+			fmt.Errorf("Deploy webhook is disabled for this deployment."),
+			http.StatusBadRequest,
+		))
+
+		return
+	}
+
+	registries, err := c.Repo().Registry().ListRegistriesByProjectID(release.ProjectID)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	conf := &helm.UpgradeReleaseConfig{
+		Name:       release.Name,
+		Cluster:    cluster,
+		Repo:       c.Repo(),
+		Registries: registries,
+		Values:     rel.Config,
+	}
+
+	slackInts, _ := c.Repo().SlackIntegration().ListSlackIntegrationsByProjectID(release.ProjectID)
+
+	var notifConf *models.NotificationConfigExternal
+	notifConf = nil
+	if release != nil && release.NotificationConfig != 0 {
+		conf, err := c.Repo().NotificationConfig().ReadNotificationConfig(release.NotificationConfig)
+
+		if err != nil {
+			c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+			return
+		}
+
+		notifConf = conf.Externalize()
+	}
+
+	notifier := slack.NewSlackNotifier(notifConf, slackInts...)
+
+	notifyOpts := &slack.NotifyOpts{
+		ProjectID:   release.ProjectID,
+		ClusterID:   cluster.ID,
+		ClusterName: cluster.Name,
+		Name:        rel.Name,
+		Namespace:   rel.Namespace,
+		URL: fmt.Sprintf(
+			"%s/applications/%s/%s/%s",
+			c.Config().ServerConf.ServerURL,
+			url.PathEscape(cluster.Name),
+			release.Namespace,
+			rel.Name,
+		) + fmt.Sprintf("?project_id=%d", release.ProjectID),
+	}
+
+	rel, err = helmAgent.UpgradeReleaseByValues(conf, c.Config().DOConf)
+
+	if err != nil {
+		notifyOpts.Status = slack.StatusFailed
+		notifyOpts.Info = err.Error()
+
+		notifier.Notify(notifyOpts)
+
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
+			err,
+			http.StatusBadRequest,
+		))
+
+		return
+	}
+
+	notifyOpts.Status = string(rel.Info.Status)
+	notifyOpts.Version = rel.Version
+
+	notifier.Notify(notifyOpts)
+}

+ 26 - 0
api/server/router/base.go

@@ -3,6 +3,7 @@ package router
 import (
 	"github.com/go-chi/chi"
 	"github.com/porter-dev/porter/api/server/handlers/metadata"
+	"github.com/porter-dev/porter/api/server/handlers/release"
 	"github.com/porter-dev/porter/api/server/handlers/user"
 	"github.com/porter-dev/porter/api/server/shared"
 	"github.com/porter-dev/porter/api/server/shared/config"
@@ -192,5 +193,30 @@ func GetBaseRoutes(
 		Router:   r,
 	})
 
+	// POST /api/webhooks/deploy/{token} -> release.NewWebhookHandler
+	webhookEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbUpdate,
+			Method: types.HTTPVerbPost,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: "/webhooks/deploy/{token}",
+			},
+			Scopes: []types.PermissionScope{},
+		},
+	)
+
+	webhookHandler := release.NewWebhookHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: webhookEndpoint,
+		Handler:  webhookHandler,
+		Router:   r,
+	})
+
 	return routes
 }

+ 6 - 0
api/types/release.go

@@ -66,3 +66,9 @@ type GetJobsStatusResponse struct {
 	Status    string       `json:"status,omitempty"`
 	StartTime *metav1.Time `json:"start_time,omitempty"`
 }
+
+const URLParamToken URLParam = "token"
+
+type WebhookRequest struct {
+	Commit string `schema:"commit"`
+}

+ 1 - 1
docs/developing/backend-refactor-status.md

@@ -150,4 +150,4 @@
 | <li>- [x] `GET /api/users/{user_id}`                                                                                        | AB          | yes             |             | yes              |
 | <li>- [x] `DELETE /api/users/{user_id}`                                                                                     | AB          | yes             |             |                  |
 | <li>- [x] `GET /api/users/{user_id}/projects`                                                                               | AB          | yes             |             | yes              |
-| <li>- [ ] `POST /api/webhooks/deploy/{token}`                                                                               |             |                 |             |                  |
+| <li>- [X] `POST /api/webhooks/deploy/{token}`                                                                               | AB          |                 |             |                  |