You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ha...@apache.org on 2024/04/28 04:38:37 UTC
(skywalking-banyandb) 01/01: Validate resource
This is an automated email from the ASF dual-hosted git repository.
hanahmily pushed a commit to branch metadata
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git
commit 361e4d3ebe1ecf99a7f8fd480882b370ec814e0f
Author: Gao Hongtao <ha...@gmail.com>
AuthorDate: Sun Apr 28 12:35:01 2024 +0800
Validate resource
Signed-off-by: Gao Hongtao <ha...@gmail.com>
---
CONTRIBUTING.md | 2 +-
api/validate/validate.go | 243 +++++++++++++++++++++++++++
banyand/measure/metadata.go | 21 +++
banyand/measure/metadata_test.go | 8 +-
banyand/metadata/schema/group.go | 15 ++
banyand/metadata/schema/index.go | 19 +++
banyand/metadata/schema/measure.go | 26 +++
banyand/metadata/schema/property.go | 23 ++-
banyand/metadata/schema/stream.go | 23 ++-
banyand/metadata/schema/topn.go | 10 ++
banyand/metadata/schema/watcher_test.go | 62 ++++++-
banyand/stream/metadata.go | 17 ++
docs/release.md | 2 +-
ui/src/components/Aside/index.vue | 14 +-
ui/src/components/Editor/fieldsEditor.vue | 12 --
ui/src/components/Editor/tagEditor.vue | 4 -
ui/src/components/IndexRule/Editor.vue | 4 -
ui/src/components/TopNAggregation/Editor.vue | 4 -
18 files changed, 468 insertions(+), 41 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d947479e..662e57a1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -75,7 +75,7 @@ Finally, run `make build` in the source directory, which will build the default
make build
```
-Please refer to the [installation](./docs/installation/binaries.md) for more details.
+Please refer to the [installation](./docs/installation/binaries.md#Build-From-Source) for more details.
Test your changes before submitting them by
diff --git a/api/validate/validate.go b/api/validate/validate.go
new file mode 100644
index 00000000..1c4481aa
--- /dev/null
+++ b/api/validate/validate.go
@@ -0,0 +1,243 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Package validate provides functions to validate the provided objects.
+package validate
+
+import (
+ "errors"
+
+ commonv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/common/v1"
+ databasev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
+)
+
+// GroupForStreamOrMeasure validates the provided Group object for Stream or Measure.
+// It checks for nil values, empty strings, and unspecified enum values.
+func GroupForStreamOrMeasure(group *commonv1.Group) error {
+ if group == nil {
+ return errors.New("group is nil")
+ }
+ if group.Metadata == nil {
+ return errors.New("group metadata is nil")
+ }
+ if group.Metadata.Name == "" {
+ return errors.New("group name is empty")
+ }
+ if group.Catalog == commonv1.Catalog_CATALOG_UNSPECIFIED {
+ return errors.New("group catalog is unspecified")
+ }
+ if group.ResourceOpts == nil {
+ return errors.New("group resourceOpts is nil")
+ }
+ if group.ResourceOpts.ShardNum <= 0 {
+ return errors.New("group shardNum is invalid")
+ }
+ if group.ResourceOpts.SegmentInterval == nil {
+ return errors.New("group segmentInterval is nil")
+ }
+ if group.ResourceOpts.SegmentInterval.Num <= 0 {
+ return errors.New("group segmentInterval num is invalid")
+ }
+ if group.ResourceOpts.SegmentInterval.Unit == commonv1.IntervalRule_UNIT_UNSPECIFIED {
+ return errors.New("group segmentInterval unit is unspecified")
+ }
+ if group.ResourceOpts.Ttl == nil {
+ return errors.New("group ttl is nil")
+ }
+ if group.ResourceOpts.Ttl.Num <= 0 {
+ return errors.New("group ttl num is invalid")
+ }
+ if group.ResourceOpts.Ttl.Unit == commonv1.IntervalRule_UNIT_UNSPECIFIED {
+ return errors.New("group ttl unit is unspecified")
+ }
+ return nil
+}
+
+// Stream validates the provided Stream object.
+// It checks for nil values, empty strings, and unspecified enum values.
+func Stream(stream *databasev1.Stream) error {
+ if stream == nil {
+ return errors.New("stream is nil")
+ }
+ if stream.Metadata == nil {
+ return errors.New("stream metadata is nil")
+ }
+ if stream.Metadata.Name == "" {
+ return errors.New("stream name is empty")
+ }
+ if stream.Metadata.Group == "" {
+ return errors.New("stream group is empty")
+ }
+ if len(stream.TagFamilies) == 0 {
+ return errors.New("stream tag families is empty")
+ }
+ if stream.Entity == nil {
+ return errors.New("stream entity is nil")
+ }
+ if len(stream.Entity.TagNames) == 0 {
+ return errors.New("stream entity tag names is empty")
+ }
+ return tagFamily(stream.TagFamilies)
+}
+
+// Measure validates the provided Measure object.
+// It checks for nil values, empty strings, and unspecified enum values.
+func Measure(measure *databasev1.Measure) error {
+ if measure == nil {
+ return errors.New("measure is nil")
+ }
+ if measure.Metadata == nil {
+ return errors.New("measure metadata is nil")
+ }
+ if measure.Metadata.Name == "" {
+ return errors.New("measure name is empty")
+ }
+ if measure.Metadata.Group == "" {
+ return errors.New("measure group is empty")
+ }
+ if measure.Entity == nil {
+ return errors.New("measure entity is nil")
+ }
+ if len(measure.Entity.TagNames) == 0 {
+ return errors.New("measure entity tag names is empty")
+ }
+ for i := range measure.Fields {
+ if measure.Fields[i].Name == "" {
+ return errors.New("field name is empty")
+ }
+ if measure.Fields[i].FieldType == databasev1.FieldType_FIELD_TYPE_UNSPECIFIED {
+ return errors.New("field type is unspecified")
+ }
+ if measure.Fields[i].CompressionMethod == databasev1.CompressionMethod_COMPRESSION_METHOD_UNSPECIFIED {
+ return errors.New("compression method is unspecified")
+ }
+ if measure.Fields[i].CompressionMethod == databasev1.CompressionMethod_COMPRESSION_METHOD_UNSPECIFIED {
+ return errors.New("compression method is unspecified")
+ }
+ }
+ if len(measure.TagFamilies) == 0 {
+ return errors.New("measure tag families is empty")
+ }
+ return tagFamily(measure.TagFamilies)
+}
+
+func tagFamily(tagFamilies []*databasev1.TagFamilySpec) error {
+ for i := range tagFamilies {
+ if tagFamilies[i].Name == "" {
+ return errors.New("tag family name is empty")
+ }
+ for j := range tagFamilies[i].Tags {
+ if tagFamilies[i].Tags[j].Name == "" {
+ return errors.New("tag name is empty")
+ }
+ if tagFamilies[i].Tags[j].Type == databasev1.TagType_TAG_TYPE_UNSPECIFIED {
+ return errors.New("tag type is unspecified")
+ }
+ }
+ }
+ return nil
+}
+
+// IndexRule validates the provided IndexRule object.
+// It checks for nil values, empty strings, and unspecified enum values.
+func IndexRule(indexRule *databasev1.IndexRule) error {
+ if indexRule == nil {
+ return errors.New("indexRule is nil")
+ }
+ if indexRule.Metadata == nil {
+ return errors.New("indexRule metadata is nil")
+ }
+ if indexRule.Metadata.Name == "" {
+ return errors.New("indexRule name is empty")
+ }
+ if indexRule.Metadata.Group == "" {
+ return errors.New("indexRule group is empty")
+ }
+ if indexRule.Metadata.Id <= 0 {
+ return errors.New("indexRule id is invalid")
+ }
+ if len(indexRule.Tags) == 0 {
+ return errors.New("indexRule tags is empty")
+ }
+ if indexRule.Type == databasev1.IndexRule_TYPE_UNSPECIFIED {
+ return errors.New("indexRule type is unspecified")
+ }
+ return nil
+}
+
+// IndexRuleBinding validates the provided IndexRuleBinding object.
+// It checks for nil values, empty strings, and unspecified enum values.
+func IndexRuleBinding(indexRuleBinding *databasev1.IndexRuleBinding) error {
+ if indexRuleBinding == nil {
+ return errors.New("indexRuleBinding is nil")
+ }
+ if indexRuleBinding.Metadata == nil {
+ return errors.New("indexRuleBinding metadata is nil")
+ }
+ if indexRuleBinding.Metadata.Name == "" {
+ return errors.New("indexRuleBinding name is empty")
+ }
+ if indexRuleBinding.Metadata.Group == "" {
+ return errors.New("indexRuleBinding group is empty")
+ }
+ if indexRuleBinding.Subject == nil {
+ return errors.New("indexRuleBinding subject is nil")
+ }
+ if indexRuleBinding.Subject.Name == "" {
+ return errors.New("indexRuleBinding subject name is empty")
+ }
+ if indexRuleBinding.Subject.Catalog == commonv1.Catalog_CATALOG_UNSPECIFIED {
+ return errors.New("indexRuleBinding subject catalog is unspecified")
+ }
+ if len(indexRuleBinding.Rules) == 0 {
+ return errors.New("indexRuleBinding rules is empty")
+ }
+ return nil
+}
+
+// TopNAggregation validates the provided TopNAggregation object.
+// It checks for nil values, empty strings, and unspecified enum values.
+func TopNAggregation(topNAggregation *databasev1.TopNAggregation) error {
+ if topNAggregation == nil {
+ return errors.New("topNAggregation is nil")
+ }
+ if topNAggregation.Metadata == nil {
+ return errors.New("topNAggregation metadata is nil")
+ }
+ if topNAggregation.Metadata.Name == "" {
+ return errors.New("topNAggregation name is empty")
+ }
+ if topNAggregation.Metadata.Group == "" {
+ return errors.New("topNAggregation group is empty")
+ }
+ if topNAggregation.SourceMeasure == nil {
+ return errors.New("topNAggregation sourceMeasure is nil")
+ }
+ if topNAggregation.SourceMeasure.Name == "" {
+ return errors.New("topNAggregation sourceMeasure name is empty")
+ }
+ if topNAggregation.SourceMeasure.Group == "" {
+ return errors.New("topNAggregation sourceMeasure group is empty")
+ }
+ if topNAggregation.CountersNumber <= 0 {
+ return errors.New("topNAggregation countersNumber is invalid")
+ }
+ if topNAggregation.FieldName == "" {
+ return errors.New("topNAggregation fieldName is empty")
+ }
+ return nil
+}
diff --git a/banyand/measure/metadata.go b/banyand/measure/metadata.go
index dece4a1f..c940844a 100644
--- a/banyand/measure/metadata.go
+++ b/banyand/measure/metadata.go
@@ -29,6 +29,7 @@ import (
"github.com/apache/skywalking-banyandb/api/common"
commonv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/common/v1"
databasev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
+ "github.com/apache/skywalking-banyandb/api/validate"
"github.com/apache/skywalking-banyandb/banyand/internal/storage"
"github.com/apache/skywalking-banyandb/banyand/metadata"
"github.com/apache/skywalking-banyandb/banyand/metadata/schema"
@@ -107,12 +108,20 @@ func (sr *schemaRepo) OnAddOrUpdate(metadata schema.Metadata) {
if g.Catalog != commonv1.Catalog_CATALOG_MEASURE {
return
}
+ if err := validate.GroupForStreamOrMeasure(g); err != nil {
+ sr.l.Warn().Err(err).Msg("group is ignored")
+ return
+ }
sr.SendMetadataEvent(resourceSchema.MetadataEvent{
Typ: resourceSchema.EventAddOrUpdate,
Kind: resourceSchema.EventKindGroup,
Metadata: g,
})
case schema.KindMeasure:
+ if err := validate.Measure(metadata.Spec.(*databasev1.Measure)); err != nil {
+ sr.l.Warn().Err(err).Msg("measure is ignored")
+ return
+ }
sr.SendMetadataEvent(resourceSchema.MetadataEvent{
Typ: resourceSchema.EventAddOrUpdate,
Kind: resourceSchema.EventKindResource,
@@ -124,6 +133,10 @@ func (sr *schemaRepo) OnAddOrUpdate(metadata schema.Metadata) {
sr.l.Warn().Msg("fail to convert message to IndexRuleBinding")
return
}
+ if err := validate.IndexRuleBinding(irb); err != nil {
+ sr.l.Warn().Err(err).Msg("index rule binding is ignored")
+ return
+ }
if irb.GetSubject().Catalog == commonv1.Catalog_CATALOG_MEASURE {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
stm, err := sr.metadata.MeasureRegistry().GetMeasure(ctx, &commonv1.Metadata{
@@ -141,6 +154,10 @@ func (sr *schemaRepo) OnAddOrUpdate(metadata schema.Metadata) {
})
}
case schema.KindIndexRule:
+ if err := validate.IndexRule(metadata.Spec.(*databasev1.IndexRule)); err != nil {
+ sr.l.Warn().Err(err).Msg("index rule is ignored")
+ return
+ }
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
subjects, err := sr.metadata.Subjects(ctx, metadata.Spec.(*databasev1.IndexRule), commonv1.Catalog_CATALOG_MEASURE)
@@ -155,6 +172,10 @@ func (sr *schemaRepo) OnAddOrUpdate(metadata schema.Metadata) {
})
}
case schema.KindTopNAggregation:
+ if err := validate.TopNAggregation(metadata.Spec.(*databasev1.TopNAggregation)); err != nil {
+ sr.l.Warn().Err(err).Msg("topNAggregation is ignored")
+ return
+ }
sr.SendMetadataEvent(resourceSchema.MetadataEvent{
Typ: resourceSchema.EventAddOrUpdate,
Kind: resourceSchema.EventKindTopNAgg,
diff --git a/banyand/measure/metadata_test.go b/banyand/measure/metadata_test.go
index 808a123d..d07b091d 100644
--- a/banyand/measure/metadata_test.go
+++ b/banyand/measure/metadata_test.go
@@ -119,12 +119,12 @@ var _ = Describe("Metadata", func() {
It("should update a new measure", func() {
// Remove the first tag from the entity
- measureSchema.Entity.TagNames = measureSchema.Entity.TagNames[1:]
- entitySize := len(measureSchema.Entity.TagNames)
+ newEntityTag := measureSchema.Entity.TagNames[0] + "_updated"
+ measureSchema.Entity.TagNames[0] = newEntityTag
modRevision, err := svcs.metadataService.MeasureRegistry().UpdateMeasure(context.TODO(), measureSchema)
- Expect(modRevision).ShouldNot(BeZero())
Expect(err).ShouldNot(HaveOccurred())
+ Expect(modRevision).ShouldNot(BeZero())
Eventually(func() bool {
val, err := svcs.measure.Measure(&commonv1.Metadata{
@@ -135,7 +135,7 @@ var _ = Describe("Metadata", func() {
return false
}
- return len(val.GetSchema().GetEntity().TagNames) == entitySize
+ return newEntityTag == val.GetSchema().Entity.TagNames[0]
}).WithTimeout(flags.EventuallyTimeout).Should(BeTrue())
})
})
diff --git a/banyand/metadata/schema/group.go b/banyand/metadata/schema/group.go
index 0ef85e59..7040bb2b 100644
--- a/banyand/metadata/schema/group.go
+++ b/banyand/metadata/schema/group.go
@@ -76,6 +76,12 @@ func (e *etcdSchemaRegistry) CreateGroup(ctx context.Context, group *commonv1.Gr
if group.UpdatedAt != nil {
group.UpdatedAt = timestamppb.Now()
}
+ if group.Metadata == nil {
+ return errors.New("metadata is required")
+ }
+ if group.Metadata.Name == "" {
+ return errors.New("metadata.name is required")
+ }
_, err := e.create(ctx, Metadata{
TypeMeta: TypeMeta{
Kind: KindGroup,
@@ -87,6 +93,15 @@ func (e *etcdSchemaRegistry) CreateGroup(ctx context.Context, group *commonv1.Gr
}
func (e *etcdSchemaRegistry) UpdateGroup(ctx context.Context, group *commonv1.Group) error {
+ if group.UpdatedAt != nil {
+ group.UpdatedAt = timestamppb.Now()
+ }
+ if group.Metadata == nil {
+ return errors.New("metadata is required")
+ }
+ if group.Metadata.Name == "" {
+ return errors.New("metadata.name is required")
+ }
_, err := e.update(ctx, Metadata{
TypeMeta: TypeMeta{
Kind: KindGroup,
diff --git a/banyand/metadata/schema/index.go b/banyand/metadata/schema/index.go
index 41b1b284..45687c2d 100644
--- a/banyand/metadata/schema/index.go
+++ b/banyand/metadata/schema/index.go
@@ -25,6 +25,7 @@ import (
commonv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/common/v1"
databasev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
+ "github.com/apache/skywalking-banyandb/api/validate"
)
var (
@@ -59,6 +60,9 @@ func (e *etcdSchemaRegistry) CreateIndexRuleBinding(ctx context.Context, indexRu
if indexRuleBinding.UpdatedAt != nil {
indexRuleBinding.UpdatedAt = timestamppb.Now()
}
+ if err := validate.IndexRuleBinding(indexRuleBinding); err != nil {
+ return err
+ }
_, err := e.create(ctx, Metadata{
TypeMeta: TypeMeta{
Kind: KindIndexRuleBinding,
@@ -71,6 +75,12 @@ func (e *etcdSchemaRegistry) CreateIndexRuleBinding(ctx context.Context, indexRu
}
func (e *etcdSchemaRegistry) UpdateIndexRuleBinding(ctx context.Context, indexRuleBinding *databasev1.IndexRuleBinding) error {
+ if indexRuleBinding.UpdatedAt != nil {
+ indexRuleBinding.UpdatedAt = timestamppb.Now()
+ }
+ if err := validate.IndexRuleBinding(indexRuleBinding); err != nil {
+ return err
+ }
_, err := e.update(ctx, Metadata{
TypeMeta: TypeMeta{
Kind: KindIndexRuleBinding,
@@ -131,6 +141,9 @@ func (e *etcdSchemaRegistry) CreateIndexRule(ctx context.Context, indexRule *dat
buf = append(buf, indexRule.Metadata.Name...)
indexRule.Metadata.Id = crc32.ChecksumIEEE(buf)
}
+ if err := validate.IndexRule(indexRule); err != nil {
+ return err
+ }
_, err := e.create(ctx, Metadata{
TypeMeta: TypeMeta{
Kind: KindIndexRule,
@@ -150,6 +163,12 @@ func (e *etcdSchemaRegistry) UpdateIndexRule(ctx context.Context, indexRule *dat
}
indexRule.Metadata.Id = existingIndexRule.Metadata.Id
}
+ if indexRule.UpdatedAt != nil {
+ indexRule.UpdatedAt = timestamppb.Now()
+ }
+ if err := validate.IndexRule(indexRule); err != nil {
+ return err
+ }
_, err := e.update(ctx, Metadata{
TypeMeta: TypeMeta{
Kind: KindIndexRule,
diff --git a/banyand/metadata/schema/measure.go b/banyand/metadata/schema/measure.go
index a17f8fcd..76ec00ac 100644
--- a/banyand/metadata/schema/measure.go
+++ b/banyand/metadata/schema/measure.go
@@ -25,6 +25,7 @@ import (
commonv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/common/v1"
databasev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
+ "github.com/apache/skywalking-banyandb/api/validate"
"github.com/apache/skywalking-banyandb/pkg/timestamp"
)
@@ -65,6 +66,17 @@ func (e *etcdSchemaRegistry) CreateMeasure(ctx context.Context, measure *databas
return 0, errors.Wrap(err, "interval is malformed")
}
}
+ if err := validate.Measure(measure); err != nil {
+ return 0, err
+ }
+ group := measure.Metadata.GetGroup()
+ g, err := e.GetGroup(ctx, group)
+ if err != nil {
+ return 0, err
+ }
+ if err := validate.GroupForStreamOrMeasure(g); err != nil {
+ return 0, err
+ }
return e.create(ctx, Metadata{
TypeMeta: TypeMeta{
Kind: KindMeasure,
@@ -76,11 +88,25 @@ func (e *etcdSchemaRegistry) CreateMeasure(ctx context.Context, measure *databas
}
func (e *etcdSchemaRegistry) UpdateMeasure(ctx context.Context, measure *databasev1.Measure) (int64, error) {
+ if measure.UpdatedAt != nil {
+ measure.UpdatedAt = timestamppb.Now()
+ }
if measure.GetInterval() != "" {
if _, err := timestamp.ParseDuration(measure.GetInterval()); err != nil {
return 0, errors.Wrap(err, "interval is malformed")
}
}
+ if err := validate.Measure(measure); err != nil {
+ return 0, err
+ }
+ group := measure.Metadata.GetGroup()
+ g, err := e.GetGroup(ctx, group)
+ if err != nil {
+ return 0, err
+ }
+ if err := validate.GroupForStreamOrMeasure(g); err != nil {
+ return 0, err
+ }
return e.update(ctx, Metadata{
TypeMeta: TypeMeta{
Kind: KindMeasure,
diff --git a/banyand/metadata/schema/property.go b/banyand/metadata/schema/property.go
index 1f79597b..fae0e27c 100644
--- a/banyand/metadata/schema/property.go
+++ b/banyand/metadata/schema/property.go
@@ -110,10 +110,31 @@ func (e *etcdSchemaRegistry) ApplyProperty(ctx context.Context, property *proper
return false, 0, 0, ErrClosed
}
defer e.closer.Done()
+ if property.Metadata == nil {
+ return false, 0, 0, BadRequest("metadata", "metadata should not be nil")
+ }
+ if property.Metadata.Container == nil {
+ return false, 0, 0, BadRequest("metadata.container", "container should not be nil")
+ }
+ if property.Metadata.Container.Name == "" {
+ return false, 0, 0, BadRequest("metadata.container.name", "container.name should not be empty")
+ }
+ if property.Metadata.Id == "" {
+ return false, 0, 0, BadRequest("metadata.id", "id should not be empty")
+ }
+ if len(property.Tags) == 0 {
+ return false, 0, 0, BadRequest("tags", "tags should not be empty")
+ }
m := transformKey(property.GetMetadata())
group := m.GetGroup()
- if _, getGroupErr := e.GetGroup(ctx, group); getGroupErr != nil {
+ if g, getGroupErr := e.GetGroup(ctx, group); getGroupErr != nil {
return false, 0, 0, errors.Wrap(getGroupErr, "group is not exist")
+ } else if g.Catalog != commonv1.Catalog_CATALOG_UNSPECIFIED {
+ return false, 0, 0, errors.New("group is not allowed to have properties")
+ }
+
+ if property.UpdatedAt != nil {
+ property.UpdatedAt = timestamppb.Now()
}
md := Metadata{
TypeMeta: TypeMeta{
diff --git a/banyand/metadata/schema/stream.go b/banyand/metadata/schema/stream.go
index 645a635d..81686ec7 100644
--- a/banyand/metadata/schema/stream.go
+++ b/banyand/metadata/schema/stream.go
@@ -24,6 +24,7 @@ import (
commonv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/common/v1"
databasev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
+ "github.com/apache/skywalking-banyandb/api/validate"
)
var streamKeyPrefix = "/streams/"
@@ -52,6 +53,20 @@ func (e *etcdSchemaRegistry) ListStream(ctx context.Context, opt ListOpt) ([]*da
}
func (e *etcdSchemaRegistry) UpdateStream(ctx context.Context, stream *databasev1.Stream) (int64, error) {
+ if stream.UpdatedAt != nil {
+ stream.UpdatedAt = timestamppb.Now()
+ }
+ if err := validate.Stream(stream); err != nil {
+ return 0, err
+ }
+ group := stream.Metadata.GetGroup()
+ g, err := e.GetGroup(ctx, group)
+ if err != nil {
+ return 0, err
+ }
+ if err := validate.GroupForStreamOrMeasure(g); err != nil {
+ return 0, err
+ }
return e.update(ctx, Metadata{
TypeMeta: TypeMeta{
Kind: KindStream,
@@ -67,11 +82,17 @@ func (e *etcdSchemaRegistry) CreateStream(ctx context.Context, stream *databasev
if stream.UpdatedAt != nil {
stream.UpdatedAt = timestamppb.Now()
}
+ if err := validate.Stream(stream); err != nil {
+ return 0, err
+ }
group := stream.Metadata.GetGroup()
- _, err := e.GetGroup(ctx, group)
+ g, err := e.GetGroup(ctx, group)
if err != nil {
return 0, err
}
+ if err := validate.GroupForStreamOrMeasure(g); err != nil {
+ return 0, err
+ }
return e.create(ctx, Metadata{
TypeMeta: TypeMeta{
Kind: KindStream,
diff --git a/banyand/metadata/schema/topn.go b/banyand/metadata/schema/topn.go
index 60597f1a..0eb4466f 100644
--- a/banyand/metadata/schema/topn.go
+++ b/banyand/metadata/schema/topn.go
@@ -24,6 +24,7 @@ import (
commonv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/common/v1"
databasev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
+ "github.com/apache/skywalking-banyandb/api/validate"
)
var topNAggregationKeyPrefix = "/topnagg/"
@@ -55,6 +56,9 @@ func (e *etcdSchemaRegistry) CreateTopNAggregation(ctx context.Context, topNAggr
if topNAggregation.UpdatedAt != nil {
topNAggregation.UpdatedAt = timestamppb.Now()
}
+ if err := validate.TopNAggregation(topNAggregation); err != nil {
+ return err
+ }
_, err := e.create(ctx, Metadata{
TypeMeta: TypeMeta{
Kind: KindTopNAggregation,
@@ -67,6 +71,12 @@ func (e *etcdSchemaRegistry) CreateTopNAggregation(ctx context.Context, topNAggr
}
func (e *etcdSchemaRegistry) UpdateTopNAggregation(ctx context.Context, topNAggregation *databasev1.TopNAggregation) error {
+ if topNAggregation.UpdatedAt != nil {
+ topNAggregation.UpdatedAt = timestamppb.Now()
+ }
+ if err := validate.TopNAggregation(topNAggregation); err != nil {
+ return err
+ }
_, err := e.update(ctx, Metadata{
TypeMeta: TypeMeta{
Kind: KindTopNAggregation,
diff --git a/banyand/metadata/schema/watcher_test.go b/banyand/metadata/schema/watcher_test.go
index 64ed142c..b7428fcc 100644
--- a/banyand/metadata/schema/watcher_test.go
+++ b/banyand/metadata/schema/watcher_test.go
@@ -116,6 +116,18 @@ var _ = ginkgo.Describe("Watcher", func() {
Metadata: &commonv1.Metadata{
Name: "testgroup-measure",
},
+ Catalog: commonv1.Catalog_CATALOG_MEASURE,
+ ResourceOpts: &commonv1.ResourceOpts{
+ ShardNum: 1,
+ SegmentInterval: &commonv1.IntervalRule{
+ Num: 1,
+ Unit: commonv1.IntervalRule_UNIT_DAY,
+ },
+ Ttl: &commonv1.IntervalRule{
+ Num: 3,
+ Unit: commonv1.IntervalRule_UNIT_DAY,
+ },
+ },
})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
var modRevision int64
@@ -125,9 +137,31 @@ var _ = ginkgo.Describe("Watcher", func() {
Name: fmt.Sprintf("testkey%d", i+1),
Group: "testgroup-measure",
},
+ Entity: &databasev1.Entity{
+ TagNames: []string{"testtag"},
+ },
+ TagFamilies: []*databasev1.TagFamilySpec{
+ {
+ Name: "testtagfamily",
+ Tags: []*databasev1.TagSpec{
+ {
+ Name: "testtag",
+ Type: databasev1.TagType_TAG_TYPE_STRING,
+ },
+ },
+ },
+ },
+ Fields: []*databasev1.FieldSpec{
+ {
+ Name: "testfield",
+ FieldType: databasev1.FieldType_FIELD_TYPE_INT,
+ EncodingMethod: databasev1.EncodingMethod_ENCODING_METHOD_GORILLA,
+ CompressionMethod: databasev1.CompressionMethod_COMPRESSION_METHOD_ZSTD,
+ },
+ },
})
- gomega.Expect(modRevision).ShouldNot(gomega.BeZero())
gomega.Expect(err).NotTo(gomega.HaveOccurred())
+ gomega.Expect(modRevision).ShouldNot(gomega.BeZero())
}
// Start the watcher
@@ -156,6 +190,18 @@ var _ = ginkgo.Describe("Watcher", func() {
Metadata: &commonv1.Metadata{
Name: "testgroup-stream",
},
+ Catalog: commonv1.Catalog_CATALOG_STREAM,
+ ResourceOpts: &commonv1.ResourceOpts{
+ ShardNum: 1,
+ SegmentInterval: &commonv1.IntervalRule{
+ Num: 1,
+ Unit: commonv1.IntervalRule_UNIT_DAY,
+ },
+ Ttl: &commonv1.IntervalRule{
+ Num: 3,
+ Unit: commonv1.IntervalRule_UNIT_DAY,
+ },
+ },
})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
var modRevision int64
@@ -164,6 +210,20 @@ var _ = ginkgo.Describe("Watcher", func() {
Name: "testkey",
Group: "testgroup-stream",
},
+ Entity: &databasev1.Entity{
+ TagNames: []string{"testtag"},
+ },
+ TagFamilies: []*databasev1.TagFamilySpec{
+ {
+ Name: "testtagfamily",
+ Tags: []*databasev1.TagSpec{
+ {
+ Name: "testtag",
+ Type: databasev1.TagType_TAG_TYPE_STRING,
+ },
+ },
+ },
+ },
})
gomega.Expect(modRevision).ShouldNot(gomega.BeZero())
gomega.Expect(err).NotTo(gomega.HaveOccurred())
diff --git a/banyand/stream/metadata.go b/banyand/stream/metadata.go
index 5e823902..fcc08065 100644
--- a/banyand/stream/metadata.go
+++ b/banyand/stream/metadata.go
@@ -29,6 +29,7 @@ import (
"github.com/apache/skywalking-banyandb/api/common"
commonv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/common/v1"
databasev1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
+ "github.com/apache/skywalking-banyandb/api/validate"
"github.com/apache/skywalking-banyandb/banyand/internal/storage"
"github.com/apache/skywalking-banyandb/banyand/metadata"
"github.com/apache/skywalking-banyandb/banyand/metadata/schema"
@@ -107,12 +108,20 @@ func (sr *schemaRepo) OnAddOrUpdate(metadata schema.Metadata) {
if g.Catalog != commonv1.Catalog_CATALOG_STREAM {
return
}
+ if err := validate.GroupForStreamOrMeasure(g); err != nil {
+ sr.l.Warn().Err(err).Msg("group is ignored")
+ return
+ }
sr.SendMetadataEvent(resourceSchema.MetadataEvent{
Typ: resourceSchema.EventAddOrUpdate,
Kind: resourceSchema.EventKindGroup,
Metadata: g,
})
case schema.KindStream:
+ if err := validate.Stream(metadata.Spec.(*databasev1.Stream)); err != nil {
+ sr.l.Warn().Err(err).Msg("stream is ignored")
+ return
+ }
sr.SendMetadataEvent(resourceSchema.MetadataEvent{
Typ: resourceSchema.EventAddOrUpdate,
Kind: resourceSchema.EventKindResource,
@@ -124,6 +133,10 @@ func (sr *schemaRepo) OnAddOrUpdate(metadata schema.Metadata) {
sr.l.Warn().Msg("fail to convert message to IndexRuleBinding")
return
}
+ if err := validate.IndexRuleBinding(irb); err != nil {
+ sr.l.Warn().Err(err).Msg("index rule binding is ignored")
+ return
+ }
if irb.GetSubject().Catalog == commonv1.Catalog_CATALOG_STREAM {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
stm, err := sr.metadata.StreamRegistry().GetStream(ctx, &commonv1.Metadata{
@@ -141,6 +154,10 @@ func (sr *schemaRepo) OnAddOrUpdate(metadata schema.Metadata) {
})
}
case schema.KindIndexRule:
+ if err := validate.IndexRule(metadata.Spec.(*databasev1.IndexRule)); err != nil {
+ sr.l.Warn().Err(err).Msg("index rule is ignored")
+ return
+ }
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
subjects, err := sr.metadata.Subjects(ctx, metadata.Spec.(*databasev1.IndexRule), commonv1.Catalog_CATALOG_STREAM)
diff --git a/docs/release.md b/docs/release.md
index 3efc69c9..4201b70e 100644
--- a/docs/release.md
+++ b/docs/release.md
@@ -75,7 +75,7 @@ Keys to verify the Release Candidate :
Guide to build the release from source :
- * https://github.com/apache/skywalking-banyandb/blob/v$VERSION/docs/installation/binaries.md
+ * https://github.com/apache/skywalking-banyandb/blob/v$VERSION/docs/installation/binaries.md#Build-From-Source
Voting will start now and will remain open for at least 72 hours, all PMC members are required to give their votes.
diff --git a/ui/src/components/Aside/index.vue b/ui/src/components/Aside/index.vue
index 67e5adaf..ca4aa64a 100644
--- a/ui/src/components/Aside/index.vue
+++ b/ui/src/components/Aside/index.vue
@@ -59,10 +59,10 @@ const data = reactive({
name: null,
catalog: 'CATALOG_STREAM',
shardNum: 1,
- segmentIntervalUnit: "UNIT_UNSPECIFIED",
+ segmentIntervalUnit: "UNIT_DAY",
segmentIntervalNum: 1,
- ttlUnit: "UNIT_UNSPECIFIED",
- ttlNum: 1
+ ttlUnit: "UNIT_DAY",
+ ttlNum: 3
},
activeMenu: '',
search: '',
@@ -868,10 +868,10 @@ function clearGroupForm() {
name: null,
catalog: 'CATALOG_STREAM',
shardNum: 1,
- segmentIntervalUnit: "UNIT_UNSPECIFIED",
+ segmentIntervalUnit: "UNIT_DAY",
segmentIntervalNum: 1,
- ttlUnit: "UNIT_UNSPECIFIED",
- ttlNum: 1
+ ttlUnit: "UNIT_DAY",
+ ttlNum: 3
}
}
function initActiveMenu() {
@@ -1103,7 +1103,6 @@ initActiveMenu()
</el-form-item>
<el-form-item label="segment interval unit" :label-width="data.formLabelWidth" prop="segmentIntervalUnit">
<el-select v-model="data.groupForm.segmentIntervalUnit" placeholder="please select" style="width: 100%">
- <el-option label="UNIT_UNSPECIFIED" value="UNIT_UNSPECIFIED"></el-option>
<el-option label="UNIT_HOUR" value="UNIT_HOUR"></el-option>
<el-option label="UNIT_DAY" value="UNIT_DAY"></el-option>
</el-select>
@@ -1113,7 +1112,6 @@ initActiveMenu()
</el-form-item>
<el-form-item label="ttl unit" :label-width="data.formLabelWidth" prop="ttlUnit">
<el-select v-model="data.groupForm.ttlUnit" placeholder="please select" style="width: 100%">
- <el-option label="UNIT_UNSPECIFIED" value="UNIT_UNSPECIFIED"></el-option>
<el-option label="UNIT_HOUR" value="UNIT_HOUR"></el-option>
<el-option label="UNIT_DAY" value="UNIT_DAY"></el-option>
</el-select>
diff --git a/ui/src/components/Editor/fieldsEditor.vue b/ui/src/components/Editor/fieldsEditor.vue
index 2e525911..d1fe4b5b 100644
--- a/ui/src/components/Editor/fieldsEditor.vue
+++ b/ui/src/components/Editor/fieldsEditor.vue
@@ -37,10 +37,6 @@ const data = reactive({
})
const typeOptions = [
- {
- value: 'FIELD_TYPE_UNSPECIFIED',
- label: 'UNSPECIFIED'
- },
{
value: 'FIELD_TYPE_STRING',
label: 'STRING'
@@ -59,20 +55,12 @@ const typeOptions = [
}
]
const encodingMethodOptions = [
- {
- value: 'ENCODING_METHOD_UNSPECIFIED',
- label: 'UNSPECIFIED'
- },
{
value: 'ENCODING_METHOD_GORILLA',
label: 'GORILLA'
}
]
const compressionMethodOptions = [
- {
- value: 'COMPRESSION_METHOD_UNSPECIFIED',
- label: 'UNSPECIFIED'
- },
{
value: 'COMPRESSION_METHOD_ZSTD',
label: 'ZSTD'
diff --git a/ui/src/components/Editor/tagEditor.vue b/ui/src/components/Editor/tagEditor.vue
index a4c9c84b..b5df7723 100644
--- a/ui/src/components/Editor/tagEditor.vue
+++ b/ui/src/components/Editor/tagEditor.vue
@@ -73,10 +73,6 @@ const typeOptions = [
{
value: 'TAG_TYPE_DATA_BINARY',
label: 'DATA_BINARY'
- },
- {
- value: 'TAG_TYPE_UNSPECIFIED',
- label: 'UNSPECIFIED'
}
]
const validateTag = (rule: any, value: any, callback: any) => {
diff --git a/ui/src/components/IndexRule/Editor.vue b/ui/src/components/IndexRule/Editor.vue
index 3f932cc2..56e2543c 100644
--- a/ui/src/components/IndexRule/Editor.vue
+++ b/ui/src/components/IndexRule/Editor.vue
@@ -59,10 +59,6 @@ const rules = {
]
}
const typeList = [
- {
- label: "TYPE_UNSPECIFIED",
- value: "TYPE_UNSPECIFIED"
- },
{
label: "TYPE_INVERTED",
value: "TYPE_INVERTED"
diff --git a/ui/src/components/TopNAggregation/Editor.vue b/ui/src/components/TopNAggregation/Editor.vue
index dfff02ed..149fa084 100644
--- a/ui/src/components/TopNAggregation/Editor.vue
+++ b/ui/src/components/TopNAggregation/Editor.vue
@@ -79,10 +79,6 @@ const rules = {
]
}
const fieldValueSortList = [
- {
- label: "SORT_UNSPECIFIED",
- value: "SORT_UNSPECIFIED"
- },
{
label: "SORT_DESC",
value: "SORT_DESC"