2018-05-14 16:26:38 +00:00
|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
2018-09-10 19:26:08 +00:00
|
|
|
"fmt"
|
2018-05-14 16:26:38 +00:00
|
|
|
"net/http"
|
2018-05-16 18:59:35 +00:00
|
|
|
"path"
|
2018-10-22 22:18:34 +00:00
|
|
|
"time"
|
2018-05-14 16:26:38 +00:00
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
"github.com/influxdata/influxdb"
|
2018-05-14 16:26:38 +00:00
|
|
|
"github.com/julienschmidt/httprouter"
|
2018-12-20 16:07:46 +00:00
|
|
|
"go.uber.org/zap"
|
2018-05-14 16:26:38 +00:00
|
|
|
)
|
|
|
|
|
2019-01-16 03:11:22 +00:00
|
|
|
// BucketBackend is all services and associated parameters required to construct
|
|
|
|
// the BucketHandler.
|
|
|
|
type BucketBackend struct {
|
|
|
|
Logger *zap.Logger
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
BucketService influxdb.BucketService
|
|
|
|
BucketOperationLogService influxdb.BucketOperationLogService
|
|
|
|
UserResourceMappingService influxdb.UserResourceMappingService
|
|
|
|
LabelService influxdb.LabelService
|
|
|
|
UserService influxdb.UserService
|
|
|
|
OrganizationService influxdb.OrganizationService
|
2019-01-16 03:11:22 +00:00
|
|
|
}
|
|
|
|
|
2019-01-16 16:03:38 +00:00
|
|
|
// NewBucketBackend returns a new instance of BucketBackend.
|
2019-01-16 03:11:22 +00:00
|
|
|
func NewBucketBackend(b *APIBackend) *BucketBackend {
|
|
|
|
return &BucketBackend{
|
|
|
|
Logger: b.Logger.With(zap.String("handler", "bucket")),
|
|
|
|
|
|
|
|
BucketService: b.BucketService,
|
|
|
|
BucketOperationLogService: b.BucketOperationLogService,
|
|
|
|
UserResourceMappingService: b.UserResourceMappingService,
|
|
|
|
LabelService: b.LabelService,
|
|
|
|
UserService: b.UserService,
|
|
|
|
OrganizationService: b.OrganizationService,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-14 16:26:38 +00:00
|
|
|
// BucketHandler represents an HTTP API handler for buckets.
|
|
|
|
type BucketHandler struct {
|
|
|
|
*httprouter.Router
|
|
|
|
|
2018-12-20 16:07:46 +00:00
|
|
|
Logger *zap.Logger
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
BucketService influxdb.BucketService
|
|
|
|
BucketOperationLogService influxdb.BucketOperationLogService
|
|
|
|
UserResourceMappingService influxdb.UserResourceMappingService
|
|
|
|
LabelService influxdb.LabelService
|
|
|
|
UserService influxdb.UserService
|
|
|
|
OrganizationService influxdb.OrganizationService
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-10-01 09:19:44 +00:00
|
|
|
const (
|
2019-01-18 19:03:36 +00:00
|
|
|
bucketsPath = "/api/v2/buckets"
|
|
|
|
bucketsIDPath = "/api/v2/buckets/:id"
|
|
|
|
bucketsIDLogPath = "/api/v2/buckets/:id/log"
|
|
|
|
bucketsIDMembersPath = "/api/v2/buckets/:id/members"
|
|
|
|
bucketsIDMembersIDPath = "/api/v2/buckets/:id/members/:userID"
|
|
|
|
bucketsIDOwnersPath = "/api/v2/buckets/:id/owners"
|
|
|
|
bucketsIDOwnersIDPath = "/api/v2/buckets/:id/owners/:userID"
|
|
|
|
bucketsIDLabelsPath = "/api/v2/buckets/:id/labels"
|
|
|
|
bucketsIDLabelsIDPath = "/api/v2/buckets/:id/labels/:lid"
|
2018-10-01 09:19:44 +00:00
|
|
|
)
|
|
|
|
|
2018-05-14 16:26:38 +00:00
|
|
|
// NewBucketHandler returns a new instance of BucketHandler.
|
2019-01-16 03:11:22 +00:00
|
|
|
func NewBucketHandler(b *BucketBackend) *BucketHandler {
|
2018-05-14 16:26:38 +00:00
|
|
|
h := &BucketHandler{
|
2019-01-08 00:37:16 +00:00
|
|
|
Router: NewRouter(),
|
2019-01-16 03:11:22 +00:00
|
|
|
Logger: b.Logger,
|
2019-01-08 00:37:16 +00:00
|
|
|
|
2019-01-16 03:11:22 +00:00
|
|
|
BucketService: b.BucketService,
|
|
|
|
BucketOperationLogService: b.BucketOperationLogService,
|
|
|
|
UserResourceMappingService: b.UserResourceMappingService,
|
|
|
|
LabelService: b.LabelService,
|
|
|
|
UserService: b.UserService,
|
|
|
|
OrganizationService: b.OrganizationService,
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-10-01 09:19:44 +00:00
|
|
|
h.HandlerFunc("POST", bucketsPath, h.handlePostBucket)
|
|
|
|
h.HandlerFunc("GET", bucketsPath, h.handleGetBuckets)
|
|
|
|
h.HandlerFunc("GET", bucketsIDPath, h.handleGetBucket)
|
2018-11-02 18:21:14 +00:00
|
|
|
h.HandlerFunc("GET", bucketsIDLogPath, h.handleGetBucketLog)
|
2018-10-01 09:19:44 +00:00
|
|
|
h.HandlerFunc("PATCH", bucketsIDPath, h.handlePatchBucket)
|
|
|
|
h.HandlerFunc("DELETE", bucketsIDPath, h.handleDeleteBucket)
|
|
|
|
|
2019-01-17 03:35:54 +00:00
|
|
|
memberBackend := MemberBackend{
|
|
|
|
Logger: b.Logger.With(zap.String("handler", "member")),
|
2019-02-07 23:19:30 +00:00
|
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
|
|
UserType: influxdb.Member,
|
2019-01-17 03:35:54 +00:00
|
|
|
UserResourceMappingService: b.UserResourceMappingService,
|
|
|
|
UserService: b.UserService,
|
|
|
|
}
|
|
|
|
h.HandlerFunc("POST", bucketsIDMembersPath, newPostMemberHandler(memberBackend))
|
|
|
|
h.HandlerFunc("GET", bucketsIDMembersPath, newGetMembersHandler(memberBackend))
|
|
|
|
h.HandlerFunc("DELETE", bucketsIDMembersIDPath, newDeleteMemberHandler(memberBackend))
|
|
|
|
|
|
|
|
ownerBackend := MemberBackend{
|
|
|
|
Logger: b.Logger.With(zap.String("handler", "member")),
|
2019-02-07 23:19:30 +00:00
|
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
|
|
UserType: influxdb.Owner,
|
2019-01-17 03:35:54 +00:00
|
|
|
UserResourceMappingService: b.UserResourceMappingService,
|
|
|
|
UserService: b.UserService,
|
|
|
|
}
|
|
|
|
h.HandlerFunc("POST", bucketsIDOwnersPath, newPostMemberHandler(ownerBackend))
|
|
|
|
h.HandlerFunc("GET", bucketsIDOwnersPath, newGetMembersHandler(ownerBackend))
|
|
|
|
h.HandlerFunc("DELETE", bucketsIDOwnersIDPath, newDeleteMemberHandler(ownerBackend))
|
2018-10-01 09:19:44 +00:00
|
|
|
|
2019-01-17 04:33:09 +00:00
|
|
|
labelBackend := &LabelBackend{
|
|
|
|
Logger: b.Logger.With(zap.String("handler", "label")),
|
|
|
|
LabelService: b.LabelService,
|
|
|
|
}
|
|
|
|
h.HandlerFunc("GET", bucketsIDLabelsPath, newGetLabelsHandler(labelBackend))
|
|
|
|
h.HandlerFunc("POST", bucketsIDLabelsPath, newPostLabelHandler(labelBackend))
|
|
|
|
h.HandlerFunc("DELETE", bucketsIDLabelsIDPath, newDeleteLabelHandler(labelBackend))
|
|
|
|
h.HandlerFunc("PATCH", bucketsIDLabelsIDPath, newPatchLabelHandler(labelBackend))
|
2018-12-03 16:07:08 +00:00
|
|
|
|
2018-05-14 16:26:38 +00:00
|
|
|
return h
|
|
|
|
}
|
|
|
|
|
2018-09-25 16:16:24 +00:00
|
|
|
// bucket is used for serialization/deserialization with duration string syntax.
|
|
|
|
type bucket struct {
|
2019-02-07 23:19:30 +00:00
|
|
|
ID influxdb.ID `json:"id,omitempty"`
|
|
|
|
OrganizationID influxdb.ID `json:"organizationID,omitempty"`
|
2018-10-22 22:18:34 +00:00
|
|
|
Organization string `json:"organization,omitempty"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
RetentionPolicyName string `json:"rp,omitempty"` // This to support v1 sources
|
|
|
|
RetentionRules []retentionRule `json:"retentionRules"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// retentionRule is the retention rule action for a bucket.
|
|
|
|
type retentionRule struct {
|
|
|
|
Type string `json:"type"`
|
|
|
|
EverySeconds int64 `json:"everySeconds"`
|
2018-09-25 16:16:24 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
func (b *bucket) toInfluxDB() (*influxdb.Bucket, error) {
|
2018-09-25 16:16:24 +00:00
|
|
|
if b == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2018-10-22 22:18:34 +00:00
|
|
|
var d time.Duration // zero value implies infinite retention policy
|
|
|
|
|
|
|
|
// Only support a single retention period for the moment
|
|
|
|
if len(b.RetentionRules) > 0 {
|
|
|
|
d = time.Duration(b.RetentionRules[0].EverySeconds) * time.Second
|
|
|
|
if d < time.Second {
|
2019-02-07 23:19:30 +00:00
|
|
|
return nil, &influxdb.Error{
|
|
|
|
Code: influxdb.EUnprocessableEntity,
|
2019-01-24 00:15:42 +00:00
|
|
|
Msg: "expiration seconds must be greater than or equal to one second",
|
|
|
|
}
|
2018-10-22 22:18:34 +00:00
|
|
|
}
|
2018-09-25 16:16:24 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
return &influxdb.Bucket{
|
2018-09-25 16:16:24 +00:00
|
|
|
ID: b.ID,
|
|
|
|
OrganizationID: b.OrganizationID,
|
|
|
|
Organization: b.Organization,
|
|
|
|
Name: b.Name,
|
|
|
|
RetentionPolicyName: b.RetentionPolicyName,
|
|
|
|
RetentionPeriod: d,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
func newBucket(pb *influxdb.Bucket) *bucket {
|
2018-09-25 16:16:24 +00:00
|
|
|
if pb == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-10-22 22:18:34 +00:00
|
|
|
rules := []retentionRule{}
|
|
|
|
rp := int64(pb.RetentionPeriod.Round(time.Second) / time.Second)
|
|
|
|
if rp > 0 {
|
|
|
|
rules = append(rules, retentionRule{
|
|
|
|
Type: "expire",
|
|
|
|
EverySeconds: rp,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-09-25 16:16:24 +00:00
|
|
|
return &bucket{
|
|
|
|
ID: pb.ID,
|
|
|
|
OrganizationID: pb.OrganizationID,
|
|
|
|
Organization: pb.Organization,
|
|
|
|
Name: pb.Name,
|
|
|
|
RetentionPolicyName: pb.RetentionPolicyName,
|
2018-10-22 22:18:34 +00:00
|
|
|
RetentionRules: rules,
|
2018-09-25 16:16:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-22 22:18:34 +00:00
|
|
|
// bucketUpdate is used for serialization/deserialization with retention rules.
|
2018-09-25 16:16:24 +00:00
|
|
|
type bucketUpdate struct {
|
2018-10-22 22:18:34 +00:00
|
|
|
Name *string `json:"name,omitempty"`
|
|
|
|
RetentionRules []retentionRule `json:"retentionRules,omitempty"`
|
2018-09-25 16:16:24 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
func (b *bucketUpdate) toInfluxDB() (*influxdb.BucketUpdate, error) {
|
2018-09-25 16:16:24 +00:00
|
|
|
if b == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2018-10-22 22:18:34 +00:00
|
|
|
// For now, only use a single retention rule.
|
2018-10-25 22:23:05 +00:00
|
|
|
var d time.Duration
|
2018-10-22 22:18:34 +00:00
|
|
|
if len(b.RetentionRules) > 0 {
|
2018-10-25 22:23:05 +00:00
|
|
|
d = time.Duration(b.RetentionRules[0].EverySeconds) * time.Second
|
2018-10-22 22:18:34 +00:00
|
|
|
if d < time.Second {
|
2019-02-07 23:19:30 +00:00
|
|
|
return nil, &influxdb.Error{
|
|
|
|
Code: influxdb.EUnprocessableEntity,
|
2019-01-24 00:15:42 +00:00
|
|
|
Msg: "expiration seconds must be greater than or equal to one second",
|
|
|
|
}
|
2018-09-25 16:16:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
return &influxdb.BucketUpdate{
|
2018-10-25 22:23:05 +00:00
|
|
|
Name: b.Name,
|
|
|
|
RetentionPeriod: &d,
|
|
|
|
}, nil
|
2018-09-25 16:16:24 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
func newBucketUpdate(pb *influxdb.BucketUpdate) *bucketUpdate {
|
2018-09-25 16:16:24 +00:00
|
|
|
if pb == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
up := &bucketUpdate{
|
2018-10-22 22:18:34 +00:00
|
|
|
Name: pb.Name,
|
|
|
|
RetentionRules: []retentionRule{},
|
2018-09-25 16:16:24 +00:00
|
|
|
}
|
2018-10-22 22:18:34 +00:00
|
|
|
|
2018-09-25 16:16:24 +00:00
|
|
|
if pb.RetentionPeriod != nil {
|
2018-10-22 22:18:34 +00:00
|
|
|
d := int64((*pb.RetentionPeriod).Round(time.Second) / time.Second)
|
|
|
|
up.RetentionRules = append(up.RetentionRules, retentionRule{
|
|
|
|
Type: "expire",
|
|
|
|
EverySeconds: d,
|
|
|
|
})
|
2018-09-25 16:16:24 +00:00
|
|
|
}
|
|
|
|
return up
|
|
|
|
}
|
|
|
|
|
2018-09-10 19:26:08 +00:00
|
|
|
type bucketResponse struct {
|
|
|
|
Links map[string]string `json:"links"`
|
2018-09-25 16:16:24 +00:00
|
|
|
bucket
|
2019-02-07 23:19:30 +00:00
|
|
|
Labels []influxdb.Label `json:"labels"`
|
2018-09-10 19:26:08 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
func newBucketResponse(b *influxdb.Bucket, labels []*influxdb.Label) *bucketResponse {
|
2019-01-03 23:56:52 +00:00
|
|
|
res := &bucketResponse{
|
2018-09-10 19:26:08 +00:00
|
|
|
Links: map[string]string{
|
2019-01-03 23:56:52 +00:00
|
|
|
"self": fmt.Sprintf("/api/v2/buckets/%s", b.ID),
|
|
|
|
"log": fmt.Sprintf("/api/v2/buckets/%s/log", b.ID),
|
|
|
|
"labels": fmt.Sprintf("/api/v2/buckets/%s/labels", b.ID),
|
|
|
|
"org": fmt.Sprintf("/api/v2/orgs/%s", b.OrganizationID),
|
2018-09-10 19:26:08 +00:00
|
|
|
},
|
2018-09-25 16:16:24 +00:00
|
|
|
bucket: *newBucket(b),
|
2019-02-07 23:19:30 +00:00
|
|
|
Labels: []influxdb.Label{},
|
2018-09-10 19:26:08 +00:00
|
|
|
}
|
2019-01-03 23:56:52 +00:00
|
|
|
|
|
|
|
for _, l := range labels {
|
|
|
|
res.Labels = append(res.Labels, *l)
|
|
|
|
}
|
|
|
|
|
|
|
|
return res
|
2018-09-10 19:26:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type bucketsResponse struct {
|
2019-02-07 23:19:30 +00:00
|
|
|
Links *influxdb.PagingLinks `json:"links"`
|
2018-12-09 15:25:35 +00:00
|
|
|
Buckets []*bucketResponse `json:"buckets"`
|
2018-09-10 19:26:08 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
func newBucketsResponse(ctx context.Context, opts influxdb.FindOptions, f influxdb.BucketFilter, bs []*influxdb.Bucket, labelService influxdb.LabelService) *bucketsResponse {
|
2018-09-10 19:26:08 +00:00
|
|
|
rs := make([]*bucketResponse, 0, len(bs))
|
|
|
|
for _, b := range bs {
|
2019-02-07 23:19:30 +00:00
|
|
|
labels, _ := labelService.FindResourceLabels(ctx, influxdb.LabelMappingFilter{ResourceID: b.ID})
|
2019-01-03 23:56:52 +00:00
|
|
|
rs = append(rs, newBucketResponse(b, labels))
|
2018-09-10 19:26:08 +00:00
|
|
|
}
|
|
|
|
return &bucketsResponse{
|
2018-12-09 15:25:35 +00:00
|
|
|
Links: newPagingLinks(bucketsPath, opts, f, len(bs)),
|
2018-09-10 19:26:08 +00:00
|
|
|
Buckets: rs,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-26 08:49:19 +00:00
|
|
|
// handlePostBucket is the HTTP handler for the POST /api/v2/buckets route.
|
2018-05-14 16:26:38 +00:00
|
|
|
func (h *BucketHandler) handlePostBucket(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
req, err := decodePostBucketRequest(ctx, r)
|
|
|
|
if err != nil {
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-01-17 01:09:03 +00:00
|
|
|
if !req.Bucket.OrganizationID.Valid() {
|
|
|
|
// Resolve organization name to ID before create
|
2019-02-07 23:19:30 +00:00
|
|
|
o, err := h.OrganizationService.FindOrganization(ctx, influxdb.OrganizationFilter{Name: &req.Bucket.Organization})
|
2019-01-17 01:09:03 +00:00
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
req.Bucket.OrganizationID = o.ID
|
2019-01-16 23:28:27 +00:00
|
|
|
}
|
|
|
|
|
2018-05-14 16:26:38 +00:00
|
|
|
if err := h.BucketService.CreateBucket(ctx, req.Bucket); err != nil {
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
if err := encodeResponse(ctx, w, http.StatusCreated, newBucketResponse(req.Bucket, []*influxdb.Label{})); err != nil {
|
2018-12-20 16:07:46 +00:00
|
|
|
logEncodingError(h.Logger, r, err)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type postBucketRequest struct {
|
2019-02-07 23:19:30 +00:00
|
|
|
Bucket *influxdb.Bucket
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 23:03:38 +00:00
|
|
|
func (b postBucketRequest) Validate() error {
|
2018-09-25 16:55:34 +00:00
|
|
|
if b.Bucket.Organization == "" && !b.Bucket.OrganizationID.Valid() {
|
2018-09-14 23:03:38 +00:00
|
|
|
return fmt.Errorf("bucket requires an organization")
|
2018-09-11 08:05:54 +00:00
|
|
|
}
|
2018-09-14 23:03:38 +00:00
|
|
|
return nil
|
|
|
|
}
|
2018-09-11 08:05:54 +00:00
|
|
|
|
2018-09-14 23:03:38 +00:00
|
|
|
func decodePostBucketRequest(ctx context.Context, r *http.Request) (*postBucketRequest, error) {
|
2018-09-25 16:16:24 +00:00
|
|
|
b := &bucket{}
|
2018-05-14 16:26:38 +00:00
|
|
|
if err := json.NewDecoder(r.Body).Decode(b); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
pb, err := b.toInfluxDB()
|
2018-09-25 16:16:24 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-09-14 23:03:38 +00:00
|
|
|
req := &postBucketRequest{
|
2018-09-25 16:16:24 +00:00
|
|
|
Bucket: pb,
|
2018-09-14 23:03:38 +00:00
|
|
|
}
|
2018-09-25 16:16:24 +00:00
|
|
|
|
2018-09-14 23:03:38 +00:00
|
|
|
return req, req.Validate()
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-26 08:49:19 +00:00
|
|
|
// handleGetBucket is the HTTP handler for the GET /api/v2/buckets/:id route.
|
2018-05-14 16:26:38 +00:00
|
|
|
func (h *BucketHandler) handleGetBucket(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
req, err := decodeGetBucketRequest(ctx, r)
|
|
|
|
if err != nil {
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
b, err := h.BucketService.FindBucketByID(ctx, req.BucketID)
|
|
|
|
if err != nil {
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
labels, err := h.LabelService.FindResourceLabels(ctx, influxdb.LabelMappingFilter{ResourceID: b.ID})
|
2019-01-03 23:56:52 +00:00
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := encodeResponse(ctx, w, http.StatusOK, newBucketResponse(b, labels)); err != nil {
|
2018-12-20 16:07:46 +00:00
|
|
|
logEncodingError(h.Logger, r, err)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type getBucketRequest struct {
|
2019-02-07 23:19:30 +00:00
|
|
|
BucketID influxdb.ID
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func decodeGetBucketRequest(ctx context.Context, r *http.Request) (*getBucketRequest, error) {
|
|
|
|
params := httprouter.ParamsFromContext(ctx)
|
|
|
|
id := params.ByName("id")
|
|
|
|
if id == "" {
|
2019-02-07 23:19:30 +00:00
|
|
|
return nil, &influxdb.Error{
|
|
|
|
Code: influxdb.EInvalid,
|
2019-01-24 00:15:42 +00:00
|
|
|
Msg: "url missing id",
|
|
|
|
}
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
var i influxdb.ID
|
2018-05-16 18:59:35 +00:00
|
|
|
if err := i.DecodeFromString(id); err != nil {
|
2018-05-14 16:26:38 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
req := &getBucketRequest{
|
|
|
|
BucketID: i,
|
|
|
|
}
|
|
|
|
|
|
|
|
return req, nil
|
|
|
|
}
|
|
|
|
|
2018-09-26 08:49:19 +00:00
|
|
|
// handleDeleteBucket is the HTTP handler for the DELETE /api/v2/buckets/:id route.
|
2018-05-16 18:59:35 +00:00
|
|
|
func (h *BucketHandler) handleDeleteBucket(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
req, err := decodeDeleteBucketRequest(ctx, r)
|
|
|
|
if err != nil {
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-16 18:59:35 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := h.BucketService.DeleteBucket(ctx, req.BucketID); err != nil {
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-16 18:59:35 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-09-10 19:26:08 +00:00
|
|
|
w.WriteHeader(http.StatusNoContent)
|
2018-05-16 18:59:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type deleteBucketRequest struct {
|
2019-02-07 23:19:30 +00:00
|
|
|
BucketID influxdb.ID
|
2018-05-16 18:59:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func decodeDeleteBucketRequest(ctx context.Context, r *http.Request) (*deleteBucketRequest, error) {
|
|
|
|
params := httprouter.ParamsFromContext(ctx)
|
|
|
|
id := params.ByName("id")
|
|
|
|
if id == "" {
|
2019-02-07 23:19:30 +00:00
|
|
|
return nil, &influxdb.Error{
|
|
|
|
Code: influxdb.EInvalid,
|
2019-01-24 00:15:42 +00:00
|
|
|
Msg: "url missing id",
|
|
|
|
}
|
2018-05-16 18:59:35 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
var i influxdb.ID
|
2018-05-16 18:59:35 +00:00
|
|
|
if err := i.DecodeFromString(id); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
req := &deleteBucketRequest{
|
|
|
|
BucketID: i,
|
|
|
|
}
|
|
|
|
|
|
|
|
return req, nil
|
|
|
|
}
|
|
|
|
|
2018-09-26 08:49:19 +00:00
|
|
|
// handleGetBuckets is the HTTP handler for the GET /api/v2/buckets route.
|
2018-05-14 16:26:38 +00:00
|
|
|
func (h *BucketHandler) handleGetBuckets(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
req, err := decodeGetBucketsRequest(ctx, r)
|
|
|
|
if err != nil {
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-09 15:25:35 +00:00
|
|
|
bs, _, err := h.BucketService.FindBuckets(ctx, req.filter, req.opts)
|
2018-05-14 16:26:38 +00:00
|
|
|
if err != nil {
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-01-03 23:56:52 +00:00
|
|
|
if err := encodeResponse(ctx, w, http.StatusOK, newBucketsResponse(ctx, req.opts, req.filter, bs, h.LabelService)); err != nil {
|
2018-12-20 16:07:46 +00:00
|
|
|
logEncodingError(h.Logger, r, err)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type getBucketsRequest struct {
|
2019-02-07 23:19:30 +00:00
|
|
|
filter influxdb.BucketFilter
|
|
|
|
opts influxdb.FindOptions
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func decodeGetBucketsRequest(ctx context.Context, r *http.Request) (*getBucketsRequest, error) {
|
|
|
|
qp := r.URL.Query()
|
|
|
|
req := &getBucketsRequest{}
|
|
|
|
|
2018-12-09 15:25:35 +00:00
|
|
|
opts, err := decodeFindOptions(ctx, r)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
req.opts = *opts
|
|
|
|
|
2018-10-10 19:18:29 +00:00
|
|
|
if orgID := qp.Get("orgID"); orgID != "" {
|
2019-02-07 23:19:30 +00:00
|
|
|
id, err := influxdb.IDFromString(orgID)
|
2018-07-20 10:24:07 +00:00
|
|
|
if err != nil {
|
2018-05-14 16:26:38 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2018-10-10 19:18:29 +00:00
|
|
|
req.filter.OrganizationID = id
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-05-16 18:59:35 +00:00
|
|
|
if org := qp.Get("org"); org != "" {
|
|
|
|
req.filter.Organization = &org
|
|
|
|
}
|
|
|
|
|
|
|
|
if name := qp.Get("name"); name != "" {
|
2018-05-14 16:26:38 +00:00
|
|
|
req.filter.Name = &name
|
|
|
|
}
|
|
|
|
|
2019-01-22 19:34:01 +00:00
|
|
|
if bucketID := qp.Get("id"); bucketID != "" {
|
2019-02-07 23:19:30 +00:00
|
|
|
id, err := influxdb.IDFromString(bucketID)
|
2019-01-22 19:34:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
req.filter.ID = id
|
|
|
|
}
|
|
|
|
|
2018-05-14 16:26:38 +00:00
|
|
|
return req, nil
|
|
|
|
}
|
|
|
|
|
2019-01-18 19:03:36 +00:00
|
|
|
// handlePatchBucket is the HTTP handler for the PATCH /api/v2/buckets route.
|
2018-05-14 16:26:38 +00:00
|
|
|
func (h *BucketHandler) handlePatchBucket(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
req, err := decodePatchBucketRequest(ctx, r)
|
|
|
|
if err != nil {
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
b, err := h.BucketService.UpdateBucket(ctx, req.BucketID, req.Update)
|
|
|
|
if err != nil {
|
2018-06-28 19:32:16 +00:00
|
|
|
EncodeError(ctx, err, w)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
labels, err := h.LabelService.FindResourceLabels(ctx, influxdb.LabelMappingFilter{ResourceID: b.ID})
|
2019-01-03 23:56:52 +00:00
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := encodeResponse(ctx, w, http.StatusOK, newBucketResponse(b, labels)); err != nil {
|
2018-12-20 16:07:46 +00:00
|
|
|
logEncodingError(h.Logger, r, err)
|
2018-05-14 16:26:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type patchBucketRequest struct {
|
2019-02-07 23:19:30 +00:00
|
|
|
Update influxdb.BucketUpdate
|
|
|
|
BucketID influxdb.ID
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func decodePatchBucketRequest(ctx context.Context, r *http.Request) (*patchBucketRequest, error) {
|
|
|
|
params := httprouter.ParamsFromContext(ctx)
|
|
|
|
id := params.ByName("id")
|
|
|
|
if id == "" {
|
2019-02-07 23:19:30 +00:00
|
|
|
return nil, &influxdb.Error{
|
|
|
|
Code: influxdb.EInvalid,
|
2019-01-24 00:15:42 +00:00
|
|
|
Msg: "url missing id",
|
|
|
|
}
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
var i influxdb.ID
|
2018-05-16 18:59:35 +00:00
|
|
|
if err := i.DecodeFromString(id); err != nil {
|
2019-02-07 23:19:30 +00:00
|
|
|
return nil, &influxdb.Error{
|
|
|
|
Code: influxdb.EInvalid,
|
2019-01-24 00:15:42 +00:00
|
|
|
Msg: err.Error(),
|
|
|
|
}
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-25 16:16:24 +00:00
|
|
|
bu := &bucketUpdate{}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(bu); err != nil {
|
2019-02-07 23:19:30 +00:00
|
|
|
return nil, &influxdb.Error{
|
|
|
|
Code: influxdb.EInvalid,
|
2019-01-24 00:15:42 +00:00
|
|
|
Msg: err.Error(),
|
|
|
|
}
|
2018-09-25 16:16:24 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
upd, err := bu.toInfluxDB()
|
2018-09-25 16:16:24 +00:00
|
|
|
if err != nil {
|
2018-05-14 16:26:38 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &patchBucketRequest{
|
2018-09-25 16:16:24 +00:00
|
|
|
Update: *upd,
|
2018-05-14 16:26:38 +00:00
|
|
|
BucketID: i,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2018-09-26 08:49:19 +00:00
|
|
|
bucketPath = "/api/v2/buckets"
|
2018-05-14 16:26:38 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// BucketService connects to Influx via HTTP using tokens to manage buckets
|
|
|
|
type BucketService struct {
|
|
|
|
Addr string
|
|
|
|
Token string
|
|
|
|
InsecureSkipVerify bool
|
2018-11-30 18:27:40 +00:00
|
|
|
// OpPrefix is an additional property for error
|
|
|
|
// find bucket service, when finds nothing.
|
|
|
|
OpPrefix string
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FindBucketByID returns a single bucket by ID.
|
2019-02-07 23:19:30 +00:00
|
|
|
func (s *BucketService) FindBucketByID(ctx context.Context, id influxdb.ID) (*influxdb.Bucket, error) {
|
2018-05-16 18:59:35 +00:00
|
|
|
u, err := newURL(s.Addr, bucketIDPath(id))
|
2018-05-14 16:26:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("GET", u.String(), nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-08-27 19:18:11 +00:00
|
|
|
SetToken(s.Token, req)
|
2018-05-14 16:26:38 +00:00
|
|
|
|
|
|
|
hc := newClient(u.Scheme, s.InsecureSkipVerify)
|
|
|
|
resp, err := hc.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-01-18 22:32:53 +00:00
|
|
|
defer resp.Body.Close()
|
2018-05-14 16:26:38 +00:00
|
|
|
|
2019-01-24 01:02:37 +00:00
|
|
|
if err := CheckError(resp); err != nil {
|
2018-05-23 18:29:01 +00:00
|
|
|
return nil, err
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-25 16:16:24 +00:00
|
|
|
var br bucketResponse
|
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&br); err != nil {
|
2018-05-14 16:26:38 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2019-02-07 23:19:30 +00:00
|
|
|
return br.toInfluxDB()
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FindBucket returns the first bucket that matches filter.
|
2019-02-07 23:19:30 +00:00
|
|
|
func (s *BucketService) FindBucket(ctx context.Context, filter influxdb.BucketFilter) (*influxdb.Bucket, error) {
|
2018-05-14 16:26:38 +00:00
|
|
|
bs, n, err := s.FindBuckets(ctx, filter)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if n == 0 {
|
2019-02-07 23:19:30 +00:00
|
|
|
return nil, &influxdb.Error{
|
|
|
|
Code: influxdb.ENotFound,
|
|
|
|
Op: s.OpPrefix + influxdb.OpFindBucket,
|
2018-12-12 18:24:33 +00:00
|
|
|
Msg: "bucket not found",
|
2018-11-30 18:27:40 +00:00
|
|
|
}
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return bs[0], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FindBuckets returns a list of buckets that match filter and the total count of matching buckets.
|
|
|
|
// Additional options provide pagination & sorting.
|
2019-02-07 23:19:30 +00:00
|
|
|
func (s *BucketService) FindBuckets(ctx context.Context, filter influxdb.BucketFilter, opt ...influxdb.FindOptions) ([]*influxdb.Bucket, int, error) {
|
2018-05-14 16:26:38 +00:00
|
|
|
u, err := newURL(s.Addr, bucketPath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
query := u.Query()
|
|
|
|
if filter.OrganizationID != nil {
|
|
|
|
query.Add("orgID", filter.OrganizationID.String())
|
|
|
|
}
|
2018-05-16 18:59:35 +00:00
|
|
|
if filter.Organization != nil {
|
|
|
|
query.Add("org", *filter.Organization)
|
|
|
|
}
|
2018-05-14 16:26:38 +00:00
|
|
|
if filter.ID != nil {
|
2018-05-16 18:59:35 +00:00
|
|
|
query.Add("id", filter.ID.String())
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
if filter.Name != nil {
|
2018-05-16 18:59:35 +00:00
|
|
|
query.Add("name", *filter.Name)
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-12-30 10:07:11 +00:00
|
|
|
if len(opt) > 0 {
|
|
|
|
for k, vs := range opt[0].QueryParams() {
|
|
|
|
for _, v := range vs {
|
|
|
|
query.Add(k, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-14 16:26:38 +00:00
|
|
|
req, err := http.NewRequest("GET", u.String(), nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
req.URL.RawQuery = query.Encode()
|
2018-08-27 19:18:11 +00:00
|
|
|
SetToken(s.Token, req)
|
2018-05-14 16:26:38 +00:00
|
|
|
|
|
|
|
hc := newClient(u.Scheme, s.InsecureSkipVerify)
|
|
|
|
resp, err := hc.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
2019-01-18 22:32:53 +00:00
|
|
|
defer resp.Body.Close()
|
2018-05-14 16:26:38 +00:00
|
|
|
|
2019-01-24 01:02:37 +00:00
|
|
|
if err := CheckError(resp); err != nil {
|
2018-05-23 18:29:01 +00:00
|
|
|
return nil, 0, err
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-10 19:26:08 +00:00
|
|
|
var bs bucketsResponse
|
2018-05-16 18:59:35 +00:00
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&bs); err != nil {
|
2018-05-14 16:26:38 +00:00
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
buckets := make([]*influxdb.Bucket, 0, len(bs.Buckets))
|
2018-09-10 19:26:08 +00:00
|
|
|
for _, b := range bs.Buckets {
|
2019-02-07 23:19:30 +00:00
|
|
|
pb, err := b.bucket.toInfluxDB()
|
2018-09-25 16:16:24 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
buckets = append(buckets, pb)
|
2018-09-10 19:26:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return buckets, len(buckets), nil
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateBucket creates a new bucket and sets b.ID with the new identifier.
|
2019-02-07 23:19:30 +00:00
|
|
|
func (s *BucketService) CreateBucket(ctx context.Context, b *influxdb.Bucket) error {
|
2018-05-14 16:26:38 +00:00
|
|
|
u, err := newURL(s.Addr, bucketPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-09-25 16:16:24 +00:00
|
|
|
octets, err := json.Marshal(newBucket(b))
|
2018-05-14 16:26:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("POST", u.String(), bytes.NewReader(octets))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
2018-08-27 19:18:11 +00:00
|
|
|
SetToken(s.Token, req)
|
2018-05-14 16:26:38 +00:00
|
|
|
|
|
|
|
hc := newClient(u.Scheme, s.InsecureSkipVerify)
|
|
|
|
|
|
|
|
resp, err := hc.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-01-18 22:32:53 +00:00
|
|
|
defer resp.Body.Close()
|
2018-05-14 16:26:38 +00:00
|
|
|
|
2018-05-23 18:29:01 +00:00
|
|
|
// TODO(jsternberg): Should this check for a 201 explicitly?
|
2019-01-24 01:02:37 +00:00
|
|
|
if err := CheckError(resp); err != nil {
|
2018-05-23 18:29:01 +00:00
|
|
|
return err
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-25 16:16:24 +00:00
|
|
|
var br bucketResponse
|
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&br); err != nil {
|
2018-05-14 16:26:38 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
pb, err := br.toInfluxDB()
|
2018-09-26 17:03:15 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*b = *pb
|
|
|
|
return nil
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateBucket updates a single bucket with changeset.
|
|
|
|
// Returns the new bucket state after update.
|
2019-02-07 23:19:30 +00:00
|
|
|
func (s *BucketService) UpdateBucket(ctx context.Context, id influxdb.ID, upd influxdb.BucketUpdate) (*influxdb.Bucket, error) {
|
2018-05-16 18:59:35 +00:00
|
|
|
u, err := newURL(s.Addr, bucketIDPath(id))
|
2018-05-14 16:26:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-09-25 16:16:24 +00:00
|
|
|
bu := newBucketUpdate(&upd)
|
|
|
|
octets, err := json.Marshal(bu)
|
2018-05-14 16:26:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("PATCH", u.String(), bytes.NewReader(octets))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
2018-08-27 19:18:11 +00:00
|
|
|
SetToken(s.Token, req)
|
2018-05-14 16:26:38 +00:00
|
|
|
|
|
|
|
hc := newClient(u.Scheme, s.InsecureSkipVerify)
|
|
|
|
|
|
|
|
resp, err := hc.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-01-18 22:32:53 +00:00
|
|
|
defer resp.Body.Close()
|
2018-05-14 16:26:38 +00:00
|
|
|
|
2019-01-24 01:02:37 +00:00
|
|
|
if err := CheckError(resp); err != nil {
|
2018-05-23 18:29:01 +00:00
|
|
|
return nil, err
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2018-09-25 16:16:24 +00:00
|
|
|
var br bucketResponse
|
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&br); err != nil {
|
2018-05-14 16:26:38 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2019-02-07 23:19:30 +00:00
|
|
|
return br.toInfluxDB()
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteBucket removes a bucket by ID.
|
2019-02-07 23:19:30 +00:00
|
|
|
func (s *BucketService) DeleteBucket(ctx context.Context, id influxdb.ID) error {
|
2018-05-16 18:59:35 +00:00
|
|
|
u, err := newURL(s.Addr, bucketIDPath(id))
|
2018-05-14 16:26:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("DELETE", u.String(), nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-08-27 19:18:11 +00:00
|
|
|
SetToken(s.Token, req)
|
2018-05-14 16:26:38 +00:00
|
|
|
|
|
|
|
hc := newClient(u.Scheme, s.InsecureSkipVerify)
|
|
|
|
resp, err := hc.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-01-18 22:32:53 +00:00
|
|
|
defer resp.Body.Close()
|
|
|
|
|
2019-01-24 01:02:37 +00:00
|
|
|
return CheckError(resp)
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
func bucketIDPath(id influxdb.ID) string {
|
2018-05-16 18:59:35 +00:00
|
|
|
return path.Join(bucketPath, id.String())
|
2018-05-14 16:26:38 +00:00
|
|
|
}
|
2018-11-02 18:21:14 +00:00
|
|
|
|
|
|
|
// hanldeGetBucketLog retrieves a bucket log by the buckets ID.
|
|
|
|
func (h *BucketHandler) handleGetBucketLog(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
req, err := decodeGetBucketLogRequest(ctx, r)
|
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
log, _, err := h.BucketOperationLogService.GetBucketOperationLog(ctx, req.BucketID, req.opts)
|
|
|
|
if err != nil {
|
|
|
|
EncodeError(ctx, err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := encodeResponse(ctx, w, http.StatusOK, newBucketLogResponse(req.BucketID, log)); err != nil {
|
2018-12-20 16:07:46 +00:00
|
|
|
logEncodingError(h.Logger, r, err)
|
2018-11-02 18:21:14 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type getBucketLogRequest struct {
|
2019-02-07 23:19:30 +00:00
|
|
|
BucketID influxdb.ID
|
|
|
|
opts influxdb.FindOptions
|
2018-11-02 18:21:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func decodeGetBucketLogRequest(ctx context.Context, r *http.Request) (*getBucketLogRequest, error) {
|
|
|
|
params := httprouter.ParamsFromContext(ctx)
|
|
|
|
id := params.ByName("id")
|
|
|
|
if id == "" {
|
2019-02-07 23:19:30 +00:00
|
|
|
return nil, &influxdb.Error{
|
|
|
|
Code: influxdb.EInvalid,
|
2019-01-24 00:15:42 +00:00
|
|
|
Msg: "url missing id",
|
|
|
|
}
|
2018-11-02 18:21:14 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
var i influxdb.ID
|
2018-11-02 18:21:14 +00:00
|
|
|
if err := i.DecodeFromString(id); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-02-01 03:10:05 +00:00
|
|
|
opts, err := decodeFindOptions(ctx, r)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2018-11-02 18:21:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return &getBucketLogRequest{
|
|
|
|
BucketID: i,
|
2019-02-01 03:10:05 +00:00
|
|
|
opts: *opts,
|
2018-11-02 18:21:14 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-02-07 23:19:30 +00:00
|
|
|
func newBucketLogResponse(id influxdb.ID, es []*influxdb.OperationLogEntry) *operationLogResponse {
|
2018-11-02 18:21:14 +00:00
|
|
|
log := make([]*operationLogEntryResponse, 0, len(es))
|
|
|
|
for _, e := range es {
|
|
|
|
log = append(log, newOperationLogEntryResponse(e))
|
|
|
|
}
|
|
|
|
return &operationLogResponse{
|
|
|
|
Links: map[string]string{
|
|
|
|
"self": fmt.Sprintf("/api/v2/buckets/%s/log", id),
|
|
|
|
},
|
|
|
|
Log: log,
|
|
|
|
}
|
|
|
|
}
|