Skip to content

Commit

Permalink
Analytics: allow more control over merging of document contexts
Browse files Browse the repository at this point in the history
Signed-off-by: Richard Kosegi <richard.kosegi@gmail.com>
  • Loading branch information
rkosegi committed May 9, 2024
1 parent 6bb5dfa commit b910992
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 13 deletions.
59 changes: 48 additions & 11 deletions analytics/document_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package analytics

import (
"errors"
"fmt"
"github.com/rkosegi/yaml-toolkit/dom"
"github.com/rkosegi/yaml-toolkit/k8s"
Expand All @@ -33,6 +34,10 @@ const (
wildcardTag = "*"
)

var (
ErrLayerAlreadyExists = errors.New("layer already exists")
)

var (
b = dom.Builder()
defaultOpts = []AddLayerOpt{
Expand Down Expand Up @@ -60,6 +65,8 @@ type docContext struct {
doc dom.ContainerBuilder
// list of tags
tags []string
// function used to merge 2 contexts
mergeFn func(ctx *docContext, doc dom.ContainerBuilder) error
}

// DefaultFileDecoderProvider is FileDecoderProvider that uses file suffix to choose dom.DecoderFunc
Expand All @@ -82,6 +89,25 @@ func WithTags(tag ...string) AddLayerOpt {
}
}

// MergeTags does not consider newly added dom.ContainerBuilder, it just merges tags into existing context
func MergeTags() AddLayerOpt {
return func(_ *documentSet, _ string, context *docContext) {
context.mergeFn = func(newCtx *docContext, doc dom.ContainerBuilder) error {
context.tags = utils.Unique(append(context.tags, newCtx.tags...))
return nil
}
}
}

// MustCreate ensures that layer does not already exist
func MustCreate() AddLayerOpt {
return func(_ *documentSet, _ string, context *docContext) {
context.mergeFn = func(newCtx *docContext, doc dom.ContainerBuilder) error {
return ErrLayerAlreadyExists
}
}
}

func (ds *documentSet) applyOpts(name string, ctx *docContext, opts ...AddLayerOpt) *docContext {
for _, opt := range defaultOpts {
opt(ds, name, ctx)
Expand All @@ -92,31 +118,43 @@ func (ds *documentSet) applyOpts(name string, ctx *docContext, opts ...AddLayerO
return ctx
}

func (ds *documentSet) addContext(name string, doc dom.ContainerBuilder, ctx *docContext) {
ctx.doc = doc
ds.ctxMap[name] = ctx
func (ds *documentSet) addContext(name string, doc dom.ContainerBuilder, newCtx *docContext) error {
existingCtx, exists := ds.ctxMap[name]
if exists {
if newCtx.mergeFn != nil {
err := newCtx.mergeFn(existingCtx, doc)
if err != nil {
return err
}
}
ds.ctxMap[name] = newCtx
return nil
} else {
newCtx.doc = doc
ds.ctxMap[name] = newCtx
return nil
}
}

func (ds *documentSet) newContext(name string, opts ...AddLayerOpt) *docContext {
return ds.applyOpts(name, &docContext{}, opts...)
}

func (ds *documentSet) AddDocument(name string, doc dom.ContainerBuilder, opts ...AddLayerOpt) {
ds.addContext(name, doc, ds.newContext(name, opts...))
func (ds *documentSet) AddDocument(name string, doc dom.ContainerBuilder, opts ...AddLayerOpt) error {
return ds.addContext(name, doc, ds.newContext(name, opts...))
}

func (ds *documentSet) AddUnnamedDocument(doc dom.ContainerBuilder, opts ...AddLayerOpt) {
func (ds *documentSet) AddUnnamedDocument(doc dom.ContainerBuilder, opts ...AddLayerOpt) error {
ds.unnamedLayerId++
ds.AddDocument(fmt.Sprintf("default__%d", ds.unnamedLayerId), doc, opts...)
return ds.AddDocument(fmt.Sprintf("default__%d", ds.unnamedLayerId), doc, opts...)
}

func (ds *documentSet) AddDocumentFromReader(name string, r io.Reader, dec dom.DecoderFunc, opts ...AddLayerOpt) error {
cb, err := b.FromReader(r, dec)
if err != nil {
return err
}
ds.addContext(name, cb, ds.newContext(name, opts...))
return nil
return ds.AddDocument(name, cb, opts...)
}

func (ds *documentSet) AddDocumentFromFile(file string, dec dom.DecoderFunc, opts ...AddLayerOpt) error {
Expand Down Expand Up @@ -164,9 +202,8 @@ func (ds *documentSet) AddPropertiesFromManifest(manifest string, opts ...AddLay
if err != nil {
return err
} else {
ds.AddDocument(manifest, doc.Document(), opts...)
return ds.AddDocument(manifest, doc.Document(), opts...)
}
return nil
}

func containsAnyOf(col []string, contains []string) bool {
Expand Down
13 changes: 13 additions & 0 deletions analytics/document_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,16 @@ func TestDocumentSetAdd(t *testing.T) {
assert.Nil(t, ds.NamedDocument("invalid"))
assert.NotNil(t, ds.NamedDocument("../testdata/cm2.yaml"))
}

func TestDocumentSetMergeTags(t *testing.T) {
ds := NewDocumentSet()
assert.NoError(t, ds.AddPropertiesFromManifest("../testdata/secret3.yaml", WithTags("prop1")))
assert.NoError(t, ds.AddPropertiesFromManifest("../testdata/secret3.yaml", WithTags("prop2"), MergeTags()))
assert.Equal(t, 3, len(ds.(*documentSet).ctxMap["../testdata/secret3.yaml"].tags))
}

func TestDocumentSetMustCreate(t *testing.T) {
ds := NewDocumentSet()
assert.NoError(t, ds.AddPropertiesFromManifest("../testdata/secret3.yaml", WithTags("prop1")))
assert.Error(t, ds.AddPropertiesFromManifest("../testdata/secret3.yaml", WithTags("prop2"), MustCreate()))
}
4 changes: 2 additions & 2 deletions analytics/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ type DocumentSet interface {
AsOne() dom.OverlayDocument

// AddDocument adds given document into documentSet.
AddDocument(name string, doc dom.ContainerBuilder, opts ...AddLayerOpt)
AddDocument(name string, doc dom.ContainerBuilder, opts ...AddLayerOpt) error

// AddUnnamedDocument adds document that has no particular name designation, one will be generated internally
AddUnnamedDocument(doc dom.ContainerBuilder, opts ...AddLayerOpt)
AddUnnamedDocument(doc dom.ContainerBuilder, opts ...AddLayerOpt) error

// AddDocumentFromFile calls AddDocumentFromFileWithDecoder with second argument set to DefaultFileDecoderProvider.
AddDocumentFromFile(file string, dec dom.DecoderFunc, opts ...AddLayerOpt) error
Expand Down

0 comments on commit b910992

Please sign in to comment.