2017-03-20 22:26:48 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
2017-03-23 20:53:28 +00:00
|
|
|
"context"
|
2017-03-23 11:56:36 +00:00
|
|
|
"encoding/json"
|
2017-03-22 20:27:36 +00:00
|
|
|
"fmt"
|
2017-03-21 19:59:27 +00:00
|
|
|
"net/http"
|
2017-03-23 06:21:21 +00:00
|
|
|
|
2017-03-23 11:56:36 +00:00
|
|
|
"github.com/bouk/httprouter"
|
2017-03-23 06:21:21 +00:00
|
|
|
"github.com/influxdata/chronograf"
|
2017-03-20 22:26:48 +00:00
|
|
|
)
|
|
|
|
|
2017-03-21 19:59:27 +00:00
|
|
|
type dbLinks struct {
|
2017-03-23 20:16:02 +00:00
|
|
|
Self string `json:"self"` // Self link mapping to this resource
|
|
|
|
RPs string `json:"retentionPolicies"` // URL for retention policies for this database
|
2017-03-21 19:59:27 +00:00
|
|
|
}
|
|
|
|
|
2017-03-22 08:40:30 +00:00
|
|
|
type dbResponse struct {
|
2017-04-04 21:42:24 +00:00
|
|
|
Name string `json:"name"` // a unique string identifier for the database
|
|
|
|
Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy)
|
|
|
|
Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy)
|
|
|
|
ShardDuration string `json:"shardDuration,omitempty"` // the shard duration (when creating a default retention policy)
|
|
|
|
RPs []rpResponse `json:"retentionPolicies"` // RPs are the retention policies for a database
|
|
|
|
Links dbLinks `json:"links"` // Links are URI locations related to the database
|
2017-03-21 19:59:27 +00:00
|
|
|
}
|
|
|
|
|
2017-03-23 20:34:09 +00:00
|
|
|
// newDBResponse creates the response for the /databases endpoint
|
2017-03-23 20:53:28 +00:00
|
|
|
func newDBResponse(srcID int, name string, rps []rpResponse) dbResponse {
|
2017-03-23 20:16:02 +00:00
|
|
|
base := "/chronograf/v1/sources"
|
|
|
|
return dbResponse{
|
|
|
|
Name: name,
|
2017-03-23 20:53:28 +00:00
|
|
|
RPs: rps,
|
2017-03-23 20:16:02 +00:00
|
|
|
Links: dbLinks{
|
|
|
|
Self: fmt.Sprintf("%s/%d/dbs/%s", base, srcID, name),
|
2017-03-23 20:29:13 +00:00
|
|
|
RPs: fmt.Sprintf("%s/%d/dbs/%s/rps", base, srcID, name),
|
2017-03-23 20:16:02 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-22 08:40:30 +00:00
|
|
|
type dbsResponse struct {
|
2017-03-23 11:56:36 +00:00
|
|
|
Databases []dbResponse `json:"databases"`
|
2017-03-20 22:26:48 +00:00
|
|
|
}
|
|
|
|
|
2017-03-23 10:06:59 +00:00
|
|
|
type rpLinks struct {
|
|
|
|
Self string `json:"self"` // Self link mapping to this resource
|
|
|
|
}
|
|
|
|
|
|
|
|
type rpResponse struct {
|
|
|
|
Name string `json:"name"` // a unique string identifier for the retention policy
|
|
|
|
Duration string `json:"duration"` // the duration
|
|
|
|
Replication int32 `json:"replication"` // the replication factor
|
|
|
|
ShardDuration string `json:"shardDuration"` // the shard duration
|
2017-03-24 17:06:59 +00:00
|
|
|
Default bool `json:"isDefault"` // whether the RP should be the default
|
2017-03-23 10:06:59 +00:00
|
|
|
Links rpLinks `json:"links"` // Links are URI locations related to the database
|
|
|
|
}
|
|
|
|
|
2017-03-23 20:29:13 +00:00
|
|
|
// WithLinks adds links to an rpResponse in place
|
2017-03-23 20:16:02 +00:00
|
|
|
func (r *rpResponse) WithLinks(srcID int, dbName string) {
|
|
|
|
base := "/chronograf/v1/sources"
|
|
|
|
r.Links = rpLinks{
|
|
|
|
Self: fmt.Sprintf("%s/%d/dbs/%s/rps/%s", base, srcID, dbName, r.Name),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-23 10:06:59 +00:00
|
|
|
type rpsResponse struct {
|
2017-03-23 11:56:36 +00:00
|
|
|
RetentionPolicies []rpResponse `json:"retentionPolicies"`
|
2017-03-23 10:06:59 +00:00
|
|
|
}
|
|
|
|
|
2017-03-23 20:29:13 +00:00
|
|
|
// GetDatabases queries the list of all databases for a source
|
2017-03-22 20:27:36 +00:00
|
|
|
func (h *Service) GetDatabases(w http.ResponseWriter, r *http.Request) {
|
2017-03-23 11:56:36 +00:00
|
|
|
ctx := r.Context()
|
2017-03-21 19:59:27 +00:00
|
|
|
|
2017-03-23 11:56:36 +00:00
|
|
|
srcID, err := paramID("id", r)
|
2017-03-22 20:27:36 +00:00
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-10-31 20:41:17 +00:00
|
|
|
src, err := h.Store.Sources(ctx).Get(ctx, srcID)
|
2017-03-22 20:27:36 +00:00
|
|
|
if err != nil {
|
|
|
|
notFound(w, srcID, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-03-23 11:56:36 +00:00
|
|
|
db := h.Databases
|
2017-03-22 20:27:36 +00:00
|
|
|
if err = db.Connect(ctx, &src); err != nil {
|
|
|
|
msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err)
|
|
|
|
Error(w, http.StatusBadRequest, msg, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-03-23 11:56:36 +00:00
|
|
|
databases, err := db.AllDB(ctx)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusBadRequest, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
2017-03-21 19:59:27 +00:00
|
|
|
|
2017-03-23 11:56:36 +00:00
|
|
|
dbs := make([]dbResponse, len(databases))
|
|
|
|
for i, d := range databases {
|
2017-03-23 20:53:28 +00:00
|
|
|
rps, err := h.allRPs(ctx, db, srcID, d.Name)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusBadRequest, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
dbs[i] = newDBResponse(srcID, d.Name, rps)
|
2017-03-23 11:56:36 +00:00
|
|
|
}
|
2017-03-21 19:59:27 +00:00
|
|
|
|
2017-03-22 08:40:30 +00:00
|
|
|
res := dbsResponse{
|
|
|
|
Databases: dbs,
|
2017-03-21 19:59:27 +00:00
|
|
|
}
|
2017-03-20 22:26:48 +00:00
|
|
|
|
2017-03-21 19:59:27 +00:00
|
|
|
encodeJSON(w, http.StatusOK, res, h.Logger)
|
2017-03-20 22:26:48 +00:00
|
|
|
}
|
2017-03-23 05:21:25 +00:00
|
|
|
|
2017-03-23 20:29:13 +00:00
|
|
|
// NewDatabase creates a new database within the datastore
|
2017-03-23 05:21:25 +00:00
|
|
|
func (h *Service) NewDatabase(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
srcID, err := paramID("id", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-10-31 20:41:17 +00:00
|
|
|
src, err := h.Store.Sources(ctx).Get(ctx, srcID)
|
2017-03-23 05:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
notFound(w, srcID, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
db := h.Databases
|
|
|
|
|
|
|
|
if err = db.Connect(ctx, &src); err != nil {
|
|
|
|
msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err)
|
|
|
|
Error(w, http.StatusBadRequest, msg, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-03-23 06:21:21 +00:00
|
|
|
postedDB := &chronograf.Database{}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(postedDB); err != nil {
|
|
|
|
invalidJSON(w, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := ValidDatabaseRequest(postedDB); err != nil {
|
|
|
|
invalidData(w, err, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
database, err := db.CreateDB(ctx, postedDB)
|
2017-03-23 05:21:25 +00:00
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusBadRequest, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-03-23 20:53:28 +00:00
|
|
|
rps, err := h.allRPs(ctx, db, srcID, database.Name)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusBadRequest, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
res := newDBResponse(srcID, database.Name, rps)
|
2017-03-23 06:21:21 +00:00
|
|
|
encodeJSON(w, http.StatusCreated, res, h.Logger)
|
|
|
|
}
|
|
|
|
|
2017-03-23 20:29:13 +00:00
|
|
|
// DropDatabase removes a database from a data source
|
2017-03-23 08:04:35 +00:00
|
|
|
func (h *Service) DropDatabase(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
srcID, err := paramID("id", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-10-31 20:41:17 +00:00
|
|
|
src, err := h.Store.Sources(ctx).Get(ctx, srcID)
|
2017-03-23 08:04:35 +00:00
|
|
|
if err != nil {
|
|
|
|
notFound(w, srcID, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
db := h.Databases
|
|
|
|
|
|
|
|
if err = db.Connect(ctx, &src); err != nil {
|
|
|
|
msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err)
|
|
|
|
Error(w, http.StatusBadRequest, msg, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
dbID := httprouter.GetParamFromContext(ctx, "dbid")
|
|
|
|
|
|
|
|
dropErr := db.DropDB(ctx, dbID)
|
|
|
|
if dropErr != nil {
|
|
|
|
Error(w, http.StatusBadRequest, dropErr.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
}
|
|
|
|
|
2017-03-23 20:29:13 +00:00
|
|
|
// RetentionPolicies lists retention policies within a database
|
2017-03-23 10:06:59 +00:00
|
|
|
func (h *Service) RetentionPolicies(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
srcID, err := paramID("id", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-10-31 20:41:17 +00:00
|
|
|
src, err := h.Store.Sources(ctx).Get(ctx, srcID)
|
2017-03-23 10:06:59 +00:00
|
|
|
if err != nil {
|
|
|
|
notFound(w, srcID, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
db := h.Databases
|
|
|
|
if err = db.Connect(ctx, &src); err != nil {
|
|
|
|
msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err)
|
|
|
|
Error(w, http.StatusBadRequest, msg, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
dbID := httprouter.GetParamFromContext(ctx, "dbid")
|
2017-03-23 20:53:28 +00:00
|
|
|
res, err := h.allRPs(ctx, db, srcID, dbID)
|
2017-03-23 10:06:59 +00:00
|
|
|
if err != nil {
|
2017-03-23 20:53:28 +00:00
|
|
|
msg := fmt.Sprintf("Unable to connect get RPs %d: %v", srcID, err)
|
|
|
|
Error(w, http.StatusBadRequest, msg, h.Logger)
|
2017-03-23 10:06:59 +00:00
|
|
|
return
|
|
|
|
}
|
2017-03-23 20:53:28 +00:00
|
|
|
encodeJSON(w, http.StatusOK, res, h.Logger)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *Service) allRPs(ctx context.Context, db chronograf.Databases, srcID int, dbID string) ([]rpResponse, error) {
|
|
|
|
allRP, err := db.AllRP(ctx, dbID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-03-23 10:06:59 +00:00
|
|
|
|
|
|
|
rps := make([]rpResponse, len(allRP))
|
|
|
|
for i, rp := range allRP {
|
2017-03-23 20:16:02 +00:00
|
|
|
rp := rpResponse{
|
2017-03-23 11:56:36 +00:00
|
|
|
Name: rp.Name,
|
|
|
|
Duration: rp.Duration,
|
|
|
|
Replication: rp.Replication,
|
2017-03-23 10:06:59 +00:00
|
|
|
ShardDuration: rp.ShardDuration,
|
2017-03-23 11:56:36 +00:00
|
|
|
Default: rp.Default,
|
2017-03-23 10:06:59 +00:00
|
|
|
}
|
2017-03-23 20:16:02 +00:00
|
|
|
rp.WithLinks(srcID, dbID)
|
|
|
|
rps[i] = rp
|
2017-03-23 10:06:59 +00:00
|
|
|
}
|
2017-03-23 20:53:28 +00:00
|
|
|
return rps, nil
|
2017-03-23 10:06:59 +00:00
|
|
|
}
|
|
|
|
|
2017-03-23 20:29:13 +00:00
|
|
|
// NewRetentionPolicy creates a new retention policy for a database
|
2017-03-23 11:27:53 +00:00
|
|
|
func (h *Service) NewRetentionPolicy(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
srcID, err := paramID("id", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-10-31 20:41:17 +00:00
|
|
|
src, err := h.Store.Sources(ctx).Get(ctx, srcID)
|
2017-03-23 11:27:53 +00:00
|
|
|
if err != nil {
|
|
|
|
notFound(w, srcID, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
db := h.Databases
|
|
|
|
if err = db.Connect(ctx, &src); err != nil {
|
|
|
|
msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err)
|
|
|
|
Error(w, http.StatusBadRequest, msg, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
postedRP := &chronograf.RetentionPolicy{}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(postedRP); err != nil {
|
|
|
|
invalidJSON(w, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err := ValidRetentionPolicyRequest(postedRP); err != nil {
|
|
|
|
invalidData(w, err, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
dbID := httprouter.GetParamFromContext(ctx, "dbid")
|
2017-03-23 21:30:24 +00:00
|
|
|
rp, err := db.CreateRP(ctx, dbID, postedRP)
|
2017-03-23 11:27:53 +00:00
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusBadRequest, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
2017-03-23 21:30:24 +00:00
|
|
|
res := rpResponse{
|
|
|
|
Name: rp.Name,
|
|
|
|
Duration: rp.Duration,
|
|
|
|
Replication: rp.Replication,
|
|
|
|
ShardDuration: rp.ShardDuration,
|
|
|
|
Default: rp.Default,
|
|
|
|
}
|
|
|
|
res.WithLinks(srcID, dbID)
|
2017-03-23 11:27:53 +00:00
|
|
|
encodeJSON(w, http.StatusCreated, res, h.Logger)
|
|
|
|
}
|
|
|
|
|
2017-03-23 20:29:13 +00:00
|
|
|
// UpdateRetentionPolicy modifies an existing retention policy for a database
|
2017-03-23 13:13:41 +00:00
|
|
|
func (h *Service) UpdateRetentionPolicy(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
srcID, err := paramID("id", r)
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-10-31 20:41:17 +00:00
|
|
|
src, err := h.Store.Sources(ctx).Get(ctx, srcID)
|
2017-03-23 13:13:41 +00:00
|
|
|
if err != nil {
|
|
|
|
notFound(w, srcID, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
db := h.Databases
|
|
|
|
if err = db.Connect(ctx, &src); err != nil {
|
|
|
|
msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err)
|
|
|
|
Error(w, http.StatusBadRequest, msg, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
postedRP := &chronograf.RetentionPolicy{}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(postedRP); err != nil {
|
|
|
|
invalidJSON(w, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err := ValidRetentionPolicyRequest(postedRP); err != nil {
|
|
|
|
invalidData(w, err, h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
dbID := httprouter.GetParamFromContext(ctx, "dbid")
|
|
|
|
rpID := httprouter.GetParamFromContext(ctx, "rpid")
|
|
|
|
rp, err := db.UpdateRP(ctx, dbID, rpID, postedRP)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
Error(w, http.StatusBadRequest, err.Error(), h.Logger)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-03-23 21:30:24 +00:00
|
|
|
res := rpResponse{
|
|
|
|
Name: rp.Name,
|
|
|
|
Duration: rp.Duration,
|
|
|
|
Replication: rp.Replication,
|
|
|
|
ShardDuration: rp.ShardDuration,
|
|
|
|
Default: rp.Default,
|
|
|
|
}
|
|
|
|
res.WithLinks(srcID, dbID)
|
2017-03-23 13:13:41 +00:00
|
|
|
encodeJSON(w, http.StatusCreated, res, h.Logger)
|
|
|
|
}
|
|
|
|
|
2017-03-23 20:29:13 +00:00
|
|
|
// DropRetentionPolicy removes a retention policy from a database
|
2017-10-10 22:27:58 +00:00
|
|
|
func (s *Service) DropRetentionPolicy(w http.ResponseWriter, r *http.Request) {
|
2017-03-23 11:51:08 +00:00
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
srcID, err := paramID("id", r)
|
|
|
|
if err != nil {
|
2017-10-10 22:27:58 +00:00
|
|
|
Error(w, http.StatusUnprocessableEntity, err.Error(), s.Logger)
|
2017-03-23 11:51:08 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-10-31 20:41:17 +00:00
|
|
|
src, err := s.Store.Sources(ctx).Get(ctx, srcID)
|
2017-03-23 11:51:08 +00:00
|
|
|
if err != nil {
|
2017-10-10 22:27:58 +00:00
|
|
|
notFound(w, srcID, s.Logger)
|
2017-03-23 11:51:08 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-10-10 22:27:58 +00:00
|
|
|
db := s.Databases
|
2017-03-23 11:51:08 +00:00
|
|
|
if err = db.Connect(ctx, &src); err != nil {
|
|
|
|
msg := fmt.Sprintf("Unable to connect to source %d: %v", srcID, err)
|
2017-10-10 22:27:58 +00:00
|
|
|
Error(w, http.StatusBadRequest, msg, s.Logger)
|
2017-03-23 11:51:08 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
dbID := httprouter.GetParamFromContext(ctx, "dbid")
|
|
|
|
rpID := httprouter.GetParamFromContext(ctx, "rpid")
|
|
|
|
dropErr := db.DropRP(ctx, dbID, rpID)
|
|
|
|
if dropErr != nil {
|
2017-10-10 22:27:58 +00:00
|
|
|
Error(w, http.StatusBadRequest, dropErr.Error(), s.Logger)
|
2017-03-23 11:51:08 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
}
|
|
|
|
|
2017-03-23 20:29:13 +00:00
|
|
|
// ValidDatabaseRequest checks if the database posted is valid
|
2017-03-23 06:21:21 +00:00
|
|
|
func ValidDatabaseRequest(d *chronograf.Database) error {
|
|
|
|
if len(d.Name) == 0 {
|
|
|
|
return fmt.Errorf("name is required")
|
|
|
|
}
|
|
|
|
return nil
|
2017-03-23 05:21:25 +00:00
|
|
|
}
|
2017-03-23 11:27:53 +00:00
|
|
|
|
2017-03-23 20:29:13 +00:00
|
|
|
// ValidRetentionPolicyRequest checks if a retention policy is valid on POST
|
2017-03-23 11:27:53 +00:00
|
|
|
func ValidRetentionPolicyRequest(rp *chronograf.RetentionPolicy) error {
|
|
|
|
if len(rp.Name) == 0 {
|
|
|
|
return fmt.Errorf("name is required")
|
|
|
|
}
|
|
|
|
if len(rp.Duration) == 0 {
|
|
|
|
return fmt.Errorf("duration is required")
|
|
|
|
}
|
|
|
|
if rp.Replication == 0 {
|
|
|
|
return fmt.Errorf("replication factor is invalid")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|