Deprecate URLs with "team" and "query" terminology (#40520)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #40519 # Details This PR adds a new system for registering deprecated URLs separately from the main URLs (i.e. not clogging up `handler.go` with a bunch of `.WithAltPaths()` or similar. It uses a registry that's shared between all the different endpointer, which is then iterated over and a new handler is created for the deprecated endpoint which stores info about the deprecation (the old and new URLs) in the context. A new middleware looks for that context info and, if found, logs a deprecation warning (if the topic is enabled). # Checklist for submitter If some of the following don't apply, delete the relevant line. - [ ] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. no need for a changelog as we are not logging the warnings by default ## Testing - [X] Added/updated automated tests - [X] QA'd all new/changed functionality manually * Verified that going to `/teams` with `--logging_enable_topics=deprecated-field-names` got me this log: ``` deprecated_path=/api/_version_/fleet/teams deprecation_warning="API `/api/_version_/fleet/teams` is deprecated, use `/api/_version_/fleet/fleets` instead ``` * Going to `/fleets` with that flag enabled resulted in no deprecation log * Going to `/teams` _without_ the flag enabled resulted in no deprecation log
This commit is contained in:
@@ -744,6 +744,87 @@ func WriteBrowserSecurityHeaders(w http.ResponseWriter) {
|
||||
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
||||
}
|
||||
|
||||
// handlerKey identifies a registered handler by HTTP method and unversioned path template.
|
||||
type handlerKey struct {
|
||||
method string
|
||||
path string // unversioned path template, e.g. "/api/_version_/fleet/fleets"
|
||||
}
|
||||
|
||||
// HandlerRegistry stores HTTP handlers by method+path during endpoint registration,
|
||||
// enabling lookup for deprecated path alias registration.
|
||||
type HandlerRegistry struct {
|
||||
handlers map[handlerKey]http.Handler
|
||||
}
|
||||
|
||||
// NewHandlerRegistry creates an empty HandlerRegistry.
|
||||
func NewHandlerRegistry() *HandlerRegistry {
|
||||
return &HandlerRegistry{handlers: make(map[handlerKey]http.Handler)}
|
||||
}
|
||||
|
||||
// DeprecatedPathAlias maps a primary (canonical) path to one or more deprecated
|
||||
// paths that should serve the same handler.
|
||||
type DeprecatedPathAlias struct {
|
||||
Method string
|
||||
PrimaryPath string // canonical path (must already be registered)
|
||||
DeprecatedPaths []string // old paths to alias
|
||||
}
|
||||
|
||||
// deprecatedPathInfoKey is the context key for deprecated URL path info.
|
||||
type deprecatedPathInfoKey struct{}
|
||||
|
||||
// deprecatedPathInfo holds the deprecated and canonical paths for logging.
|
||||
type deprecatedPathInfo struct {
|
||||
deprecatedPath string
|
||||
primaryPath string
|
||||
}
|
||||
|
||||
// LogDeprecatedPathAlias is a kithttp.RequestFunc (ServerBefore function)
|
||||
// that checks if the request is using a deprecated URL path alias and, if so,
|
||||
// elevates the log level to Warn and adds deprecation info to the request log.
|
||||
// It must run after the LoggingContext is created (i.e. after SetRequestsContexts).
|
||||
func LogDeprecatedPathAlias(ctx context.Context, _ *http.Request) context.Context {
|
||||
if !platform_logging.TopicEnabled(platform_logging.DeprecatedFieldTopic) {
|
||||
return ctx
|
||||
}
|
||||
info, ok := ctx.Value(deprecatedPathInfoKey{}).(deprecatedPathInfo)
|
||||
if !ok {
|
||||
return ctx
|
||||
}
|
||||
logging.WithLevel(ctx, slog.LevelWarn)
|
||||
logging.WithExtras(ctx,
|
||||
"deprecated_path", info.deprecatedPath,
|
||||
"deprecation_warning", fmt.Sprintf("API `%s` is deprecated, use `%s` instead", info.deprecatedPath, info.primaryPath),
|
||||
)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// RegisterDeprecatedPathAliases registers deprecated URL path aliases that point
|
||||
// to the same handler as the canonical path, and wraps them in a handler that
|
||||
// can log deprecation warnings.
|
||||
func RegisterDeprecatedPathAliases(r *mux.Router, versions []string, registry *HandlerRegistry, aliases []DeprecatedPathAlias) {
|
||||
allVersions := append(append([]string{}, versions...), "latest")
|
||||
versionRegex := strings.Join(allVersions, "|")
|
||||
for _, alias := range aliases {
|
||||
handler := registry.handlers[handlerKey{alias.Method, alias.PrimaryPath}]
|
||||
if handler == nil {
|
||||
panic(fmt.Sprintf("deprecated alias: no handler registered for %s %s", alias.Method, alias.PrimaryPath))
|
||||
}
|
||||
for _, path := range alias.DeprecatedPaths {
|
||||
// Replace the version placeholder in the deprecated path with a regex that matches all versions,
|
||||
// so that the same handler can be used for all versions of the deprecated path.
|
||||
pathForHandler := strings.Replace(path, "/_version_/", fmt.Sprintf("/{fleetversion:(?:%s)}/", versionRegex), 1)
|
||||
info := deprecatedPathInfo{deprecatedPath: path, primaryPath: alias.PrimaryPath}
|
||||
// Wrap the handler to inject deprecation info into the context for logging.
|
||||
wrappedHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := context.WithValue(r.Context(), deprecatedPathInfoKey{}, info)
|
||||
handler.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
nameAndVerb := getNameFromPathAndVerb(alias.Method, path, "")
|
||||
r.Handle(pathForHandler, wrappedHandler).Name(nameAndVerb).Methods(alias.Method)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type CommonEndpointer[H any] struct {
|
||||
EP Endpointer[H]
|
||||
MakeDecoderFn func(iface any, requestBodyLimit int64) kithttp.DecodeRequestFunc
|
||||
@@ -760,6 +841,12 @@ type CommonEndpointer[H any] struct {
|
||||
// CustomMiddlewareAfterAuth are middlewares that run after authentication.
|
||||
CustomMiddlewareAfterAuth []endpoint.Middleware
|
||||
|
||||
// HandlerRegistry, if set, records handlers by method+path for deprecated
|
||||
// path alias lookup. The pointer is shared across shallow copies (created
|
||||
// by builder methods like WithAltPaths) so all registrations land in the
|
||||
// same map.
|
||||
HandlerRegistry *HandlerRegistry
|
||||
|
||||
startingAtVersion string
|
||||
endingAtVersion string
|
||||
alternativePaths []string
|
||||
@@ -971,10 +1058,14 @@ func (e *CommonEndpointer[H]) HandlePathHandler(path string, pathHandler func(pa
|
||||
|
||||
versionedPath := strings.Replace(path, "/_version_/", fmt.Sprintf("/{fleetversion:(?:%s)}/", strings.Join(versions, "|")), 1)
|
||||
nameAndVerb := getNameFromPathAndVerb(verb, path, e.startingAtVersion)
|
||||
handler := pathHandler(versionedPath)
|
||||
if e.usePathPrefix {
|
||||
e.Router.PathPrefix(versionedPath).Handler(pathHandler(versionedPath)).Name(nameAndVerb).Methods(verb)
|
||||
e.Router.PathPrefix(versionedPath).Handler(handler).Name(nameAndVerb).Methods(verb)
|
||||
} else {
|
||||
e.Router.Handle(versionedPath, pathHandler(versionedPath)).Name(nameAndVerb).Methods(verb)
|
||||
e.Router.Handle(versionedPath, handler).Name(nameAndVerb).Methods(verb)
|
||||
}
|
||||
if e.HandlerRegistry != nil {
|
||||
e.HandlerRegistry.handlers[handlerKey{verb, path}] = handler
|
||||
}
|
||||
for _, alias := range e.alternativePaths {
|
||||
nameAndVerb := getNameFromPathAndVerb(verb, alias, e.startingAtVersion)
|
||||
|
||||
@@ -3,11 +3,13 @@ package endpointer
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
authz_ctx "github.com/fleetdm/fleet/v4/server/contexts/authz"
|
||||
"github.com/fleetdm/fleet/v4/server/contexts/logging"
|
||||
platform_http "github.com/fleetdm/fleet/v4/server/platform/http"
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
kithttp "github.com/go-kit/kit/transport/http"
|
||||
@@ -123,3 +125,104 @@ func (n nopEP) CallHandlerFunc(f testHandlerFunc, ctx context.Context, request a
|
||||
func (n nopEP) Service() any {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestRegisterDeprecatedPathAliases(t *testing.T) {
|
||||
// Set up a router and register a primary endpoint via CommonEndpointer.
|
||||
r := mux.NewRouter()
|
||||
registry := NewHandlerRegistry()
|
||||
versions := []string{"v1", "2022-04"}
|
||||
|
||||
authMiddleware := func(next endpoint.Endpoint) endpoint.Endpoint {
|
||||
return func(ctx context.Context, req any) (any, error) {
|
||||
if authctx, ok := authz_ctx.FromContext(ctx); ok {
|
||||
authctx.SetChecked()
|
||||
}
|
||||
return next(ctx, req)
|
||||
}
|
||||
}
|
||||
|
||||
ce := &CommonEndpointer[testHandlerFunc]{
|
||||
EP: nopEP{},
|
||||
MakeDecoderFn: func(iface any, requestBodySizeLimit int64) kithttp.DecodeRequestFunc {
|
||||
return func(ctx context.Context, r *http.Request) (request any, err error) {
|
||||
return nopRequest{}, nil
|
||||
}
|
||||
},
|
||||
EncodeFn: func(ctx context.Context, w http.ResponseWriter, i any) error {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return nil
|
||||
},
|
||||
AuthMiddleware: authMiddleware,
|
||||
Router: r,
|
||||
Versions: versions,
|
||||
HandlerRegistry: registry,
|
||||
}
|
||||
|
||||
// Register the primary endpoint.
|
||||
ce.GET("/api/_version_/fleet/fleets", func(ctx context.Context, request any) (platform_http.Errorer, error) {
|
||||
return nopResponse{}, nil
|
||||
}, nil)
|
||||
|
||||
// Register a deprecated alias for it.
|
||||
RegisterDeprecatedPathAliases(r, versions, registry, []DeprecatedPathAlias{
|
||||
{
|
||||
Method: "GET",
|
||||
PrimaryPath: "/api/_version_/fleet/fleets",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/teams"},
|
||||
},
|
||||
})
|
||||
|
||||
s := httptest.NewServer(r)
|
||||
t.Cleanup(s.Close)
|
||||
|
||||
// Both the primary and deprecated paths should return 200.
|
||||
for _, path := range []string{"/api/v1/fleet/fleets", "/api/v1/fleet/teams", "/api/latest/fleet/teams"} {
|
||||
resp, err := http.Get(s.URL + path)
|
||||
require.NoError(t, err)
|
||||
resp.Body.Close()
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode, "path %s should return 200", path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogDeprecatedPathAlias(t *testing.T) {
|
||||
// Without deprecated path info in context, LogDeprecatedPathAlias is a no-op.
|
||||
lc := &logging.LoggingContext{}
|
||||
ctx := logging.NewContext(context.Background(), lc)
|
||||
ctx2 := LogDeprecatedPathAlias(ctx, nil)
|
||||
require.Equal(t, ctx, ctx2, "should return same context when no deprecated path info")
|
||||
require.Empty(t, lc.Extras)
|
||||
|
||||
// With deprecated path info, it should set warn level and extras.
|
||||
ctx = context.WithValue(ctx, deprecatedPathInfoKey{}, deprecatedPathInfo{
|
||||
deprecatedPath: "/api/_version_/fleet/teams",
|
||||
primaryPath: "/api/_version_/fleet/fleets",
|
||||
})
|
||||
LogDeprecatedPathAlias(ctx, nil)
|
||||
|
||||
// Extras is a flat []interface{} of key-value pairs.
|
||||
require.Len(t, lc.Extras, 4) // "deprecated_path", value, "deprecation_warning", value
|
||||
require.Equal(t, "deprecated_path", lc.Extras[0])
|
||||
require.Equal(t, "/api/_version_/fleet/teams", lc.Extras[1])
|
||||
require.Equal(t, "deprecation_warning", lc.Extras[2])
|
||||
require.Contains(t, lc.Extras[3], "deprecated")
|
||||
|
||||
// ForceLevel should be set to Warn.
|
||||
require.NotNil(t, lc.ForceLevel)
|
||||
require.Equal(t, slog.LevelWarn, *lc.ForceLevel)
|
||||
}
|
||||
|
||||
func TestRegisterDeprecatedPathAliasesPanicsOnMissing(t *testing.T) {
|
||||
r := mux.NewRouter()
|
||||
registry := NewHandlerRegistry()
|
||||
versions := []string{"v1"}
|
||||
|
||||
require.Panics(t, func() {
|
||||
RegisterDeprecatedPathAliases(r, versions, registry, []DeprecatedPathAlias{
|
||||
{
|
||||
Method: "GET",
|
||||
PrimaryPath: "/api/_version_/fleet/nonexistent",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/old"},
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
+47
-52
@@ -125,6 +125,7 @@ func MakeHandler(
|
||||
kithttp.ServerBefore(
|
||||
kithttp.PopulateRequestContext, // populate the request context with common fields
|
||||
auth.SetRequestsContexts(svc),
|
||||
endpointer.LogDeprecatedPathAlias, // log deprecation warning for deprecated URL path aliases
|
||||
setCarveStoreInRequestContext(carveStore),
|
||||
),
|
||||
kithttp.ServerErrorHandler(&endpointer.ErrorHandler{Logger: logger.SlogLogger()}),
|
||||
@@ -282,9 +283,11 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
|
||||
extra extraHandlerOpts,
|
||||
) {
|
||||
apiVersions := []string{"v1", "2022-04"}
|
||||
registry := endpointer.NewHandlerRegistry()
|
||||
|
||||
// user-authenticated endpoints
|
||||
ue := newUserAuthenticatedEndpointer(svc, opts, r, apiVersions...)
|
||||
ue.HandlerRegistry = registry
|
||||
|
||||
ue.POST("/api/_version_/fleet/trigger", triggerEndpoint, triggerRequest{})
|
||||
|
||||
@@ -301,18 +304,18 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
|
||||
|
||||
ue.POST("/api/_version_/fleet/users/roles/spec", applyUserRoleSpecsEndpoint, applyUserRoleSpecsRequest{})
|
||||
ue.POST("/api/_version_/fleet/translate", translatorEndpoint, translatorRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/spec/teams").WithRequestBodySizeLimit(5*units.MiB).POST("/api/_version_/fleet/spec/fleets", applyTeamSpecsEndpoint, applyTeamSpecsRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/teams/{fleet_id:[0-9]+}/secrets").PATCH("/api/_version_/fleet/fleets/{fleet_id:[0-9]+}/secrets", modifyTeamEnrollSecretsEndpoint, modifyTeamEnrollSecretsRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/teams").POST("/api/_version_/fleet/fleets", createTeamEndpoint, createTeamRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/teams").GET("/api/_version_/fleet/fleets", listTeamsEndpoint, listTeamsRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/teams/{id:[0-9]+}").GET("/api/_version_/fleet/fleets/{id:[0-9]+}", getTeamEndpoint, getTeamRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/teams/{id:[0-9]+}").PATCH("/api/_version_/fleet/fleets/{id:[0-9]+}", modifyTeamEndpoint, modifyTeamRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/teams/{id:[0-9]+}").DELETE("/api/_version_/fleet/fleets/{id:[0-9]+}", deleteTeamEndpoint, deleteTeamRequest{})
|
||||
ue.WithRequestBodySizeLimit(2*units.MiB).WithAltPaths("/api/_version_/fleet/teams/{id:[0-9]+}/agent_options").POST("/api/_version_/fleet/fleets/{id:[0-9]+}/agent_options", modifyTeamAgentOptionsEndpoint, modifyTeamAgentOptionsRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/teams/{id:[0-9]+}/users").GET("/api/_version_/fleet/fleets/{id:[0-9]+}/users", listTeamUsersEndpoint, listTeamUsersRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/teams/{id:[0-9]+}/users").PATCH("/api/_version_/fleet/fleets/{id:[0-9]+}/users", addTeamUsersEndpoint, modifyTeamUsersRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/teams/{id:[0-9]+}/users").DELETE("/api/_version_/fleet/fleets/{id:[0-9]+}/users", deleteTeamUsersEndpoint, modifyTeamUsersRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/teams/{id:[0-9]+}/secrets").GET("/api/_version_/fleet/fleets/{id:[0-9]+}/secrets", teamEnrollSecretsEndpoint, teamEnrollSecretsRequest{})
|
||||
ue.WithRequestBodySizeLimit(5*units.MiB).POST("/api/_version_/fleet/spec/fleets", applyTeamSpecsEndpoint, applyTeamSpecsRequest{})
|
||||
ue.PATCH("/api/_version_/fleet/fleets/{fleet_id:[0-9]+}/secrets", modifyTeamEnrollSecretsEndpoint, modifyTeamEnrollSecretsRequest{})
|
||||
ue.POST("/api/_version_/fleet/fleets", createTeamEndpoint, createTeamRequest{})
|
||||
ue.GET("/api/_version_/fleet/fleets", listTeamsEndpoint, listTeamsRequest{})
|
||||
ue.GET("/api/_version_/fleet/fleets/{id:[0-9]+}", getTeamEndpoint, getTeamRequest{})
|
||||
ue.PATCH("/api/_version_/fleet/fleets/{id:[0-9]+}", modifyTeamEndpoint, modifyTeamRequest{})
|
||||
ue.DELETE("/api/_version_/fleet/fleets/{id:[0-9]+}", deleteTeamEndpoint, deleteTeamRequest{})
|
||||
ue.WithRequestBodySizeLimit(2*units.MiB).POST("/api/_version_/fleet/fleets/{id:[0-9]+}/agent_options", modifyTeamAgentOptionsEndpoint, modifyTeamAgentOptionsRequest{})
|
||||
ue.GET("/api/_version_/fleet/fleets/{id:[0-9]+}/users", listTeamUsersEndpoint, listTeamUsersRequest{})
|
||||
ue.PATCH("/api/_version_/fleet/fleets/{id:[0-9]+}/users", addTeamUsersEndpoint, modifyTeamUsersRequest{})
|
||||
ue.DELETE("/api/_version_/fleet/fleets/{id:[0-9]+}/users", deleteTeamUsersEndpoint, modifyTeamUsersRequest{})
|
||||
ue.GET("/api/_version_/fleet/fleets/{id:[0-9]+}/secrets", teamEnrollSecretsEndpoint, teamEnrollSecretsRequest{})
|
||||
|
||||
ue.GET("/api/_version_/fleet/users", listUsersEndpoint, listUsersRequest{})
|
||||
ue.POST("/api/_version_/fleet/users/admin", createUserEndpoint, createUserRequest{})
|
||||
@@ -347,18 +350,12 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
|
||||
ue.StartingAtVersion("2022-04").PATCH("/api/_version_/fleet/policies/{policy_id}", modifyGlobalPolicyEndpoint, modifyGlobalPolicyRequest{})
|
||||
ue.POST("/api/_version_/fleet/automations/reset", resetAutomationEndpoint, resetAutomationRequest{})
|
||||
|
||||
// Alias /api/_version_/fleet/team/ -> /api/_version_/fleet/teams/
|
||||
ue.WithAltPaths("/api/_version_/fleet/team/{fleet_id}/policies", "/api/_version_/fleet/teams/{fleet_id}/policies").
|
||||
POST("/api/_version_/fleet/fleets/{fleet_id}/policies", teamPolicyEndpoint, teamPolicyRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/team/{fleet_id}/policies", "/api/_version_/fleet/teams/{fleet_id}/policies").
|
||||
GET("/api/_version_/fleet/fleets/{fleet_id}/policies", listTeamPoliciesEndpoint, listTeamPoliciesRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/team/{fleet_id}/policies/count", "/api/_version_/fleet/teams/{fleet_id}/policies/count").
|
||||
GET("/api/_version_/fleet/fleets/{fleet_id}/policies/count", countTeamPoliciesEndpoint, countTeamPoliciesRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/team/{fleet_id}/policies/{policy_id}", "/api/_version_/fleet/teams/{fleet_id}/policies/{policy_id}").
|
||||
GET("/api/_version_/fleet/fleets/{fleet_id}/policies/{policy_id}", getTeamPolicyByIDEndpoint, getTeamPolicyByIDRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/team/{fleet_id}/policies/delete", "/api/_version_/fleet/teams/{fleet_id}/policies/delete").
|
||||
POST("/api/_version_/fleet/fleets/{fleet_id}/policies/delete", deleteTeamPoliciesEndpoint, deleteTeamPoliciesRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/teams/{fleet_id}/policies/{policy_id}").PATCH("/api/_version_/fleet/fleets/{fleet_id}/policies/{policy_id}", modifyTeamPolicyEndpoint, modifyTeamPolicyRequest{})
|
||||
ue.POST("/api/_version_/fleet/fleets/{fleet_id}/policies", teamPolicyEndpoint, teamPolicyRequest{})
|
||||
ue.GET("/api/_version_/fleet/fleets/{fleet_id}/policies", listTeamPoliciesEndpoint, listTeamPoliciesRequest{})
|
||||
ue.GET("/api/_version_/fleet/fleets/{fleet_id}/policies/count", countTeamPoliciesEndpoint, countTeamPoliciesRequest{})
|
||||
ue.GET("/api/_version_/fleet/fleets/{fleet_id}/policies/{policy_id}", getTeamPolicyByIDEndpoint, getTeamPolicyByIDRequest{})
|
||||
ue.POST("/api/_version_/fleet/fleets/{fleet_id}/policies/delete", deleteTeamPoliciesEndpoint, deleteTeamPoliciesRequest{})
|
||||
ue.PATCH("/api/_version_/fleet/fleets/{fleet_id}/policies/{policy_id}", modifyTeamPolicyEndpoint, modifyTeamPolicyRequest{})
|
||||
ue.WithRequestBodySizeLimit(fleet.MaxSpecSize).POST("/api/_version_/fleet/spec/policies", applyPolicySpecsEndpoint, applyPolicySpecsRequest{})
|
||||
|
||||
ue.POST("/api/_version_/fleet/certificates", createCertificateTemplateEndpoint, createCertificateTemplateRequest{})
|
||||
@@ -368,17 +365,17 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
|
||||
ue.POST("/api/_version_/fleet/spec/certificates", applyCertificateTemplateSpecsEndpoint, applyCertificateTemplateSpecsRequest{})
|
||||
ue.DELETE("/api/_version_/fleet/spec/certificates", deleteCertificateTemplateSpecsEndpoint, deleteCertificateTemplateSpecsRequest{})
|
||||
|
||||
ue.WithAltPaths("/api/_version_/fleet/queries/{id:[0-9]+}").GET("/api/_version_/fleet/reports/{id:[0-9]+}", getQueryEndpoint, getQueryRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/queries").GET("/api/_version_/fleet/reports", listQueriesEndpoint, listQueriesRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/queries/{id:[0-9]+}/report").GET("/api/_version_/fleet/reports/{id:[0-9]+}/report", getQueryReportEndpoint, getQueryReportRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/queries").POST("/api/_version_/fleet/reports", createQueryEndpoint, createQueryRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/queries/{id:[0-9]+}").PATCH("/api/_version_/fleet/reports/{id:[0-9]+}", modifyQueryEndpoint, modifyQueryRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/queries/{name}").DELETE("/api/_version_/fleet/reports/{name}", deleteQueryEndpoint, deleteQueryRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/queries/id/{id:[0-9]+}").DELETE("/api/_version_/fleet/reports/id/{id:[0-9]+}", deleteQueryByIDEndpoint, deleteQueryByIDRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/queries/delete").POST("/api/_version_/fleet/reports/delete", deleteQueriesEndpoint, deleteQueriesRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/spec/queries").WithRequestBodySizeLimit(fleet.MaxSpecSize).POST("/api/_version_/fleet/spec/reports", applyQuerySpecsEndpoint, applyQuerySpecsRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/spec/queries").GET("/api/_version_/fleet/spec/reports", getQuerySpecsEndpoint, getQuerySpecsRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/spec/queries/{name}").GET("/api/_version_/fleet/spec/reports/{name}", getQuerySpecEndpoint, getQuerySpecRequest{})
|
||||
ue.GET("/api/_version_/fleet/reports/{id:[0-9]+}", getQueryEndpoint, getQueryRequest{})
|
||||
ue.GET("/api/_version_/fleet/reports", listQueriesEndpoint, listQueriesRequest{})
|
||||
ue.GET("/api/_version_/fleet/reports/{id:[0-9]+}/report", getQueryReportEndpoint, getQueryReportRequest{})
|
||||
ue.POST("/api/_version_/fleet/reports", createQueryEndpoint, createQueryRequest{})
|
||||
ue.PATCH("/api/_version_/fleet/reports/{id:[0-9]+}", modifyQueryEndpoint, modifyQueryRequest{})
|
||||
ue.DELETE("/api/_version_/fleet/reports/{name}", deleteQueryEndpoint, deleteQueryRequest{})
|
||||
ue.DELETE("/api/_version_/fleet/reports/id/{id:[0-9]+}", deleteQueryByIDEndpoint, deleteQueryByIDRequest{})
|
||||
ue.POST("/api/_version_/fleet/reports/delete", deleteQueriesEndpoint, deleteQueriesRequest{})
|
||||
ue.WithRequestBodySizeLimit(fleet.MaxSpecSize).POST("/api/_version_/fleet/spec/reports", applyQuerySpecsEndpoint, applyQuerySpecsRequest{})
|
||||
ue.GET("/api/_version_/fleet/spec/reports", getQuerySpecsEndpoint, getQuerySpecsRequest{})
|
||||
ue.GET("/api/_version_/fleet/spec/reports/{name}", getQuerySpecEndpoint, getQuerySpecRequest{})
|
||||
|
||||
ue.GET("/api/_version_/fleet/packs/{id:[0-9]+}", getPackEndpoint, getPackRequest{})
|
||||
ue.POST("/api/_version_/fleet/packs", createPackEndpoint, createPackRequest{})
|
||||
@@ -475,7 +472,7 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
|
||||
ue.GET("/api/_version_/fleet/hosts/report", hostsReportEndpoint, hostsReportRequest{})
|
||||
ue.GET("/api/_version_/fleet/os_versions", osVersionsEndpoint, osVersionsRequest{})
|
||||
ue.GET("/api/_version_/fleet/os_versions/{id:[0-9]+}", getOSVersionEndpoint, getOSVersionRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/hosts/{id:[0-9]+}/queries/{report_id:[0-9]+}").GET("/api/_version_/fleet/hosts/{id:[0-9]+}/reports/{report_id:[0-9]+}", getHostQueryReportEndpoint, getHostQueryReportRequest{})
|
||||
ue.GET("/api/_version_/fleet/hosts/{id:[0-9]+}/reports/{report_id:[0-9]+}", getHostQueryReportEndpoint, getHostQueryReportRequest{})
|
||||
ue.GET("/api/_version_/fleet/hosts/{id:[0-9]+}/health", getHostHealthEndpoint, getHostHealthRequest{})
|
||||
ue.POST("/api/_version_/fleet/hosts/{id:[0-9]+}/labels", addLabelsToHostEndpoint, addLabelsToHostRequest{})
|
||||
ue.DELETE("/api/_version_/fleet/hosts/{id:[0-9]+}/labels", removeLabelsFromHostEndpoint, removeLabelsFromHostRequest{})
|
||||
@@ -498,16 +495,16 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
|
||||
ue.GET("/api/_version_/fleet/spec/labels/{name}", getLabelSpecEndpoint, getGenericSpecRequest{})
|
||||
|
||||
// This endpoint runs live queries synchronously (with a configured timeout).
|
||||
ue.WithAltPaths("/api/_version_/fleet/queries/{id:[0-9]+}/run").POST("/api/_version_/fleet/reports/{id:[0-9]+}/run", runOneLiveQueryEndpoint, runOneLiveQueryRequest{})
|
||||
ue.POST("/api/_version_/fleet/reports/{id:[0-9]+}/run", runOneLiveQueryEndpoint, runOneLiveQueryRequest{})
|
||||
// Old endpoint, removed from docs. This GET endpoint runs live queries synchronously (with a configured timeout).
|
||||
ue.WithAltPaths("/api/_version_/fleet/queries/run").GET("/api/_version_/fleet/reports/run", runLiveQueryEndpoint, runLiveQueryRequest{})
|
||||
ue.GET("/api/_version_/fleet/reports/run", runLiveQueryEndpoint, runLiveQueryRequest{})
|
||||
// The following two POST APIs are the asynchronous way to run live queries.
|
||||
// The live queries are created with these two endpoints and their results can be queried via
|
||||
// websockets via the `GET /api/_version_/fleet/results/` endpoint.
|
||||
ue.WithAltPaths("/api/_version_/fleet/queries/run").POST("/api/_version_/fleet/reports/run", createDistributedQueryCampaignEndpoint, createDistributedQueryCampaignRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/queries/run_by_identifiers").POST("/api/_version_/fleet/reports/run_by_identifiers", createDistributedQueryCampaignByIdentifierEndpoint, createDistributedQueryCampaignByIdentifierRequest{})
|
||||
ue.POST("/api/_version_/fleet/reports/run", createDistributedQueryCampaignEndpoint, createDistributedQueryCampaignRequest{})
|
||||
ue.POST("/api/_version_/fleet/reports/run_by_identifiers", createDistributedQueryCampaignByIdentifierEndpoint, createDistributedQueryCampaignByIdentifierRequest{})
|
||||
// This endpoint is deprecated and maintained for backwards compatibility. This and above endpoint are functionally equivalent
|
||||
ue.WithAltPaths("/api/_version_/fleet/queries/run_by_names").POST("/api/_version_/fleet/reports/run_by_names", createDistributedQueryCampaignByIdentifierEndpoint, createDistributedQueryCampaignByIdentifierRequest{})
|
||||
ue.POST("/api/_version_/fleet/reports/run_by_names", createDistributedQueryCampaignByIdentifierEndpoint, createDistributedQueryCampaignByIdentifierRequest{})
|
||||
|
||||
ue.GET("/api/_version_/fleet/packs/{id:[0-9]+}/scheduled", getScheduledQueriesInPackEndpoint, getScheduledQueriesInPackRequest{})
|
||||
ue.EndingAtVersion("v1").POST("/api/_version_/fleet/schedule", scheduleQueryEndpoint, scheduleQueryRequest{})
|
||||
@@ -527,15 +524,10 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
|
||||
ue.EndingAtVersion("v1").DELETE("/api/_version_/fleet/global/schedule/{id:[0-9]+}", deleteGlobalScheduleEndpoint, deleteGlobalScheduleRequest{})
|
||||
ue.StartingAtVersion("2022-04").DELETE("/api/_version_/fleet/schedule/{id:[0-9]+}", deleteGlobalScheduleEndpoint, deleteGlobalScheduleRequest{})
|
||||
|
||||
// Alias /api/_version_/fleet/team/ -> /api/_version_/fleet/teams/
|
||||
ue.WithAltPaths("/api/_version_/fleet/team/{fleet_id}/schedule", "/api/_version_/fleet/teams/{fleet_id}/schedule").
|
||||
GET("/api/_version_/fleet/fleets/{fleet_id}/schedule", getTeamScheduleEndpoint, getTeamScheduleRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/team/{fleet_id}/schedule", "/api/_version_/fleet/teams/{fleet_id}/schedule").
|
||||
POST("/api/_version_/fleet/fleets/{fleet_id}/schedule", teamScheduleQueryEndpoint, teamScheduleQueryRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/team/{fleet_id}/schedule/{report_id}", "/api/_version_/fleet/teams/{fleet_id}/schedule/{report_id}").
|
||||
PATCH("/api/_version_/fleet/fleets/{fleet_id}/schedule/{report_id}", modifyTeamScheduleEndpoint, modifyTeamScheduleRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/team/{fleet_id}/schedule/{report_id}", "/api/_version_/fleet/teams/{fleet_id}/schedule/{report_id}").
|
||||
DELETE("/api/_version_/fleet/fleets/{fleet_id}/schedule/{report_id}", deleteTeamScheduleEndpoint, deleteTeamScheduleRequest{})
|
||||
ue.GET("/api/_version_/fleet/fleets/{fleet_id}/schedule", getTeamScheduleEndpoint, getTeamScheduleRequest{})
|
||||
ue.POST("/api/_version_/fleet/fleets/{fleet_id}/schedule", teamScheduleQueryEndpoint, teamScheduleQueryRequest{})
|
||||
ue.PATCH("/api/_version_/fleet/fleets/{fleet_id}/schedule/{report_id}", modifyTeamScheduleEndpoint, modifyTeamScheduleRequest{})
|
||||
ue.DELETE("/api/_version_/fleet/fleets/{fleet_id}/schedule/{report_id}", deleteTeamScheduleEndpoint, deleteTeamScheduleRequest{})
|
||||
|
||||
ue.GET("/api/_version_/fleet/carves", listCarvesEndpoint, listCarvesRequest{})
|
||||
ue.GET("/api/_version_/fleet/carves/{id:[0-9]+}", getCarveEndpoint, getCarveRequest{})
|
||||
@@ -829,7 +821,7 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
|
||||
ue.DELETE("/api/_version_/fleet/abm_tokens/{id:[0-9]+}", deleteABMTokenEndpoint, deleteABMTokenRequest{})
|
||||
ue.GET("/api/_version_/fleet/abm_tokens", listABMTokensEndpoint, nil)
|
||||
ue.GET("/api/_version_/fleet/abm_tokens/count", countABMTokensEndpoint, nil)
|
||||
ue.WithAltPaths("/api/_version_/fleet/abm_tokens/{id:[0-9]+}/teams").PATCH("/api/_version_/fleet/abm_tokens/{id:[0-9]+}/fleets", updateABMTokenTeamsEndpoint, updateABMTokenTeamsRequest{})
|
||||
ue.PATCH("/api/_version_/fleet/abm_tokens/{id:[0-9]+}/fleets", updateABMTokenTeamsEndpoint, updateABMTokenTeamsRequest{})
|
||||
ue.PATCH("/api/_version_/fleet/abm_tokens/{id:[0-9]+}/renew", renewABMTokenEndpoint, renewABMTokenRequest{})
|
||||
|
||||
ue.GET("/api/_version_/fleet/mdm/apple/request_csr", getMDMAppleCSREndpoint, getMDMAppleCSRRequest{})
|
||||
@@ -839,7 +831,7 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
|
||||
// VPP Tokens
|
||||
ue.GET("/api/_version_/fleet/vpp_tokens", getVPPTokens, getVPPTokensRequest{})
|
||||
ue.POST("/api/_version_/fleet/vpp_tokens", uploadVPPTokenEndpoint, uploadVPPTokenRequest{})
|
||||
ue.WithAltPaths("/api/_version_/fleet/vpp_tokens/{id}/teams").PATCH("/api/_version_/fleet/vpp_tokens/{id}/fleets", patchVPPTokensTeams, patchVPPTokensTeamsRequest{})
|
||||
ue.PATCH("/api/_version_/fleet/vpp_tokens/{id}/fleets", patchVPPTokensTeams, patchVPPTokensTeamsRequest{})
|
||||
ue.PATCH("/api/_version_/fleet/vpp_tokens/{id}/renew", patchVPPTokenRenewEndpoint, patchVPPTokenRenewRequest{})
|
||||
ue.DELETE("/api/_version_/fleet/vpp_tokens/{id}", deleteVPPToken, deleteVPPTokenRequest{})
|
||||
|
||||
@@ -1103,6 +1095,9 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC
|
||||
POST("/api/_version_/fleet/mdm/sso", initiateMDMSSOEndpoint, initiateMDMSSORequest{})
|
||||
ne.WithCustomMiddleware(mdmSsoLimiter).
|
||||
POST("/api/_version_/fleet/mdm/sso/callback", callbackMDMSSOEndpoint, callbackMDMSSORequest{})
|
||||
|
||||
// Register all deprecated URL path aliases from the declarative table.
|
||||
endpointer.RegisterDeprecatedPathAliases(r, apiVersions, registry, deprecatedPathAliases)
|
||||
}
|
||||
|
||||
// WithSetup is an http middleware that checks if setup procedures have been completed.
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
eu "github.com/fleetdm/fleet/v4/server/platform/endpointer"
|
||||
)
|
||||
|
||||
// deprecatedPathAliases defines deprecated URL path aliases that map old
|
||||
// (deprecated) paths to their canonical (primary) paths. Each entry causes
|
||||
// the deprecated path(s) to serve the same handler as the primary path.
|
||||
//
|
||||
// These are organized by category:
|
||||
// - teams → fleets: team CRUD, secrets, agent_options, users, spec
|
||||
// - team/teams → fleets: policies, schedule (both singular and plural deprecated)
|
||||
// - queries → reports: query CRUD, spec, report data
|
||||
// - host queries → reports
|
||||
// - live queries → reports: run, run_by_identifiers, run_by_names
|
||||
// - ABM/VPP token teams → fleets
|
||||
var deprecatedPathAliases = []eu.DeprecatedPathAlias{
|
||||
// ---- teams → fleets ----
|
||||
{
|
||||
Method: "POST", PrimaryPath: "/api/_version_/fleet/spec/fleets",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/spec/teams"},
|
||||
},
|
||||
{
|
||||
Method: "PATCH", PrimaryPath: "/api/_version_/fleet/fleets/{fleet_id:[0-9]+}/secrets",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/teams/{fleet_id:[0-9]+}/secrets"},
|
||||
},
|
||||
{
|
||||
Method: "POST", PrimaryPath: "/api/_version_/fleet/fleets",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/teams"},
|
||||
},
|
||||
{
|
||||
Method: "GET", PrimaryPath: "/api/_version_/fleet/fleets",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/teams"},
|
||||
},
|
||||
{
|
||||
Method: "GET", PrimaryPath: "/api/_version_/fleet/fleets/{id:[0-9]+}",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/teams/{id:[0-9]+}"},
|
||||
},
|
||||
{
|
||||
Method: "PATCH", PrimaryPath: "/api/_version_/fleet/fleets/{id:[0-9]+}",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/teams/{id:[0-9]+}"},
|
||||
},
|
||||
{
|
||||
Method: "DELETE", PrimaryPath: "/api/_version_/fleet/fleets/{id:[0-9]+}",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/teams/{id:[0-9]+}"},
|
||||
},
|
||||
{
|
||||
Method: "POST", PrimaryPath: "/api/_version_/fleet/fleets/{id:[0-9]+}/agent_options",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/teams/{id:[0-9]+}/agent_options"},
|
||||
},
|
||||
{
|
||||
Method: "GET", PrimaryPath: "/api/_version_/fleet/fleets/{id:[0-9]+}/users",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/teams/{id:[0-9]+}/users"},
|
||||
},
|
||||
{
|
||||
Method: "PATCH", PrimaryPath: "/api/_version_/fleet/fleets/{id:[0-9]+}/users",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/teams/{id:[0-9]+}/users"},
|
||||
},
|
||||
{
|
||||
Method: "DELETE", PrimaryPath: "/api/_version_/fleet/fleets/{id:[0-9]+}/users",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/teams/{id:[0-9]+}/users"},
|
||||
},
|
||||
{
|
||||
Method: "GET", PrimaryPath: "/api/_version_/fleet/fleets/{id:[0-9]+}/secrets",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/teams/{id:[0-9]+}/secrets"},
|
||||
},
|
||||
|
||||
// ---- team/teams → fleets (policies) ----
|
||||
{
|
||||
Method: "POST", PrimaryPath: "/api/_version_/fleet/fleets/{fleet_id}/policies",
|
||||
DeprecatedPaths: []string{
|
||||
"/api/_version_/fleet/team/{fleet_id}/policies",
|
||||
"/api/_version_/fleet/teams/{fleet_id}/policies",
|
||||
},
|
||||
},
|
||||
{
|
||||
Method: "GET", PrimaryPath: "/api/_version_/fleet/fleets/{fleet_id}/policies",
|
||||
DeprecatedPaths: []string{
|
||||
"/api/_version_/fleet/team/{fleet_id}/policies",
|
||||
"/api/_version_/fleet/teams/{fleet_id}/policies",
|
||||
},
|
||||
},
|
||||
{
|
||||
Method: "GET", PrimaryPath: "/api/_version_/fleet/fleets/{fleet_id}/policies/count",
|
||||
DeprecatedPaths: []string{
|
||||
"/api/_version_/fleet/team/{fleet_id}/policies/count",
|
||||
"/api/_version_/fleet/teams/{fleet_id}/policies/count",
|
||||
},
|
||||
},
|
||||
{
|
||||
Method: "GET", PrimaryPath: "/api/_version_/fleet/fleets/{fleet_id}/policies/{policy_id}",
|
||||
DeprecatedPaths: []string{
|
||||
"/api/_version_/fleet/team/{fleet_id}/policies/{policy_id}",
|
||||
"/api/_version_/fleet/teams/{fleet_id}/policies/{policy_id}",
|
||||
},
|
||||
},
|
||||
{
|
||||
Method: "POST", PrimaryPath: "/api/_version_/fleet/fleets/{fleet_id}/policies/delete",
|
||||
DeprecatedPaths: []string{
|
||||
"/api/_version_/fleet/team/{fleet_id}/policies/delete",
|
||||
"/api/_version_/fleet/teams/{fleet_id}/policies/delete",
|
||||
},
|
||||
},
|
||||
{
|
||||
Method: "PATCH", PrimaryPath: "/api/_version_/fleet/fleets/{fleet_id}/policies/{policy_id}",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/teams/{fleet_id}/policies/{policy_id}"},
|
||||
},
|
||||
|
||||
// ---- queries → reports ----
|
||||
{
|
||||
Method: "GET", PrimaryPath: "/api/_version_/fleet/reports/{id:[0-9]+}",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/queries/{id:[0-9]+}"},
|
||||
},
|
||||
{
|
||||
Method: "GET", PrimaryPath: "/api/_version_/fleet/reports",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/queries"},
|
||||
},
|
||||
{
|
||||
Method: "GET", PrimaryPath: "/api/_version_/fleet/reports/{id:[0-9]+}/report",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/queries/{id:[0-9]+}/report"},
|
||||
},
|
||||
{
|
||||
Method: "POST", PrimaryPath: "/api/_version_/fleet/reports",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/queries"},
|
||||
},
|
||||
{
|
||||
Method: "PATCH", PrimaryPath: "/api/_version_/fleet/reports/{id:[0-9]+}",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/queries/{id:[0-9]+}"},
|
||||
},
|
||||
{
|
||||
Method: "DELETE", PrimaryPath: "/api/_version_/fleet/reports/{name}",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/queries/{name}"},
|
||||
},
|
||||
{
|
||||
Method: "DELETE", PrimaryPath: "/api/_version_/fleet/reports/id/{id:[0-9]+}",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/queries/id/{id:[0-9]+}"},
|
||||
},
|
||||
{
|
||||
Method: "POST", PrimaryPath: "/api/_version_/fleet/reports/delete",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/queries/delete"},
|
||||
},
|
||||
{
|
||||
Method: "POST", PrimaryPath: "/api/_version_/fleet/spec/reports",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/spec/queries"},
|
||||
},
|
||||
{
|
||||
Method: "GET", PrimaryPath: "/api/_version_/fleet/spec/reports",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/spec/queries"},
|
||||
},
|
||||
{
|
||||
Method: "GET", PrimaryPath: "/api/_version_/fleet/spec/reports/{name}",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/spec/queries/{name}"},
|
||||
},
|
||||
|
||||
// ---- host queries → reports ----
|
||||
{
|
||||
Method: "GET", PrimaryPath: "/api/_version_/fleet/hosts/{id:[0-9]+}/reports/{report_id:[0-9]+}",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/hosts/{id:[0-9]+}/queries/{report_id:[0-9]+}"},
|
||||
},
|
||||
|
||||
// ---- live queries → reports ----
|
||||
{
|
||||
Method: "POST", PrimaryPath: "/api/_version_/fleet/reports/{id:[0-9]+}/run",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/queries/{id:[0-9]+}/run"},
|
||||
},
|
||||
{
|
||||
Method: "GET", PrimaryPath: "/api/_version_/fleet/reports/run",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/queries/run"},
|
||||
},
|
||||
{
|
||||
Method: "POST", PrimaryPath: "/api/_version_/fleet/reports/run",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/queries/run"},
|
||||
},
|
||||
{
|
||||
Method: "POST", PrimaryPath: "/api/_version_/fleet/reports/run_by_identifiers",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/queries/run_by_identifiers"},
|
||||
},
|
||||
{
|
||||
Method: "POST", PrimaryPath: "/api/_version_/fleet/reports/run_by_names",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/queries/run_by_names"},
|
||||
},
|
||||
|
||||
// ---- team/teams → fleets (schedule) ----
|
||||
{
|
||||
Method: "GET", PrimaryPath: "/api/_version_/fleet/fleets/{fleet_id}/schedule",
|
||||
DeprecatedPaths: []string{
|
||||
"/api/_version_/fleet/team/{fleet_id}/schedule",
|
||||
"/api/_version_/fleet/teams/{fleet_id}/schedule",
|
||||
},
|
||||
},
|
||||
{
|
||||
Method: "POST", PrimaryPath: "/api/_version_/fleet/fleets/{fleet_id}/schedule",
|
||||
DeprecatedPaths: []string{
|
||||
"/api/_version_/fleet/team/{fleet_id}/schedule",
|
||||
"/api/_version_/fleet/teams/{fleet_id}/schedule",
|
||||
},
|
||||
},
|
||||
{
|
||||
Method: "PATCH", PrimaryPath: "/api/_version_/fleet/fleets/{fleet_id}/schedule/{report_id}",
|
||||
DeprecatedPaths: []string{
|
||||
"/api/_version_/fleet/team/{fleet_id}/schedule/{report_id}",
|
||||
"/api/_version_/fleet/teams/{fleet_id}/schedule/{report_id}",
|
||||
},
|
||||
},
|
||||
{
|
||||
Method: "DELETE", PrimaryPath: "/api/_version_/fleet/fleets/{fleet_id}/schedule/{report_id}",
|
||||
DeprecatedPaths: []string{
|
||||
"/api/_version_/fleet/team/{fleet_id}/schedule/{report_id}",
|
||||
"/api/_version_/fleet/teams/{fleet_id}/schedule/{report_id}",
|
||||
},
|
||||
},
|
||||
|
||||
// ---- ABM/VPP token teams → fleets ----
|
||||
{
|
||||
Method: "PATCH", PrimaryPath: "/api/_version_/fleet/abm_tokens/{id:[0-9]+}/fleets",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/abm_tokens/{id:[0-9]+}/teams"},
|
||||
},
|
||||
{
|
||||
Method: "PATCH", PrimaryPath: "/api/_version_/fleet/vpp_tokens/{id}/fleets",
|
||||
DeprecatedPaths: []string{"/api/_version_/fleet/vpp_tokens/{id}/teams"},
|
||||
},
|
||||
}
|
||||
@@ -95,12 +95,14 @@ func (s *integrationLoggerTestSuite) TestLogger() {
|
||||
assert.Equal(t, "/api/latest/fleet/config", attrs["uri"])
|
||||
assert.Equal(t, "admin1@example.com", attrs["user"])
|
||||
case 2:
|
||||
assert.Equal(t, slog.LevelDebug, rec.Level)
|
||||
assert.Equal(t, slog.LevelWarn, rec.Level) // Warn because /queries is a deprecated path
|
||||
assert.Equal(t, "POST", attrs["method"])
|
||||
assert.Equal(t, "/api/latest/fleet/queries", attrs["uri"])
|
||||
assert.Equal(t, "admin1@example.com", attrs["user"])
|
||||
assert.Equal(t, "somequery", attrs["name"])
|
||||
assert.Equal(t, "select 1 from osquery;", attrs["sql"])
|
||||
assert.Equal(t, "/api/_version_/fleet/queries", attrs["deprecated_path"])
|
||||
assert.Contains(t, attrs["deprecation_warning"], "deprecated")
|
||||
default:
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user