mirror of
https://github.com/micromdm/micromdm/
synced 2026-05-13 01:46:05 +08:00
Add endpoint for inspecting the MDM command queue (#895)
This commit is contained in:
@@ -2,10 +2,12 @@
|
||||
|
||||
- Add `-log-time` flag to include timestamps in log messages (#890)
|
||||
- Add `-device-signature-skew` flag to allow configuring clock skew when verifying device signatures (#887)
|
||||
- Tidy code for Go 1.20, and update Go version for Docker and CI
|
||||
- Tidy code for Go 1.20 and update Go version for Docker and CI (#902)
|
||||
- Add support for inspecting the MDM command queue (#895)
|
||||
- See the [docs](https://github.com/micromdm/micromdm/blob/main/docs/user-guide/api-and-webhooks.md#inspecting-the-command-queue) for how to use
|
||||
- Project dependency updates (#888, #889, #900)
|
||||
|
||||
Thanks to our contributors: @jamesez, @korylprince
|
||||
Thanks to our contributors: @grahamgilbert, @jamesez, @korylprince
|
||||
|
||||
## [v1.11.0](https://github.com/micromdm/micromdm/compare/v1.10.1...v1.11.0)
|
||||
|
||||
|
||||
@@ -294,3 +294,27 @@ Authorization: Basic bWljcm9tZG06c3VwZXJzZWNyZXQ=
|
||||
A helper script is also available at `./tools/api/clear_queue`:
|
||||
|
||||
`$ ./clear_queue 55693EB3-DF03-5FD1-9263-F7CDB8AD7FFD`
|
||||
|
||||
# Inspecting the Command Queue
|
||||
|
||||
[PR #895](https://github.com/micromdm/micromdm/pull/895) added support for inspecting the command queue, which can be useful when diagnosing issues with commands.
|
||||
|
||||
Assuming the example command from [Schedule Raw Commands with the API](#schedule-raw-commands-with-the-api) is in the command queue, inspecting the queue looks like:
|
||||
|
||||
```
|
||||
GET /v1/commands/55693EB3-DF03-5FD1-9263-F7CDB8AD7FFD HTTP/1.1
|
||||
Authorization: Basic bWljcm9tZG06c3VwZXJzZWNyZXQ=
|
||||
|
||||
{
|
||||
"commands": [
|
||||
{
|
||||
"uuid": "0001_ProfileList",
|
||||
"payload": "<base64 encoding of plist command>"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
A helper script is also available at `./tools/api/inspect_queue`:
|
||||
|
||||
`$ ./inspect_queue 55693EB3-DF03-5FD1-9263-F7CDB8AD7FFD`
|
||||
|
||||
@@ -36,10 +36,17 @@ type BootstrapTokenRetriever interface {
|
||||
GetBootstrapToken(ctx context.Context, udid string) ([]byte, error)
|
||||
}
|
||||
|
||||
// Command is an MDM Command
|
||||
type Command struct {
|
||||
UUID string `json:"uuid"`
|
||||
Payload []byte `json:"payload"`
|
||||
}
|
||||
|
||||
// Queue is an MDM Command Queue.
|
||||
type Queue interface {
|
||||
Next(context.Context, Response) ([]byte, error)
|
||||
Clear(context.Context, CheckinEvent) error
|
||||
ViewQueue(context.Context, CheckinEvent) ([]*Command, error)
|
||||
}
|
||||
|
||||
type MDMService struct {
|
||||
|
||||
@@ -11,6 +11,7 @@ type Endpoints struct {
|
||||
NewCommandEndpoint endpoint.Endpoint
|
||||
NewRawCommandEndpoint endpoint.Endpoint
|
||||
ClearQueueEndpoint endpoint.Endpoint
|
||||
ViewQueueEndpoint endpoint.Endpoint
|
||||
}
|
||||
|
||||
func MakeServerEndpoints(s Service, outer endpoint.Middleware, others ...endpoint.Middleware) Endpoints {
|
||||
@@ -18,10 +19,19 @@ func MakeServerEndpoints(s Service, outer endpoint.Middleware, others ...endpoin
|
||||
NewCommandEndpoint: endpoint.Chain(outer, others...)(MakeNewCommandEndpoint(s)),
|
||||
NewRawCommandEndpoint: endpoint.Chain(outer, others...)(MakeNewRawCommandEndpoint(s)),
|
||||
ClearQueueEndpoint: endpoint.Chain(outer, others...)(MakeClearQueueEndpoint(s)),
|
||||
ViewQueueEndpoint: endpoint.Chain(outer, others...)(MakeViewQueueEndpoint(s)),
|
||||
}
|
||||
}
|
||||
|
||||
func RegisterHTTPHandlers(r *mux.Router, e Endpoints, options ...httptransport.ServerOption) {
|
||||
// GET /v1/commands/udid View device queue.
|
||||
r.Methods("GET").Path("/v1/commands/{udid}").Handler(httptransport.NewServer(
|
||||
e.ViewQueueEndpoint,
|
||||
decodeViewQueueRequest,
|
||||
httputil.EncodeJSONResponse,
|
||||
options...,
|
||||
))
|
||||
|
||||
// POST /v1/commands Add new MDM Command to device queue.
|
||||
r.Methods("POST").Path("/v1/commands").Handler(httptransport.NewServer(
|
||||
e.NewCommandEndpoint,
|
||||
|
||||
@@ -12,11 +12,13 @@ type Service interface {
|
||||
NewCommand(context.Context, *mdm.CommandRequest) (*mdm.CommandPayload, error)
|
||||
NewRawCommand(context.Context, *RawCommand) error
|
||||
ClearQueue(ctx context.Context, udid string) error
|
||||
ViewQueue(ctx context.Context, udid string) ([]*mdmsvc.Command, error)
|
||||
}
|
||||
|
||||
// Queue is an MDM Command Queue.
|
||||
type Queue interface {
|
||||
Clear(context.Context, mdmsvc.CheckinEvent) error
|
||||
ViewQueue(context.Context, mdmsvc.CheckinEvent) ([]*mdmsvc.Command, error)
|
||||
}
|
||||
|
||||
type CommandService struct {
|
||||
|
||||
54
platform/command/view.go
Normal file
54
platform/command/view.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/micromdm/micromdm/mdm"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (svc *CommandService) ViewQueue(ctx context.Context, udid string) ([]*mdm.Command, error) {
|
||||
commands, err := svc.queue.ViewQueue(
|
||||
ctx,
|
||||
mdm.CheckinEvent{Command: mdm.CheckinCommand{UDID: udid}},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "clearing command queue")
|
||||
}
|
||||
return commands, nil
|
||||
}
|
||||
|
||||
type viewQueueRequest struct {
|
||||
UDID string
|
||||
}
|
||||
|
||||
type viewQueueResponse struct {
|
||||
Err error `json:"error,omitempty"`
|
||||
Commands []*mdm.Command `json:"commands,omitempty"`
|
||||
}
|
||||
|
||||
func (r viewQueueResponse) Failed() error { return r.Err }
|
||||
func (r viewQueueResponse) StatusCode() int { return http.StatusOK }
|
||||
|
||||
func decodeViewQueueRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
return viewQueueRequest{UDID: mux.Vars(r)["udid"]}, nil
|
||||
}
|
||||
|
||||
// MakeViewQueueEndpoint creates an endpoint which views device queues.
|
||||
func MakeViewQueueEndpoint(svc Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(viewQueueRequest)
|
||||
if req.UDID == "" {
|
||||
return viewQueueResponse{Err: errEmptyRequest}, nil
|
||||
}
|
||||
commands, err := svc.ViewQueue(ctx, req.UDID)
|
||||
if err != nil {
|
||||
return viewQueueResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
return viewQueueResponse{Commands: commands}, nil
|
||||
}
|
||||
}
|
||||
@@ -121,6 +121,30 @@ func (q *QueueInMem) Clear(_ context.Context, event mdm.CheckinEvent) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// View returns the command queue for the device in event
|
||||
func (q *QueueInMem) ViewQueue(_ context.Context, event mdm.CheckinEvent) ([]*mdm.Command, error) {
|
||||
udid := event.Command.UDID
|
||||
if event.Command.UserID != "" {
|
||||
udid = event.Command.UserID
|
||||
}
|
||||
if event.Command.EnrollmentID != "" {
|
||||
udid = event.Command.EnrollmentID
|
||||
}
|
||||
|
||||
l := q.getList(udid)
|
||||
|
||||
cmds := make([]*mdm.Command, 0, l.Len())
|
||||
for item := l.Front(); item != nil; item = item.Next() {
|
||||
cmd := item.Value.(*queuedCommand)
|
||||
cmds = append(cmds, &mdm.Command{
|
||||
UUID: cmd.uuid,
|
||||
Payload: cmd.payload,
|
||||
})
|
||||
}
|
||||
|
||||
return cmds, nil
|
||||
}
|
||||
|
||||
func (q *QueueInMem) startPolling(pubsub pubsub.PublishSubscriber) error {
|
||||
events, err := pubsub.Subscribe(context.TODO(), "command-queue", command.CommandTopic)
|
||||
if err != nil {
|
||||
|
||||
@@ -54,6 +54,33 @@ func (db *Store) Next(ctx context.Context, resp mdm.Response) ([]byte, error) {
|
||||
return cmd.Payload, nil
|
||||
}
|
||||
|
||||
func (db *Store) ViewQueue(ctx context.Context, event mdm.CheckinEvent) ([]*mdm.Command, error) {
|
||||
udid := event.Command.UDID
|
||||
if event.Command.UserID != "" {
|
||||
udid = event.Command.UserID
|
||||
}
|
||||
if event.Command.EnrollmentID != "" {
|
||||
udid = event.Command.EnrollmentID
|
||||
}
|
||||
|
||||
dc, err := db.DeviceCommand(udid)
|
||||
if isNotFound(err) {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, errors.Wrapf(err, "get device commands, udid: %s", udid)
|
||||
}
|
||||
|
||||
cmds := make([]*mdm.Command, len(dc.Commands))
|
||||
for idx, cmd := range dc.Commands {
|
||||
cmds[idx] = &mdm.Command{
|
||||
UUID: cmd.UUID,
|
||||
Payload: cmd.Payload,
|
||||
}
|
||||
}
|
||||
|
||||
return cmds, nil
|
||||
}
|
||||
|
||||
func (db *Store) Clear(ctx context.Context, event mdm.CheckinEvent) error {
|
||||
udid := event.Command.UDID
|
||||
if event.Command.UserID != "" {
|
||||
|
||||
5
tools/api/inspect_queue
Executable file
5
tools/api/inspect_queue
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
source $MICROMDM_ENV_PATH
|
||||
endpoint="v1/commands/$1"
|
||||
|
||||
curl $CURL_OPTS -K <(cat <<< "-u micromdm:$API_TOKEN") -X GET "$SERVER_URL/$endpoint"
|
||||
Reference in New Issue
Block a user