-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add role to parse tag * add parsetag tests
- Loading branch information
1 parent
33003cf
commit 428222b
Showing
4 changed files
with
285 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// Copyright 2024 Canonical. | ||
package names_test | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
qt "github.com/frankban/quicktest" | ||
"github.com/google/uuid" | ||
jujunames "github.com/juju/names/v5" | ||
|
||
"github.com/canonical/jimm/v3/pkg/names" | ||
) | ||
|
||
func TestParseTag(t *testing.T) { | ||
c := qt.New(t) | ||
uuid := uuid.NewString() | ||
tests := []struct { | ||
tagString string | ||
expectedTag jujunames.Tag | ||
expectedValid bool | ||
}{ | ||
{ | ||
tagString: fmt.Sprintf("group-%s", uuid), | ||
expectedTag: names.NewGroupTag(uuid), | ||
expectedValid: true, | ||
}, | ||
{ | ||
tagString: fmt.Sprintf("role-%s", uuid), | ||
expectedTag: names.NewRoleTag(uuid), | ||
expectedValid: true, | ||
}, | ||
{ | ||
tagString: fmt.Sprintf("serviceaccount-%s@serviceaccount", uuid), | ||
expectedTag: names.NewServiceAccountTag(fmt.Sprintf("%s@serviceaccount", uuid)), | ||
expectedValid: true, | ||
}, | ||
{ | ||
tagString: fmt.Sprintf("not-exisintg-%s@serviceaccount", uuid), | ||
expectedTag: names.NewServiceAccountTag(fmt.Sprintf("%s@serviceaccount", uuid)), | ||
expectedValid: false, | ||
}, | ||
{ | ||
tagString: "group1", | ||
expectedValid: false, | ||
}, | ||
} | ||
for _, test := range tests { | ||
tag, err := names.ParseTag(test.tagString) | ||
if test.expectedValid { | ||
c.Assert(tag, qt.Equals, test.expectedTag) | ||
} else { | ||
c.Assert(err, qt.IsNotNil) | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Copyright 2024 Canonical. | ||
|
||
package names | ||
|
||
import ( | ||
"fmt" | ||
"regexp" | ||
) | ||
|
||
const ( | ||
RoleTagKind = "role" | ||
) | ||
|
||
var ( | ||
validRoleName = regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9._-]+[a-zA-Z0-9]$") | ||
validRoleIdSnippet = `^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}((#|\z)[a-z]+)?$` | ||
validRoleId = regexp.MustCompile(validRoleIdSnippet) | ||
) | ||
|
||
// RoleTag represents a role. | ||
// Implements juju names.Tag | ||
type RoleTag struct { | ||
id string | ||
} | ||
|
||
// Id implements juju names.Tag | ||
func (t RoleTag) Id() string { return t.id } | ||
|
||
// Kind implements juju names.Tag | ||
func (t RoleTag) Kind() string { return RoleTagKind } | ||
|
||
// String implements juju names.Tag | ||
func (t RoleTag) String() string { return RoleTagKind + "-" + t.Id() } | ||
|
||
// NewRoleTag creates a valid RoleTag if it is possible to parse | ||
// the provided tag. | ||
func NewRoleTag(roleId string) RoleTag { | ||
id := validRoleId.FindString(roleId) | ||
|
||
if id == "" { | ||
panic(fmt.Sprintf("invalid role tag %q", roleId)) | ||
} | ||
|
||
return RoleTag{id: id} | ||
} | ||
|
||
// ParseRoleTag parses a user role string. | ||
func ParseRoleTag(tag string) (RoleTag, error) { | ||
t, err := ParseTag(tag) | ||
if err != nil { | ||
return RoleTag{}, err | ||
} | ||
gt, ok := t.(RoleTag) | ||
if !ok { | ||
return RoleTag{}, invalidTagError(tag, RoleTagKind) | ||
} | ||
return gt, nil | ||
} | ||
|
||
// IsValidRoleId verifies the id of the tag is valid according to a regex internally. | ||
func IsValidRoleId(id string) bool { | ||
return validRoleId.MatchString(id) | ||
} | ||
|
||
// IsValidRoleName verifies the name of the role is valid | ||
// according to the role name regexp. | ||
// A valid role name: | ||
// - starts with an upper- or lower-case character | ||
// - ends with an upper- or lower-case character or a number | ||
// - may contain ., _, or - | ||
// - must at least 6 characters long. | ||
func IsValidRoleName(name string) bool { | ||
return validRoleName.MatchString(name) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// Copyright 2024 Canonical. | ||
|
||
package names_test | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
qt "github.com/frankban/quicktest" | ||
"github.com/google/uuid" | ||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/canonical/jimm/v3/pkg/names" | ||
) | ||
|
||
func TestParseRoleTag(t *testing.T) { | ||
c := qt.New(t) | ||
uuid := uuid.NewString() | ||
|
||
tests := []struct { | ||
tag string | ||
expectedError string | ||
expectedTag string | ||
expectedId string | ||
}{{ | ||
tag: fmt.Sprintf("role-%s", uuid), | ||
expectedId: uuid, | ||
expectedTag: fmt.Sprintf("role-%s", uuid), | ||
}, { | ||
tag: fmt.Sprintf("role-%s#member", uuid), | ||
expectedId: fmt.Sprintf("%s#member", uuid), | ||
expectedTag: fmt.Sprintf("role-%s#member", uuid), | ||
}, { | ||
tag: "pokemon-diglett", | ||
expectedError: "\"pokemon-diglett\" is not a valid tag", | ||
}} | ||
|
||
for i, test := range tests { | ||
test := test | ||
c.Run(fmt.Sprintf("test case %d", i), func(c *qt.C) { | ||
gt, err := names.ParseRoleTag(test.tag) | ||
if test.expectedError == "" { | ||
c.Assert(err, qt.IsNil) | ||
c.Assert(gt.Id(), qt.Equals, test.expectedId) | ||
c.Assert(gt.Kind(), qt.Equals, "role") | ||
c.Assert(gt.String(), qt.Equals, test.expectedTag) | ||
} else { | ||
c.Assert(err, qt.ErrorMatches, test.expectedError) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestParseRoleTagDeniesBadKinds(t *testing.T) { | ||
_, err := names.ParseRoleTag("pokemon-diglett") | ||
assert.Error(t, err) | ||
assert.ErrorContains(t, err, "\"pokemon-diglett\" is not a valid tag") | ||
} | ||
|
||
func TestIsValidRoleId(t *testing.T) { | ||
uuid := uuid.NewString() | ||
tests := []struct { | ||
id string | ||
expectedValid bool | ||
}{{ | ||
id: uuid, | ||
expectedValid: true, | ||
}, { | ||
id: fmt.Sprintf("%s#member", uuid), | ||
expectedValid: true, | ||
}, { | ||
id: fmt.Sprintf("%s#member#member", uuid), | ||
expectedValid: false, | ||
}, { | ||
id: fmt.Sprintf("%s#", uuid), | ||
expectedValid: false, | ||
}, { | ||
id: "0#member", | ||
expectedValid: false, | ||
}, { | ||
id: "0", | ||
expectedValid: false, | ||
}} | ||
for _, test := range tests { | ||
assert.Equal(t, names.IsValidRoleId(test.id), test.expectedValid) | ||
} | ||
} | ||
|
||
func TestIsValidRoleName(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
expectedValidity bool | ||
}{{ | ||
name: "role-1", | ||
expectedValidity: true, | ||
}, { | ||
name: "Role1", | ||
expectedValidity: true, | ||
}, { | ||
name: "1role", | ||
expectedValidity: false, | ||
}, { | ||
name: ".role", | ||
expectedValidity: false, | ||
}, { | ||
name: "role.A", | ||
expectedValidity: true, | ||
}, { | ||
name: "role.A1", | ||
expectedValidity: true, | ||
}, { | ||
name: "role_test_a_1", | ||
expectedValidity: true, | ||
}, { | ||
name: "role+a", | ||
expectedValidity: false, | ||
}, { | ||
name: "Test.Role.1.A", | ||
expectedValidity: true, | ||
}, { | ||
name: "", | ||
expectedValidity: false, | ||
}, { | ||
name: "no", | ||
expectedValidity: false, | ||
}, { | ||
name: "foo", | ||
expectedValidity: true, | ||
}, { | ||
name: "short", | ||
expectedValidity: true, | ||
}, { | ||
name: "short1", | ||
expectedValidity: true, | ||
}, { | ||
name: "short_", | ||
expectedValidity: false, | ||
}, { | ||
name: "role.A#member", | ||
expectedValidity: false, | ||
}} | ||
|
||
for _, test := range tests { | ||
t.Logf("testing role name %q, expected validity %v", test.name, test.expectedValidity) | ||
|
||
valid := names.IsValidRoleName(test.name) | ||
assert.Equal(t, valid, test.expectedValidity) | ||
} | ||
} |