Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ report.xml
*.cid
*.iid
*.out
*.test

.vscode
1 change: 1 addition & 0 deletions funcs/cel_exports.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions funcs/uuid.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package funcs

import (
"context"
"crypto/sha256"

"github.com/flanksource/gomplate/v3/conv"

Expand Down Expand Up @@ -79,3 +80,20 @@ func (UUIDFuncs) Parse(in interface{}) (string, error) {
}
return u.String(), err
}

// HashUUID - return a deterministic UUID based on the SHA256 hash of the input arguments.
// This function always returns the same UUID for the same input, making it idempotent.
// It uses the nil UUID as the namespace and SHA256 as the hashing algorithm.
func (UUIDFuncs) HashUUID(args ...interface{}) (string, error) {
// Concatenate all arguments into a single byte slice
var data []byte
for _, arg := range args {
data = append(data, []byte(conv.ToString(arg))...)
}

// Use uuid.Nil as the namespace
// Generate a version 5-style UUID (SHA-based) using SHA256
u := uuid.NewHash(sha256.New(), uuid.Nil, data, 5)

return u.String(), nil
}
26 changes: 26 additions & 0 deletions funcs/uuid_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 60 additions & 0 deletions funcs/uuid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,63 @@ func TestParse(t *testing.T) {
assert.Equal(t, in, uid)
}
}

func TestHashUUID(t *testing.T) {
t.Parallel()

u := UUIDNS()

// Test that the same input produces the same UUID
uuid1, err := u.HashUUID("test")
assert.NoError(t, err)
assert.NotEmpty(t, uuid1)

uuid2, err := u.HashUUID("test")
assert.NoError(t, err)
assert.Equal(t, uuid1, uuid2, "Same input should produce same UUID")

// Test that different inputs produce different UUIDs
uuid3, err := u.HashUUID("different")
assert.NoError(t, err)
assert.NotEqual(t, uuid1, uuid3, "Different inputs should produce different UUIDs")

// Test with multiple arguments
uuid4, err := u.HashUUID("arg1", "arg2", "arg3")
assert.NoError(t, err)
assert.NotEmpty(t, uuid4)

uuid5, err := u.HashUUID("arg1", "arg2", "arg3")
assert.NoError(t, err)
assert.Equal(t, uuid4, uuid5, "Same multiple arguments should produce same UUID")

// Test that order matters
uuid6, err := u.HashUUID("arg2", "arg1", "arg3")
assert.NoError(t, err)
assert.NotEqual(t, uuid4, uuid6, "Different order should produce different UUIDs")

// Test with numeric arguments
uuid7, err := u.HashUUID(123, 456)
assert.NoError(t, err)
assert.NotEmpty(t, uuid7)

uuid8, err := u.HashUUID(123, 456)
assert.NoError(t, err)
assert.Equal(t, uuid7, uuid8, "Same numeric arguments should produce same UUID")

// Test with no arguments
uuid9, err := u.HashUUID()
assert.NoError(t, err)
assert.NotEmpty(t, uuid9)

uuid10, err := u.HashUUID()
assert.NoError(t, err)
assert.Equal(t, uuid9, uuid10, "No arguments should produce same UUID each time")

// Verify the UUID format is valid (version 5, SHA-based)
parsed, err := u.Parse(uuid1)
assert.NoError(t, err)
assert.Equal(t, uuid1, parsed)

// Check that it's a valid UUID v5 pattern
assert.Regexp(t, "^[[:xdigit:]]{8}-[[:xdigit:]]{4}-5[[:xdigit:]]{3}-[89ab][[:xdigit:]]{3}-[[:xdigit:]]{12}$", uuid1)
}
36 changes: 36 additions & 0 deletions tests/cel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -689,4 +689,40 @@ func TestCelUUID(t *testing.T) {
{nil, "uuid.IsValid('2a42e576-c308-4db9-8525-0513af307586')", "true"},
{nil, "string(uuid.Parse('2a42e576-c308-4db9-8525-0513af307586'))", "2a42e576-c308-4db9-8525-0513af307586"},
})

// Test HashUUID for idempotency
expr1 := `uuid.HashUUID(['test'])`
out1, err := gomplate.RunTemplate(nil, gomplate.Template{
Expression: expr1,
})
assert.NoError(t, err)
assert.NotEmpty(t, out1)

out2, err := gomplate.RunTemplate(nil, gomplate.Template{
Expression: expr1,
})
assert.NoError(t, err)
assert.Equal(t, out1, out2, "Same input should produce same UUID")

// Test with different input
expr2 := `uuid.HashUUID(['different'])`
out3, err := gomplate.RunTemplate(nil, gomplate.Template{
Expression: expr2,
})
assert.NoError(t, err)
assert.NotEqual(t, out1, out3, "Different input should produce different UUID")

// Test with multiple arguments
expr3 := `uuid.HashUUID(['arg1', 'arg2'])`
out4, err := gomplate.RunTemplate(nil, gomplate.Template{
Expression: expr3,
})
assert.NoError(t, err)
assert.NotEmpty(t, out4)

out5, err := gomplate.RunTemplate(nil, gomplate.Template{
Expression: expr3,
})
assert.NoError(t, err)
assert.Equal(t, out4, out5, "Same multiple arguments should produce same UUID")
}
39 changes: 39 additions & 0 deletions tests/gomplate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,45 @@ func TestGomplate(t *testing.T) {
}
}

func TestHashUUID(t *testing.T) {
// Test that HashUUID returns the same UUID for the same input
template := `{{ uuid.HashUUID "test" }}`

out1, err := gomplate.RunTemplate(map[string]any{}, gomplate.Template{
Template: template,
})
assert.NoError(t, err)
assert.NotEmpty(t, out1)

out2, err := gomplate.RunTemplate(map[string]any{}, gomplate.Template{
Template: template,
})
assert.NoError(t, err)
assert.Equal(t, out1, out2, "Same input should produce same UUID")

// Test with different input produces different UUID
template2 := `{{ uuid.HashUUID "different" }}`
out3, err := gomplate.RunTemplate(map[string]any{}, gomplate.Template{
Template: template2,
})
assert.NoError(t, err)
assert.NotEqual(t, out1, out3, "Different input should produce different UUID")

// Test with multiple arguments
template3 := `{{ uuid.HashUUID "arg1" "arg2" }}`
out4, err := gomplate.RunTemplate(map[string]any{}, gomplate.Template{
Template: template3,
})
assert.NoError(t, err)
assert.NotEmpty(t, out4)

out5, err := gomplate.RunTemplate(map[string]any{}, gomplate.Template{
Template: template3,
})
assert.NoError(t, err)
assert.Equal(t, out4, out5, "Same multiple arguments should produce same UUID")
}

func TestGomplateHeaders(t *testing.T) {
tests := []struct {
env map[string]any
Expand Down
Loading