mirror of
https://github.com/micromdm/micromdm/
synced 2026-08-06 17:53:14 +08:00
Add raw command and clear device queue endpoints (#864)
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
## [Unreleased](https://github.com/micromdm/micromdm/compare/v1.10.1...main)
|
||||
|
||||
- Add SoftwareUpdateSettings to Settings command (#771, #856)
|
||||
- Ensure errors are logged on the checkin and connect endpoints (#871)
|
||||
- Add support for raw plist commands (#864)
|
||||
|
||||
## [v1.10.1](https://github.com/micromdm/micromdm/compare/v1.10.0...v1.10.1) January 24, 2023
|
||||
|
||||
|
||||
@@ -212,6 +212,7 @@ For example, to install a configuration profile to a device, you can schedule th
|
||||
```
|
||||
POST /v1/commands HTTP/1.1
|
||||
Authorization: Basic bWljcm9tZG06c3VwZXJzZWNyZXQ=
|
||||
|
||||
{
|
||||
"udid": "55693EB3-DF03-5FD1-9263-F7CDB8AD7FFD",
|
||||
"request_type": "InstallProfile",
|
||||
@@ -220,3 +221,53 @@ Authorization: Basic bWljcm9tZG06c3VwZXJzZWNyZXQ=
|
||||
```
|
||||
|
||||
MicroMDM will convert this request into a complete command, and schedule it on the queue. It will then send a push notification to ask the device to check in, and respond with the InstallProfile command.
|
||||
|
||||
# Schedule Raw Commands with the API
|
||||
|
||||
[PR #864](https://github.com/micromdm/micromdm/pull/864) added support for queuing raw plist commands. This is useful for queuing commands that aren't currently supported (e.g. missing commands or missing fields) by MicroMDM and can also help migrate to NanoMDM.
|
||||
|
||||
The raw command endpoint differs from the main command endpoint in the following ways:
|
||||
|
||||
* The MDM topic (e.g. the device UDID) is specified in the URL
|
||||
* The body is the raw command plist instead of MicroMDM's JSON schema
|
||||
* The command is not validated (the body is only checked to be a valid plist)
|
||||
|
||||
Here's an example [ProfileList](https://developer.apple.com/documentation/devicemanagement/list_the_installed_profiles) command:
|
||||
|
||||
```
|
||||
POST /v1/commands/55693EB3-DF03-5FD1-9263-F7CDB8AD7FFD HTTP/1.1
|
||||
Authorization: Basic bWljcm9tZG06c3VwZXJzZWNyZXQ=
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Command</key>
|
||||
<dict>
|
||||
<key>ManagedOnly</key>
|
||||
<false/>
|
||||
<key>RequestType</key>
|
||||
<string>ProfileList</string>
|
||||
</dict>
|
||||
<key>CommandUUID</key>
|
||||
<string>0001_ProfileList</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
A helper script is also available at `./tools/api/raw_command`:
|
||||
|
||||
`$ ./raw_command 55693EB3-DF03-5FD1-9263-F7CDB8AD7FFD path/to/cmd.plist`
|
||||
|
||||
## Clearing the Command Queue
|
||||
|
||||
Since the raw command api endpoint doesn't validate the body as a valid command, it's possible to queue a malformed command, which may cause the client device to stop processing commands from the queue. [PR #864](https://github.com/micromdm/micromdm/pull/864) also includes an endpoint to clear the command queue for a device:
|
||||
|
||||
```
|
||||
DELETE /v1/commands/<udid> HTTP/1.1
|
||||
Authorization: Basic bWljcm9tZG06c3VwZXJzZWNyZXQ=
|
||||
```
|
||||
|
||||
A helper script is also available at `./tools/api/clear_queue`:
|
||||
|
||||
`$ ./clear_queue 55693EB3-DF03-5FD1-9263-F7CDB8AD7FFD`
|
||||
|
||||
47
platform/command/clear.go
Normal file
47
platform/command/clear.go
Normal file
@@ -0,0 +1,47 @@
|
||||
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) ClearQueue(ctx context.Context, udid string) error {
|
||||
if err := svc.queue.Clear(ctx, mdm.CheckinEvent{Command: mdm.CheckinCommand{UDID: udid}}); err != nil {
|
||||
return errors.Wrap(err, "clearing command queue")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type clearRequest struct {
|
||||
UDID string
|
||||
}
|
||||
|
||||
type clearResponse struct {
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r clearResponse) Failed() error { return r.Err }
|
||||
func (r clearResponse) StatusCode() int { return http.StatusOK }
|
||||
|
||||
func decodeClearRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
return clearRequest{UDID: mux.Vars(r)["udid"]}, nil
|
||||
}
|
||||
|
||||
// MakeClearQueueEndpoint creates an endpoint which clears device queues.
|
||||
func MakeClearQueueEndpoint(svc Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(clearRequest)
|
||||
if req.UDID == "" {
|
||||
return clearResponse{Err: errEmptyRequest}, nil
|
||||
}
|
||||
if err := svc.ClearQueue(ctx, req.UDID); err != nil {
|
||||
return clearResponse{Err: err}, nil
|
||||
}
|
||||
return clearResponse{}, nil
|
||||
}
|
||||
}
|
||||
@@ -61,3 +61,45 @@ func UnmarshalEvent(data []byte, e *Event) error {
|
||||
e.Payload = &payload
|
||||
return nil
|
||||
}
|
||||
|
||||
type RawEvent struct {
|
||||
CommandUUID string
|
||||
Time time.Time
|
||||
DeviceUDID string
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// NewRawEvent returns a RawEvent with the current time.
|
||||
func NewRawEvent(cmd *RawCommand) *RawEvent {
|
||||
event := RawEvent{
|
||||
CommandUUID: cmd.CommandUUID,
|
||||
Time: time.Now().UTC(),
|
||||
DeviceUDID: cmd.UDID,
|
||||
Payload: cmd.Raw,
|
||||
}
|
||||
return &event
|
||||
}
|
||||
|
||||
// MarshalRawEvent serializes a RawEvent to a protocol buffer wire format.
|
||||
func MarshalRawEvent(e *RawEvent) ([]byte, error) {
|
||||
return proto.Marshal(&commandproto.Event{
|
||||
Id: e.CommandUUID, // Id isn't used anywhere, so it's repurposed for CommandUUID
|
||||
Time: e.Time.UnixNano(),
|
||||
DeviceUdid: e.DeviceUDID,
|
||||
PayloadBytes: e.Payload,
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalRawEvent parses a protocol buffer representation of data into
|
||||
// the RawEvent.
|
||||
func UnmarshalRawEvent(data []byte, e *RawEvent) error {
|
||||
var pb commandproto.Event
|
||||
if err := proto.Unmarshal(data, &pb); err != nil {
|
||||
return errors.Wrap(err, "unmarshal pb Event")
|
||||
}
|
||||
e.CommandUUID = pb.Id
|
||||
e.Time = time.Unix(0, pb.Time).UTC()
|
||||
e.DeviceUDID = pb.DeviceUdid
|
||||
e.Payload = pb.PayloadBytes
|
||||
return nil
|
||||
}
|
||||
|
||||
49
platform/command/event_test.go
Normal file
49
platform/command/event_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package command_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/micromdm/micromdm/platform/command"
|
||||
)
|
||||
|
||||
const testRawCmd = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Command</key>
|
||||
<dict>
|
||||
<key>ManagedOnly</key>
|
||||
<true/>
|
||||
<key>RequestType</key>
|
||||
<string>ProfileList</string>
|
||||
</dict>
|
||||
<key>CommandUUID</key>
|
||||
<string>0001_ProfileList</string>
|
||||
</dict>
|
||||
</plist>`
|
||||
|
||||
func TestRawEvent(t *testing.T) {
|
||||
cmd := &command.RawCommand{
|
||||
UDID: "1234",
|
||||
CommandUUID: "0001_ProfileList",
|
||||
Raw: []byte(testRawCmd),
|
||||
}
|
||||
cmd.Command.RequestType = "ProfileList"
|
||||
|
||||
ev := command.NewRawEvent(cmd)
|
||||
|
||||
buf, err := command.MarshalRawEvent(ev)
|
||||
if err != nil {
|
||||
t.Fatalf("could not marshal event: %v", err)
|
||||
}
|
||||
|
||||
ev2 := new(command.RawEvent)
|
||||
if err = command.UnmarshalRawEvent(buf, ev2); err != nil {
|
||||
t.Fatalf("could not unmarshal event: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(ev, ev2) {
|
||||
t.Error("expected events to be equal")
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/groob/plist"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
@@ -14,6 +19,9 @@ import (
|
||||
const (
|
||||
// CommandTopic is a PubSub topic that events are published to.
|
||||
CommandTopic = "mdm.Command"
|
||||
|
||||
// RawCommandTopic is a PubSub topic that events are published to.
|
||||
RawCommandTopic = "mdm.RawCommand"
|
||||
)
|
||||
|
||||
func (svc *CommandService) NewCommand(ctx context.Context, request *mdm.CommandRequest) (*mdm.CommandPayload, error) {
|
||||
@@ -35,6 +43,21 @@ func (svc *CommandService) NewCommand(ctx context.Context, request *mdm.CommandR
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
func (svc *CommandService) NewRawCommand(ctx context.Context, cmd *RawCommand) error {
|
||||
if cmd == nil {
|
||||
return errors.New("empty RawCommand")
|
||||
}
|
||||
event := NewRawEvent(cmd)
|
||||
msg, err := MarshalRawEvent(event)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshalling raw mdm command event")
|
||||
}
|
||||
if err := svc.publisher.Publish(context.TODO(), RawCommandTopic, msg); err != nil {
|
||||
return errors.Wrapf(err, "publish raw mdm command on topic: %s", RawCommandTopic)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type newCommandRequest struct {
|
||||
mdm.CommandRequest
|
||||
}
|
||||
@@ -69,3 +92,65 @@ func MakeNewCommandEndpoint(svc Service) endpoint.Endpoint {
|
||||
return newCommandResponse{Payload: payload}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type RawCommand struct {
|
||||
UDID string `json:"udid" plist:"-"`
|
||||
CommandUUID string `json:"command_uuid"`
|
||||
Command struct {
|
||||
RequestType string `json:"request_type"`
|
||||
} `json:"command"`
|
||||
Raw []byte `plist:"-" json:"payload"`
|
||||
}
|
||||
|
||||
type newRawCommandRequest struct {
|
||||
RawCommand
|
||||
}
|
||||
|
||||
type newRawCommandResponse struct {
|
||||
Payload *RawCommand `json:"payload,omitempty"`
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r newRawCommandResponse) Failed() error { return r.Err }
|
||||
func (r newRawCommandResponse) StatusCode() int { return http.StatusCreated }
|
||||
|
||||
func decodeNewRawCommandRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
udid, ok := mux.Vars(r)["udid"]
|
||||
if !ok {
|
||||
return nil, errors.New("empty udid")
|
||||
}
|
||||
|
||||
payload, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read payload body: %w", err)
|
||||
}
|
||||
|
||||
// verify body is valid plist and parse CommandUUID and RequestType
|
||||
var req newRawCommandRequest
|
||||
if err := plist.NewXMLDecoder(bytes.NewBuffer(payload)).Decode(&req); err != nil {
|
||||
return nil, fmt.Errorf("parse payload as plist: %w", err)
|
||||
}
|
||||
|
||||
req.UDID = udid
|
||||
req.Raw = payload
|
||||
return req, nil
|
||||
}
|
||||
|
||||
var errMalformedRequest = errors.New("request is malformed")
|
||||
|
||||
// MakeNewRawCommandEndpoint creates an endpoint which creates new raw MDM Commands.
|
||||
func MakeNewRawCommandEndpoint(svc Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(newRawCommandRequest)
|
||||
if req.UDID == "" {
|
||||
return newRawCommandResponse{Err: errEmptyRequest}, nil
|
||||
}
|
||||
if req.CommandUUID == "" || req.Command.RequestType == "" {
|
||||
return newRawCommandResponse{Err: errMalformedRequest}, nil
|
||||
}
|
||||
if err := svc.NewRawCommand(ctx, &req.RawCommand); err != nil {
|
||||
return newRawCommandResponse{Err: err}, nil
|
||||
}
|
||||
return newRawCommandResponse{Payload: &req.RawCommand}, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,22 +8,41 @@ import (
|
||||
)
|
||||
|
||||
type Endpoints struct {
|
||||
NewCommandEndpoint endpoint.Endpoint
|
||||
NewCommandEndpoint endpoint.Endpoint
|
||||
NewRawCommandEndpoint endpoint.Endpoint
|
||||
ClearQueueEndpoint endpoint.Endpoint
|
||||
}
|
||||
|
||||
func MakeServerEndpoints(s Service, outer endpoint.Middleware, others ...endpoint.Middleware) Endpoints {
|
||||
return Endpoints{
|
||||
NewCommandEndpoint: endpoint.Chain(outer, others...)(MakeNewCommandEndpoint(s)),
|
||||
NewCommandEndpoint: endpoint.Chain(outer, others...)(MakeNewCommandEndpoint(s)),
|
||||
NewRawCommandEndpoint: endpoint.Chain(outer, others...)(MakeNewRawCommandEndpoint(s)),
|
||||
ClearQueueEndpoint: endpoint.Chain(outer, others...)(MakeClearQueueEndpoint(s)),
|
||||
}
|
||||
}
|
||||
|
||||
func RegisterHTTPHandlers(r *mux.Router, e Endpoints, options ...httptransport.ServerOption) {
|
||||
// POST /v1/commands Add new MDM Command to device queue.
|
||||
|
||||
r.Methods("POST").Path("/v1/commands").Handler(httptransport.NewServer(
|
||||
e.NewCommandEndpoint,
|
||||
decodeNewCommandRequest,
|
||||
httputil.EncodeJSONResponse,
|
||||
options...,
|
||||
))
|
||||
|
||||
// POST /v1/commands/udid Add new MDM Command with raw plist to device queue.
|
||||
r.Methods("POST").Path("/v1/commands/{udid}").Handler(httptransport.NewServer(
|
||||
e.NewRawCommandEndpoint,
|
||||
decodeNewRawCommandRequest,
|
||||
httputil.EncodeJSONResponse,
|
||||
options...,
|
||||
))
|
||||
|
||||
// DELETE /v1/commands/udid Clear device queue.
|
||||
r.Methods("DELETE").Path("/v1/commands/{udid}").Handler(httptransport.NewServer(
|
||||
e.ClearQueueEndpoint,
|
||||
decodeClearRequest,
|
||||
httputil.EncodeJSONResponse,
|
||||
options...,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
mdmsvc "github.com/micromdm/micromdm/mdm"
|
||||
"github.com/micromdm/micromdm/mdm/mdm"
|
||||
"github.com/micromdm/micromdm/platform/pubsub"
|
||||
"golang.org/x/net/context"
|
||||
@@ -9,15 +10,24 @@ import (
|
||||
|
||||
type Service interface {
|
||||
NewCommand(context.Context, *mdm.CommandRequest) (*mdm.CommandPayload, error)
|
||||
NewRawCommand(context.Context, *RawCommand) error
|
||||
ClearQueue(ctx context.Context, udid string) error
|
||||
}
|
||||
|
||||
// Queue is an MDM Command Queue.
|
||||
type Queue interface {
|
||||
Clear(context.Context, mdmsvc.CheckinEvent) error
|
||||
}
|
||||
|
||||
type CommandService struct {
|
||||
publisher pubsub.Publisher
|
||||
queue Queue
|
||||
}
|
||||
|
||||
func New(pub pubsub.Publisher) (*CommandService, error) {
|
||||
func New(pub pubsub.Publisher, queue Queue) (*CommandService, error) {
|
||||
svc := CommandService{
|
||||
publisher: pub,
|
||||
queue: queue,
|
||||
}
|
||||
return &svc, nil
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ func New(pubsub pubsub.PublishSubscriber, logger log.Logger) *QueueInMem {
|
||||
queue: make(map[string]*list.List),
|
||||
}
|
||||
q.startPolling(pubsub)
|
||||
q.startRawPolling(pubsub)
|
||||
return q
|
||||
}
|
||||
|
||||
@@ -169,3 +170,44 @@ func (q *QueueInMem) startPolling(pubsub pubsub.PublishSubscriber) error {
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *QueueInMem) startRawPolling(pubsub pubsub.PublishSubscriber) error {
|
||||
events, err := pubsub.Subscribe(context.TODO(), "command-queue", command.RawCommandTopic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event := <-events:
|
||||
var cmdEvent command.RawEvent
|
||||
if err := command.UnmarshalRawEvent(event.Message, &cmdEvent); err != nil {
|
||||
level.Info(q.logger).Log(
|
||||
"msg", "unmarshal command event from pubsub",
|
||||
"err", err,
|
||||
)
|
||||
continue
|
||||
}
|
||||
q.enqueue(
|
||||
q.getList(cmdEvent.DeviceUDID),
|
||||
cmdEvent.CommandUUID,
|
||||
cmdEvent.Payload,
|
||||
)
|
||||
level.Info(q.logger).Log(
|
||||
"msg", "queued raw command for device",
|
||||
"device_udid", cmdEvent.DeviceUDID,
|
||||
"command_uuid", cmdEvent.CommandUUID,
|
||||
)
|
||||
|
||||
err = boltqueue.PublishCommandQueued(pubsub, cmdEvent.DeviceUDID, cmdEvent.CommandUUID)
|
||||
if err != nil {
|
||||
level.Info(q.logger).Log(
|
||||
"msg", "publish command to queued topic",
|
||||
"err", err,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -212,6 +212,10 @@ func NewQueue(db *bolt.DB, pubsub pubsub.PublishSubscriber, opts ...Option) (*St
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := datastore.pollRawCommands(pubsub); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return datastore, nil
|
||||
}
|
||||
|
||||
@@ -318,6 +322,58 @@ func (db *Store) pollCommands(pubsub pubsub.PublishSubscriber) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *Store) pollRawCommands(pubsub pubsub.PublishSubscriber) error {
|
||||
commandEvents, err := pubsub.Subscribe(context.TODO(), "command-queue", command.RawCommandTopic)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err,
|
||||
"subscribing push to %s topic", command.RawCommandTopic)
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event := <-commandEvents:
|
||||
var ev command.RawEvent
|
||||
if err := command.UnmarshalRawEvent(event.Message, &ev); err != nil {
|
||||
level.Info(db.logger).Log("msg", "unmarshal raw command event in queue", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
cmd := new(DeviceCommand)
|
||||
cmd.DeviceUDID = ev.DeviceUDID
|
||||
byUDID, err := db.DeviceCommand(ev.DeviceUDID)
|
||||
if err == nil && byUDID != nil {
|
||||
cmd = byUDID
|
||||
}
|
||||
newCmd := Command{
|
||||
UUID: ev.CommandUUID,
|
||||
Payload: ev.Payload,
|
||||
}
|
||||
cmd.Commands = append(cmd.Commands, newCmd)
|
||||
if err := db.Save(cmd); err != nil {
|
||||
level.Info(db.logger).Log("msg", "save command in db", "err", err)
|
||||
continue
|
||||
}
|
||||
level.Info(db.logger).Log(
|
||||
"msg", "queued raw event for device",
|
||||
"device_udid", ev.DeviceUDID,
|
||||
"command_uuid", ev.CommandUUID,
|
||||
)
|
||||
|
||||
err = PublishCommandQueued(pubsub, ev.DeviceUDID, ev.CommandUUID)
|
||||
if err != nil {
|
||||
level.Info(db.logger).Log(
|
||||
"msg", "publish command to queued topic",
|
||||
"err", err,
|
||||
)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isNotFound(err error) bool {
|
||||
if _, ok := err.(*notFound); ok {
|
||||
return true
|
||||
|
||||
@@ -74,6 +74,8 @@ type Server struct {
|
||||
SCEPService scep.Service
|
||||
ConfigService config.Service
|
||||
|
||||
CommandQueue mdm.Queue
|
||||
|
||||
WebhooksHTTPClient *http.Client
|
||||
}
|
||||
|
||||
@@ -102,10 +104,6 @@ func (c *Server) Setup(logger log.Logger) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.setupCommandService(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.setupWebhooks(logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -114,6 +112,10 @@ func (c *Server) Setup(logger log.Logger) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.setupCommandService(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.setupDepClient(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -162,7 +164,7 @@ func (c *Server) setupRemoveService() error {
|
||||
}
|
||||
|
||||
func (c *Server) setupCommandService() error {
|
||||
commandService, err := command.New(c.PubClient)
|
||||
commandService, err := command.New(c.PubClient, c.CommandQueue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -191,6 +193,8 @@ func (c *Server) setupCommandQueue(logger log.Logger) error {
|
||||
return fmt.Errorf("invalid command queue type: %s", c.Queue)
|
||||
}
|
||||
|
||||
c.CommandQueue = q
|
||||
|
||||
devDB, err := devicebuiltin.NewDB(c.DB)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "new device db")
|
||||
|
||||
5
tools/api/clear_queue
Executable file
5
tools/api/clear_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 DELETE "$SERVER_URL/$endpoint"
|
||||
6
tools/api/raw_command
Executable file
6
tools/api/raw_command
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# raw_cmd $udid $path/to/cmd.plist
|
||||
source $MICROMDM_ENV_PATH
|
||||
endpoint="v1/commands/$1"
|
||||
|
||||
curl $CURL_OPTS -K <(cat <<< "-u micromdm:$API_TOKEN") --data "@$2" "$SERVER_URL/$endpoint"
|
||||
Reference in New Issue
Block a user