mirror of https://github.com/grafana/grafana
fix(unified-storage): use continue token containing both formats for dualwriter (#106525)
parent
8504f7ea90
commit
5f21f320f7
@ -0,0 +1,33 @@ |
||||
package dualwrite |
||||
|
||||
import ( |
||||
"encoding/base64" |
||||
"fmt" |
||||
"strings" |
||||
) |
||||
|
||||
// parseContinueTokens splits a dualwriter continue token (legacy, unified) if we received one.
|
||||
// If we receive a single token not separated by a comma, we return the token as-is as a legacy
|
||||
// token and an empty unified token. This is to ensure a smooth transition to the new token format.
|
||||
func parseContinueTokens(token string) (string, string, error) { |
||||
if token == "" { |
||||
return "", "", nil |
||||
} |
||||
decodedToken, err := base64.StdEncoding.DecodeString(token) |
||||
if err != nil { |
||||
return "", "", fmt.Errorf("failed to decode dualwriter continue token: %w", err) |
||||
} |
||||
decodedTokens := strings.Split(string(decodedToken), ",") |
||||
if len(decodedTokens) > 1 { |
||||
return decodedTokens[0], decodedTokens[1], nil |
||||
} |
||||
return token, "", nil |
||||
} |
||||
|
||||
func buildContinueToken(legacyToken, unifiedToken string) string { |
||||
if legacyToken == "" && unifiedToken == "" { |
||||
return "" |
||||
} |
||||
return base64.StdEncoding.EncodeToString([]byte( |
||||
strings.Join([]string{legacyToken, unifiedToken}, ","))) |
||||
} |
@ -0,0 +1,98 @@ |
||||
package dualwrite |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
) |
||||
|
||||
func TestParseContinueTokens(t *testing.T) { |
||||
tcs := []struct { |
||||
name string |
||||
token string |
||||
legacyToken string |
||||
unifiedToken string |
||||
}{ |
||||
{ |
||||
name: "Should handle empty token", |
||||
token: "", |
||||
legacyToken: "", |
||||
unifiedToken: "", |
||||
}, |
||||
{ |
||||
name: "Should handle legacy token", |
||||
token: "MXwy", |
||||
legacyToken: "MXwy", |
||||
unifiedToken: "", |
||||
}, |
||||
{ |
||||
name: "Should handle new token format", |
||||
// both slots taken 'MXwy,eyJvIjoxLCJ2IjoxNzQ5NTY1NTU4MDc4OTkwLCJzIjpmYWxzZX0='
|
||||
token: "TVh3eSxleUp2SWpveExDSjJJam94TnpRNU5UWTFOVFU0TURjNE9Ua3dMQ0p6SWpwbVlXeHpaWDA9", |
||||
legacyToken: "MXwy", |
||||
unifiedToken: "eyJvIjoxLCJ2IjoxNzQ5NTY1NTU4MDc4OTkwLCJzIjpmYWxzZX0=", |
||||
}, |
||||
{ |
||||
name: "Should handle new token with only unified token (mode >= 3)", |
||||
// first slot empty ',eyJvIjoxLCJ2IjoxNzQ5NTY1NTU4MDc4OTkwLCJzIjpmYWxzZX0='
|
||||
token: "LGV5SnZJam94TENKMklqb3hOelE1TlRZMU5UVTRNRGM0T1Rrd0xDSnpJanBtWVd4elpYMD0=", |
||||
legacyToken: "", |
||||
unifiedToken: "eyJvIjoxLCJ2IjoxNzQ5NTY1NTU4MDc4OTkwLCJzIjpmYWxzZX0=", |
||||
}, |
||||
} |
||||
|
||||
for _, tc := range tcs { |
||||
t.Run(tc.name, func(t *testing.T) { |
||||
legacyToken, unifiedToken, err := parseContinueTokens(tc.token) |
||||
require.NoError(t, err) |
||||
require.Equal(t, legacyToken, tc.legacyToken) |
||||
require.Equal(t, unifiedToken, tc.unifiedToken) |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func TestBuildContinueToken(t *testing.T) { |
||||
tcs := []struct { |
||||
name string |
||||
legacyToken string |
||||
unifiedToken string |
||||
shouldBeEmpty bool |
||||
}{ |
||||
{ |
||||
name: "Should handle both tokens", |
||||
legacyToken: "abc", |
||||
unifiedToken: "xyz", |
||||
}, |
||||
{ |
||||
name: "Should handle legacy token standalone", |
||||
legacyToken: "abc", |
||||
}, |
||||
{ |
||||
name: "Should handle unified token standalone", |
||||
unifiedToken: "xyz", |
||||
}, |
||||
{ |
||||
name: "Should handle both tokens empty", |
||||
shouldBeEmpty: true, |
||||
}, |
||||
} |
||||
for _, tc := range tcs { |
||||
t.Run(tc.name, func(t *testing.T) { |
||||
token := buildContinueToken(tc.legacyToken, tc.unifiedToken) |
||||
legacyToken, unifiedToken, err := parseContinueTokens(token) |
||||
require.NoError(t, err) |
||||
require.Equal(t, legacyToken, tc.legacyToken) |
||||
require.Equal(t, unifiedToken, tc.unifiedToken) |
||||
if tc.shouldBeEmpty { |
||||
require.Equal(t, "", token) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func TestInvalidToken(t *testing.T) { |
||||
// nolint: gosec
|
||||
invalidToken := "325232ff4fF->" |
||||
_, _, err := parseContinueTokens(invalidToken) |
||||
require.Error(t, err) |
||||
} |
Loading…
Reference in new issue