Skip to content

Commit

Permalink
Merge branch 'main' into bump-troubleshoot-v0.71.1
Browse files Browse the repository at this point in the history
  • Loading branch information
arcolife authored Sep 15, 2023
2 parents c55e4cc + 5de9edb commit fb8f84a
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 146 deletions.
7 changes: 0 additions & 7 deletions pkg/apiserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,13 +200,6 @@ func Start(params *APIServerParams) {

handlers.RegisterSessionAuthRoutes(r.PathPrefix("").Subrouter(), kotsStore, handler, policyMiddleware)

/**********************************************************************
* Session auth routes
**********************************************************************/

licenseAuthRouter := r.PathPrefix("").Subrouter()
handlers.RegisterLicenseIDAuthRoutes(licenseAuthRouter, kotsStore, handler, policyMiddleware)

// Prevent API requests that don't match anything in this router from returning UI content
r.PathPrefix("/api").Handler(handlers.StatusNotFoundHandler{})

Expand Down
75 changes: 48 additions & 27 deletions pkg/handlers/custom_metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import (

"github.com/pkg/errors"
"github.com/replicatedhq/kots/pkg/kotsadm"
"github.com/replicatedhq/kots/pkg/kotsutil"
"github.com/replicatedhq/kots/pkg/logger"
"github.com/replicatedhq/kots/pkg/replicatedapp"
"github.com/replicatedhq/kots/pkg/session"
"github.com/replicatedhq/kots/pkg/store"
)

type SendCustomApplicationMetricsRequest struct {
Expand All @@ -18,37 +19,57 @@ type SendCustomApplicationMetricsRequest struct {

type ApplicationMetricsData map[string]interface{}

func (h *Handler) SendCustomApplicationMetrics(w http.ResponseWriter, r *http.Request) {
if kotsadm.IsAirgap() {
w.WriteHeader(http.StatusForbidden)
w.Write([]byte("This request cannot be satisfied in airgap mode"))
return
}
func (h *Handler) GetSendCustomApplicationMetricsHandler(kotsStore store.Store) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if kotsadm.IsAirgap() {
w.WriteHeader(http.StatusForbidden)
w.Write([]byte("This request cannot be satisfied in airgap mode"))
return
}

license := session.ContextGetLicense(r)
app := session.ContextGetApp(r)
apps, err := kotsStore.ListInstalledApps()
if err != nil {
logger.Error(errors.Wrap(err, "list installed apps"))
w.WriteHeader(http.StatusInternalServerError)
return
}

request := SendCustomApplicationMetricsRequest{}
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
logger.Error(errors.Wrap(err, "decode request"))
w.WriteHeader(http.StatusBadRequest)
return
}
if len(apps) != 1 {
logger.Errorf("custom application metrics can be sent only if one app is installed")
w.WriteHeader(http.StatusInternalServerError)
return
}

if err := validateCustomMetricsData(request.Data); err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
app := apps[0]
license, err := kotsutil.LoadLicenseFromBytes([]byte(app.License))
if err != nil {
logger.Error(errors.Wrap(err, "load license from bytes"))
w.WriteHeader(http.StatusInternalServerError)
return
}

err := replicatedapp.SendApplicationMetricsData(license, app, request.Data)
if err != nil {
logger.Error(errors.Wrap(err, "set application data"))
w.WriteHeader(http.StatusBadRequest)
return
}
request := SendCustomApplicationMetricsRequest{}
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
logger.Error(errors.Wrap(err, "decode request"))
w.WriteHeader(http.StatusBadRequest)
return
}

if err := validateCustomMetricsData(request.Data); err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}

JSON(w, http.StatusOK, "")
err = replicatedapp.SendApplicationMetricsData(license, app, request.Data)
if err != nil {
logger.Error(errors.Wrap(err, "set application data"))
w.WriteHeader(http.StatusBadRequest)
return
}

JSON(w, http.StatusOK, "")
}
}

