add resource group to grn (#77396)
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
|
||||
// A GRN string is expressed in the format:
|
||||
//
|
||||
// grn:${tenant_id}:${kind}/${id}
|
||||
// grn:${tenant_id}:${group}/${kind}/${id}
|
||||
//
|
||||
// The format of the final id is defined by the owning service and not
|
||||
// validated by the GRN parser. Prefer using UIDs where possible.
|
||||
|
||||
+32
-14
@@ -8,10 +8,16 @@ import (
|
||||
|
||||
// ParseStr attempts to parse a string into a GRN. It returns an error if the
|
||||
// given string does not match the GRN format, but does not validate the values.
|
||||
// grn:<TenantID>:<ResourceGroup>/<ResourceKind>/<ResourceIdentifier>
|
||||
// No component of the GRN may contain a colon
|
||||
// The TenantID is optional, but must be an integer string if specified
|
||||
// The ResourceGroup, ResourceKind and ResourceIdentifier must be non-empty strings
|
||||
// The ResourceGroup and ResourceKind may not contain slashes
|
||||
// The ResourceIdentifier may contain slashes
|
||||
func ParseStr(str string) (*GRN, error) {
|
||||
ret := &GRN{}
|
||||
parts := strings.Split(str, ":")
|
||||
|
||||
parts := strings.Split(str, ":")
|
||||
if len(parts) != 3 {
|
||||
return ret, ErrInvalidGRN.Errorf("%q is not a complete GRN", str)
|
||||
}
|
||||
@@ -20,22 +26,33 @@ func ParseStr(str string) (*GRN, error) {
|
||||
return ret, ErrInvalidGRN.Errorf("%q does not look like a GRN", str)
|
||||
}
|
||||
|
||||
// split the final segment into Kind and ID. This only splits after the
|
||||
// first occurrence of "/"; a ResourceIdentifier may contain "/"
|
||||
kind, id, found := strings.Cut(parts[2], "/")
|
||||
if !found { // missing "/"
|
||||
return ret, ErrInvalidGRN.Errorf("invalid resource identifier in GRN %q", str)
|
||||
}
|
||||
ret.ResourceIdentifier = id
|
||||
ret.ResourceKind = kind
|
||||
|
||||
if parts[1] != "" {
|
||||
tID, err := strconv.ParseInt(parts[1], 10, 64)
|
||||
if err != nil {
|
||||
return ret, ErrInvalidGRN.Errorf("ID segment cannot be converted to an integer")
|
||||
} else {
|
||||
ret.TenantID = tID
|
||||
return ret, ErrInvalidGRN.Errorf("Tenant ID segment cannot be converted to an integer")
|
||||
}
|
||||
|
||||
ret.TenantID = tID
|
||||
}
|
||||
|
||||
// split the rest of the GRN into Group, Kind and Identifier
|
||||
parts = strings.SplitN(parts[2], "/", 3)
|
||||
if len(parts) != 3 {
|
||||
return ret, ErrInvalidGRN.Errorf("%q is not a complete GRN", str)
|
||||
}
|
||||
|
||||
ret.ResourceGroup = parts[0]
|
||||
ret.ResourceKind = parts[1]
|
||||
ret.ResourceIdentifier = parts[2]
|
||||
|
||||
if ret.ResourceGroup == "" {
|
||||
return ret, ErrInvalidGRN.Errorf("Cannot find resource group in GRN %q", str)
|
||||
}
|
||||
if ret.ResourceKind == "" {
|
||||
return ret, ErrInvalidGRN.Errorf("Cannot find resource kind in GRN %q", str)
|
||||
}
|
||||
if ret.ResourceIdentifier == "" {
|
||||
return ret, ErrInvalidGRN.Errorf("Cannot find resource identifier in GRN %q", str)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
@@ -54,7 +71,7 @@ func MustParseStr(str string) *GRN {
|
||||
// ToGRNString returns a string representation of a grn in the format
|
||||
// grn:tenantID:kind/resourceIdentifier
|
||||
func (g *GRN) ToGRNString() string {
|
||||
return fmt.Sprintf("grn:%d:%s/%s", g.TenantID, g.ResourceKind, g.ResourceIdentifier)
|
||||
return fmt.Sprintf("grn:%d:%s/%s/%s", g.TenantID, g.ResourceGroup, g.ResourceKind, g.ResourceIdentifier)
|
||||
}
|
||||
|
||||
// Check if the two GRNs reference to the same object
|
||||
@@ -64,6 +81,7 @@ func (g *GRN) Equal(b *GRN) bool {
|
||||
return false
|
||||
}
|
||||
return g == b || (g.TenantID == b.TenantID &&
|
||||
g.ResourceGroup == b.ResourceGroup &&
|
||||
g.ResourceKind == b.ResourceKind &&
|
||||
g.ResourceIdentifier == b.ResourceIdentifier)
|
||||
}
|
||||
|
||||
+24
-11
@@ -36,6 +36,9 @@ type GRN struct {
|
||||
// ResourceIdentifier is used by the underlying service to identify the
|
||||
// resource.
|
||||
ResourceIdentifier string `protobuf:"bytes,4,opt,name=ResourceIdentifier,proto3" json:"ResourceIdentifier,omitempty"`
|
||||
// The group represents the specific resource group the resource belongs to.
|
||||
// This is a unique value for each plugin and maps to the k8s Group
|
||||
ResourceGroup string `protobuf:"bytes,5,opt,name=ResourceGroup,proto3" json:"ResourceGroup,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GRN) Reset() {
|
||||
@@ -91,21 +94,31 @@ func (x *GRN) GetResourceIdentifier() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GRN) GetResourceGroup() string {
|
||||
if x != nil {
|
||||
return x.ResourceGroup
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_grn_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_grn_proto_rawDesc = []byte{
|
||||
0x0a, 0x09, 0x67, 0x72, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x67, 0x72, 0x6e,
|
||||
0x22, 0x75, 0x0a, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x1a, 0x0a, 0x08, 0x54, 0x65, 0x6e, 0x61, 0x6e,
|
||||
0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x54, 0x65, 0x6e, 0x61, 0x6e,
|
||||
0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b,
|
||||
0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x2e, 0x0a, 0x12, 0x52, 0x65, 0x73, 0x6f, 0x75,
|
||||
0x72, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x12, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x65,
|
||||
0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x67, 0x72,
|
||||
0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x69, 0x6e, 0x66, 0x72, 0x61, 0x2f,
|
||||
0x67, 0x72, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x22, 0x9b, 0x01, 0x0a, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x1a, 0x0a, 0x08, 0x54, 0x65, 0x6e, 0x61,
|
||||
0x6e, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x54, 0x65, 0x6e, 0x61,
|
||||
0x6e, 0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||
0x4b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x52, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x2e, 0x0a, 0x12, 0x52, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x04,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64,
|
||||
0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f,
|
||||
0x75, 0x72, 0x63, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x2a,
|
||||
0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61,
|
||||
0x66, 0x61, 0x6e, 0x61, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x6b, 0x67,
|
||||
0x2f, 0x69, 0x6e, 0x66, 0x72, 0x61, 0x2f, 0x67, 0x72, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -17,4 +17,8 @@ message GRN {
|
||||
// ResourceIdentifier is used by the underlying service to identify the
|
||||
// resource.
|
||||
string ResourceIdentifier = 4;
|
||||
|
||||
// The group represents the specific resource group the resource belongs to.
|
||||
// This is a unique value for each plugin and maps to the k8s Group
|
||||
string ResourceGroup = 5;
|
||||
}
|
||||
|
||||
@@ -38,24 +38,29 @@ func TestParseGRNStr(t *testing.T) {
|
||||
&GRN{},
|
||||
true,
|
||||
},
|
||||
{ // Missing Group
|
||||
"grn::foo/Bar",
|
||||
&GRN{},
|
||||
true,
|
||||
},
|
||||
{ // good!
|
||||
"grn::roles/Admin",
|
||||
&GRN{TenantID: 0, ResourceKind: "roles", ResourceIdentifier: "Admin"},
|
||||
"grn::core.grafana.com/Role/Admin",
|
||||
&GRN{TenantID: 0, ResourceGroup: "core.grafana.com", ResourceKind: "Role", ResourceIdentifier: "Admin"},
|
||||
false,
|
||||
},
|
||||
{ // good!
|
||||
"grn::roles/Admin/with/some/slashes",
|
||||
&GRN{TenantID: 0, ResourceKind: "roles", ResourceIdentifier: "Admin/with/some/slashes"},
|
||||
"grn::core.grafana.com/Role/Admin/with/some/slashes",
|
||||
&GRN{TenantID: 0, ResourceGroup: "core.grafana.com", ResourceKind: "Role", ResourceIdentifier: "Admin/with/some/slashes"},
|
||||
false,
|
||||
},
|
||||
{ // good!
|
||||
"grn:123456789:roles/Admin/with/some/slashes",
|
||||
&GRN{TenantID: 123456789, ResourceKind: "roles", ResourceIdentifier: "Admin/with/some/slashes"},
|
||||
"grn:123456789:core.grafana.com/Role/Admin/with/some/slashes",
|
||||
&GRN{TenantID: 123456789, ResourceGroup: "core.grafana.com", ResourceKind: "Role", ResourceIdentifier: "Admin/with/some/slashes"},
|
||||
false,
|
||||
},
|
||||
{ // Weird, but valid.
|
||||
"grn::roles///Admin/with/leading/slashes",
|
||||
&GRN{TenantID: 0, ResourceKind: "roles", ResourceIdentifier: "//Admin/with/leading/slashes"},
|
||||
"grn::core.grafana.com/Role///Admin/with/leading/slashes",
|
||||
&GRN{TenantID: 0, ResourceGroup: "core.grafana.com", ResourceKind: "Role", ResourceIdentifier: "//Admin/with/leading/slashes"},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user