Skip to content

Ruler Object Storage Base64 Encoding #2646

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 39 additions & 19 deletions integration/api_ruler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@ import (
)

func TestRulerAPI(t *testing.T) {
var (
namespaceOne = "test_/encoded_+namespace/?"
namespaceTwo = "test_/encoded_+namespace/?/two"

ruleGroup = rulefmt.RuleGroup{
Name: "test_encoded_+\"+group_name/?",
Interval: 100,
Rules: []rulefmt.Rule{
{
Record: "test_rule",
Expr: "up",
},
},
}
)
s, err := e2e.NewScenario(networkName)
require.NoError(t, err)
defer s.Close()
Expand All @@ -32,22 +47,8 @@ func TestRulerAPI(t *testing.T) {
c, err := e2ecortex.NewClient("", "", "", ruler.HTTPEndpoint(), "user-1")
require.NoError(t, err)

// Create example namespace and rule group to use for tests, using strings that
// require url escaping.
namespace := "test_encoded_+namespace?"
rg := rulefmt.RuleGroup{
Name: "test_encoded_+\"+group_name?",
Interval: 100,
Rules: []rulefmt.Rule{
{
Record: "test_rule",
Expr: "up",
},
},
}

// Set the rule group into the ruler
require.NoError(t, c.SetRuleGroup(rg, namespace))
require.NoError(t, c.SetRuleGroup(ruleGroup, namespaceOne))

// Wait until the user manager is created
require.NoError(t, ruler.WaitSumMetrics(e2e.Equals(1), "cortex_ruler_managers_total"))
Expand All @@ -56,13 +57,32 @@ func TestRulerAPI(t *testing.T) {
rgs, err := c.GetRuleGroups()
require.NoError(t, err)

retrievedNamespace, exists := rgs[namespace]
retrievedNamespace, exists := rgs[namespaceOne]
require.True(t, exists)
require.Len(t, retrievedNamespace, 1)
require.Equal(t, retrievedNamespace[0].Name, ruleGroup.Name)

// Add a second rule group with a similar namespace
require.NoError(t, c.SetRuleGroup(ruleGroup, namespaceTwo))
require.NoError(t, ruler.WaitSumMetrics(e2e.Equals(2), "cortex_prometheus_rule_group_rules"))

// Check to ensure the rules running in the ruler match what was set
rgs, err = c.GetRuleGroups()
require.NoError(t, err)

retrievedNamespace, exists = rgs[namespaceOne]
require.True(t, exists)
require.Len(t, retrievedNamespace, 1)
require.Equal(t, retrievedNamespace[0].Name, ruleGroup.Name)

retrievedNamespace, exists = rgs[namespaceTwo]
require.True(t, exists)
require.Len(t, retrievedNamespace, 1)
require.Equal(t, retrievedNamespace[0].Name, rg.Name)
require.Equal(t, retrievedNamespace[0].Name, ruleGroup.Name)

// Delete the set rule group
require.NoError(t, c.DeleteRuleGroup(namespace, rg.Name))
// Delete the set rule groups
require.NoError(t, c.DeleteRuleGroup(namespaceOne, ruleGroup.Name))
require.NoError(t, c.DeleteRuleGroup(namespaceTwo, ruleGroup.Name))

// Wait until the users manager has been terminated
require.NoError(t, ruler.WaitSumMetrics(e2e.Equals(0), "cortex_ruler_managers_total"))
Expand Down
9 changes: 7 additions & 2 deletions pkg/ruler/rules/objectclient/rule_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package objectclient
import (
"bytes"
"context"
"encoding/base64"
"io/ioutil"
strings "strings"

Expand All @@ -16,8 +17,12 @@ import (

// Object Rule Storage Schema
// =======================
// Object Name: "rules/<user_id>/<namespace>/<group_name>"
// Object Name: "rules/<user_id>/<base64 URL Encoded: namespace>/<base64 URL Encoded: group_name>"
// Storage Format: Encoded RuleGroupDesc
//
// Prometheus Rule Groups can include a large number of characters that are not valid object names
// in common object storage systems. A URL Base64 encoding allows for generic consistent naming
// across all backends

const (
rulePrefix = "rules/"
Expand Down Expand Up @@ -152,7 +157,7 @@ func generateRuleObjectKey(id, namespace, name string) string {
if namespace == "" {
return prefix
}
return prefix + namespace + "/" + name
return prefix + base64.URLEncoding.EncodeToString([]byte(namespace)) + "/" + base64.URLEncoding.EncodeToString([]byte(name))
}

func decomposeRuleObjectKey(handle string) string {
Expand Down