-
Notifications
You must be signed in to change notification settings - Fork 239
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
boilerplate addon dashboard code (#4526)
- Loading branch information
Feroze Mohideen
authored
Apr 29, 2024
1 parent
171bf19
commit 41362b6
Showing
49 changed files
with
5,191 additions
and
612 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package addons | ||
|
||
import ( | ||
"net/http" | ||
|
||
"connectrpc.com/connect" | ||
"github.com/google/uuid" | ||
porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1" | ||
"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" | ||
"github.com/porter-dev/porter/internal/telemetry" | ||
) | ||
|
||
// DeleteAddonHandler handles requests to the /addons/delete endpoint | ||
type DeleteAddonHandler struct { | ||
handlers.PorterHandlerReadWriter | ||
} | ||
|
||
// NewDeleteAddonHandler returns a new DeleteAddonHandler | ||
func NewDeleteAddonHandler( | ||
config *config.Config, | ||
decoderValidator shared.RequestDecoderValidator, | ||
writer shared.ResultWriter, | ||
) *DeleteAddonHandler { | ||
return &DeleteAddonHandler{ | ||
PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer), | ||
} | ||
} | ||
|
||
func (c *DeleteAddonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
ctx, span := telemetry.NewSpan(r.Context(), "serve-delete-addon") | ||
defer span.End() | ||
|
||
project, _ := ctx.Value(types.ProjectScope).(*models.Project) | ||
deploymentTarget, _ := ctx.Value(types.DeploymentTargetScope).(types.DeploymentTarget) | ||
|
||
addonName, reqErr := requestutils.GetURLParamString(r, types.URLParamAddonName) | ||
if reqErr != nil { | ||
err := telemetry.Error(ctx, span, reqErr, "error parsing addon name") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest)) | ||
return | ||
} | ||
|
||
var deploymentTargetIdentifier *porterv1.DeploymentTargetIdentifier | ||
if deploymentTarget.ID != uuid.Nil { | ||
deploymentTargetIdentifier = &porterv1.DeploymentTargetIdentifier{ | ||
Id: deploymentTarget.ID.String(), | ||
} | ||
} | ||
|
||
if addonName == "" { | ||
err := telemetry.Error(ctx, span, nil, "no addon name provided") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest)) | ||
return | ||
} | ||
|
||
deleteAddonRequest := connect.NewRequest(&porterv1.DeleteAddonRequest{ | ||
ProjectId: int64(project.ID), | ||
DeploymentTargetIdentifier: deploymentTargetIdentifier, | ||
AddonName: addonName, | ||
}) | ||
|
||
_, err := c.Config().ClusterControlPlaneClient.DeleteAddon(ctx, deleteAddonRequest) | ||
if err != nil { | ||
err = telemetry.Error(ctx, span, err, "error deleting addon") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError)) | ||
return | ||
} | ||
|
||
c.WriteResult(w, r, "") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package addons | ||
|
||
import ( | ||
"encoding/base64" | ||
"net/http" | ||
|
||
"connectrpc.com/connect" | ||
"github.com/google/uuid" | ||
"github.com/porter-dev/api-contracts/generated/go/helpers" | ||
porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1" | ||
"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" | ||
"github.com/porter-dev/porter/internal/telemetry" | ||
) | ||
|
||
// AddonHandler handles requests to the /addons/{addon_name} endpoint | ||
type AddonHandler struct { | ||
handlers.PorterHandlerReadWriter | ||
} | ||
|
||
// NewAddonHandler returns a new AddonHandler | ||
func NewAddonHandler( | ||
config *config.Config, | ||
decoderValidator shared.RequestDecoderValidator, | ||
writer shared.ResultWriter, | ||
) *AddonHandler { | ||
return &AddonHandler{ | ||
PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer), | ||
} | ||
} | ||
|
||
// AddonResponse represents the response from the /addons/{addon_name} endpoints | ||
type AddonResponse struct { | ||
Addon string `json:"addon"` | ||
} | ||
|
||
func (c *AddonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
ctx, span := telemetry.NewSpan(r.Context(), "serve-get-addon") | ||
defer span.End() | ||
|
||
project, _ := ctx.Value(types.ProjectScope).(*models.Project) | ||
deploymentTarget, _ := ctx.Value(types.DeploymentTargetScope).(types.DeploymentTarget) | ||
|
||
addonName, reqErr := requestutils.GetURLParamString(r, types.URLParamAddonName) | ||
if reqErr != nil { | ||
err := telemetry.Error(ctx, span, reqErr, "error parsing addon name") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest)) | ||
return | ||
} | ||
|
||
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "addon-name", Value: addonName}) | ||
|
||
var deploymentTargetIdentifier *porterv1.DeploymentTargetIdentifier | ||
if deploymentTarget.ID != uuid.Nil { | ||
deploymentTargetIdentifier = &porterv1.DeploymentTargetIdentifier{ | ||
Id: deploymentTarget.ID.String(), | ||
} | ||
} | ||
|
||
if addonName == "" { | ||
err := telemetry.Error(ctx, span, nil, "no addon name provided") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest)) | ||
return | ||
} | ||
|
||
addonRequest := connect.NewRequest(&porterv1.AddonRequest{ | ||
ProjectId: int64(project.ID), | ||
DeploymentTargetIdentifier: deploymentTargetIdentifier, | ||
AddonName: addonName, | ||
}) | ||
|
||
resp, err := c.Config().ClusterControlPlaneClient.Addon(ctx, addonRequest) | ||
if err != nil { | ||
err = telemetry.Error(ctx, span, err, "error getting addon") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError)) | ||
return | ||
} | ||
|
||
if resp == nil || resp.Msg == nil { | ||
err = telemetry.Error(ctx, span, nil, "addon response is nil") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError)) | ||
return | ||
} | ||
|
||
by, err := helpers.MarshalContractObject(ctx, resp.Msg.Addon) | ||
if err != nil { | ||
err = telemetry.Error(ctx, span, err, "error marshaling addon") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError)) | ||
return | ||
} | ||
|
||
encoded := base64.StdEncoding.EncodeToString(by) | ||
|
||
res := &AddonResponse{ | ||
Addon: encoded, | ||
} | ||
|
||
c.WriteResult(w, r, res) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package addons | ||
|
||
import ( | ||
"fmt" | ||
"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/types" | ||
"github.com/porter-dev/porter/internal/models" | ||
"github.com/porter-dev/porter/internal/telemetry" | ||
) | ||
|
||
// TailscaleServicesHandler handles requests to the /addons/tailscale-services endpoint | ||
type TailscaleServicesHandler struct { | ||
handlers.PorterHandlerReadWriter | ||
authz.KubernetesAgentGetter | ||
} | ||
|
||
// NewTailscaleServicesHandler returns a new TailscaleServicesHandler | ||
func NewTailscaleServicesHandler( | ||
config *config.Config, | ||
decoderValidator shared.RequestDecoderValidator, | ||
writer shared.ResultWriter, | ||
) *TailscaleServicesHandler { | ||
return &TailscaleServicesHandler{ | ||
PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer), | ||
KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config), | ||
} | ||
} | ||
|
||
// TailscaleServicesResponse represents the response from the /addons/tailscale-services endpoints | ||
type TailscaleServicesResponse struct { | ||
Services []TailscaleService `json:"services"` | ||
} | ||
|
||
// TailscaleService represents a Tailscale service | ||
type TailscaleService struct { | ||
Name string `json:"name"` | ||
IP string `json:"ip"` | ||
Port int `json:"port"` | ||
} | ||
|
||
// ServeHTTP returns all services that can be accessed through Tailscale | ||
// TODO: move this logic to CCP | ||
func (c *TailscaleServicesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
ctx, span := telemetry.NewSpan(r.Context(), "serve-get-tailscale-services") | ||
defer span.End() | ||
|
||
project, _ := ctx.Value(types.ProjectScope).(*models.Project) | ||
deploymentTarget, _ := ctx.Value(types.DeploymentTargetScope).(types.DeploymentTarget) | ||
|
||
telemetry.WithAttributes(span, | ||
telemetry.AttributeKV{Key: "namespace", Value: deploymentTarget.Namespace}, | ||
telemetry.AttributeKV{Key: "cluster-id", Value: deploymentTarget.ClusterID}, | ||
) | ||
|
||
cluster, err := c.Repo().Cluster().ReadCluster(project.ID, deploymentTarget.ClusterID) | ||
if err != nil { | ||
err = telemetry.Error(ctx, span, err, "error reading cluster") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError)) | ||
return | ||
} | ||
|
||
agent, err := c.GetAgent(r, cluster, deploymentTarget.Namespace) | ||
if err != nil { | ||
err = telemetry.Error(ctx, span, err, "error getting agent") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError)) | ||
return | ||
} | ||
|
||
svcList, err := agent.ListServices(ctx, deploymentTarget.Namespace, "porter.run/tailscale-svc=true") | ||
if err != nil { | ||
err = telemetry.Error(ctx, span, err, "error listing services") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError)) | ||
return | ||
} | ||
|
||
var services []TailscaleService | ||
for _, svc := range svcList.Items { | ||
var port int | ||
if len(svc.Spec.Ports) > 0 { | ||
port = int(svc.Spec.Ports[0].Port) | ||
} | ||
service := TailscaleService{ | ||
Name: svc.Name, | ||
IP: svc.Spec.ClusterIP, | ||
Port: port, | ||
} | ||
if appName, ok := svc.Labels["porter.run/app-name"]; ok { | ||
if serviceName, ok := svc.Labels["porter.run/service-name"]; ok { | ||
service.Name = fmt.Sprintf("%s (%s)", serviceName, appName) | ||
} | ||
} | ||
|
||
services = append(services, service) | ||
} | ||
|
||
resp := TailscaleServicesResponse{ | ||
Services: services, | ||
} | ||
|
||
c.WriteResult(w, r, resp) | ||
} |
Oops, something went wrong.