This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

util: Create a new str package

The util package had several basic string manipulation functions.
This patch pulls them into their own str package, mostly so that
they're a little bit prettier when called.  It also adds new functions
useful in a future patch.
  • Loading branch information...
ejj committed Nov 8, 2017
1 parent 1a98b9f commit cf134177bfedc9a432e567e46572deb056898c20
View
@@ -9,6 +9,7 @@ import (
"github.com/kelda/kelda/db"
"github.com/kelda/kelda/join"
"github.com/kelda/kelda/util"
"github.com/kelda/kelda/util/str"
log "github.com/sirupsen/logrus"
)
@@ -143,5 +144,5 @@ func containerValueMapKey(x map[string]blueprint.ContainerValue) string {
for key, val := range x {
m[key] = fmt.Sprintf("%+v", val)
}
return util.MapAsString(m)
return str.MapAsString(m)
}
@@ -10,7 +10,7 @@ import (
"github.com/kelda/kelda/join"
"github.com/kelda/kelda/minion/ipdef"
"github.com/kelda/kelda/minion/ovsdb"
"github.com/kelda/kelda/util"
"github.com/kelda/kelda/util/str"
)
/*
@@ -83,7 +83,7 @@ func updateLoadBalancerIPs(client ovsdb.Client, loadBalancers []db.LoadBalancer,
lb := intf.(ovsdb.LoadBalancer)
return struct{ Name, VIPs string }{
Name: lb.Name,
VIPs: util.MapAsString(lb.VIPs),
VIPs: str.MapAsString(lb.VIPs),
}
}
_, toAdd, toRemove := join.HashJoin(loadBalancerSlice(target),
View
@@ -11,7 +11,7 @@ import (
"github.com/kelda/kelda/db"
"github.com/kelda/kelda/join"
"github.com/kelda/kelda/minion/nl"
"github.com/kelda/kelda/util"
"github.com/kelda/kelda/util/str"
"github.com/coreos/go-iptables/iptables"
log "github.com/sirupsen/logrus"
@@ -138,7 +138,7 @@ func ruleKey(intf interface{}) interface{} {
opts[flagName] = flagVal
}
return util.MapAsString(opts)
return str.MapAsString(opts)
}
func syncChain(ipt IPTables, table, chain string, target []string) error {
View
@@ -12,7 +12,7 @@ import (
"github.com/kelda/kelda/db"
"github.com/kelda/kelda/minion/ipdef"
"github.com/kelda/kelda/minion/nl"
"github.com/kelda/kelda/util"
"github.com/kelda/kelda/util/str"
)
var subnetC = counter.New("Subnet Sync")
@@ -51,7 +51,7 @@ func writeSubnetsOnce(conn db.Conn) error {
conn.Txn(db.MinionTable).Run(func(view db.Database) error {
self := view.MinionSelf()
if !util.StrSliceEqual(subnets, self.HostSubnets) {
if !str.SliceEq(subnets, self.HostSubnets) {
subnetC.Inc("Update subnets")
self.HostSubnets = subnets
view.Commit(self)
@@ -6,7 +6,7 @@ import (
"sort"
"github.com/kelda/kelda/db"
"github.com/kelda/kelda/util"
"github.com/kelda/kelda/util/str"
log "github.com/sirupsen/logrus"
)
@@ -256,7 +256,7 @@ func (s dbcSlice) Less(i, j int) bool {
switch {
case s[i].Image != s[j].Image:
return s[i].Image < s[j].Image
case !util.StrSliceEqual(s[i].Command, s[j].Command):
case !str.SliceEq(s[i].Command, s[j].Command):
return fmt.Sprintf("%s", s[i].Command) < fmt.Sprintf("%s", s[j].Command)
default:
return s[i].BlueprintID < s[j].BlueprintID
@@ -16,7 +16,7 @@ import (
"github.com/kelda/kelda/minion/network/openflow"
"github.com/kelda/kelda/minion/network/plugin"
"github.com/kelda/kelda/minion/vault"
"github.com/kelda/kelda/util"
"github.com/kelda/kelda/util/str"
log "github.com/sirupsen/logrus"
)
@@ -243,16 +243,16 @@ func syncJoinScore(left, right interface{}) int {
cmd1 := dkc.Args
cmd2 := append([]string{dkc.Path}, dkc.Args...)
if len(dbc.Command) != 0 &&
!util.StrSliceEqual(dbc.Command, cmd1) &&
!util.StrSliceEqual(dbc.Command, cmd2) {
!str.SliceEq(dbc.Command, cmd1) &&
!str.SliceEq(dbc.Command, cmd2) {
return -1
}
return 0
}
func filesHash(filepathToContent map[string]string) string {
toHash := util.MapAsString(filepathToContent)
toHash := str.MapAsString(filepathToContent)
return fmt.Sprintf("%x", sha1.Sum([]byte(toHash)))
}
@@ -6,6 +6,7 @@ import (
"github.com/kelda/kelda/db"
"github.com/kelda/kelda/minion/supervisor/images"
"github.com/kelda/kelda/util"
"github.com/kelda/kelda/util/str"
)
func runMaster() {
@@ -35,7 +36,7 @@ func runMasterOnce() {
etcdIPs := etcdRow.EtcdIPs
leader := etcdRow.Leader
if oldIP != IP || !util.StrSliceEqual(oldEtcdIPs, etcdIPs) {
if oldIP != IP || !str.SliceEq(oldEtcdIPs, etcdIPs) {
c.Inc("Reset Etcd")
Remove(images.Etcd)
}
@@ -10,6 +10,7 @@ import (
"github.com/kelda/kelda/minion/nl"
"github.com/kelda/kelda/minion/supervisor/images"
"github.com/kelda/kelda/util"
"github.com/kelda/kelda/util/str"
log "github.com/sirupsen/logrus"
)
@@ -64,7 +65,7 @@ func runWorkerOnce() {
leaderIP := etcdRow.LeaderIP
IP := minion.PrivateIP
if !util.StrSliceEqual(oldEtcdIPs, etcdIPs) {
if !str.SliceEq(oldEtcdIPs, etcdIPs) {
c.Inc("Reset Etcd")
Remove(images.Etcd)
}
View
@@ -0,0 +1,75 @@
package str
import (
"fmt"
"sort"
)
// SliceFilterOut returns a new slice that omits all instances of `item`.
func SliceFilterOut(slice []string, item string) []string {
var result []string
for _, s := range slice {
if s != item {
result = append(result, s)
}
}
return result
}
// SliceContains returns true if `item` is in `slice`.
func SliceContains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
}
// SliceEq returns true of the string slices 'x' and 'y' are identical.
func SliceEq(x, y []string) bool {
return SliceCmp(x, y) == 0
}
// SliceCmp applies an arbitrary ordering to slices of strings. Returns -1 if x < y, 1
// if x > y, 0 otherwise.
func SliceCmp(x, y []string) int {
if len(x) < len(y) {
return -1
} else if len(x) > len(y) {
return 1
}
for i, v := range x {
if v < y[i] {
return -1
} else if v > y[i] {
return 1
}
}
return 0
}
// MapEq returns true if the string->string maps 'x' and 'y' are equal.
func MapEq(x, y map[string]string) bool {
if len(x) != len(y) {
return false
}
for k, v := range x {
if yVal, ok := y[k]; !ok || v != yVal {
return false
}
}
return true
}
// MapAsString creates a deterministic string representing the given map.
func MapAsString(m map[string]string) string {
var strs []string
for k, v := range m {
strs = append(strs, fmt.Sprintf("%s=%s", k, v))
}
sort.Sort(sort.StringSlice(strs))
return fmt.Sprintf("%v", strs)
}
View
@@ -0,0 +1,78 @@
package str
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSliceFilter(t *testing.T) {
t.Parallel()
assert.Equal(t, SliceFilterOut([]string{"a", "b", "a"}, "a"), []string{"b"})
assert.Equal(t, SliceFilterOut([]string{"a", "b"}, "c"), []string{"a", "b"})
}
func TestSliceContains(t *testing.T) {
t.Parallel()
assert.True(t, SliceContains([]string{"b", "a"}, "a"))
assert.False(t, SliceContains([]string{"b", "a"}, "c"))
}
func TestSliceCmp(t *testing.T) {
t.Parallel()
x := []string{}
y := []string{}
assert.Zero(t, SliceCmp(x, y))
assert.True(t, SliceEq(x, y))
x = append(x, "1")
assert.Equal(t, 1, SliceCmp(x, y))
assert.Equal(t, -1, SliceCmp(y, x))
assert.False(t, SliceEq(x, y))
y = append(y, "1")
assert.Zero(t, SliceCmp(x, y))
assert.True(t, SliceEq(x, y))
x = append(x, "a")
y = append(y, "b")
assert.Equal(t, -1, SliceCmp(x, y))
assert.Equal(t, 1, SliceCmp(y, x))
assert.False(t, SliceEq(x, y))
}
func TestMapEqual(t *testing.T) {
t.Parallel()
a := map[string]string{}
b := map[string]string{}
assert.True(t, MapEq(a, b))
a["1"] = "1"
assert.False(t, MapEq(a, b))
b["1"] = a["1"]
assert.True(t, MapEq(a, b))
b["1"] = "2"
assert.False(t, MapEq(a, b))
}
func TestMapString(t *testing.T) {
t.Parallel()
// Run the tests multiple times to test determinism.
for i := 0; i < 10; i++ {
assert.Equal(t, "[a=1 b=2]", MapAsString(
map[string]string{"a": "1", "b": "2"}))
// Nil and empty maps are the same.
assert.Equal(t, "[]", MapAsString(nil))
assert.Equal(t, "[]", MapAsString(map[string]string{}))
}
}
View
@@ -11,7 +11,6 @@ import (
"net/http"
"os"
"path/filepath"
"sort"
"strings"
"time"
@@ -150,44 +149,6 @@ func Walk(root string, walkFn filepath.WalkFunc) error {
return afero.Walk(a, root, walkFn)
}
// StrSliceEqual returns true of the string slices 'x' and 'y' are identical.
func StrSliceEqual(x, y []string) bool {
if len(x) != len(y) {
return false
}
for i, v := range x {
if v != y[i] {
return false
}
}
return true
}
// StrStrMapEqual returns true of the string->string maps 'x' and 'y' are equal.
func StrStrMapEqual(x, y map[string]string) bool {
if len(x) != len(y) {
return false
}
for k, v := range x {
if yVal, ok := y[k]; !ok {
return false
} else if v != yVal {
return false
}
}
return true
}
// MapAsString creates a deterministic string representing the given map.
func MapAsString(m map[string]string) string {
var strs []string
for k, v := range m {
strs = append(strs, fmt.Sprintf("%s=%s", k, v))
}
sort.Sort(sort.StringSlice(strs))
return fmt.Sprintf("%v", strs)
}
// After returns whether the current time is after t. It is stored in a variable so it
// can be mocked out for unit tests.
var After = func(t time.Time) bool {
View
@@ -68,18 +68,6 @@ func TestWaitFor(t *testing.T) {
assert.EqualError(t, err, "timed out")
}
func TestMapAsString(t *testing.T) {
// Run the tests multiple times to test determinism.
for i := 0; i < 10; i++ {
assert.Equal(t, "[a=1 b=2]", MapAsString(
map[string]string{"a": "1", "b": "2"}))
// Nil and empty maps are the same.
assert.Equal(t, "[]", MapAsString(nil))
assert.Equal(t, "[]", MapAsString(map[string]string{}))
}
}
func TestJoinNotifiers(t *testing.T) {
t.Parallel()

0 comments on commit cf13417

Please sign in to comment.