fleet/server/service/endpoint_packs.go
Mike Arpaia 0122f6cb0a Add host_ids and label_ids fields to the packs API (#737)
This PR adds the `host_ids` and `label_ids` field to the packs HTTP API so that one can operate on the hosts/labels which a pack is scheduled to be executed on. This replaces (and deletes) the `/api/v1/kolide/packs/123/labels/456` API in favor of `PATCH /api/v1/packs/123` and specifying the `label_ids` field. This also allows for bulk operations.

Consider the following API examples:

## Creating a pack with a known set of hosts and labels

The key addition is the `host_ids` and `label_ids` field in both the request and the response.

### Request

```
POST /api/v1/kolide/packs
```

```json
{
	"name": "My new pack",
	"description": "The newest of the packs",
	"host_ids": [1, 2, 3],
	"label_ids": [1, 3, 5]
}
```

### Response

```json
{
	"pack": {
		"id": 123,
		"name": "My new pack",
		"description": "The newest of the packs",
		"platform": "",
		"created_by": 1,
		"disabled": false,
		"query_count": 0,
		"total_hosts_count": 5,
		"host_ids": [1, 2, 3],
		"label_ids": [1, 3, 5]
	}
}
```

## Modifying the hosts and/or labels that a pack is scheduled to execute on

### Request

```
PATCH /api/v1/kolide/packs/123
```

```json
{
	"host_ids": [1, 2, 3, 4, 5],
	"label_ids": [1, 3, 5, 7]
}
```

### Response

```json
{
	"pack": {
		"id": 123,
		"name": "My new pack",
		"description": "The newest of the packs",
		"platform": "",
		"created_by": 1,
		"disabled": false,
		"query_count": 0,
		"total_hosts_count": 5,
		"host_ids": [1, 2, 3, 4, 5],
		"label_ids": [1, 3, 5, 7]
	}
}
```

close #633
2017-01-03 10:32:06 -07:00

210 lines
5.5 KiB
Go

package service
import (
"github.com/go-kit/kit/endpoint"
"github.com/kolide/kolide-ose/server/kolide"
"golang.org/x/net/context"
)
type packResponse struct {
kolide.Pack
QueryCount uint `json:"query_count"`
TotalHostsCount uint `json:"total_hosts_count"`
HostIDs []uint `json:"host_ids"`
LabelIDs []uint `json:"label_ids"`
}
func packResponseForPack(ctx context.Context, svc kolide.Service, pack kolide.Pack) (*packResponse, error) {
queries, err := svc.GetScheduledQueriesInPack(ctx, pack.ID, kolide.ListOptions{})
if err != nil {
return nil, err
}
hosts, err := svc.ListHostsInPack(ctx, pack.ID, kolide.ListOptions{})
if err != nil {
return nil, err
}
hostIDs := make([]uint, len(hosts), len(hosts))
for _, host := range hosts {
hostIDs = append(hostIDs, host.ID)
}
labels, err := svc.ListLabelsForPack(ctx, pack.ID)
labelIDs := make([]uint, len(labels), len(labels))
for _, label := range labels {
labelIDs = append(labelIDs, label.ID)
}
if err != nil {
return nil, err
}
return &packResponse{
Pack: pack,
QueryCount: uint(len(queries)),
TotalHostsCount: uint(len(hosts)),
HostIDs: hostIDs,
LabelIDs: labelIDs,
}, nil
}
////////////////////////////////////////////////////////////////////////////////
// Get Pack
////////////////////////////////////////////////////////////////////////////////
type getPackRequest struct {
ID uint
}
type getPackResponse struct {
Pack packResponse `json:"pack,omitempty"`
Err error `json:"error,omitempty"`
}
func (r getPackResponse) error() error { return r.Err }
func makeGetPackEndpoint(svc kolide.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(getPackRequest)
pack, err := svc.GetPack(ctx, req.ID)
if err != nil {
return getPackResponse{Err: err}, nil
}
resp, err := packResponseForPack(ctx, svc, *pack)
if err != nil {
return getPackResponse{Err: err}, nil
}
return getPackResponse{
Pack: *resp,
}, nil
}
}
////////////////////////////////////////////////////////////////////////////////
// List Packs
////////////////////////////////////////////////////////////////////////////////
type listPacksRequest struct {
ListOptions kolide.ListOptions
}
type listPacksResponse struct {
Packs []packResponse `json:"packs"`
Err error `json:"error,omitempty"`
}
func (r listPacksResponse) error() error { return r.Err }
func makeListPacksEndpoint(svc kolide.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(listPacksRequest)
packs, err := svc.ListPacks(ctx, req.ListOptions)
if err != nil {
return getPackResponse{Err: err}, nil
}
resp := listPacksResponse{Packs: make([]packResponse, len(packs), len(packs))}
for _, pack := range packs {
packResp, err := packResponseForPack(ctx, svc, *pack)
if err != nil {
return getPackResponse{Err: err}, nil
}
resp.Packs = append(resp.Packs, *packResp)
}
return resp, nil
}
}
////////////////////////////////////////////////////////////////////////////////
// Create Pack
////////////////////////////////////////////////////////////////////////////////
type createPackRequest struct {
payload kolide.PackPayload
}
type createPackResponse struct {
Pack packResponse `json:"pack,omitempty"`
Err error `json:"error,omitempty"`
}
func (r createPackResponse) error() error { return r.Err }
func makeCreatePackEndpoint(svc kolide.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(createPackRequest)
pack, err := svc.NewPack(ctx, req.payload)
if err != nil {
return createPackResponse{Err: err}, nil
}
resp, err := packResponseForPack(ctx, svc, *pack)
if err != nil {
return createPackResponse{Err: err}, nil
}
return createPackResponse{
Pack: *resp,
}, nil
}
}
////////////////////////////////////////////////////////////////////////////////
// Modify Pack
////////////////////////////////////////////////////////////////////////////////
type modifyPackRequest struct {
ID uint
payload kolide.PackPayload
}
type modifyPackResponse struct {
Pack packResponse `json:"pack,omitempty"`
Err error `json:"error,omitempty"`
}
func (r modifyPackResponse) error() error { return r.Err }
func makeModifyPackEndpoint(svc kolide.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(modifyPackRequest)
pack, err := svc.ModifyPack(ctx, req.ID, req.payload)
if err != nil {
return modifyPackResponse{Err: err}, nil
}
resp, err := packResponseForPack(ctx, svc, *pack)
if err != nil {
return modifyPackResponse{Err: err}, nil
}
return modifyPackResponse{
Pack: *resp,
}, nil
}
}
////////////////////////////////////////////////////////////////////////////////
// Delete Pack
////////////////////////////////////////////////////////////////////////////////
type deletePackRequest struct {
ID uint
}
type deletePackResponse struct {
Err error `json:"error,omitempty"`
}
func (r deletePackResponse) error() error { return r.Err }
func makeDeletePackEndpoint(svc kolide.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(deletePackRequest)
err := svc.DeletePack(ctx, req.ID)
if err != nil {
return deletePackResponse{Err: err}, nil
}
return deletePackResponse{}, nil
}
}