mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 08:58:41 +00:00
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
This commit is contained in:
parent
b03e504a80
commit
0122f6cb0a
13 changed files with 408 additions and 264 deletions
|
|
@ -105,3 +105,22 @@ func testGetHostsInPack(t *testing.T, ds kolide.Datastore) {
|
|||
require.Nil(t, err)
|
||||
require.Len(t, hostsInPack, 2)
|
||||
}
|
||||
|
||||
func testAddLabelToPackTwice(t *testing.T, ds kolide.Datastore) {
|
||||
l1 := test.NewLabel(t, ds, "l1", "select 1;")
|
||||
p1 := test.NewPack(t, ds, "p1")
|
||||
|
||||
err := ds.AddLabelToPack(l1.ID, p1.ID)
|
||||
assert.Nil(t, err)
|
||||
|
||||
labels, err := ds.ListLabelsForPack(p1.ID)
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, labels, 1)
|
||||
|
||||
err = ds.AddLabelToPack(l1.ID, p1.ID)
|
||||
assert.Nil(t, err)
|
||||
|
||||
labels, err = ds.ListLabelsForPack(p1.ID)
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, labels, 1)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,4 +59,5 @@ var testFunctions = [...]func(*testing.T, kolide.Datastore){
|
|||
testOptions,
|
||||
testNewScheduledQuery,
|
||||
testOptionsToConfig,
|
||||
testAddLabelToPackTwice,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ func (d *Datastore) NewPack(pack *kolide.Pack) (*kolide.Pack, error) {
|
|||
}
|
||||
|
||||
d.mtx.Lock()
|
||||
defer d.mtx.Unlock()
|
||||
newPack.ID = d.nextID(pack)
|
||||
d.packs[newPack.ID] = &newPack
|
||||
d.mtx.Unlock()
|
||||
|
||||
pack.ID = newPack.ID
|
||||
|
||||
|
|
@ -26,33 +26,36 @@ func (d *Datastore) NewPack(pack *kolide.Pack) (*kolide.Pack, error) {
|
|||
}
|
||||
|
||||
func (d *Datastore) SavePack(pack *kolide.Pack) error {
|
||||
d.mtx.Lock()
|
||||
defer d.mtx.Unlock()
|
||||
|
||||
if _, ok := d.packs[pack.ID]; !ok {
|
||||
return notFound("Pack").WithID(pack.ID)
|
||||
}
|
||||
|
||||
d.mtx.Lock()
|
||||
d.packs[pack.ID] = pack
|
||||
d.mtx.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Datastore) DeletePack(pid uint) error {
|
||||
d.mtx.Lock()
|
||||
defer d.mtx.Unlock()
|
||||
|
||||
if _, ok := d.packs[pid]; !ok {
|
||||
return notFound("Pack").WithID(pid)
|
||||
}
|
||||
|
||||
d.mtx.Lock()
|
||||
delete(d.packs, pid)
|
||||
d.mtx.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Datastore) Pack(id uint) (*kolide.Pack, error) {
|
||||
d.mtx.Lock()
|
||||
defer d.mtx.Unlock()
|
||||
|
||||
pack, ok := d.packs[id]
|
||||
d.mtx.Unlock()
|
||||
if !ok {
|
||||
return nil, notFound("Pack").WithID(id)
|
||||
}
|
||||
|
|
@ -61,9 +64,11 @@ func (d *Datastore) Pack(id uint) (*kolide.Pack, error) {
|
|||
}
|
||||
|
||||
func (d *Datastore) ListPacks(opt kolide.ListOptions) ([]*kolide.Pack, error) {
|
||||
d.mtx.Lock()
|
||||
defer d.mtx.Unlock()
|
||||
|
||||
// We need to sort by keys to provide reliable ordering
|
||||
keys := []int{}
|
||||
d.mtx.Lock()
|
||||
for k, _ := range d.packs {
|
||||
keys = append(keys, int(k))
|
||||
}
|
||||
|
|
@ -73,7 +78,6 @@ func (d *Datastore) ListPacks(opt kolide.ListOptions) ([]*kolide.Pack, error) {
|
|||
for _, k := range keys {
|
||||
packs = append(packs, d.packs[uint(k)])
|
||||
}
|
||||
d.mtx.Unlock()
|
||||
|
||||
// Apply ordering
|
||||
if opt.OrderKey != "" {
|
||||
|
|
@ -96,7 +100,15 @@ func (d *Datastore) ListPacks(opt kolide.ListOptions) ([]*kolide.Pack, error) {
|
|||
return packs, nil
|
||||
}
|
||||
|
||||
func (d *Datastore) AddLabelToPack(lid uint, pid uint) error {
|
||||
func (d *Datastore) AddLabelToPack(lid, pid uint) error {
|
||||
d.mtx.Lock()
|
||||
defer d.mtx.Unlock()
|
||||
|
||||
for _, pt := range d.packTargets {
|
||||
if pt.PackID == pid && pt.Target.Type == kolide.TargetLabel && pt.Target.TargetID == lid {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
pt := &kolide.PackTarget{
|
||||
PackID: pid,
|
||||
Target: kolide.Target{
|
||||
|
|
@ -104,35 +116,56 @@ func (d *Datastore) AddLabelToPack(lid uint, pid uint) error {
|
|||
TargetID: lid,
|
||||
},
|
||||
}
|
||||
|
||||
d.mtx.Lock()
|
||||
pt.ID = d.nextID(pt)
|
||||
d.packTargets[pt.ID] = pt
|
||||
d.mtx.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Datastore) AddHostToPack(hid, pid uint) error {
|
||||
d.mtx.Lock()
|
||||
defer d.mtx.Unlock()
|
||||
|
||||
for _, pt := range d.packTargets {
|
||||
if pt.PackID == pid && pt.Target.Type == kolide.TargetHost && pt.Target.TargetID == hid {
|
||||
d.mtx.Unlock()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
pt := &kolide.PackTarget{
|
||||
PackID: pid,
|
||||
Target: kolide.Target{
|
||||
Type: kolide.TargetHost,
|
||||
TargetID: hid,
|
||||
},
|
||||
}
|
||||
pt.ID = d.nextID(pt)
|
||||
d.packTargets[pt.ID] = pt
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Datastore) ListLabelsForPack(pid uint) ([]*kolide.Label, error) {
|
||||
var labels []*kolide.Label
|
||||
|
||||
d.mtx.Lock()
|
||||
defer d.mtx.Unlock()
|
||||
var labels []*kolide.Label
|
||||
for _, pt := range d.packTargets {
|
||||
if pt.Type == kolide.TargetLabel && pt.PackID == pid {
|
||||
labels = append(labels, d.labels[pt.TargetID])
|
||||
}
|
||||
}
|
||||
d.mtx.Unlock()
|
||||
|
||||
return labels, nil
|
||||
}
|
||||
|
||||
func (d *Datastore) RemoveLabelFromPack(label *kolide.Label, pack *kolide.Pack) error {
|
||||
func (d *Datastore) RemoveLabelFromPack(lid, pid uint) error {
|
||||
d.mtx.Lock()
|
||||
defer d.mtx.Unlock()
|
||||
|
||||
var labelsToDelete []uint
|
||||
|
||||
d.mtx.Lock()
|
||||
for _, pt := range d.packTargets {
|
||||
if pt.Type == kolide.TargetLabel && pt.TargetID == label.ID && pt.PackID == pack.ID {
|
||||
if pt.Type == kolide.TargetLabel && pt.TargetID == lid && pt.PackID == pid {
|
||||
labelsToDelete = append(labelsToDelete, pt.ID)
|
||||
}
|
||||
}
|
||||
|
|
@ -140,16 +173,36 @@ func (d *Datastore) RemoveLabelFromPack(label *kolide.Label, pack *kolide.Pack)
|
|||
for _, id := range labelsToDelete {
|
||||
delete(d.packTargets, id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Datastore) RemoveHostFromPack(hid, pid uint) error {
|
||||
d.mtx.Lock()
|
||||
d.mtx.Unlock()
|
||||
|
||||
var hostsToDelete []uint
|
||||
|
||||
for _, pt := range d.packTargets {
|
||||
if pt.Type == kolide.TargetHost && pt.TargetID == hid && pt.PackID == pid {
|
||||
hostsToDelete = append(hostsToDelete, pt.ID)
|
||||
}
|
||||
}
|
||||
|
||||
for _, id := range hostsToDelete {
|
||||
delete(d.packTargets, id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Datastore) ListHostsInPack(pid uint, opt kolide.ListOptions) ([]*kolide.Host, error) {
|
||||
d.mtx.Lock()
|
||||
defer d.mtx.Unlock()
|
||||
|
||||
hosts := []*kolide.Host{}
|
||||
hostLookup := map[uint]bool{}
|
||||
|
||||
d.mtx.Lock()
|
||||
for _, pt := range d.packTargets {
|
||||
if pt.PackID != pid {
|
||||
continue
|
||||
|
|
@ -170,7 +223,6 @@ func (d *Datastore) ListHostsInPack(pid uint, opt kolide.ListOptions) ([]*kolide
|
|||
}
|
||||
}
|
||||
}
|
||||
d.mtx.Unlock()
|
||||
|
||||
// Apply ordering
|
||||
if opt.OrderKey != "" {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package migration
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/pressly/goose"
|
||||
)
|
||||
|
||||
func init() {
|
||||
goose.AddMigration(Up_20161230162221, Down_20161230162221)
|
||||
}
|
||||
|
||||
func Up_20161230162221(tx *sql.Tx) error {
|
||||
_, err := tx.Exec(
|
||||
"ALTER TABLE `pack_targets` " +
|
||||
"ADD CONSTRAINT `constraint_pack_target_unique` " +
|
||||
"UNIQUE (`pack_id`, `target_id`, `type`);",
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func Down_20161230162221(tx *sql.Tx) error {
|
||||
_, err := tx.Exec(
|
||||
"ALTER TABLE `pack_targets` " +
|
||||
"DROP INDEX `constraint_pack_target_unique`;",
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
|
@ -82,6 +82,7 @@ func (d *Datastore) AddLabelToPack(lid uint, pid uint) error {
|
|||
sql := `
|
||||
INSERT INTO pack_targets ( pack_id, type, target_id )
|
||||
VALUES ( ?, ?, ? )
|
||||
ON DUPLICATE KEY UPDATE id=id
|
||||
`
|
||||
_, err := d.db.Exec(sql, pid, kolide.TargetLabel, lid)
|
||||
if err != nil {
|
||||
|
|
@ -91,6 +92,21 @@ func (d *Datastore) AddLabelToPack(lid uint, pid uint) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// AddHostToPack associates a kolide.Host with a kolide.Pack
|
||||
func (d *Datastore) AddHostToPack(hid, pid uint) error {
|
||||
sql := `
|
||||
INSERT INTO pack_targets ( pack_id, type, target_id )
|
||||
VALUES ( ?, ?, ? )
|
||||
ON DUPLICATE KEY UPDATE id=id
|
||||
`
|
||||
_, err := d.db.Exec(sql, pid, kolide.TargetHost, hid)
|
||||
if err != nil {
|
||||
return errors.DatabaseError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListLabelsForPack will return a list of kolide.Label records associated with kolide.Pack
|
||||
func (d *Datastore) ListLabelsForPack(pid uint) ([]*kolide.Label, error) {
|
||||
sql := `
|
||||
|
|
@ -123,18 +139,33 @@ func (d *Datastore) ListLabelsForPack(pid uint) ([]*kolide.Label, error) {
|
|||
|
||||
// RemoreLabelFromPack will remove the association between a kolide.Label and
|
||||
// a kolide.Pack
|
||||
func (d *Datastore) RemoveLabelFromPack(label *kolide.Label, pack *kolide.Pack) error {
|
||||
func (d *Datastore) RemoveLabelFromPack(lid, pid uint) error {
|
||||
sql := `
|
||||
DELETE FROM pack_labels
|
||||
WHERE target_id = ? AND pack_id = ?
|
||||
DELETE FROM pack_targets
|
||||
WHERE target_id = ? AND pack_id = ? AND type = ?
|
||||
`
|
||||
if _, err := d.db.Exec(sql, label.ID, pack.ID); err != nil {
|
||||
if _, err := d.db.Exec(sql, lid, pid, kolide.TargetLabel); err != nil {
|
||||
return errors.DatabaseError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveHostFromPack will remove the association between a kolide.Host and a
|
||||
// kolide.Pack
|
||||
func (d *Datastore) RemoveHostFromPack(hid, pid uint) error {
|
||||
sql := `
|
||||
DELETE FROM pack_targets
|
||||
WHERE target_id = ? AND pack_id = ? AND type = ?
|
||||
`
|
||||
if _, err := d.db.Exec(sql, hid, pid, kolide.TargetHost); err != nil {
|
||||
return errors.DatabaseError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (d *Datastore) ListHostsInPack(pid uint, opt kolide.ListOptions) ([]*kolide.Host, error) {
|
||||
sql := `
|
||||
SELECT DISTINCT h.*
|
||||
|
|
|
|||
|
|
@ -13,24 +13,32 @@ type PackStore interface {
|
|||
ListPacks(opt ListOptions) ([]*Pack, error)
|
||||
|
||||
// Modifying the labels for packs
|
||||
AddLabelToPack(lid uint, pid uint) error
|
||||
AddLabelToPack(lid, pid uint) error
|
||||
RemoveLabelFromPack(lid, pid uint) error
|
||||
ListLabelsForPack(pid uint) ([]*Label, error)
|
||||
RemoveLabelFromPack(label *Label, pack *Pack) error
|
||||
|
||||
// Modifying the hosts for packs
|
||||
AddHostToPack(hid uint, pid uint) error
|
||||
RemoveHostFromPack(hid uint, pid uint) error
|
||||
ListHostsInPack(pid uint, opt ListOptions) ([]*Host, error)
|
||||
}
|
||||
|
||||
type PackService interface {
|
||||
// Pack methods
|
||||
ListPacks(ctx context.Context, opt ListOptions) ([]*Pack, error)
|
||||
GetPack(ctx context.Context, id uint) (*Pack, error)
|
||||
NewPack(ctx context.Context, p PackPayload) (*Pack, error)
|
||||
ModifyPack(ctx context.Context, id uint, p PackPayload) (*Pack, error)
|
||||
DeletePack(ctx context.Context, id uint) error
|
||||
|
||||
// Modifying the labels for packs
|
||||
AddLabelToPack(ctx context.Context, lid, pid uint) error
|
||||
ListLabelsForPack(ctx context.Context, pid uint) ([]*Label, error)
|
||||
RemoveLabelFromPack(ctx context.Context, lid, pid uint) error
|
||||
ListLabelsForPack(ctx context.Context, pid uint) ([]*Label, error)
|
||||
|
||||
// Modifying the hosts for packs
|
||||
AddHostToPack(ctx context.Context, hid, pid uint) error
|
||||
RemoveHostFromPack(ctx context.Context, hid, pid uint) error
|
||||
ListPacksForHost(ctx context.Context, hid uint) ([]*Pack, error)
|
||||
ListHostsInPack(ctx context.Context, pid uint, opt ListOptions) ([]*Host, error)
|
||||
}
|
||||
|
|
@ -47,10 +55,12 @@ type Pack struct {
|
|||
}
|
||||
|
||||
type PackPayload struct {
|
||||
Name *string
|
||||
Description *string
|
||||
Platform *string
|
||||
Disabled *bool
|
||||
Name *string `json:"name"`
|
||||
Description *string `json:"description"`
|
||||
Platform *string `json:"platform"`
|
||||
Disabled *bool `json:"disabled"`
|
||||
HostIDs *[]uint `json:"host_ids"`
|
||||
LabelIDs *[]uint `json:"label_ids"`
|
||||
}
|
||||
|
||||
type PackTarget struct {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,44 @@ import (
|
|||
"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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -14,12 +52,6 @@ type getPackRequest struct {
|
|||
ID uint
|
||||
}
|
||||
|
||||
type packResponse struct {
|
||||
kolide.Pack
|
||||
QueryCount uint `json:"query_count"`
|
||||
TotalHostsCount uint `json:"total_hosts_count"`
|
||||
}
|
||||
|
||||
type getPackResponse struct {
|
||||
Pack packResponse `json:"pack,omitempty"`
|
||||
Err error `json:"error,omitempty"`
|
||||
|
|
@ -36,22 +68,13 @@ func makeGetPackEndpoint(svc kolide.Service) endpoint.Endpoint {
|
|||
return getPackResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
queries, err := svc.GetScheduledQueriesInPack(ctx, pack.ID, kolide.ListOptions{})
|
||||
if err != nil {
|
||||
return getPackResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
hosts, err := svc.ListHostsInPack(ctx, pack.ID, kolide.ListOptions{})
|
||||
resp, err := packResponseForPack(ctx, svc, *pack)
|
||||
if err != nil {
|
||||
return getPackResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
return getPackResponse{
|
||||
Pack: packResponse{
|
||||
Pack: *pack,
|
||||
QueryCount: uint(len(queries)),
|
||||
TotalHostsCount: uint(len(hosts)),
|
||||
},
|
||||
Pack: *resp,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -79,21 +102,13 @@ func makeListPacksEndpoint(svc kolide.Service) endpoint.Endpoint {
|
|||
return getPackResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
resp := listPacksResponse{Packs: []packResponse{}}
|
||||
resp := listPacksResponse{Packs: make([]packResponse, len(packs), len(packs))}
|
||||
for _, pack := range packs {
|
||||
queries, err := svc.GetScheduledQueriesInPack(ctx, pack.ID, kolide.ListOptions{})
|
||||
packResp, err := packResponseForPack(ctx, svc, *pack)
|
||||
if err != nil {
|
||||
return getPackResponse{Err: err}, nil
|
||||
}
|
||||
hosts, err := svc.ListHostsInPack(ctx, pack.ID, kolide.ListOptions{})
|
||||
if err != nil {
|
||||
return getPackResponse{Err: err}, nil
|
||||
}
|
||||
resp.Packs = append(resp.Packs, packResponse{
|
||||
Pack: *pack,
|
||||
QueryCount: uint(len(queries)),
|
||||
TotalHostsCount: uint(len(hosts)),
|
||||
})
|
||||
resp.Packs = append(resp.Packs, *packResp)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
|
@ -122,23 +137,13 @@ func makeCreatePackEndpoint(svc kolide.Service) endpoint.Endpoint {
|
|||
return createPackResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
queries, err := svc.GetScheduledQueriesInPack(ctx, pack.ID, kolide.ListOptions{})
|
||||
if err != nil {
|
||||
return createPackResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
hosts, err := svc.ListHostsInPack(ctx, pack.ID, kolide.ListOptions{})
|
||||
resp, err := packResponseForPack(ctx, svc, *pack)
|
||||
if err != nil {
|
||||
return createPackResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
return createPackResponse{
|
||||
Pack: packResponse{
|
||||
Pack: *pack,
|
||||
QueryCount: uint(len(queries)),
|
||||
TotalHostsCount: uint(len(hosts)),
|
||||
},
|
||||
Err: nil,
|
||||
Pack: *resp,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -167,23 +172,13 @@ func makeModifyPackEndpoint(svc kolide.Service) endpoint.Endpoint {
|
|||
return modifyPackResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
queries, err := svc.GetScheduledQueriesInPack(ctx, pack.ID, kolide.ListOptions{})
|
||||
if err != nil {
|
||||
return modifyPackResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
hosts, err := svc.ListHostsInPack(ctx, pack.ID, kolide.ListOptions{})
|
||||
resp, err := packResponseForPack(ctx, svc, *pack)
|
||||
if err != nil {
|
||||
return modifyPackResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
return modifyPackResponse{
|
||||
Pack: packResponse{
|
||||
Pack: *pack,
|
||||
QueryCount: uint(len(queries)),
|
||||
TotalHostsCount: uint(len(hosts)),
|
||||
},
|
||||
Err: nil,
|
||||
Pack: *resp,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
|
@ -212,86 +207,3 @@ func makeDeletePackEndpoint(svc kolide.Service) endpoint.Endpoint {
|
|||
return deletePackResponse{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Add Label To Pack
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type addLabelToPackRequest struct {
|
||||
PackID uint
|
||||
LabelID uint
|
||||
}
|
||||
|
||||
type addLabelToPackResponse struct {
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r addLabelToPackResponse) error() error { return r.Err }
|
||||
|
||||
func makeAddLabelToPackEndpoint(svc kolide.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(addLabelToPackRequest)
|
||||
err := svc.AddLabelToPack(ctx, req.LabelID, req.PackID)
|
||||
if err != nil {
|
||||
return addLabelToPackResponse{Err: err}, nil
|
||||
}
|
||||
return addLabelToPackResponse{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Get Labels For Pack
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type getLabelsForPackRequest struct {
|
||||
PackID uint
|
||||
}
|
||||
|
||||
type getLabelsForPackResponse struct {
|
||||
Labels []kolide.Label `json:"labels"`
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r getLabelsForPackResponse) error() error { return r.Err }
|
||||
|
||||
func makeGetLabelsForPackEndpoint(svc kolide.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(getLabelsForPackRequest)
|
||||
labels, err := svc.ListLabelsForPack(ctx, req.PackID)
|
||||
if err != nil {
|
||||
return getLabelsForPackResponse{Err: err}, nil
|
||||
}
|
||||
|
||||
var resp getLabelsForPackResponse
|
||||
for _, label := range labels {
|
||||
resp.Labels = append(resp.Labels, *label)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Delete Label From Pack
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type deleteLabelFromPackRequest struct {
|
||||
LabelID uint
|
||||
PackID uint
|
||||
}
|
||||
|
||||
type deleteLabelFromPackResponse struct {
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r deleteLabelFromPackResponse) error() error { return r.Err }
|
||||
|
||||
func makeDeleteLabelFromPackEndpoint(svc kolide.Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(deleteLabelFromPackRequest)
|
||||
err := svc.RemoveLabelFromPack(ctx, req.LabelID, req.PackID)
|
||||
if err != nil {
|
||||
return deleteLabelFromPackResponse{Err: err}, nil
|
||||
}
|
||||
return deleteLabelFromPackResponse{}, nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,9 +60,6 @@ type KolideEndpoints struct {
|
|||
ListLabels endpoint.Endpoint
|
||||
CreateLabel endpoint.Endpoint
|
||||
DeleteLabel endpoint.Endpoint
|
||||
AddLabelToPack endpoint.Endpoint
|
||||
GetLabelsForPack endpoint.Endpoint
|
||||
DeleteLabelFromPack endpoint.Endpoint
|
||||
GetHost endpoint.Endpoint
|
||||
DeleteHost endpoint.Endpoint
|
||||
ListHosts endpoint.Endpoint
|
||||
|
|
@ -126,9 +123,6 @@ func MakeKolideServerEndpoints(svc kolide.Service, jwtKey string) KolideEndpoint
|
|||
ListLabels: authenticatedUser(jwtKey, svc, makeListLabelsEndpoint(svc)),
|
||||
CreateLabel: authenticatedUser(jwtKey, svc, makeCreateLabelEndpoint(svc)),
|
||||
DeleteLabel: authenticatedUser(jwtKey, svc, makeDeleteLabelEndpoint(svc)),
|
||||
AddLabelToPack: authenticatedUser(jwtKey, svc, makeAddLabelToPackEndpoint(svc)),
|
||||
GetLabelsForPack: authenticatedUser(jwtKey, svc, makeGetLabelsForPackEndpoint(svc)),
|
||||
DeleteLabelFromPack: authenticatedUser(jwtKey, svc, makeDeleteLabelFromPackEndpoint(svc)),
|
||||
SearchTargets: authenticatedUser(jwtKey, svc, makeSearchTargetsEndpoint(svc)),
|
||||
GetOptions: authenticatedUser(jwtKey, svc, mustBeAdmin(makeGetOptionsEndpoint(svc))),
|
||||
ModifyOptions: authenticatedUser(jwtKey, svc, mustBeAdmin(makeModifyOptionsEndpoint(svc))),
|
||||
|
|
@ -189,9 +183,6 @@ type kolideHandlers struct {
|
|||
ListLabels http.Handler
|
||||
CreateLabel http.Handler
|
||||
DeleteLabel http.Handler
|
||||
AddLabelToPack http.Handler
|
||||
GetLabelsForPack http.Handler
|
||||
DeleteLabelFromPack http.Handler
|
||||
GetHost http.Handler
|
||||
DeleteHost http.Handler
|
||||
ListHosts http.Handler
|
||||
|
|
@ -251,9 +242,6 @@ func makeKolideKitHandlers(ctx context.Context, e KolideEndpoints, opts []kithtt
|
|||
ListLabels: newServer(e.ListLabels, decodeListLabelsRequest),
|
||||
CreateLabel: newServer(e.CreateLabel, decodeCreateLabelRequest),
|
||||
DeleteLabel: newServer(e.DeleteLabel, decodeDeleteLabelRequest),
|
||||
AddLabelToPack: newServer(e.AddLabelToPack, decodeAddLabelToPackRequest),
|
||||
GetLabelsForPack: newServer(e.GetLabelsForPack, decodeGetLabelsForPackRequest),
|
||||
DeleteLabelFromPack: newServer(e.DeleteLabelFromPack, decodeDeleteLabelFromPackRequest),
|
||||
GetHost: newServer(e.GetHost, decodeGetHostRequest),
|
||||
DeleteHost: newServer(e.DeleteHost, decodeDeleteHostRequest),
|
||||
ListHosts: newServer(e.ListHosts, decodeListHostsRequest),
|
||||
|
|
@ -347,9 +335,6 @@ func attachKolideAPIRoutes(r *mux.Router, h *kolideHandlers) {
|
|||
r.Handle("/api/v1/kolide/labels", h.ListLabels).Methods("GET").Name("list_labels")
|
||||
r.Handle("/api/v1/kolide/labels", h.CreateLabel).Methods("POST").Name("create_label")
|
||||
r.Handle("/api/v1/kolide/labels/{id}", h.DeleteLabel).Methods("DELETE").Name("delete_label")
|
||||
r.Handle("/api/v1/kolide/packs/{pid}/labels/{lid}", h.AddLabelToPack).Methods("POST").Name("add_label_to_pack")
|
||||
r.Handle("/api/v1/kolide/packs/{pid}/labels", h.GetLabelsForPack).Methods("GET").Name("get_labels_for_pack")
|
||||
r.Handle("/api/v1/kolide/packs/{pid}/labels/{lid}", h.DeleteLabelFromPack).Methods("DELETE").Name("delete_label_from_pack")
|
||||
|
||||
r.Handle("/api/v1/kolide/hosts", h.ListHosts).Methods("GET").Name("list_hosts")
|
||||
r.Handle("/api/v1/kolide/hosts/{id}", h.GetHost).Methods("GET").Name("get_host")
|
||||
|
|
|
|||
|
|
@ -184,18 +184,6 @@ func TestAPIRoutes(t *testing.T) {
|
|||
verb: "DELETE",
|
||||
uri: "/api/v1/kolide/labels/1",
|
||||
},
|
||||
{
|
||||
verb: "POST",
|
||||
uri: "/api/v1/kolide/packs/1/labels/2",
|
||||
},
|
||||
{
|
||||
verb: "GET",
|
||||
uri: "/api/v1/kolide/packs/1/labels",
|
||||
},
|
||||
{
|
||||
verb: "DELETE",
|
||||
uri: "/api/v1/kolide/packs/1/labels/2",
|
||||
},
|
||||
{
|
||||
verb: "GET",
|
||||
uri: "/api/v1/kolide/hosts/1",
|
||||
|
|
|
|||
|
|
@ -44,6 +44,25 @@ func (svc service) NewPack(ctx context.Context, p kolide.PackPayload) (*kolide.P
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if p.HostIDs != nil {
|
||||
for _, hostID := range *p.HostIDs {
|
||||
err = svc.AddHostToPack(ctx, hostID, pack.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if p.LabelIDs != nil {
|
||||
for _, labelID := range *p.LabelIDs {
|
||||
err = svc.AddLabelToPack(ctx, labelID, pack.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &pack, nil
|
||||
}
|
||||
|
||||
|
|
@ -74,6 +93,102 @@ func (svc service) ModifyPack(ctx context.Context, id uint, p kolide.PackPayload
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// we must determine what hosts are attached to this pack. then, given
|
||||
// our new set of host_ids, we will mutate the database to reflect the
|
||||
// desired state.
|
||||
if p.HostIDs != nil {
|
||||
|
||||
// first, let's retrieve the total set of hosts
|
||||
hosts, err := svc.ListHostsInPack(ctx, pack.ID, kolide.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// it will be efficient to create a data structure with constant time
|
||||
// lookups to determine whether or not a host is already added
|
||||
existingHosts := map[uint]bool{}
|
||||
for _, host := range hosts {
|
||||
existingHosts[host.ID] = true
|
||||
}
|
||||
|
||||
// we will also make a constant time lookup map for the desired set of
|
||||
// hosts as well.
|
||||
desiredHosts := map[uint]bool{}
|
||||
for _, hostID := range *p.HostIDs {
|
||||
desiredHosts[hostID] = true
|
||||
}
|
||||
|
||||
// if the request declares a host ID but the host is not already
|
||||
// associated with the pack, we add it
|
||||
for _, hostID := range *p.HostIDs {
|
||||
if !existingHosts[hostID] {
|
||||
err = svc.AddHostToPack(ctx, hostID, pack.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the request does not declare the ID of a host which currently
|
||||
// exists, we delete the existing relationship
|
||||
for hostID := range existingHosts {
|
||||
if !desiredHosts[hostID] {
|
||||
err = svc.RemoveHostFromPack(ctx, hostID, pack.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we must determine what labels are attached to this pack. then, given
|
||||
// our new set of label_ids, we will mutate the database to reflect the
|
||||
// desired state.
|
||||
if p.LabelIDs != nil {
|
||||
|
||||
// first, let's retrieve the total set of labels
|
||||
labels, err := svc.ListLabelsForPack(ctx, pack.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// it will be efficient to create a data structure with constant time
|
||||
// lookups to determine whether or not a label is already added
|
||||
existingLabels := map[uint]bool{}
|
||||
for _, label := range labels {
|
||||
existingLabels[label.ID] = true
|
||||
}
|
||||
|
||||
// we will also make a constant time lookup map for the desired set of
|
||||
// labels as well.
|
||||
desiredLabels := map[uint]bool{}
|
||||
for _, labelID := range *p.LabelIDs {
|
||||
desiredLabels[labelID] = true
|
||||
}
|
||||
|
||||
// if the request declares a label ID but the label is not already
|
||||
// associated with the pack, we add it
|
||||
for _, labelID := range *p.LabelIDs {
|
||||
if !existingLabels[labelID] {
|
||||
err = svc.AddLabelToPack(ctx, labelID, pack.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the request does not declare the ID of a label which currently
|
||||
// exists, we delete the existing relationship
|
||||
for labelID := range existingLabels {
|
||||
if !desiredLabels[labelID] {
|
||||
err = svc.RemoveLabelFromPack(ctx, labelID, pack.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pack, err
|
||||
}
|
||||
|
||||
|
|
@ -86,31 +201,19 @@ func (svc service) AddLabelToPack(ctx context.Context, lid, pid uint) error {
|
|||
}
|
||||
|
||||
func (svc service) ListLabelsForPack(ctx context.Context, pid uint) ([]*kolide.Label, error) {
|
||||
labels, err := svc.ds.ListLabelsForPack(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return labels, nil
|
||||
return svc.ds.ListLabelsForPack(pid)
|
||||
}
|
||||
|
||||
func (svc service) RemoveLabelFromPack(ctx context.Context, lid, pid uint) error {
|
||||
pack, err := svc.ds.Pack(pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return svc.ds.RemoveLabelFromPack(lid, pid)
|
||||
}
|
||||
|
||||
label, err := svc.ds.Label(lid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (svc service) AddHostToPack(ctx context.Context, hid, pid uint) error {
|
||||
return svc.ds.AddHostToPack(hid, pid)
|
||||
}
|
||||
|
||||
err = svc.ds.RemoveLabelFromPack(label, pack)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
func (svc service) RemoveHostFromPack(ctx context.Context, hid, pid uint) error {
|
||||
return svc.ds.RemoveHostFromPack(hid, pid)
|
||||
}
|
||||
|
||||
func (svc service) ListHostsInPack(ctx context.Context, pid uint, opt kolide.ListOptions) ([]*kolide.Host, error) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/kolide/kolide-ose/server/datastore/inmem"
|
||||
"github.com/kolide/kolide-ose/server/kolide"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
|
|
@ -64,16 +65,31 @@ func TestNewPack(t *testing.T) {
|
|||
|
||||
ctx := context.Background()
|
||||
|
||||
name := "foo"
|
||||
_, err = svc.NewPack(ctx, kolide.PackPayload{
|
||||
Name: &name,
|
||||
labelName := "label"
|
||||
labelQuery := "select 1"
|
||||
label, err := svc.NewLabel(ctx, kolide.LabelPayload{
|
||||
Name: &labelName,
|
||||
Query: &labelQuery,
|
||||
})
|
||||
|
||||
packName := "foo"
|
||||
packLabelIDs := []uint{label.ID}
|
||||
pack, err := svc.NewPack(ctx, kolide.PackPayload{
|
||||
Name: &packName,
|
||||
LabelIDs: &packLabelIDs,
|
||||
})
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
queries, err := ds.ListPacks(kolide.ListOptions{})
|
||||
packs, err := ds.ListPacks(kolide.ListOptions{})
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, queries, 1)
|
||||
require.Len(t, packs, 1)
|
||||
assert.Equal(t, pack.ID, packs[0].ID)
|
||||
|
||||
labels, err := ds.ListLabelsForPack(pack.ID)
|
||||
assert.Nil(t, err)
|
||||
require.Len(t, labels, 1)
|
||||
assert.Equal(t, label.ID, labels[0].ID)
|
||||
}
|
||||
|
||||
func TestModifyPack(t *testing.T) {
|
||||
|
|
@ -85,21 +101,48 @@ func TestModifyPack(t *testing.T) {
|
|||
|
||||
ctx := context.Background()
|
||||
|
||||
label := &kolide.Label{
|
||||
Name: "label",
|
||||
Query: "select 1",
|
||||
}
|
||||
label, err = ds.NewLabel(label)
|
||||
assert.Nil(t, err)
|
||||
assert.NotZero(t, label.ID)
|
||||
|
||||
pack := &kolide.Pack{
|
||||
Name: "foo",
|
||||
}
|
||||
_, err = ds.NewPack(pack)
|
||||
pack, err = ds.NewPack(pack)
|
||||
assert.Nil(t, err)
|
||||
assert.NotZero(t, pack.ID)
|
||||
|
||||
newName := "bar"
|
||||
labelIDs := []uint{label.ID}
|
||||
packVerify, err := svc.ModifyPack(ctx, pack.ID, kolide.PackPayload{
|
||||
Name: &newName,
|
||||
Name: &newName,
|
||||
LabelIDs: &labelIDs,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, pack.ID, packVerify.ID)
|
||||
assert.Equal(t, "bar", packVerify.Name)
|
||||
|
||||
labels, err := ds.ListLabelsForPack(pack.ID)
|
||||
assert.Nil(t, err)
|
||||
require.Len(t, labels, 1)
|
||||
assert.Equal(t, label.ID, labels[0].ID)
|
||||
|
||||
newLabelIDs := []uint{}
|
||||
packVerify2, err := svc.ModifyPack(ctx, pack.ID, kolide.PackPayload{
|
||||
LabelIDs: &newLabelIDs,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, pack.ID, packVerify2.ID)
|
||||
|
||||
labels, err = ds.ListLabelsForPack(pack.ID)
|
||||
assert.Nil(t, err)
|
||||
require.Len(t, labels, 0)
|
||||
}
|
||||
|
||||
func TestDeletePack(t *testing.T) {
|
||||
|
|
@ -124,5 +167,4 @@ func TestDeletePack(t *testing.T) {
|
|||
queries, err := ds.ListPacks(kolide.ListOptions{})
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, queries, 0)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,43 +56,3 @@ func decodeListPacksRequest(ctx context.Context, r *http.Request) (interface{},
|
|||
}
|
||||
return listPacksRequest{ListOptions: opt}, nil
|
||||
}
|
||||
|
||||
func decodeAddLabelToPackRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
lid, err := idFromRequest(r, "lid")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pid, err := idFromRequest(r, "pid")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addLabelToPackRequest{
|
||||
PackID: pid,
|
||||
LabelID: lid,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func decodeGetLabelsForPackRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
pid, err := idFromRequest(r, "pid")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var req getLabelsForPackRequest
|
||||
req.PackID = pid
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func decodeDeleteLabelFromPackRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
||||
lid, err := idFromRequest(r, "lid")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pid, err := idFromRequest(r, "pid")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var req deleteLabelFromPackRequest
|
||||
req.PackID = pid
|
||||
req.LabelID = lid
|
||||
return req, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
|
|
@ -20,12 +21,18 @@ func TestDecodeCreatePackRequest(t *testing.T) {
|
|||
params := r.(createPackRequest)
|
||||
assert.Equal(t, "foo", *params.payload.Name)
|
||||
assert.Equal(t, "bar", *params.payload.Description)
|
||||
require.NotNil(t, params.payload.HostIDs)
|
||||
assert.Len(t, *params.payload.HostIDs, 3)
|
||||
require.NotNil(t, params.payload.LabelIDs)
|
||||
assert.Len(t, *params.payload.LabelIDs, 2)
|
||||
}).Methods("POST")
|
||||
|
||||
var body bytes.Buffer
|
||||
body.Write([]byte(`{
|
||||
"name": "foo",
|
||||
"description": "bar"
|
||||
"description": "bar",
|
||||
"host_ids": [1, 2, 3],
|
||||
"label_ids": [1, 5]
|
||||
}`))
|
||||
|
||||
router.ServeHTTP(
|
||||
|
|
@ -41,15 +48,21 @@ func TestDecodeModifyPackRequest(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
|
||||
params := r.(modifyPackRequest)
|
||||
assert.Equal(t, uint(1), params.ID)
|
||||
assert.Equal(t, "foo", *params.payload.Name)
|
||||
assert.Equal(t, "bar", *params.payload.Description)
|
||||
assert.Equal(t, uint(1), params.ID)
|
||||
require.NotNil(t, params.payload.HostIDs)
|
||||
assert.Len(t, *params.payload.HostIDs, 3)
|
||||
require.NotNil(t, params.payload.LabelIDs)
|
||||
assert.Len(t, *params.payload.LabelIDs, 2)
|
||||
}).Methods("PATCH")
|
||||
|
||||
var body bytes.Buffer
|
||||
body.Write([]byte(`{
|
||||
"name": "foo",
|
||||
"description": "bar"
|
||||
"description": "bar",
|
||||
"host_ids": [1, 2, 3],
|
||||
"label_ids": [1, 5]
|
||||
}`))
|
||||
|
||||
router.ServeHTTP(
|
||||
|
|
|
|||
Loading…
Reference in a new issue