Port envd from e2b with internalized shared packages and Connect RPC
- Copy envd source from e2b-dev/infra, internalize shared dependencies
into envd/internal/shared/ (keys, filesystem, id, smap, utils)
- Switch from gRPC to Connect RPC for all envd services
- Update module paths to git.omukk.dev/wrenn/{sandbox,sandbox/envd}
- Add proto specs (process, filesystem) with buf-based code generation
- Implement full envd: process exec, filesystem ops, port forwarding,
cgroup management, MMDS integration, and HTTP API
- Update main module dependencies (firecracker SDK, pgx, goose, etc.)
- Remove placeholder .gitkeep files replaced by real implementations
This commit is contained in:
164
envd/internal/shared/id/id.go
Normal file
164
envd/internal/shared/id/id.go
Normal file
@ -0,0 +1,164 @@
|
||||
package id
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/dchest/uniuri"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
var (
|
||||
caseInsensitiveAlphabet = []byte("abcdefghijklmnopqrstuvwxyz1234567890")
|
||||
identifierRegex = regexp.MustCompile(`^[a-z0-9-_]+$`)
|
||||
tagRegex = regexp.MustCompile(`^[a-z0-9-_.]+$`)
|
||||
sandboxIDRegex = regexp.MustCompile(`^[a-z0-9]+$`)
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultTag = "default"
|
||||
TagSeparator = ":"
|
||||
NamespaceSeparator = "/"
|
||||
)
|
||||
|
||||
func Generate() string {
|
||||
return uniuri.NewLenChars(uniuri.UUIDLen, caseInsensitiveAlphabet)
|
||||
}
|
||||
|
||||
// ValidateSandboxID checks that a sandbox ID contains only lowercase alphanumeric characters.
|
||||
func ValidateSandboxID(sandboxID string) error {
|
||||
if !sandboxIDRegex.MatchString(sandboxID) {
|
||||
return fmt.Errorf("invalid sandbox ID: %q", sandboxID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func cleanAndValidate(value, name string, re *regexp.Regexp) (string, error) {
|
||||
cleaned := strings.ToLower(strings.TrimSpace(value))
|
||||
if !re.MatchString(cleaned) {
|
||||
return "", fmt.Errorf("invalid %s: %s", name, value)
|
||||
}
|
||||
|
||||
return cleaned, nil
|
||||
}
|
||||
|
||||
func validateTag(tag string) (string, error) {
|
||||
cleanedTag, err := cleanAndValidate(tag, "tag", tagRegex)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Prevent tags from being a UUID
|
||||
_, err = uuid.Parse(cleanedTag)
|
||||
if err == nil {
|
||||
return "", errors.New("tag cannot be a UUID")
|
||||
}
|
||||
|
||||
return cleanedTag, nil
|
||||
}
|
||||
|
||||
func ValidateAndDeduplicateTags(tags []string) ([]string, error) {
|
||||
seen := make(map[string]struct{})
|
||||
|
||||
for _, tag := range tags {
|
||||
cleanedTag, err := validateTag(tag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid tag '%s': %w", tag, err)
|
||||
}
|
||||
|
||||
seen[cleanedTag] = struct{}{}
|
||||
}
|
||||
|
||||
return slices.Collect(maps.Keys(seen)), nil
|
||||
}
|
||||
|
||||
// SplitIdentifier splits "namespace/alias" into its parts.
|
||||
// Returns nil namespace for bare aliases, pointer for explicit namespace.
|
||||
func SplitIdentifier(identifier string) (namespace *string, alias string) {
|
||||
before, after, found := strings.Cut(identifier, NamespaceSeparator)
|
||||
if !found {
|
||||
return nil, before
|
||||
}
|
||||
|
||||
return &before, after
|
||||
}
|
||||
|
||||
// ParseName parses and validates "namespace/alias:tag" or "alias:tag".
|
||||
// Returns the cleaned identifier (namespace/alias or alias) and optional tag.
|
||||
// All components are validated and normalized (lowercase, trimmed).
|
||||
func ParseName(input string) (identifier string, tag *string, err error) {
|
||||
input = strings.TrimSpace(input)
|
||||
|
||||
// Extract raw parts
|
||||
identifierPart, tagPart, hasTag := strings.Cut(input, TagSeparator)
|
||||
namespacePart, aliasPart := SplitIdentifier(identifierPart)
|
||||
|
||||
// Validate tag
|
||||
if hasTag {
|
||||
validated, err := cleanAndValidate(tagPart, "tag", tagRegex)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if !strings.EqualFold(validated, DefaultTag) {
|
||||
tag = &validated
|
||||
}
|
||||
}
|
||||
|
||||
// Validate namespace
|
||||
if namespacePart != nil {
|
||||
validated, err := cleanAndValidate(*namespacePart, "namespace", identifierRegex)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
namespacePart = &validated
|
||||
}
|
||||
|
||||
// Validate alias
|
||||
aliasPart, err = cleanAndValidate(aliasPart, "template ID", identifierRegex)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
// Build identifier
|
||||
if namespacePart != nil {
|
||||
identifier = WithNamespace(*namespacePart, aliasPart)
|
||||
} else {
|
||||
identifier = aliasPart
|
||||
}
|
||||
|
||||
return identifier, tag, nil
|
||||
}
|
||||
|
||||
// WithTag returns the identifier with the given tag appended (e.g. "templateID:tag").
|
||||
func WithTag(identifier, tag string) string {
|
||||
return identifier + TagSeparator + tag
|
||||
}
|
||||
|
||||
// WithNamespace returns identifier with the given namespace prefix.
|
||||
func WithNamespace(namespace, alias string) string {
|
||||
return namespace + NamespaceSeparator + alias
|
||||
}
|
||||
|
||||
// ExtractAlias returns just the alias portion from an identifier (namespace/alias or alias).
|
||||
func ExtractAlias(identifier string) string {
|
||||
_, alias := SplitIdentifier(identifier)
|
||||
|
||||
return alias
|
||||
}
|
||||
|
||||
// ValidateNamespaceMatchesTeam checks if an explicit namespace in the identifier matches the team's slug.
|
||||
// Returns an error if the namespace doesn't match.
|
||||
// If the identifier has no explicit namespace, returns nil (valid).
|
||||
func ValidateNamespaceMatchesTeam(identifier, teamSlug string) error {
|
||||
namespace, _ := SplitIdentifier(identifier)
|
||||
if namespace != nil && *namespace != teamSlug {
|
||||
return fmt.Errorf("namespace '%s' must match your team '%s'", *namespace, teamSlug)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
380
envd/internal/shared/id/id_test.go
Normal file
380
envd/internal/shared/id/id_test.go
Normal file
@ -0,0 +1,380 @@
|
||||
package id
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"git.omukk.dev/wrenn/sandbox/envd/internal/shared/utils"
|
||||
)
|
||||
|
||||
func TestParseName(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantIdentifier string
|
||||
wantTag *string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "bare alias only",
|
||||
input: "my-template",
|
||||
wantIdentifier: "my-template",
|
||||
wantTag: nil,
|
||||
},
|
||||
{
|
||||
name: "alias with tag",
|
||||
input: "my-template:v1",
|
||||
wantIdentifier: "my-template",
|
||||
wantTag: utils.ToPtr("v1"),
|
||||
},
|
||||
{
|
||||
name: "namespace and alias",
|
||||
input: "acme/my-template",
|
||||
wantIdentifier: "acme/my-template",
|
||||
wantTag: nil,
|
||||
},
|
||||
{
|
||||
name: "namespace, alias and tag",
|
||||
input: "acme/my-template:v1",
|
||||
wantIdentifier: "acme/my-template",
|
||||
wantTag: utils.ToPtr("v1"),
|
||||
},
|
||||
{
|
||||
name: "namespace with hyphens",
|
||||
input: "my-team/my-template:prod",
|
||||
wantIdentifier: "my-team/my-template",
|
||||
wantTag: utils.ToPtr("prod"),
|
||||
},
|
||||
{
|
||||
name: "default tag normalized to nil",
|
||||
input: "my-template:default",
|
||||
wantIdentifier: "my-template",
|
||||
wantTag: nil,
|
||||
},
|
||||
{
|
||||
name: "uppercase converted to lowercase",
|
||||
input: "MyTemplate:Prod",
|
||||
wantIdentifier: "mytemplate",
|
||||
wantTag: utils.ToPtr("prod"),
|
||||
},
|
||||
{
|
||||
name: "whitespace trimmed",
|
||||
input: " my-template : v1 ",
|
||||
wantIdentifier: "my-template",
|
||||
wantTag: utils.ToPtr("v1"),
|
||||
},
|
||||
{
|
||||
name: "invalid - empty namespace",
|
||||
input: "/my-template",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - empty tag after colon",
|
||||
input: "my-template:",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - special characters in alias",
|
||||
input: "my template!",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - special characters in namespace",
|
||||
input: "my team!/my-template",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gotIdentifier, gotTag, err := ParseName(tt.input)
|
||||
|
||||
if tt.wantErr {
|
||||
require.Error(t, err, "Expected ParseName() to return error, got")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err, "Expected ParseName() not to return error, got: %v", err)
|
||||
assert.Equal(t, tt.wantIdentifier, gotIdentifier, "ParseName() identifier = %v, want %v", gotIdentifier, tt.wantIdentifier)
|
||||
assert.Equal(t, tt.wantTag, gotTag, "ParseName() tag = %v, want %v", utils.Sprintp(gotTag), utils.Sprintp(tt.wantTag))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithNamespace(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := WithNamespace("acme", "my-template")
|
||||
want := "acme/my-template"
|
||||
assert.Equal(t, want, got, "WithNamespace() = %q, want %q", got, want)
|
||||
}
|
||||
|
||||
func TestSplitIdentifier(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
identifier string
|
||||
wantNamespace *string
|
||||
wantAlias string
|
||||
}{
|
||||
{
|
||||
name: "bare alias",
|
||||
identifier: "my-template",
|
||||
wantNamespace: nil,
|
||||
wantAlias: "my-template",
|
||||
},
|
||||
{
|
||||
name: "with namespace",
|
||||
identifier: "acme/my-template",
|
||||
wantNamespace: ptrStr("acme"),
|
||||
wantAlias: "my-template",
|
||||
},
|
||||
{
|
||||
name: "empty namespace prefix",
|
||||
identifier: "/my-template",
|
||||
wantNamespace: ptrStr(""),
|
||||
wantAlias: "my-template",
|
||||
},
|
||||
{
|
||||
name: "multiple slashes - only first split",
|
||||
identifier: "a/b/c",
|
||||
wantNamespace: ptrStr("a"),
|
||||
wantAlias: "b/c",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gotNamespace, gotAlias := SplitIdentifier(tt.identifier)
|
||||
|
||||
if tt.wantNamespace == nil {
|
||||
assert.Nil(t, gotNamespace)
|
||||
} else {
|
||||
require.NotNil(t, gotNamespace)
|
||||
assert.Equal(t, *tt.wantNamespace, *gotNamespace)
|
||||
}
|
||||
|
||||
assert.Equal(t, tt.wantAlias, gotAlias)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func ptrStr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func TestValidateAndDeduplicateTags(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
tags []string
|
||||
want []string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "single valid tag",
|
||||
tags: []string{"v1"},
|
||||
want: []string{"v1"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "multiple unique tags",
|
||||
tags: []string{"v1", "prod", "latest"},
|
||||
want: []string{"v1", "prod", "latest"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "duplicate tags deduplicated",
|
||||
tags: []string{"v1", "V1", "v1"},
|
||||
want: []string{"v1"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "tags with dots and underscores",
|
||||
tags: []string{"v1.0", "v1_1"},
|
||||
want: []string{"v1.0", "v1_1"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid - UUID tag rejected",
|
||||
tags: []string{"550e8400-e29b-41d4-a716-446655440000"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - special characters",
|
||||
tags: []string{"v1!", "v2@"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty list returns empty",
|
||||
tags: []string{},
|
||||
want: []string{},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := ValidateAndDeduplicateTags(tt.tags)
|
||||
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.ElementsMatch(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateSandboxID(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "canonical sandbox ID",
|
||||
input: "i1a2b3c4d5e6f7g8h9j0k",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "short alphanumeric",
|
||||
input: "abc123",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "all digits",
|
||||
input: "1234567890",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "all lowercase letters",
|
||||
input: "abcdefghijklmnopqrst",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid - empty",
|
||||
input: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - contains colon (Redis separator)",
|
||||
input: "abc:def",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - contains open brace (Redis hash slot)",
|
||||
input: "abc{def",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - contains close brace (Redis hash slot)",
|
||||
input: "abc}def",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - contains newline",
|
||||
input: "abc\ndef",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - contains space",
|
||||
input: "abc def",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - contains hyphen",
|
||||
input: "abc-def",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - contains uppercase",
|
||||
input: "abcDEF",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - contains slash",
|
||||
input: "abc/def",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - contains null byte",
|
||||
input: "abc\x00def",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := ValidateSandboxID(tt.input)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateNamespaceMatchesTeam(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
identifier string
|
||||
teamSlug string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "bare alias - no namespace",
|
||||
identifier: "my-template",
|
||||
teamSlug: "acme",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "matching namespace",
|
||||
identifier: "acme/my-template",
|
||||
teamSlug: "acme",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "mismatched namespace",
|
||||
identifier: "other-team/my-template",
|
||||
teamSlug: "acme",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := ValidateNamespaceMatchesTeam(tt.identifier, tt.teamSlug)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user