Define new datastore for retrieving installed applications by device uuid

Add InstalledApps method to management service
Add endpoint for installed applications at /management/v1/devices/uuid/applications
This commit is contained in:
Mosen
2016-07-11 17:14:07 +10:00
parent fba935db87
commit 07b2debc98
7 changed files with 162 additions and 10 deletions

View File

@@ -0,0 +1,14 @@
package applications
type Application struct {
Identifier string `plist:",omitempty" json:"identifier,omitempty"`
Version string `plist:",omitempty" json:"version,omitempty"`
ShortVersion string `plist:",omitempty" json:"short_version,omitempty"`
Name string `json:"name,omitempty"`
BundleSize int `plist:",omitempty" json:"bundle_size,omitempty"`
// The size of the app's document, library, and other folders, in bytes.
DynamicSize int `plist:",omitempty" json:"dynamic_size,omitempty"`
IsValidated bool `plist:",omitempty" json:"is_validated,omitempty"`
}

60
applications/datastore.go Normal file
View File

@@ -0,0 +1,60 @@
package applications
import (
"fmt"
kitlog "github.com/go-kit/kit/log"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq" // postgres driver
"github.com/pkg/errors"
"time"
)
// Datastore manages devices in a database
type Datastore interface {
GetApplicationsByDeviceUUID(deviceUUID string) (*[]Application, error)
}
type pgStore struct {
*sqlx.DB
}
func NewDB(driver, conn string, logger kitlog.Logger) (Datastore, error) {
switch driver {
case "postgres":
db, err := sqlx.Open(driver, conn)
if err != nil {
return nil, errors.Wrap(err, "device datastore")
}
var dbError error
maxAttempts := 20
for attempts := 1; attempts <= maxAttempts; attempts++ {
dbError = db.Ping()
if dbError == nil {
break
}
logger.Log("msg", fmt.Sprintf("could not connect to postgres: %v", dbError))
time.Sleep(time.Duration(attempts) * time.Second)
}
if dbError != nil {
return nil, errors.Wrap(dbError, "device datastore")
}
return pgStore{DB: db}, nil
default:
return nil, errors.New("unknown driver")
}
}
func (store pgStore) GetApplicationsByDeviceUUID(deviceUUID string) (*[]Application, error) {
apps := []Application{}
query := `SELECT * FROM applications
RIGHT JOIN devices_applications ON applications.application_uuid = devices_applications.application_uuid
WHERE devices_applications.device_uuid=$1`
err := store.Select(&apps, query, deviceUUID)
if err != nil {
return nil, err
}
return &apps, nil
}

View File

@@ -0,0 +1 @@
package applications

13
main.go
View File

@@ -14,6 +14,7 @@ import (
"github.com/RobotsAndPencils/buford/push" "github.com/RobotsAndPencils/buford/push"
"github.com/go-kit/kit/log" "github.com/go-kit/kit/log"
"github.com/micromdm/dep" "github.com/micromdm/dep"
"github.com/micromdm/micromdm/applications"
"github.com/micromdm/micromdm/checkin" "github.com/micromdm/micromdm/checkin"
"github.com/micromdm/micromdm/command" "github.com/micromdm/micromdm/command"
"github.com/micromdm/micromdm/connect" "github.com/micromdm/micromdm/connect"
@@ -184,8 +185,18 @@ func main() {
os.Exit(1) os.Exit(1)
} }
appsDB, err := applications.NewDB(
"postgres",
*flPGconn,
logger,
)
if err != nil {
logger.Log("err", err)
os.Exit(1)
}
dc := depClient(logger, *flDEPCK, *flDEPCS, *flDEPAT, *flDEPAS, *flDEPServerURL, *flDEPsim) dc := depClient(logger, *flDEPCK, *flDEPCS, *flDEPAT, *flDEPAS, *flDEPServerURL, *flDEPsim)
mgmtSvc := management.NewService(deviceDB, workflowDB, dc, pushSvc) mgmtSvc := management.NewService(deviceDB, workflowDB, dc, pushSvc, appsDB)
commandSvc := command.NewService(commandDB) commandSvc := command.NewService(commandDB)
checkinSvc := checkin.NewService(deviceDB, mgmtSvc, commandSvc, enrollmentProfile) checkinSvc := checkin.NewService(deviceDB, mgmtSvc, commandSvc, enrollmentProfile)
connectSvc := connect.NewService(deviceDB, commandSvc) connectSvc := connect.NewService(deviceDB, commandSvc)

View File

@@ -0,0 +1,27 @@
package management
import (
"github.com/go-kit/kit/endpoint"
"github.com/micromdm/micromdm/applications"
"golang.org/x/net/context"
)
type installedAppsRequest struct {
UUID string
}
type installedAppsResponse struct {
applications []applications.Application
Err error `json:"error,omitempty"`
}
func makeInstalledAppsEndpoint(svc Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(installedAppsRequest)
apps, err := svc.InstalledApps(req.UUID)
if err != nil {
return installedAppsResponse{Err: err}, nil
}
return installedAppsResponse{applications: *apps}, nil
}
}