func validateCustomMetricsData(data ApplicationMetricsData) error {
Expand Down
33 changes: 18 additions & 15 deletions pkg/handlers/custom_metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package handlers

import (
"bytes"
"fmt"
"io"
"net/http"
"net/http/httptest"
Expand All @@ -11,8 +12,7 @@ import (
gomock "github.com/golang/mock/gomock"
"github.com/gorilla/mux"
apptypes "github.com/replicatedhq/kots/pkg/app/types"
"github.com/replicatedhq/kots/pkg/session"
"github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
mock_store "github.com/replicatedhq/kots/pkg/store/mock"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -68,9 +68,6 @@ func Test_validateCustomMetricsData(t *testing.T) {
}

func Test_SendCustomApplicationMetrics(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

req := require.New(t)
customMetricsData := []byte(`{"data":{"key1_string":"val1","key2_int":5,"key3_float":1.5,"key4_numeric_string":"1.6"}}`)
appID := "app-id-123"
Expand All @@ -94,24 +91,30 @@ func Test_SendCustomApplicationMetrics(t *testing.T) {
os.Setenv("USE_MOCK_REPORTING", "1")
defer os.Unsetenv("USE_MOCK_REPORTING")

ctrl := gomock.NewController(t)
defer ctrl.Finish()

app := apptypes.App{
ID: appID,
License: fmt.Sprintf(`apiVersion: kots.io/v1beta1
kind: License
spec:
licenseID: 2ULcK9BJd1dHGetHYZIysK9IADZ
endpoint: %s`, server.URL),
}

mockStore := mock_store.NewMockStore(ctrl)
mockStore.EXPECT().ListInstalledApps().Times(1).Return([]*apptypes.App{&app}, nil)

handler := Handler{}
clientWriter := httptest.NewRecorder()
clientRequest := &http.Request{
Body: io.NopCloser(bytes.NewBuffer(customMetricsData)),
}

clientRequest = session.ContextSetLicense(clientRequest, &v1beta1.License{
Spec: v1beta1.LicenseSpec{
Endpoint: server.URL,
},
})
clientRequest = session.ContextSetApp(clientRequest, &apptypes.App{
ID: appID,
})

// Validate

handler.SendCustomApplicationMetrics(clientWriter, clientRequest)
handler.GetSendCustomApplicationMetricsHandler(mockStore)(clientWriter, clientRequest)

req.Equal(http.StatusOK, clientWriter.Code)
}
9 changes: 2 additions & 7 deletions pkg/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,14 +339,9 @@ func RegisterUnauthenticatedRoutes(handler *Handler, kotsStore store.Store, debu
loggingRouter.Path("/api/v1/troubleshoot/supportbundle/{bundleId}/redactions").Methods("PUT").HandlerFunc(handler.SetSupportBundleRedactions)
loggingRouter.Path("/api/v1/preflight/app/{appSlug}/sequence/{sequence}").Methods("POST").HandlerFunc(handler.PostPreflightStatus)

// This the handler for license API and should be called by the application only.
// These handlers should be called by the application only.
loggingRouter.Path("/license/v1/license").Methods("GET").HandlerFunc(handler.GetPlatformLicenseCompatibility)
}

func RegisterLicenseIDAuthRoutes(r *mux.Router, kotsStore store.Store, handler KOTSHandler, middleware *policy.Middleware) {
r.Use(LoggingMiddleware, RequireValidLicenseMiddleware(kotsStore))
r.Name("SendCustomApplicationMetrics").Path("/api/v1/app/custom-metrics").Methods("POST").
HandlerFunc(middleware.EnforceLicense(handler.SendCustomApplicationMetrics))
loggingRouter.Path("/api/v1/app/custom-metrics").Methods("POST").HandlerFunc(handler.GetSendCustomApplicationMetricsHandler(kotsStore))
}

func StreamJSON(c *websocket.Conn, payload interface{}) {
Expand Down
10 changes: 0 additions & 10 deletions pkg/handlers/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1329,16 +1329,6 @@ var HandlerPolicyTests = map[string][]HandlerPolicyTest{
ExpectStatus: http.StatusOK,
},
},
"SendCustomApplicationMetrics": {
{
Roles: []rbactypes.Role{rbac.ClusterAdminRole},
SessionRoles: []string{rbac.ClusterAdminRoleID},
Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) {
handlerRecorder.SendCustomApplicationMetrics(gomock.Any(), gomock.Any())
},
ExpectStatus: http.StatusOK,
},
},
}

type HandlerPolicyTest struct {
Expand Down
3 changes: 0 additions & 3 deletions pkg/handlers/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,4 @@ type KOTSHandler interface {
// Helm
IsHelmManaged(w http.ResponseWriter, r *http.Request)
GetAppValuesFile(w http.ResponseWriter, r *http.Request)

// API available to applications (except legacy /license/v1/license)
SendCustomApplicationMetrics(w http.ResponseWriter, r *http.Request)
}
18 changes: 0 additions & 18 deletions pkg/handlers/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,21 +107,3 @@ func RequireValidSessionQuietMiddleware(kotsStore store.Store) mux.MiddlewareFun
})
}
}

