etcd: Rewrite embed etcd implementation
This is a giant cleanup of the etcd code. The earlier version was written when I was less experienced with golang. This is still not perfect, and does contain some races, but at least it's a decent base to start from. The automatic elastic clustering should be considered an experimental feature. If you need a more battle-tested cluster, then you should manage etcd manually and point mgmt at your existing cluster.
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
package coreworld
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/purpleidea/mgmt/lang/funcs"
|
||||
@@ -75,6 +76,8 @@ func (obj *ExchangeFunc) Init(init *interfaces.Init) error {
|
||||
// Stream returns the changing values that this func has over time.
|
||||
func (obj *ExchangeFunc) Stream() error {
|
||||
defer close(obj.init.Output) // the sender closes
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
for {
|
||||
select {
|
||||
// TODO: should this first chan be run as a priority channel to
|
||||
@@ -105,8 +108,13 @@ func (obj *ExchangeFunc) Stream() error {
|
||||
// TODO: support changing the namespace over time...
|
||||
// TODO: possibly removing our stored value there first!
|
||||
if obj.namespace == "" {
|
||||
obj.namespace = namespace // store it
|
||||
obj.watchChan = obj.init.World.StrMapWatch(obj.namespace) // watch for var changes
|
||||
obj.namespace = namespace // store it
|
||||
var err error
|
||||
obj.watchChan, err = obj.init.World.StrMapWatch(ctx, obj.namespace) // watch for var changes
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else if obj.namespace != namespace {
|
||||
return fmt.Errorf("can't change namespace, previously: `%s`", obj.namespace)
|
||||
}
|
||||
@@ -116,7 +124,7 @@ func (obj *ExchangeFunc) Stream() error {
|
||||
obj.init.Logf("value: %+v", value)
|
||||
}
|
||||
|
||||
if err := obj.init.World.StrMapSet(obj.namespace, value); err != nil {
|
||||
if err := obj.init.World.StrMapSet(ctx, obj.namespace, value); err != nil {
|
||||
return errwrap.Wrapf(err, "namespace write error of `%s` to `%s`", value, obj.namespace)
|
||||
}
|
||||
|
||||
@@ -134,7 +142,7 @@ func (obj *ExchangeFunc) Stream() error {
|
||||
return errwrap.Wrapf(err, "channel watch failed on `%s`", obj.namespace)
|
||||
}
|
||||
|
||||
keyMap, err := obj.init.World.StrMapGet(obj.namespace)
|
||||
keyMap, err := obj.init.World.StrMapGet(ctx, obj.namespace)
|
||||
if err != nil {
|
||||
return errwrap.Wrapf(err, "channel read failed on `%s`", obj.namespace)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package coreworld
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/purpleidea/mgmt/lang/funcs"
|
||||
@@ -73,6 +74,8 @@ func (obj *KVLookupFunc) Init(init *interfaces.Init) error {
|
||||
// Stream returns the changing values that this func has over time.
|
||||
func (obj *KVLookupFunc) Stream() error {
|
||||
defer close(obj.init.Output) // the sender closes
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
for {
|
||||
select {
|
||||
// TODO: should this first chan be run as a priority channel to
|
||||
@@ -103,10 +106,14 @@ func (obj *KVLookupFunc) Stream() error {
|
||||
// TODO: support changing the namespace over time...
|
||||
// TODO: possibly removing our stored value there first!
|
||||
if obj.namespace == "" {
|
||||
obj.namespace = namespace // store it
|
||||
obj.watchChan = obj.init.World.StrMapWatch(obj.namespace) // watch for var changes
|
||||
obj.namespace = namespace // store it
|
||||
var err error
|
||||
obj.watchChan, err = obj.init.World.StrMapWatch(ctx, obj.namespace) // watch for var changes
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result, err := obj.buildMap() // build the map...
|
||||
result, err := obj.buildMap(ctx) // build the map...
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -135,7 +142,7 @@ func (obj *KVLookupFunc) Stream() error {
|
||||
return errwrap.Wrapf(err, "channel watch failed on `%s`", obj.namespace)
|
||||
}
|
||||
|
||||
result, err := obj.buildMap() // build the map...
|
||||
result, err := obj.buildMap(ctx) // build the map...
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -166,8 +173,8 @@ func (obj *KVLookupFunc) Close() error {
|
||||
}
|
||||
|
||||
// buildMap builds the result map which we'll need. It uses struct variables.
|
||||
func (obj *KVLookupFunc) buildMap() (types.Value, error) {
|
||||
keyMap, err := obj.init.World.StrMapGet(obj.namespace)
|
||||
func (obj *KVLookupFunc) buildMap(ctx context.Context) (types.Value, error) {
|
||||
keyMap, err := obj.init.World.StrMapGet(ctx, obj.namespace)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf(err, "channel read failed on `%s`", obj.namespace)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// test with:
|
||||
// time ./mgmt run --hostname h1 --ideal-cluster-size 1 --tmp-prefix --no-pgp lang --lang examples/lang/schedule0.mcl
|
||||
// time ./mgmt run --hostname h1 --tmp-prefix --no-pgp lang --lang examples/lang/schedule0.mcl
|
||||
// time ./mgmt run --hostname h2 --seeds http://127.0.0.1:2379 --client-urls http://127.0.0.1:2381 --server-urls http://127.0.0.1:2382 --tmp-prefix --no-pgp lang --lang examples/lang/schedule0.mcl
|
||||
// time ./mgmt run --hostname h3 --seeds http://127.0.0.1:2379 --client-urls http://127.0.0.1:2383 --server-urls http://127.0.0.1:2384 --tmp-prefix --no-pgp lang --lang examples/lang/schedule0.mcl
|
||||
// kill h2 (should see h1 and h3 pick [h1, h3] instead)
|
||||
|
||||
Reference in New Issue
Block a user