refactor block service for HA (#349)

This commit is contained in:
Victor Vrantchan
2017-12-09 19:04:06 -05:00
committed by GitHub
parent c87bca7116
commit 5ff0a2f7f3
19 changed files with 301 additions and 337 deletions

View File

@@ -0,0 +1,75 @@
package remove
import (
"context"
"encoding/json"
"net/http"
"net/url"
"github.com/go-kit/kit/endpoint"
"github.com/gorilla/mux"
"github.com/pkg/errors"
)
func (svc *RemoveService) BlockDevice(ctx context.Context, udid string) error {
return svc.store.Save(&Device{UDID: udid})
}
type blockDeviceRequest struct {
UDID string
}
type blockDeviceResponse struct {
Err error `json:"err,omitempty"`
}
func (r blockDeviceResponse) error() error { return r.Err }
func decodeBlockDeviceRequest(ctx context.Context, r *http.Request) (interface{}, error) {
var errBadRoute = errors.New("bad route")
var req blockDeviceRequest
vars := mux.Vars(r)
udid, ok := vars["udid"]
if !ok {
return 0, errBadRoute
}
req.UDID = udid
return req, nil
}
func encodeBlockDeviceRequest(_ context.Context, r *http.Request, request interface{}) error {
req := request.(blockDeviceRequest)
udid := url.QueryEscape(req.UDID)
r.Method, r.URL.Path = "POST", "/v1/devices/"+udid+"/block"
return nil
}
func decodeBlockDeviceResponse(_ context.Context, r *http.Response) (interface{}, error) {
if r.StatusCode != http.StatusOK {
return nil, errorDecoder(r)
}
var resp blockDeviceResponse
err := json.NewDecoder(r.Body).Decode(&resp)
return resp, err
}
func MakeBlockDeviceEndpoint(svc Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
req := request.(blockDeviceRequest)
err = svc.BlockDevice(ctx, req.UDID)
return &blockDeviceResponse{
Err: err,
}, nil
}
}
func (e Endpoints) BlockDevice(ctx context.Context, udid string) error {
request := blockDeviceRequest{
UDID: udid,
}
resp, err := e.BlockDeviceEndpoint(ctx, request)
if err != nil {
return err
}
return resp.(blockDeviceResponse).Err
}

View File

