Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CHANGE] retracts case-sensitive support for tags #229

Closed
wants to merge 1 commit into from
Closed
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
5 changes: 5 additions & 0 deletions v2/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ go 1.18

require github.com/nats-io/nkeys v0.4.7

retract (
v2.7.1 // contains retractions only
v2.7.0 // includes case insensitive changes to tags that break jetstream placement
)

require (
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/sys v0.17.0 // indirect
Expand Down
1 change: 1 addition & 0 deletions v2/operator_claims_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ func TestTags(t *testing.T) {
}

AssertTrue(oc.GenericFields.Tags.Contains("one"), t)
AssertTrue(oc.GenericFields.Tags.Contains("ONE"), t)
AssertTrue(oc.GenericFields.Tags.Contains("TWO"), t)
AssertTrue(oc.GenericFields.Tags.Contains("three"), t)
}
Expand Down
165 changes: 118 additions & 47 deletions v2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"net"
"net/url"
"reflect"
"sort"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -421,96 +422,166 @@ func (u *StringList) Remove(p ...string) {
}
}

// TagList is a unique array of lower case strings
// TagList is a unique array of strings.
// All tag list methods lower case the strings in the arguments
type TagList []string

// Contains returns true if the list contains the tags
// Contains returns true if the list contains the tags.
func (u *TagList) Contains(p string) bool {
p = strings.ToLower(strings.TrimSpace(p))
return u.find(p) != -1
}

func (u *TagList) Equals(other *TagList) bool {
if len(*u) != len(*other) {
return false
}
for _, v := range *u {
if other.find(v) == -1 {
return false
}
}
return true
func (u *TagList) ContainsExact(p string) bool {
p = strings.TrimSpace(p)
return u.findExact(p) != -1
}

func (u *TagList) find(p string) int {
for idx, t := range *u {
if p == t {
if strings.EqualFold(t, p) {
return idx
}
}
return -1
}

// Add appends 1 or more tags to a list
func (u *TagList) Add(p ...string) {
for _, v := range p {
v = strings.TrimSpace(v)
if v == "" {
continue
}
if !u.Contains(v) {
*u = append(*u, v)
func (u *TagList) findExact(p string) int {
for idx, t := range *u {
if t == p {
return idx
}
}
return -1
}

// Remove removes 1 or more tags from a list
func (u *TagList) Remove(p ...string) error {
// Add appends 1 or more tags to a list, tags are converted to lowercase.
func (u *TagList) Add(p ...string) {
for _, v := range p {
v = strings.TrimSpace(v)
v = strings.ToLower(strings.TrimSpace(v))
idx := u.find(v)
if idx != -1 {
a := *u
*u = append(a[:idx], a[idx+1:]...)
a[idx] = v
} else {
return fmt.Errorf("unable to remove tag: %q - not found", v)
*u = append(*u, v)
}
}
return nil
}

type CIDRList []string

func (c *CIDRList) Contains(p string) bool {
p = strings.ToLower(strings.TrimSpace(p))
for _, t := range *c {
if t == p {
return true
func (u *TagList) AddExact(p ...string) {
for _, v := range p {
v = strings.TrimSpace(v)
idx := u.findExact(v)
if idx == -1 {
*u = append(*u, v)
}
}
return false
}

func (c *CIDRList) Add(p ...string) {
// Remove removes 1 or more tags from a list, removal is case-insensitive,
// and can remove any value that is EqualsFold
func (u *TagList) Remove(p ...string) {
for _, v := range p {
v = strings.ToLower(strings.TrimSpace(v))
if !c.Contains(v) && v != "" {
*c = append(*c, v)
v = strings.TrimSpace(v)
for {
idx := u.find(v)
if idx != -1 {
a := *u
*u = append(a[:idx], a[idx+1:]...)
} else {
break
}
}
}
}

func (c *CIDRList) Remove(p ...string) {
func (u *TagList) RemoveExact(p ...string) error {
for _, v := range p {
v = strings.ToLower(strings.TrimSpace(v))
for i, t := range *c {
if t == v {
a := *c
*c = append(a[:i], a[i+1:]...)
break
v = strings.TrimSpace(v)
idx := u.findExact(v)
if idx == -1 {
return fmt.Errorf("tag \"%q\" not found", v)
}
a := *u
*u = append(a[:idx], a[idx+1:]...)
}
return nil
}

// FindTag finds entries that start with the specified name
// followed by a colon (:). Names are case-insensitive for
// matches. This function returns the name+:+value (the entire "entry").
func (u *TagList) FindTag(v string) *TagList {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bit sad to be adding new case insensitive APIs here but I guess this is to replace the feature where the server today does its own walking and iterating.

var matches TagList
// must have a suffix that is name+":"
if !strings.HasSuffix(v, ":") {
v = fmt.Sprintf("%s:", v)
}
// to be a valid tag it must have a name
if v == ":" {
return &matches
}
for _, t := range *u {
idx := strings.Index(t, ":")
if idx != -1 {
prefix := t[:idx+1]
value := t[idx+1:]
// to be a valid tag, it must match the name and have a value
if strings.EqualFold(v, prefix) && len(value) > 0 {
matches = append(matches, t)
}
}
}
return &matches
}

// GetTagValueFor finds entries that start with the specified name
// followed by a colon (:). Names are case-insensitive for
// matches. This function returns the value portion of the entry
func (u *TagList) GetTagValueFor(v string) *TagList {
tags := u.FindTag(v)
if !strings.HasSuffix(v, ":") {
v = fmt.Sprintf("%s:", v)
}
start := len(v)
a := *tags
for idx, t := range a {
a[idx] = t[start:]
}
return &a
}

func (u *TagList) Equals(other *TagList) bool {
if len(*u) != len(*other) {
return false
}

a := sort.StringSlice(*u)
sort.Sort(a)
b := sort.StringSlice(*other)
sort.Sort(b)

for i, v := range a {
if v != b[i] {
return false
}
}
return true
}

type CIDRList TagList

func (c *CIDRList) Contains(p string) bool {
return (*TagList)(c).Contains(p)
}

func (c *CIDRList) Add(p ...string) {
(*TagList)(c).Add(p...)
}

func (c *CIDRList) Remove(p ...string) {
(*TagList)(c).Remove(p...)
}

func (c *CIDRList) Set(values string) {
Expand Down
Loading
Loading