Skip to content

Commit

Permalink
Merge branch 'eval-v2' into mp/rollouts-ui-its
Browse files Browse the repository at this point in the history
  • Loading branch information
markphelps committed Jul 18, 2023
2 parents 9e58d2c + 61f5307 commit c694ac8
Show file tree
Hide file tree
Showing 14 changed files with 226 additions and 41 deletions.
34 changes: 34 additions & 0 deletions build/testing/integration/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,40 @@ func API(t *testing.T, ctx context.Context, client sdk.SDK, namespace string, au
assert.Equal(t, float32(50.0), rolloutThreshold.Rule.(*flipt.Rollout_Threshold).Threshold.Percentage)
assert.Equal(t, true, rolloutThreshold.Rule.(*flipt.Rollout_Threshold).Threshold.Value)

t.Log(`Ensure multiple rollouts with same rank can not be created`)

_, err = client.Flipt().CreateRollout(ctx, &flipt.CreateRolloutRequest{
NamespaceKey: namespace,
FlagKey: "boolean_disabled",
Description: "should not work",
Rank: 2,
Rule: &flipt.CreateRolloutRequest_Threshold{
Threshold: &flipt.RolloutThreshold{
Percentage: 50,
Value: true,
},
},
})

assert.EqualError(t, err, "rpc error: code = InvalidArgument desc = rank number: 2 already exists")

t.Log(`Ensure that rollout with a non-existent segment can not be created`)

_, err = client.Flipt().CreateRollout(ctx, &flipt.CreateRolloutRequest{
NamespaceKey: namespace,
FlagKey: "boolean_disabled",
Description: "matches a segment",
Rank: 5,
Rule: &flipt.CreateRolloutRequest_Segment{
Segment: &flipt.RolloutSegment{
SegmentKey: "thisdoesnotexist",
Value: true,
},
},
})

assert.EqualError(t, err, fmt.Sprintf("rpc error: code = NotFound desc = flag \"%s/%s or segment %s\" not found", namespace, "boolean_disabled", "thisdoesnotexist"))

rollouts, err := client.Flipt().ListRollouts(ctx, &flipt.ListRolloutRequest{
NamespaceKey: namespace,
FlagKey: "boolean_disabled",
Expand Down
8 changes: 4 additions & 4 deletions build/testing/integration/readonly/readonly_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ func TestReadOnly(t *testing.T) {
require.NoError(t, err)

assert.Equal(t, evaluation.EvaluationReason_DEFAULT_EVALUATION_REASON, result.Reason)
assert.False(t, result.Value, "default flag value should be false")
assert.False(t, result.Enabled, "default flag value should be false")
})

t.Run("percentage match", func(t *testing.T) {
Expand All @@ -512,7 +512,7 @@ func TestReadOnly(t *testing.T) {
require.NoError(t, err)

assert.Equal(t, evaluation.EvaluationReason_MATCH_EVALUATION_REASON, result.Reason)
assert.True(t, result.Value, "boolean evaluation value should be true")
assert.True(t, result.Enabled, "boolean evaluation value should be true")
})

t.Run("segment match", func(t *testing.T) {
Expand All @@ -528,7 +528,7 @@ func TestReadOnly(t *testing.T) {
require.NoError(t, err)

assert.Equal(t, evaluation.EvaluationReason_MATCH_EVALUATION_REASON, result.Reason)
assert.True(t, result.Value, "segment evaluation value should be true")
assert.True(t, result.Enabled, "segment evaluation value should be true")
})
})

Expand Down Expand Up @@ -563,7 +563,7 @@ func TestReadOnly(t *testing.T) {

b, ok := result.Responses[0].Response.(*evaluation.EvaluationResponse_BooleanResponse)
assert.True(t, ok, "response should be boolean evaluation response")
assert.True(t, b.BooleanResponse.Value, "boolean response should have true value")
assert.True(t, b.BooleanResponse.Enabled, "boolean response should have true value")
assert.Equal(t, evaluation.EvaluationReason_MATCH_EVALUATION_REASON, b.BooleanResponse.Reason)
assert.Equal(t, evaluation.EvaluationResponseType_BOOLEAN_EVALUATION_RESPONSE_TYPE, result.Responses[0].Type)

Expand Down
1 change: 1 addition & 0 deletions config/migrations/cockroachdb/6_flag_type.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE flags ADD COLUMN type INTEGER DEFAULT 0 NOT NULL;
28 changes: 28 additions & 0 deletions config/migrations/cockroachdb/7_rollouts.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
CREATE TABLE IF NOT EXISTS rollouts (
id VARCHAR(255) PRIMARY KEY UNIQUE NOT NULL,
namespace_key VARCHAR(255) NOT NULL REFERENCES namespaces ON DELETE CASCADE,
flag_key VARCHAR(255) NOT NULL,
type INTEGER DEFAULT 0 NOT NULL,
description TEXT NOT NULL,
rank INTEGER DEFAULT 1 NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY (namespace_key, flag_key) REFERENCES flags (namespace_key, key) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS rollout_thresholds (
id VARCHAR(255) PRIMARY KEY UNIQUE NOT NULL,
namespace_key VARCHAR(255) NOT NULL REFERENCES namespaces ON DELETE CASCADE,
rollout_id VARCHAR(255) UNIQUE NOT NULL REFERENCES rollouts ON DELETE CASCADE,
percentage float DEFAULT 0 NOT NULL,
value BOOLEAN DEFAULT FALSE NOT NULL
);

CREATE TABLE IF NOT EXISTS rollout_segments (
id VARCHAR(255) PRIMARY KEY UNIQUE NOT NULL,
namespace_key VARCHAR(255) NOT NULL REFERENCES namespaces ON DELETE CASCADE,
rollout_id VARCHAR(255) NOT NULL REFERENCES rollouts ON DELETE CASCADE,
segment_key VARCHAR(255) NOT NULL,
value BOOLEAN DEFAULT FALSE NOT NULL,
FOREIGN KEY (namespace_key, segment_key) REFERENCES segments (namespace_key, key) ON DELETE CASCADE
);
1 change: 1 addition & 0 deletions config/migrations/mysql/7_flag_type.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE flags ADD COLUMN `type` INTEGER DEFAULT 0 NOT NULL;
36 changes: 36 additions & 0 deletions config/migrations/mysql/8_rollouts.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
CREATE TABLE IF NOT EXISTS rollouts (
id VARCHAR(255) UNIQUE NOT NULL,
namespace_key VARCHAR(255) NOT NULL,
flag_key VARCHAR(255) NOT NULL,
type INTEGER DEFAULT 0 NOT NULL,
description TEXT NOT NULL,
`rank` INTEGER DEFAULT 1 NOT NULL,
created_at TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP(6) NOT NULL,
updated_at TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP(6) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (namespace_key) REFERENCES namespaces (`key`) ON DELETE CASCADE,
FOREIGN KEY (namespace_key, flag_key) REFERENCES flags (namespace_key, `key`) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS rollout_thresholds (
id VARCHAR(255) UNIQUE NOT NULL,
namespace_key VARCHAR(255) NOT NULL,
rollout_id VARCHAR(255) UNIQUE NOT NULL,
percentage float DEFAULT 0 NOT NULL,
value BOOLEAN DEFAULT FALSE NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (namespace_key) REFERENCES namespaces (`key`) ON DELETE CASCADE,
FOREIGN KEY (rollout_id) REFERENCES rollouts (id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS rollout_segments (
id VARCHAR(255) UNIQUE NOT NULL,
namespace_key VARCHAR(255) NOT NULL,
rollout_id VARCHAR(255) NOT NULL,
segment_key VARCHAR(255) NOT NULL,
value BOOLEAN DEFAULT FALSE NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (namespace_key) REFERENCES namespaces (`key`) ON DELETE CASCADE,
FOREIGN KEY (rollout_id) REFERENCES rollouts (id) ON DELETE CASCADE,
FOREIGN KEY (namespace_key, segment_key) REFERENCES segments (namespace_key, `key`) ON DELETE CASCADE
);
28 changes: 28 additions & 0 deletions config/migrations/postgres/10_rollouts.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
CREATE TABLE IF NOT EXISTS rollouts (
id VARCHAR(255) PRIMARY KEY UNIQUE NOT NULL,
namespace_key VARCHAR(255) NOT NULL REFERENCES namespaces ON DELETE CASCADE,
flag_key VARCHAR(255) NOT NULL,
type INTEGER DEFAULT 0 NOT NULL,
description TEXT NOT NULL,
rank INTEGER DEFAULT 1 NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY (namespace_key, flag_key) REFERENCES flags (namespace_key, key) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS rollout_thresholds (
id VARCHAR(255) PRIMARY KEY UNIQUE NOT NULL,
namespace_key VARCHAR(255) NOT NULL REFERENCES namespaces ON DELETE CASCADE,
rollout_id VARCHAR(255) UNIQUE NOT NULL REFERENCES rollouts ON DELETE CASCADE,
percentage float DEFAULT 0 NOT NULL,
value BOOLEAN DEFAULT FALSE NOT NULL
);

CREATE TABLE IF NOT EXISTS rollout_segments (
id VARCHAR(255) PRIMARY KEY UNIQUE NOT NULL,
namespace_key VARCHAR(255) NOT NULL REFERENCES namespaces ON DELETE CASCADE,
rollout_id VARCHAR(255) NOT NULL REFERENCES rollouts ON DELETE CASCADE,
segment_key VARCHAR(255) NOT NULL,
value BOOLEAN DEFAULT FALSE NOT NULL,
FOREIGN KEY (namespace_key, segment_key) REFERENCES segments (namespace_key, key) ON DELETE CASCADE
);
1 change: 1 addition & 0 deletions config/migrations/postgres/9_flag_type.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE flags ADD COLUMN type INTEGER DEFAULT 0 NOT NULL;
67 changes: 34 additions & 33 deletions internal/storage/sql/common/evaluation.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,40 +167,41 @@ func (s *Store) GetEvaluationRollouts(ctx context.Context, namespaceKey, flagKey
namespaceKey = storage.DefaultNamespace
}

rows, err := s.db.QueryContext(ctx, `
rows, err := s.builder.Select(`
r.id,
r.namespace_key,
r."type",
r."rank",
rt.percentage,
rt.value,
rss.segment_key,
rss.rollout_segment_value,
rss.match_type,
rss.constraint_type,
rss.constraint_property,
rss.constraint_operator,
rss.constraint_value
`).
From("rollouts r").
LeftJoin("rollout_thresholds rt ON (r.id = rt.rollout_id)").
LeftJoin(`(
SELECT
r.id,
r.namespace_key,
r."type",
r."rank",
rt.percentage,
rt.value,
rss.segment_key,
rss.rollout_segment_value,
rss.match_type,
rss.constraint_type,
rss.constraint_property,
rss.constraint_operator,
rss.constraint_value
FROM rollouts r
LEFT JOIN rollout_thresholds rt ON (r.id = rt.rollout_id)
LEFT JOIN (
SELECT
rs.rollout_id,
rs.segment_key,
s.match_type,
rs.value AS rollout_segment_value,
c."type" AS constraint_type,
c.property AS constraint_property,
c.operator AS constraint_operator,
c.value AS constraint_value
FROM rollout_segments rs
JOIN segments s ON (rs.segment_key = s."key")
JOIN constraints c ON (rs.segment_key = c.segment_key)
) rss ON (r.id = rss.rollout_id)
WHERE r.namespace_key = $1 AND r.flag_key = $2
ORDER BY r."rank" ASC
`, namespaceKey, flagKey)
rs.rollout_id,
rs.segment_key,
s.match_type,
rs.value AS rollout_segment_value,
c."type" AS constraint_type,
c.property AS constraint_property,
c.operator AS constraint_operator,
c.value AS constraint_value
FROM rollout_segments rs
JOIN segments s ON (rs.segment_key = s."key")
JOIN constraints c ON (rs.segment_key = c.segment_key)
) rss ON (r.id = rss.rollout_id)
`).
Where(sq.And{sq.Eq{"r.namespace_key": namespaceKey}, sq.Eq{"r.flag_key": flagKey}}).
OrderBy(`r."rank" ASC`).
QueryContext(ctx)
if err != nil {
return nil, err
}
Expand Down
16 changes: 15 additions & 1 deletion internal/storage/sql/common/rollout.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,20 @@ func (s *Store) CreateRollout(ctx context.Context, r *flipt.CreateRolloutRequest
r.NamespaceKey = storage.DefaultNamespace
}

var count uint64

if err := s.builder.Select("COUNT(*)").
From(tableRollouts).
Where(sq.And{sq.Eq{"namespace_key": r.NamespaceKey}, sq.Eq{"flag_key": r.FlagKey}, sq.Eq{"\"rank\"": r.Rank}}).
QueryRowContext(ctx).
Scan(&count); err != nil {
return nil, err
}

if count > 0 {
return nil, errs.ErrInvalidf("rank number: %d already exists", r.Rank)
}

var (
now = timestamppb.Now()
rollout = &flipt.Rollout{
Expand Down Expand Up @@ -358,7 +372,7 @@ func (s *Store) CreateRollout(ctx context.Context, r *flipt.CreateRolloutRequest

if _, err := s.builder.Insert(tableRollouts).
RunWith(tx).
Columns("id", "namespace_key", "flag_key", "\"type\"", "rank", "description", "created_at", "updated_at").
Columns("id", "namespace_key", "flag_key", "\"type\"", "\"rank\"", "description", "created_at", "updated_at").
Values(rollout.Id, rollout.NamespaceKey, rollout.FlagKey, rollout.Type, rollout.Rank, rollout.Description,
&fliptsql.Timestamp{Timestamp: rollout.CreatedAt},
&fliptsql.Timestamp{Timestamp: rollout.UpdatedAt},
Expand Down
6 changes: 3 additions & 3 deletions internal/storage/sql/migrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import (

var expectedVersions = map[Driver]uint{
SQLite: 10,
Postgres: 8,
MySQL: 6,
CockroachDB: 5,
Postgres: 10,
MySQL: 8,
CockroachDB: 7,
}

// Migrator is responsible for migrating the database schema
Expand Down
19 changes: 19 additions & 0 deletions internal/storage/sql/mysql/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,25 @@ func (s *Store) CreateConstraint(ctx context.Context, r *flipt.CreateConstraintR
return constraint, nil
}

func (s *Store) CreateRollout(ctx context.Context, r *flipt.CreateRolloutRequest) (*flipt.Rollout, error) {
rollout, err := s.Store.CreateRollout(ctx, r)

if err != nil {
var merr *mysql.MySQLError

if errors.As(err, &merr) && merr.Number == constraintForeignKeyErrCode {
if segment := r.GetSegment(); segment != nil {
return nil, errs.ErrNotFoundf(`flag "%s/%s or segment %s"`, r.NamespaceKey, r.FlagKey, segment.SegmentKey)
}
return nil, errs.ErrNotFoundf(`flag "%s/%s"`, r.NamespaceKey, r.FlagKey)
}

return nil, err
}

return rollout, nil
}

func (s *Store) CreateRule(ctx context.Context, r *flipt.CreateRuleRequest) (*flipt.Rule, error) {
rule, err := s.Store.CreateRule(ctx, r)

Expand Down
19 changes: 19 additions & 0 deletions internal/storage/sql/postgres/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,25 @@ func (s *Store) CreateConstraint(ctx context.Context, r *flipt.CreateConstraintR
return constraint, nil
}

func (s *Store) CreateRollout(ctx context.Context, r *flipt.CreateRolloutRequest) (*flipt.Rollout, error) {
rollout, err := s.Store.CreateRollout(ctx, r)

if err != nil {
var perr *pq.Error

if errors.As(err, &perr) && perr.Code.Name() == constraintForeignKeyErr {
if segment := r.GetSegment(); segment != nil {
return nil, errs.ErrNotFoundf(`flag "%s/%s or segment %s"`, r.NamespaceKey, r.FlagKey, segment.SegmentKey)
}
return nil, errs.ErrNotFoundf(`flag "%s/%s"`, r.NamespaceKey, r.FlagKey)
}

return nil, err
}

return rollout, nil
}

func (s *Store) CreateRule(ctx context.Context, r *flipt.CreateRuleRequest) (*flipt.Rule, error) {
rule, err := s.Store.CreateRule(ctx, r)

Expand Down
3 changes: 3 additions & 0 deletions internal/storage/sql/sqlite/sqlite.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ func (s *Store) CreateRollout(ctx context.Context, r *flipt.CreateRolloutRequest
var serr sqlite3.Error

if errors.As(err, &serr) && serr.Code == sqlite3.ErrConstraint {
if segment := r.GetSegment(); segment != nil {
return nil, errs.ErrNotFoundf(`flag "%s/%s or segment %s"`, r.NamespaceKey, r.FlagKey, segment.SegmentKey)
}
return nil, errs.ErrNotFoundf(`flag "%s/%s"`, r.NamespaceKey, r.FlagKey)
}

Expand Down

0 comments on commit c694ac8

Please sign in to comment.