mirror of
https://github.com/micromdm/micromdm/
synced 2026-06-27 16:55:46 +08:00
Add more logic to the way code is organized. /pkg -- library code not directly connected to micromdm /mdm -- packages meant for the services devices interract with. The MDM protocol. /dep -- DEP API and related packages. /platform -- Core APIs the server provides. Commands API, Devices API, queue, pubsub etc. /workflow -- Packages/API that build on top of platform. Today that's the webhook package. Depending on what ends up here, the workflow folder might become its own repository.
85 lines
2.2 KiB
Go
85 lines
2.2 KiB
Go
package command
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/boltdb/bolt"
|
|
"github.com/micromdm/mdm"
|
|
"github.com/pkg/errors"
|
|
"golang.org/x/net/context"
|
|
|
|
"github.com/micromdm/micromdm/platform/pubsub"
|
|
)
|
|
|
|
const (
|
|
|
|
// CommandBucket is the *bolt.DB bucket where commands are archived.
|
|
CommandBucket = "mdm.Command.ARCHIVE"
|
|
|
|
// CommandTopic is a PubSub topic that events are published to.
|
|
CommandTopic = "mdm.Command"
|
|
)
|
|
|
|
type Command struct {
|
|
db *bolt.DB
|
|
publisher pubsub.Publisher
|
|
archiveFn func(int64, []byte) error
|
|
}
|
|
|
|
func New(db *bolt.DB, pub pubsub.Publisher) (*Command, error) {
|
|
err := db.Update(func(tx *bolt.Tx) error {
|
|
_, err := tx.CreateBucketIfNotExists([]byte(CommandBucket))
|
|
return err
|
|
})
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "creating %s bucket", CommandBucket)
|
|
}
|
|
svc := Command{
|
|
db: db,
|
|
publisher: pub,
|
|
}
|
|
svc.archiveFn = svc.archive
|
|
return &svc, nil
|
|
}
|
|
|
|
func (svc *Command) NewCommand(ctx context.Context, request *mdm.CommandRequest) (*mdm.Payload, error) {
|
|
if request == nil {
|
|
return nil, errors.New("empty CommandRequest")
|
|
}
|
|
payload, err := mdm.NewPayload(request)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "creating mdm payload")
|
|
}
|
|
event := NewEvent(*payload, request.UDID)
|
|
msg, err := MarshalEvent(event)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "marshalling mdm command event")
|
|
}
|
|
if err := svc.archive(event.Time.UnixNano(), msg); err != nil {
|
|
return nil, errors.Wrap(err, "archive mdm command")
|
|
}
|
|
if err := svc.publisher.Publish(context.TODO(), CommandTopic, msg); err != nil {
|
|
return nil, errors.Wrapf(err, "publish mdm command on topic: %s", CommandTopic)
|
|
}
|
|
return payload, nil
|
|
}
|
|
|
|
// archive events to BoltDB bucket using timestamp as key to preserve order.
|
|
func (svc *Command) archive(nano int64, msg []byte) error {
|
|
tx, err := svc.db.Begin(true)
|
|
if err != nil {
|
|
return errors.Wrap(err, "begin transaction")
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
bkt := tx.Bucket([]byte(CommandBucket))
|
|
if bkt == nil {
|
|
return fmt.Errorf("bucket %q not found!", CommandBucket)
|
|
}
|
|
key := []byte(fmt.Sprintf("%d", nano))
|
|
if err := bkt.Put(key, msg); err != nil {
|
|
return errors.Wrap(err, "put command event to boltdb")
|
|
}
|
|
return tx.Commit()
|
|
}
|