Skip to content

Commit

Permalink
add methods for parsing and stringifying acl related resources (#1218)
Browse files Browse the repository at this point in the history
* Add methods for marshalling/unmarshalling ACLOperationType and ACLPermissionType

* Fix unknown types

* Implement ResourceType

* make function ordering more consistent

* add patterntype and resourcetype

* make capitalization consistent

* add comment and test around ResourceTypeBroker and ResourceTypeCluster having same value
  • Loading branch information
petedannemann committed Oct 30, 2023
1 parent f2d9e08 commit c6378c3
Show file tree
Hide file tree
Showing 4 changed files with 272 additions and 0 deletions.
94 changes: 94 additions & 0 deletions createacls.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net"
"strings"
"time"

"github.com/segmentio/kafka-go/protocol/createacls"
Expand Down Expand Up @@ -42,6 +43,43 @@ const (
ACLPermissionTypeAllow ACLPermissionType = 3
)

func (apt ACLPermissionType) String() string {
mapping := map[ACLPermissionType]string{
ACLPermissionTypeUnknown: "Unknown",
ACLPermissionTypeAny: "Any",
ACLPermissionTypeDeny: "Deny",
ACLPermissionTypeAllow: "Allow",
}
s, ok := mapping[apt]
if !ok {
s = mapping[ACLPermissionTypeUnknown]
}
return s
}

// MarshalText transforms an ACLPermissionType into its string representation.
func (apt ACLPermissionType) MarshalText() ([]byte, error) {
return []byte(apt.String()), nil
}

// UnmarshalText takes a string representation of the resource type and converts it to an ACLPermissionType.
func (apt *ACLPermissionType) UnmarshalText(text []byte) error {
normalized := strings.ToLower(string(text))
mapping := map[string]ACLPermissionType{
"unknown": ACLPermissionTypeUnknown,
"any": ACLPermissionTypeAny,
"deny": ACLPermissionTypeDeny,
"allow": ACLPermissionTypeAllow,
}
parsed, ok := mapping[normalized]
if !ok {
*apt = ACLPermissionTypeUnknown
return fmt.Errorf("cannot parse %s as an ACLPermissionType", normalized)
}
*apt = parsed
return nil
}

type ACLOperationType int8

const (
Expand All @@ -60,6 +98,62 @@ const (
ACLOperationTypeIdempotentWrite ACLOperationType = 12
)

func (aot ACLOperationType) String() string {
mapping := map[ACLOperationType]string{
ACLOperationTypeUnknown: "Unknown",
ACLOperationTypeAny: "Any",
ACLOperationTypeAll: "All",
ACLOperationTypeRead: "Read",
ACLOperationTypeWrite: "Write",
ACLOperationTypeCreate: "Create",
ACLOperationTypeDelete: "Delete",
ACLOperationTypeAlter: "Alter",
ACLOperationTypeDescribe: "Describe",
ACLOperationTypeClusterAction: "ClusterAction",
ACLOperationTypeDescribeConfigs: "DescribeConfigs",
ACLOperationTypeAlterConfigs: "AlterConfigs",
ACLOperationTypeIdempotentWrite: "IdempotentWrite",
}
s, ok := mapping[aot]
if !ok {
s = mapping[ACLOperationTypeUnknown]
}
return s
}

// MarshalText transforms an ACLOperationType into its string representation.
func (aot ACLOperationType) MarshalText() ([]byte, error) {
return []byte(aot.String()), nil
}

// UnmarshalText takes a string representation of the resource type and converts it to an ACLPermissionType.
func (aot *ACLOperationType) UnmarshalText(text []byte) error {
normalized := strings.ToLower(string(text))
mapping := map[string]ACLOperationType{
"unknown": ACLOperationTypeUnknown,
"any": ACLOperationTypeAny,
"all": ACLOperationTypeAll,
"read": ACLOperationTypeRead,
"write": ACLOperationTypeWrite,
"create": ACLOperationTypeCreate,
"delete": ACLOperationTypeDelete,
"alter": ACLOperationTypeAlter,
"describe": ACLOperationTypeDescribe,
"clusteraction": ACLOperationTypeClusterAction,
"describeconfigs": ACLOperationTypeDescribeConfigs,
"alterconfigs": ACLOperationTypeAlterConfigs,
"idempotentwrite": ACLOperationTypeIdempotentWrite,
}
parsed, ok := mapping[normalized]
if !ok {
*aot = ACLOperationTypeUnknown
return fmt.Errorf("cannot parse %s as an ACLOperationType", normalized)
}
*aot = parsed
return nil

}

type ACLEntry struct {
ResourceType ResourceType
ResourceName string
Expand Down
34 changes: 34 additions & 0 deletions createacls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,37 @@ func TestClientCreateACLs(t *testing.T) {
}
}
}

func TestACLPermissionTypeMarshal(t *testing.T) {
for i := ACLPermissionTypeUnknown; i <= ACLPermissionTypeAllow; i++ {
text, err := i.MarshalText()
if err != nil {
t.Errorf("couldn't marshal %d to text: %s", i, err)
}
var got ACLPermissionType
err = got.UnmarshalText(text)
if err != nil {
t.Errorf("couldn't unmarshal %s to ACLPermissionType: %s", text, err)
}
if got != i {
t.Errorf("got %d, want %d", got, i)
}
}
}

func TestACLOperationTypeMarshal(t *testing.T) {
for i := ACLOperationTypeUnknown; i <= ACLOperationTypeIdempotentWrite; i++ {
text, err := i.MarshalText()
if err != nil {
t.Errorf("couldn't marshal %d to text: %s", i, err)
}
var got ACLOperationType
err = got.UnmarshalText(text)
if err != nil {
t.Errorf("couldn't unmarshal %s to ACLOperationType: %s", text, err)
}
if got != i {
t.Errorf("got %d, want %d", got, i)
}
}
}
86 changes: 86 additions & 0 deletions resource.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package kafka

import (
"fmt"
"strings"
)

// https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/resource/ResourceType.java
type ResourceType int8

Expand All @@ -15,6 +20,50 @@ const (
ResourceTypeDelegationToken ResourceType = 6
)

func (rt ResourceType) String() string {
mapping := map[ResourceType]string{
ResourceTypeUnknown: "Unknown",
ResourceTypeAny: "Any",
ResourceTypeTopic: "Topic",
ResourceTypeGroup: "Group",
// Note that ResourceTypeBroker and ResourceTypeCluster have the same value.
// A map cannot have duplicate values so we just use the same value for both.
ResourceTypeCluster: "Cluster",
ResourceTypeTransactionalID: "Transactionalid",
ResourceTypeDelegationToken: "Delegationtoken",
}
s, ok := mapping[rt]
if !ok {
s = mapping[ResourceTypeUnknown]
}
return s
}

func (rt ResourceType) MarshalText() ([]byte, error) {
return []byte(rt.String()), nil
}

func (rt *ResourceType) UnmarshalText(text []byte) error {
normalized := strings.ToLower(string(text))
mapping := map[string]ResourceType{
"unknown": ResourceTypeUnknown,
"any": ResourceTypeAny,
"topic": ResourceTypeTopic,
"group": ResourceTypeGroup,
"broker": ResourceTypeBroker,
"cluster": ResourceTypeCluster,
"transactionalid": ResourceTypeTransactionalID,
"delegationtoken": ResourceTypeDelegationToken,
}
parsed, ok := mapping[normalized]
if !ok {
*rt = ResourceTypeUnknown
return fmt.Errorf("cannot parse %s as a ResourceType", normalized)
}
*rt = parsed
return nil
}

// https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/resource/PatternType.java
type PatternType int8

Expand All @@ -35,3 +84,40 @@ const (
// that start with 'foo'.
PatternTypePrefixed PatternType = 4
)

func (pt PatternType) String() string {
mapping := map[PatternType]string{
PatternTypeUnknown: "Unknown",
PatternTypeAny: "Any",
PatternTypeMatch: "Match",
PatternTypeLiteral: "Literal",
PatternTypePrefixed: "Prefixed",
}
s, ok := mapping[pt]
if !ok {
s = mapping[PatternTypeUnknown]
}
return s
}

func (pt PatternType) MarshalText() ([]byte, error) {
return []byte(pt.String()), nil
}

func (pt *PatternType) UnmarshalText(text []byte) error {
normalized := strings.ToLower(string(text))
mapping := map[string]PatternType{
"unknown": PatternTypeUnknown,
"any": PatternTypeAny,
"match": PatternTypeMatch,
"literal": PatternTypeLiteral,
"prefixed": PatternTypePrefixed,
}
parsed, ok := mapping[normalized]
if !ok {
*pt = PatternTypeUnknown
return fmt.Errorf("cannot parse %s as a PatternType", normalized)
}
*pt = parsed
return nil
}
58 changes: 58 additions & 0 deletions resource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package kafka

import "testing"

func TestResourceTypeMarshal(t *testing.T) {
for i := ResourceTypeUnknown; i <= ResourceTypeDelegationToken; i++ {
text, err := i.MarshalText()
if err != nil {
t.Errorf("couldn't marshal %d to text: %s", i, err)
}
var got ResourceType
err = got.UnmarshalText(text)
if err != nil {
t.Errorf("couldn't unmarshal %s to ResourceType: %s", text, err)
}
if got != i {
t.Errorf("got %d, want %d", got, i)
}
}
}

// Verify that the text version of ResourceTypeBroker is "Cluster".
// This is added since ResourceTypeBroker and ResourceTypeCluster
// have the same value.
func TestResourceTypeBroker(t *testing.T) {
text, err := ResourceTypeBroker.MarshalText()
if err != nil {
t.Errorf("couldn't marshal %d to text: %s", ResourceTypeBroker, err)
}
if string(text) != "Cluster" {
t.Errorf("got %s, want %s", string(text), "Cluster")
}
var got ResourceType
err = got.UnmarshalText(text)
if err != nil {
t.Errorf("couldn't unmarshal %s to ResourceType: %s", text, err)
}
if got != ResourceTypeBroker {
t.Errorf("got %d, want %d", got, ResourceTypeBroker)
}
}

func TestPatternTypeMarshal(t *testing.T) {
for i := PatternTypeUnknown; i <= PatternTypePrefixed; i++ {
text, err := i.MarshalText()
if err != nil {
t.Errorf("couldn't marshal %d to text: %s", i, err)
}
var got PatternType
err = got.UnmarshalText(text)
if err != nil {
t.Errorf("couldn't unmarshal %s to PatternType: %s", text, err)
}
if got != i {
t.Errorf("got %d, want %d", got, i)
}
}
}

0 comments on commit c6378c3

Please sign in to comment.