Jelajahi Sumber

add steps handlers

Alexander Belanger 4 tahun lalu
induk
melakukan
bb83392bff

+ 66 - 0
api/server/handlers/release/get_steps.go

@@ -0,0 +1,66 @@
+package release
+
+import (
+	"net/http"
+
+	"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/models"
+	"gorm.io/gorm"
+)
+
+type GetReleaseStepsHandler struct {
+	handlers.PorterHandlerReadWriter
+	authz.KubernetesAgentGetter
+}
+
+func NewGetReleaseStepsHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *GetReleaseStepsHandler {
+	return &GetReleaseStepsHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+		KubernetesAgentGetter:   authz.NewOutOfClusterAgentGetter(config),
+	}
+}
+
+func (c *GetReleaseStepsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
+	name, _ := requestutils.GetURLParamString(r, types.URLParamReleaseName)
+	namespace := r.Context().Value(types.NamespaceScope).(string)
+
+	release, err := c.Repo().Release().ReadRelease(cluster.ID, name, namespace)
+
+	if err != nil {
+		if err == gorm.ErrRecordNotFound {
+			w.WriteHeader(http.StatusNotFound)
+			return
+		}
+
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	res := make(types.GetReleaseStepsResponse, 0)
+
+	if release.EventContainer != 0 {
+		subevents, err := c.Repo().Event().ReadEventsByContainerID(release.EventContainer)
+
+		if err != nil {
+			c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+			return
+		}
+
+		for _, sub := range subevents {
+			res = append(res, sub.ToSubEventType())
+		}
+	}
+
+	c.WriteResult(w, r, res)
+}

+ 93 - 0
api/server/handlers/release/update_steps.go

@@ -0,0 +1,93 @@
+package release
+
+import (
+	"net/http"
+
+	"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/models"
+	"gorm.io/gorm"
+)
+
+type UpdateReleaseStepsHandler struct {
+	handlers.PorterHandlerReadWriter
+	authz.KubernetesAgentGetter
+}
+
+func NewUpdateReleaseStepsHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *UpdateReleaseStepsHandler {
+	return &UpdateReleaseStepsHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+		KubernetesAgentGetter:   authz.NewOutOfClusterAgentGetter(config),
+	}
+}
+
+func (c *UpdateReleaseStepsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
+	name, _ := requestutils.GetURLParamString(r, types.URLParamReleaseName)
+	namespace := r.Context().Value(types.NamespaceScope).(string)
+
+	request := &types.UpdateReleaseStepsRequest{}
+
+	if ok := c.DecodeAndValidate(w, r, request); !ok {
+		return
+	}
+
+	release, err := c.Repo().Release().ReadRelease(cluster.ID, name, namespace)
+
+	if err != nil {
+		if err == gorm.ErrRecordNotFound {
+			w.WriteHeader(http.StatusNotFound)
+			return
+		}
+
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	if release.EventContainer == 0 {
+		// create new event container
+		container, err := c.Repo().Event().CreateEventContainer(&models.EventContainer{ReleaseID: release.ID})
+		if err != nil {
+			c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+			return
+		}
+
+		release.EventContainer = container.ID
+
+		release, err = c.Repo().Release().UpdateRelease(release)
+
+		if err != nil {
+			c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+			return
+		}
+
+	}
+
+	container, err := c.Repo().Event().ReadEventContainer(release.EventContainer)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	if err := c.Repo().Event().AppendEvent(container, &models.SubEvent{
+		EventContainerID: container.ID,
+		EventID:          request.Event.ID,
+		Name:             request.Event.Name,
+		Index:            request.Event.Index,
+		Status:           request.Event.Status,
+		Info:             request.Event.Info,
+	}); err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+}

+ 60 - 0
api/server/router/release.go

@@ -320,6 +320,66 @@ func getReleaseRoutes(
 		Router:   r,
 	})
 