View File

@@ -4,6 +4,7 @@ import (
"github.com/RobotsAndPencils/buford/payload" "github.com/RobotsAndPencils/buford/payload"
"github.com/RobotsAndPencils/buford/push" "github.com/RobotsAndPencils/buford/push"
"github.com/micromdm/dep" "github.com/micromdm/dep"
"github.com/micromdm/micromdm/applications"
"github.com/micromdm/micromdm/device" "github.com/micromdm/micromdm/device"
"github.com/micromdm/micromdm/workflow" "github.com/micromdm/micromdm/workflow"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -26,6 +27,10 @@ type Service interface {
// Devices // Devices
Devices() ([]device.Device, error) Devices() ([]device.Device, error)
Device(uuid string) (*device.Device, error) Device(uuid string) (*device.Device, error)
// Installed Applications
InstalledApps(deviceUUID string) (*[]applications.Application, error)
// AssignWorkflow assigns a workflow to a device // AssignWorkflow assigns a workflow to a device
AssignWorkflow(deviceUUID, workflowUUID string) error AssignWorkflow(deviceUUID, workflowUUID string) error
@@ -38,20 +43,22 @@ type Service interface {
} }
// NewService creates a management service // NewService creates a management service
func NewService(ds device.Datastore, ws workflow.Datastore, dc dep.Client, ps *push.Service) Service { func NewService(ds device.Datastore, ws workflow.Datastore, dc dep.Client, ps *push.Service, as applications.Datastore) Service {
return &service{ return &service{
devices: ds, devices: ds,
depClient: dc, depClient: dc,
workflows: ws, workflows: ws,
pushsvc: ps, pushsvc: ps,
applications: as,
} }
} }
type service struct { type service struct {
depClient dep.Client depClient dep.Client
devices device.Datastore devices device.Datastore
workflows workflow.Datastore workflows workflow.Datastore
pushsvc *push.Service pushsvc *push.Service
applications applications.Datastore
} }
func (svc service) Push(deviceUDID string) (string, error) { func (svc service) Push(deviceUDID string) (string, error) {
@@ -157,3 +164,12 @@ func (svc service) AssignWorkflow(deviceUUID, workflowUUID string) error {
dev.Workflow = workflowUUID dev.Workflow = workflowUUID
return svc.devices.Save("assignWorkflow", dev) return svc.devices.Save("assignWorkflow", dev)
} }
func (svc service) InstalledApps(deviceUUID string) (*[]applications.Application, error) {
apps, err := svc.applications.GetApplicationsByDeviceUUID(deviceUUID)
if err != nil {
return nil, errors.Wrap(err, "management: installed apps")
}
return apps, nil
}

View File

@@ -101,6 +101,13 @@ func ServiceHandler(ctx context.Context, svc Service, logger kitlog.Logger) http
encodeResponse, encodeResponse,
opts..., opts...,
) )
installedAppsHandler := kithttp.NewServer(
ctx,
makeInstalledAppsEndpoint(svc),
decodeInstalledAppsRequest,
encodeResponse,
opts...,
)
r := mux.NewRouter() r := mux.NewRouter()
@@ -111,6 +118,7 @@ func ServiceHandler(ctx context.Context, svc Service, logger kitlog.Logger) http
r.Handle("/management/v1/devices/{uuid}", showDeviceHandler).Methods("GET") r.Handle("/management/v1/devices/{uuid}", showDeviceHandler).Methods("GET")
r.Handle("/management/v1/devices/{uuid}", updateDeviceHandler).Methods("PATCH") r.Handle("/management/v1/devices/{uuid}", updateDeviceHandler).Methods("PATCH")
r.Handle("/management/v1/devices/{udid}/push", pushHandler).Methods("POST") r.Handle("/management/v1/devices/{udid}/push", pushHandler).Methods("POST")
r.Handle("/management/v1/devices/{uuid}/applications", installedAppsHandler).Methods("GET")
// profiles // profiles
r.Handle("/management/v1/profiles", addProfileHandler).Methods("POST") r.Handle("/management/v1/profiles", addProfileHandler).Methods("POST")
r.Handle("/management/v1/profiles", listProfilesHandler).Methods("GET") r.Handle("/management/v1/profiles", listProfilesHandler).Methods("GET")
@@ -223,6 +231,21 @@ func decodeUpdateDeviceRequest(_ context.Context, r *http.Request) (interface{},
return request, nil return request, nil
} }
func decodeInstalledAppsRequest(_ context.Context, r *http.Request) (interface{}, error) {
vars := mux.Vars(r)
deviceUUID, ok := vars["uuid"]
if !ok {
return nil, errBadRouting
}
var request = installedAppsRequest{UUID: deviceUUID}
err := json.NewDecoder(r.Body).Decode(&request)
if err == io.EOF {
return nil, errEmptyRequest
}
return request, nil
}
func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
if e, ok := response.(errorer); ok && e.error() != nil { if e, ok := response.(errorer); ok && e.error() != nil {
encodeError(ctx, e.error(), w) encodeError(ctx, e.error(), w)