@@ -1,9 +1,10 @@
package remove
package builtin
import (
"fmt"
"github.com/boltdb/bolt"
"github.com/micromdm/micromdm/platform/remove"
"github.com/pkg/errors"
)
@@ -27,20 +28,20 @@ func NewDB(db *bolt.DB) (*DB, error) {
return datastore, nil
}
func (db *DB) DeviceByUDID(udid string) (*Device, error) {
var dev Device
func (db *DB) DeviceByUDID(udid string) (*remove.Device, error) {
var dev remove.Device
err := db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(RemoveBucket))
v := b.Get([]byte(udid))
if v == nil {
return &notFound{"Device", fmt.Sprintf("udid %s", udid)}
}
return UnmarshalDevice(v, &dev)
return remove.UnmarshalDevice(v, &dev)
})
return &dev, errors.Wrap(err, "remove: get device by udid")
}
func (db *DB) Save(dev *Device) error {
func (db *DB) Save(dev *remove.Device) error {
tx, err := db.DB.Begin(true)
if err != nil {
return errors.Wrap(err, "begin transaction")
@@ -49,7 +50,7 @@ func (db *DB) Save(dev *Device) error {
if bkt == nil {
return fmt.Errorf("bucket %q not found!", RemoveBucket)
}
pb, err := MarshalDevice(dev)
pb, err := remove.MarshalDevice(dev)
if err != nil {
return errors.Wrap(err, "marshalling Device")
}
@@ -81,10 +82,6 @@ func (e *notFound) Error() string {
return fmt.Sprintf("not found: %s %s", e.ResourceType, e.Message)
}
func isNotFound(err error) bool {
cause := errors.Cause(err)
if _, ok := cause.(*notFound); ok {
return true
}
return false
func (e *notFound) NotFound() bool {
return true
}

58
platform/remove/client.go Normal file
View File

@@ -0,0 +1,58 @@
package remove
import (
"context"
"net/http"
"net/url"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/log"
httptransport "github.com/go-kit/kit/transport/http"
)
func NewHTTPClient(instance, token string, logger log.Logger, opts ...httptransport.ClientOption) (Service, error) {
u, err := url.Parse(instance)
if err != nil {
return nil, err
}
var blockDeviceEndpoint endpoint.Endpoint
{
blockDeviceEndpoint = httptransport.NewClient(
"POST",
copyURL(u, ""), // empty path, modified by the encodeRequest func
encodeRequestWithToken(token, encodeBlockDeviceRequest),
decodeBlockDeviceResponse,
opts...,
).Endpoint()
}
var unblockDeviceEndpoint endpoint.Endpoint
{
unblockDeviceEndpoint = httptransport.NewClient(
"POST",
copyURL(u, ""), //modified by encodeRequestFunc
encodeRequestWithToken(token, encodeUnblockDeviceRequest),
decodeUnblockDeviceResponse,
opts...,
).Endpoint()
}
return Endpoints{
BlockDeviceEndpoint: blockDeviceEndpoint,
UnblockDeviceEndpoint: unblockDeviceEndpoint,
}, nil
}
func encodeRequestWithToken(token string, next httptransport.EncodeRequestFunc) httptransport.EncodeRequestFunc {
return func(ctx context.Context, r *http.Request, request interface{}) error {
r.SetBasicAuth("micromdm", token)
return next(ctx, r, request)
}
}
func copyURL(base *url.URL, path string) *url.URL {
next := *base
next.Path = path
return &next
}

View File

@@ -10,27 +10,6 @@ import (
"github.com/micromdm/micromdm/platform/remove/internal/removeproto"
)
type Service interface {
BlockDevice(ctx context.Context, udid string) error
UnblockDevice(ctx context.Context, udid string) error
}
type RemoveService struct {
db *DB
}
func NewService(db *DB) (*RemoveService, error) {
return &RemoveService{db: db}, nil
}
func (svc *RemoveService) BlockDevice(ctx context.Context, udid string) error {
return svc.db.Save(&Device{UDID: udid})
}
func (svc *RemoveService) UnblockDevice(ctx context.Context, udid string) error {
return svc.db.Delete(udid)
}
type Device struct {
UDID string `json:"udid"`
}
@@ -51,23 +30,23 @@ func UnmarshalDevice(data []byte, dev *Device) error {
return nil
}
func RemoveMiddleware(db *DB) connect.Middleware {
func RemoveMiddleware(store Store) connect.Middleware {
return func(next connect.Service) connect.Service {
return &removeMiddleware{
db: db,
next: next,
store: store,
next: next,
}
}
}
type removeMiddleware struct {
db *DB
next connect.Service
store Store
next connect.Service
}
func (mw removeMiddleware) Acknowledge(ctx context.Context, req connect.MDMConnectRequest) ([]byte, error) {
udid := req.MDMResponse.UDID
_, err := mw.db.DeviceByUDID(udid)
_, err := mw.store.DeviceByUDID(udid)
if err != nil {
if !isNotFound(err) {
return nil, errors.Wrapf(err, "remove: get device by udid %s", udid)
@@ -88,3 +67,13 @@ func (checkoutErr) Error() string {
func (checkoutErr) Checkout() bool {
return true
}
func isNotFound(err error) bool {
type notFoundError interface {
error
NotFound() bool
}
_, ok := err.(notFoundError)
return ok
}

68
platform/remove/server.go Normal file
View File

@@ -0,0 +1,68 @@
package remove
import (
"encoding/json"
"errors"
"net/http"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/log"
httptransport "github.com/go-kit/kit/transport/http"
"github.com/gorilla/mux"
)
type Endpoints struct {
BlockDeviceEndpoint endpoint.Endpoint
UnblockDeviceEndpoint endpoint.Endpoint
}
func MakeServerEndpoints(s Service) Endpoints {
return Endpoints{
BlockDeviceEndpoint: MakeBlockDeviceEndpoint(s),
UnblockDeviceEndpoint: MakeUnblockDeviceEndpoint(s),
}
}
func MakeHTTPHandler(e Endpoints, logger log.Logger) http.Handler {
options := []httptransport.ServerOption{
httptransport.ServerErrorLogger(logger),
}
r := mux.NewRouter()
// POST /v1/devices/:udid/block force a device to unenroll next time it connects
// POST /v1/devices/:udid/unblock allow a blocked device to enroll again
r.Methods("POST").Path("/v1/devices/{udid}/block").Handler(httptransport.NewServer(
e.BlockDeviceEndpoint,
decodeBlockDeviceRequest,
httptransport.EncodeJSONResponse,
options...,
))
r.Methods("POST").Path("/v1/devices/{udid}/unblock").Handler(httptransport.NewServer(
e.UnblockDeviceEndpoint,
decodeUnblockDeviceRequest,
httptransport.EncodeJSONResponse,
options...,
))
return r
}
type errorWrapper struct {
Error string `json:"error"`
}
type errorer interface {
error() error
}
func errorDecoder(r *http.Response) error {
var w errorWrapper
if err := json.NewDecoder(r.Body).Decode(&w); err != nil {
return err
}
return errors.New(w.Error)
}

View File

@@ -0,0 +1,22 @@
package remove
import "context"
type Service interface {
BlockDevice(ctx context.Context, udid string) error
UnblockDevice(ctx context.Context, udid string) error
}
type Store interface {
Save(*Device) error
DeviceByUDID(string) (*Device, error)
Delete(string) error
}
type RemoveService struct {
store Store
}
func New(store Store) (*RemoveService, error) {
return &RemoveService{store: store}, nil
}

View File

@@ -0,0 +1,73 @@
package remove
import (
"context"
"encoding/json"
"errors"
"net/http"
"net/url"
"github.com/go-kit/kit/endpoint"
"github.com/gorilla/mux"
)
func (svc *RemoveService) UnblockDevice(ctx context.Context, udid string) error {
return svc.store.Delete(udid)
}
type unblockDeviceRequest struct {
UDID string
}
type unblockDeviceResponse struct {
Err error `json:"err,omitempty"`
}
func (r unblockDeviceResponse) error() error { return r.Err }
func decodeUnblockDeviceRequest(ctx context.Context, r *http.Request) (interface{}, error) {
var errBadRoute = errors.New("bad route")
var req unblockDeviceRequest
vars := mux.Vars(r)
udid, ok := vars["udid"]
if !ok {
return 0, errBadRoute
}
req.UDID = udid
return req, nil
}
func encodeUnblockDeviceRequest(_ context.Context, r *http.Request, request interface{}) error {
req := request.(unblockDeviceRequest)
udid := url.QueryEscape(req.UDID)
r.Method, r.URL.Path = "POST", "/v1/devices/"+udid+"/unblock"
return nil
}
func decodeUnblockDeviceResponse(_ context.Context, r *http.Response) (interface{}, error) {
if r.StatusCode != http.StatusOK {
return nil, errorDecoder(r)
}
var resp unblockDeviceResponse
err := json.NewDecoder(r.Body).Decode(&resp)
return resp, err
}
func MakeUnblockDeviceEndpoint(svc Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
req := request.(unblockDeviceRequest)
err = svc.UnblockDevice(ctx, req.UDID)
return unblockDeviceResponse{
Err: err,
}, nil
}
}
func (e Endpoints) UnblockDevice(ctx context.Context, udid string) error {
request := unblockDeviceRequest{UDID: udid}
resp, err := e.UnblockDeviceEndpoint(ctx, request)
if err != nil {
return err
}
return resp.(unblockDeviceResponse).Err
}