Skip to content

Commit

Permalink
get compliance check results from ccp (#4177)
Browse files Browse the repository at this point in the history
  • Loading branch information
ianedwards authored Jan 22, 2024
1 parent 75a906f commit 46cad28
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 1,122 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ openapi.yaml
.idea
portercli
local

go.work.sum

vendor
**/*.env
Expand Down
118 changes: 118 additions & 0 deletions api/server/handlers/cluster/compliance_checks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package cluster

import (
"net/http"

"connectrpc.com/connect"
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/types"
"github.com/porter-dev/porter/internal/compliance"
"github.com/porter-dev/porter/internal/models"
"github.com/porter-dev/porter/internal/telemetry"
)

// ListComplianceChecksHandler is the handler for /compliance/checks
type ListComplianceChecksHandler struct {
handlers.PorterHandlerReadWriter
}

// NewListComplianceChecksHandler returns a new ListComplianceChecksHandler
func NewListComplianceChecksHandler(
config *config.Config,
decoderValidator shared.RequestDecoderValidator,
writer shared.ResultWriter,
) *ListComplianceChecksHandler {
return &ListComplianceChecksHandler{
PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
}
}

// ListComplianceChecksRequest is the expected format for a request to /compliance/checks
type ListComplianceChecksRequest struct {
Vendor compliance.Vendor `schema:"vendor"`
}

// ListComplianceChecksResponse is the expected format for a response from /compliance/checks
type ListComplianceChecksResponse struct {
CheckGroups []compliance.CheckGroup `json:"check_groups,omitempty"`
VendorChecks []compliance.VendorComplianceCheck `json:"vendor_checks,omitempty"`
}

// ServeHTTP retrieves the evaluated compliance checks for a cluster
func (c *ListComplianceChecksHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx, span := telemetry.NewSpan(r.Context(), "serve-compliance-checks")
defer span.End()

project, _ := ctx.Value(types.ProjectScope).(*models.Project)
cluster, _ := ctx.Value(types.ClusterScope).(*models.Cluster)

request := &ListComplianceChecksRequest{}
if ok := c.DecodeAndValidate(w, r, request); !ok {
err := telemetry.Error(ctx, span, nil, "error decoding request")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
return
}

var vendor porterv1.EnumComplianceVendor
if request.Vendor != "" {
switch request.Vendor {
case compliance.Vendor_Vanta:
vendor = porterv1.EnumComplianceVendor_ENUM_COMPLIANCE_VENDOR_VANTA
default:
err := telemetry.Error(ctx, span, nil, "invalid vendor")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
return
}
}

req := connect.NewRequest(&porterv1.ContractComplianceChecksRequest{
ProjectId: int64(project.ID),
ClusterId: int64(cluster.ID),
Vendor: vendor,
})

ccpResp, err := c.Config().ClusterControlPlaneClient.ContractComplianceChecks(ctx, req)
if err != nil {
err := telemetry.Error(ctx, span, err, "error calling ccp for contract compliance checks")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}
if ccpResp == nil {
err := telemetry.Error(ctx, span, err, "ccp resp is nil")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}
if ccpResp.Msg == nil {
err := telemetry.Error(ctx, span, err, "ccp resp msg is nil")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}

cgs, err := compliance.CheckGroupsFromProto(ctx, ccpResp.Msg.CheckGroups)
if err != nil {
err := telemetry.Error(ctx, span, err, "error converting compliance check groups from proto")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "num-check-groups", Value: len(cgs)})

vendorChecks, err := compliance.VendorCheckGroupsFromProto(ctx, ccpResp.Msg.VendorChecks)
if err != nil {
err := telemetry.Error(ctx, span, err, "error converting vendor compliance check groups from proto")
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
return
}
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "num-vendor-checks", Value: len(vendorChecks)})

resp := &ListComplianceChecksResponse{
CheckGroups: cgs,
VendorChecks: vendorChecks,
}

c.WriteResult(w, r, resp)
w.WriteHeader(http.StatusOK)
}
29 changes: 29 additions & 0 deletions api/server/router/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,35 @@ func getClusterRoutes(
Router: r,
})

// GET /api/projects/{project_id}/clusters/{cluster_id}/compliance/checks -> cluster.NewListComplianceChecksHandler
listComplianceChecksEndpoint := factory.NewAPIEndpoint(
&types.APIRequestMetadata{
Verb: types.APIVerbList,
Method: types.HTTPVerbGet,
Path: &types.Path{
Parent: basePath,
RelativePath: relPath + "/compliance/checks",
},
Scopes: []types.PermissionScope{
types.UserScope,
types.ProjectScope,
types.ClusterScope,
},
},
)

listComplianceChecksHandler := cluster.NewListComplianceChecksHandler(
config,
factory.GetDecoderValidator(),
factory.GetResultWriter(),
)

routes = append(routes, &router.Route{
Endpoint: listComplianceChecksEndpoint,
Handler: listComplianceChecksHandler,
Router: r,
})

if config.ServerConf.GithubIncomingWebhookSecret != "" {

// GET /api/projects/{project_id}/clusters/{cluster_id}/environments -> environment.NewListEnvironmentHandler
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ require (
github.com/matryer/is v1.4.0
github.com/nats-io/nats.go v1.24.0
github.com/open-policy-agent/opa v0.44.0
github.com/porter-dev/api-contracts v0.2.89
github.com/porter-dev/api-contracts v0.2.90
github.com/riandyrn/otelchi v0.5.1
github.com/santhosh-tekuri/jsonschema/v5 v5.0.1
github.com/stefanmcshane/helm v0.0.0-20221213002717-88a4a2c6e77d
Expand Down
6 changes: 2 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1523,10 +1523,8 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw=
github.com/porter-dev/api-contracts v0.2.86 h1:uH6beKklp1YCzLBrtuuFVDwWfvQnlBWbxZBtDsO3+AI=
github.com/porter-dev/api-contracts v0.2.86/go.mod h1:fX6JmP5QuzxDLvqP3evFOTXjI4dHxsG0+VKNTjImZU8=
github.com/porter-dev/api-contracts v0.2.89 h1:ugkZr7aaANWdRFbkpeEHReyG1nWr31gx9c9dPvgkKMw=
github.com/porter-dev/api-contracts v0.2.89/go.mod h1:fX6JmP5QuzxDLvqP3evFOTXjI4dHxsG0+VKNTjImZU8=
github.com/porter-dev/api-contracts v0.2.90 h1:0ceIXz0xWNQpqVqhUMt3/RDeEawccfXx3KgM/tRg638=
github.com/porter-dev/api-contracts v0.2.90/go.mod h1:fX6JmP5QuzxDLvqP3evFOTXjI4dHxsG0+VKNTjImZU8=
github.com/porter-dev/switchboard v0.0.3 h1:dBuYkiVLa5Ce7059d6qTe9a1C2XEORFEanhbtV92R+M=
github.com/porter-dev/switchboard v0.0.3/go.mod h1:xSPzqSFMQ6OSbp42fhCi4AbGbQbsm6nRvOkrblFeXU4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
Expand Down
Loading

0 comments on commit 46cad28

Please sign in to comment.