func RequireValidLicenseMiddleware(kotsStore store.Store) mux.MiddlewareFunc {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
license, app, err := requireValidLicense(kotsStore, w, r)
if err != nil {
if !kotsStore.IsNotFound(err) {
logger.Error(errors.Wrapf(err, "request %q", r.RequestURI))
}
return
}

r = session.ContextSetLicense(r, license)
r = session.ContextSetApp(r, app)
next.ServeHTTP(w, r)
})
}
}
12 changes: 0 additions & 12 deletions pkg/handlers/mock/mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 0 additions & 47 deletions pkg/handlers/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,13 @@ import (
"time"

"github.com/pkg/errors"
apptypes "github.com/replicatedhq/kots/pkg/app/types"
"github.com/replicatedhq/kots/pkg/handlers/types"
"github.com/replicatedhq/kots/pkg/k8sutil"
"github.com/replicatedhq/kots/pkg/kotsutil"
"github.com/replicatedhq/kots/pkg/logger"
"github.com/replicatedhq/kots/pkg/session"
sessiontypes "github.com/replicatedhq/kots/pkg/session/types"
"github.com/replicatedhq/kots/pkg/store"
"github.com/replicatedhq/kots/pkg/util"
"github.com/replicatedhq/kotskinds/apis/kots/v1beta1"
kuberneteserrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand Down Expand Up @@ -132,50 +129,6 @@ func requireValidSession(kotsStore store.Store, w http.ResponseWriter, r *http.R
return sess, nil
}

func requireValidLicense(kotsStore store.Store, w http.ResponseWriter, r *http.Request) (*v1beta1.License, *apptypes.App, error) {
if r.Method == "OPTIONS" {
return nil, nil, nil
}

licenseID := r.Header.Get("authorization")
if licenseID == "" {
err := errors.New("missing authorization header")
response := types.ErrorResponse{Error: util.StrPointer(err.Error())}
JSON(w, http.StatusUnauthorized, response)
return nil, nil, err
}

apps, err := kotsStore.ListInstalledApps()
if err != nil {
return nil, nil, errors.Wrap(err, "get all apps")
}

var license *v1beta1.License
var app *apptypes.App

for _, a := range apps {
l, err := kotsutil.LoadLicenseFromBytes([]byte(a.License))
if err != nil {
return nil, nil, errors.Wrap(err, "load license")
}

if l.Spec.LicenseID == licenseID {
license = l
app = a
break
}
}

if license == nil {
err := errors.New("license ID is not valid")
response := types.ErrorResponse{Error: util.StrPointer(err.Error())}
JSON(w, http.StatusUnauthorized, response)
return nil, nil, err
}

return license, app, nil
}

func requireValidKOTSToken(w http.ResponseWriter, r *http.Request) error {
if r.Header.Get("Authorization") == "" {
w.WriteHeader(http.StatusUnauthorized)
Expand Down

0 comments on commit fb8f84a

Please sign in to comment.