cloudfoundry/korifi

View on GitHub
api/handlers/log_cache.go

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
package handlers
 
import (
"context"
"net/http"
 
"code.cloudfoundry.org/korifi/api/actions"
"code.cloudfoundry.org/korifi/api/authorization"
apierrors "code.cloudfoundry.org/korifi/api/errors"
"code.cloudfoundry.org/korifi/api/payloads"
"code.cloudfoundry.org/korifi/api/presenter"
"code.cloudfoundry.org/korifi/api/repositories"
"code.cloudfoundry.org/korifi/api/routing"
"code.cloudfoundry.org/korifi/tools"
 
"github.com/go-logr/logr"
)
 
const (
LogCacheInfoPath = "/api/v1/info"
LogCacheReadPath = "/api/v1/read/{source-id}"
logCacheVersion = "2.11.4+cf-k8s"
)
 
//counterfeiter:generate -o fake -fake-name ProcessStats . ProcessStats
type ProcessStats interface {
FetchStats(context.Context, authorization.Info, string) ([]actions.PodStatsRecord, error)
FetchAppProcessesStats(context.Context, authorization.Info, string) ([]actions.PodStatsRecord, error)
}
 
//counterfeiter:generate -o fake -fake-name LogRepository . LogRepository
type LogRepository interface {
GetAppLogs(context.Context, authorization.Info, repositories.GetLogsMessage) ([]repositories.LogRecord, error)
}
 
// LogCache implements the minimal set of log-cache API endpoints/features necessary
// to support the "cf push" workfloh.handlerWrapper.
type LogCache struct {
requestValidator RequestValidator
appRepo CFAppRepository
buildRepo CFBuildRepository
logRepo LogRepository
processStats ProcessStats
}
 
func NewLogCache(
requestValidator RequestValidator,
appRepo CFAppRepository,
buildRepository CFBuildRepository,
logRepo LogRepository,
processStats ProcessStats,
) *LogCache {
return &LogCache{
requestValidator: requestValidator,
appRepo: appRepo,
buildRepo: buildRepository,
logRepo: logRepo,
processStats: processStats,
}
}
 
func (h *LogCache) info(r *http.Request) (*routing.Response, error) {
return routing.NewResponse(http.StatusOK).WithBody(map[string]interface{}{
"version": logCacheVersion,
"vm_uptime": "0",
}), nil
}
 
func (h *LogCache) read(r *http.Request) (*routing.Response, error) {
authInfo, _ := authorization.InfoFromContext(r.Context())
logger := logr.FromContextOrDiscard(r.Context()).WithName("handlers.log-cache.read")
 
payload := payloads.LogCacheRead{}
if err := h.requestValidator.DecodeAndValidateURLValues(r, &payload); err != nil {
return nil, apierrors.LogAndReturn(logger, err, "failed to decode payload")
}
 
appGUID := routing.URLParam(r, "source-id")
logger = logger.WithValues("appGUID", appGUID)
 
appRecord, err := h.appRepo.GetApp(r.Context(), authInfo, appGUID)
if err != nil {
return nil, apierrors.LogAndReturn(logger, apierrors.ForbiddenAsNotFound(err), "failed to get app")
}
 
if tools.EmptyOrContains(payload.EnvelopeTypes, "LOG") {
return h.readLogs(r.Context(), authInfo, appRecord, payload)
}
 
return h.readStats(r.Context(), authInfo, appRecord)
}
 
func (h *LogCache) readLogs(ctx context.Context, authInfo authorization.Info, appRecord repositories.AppRecord, payload payloads.LogCacheRead) (*routing.Response, error) {
logger := logr.FromContextOrDiscard(ctx).WithName("handlers.log-cache.read.logs").WithValues("appGUID", appRecord.GUID)
build, err := h.buildRepo.GetLatestBuildByAppGUID(ctx, authInfo, appRecord.SpaceGUID, appRecord.GUID)
if err != nil {
return nil, apierrors.LogAndReturn(logger, apierrors.ForbiddenAsNotFound(err), "failed to get latest app build")
}
 
logs, err := h.logRepo.GetAppLogs(ctx, authInfo, repositories.GetLogsMessage{
App: appRecord,
Build: build,
StartTime: payload.StartTime,
Limit: payload.Limit,
Descending: payload.Descending,
})
if err != nil {
return nil, apierrors.LogAndReturn(logger, apierrors.ForbiddenAsNotFound(err), "failed to get app logs")
}
 
return routing.NewResponse(http.StatusOK).WithBody(presenter.ForLogs(logs)), nil
}
 
func (h *LogCache) readStats(ctx context.Context, authInfo authorization.Info, appRecord repositories.AppRecord) (*routing.Response, error) {
logger := logr.FromContextOrDiscard(ctx).WithName("handlers.log-cache.read.stats").WithValues("appGUID", appRecord.GUID)
stats, err := h.processStats.FetchAppProcessesStats(ctx, authInfo, appRecord.GUID)
if err != nil {
return nil, apierrors.LogAndReturn(logger, apierrors.ForbiddenAsNotFound(err), "failed to fetch app stats")
}
 
return routing.NewResponse(http.StatusOK).WithBody(presenter.ForStats(appRecord, stats)), nil
}
 
func (h *LogCache) UnauthenticatedRoutes() []routing.Route {
return []routing.Route{
{Method: "GET", Pattern: LogCacheInfoPath, Handler: h.info},
}
}
 
func (h *LogCache) AuthenticatedRoutes() []routing.Route {
return []routing.Route{
{Method: "GET", Pattern: LogCacheReadPath, Handler: h.read},
}
}