+	// GET /api/projects/{project_id}/clusters/{cluster_id}/namespaces/{namespace}/releases/{name}/steps -> release.NewGetReleaseStepsHandler
+	getStepsEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: "/releases/{name}/steps",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+				types.NamespaceScope,
+			},
+		},
+	)
+
+	getStepsHandler := release.NewGetReleaseStepsHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: getStepsEndpoint,
+		Handler:  getStepsHandler,
+		Router:   r,
+	})
+
+	// POST /api/projects/{project_id}/clusters/{cluster_id}/namespaces/{namespace}/releases/{name}/steps -> release.NewUpdateReleaseStepsHandler
+	updateStepsEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbCreate,
+			Method: types.HTTPVerbPost,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: "/releases/{name}/steps",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.ClusterScope,
+				types.NamespaceScope,
+			},
+		},
+	)
+
+	updateStepsHandler := release.NewUpdateReleaseStepsHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: updateStepsEndpoint,
+		Handler:  updateStepsHandler,
+		Router:   r,
+	})
+
 	// POST /api/projects/{project_id}/clusters/{cluster_id}/namespaces/{namespace}/releases -> release.NewCreateReleaseHandler
 	createReleaseEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{

+ 29 - 0
api/types/release.go

@@ -79,3 +79,32 @@ type GetGHATemplateRequest struct {
 }
 
 type GetGHATemplateResponse string
+
+type GetReleaseStepsResponse []SubEvent
+
+type SubEvent struct {
+	EventID string      `json:"event_id"`
+	Name    string      `json:"name"`
+	Index   int64       `json:"index"`
+	Status  EventStatus `json:"status"`
+	Info    string      `json:"info"`
+	Time    int64       `json:"time"`
+}
+
+type EventStatus int64
+
+const (
+	EventStatusSuccess    EventStatus = 1
+	EventStatusInProgress             = 2
+	EventStatusFailed                 = 3
+)
+
+type UpdateReleaseStepsRequest struct {
+	Event struct {
+		ID     string      `json:"event_id" form:"required"`
+		Name   string      `json:"name" form:"required"`
+		Index  int64       `json:"index" form:"required"`
+		Status EventStatus `json:"status" form:"required"`
+		Info   string      `json:"info" form:"required"`
+	} `json:"event" form:"required"`
+}

+ 2 - 2
dashboard/src/main/home/cluster-dashboard/expanded-chart/events/EventsTab.tsx

@@ -91,10 +91,10 @@ const EventsTab: React.FunctionComponent<Props> = (props) => {
           .getReleaseSteps(
               "<token>",
               {
-                cluster_id: currentCluster.id,
-                namespace: props.currentChart.namespace,
               },
               {
+                cluster_id: currentCluster.id,
+                namespace: props.currentChart.namespace,
                 id: currentProject.id,
                 name: props.currentChart.name,
               }

+ 5 - 6
dashboard/src/shared/api.tsx

@@ -691,13 +691,12 @@ const getReleaseToken = baseApi<
 });
 
 const getReleaseSteps = baseApi<
-  {
-    namespace: string;
-    cluster_id: number;
-  },
-  { name: string; id: number }
+  {},
+  { name: string; id: number; namespace: string; cluster_id: number }
 >("GET", (pathParams) => {
-  return `/api/projects/${pathParams.id}/releases/${pathParams.name}/steps`;
+  let { id, cluster_id, namespace, name } = pathParams;
+
+  return `/api/projects/${id}/clusters/${cluster_id}/namespaces/${namespace}/releases/${name}/steps`;
 });
 
 const destroyInfra = baseApi<

+ 4 - 20
internal/models/event.go

@@ -1,17 +1,10 @@
 package models
 
 import (
+	"github.com/porter-dev/porter/api/types"
 	"gorm.io/gorm"
 )
 
-type EventStatus int64
-
-const (
-	EventStatusSuccess    EventStatus = 1
-	EventStatusInProgress             = 2
-	EventStatusFailed                 = 3
-)
-
 type EventContainer struct {
 	gorm.Model
 	ReleaseID uint
@@ -26,21 +19,12 @@ type SubEvent struct {
 	EventID string // events with the same id wil be treated the same, and the highest index one is retained
 	Name    string
 	Index   int64 // priority of the event, used for sorting
-	Status  EventStatus
+	Status  types.EventStatus
 	Info    string
 }
 
-type SubEventExternal struct {
-	EventID string      `json:"event_id"`
-	Name    string      `json:"name"`
-	Index   int64       `json:"index"`
-	Status  EventStatus `json:"status"`
-	Info    string      `json:"info"`
-	Time    int64       `json:"time""`
-}
-
-func (event *SubEvent) Externalize() SubEventExternal {
-	return SubEventExternal{
+func (event *SubEvent) ToSubEventType() types.SubEvent {
+	return types.SubEvent{
 		EventID: event.EventID,
 		Name:    event.Name,
 		Index:   event.Index,