Skip to content

Commit

Permalink
Merge branch 'semver' of github.com:splitio/split-synchronizer into s…
Browse files Browse the repository at this point in the history
…emver
  • Loading branch information
mmelograno committed May 3, 2024
2 parents 218ab96 + 00c9951 commit ba224ea
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 31 deletions.
2 changes: 1 addition & 1 deletion splitio/commitversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ This file is created automatically, please do not edit
*/

// CommitVersion is the version of the last commit previous to release
const CommitVersion = "765bffc"
const CommitVersion = "eb1a57b"
2 changes: 1 addition & 1 deletion splitio/producer/conf/sections.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type Main struct {
Integrations conf.Integrations `json:"integrations" s-nested:"true"`
Logging conf.Logging `json:"logging" s-nested:"true"`
Healthcheck Healthcheck `json:"healthcheck" s-nested:"true"`
SpecVersion string
SpecVersion string `json:"specVersion" s-cli:"spec-version" s-def:"1.1" s-desc:"Spec version for flags"`
}

// BuildAdvancedConfig generates a commons-compatible advancedconfig with default + overriden parameters
Expand Down
1 change: 0 additions & 1 deletion splitio/producer/initialization.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ const (
// Start initialize the producer mode
func Start(logger logging.LoggerInterface, cfg *conf.Main) error {
// Getting initial config data
cfg.SpecVersion = "1.0" // @TODO Until is implemented
advanced := cfg.BuildAdvancedConfig()
advanced.AuthSpecVersion = cfg.SpecVersion
advanced.FlagsSpecVersion = cfg.SpecVersion
Expand Down
23 changes: 16 additions & 7 deletions splitio/producer/initialization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ func TestSanitizeRedisWithForcedCleanup(t *testing.T) {

func TestSanitizeRedisWithRedisEqualApiKey(t *testing.T) {
cfg := getDefaultConf()
cfg.Apikey = "djasghdhjasfganyr73dsah9"
cfg.Apikey = "983564etyrudhijfgknf9i08euh"
cfg.SpecVersion = "1.0"

logger := logging.NewLogger(nil)

Expand All @@ -139,9 +140,10 @@ func TestSanitizeRedisWithRedisEqualApiKey(t *testing.T) {
if err != nil {
t.Error("It should be nil")
}
hash := util.HashAPIKey(cfg.Apikey + cfg.SpecVersion + strings.Join(cfg.FlagSetsFilter, "::"))

redisClient.Set("SPLITIO.test1", "123", 0)
redisClient.Set("SPLITIO.hash", "3376912823", 0)
redisClient.Set("SPLITIO.hash", hash, 0)

miscStorage := predis.NewMiscStorage(redisClient, logger)
err = sanitizeRedis(cfg, miscStorage, logger)
Expand All @@ -155,16 +157,18 @@ func TestSanitizeRedisWithRedisEqualApiKey(t *testing.T) {
}

val, _ = redisClient.Get("SPLITIO.hash")
if val != "3376912823" {
if val != strconv.FormatUint(uint64(hash), 10) {
t.Error("Incorrect apikey hash set in redis after sanitization operation.")
}

redisClient.Del("SPLITIO.hash")
redisClient.Del("SPLITIO.test1")
}

func TestSanitizeRedisWithRedisDifferentApiKey(t *testing.T) {
cfg := getDefaultConf()
cfg.Apikey = "983564etyrudhijfgknf9i08euh"
cfg.SpecVersion = "1.0"

logger := logging.NewLogger(nil)

Expand All @@ -177,9 +181,12 @@ func TestSanitizeRedisWithRedisDifferentApiKey(t *testing.T) {
if err != nil {
t.Error("It should be nil")
}
hash := util.HashAPIKey("djasghdhjasfganyr73dsah9" + cfg.SpecVersion + strings.Join(cfg.FlagSetsFilter, "::"))

redisClient.Set("SPLITIO.test1", "123", 0)
redisClient.Set("SPLITIO.hash", "3376912823", 0)
redisClient.Set("SPLITIO.hash", "3216514561", 0)

hash = util.HashAPIKey(cfg.Apikey + cfg.SpecVersion + strings.Join(cfg.FlagSetsFilter, "::"))

miscStorage := predis.NewMiscStorage(redisClient, logger)
err = sanitizeRedis(cfg, miscStorage, logger)
Expand All @@ -193,20 +200,22 @@ func TestSanitizeRedisWithRedisDifferentApiKey(t *testing.T) {
}

val, _ = redisClient.Get("SPLITIO.hash")
if val != "1497926959" {
t.Error("Incorrect apikey hash set in redis after sanitization operation.")
if val != strconv.FormatUint(uint64(hash), 10) {
t.Error("Incorrect apikey hash set in redis after sanitization operation.", val)
}

redisClient.Del("SPLITIO.hash")
redisClient.Del("SPLITIO.test1")
}

func TestSanitizeRedisWithForcedCleanupByFlagSets(t *testing.T) {
cfg := getDefaultConf()
cfg.SpecVersion = "1.0"
cfg.Apikey = "983564etyrudhijfgknf9i08euh"
cfg.Initialization.ForceFreshStartup = true
cfg.FlagSetsFilter = []string{"flagset1", "flagset2"}

hash := util.HashAPIKey(cfg.Apikey + strings.Join(cfg.FlagSetsFilter, "::"))
hash := util.HashAPIKey(cfg.Apikey + cfg.SpecVersion + strings.Join(cfg.FlagSetsFilter, "::"))

logger := logging.NewLogger(nil)

Expand Down
30 changes: 16 additions & 14 deletions splitio/proxy/caching/caching.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,25 @@ func MakeProxyCache() *gincache.Middleware {
return gincache.New(&gincache.Options{
SuccessfulOnly: true, // we're not interested in caching non-200 responses
Size: cacheSize,
KeyFactory: func(ctx *gin.Context) string {

var encodingPrefix string
if strings.Contains(ctx.Request.Header.Get("Accept-Encoding"), "gzip") {
encodingPrefix = "gzip::"
}

if strings.HasPrefix(ctx.Request.URL.Path, "/api/auth") || strings.HasPrefix(ctx.Request.URL.Path, "/api/v2/auth") {
// For auth requests, since we don't support streaming yet, we only need a single entry in the table,
// so we strip the query-string which contains the user-list
return encodingPrefix + ctx.Request.URL.Path
}
return encodingPrefix + ctx.Request.URL.Path + ctx.Request.URL.RawQuery
},
KeyFactory: keyFactoryFN,
// we make each request handler responsible for generating the surrogates.
// this way we can use segment names as surrogates for mysegments & segment changes
// with a lot less work
SurrogateFactory: func(ctx *gin.Context) []string { return ctx.GetStringSlice(SurrogateContextKey) },
})
}

func keyFactoryFN(ctx *gin.Context) string {

var encodingPrefix string
if strings.Contains(ctx.Request.Header.Get("Accept-Encoding"), "gzip") {
encodingPrefix = "gzip::"
}

if strings.HasPrefix(ctx.Request.URL.Path, "/api/auth") || strings.HasPrefix(ctx.Request.URL.Path, "/api/v2/auth") {
// For auth requests, since we don't support streaming yet, we only need a single entry in the table,
// so we strip the query-string which contains the user-list
return encodingPrefix + ctx.Request.URL.Path
}
return encodingPrefix + ctx.Request.URL.Path + ctx.Request.URL.RawQuery
}
14 changes: 14 additions & 0 deletions splitio/proxy/caching/caching_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
package caching

import (
"net/http"
"net/url"
"testing"

"github.com/gin-gonic/gin"
"github.com/splitio/go-split-commons/v5/dtos"
"github.com/stretchr/testify/assert"
)

func TestCacheKeysDoNotOverlap(t *testing.T) {

url1, _ := url.Parse("http://proxy.split.io/api/spitChanges?since=-1")
c1 := &gin.Context{Request: &http.Request{URL: url1}}

url2, _ := url.Parse("http://proxy.split.io/api/spitChanges?s=1.1&since=-1")
c2 := &gin.Context{Request: &http.Request{URL: url2}}

assert.NotEqual(t, keyFactoryFN(c1), keyFactoryFN(c2))
}

func TestSegmentSurrogates(t *testing.T) {
assert.Equal(t, segmentPrefix+"segment1", MakeSurrogateForSegmentChanges("segment1"))
assert.NotEqual(t, MakeSurrogateForSegmentChanges("segment1"), MakeSurrogateForSegmentChanges("segment2"))
Expand Down
1 change: 1 addition & 0 deletions splitio/proxy/conf/sections.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Main struct {
Logging conf.Logging `json:"logging" s-nested:"true"`
Healthcheck Healthcheck `json:"healthcheck" s-nested:"true"`
Observability Observability `json:"observability" s-nested:"true"`
SpecVersion string `json:"specVersion" s-cli:"spec-version" s-def:"1.1" s-desc:"Spec version for flags"`
}

// BuildAdvancedConfig generates a commons-compatible advancedconfig with default + overriden parameters
Expand Down
34 changes: 34 additions & 0 deletions splitio/proxy/controllers/sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import (

"github.com/gin-gonic/gin"
"github.com/splitio/go-split-commons/v5/dtos"
"github.com/splitio/go-split-commons/v5/engine/grammar"
"github.com/splitio/go-split-commons/v5/engine/grammar/matchers"
"github.com/splitio/go-split-commons/v5/service"
"github.com/splitio/go-split-commons/v5/service/api/specs"
"github.com/splitio/go-toolkit/v5/logging"
"golang.org/x/exp/slices"

Expand All @@ -18,13 +21,18 @@ import (
"github.com/splitio/split-synchronizer/v5/splitio/proxy/storage"
)

const (
labelUnsupportedMatcher = "targeting rule type unsupported by sdk"
)

// SdkServerController bundles all request handler for sdk-server apis
type SdkServerController struct {
logger logging.LoggerInterface
fetcher service.SplitFetcher
proxySplitStorage storage.ProxySplitStorage
proxySegmentStorage storage.ProxySegmentStorage
fsmatcher flagsets.FlagSetMatcher
versionFilter specs.SplitVersionFilter
}

// NewSdkServerController instantiates a new sdk server controller
Expand All @@ -42,6 +50,7 @@ func NewSdkServerController(
proxySplitStorage: proxySplitStorage,
proxySegmentStorage: proxySegmentStorage,
fsmatcher: fsmatcher,
versionFilter: specs.NewSplitVersionFilter(),
}
}

Expand Down Expand Up @@ -77,6 +86,13 @@ func (c *SdkServerController) SplitChanges(ctx *gin.Context) {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

spec, _ := ctx.GetQuery("s")
if spec != specs.FLAG_V1_1 {
spec = specs.FLAG_V1_0
}
splits.Splits = c.patchUnsupportedMatchers(splits.Splits, spec)

ctx.JSON(http.StatusOK, splits)
ctx.Set(caching.SurrogateContextKey, []string{caching.SplitSurrogate})
ctx.Set(caching.StickyContextKey, true)
Expand Down Expand Up @@ -143,3 +159,21 @@ func (c *SdkServerController) fetchSplitChangesSince(since int64, sets []string)
fetchOptions := service.MakeFlagRequestParams().WithChangeNumber(since).WithFlagSetsFilter(strings.Join(sets, ",")) // at this point the sets have been sanitized & sorted
return c.fetcher.Fetch(fetchOptions)
}

func (c *SdkServerController) patchUnsupportedMatchers(splits []dtos.SplitDTO, version string) []dtos.SplitDTO {
for si := range splits {
for ci := range splits[si].Conditions {
for mi := range splits[si].Conditions[ci].MatcherGroup.Matchers {
if c.versionFilter.ShouldFilter(splits[si].Conditions[ci].MatcherGroup.Matchers[mi].MatcherType, version) {
splits[si].Conditions[ci].ConditionType = grammar.ConditionTypeWhitelist
splits[si].Conditions[ci].MatcherGroup.Matchers[mi].MatcherType = matchers.MatcherTypeAllKeys
splits[si].Conditions[ci].MatcherGroup.Matchers[mi].String = nil
splits[si].Conditions[ci].Label = labelUnsupportedMatcher
splits[si].Conditions[ci].Partitions = []dtos.PartitionDTO{{Treatment: "control", Size: 100}}
}
}
}
}

return splits
}
Loading

0 comments on commit ba224ea

Please sign in to comment.