2014-06-06 23:40:48 +00:00
|
|
|
/*
|
2015-05-01 16:19:44 +00:00
|
|
|
Copyright 2014 The Kubernetes Authors All rights reserved.
|
2014-06-06 23:40:48 +00:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
2014-06-23 18:32:11 +00:00
|
|
|
|
2014-06-30 19:00:14 +00:00
|
|
|
package tools
|
2014-06-06 23:40:48 +00:00
|
|
|
|
|
|
|
import (
|
2014-07-31 10:25:42 +00:00
|
|
|
"errors"
|
2015-01-26 22:03:48 +00:00
|
|
|
"sort"
|
2014-08-05 18:43:19 +00:00
|
|
|
"sync"
|
2014-06-06 23:40:48 +00:00
|
|
|
|
|
|
|
"github.com/coreos/go-etcd/etcd"
|
|
|
|
)
|
|
|
|
|
|
|
|
type EtcdResponseWithError struct {
|
|
|
|
R *etcd.Response
|
|
|
|
E error
|
2014-08-04 21:42:51 +00:00
|
|
|
// if N is non-null, it will be assigned into the map after this response is used for an operation
|
|
|
|
N *EtcdResponseWithError
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
|
2014-07-27 13:30:32 +00:00
|
|
|
// TestLogger is a type passed to Test functions to support formatted test logs.
|
|
|
|
type TestLogger interface {
|
2015-02-11 23:35:05 +00:00
|
|
|
Fatalf(format string, args ...interface{})
|
2014-07-27 13:30:32 +00:00
|
|
|
Errorf(format string, args ...interface{})
|
|
|
|
Logf(format string, args ...interface{})
|
|
|
|
}
|
|
|
|
|
2014-06-06 23:40:48 +00:00
|
|
|
type FakeEtcdClient struct {
|
2014-07-03 00:30:56 +00:00
|
|
|
watchCompletedChan chan bool
|
2014-06-30 21:48:57 +00:00
|
|
|
|
2014-07-31 11:33:18 +00:00
|
|
|
Data map[string]EtcdResponseWithError
|
|
|
|
DeletedKeys []string
|
|
|
|
expectNotFoundGetSet map[string]struct{}
|
2014-08-05 18:43:19 +00:00
|
|
|
sync.Mutex
|
|
|
|
Err error
|
2015-03-06 05:53:27 +00:00
|
|
|
CasErr error
|
2014-08-05 18:43:19 +00:00
|
|
|
t TestLogger
|
|
|
|
Ix int
|
|
|
|
TestIndex bool
|
|
|
|
ChangeIndex uint64
|
2014-09-24 00:47:05 +00:00
|
|
|
LastSetTTL uint64
|
2014-11-02 20:52:31 +00:00
|
|
|
Machines []string
|
2014-06-18 20:10:19 +00:00
|
|
|
|
|
|
|
// Will become valid after Watch is called; tester may write to it. Tester may
|
|
|
|
// also read from it to verify that it's closed after injecting an error.
|
|
|
|
WatchResponse chan *etcd.Response
|
2014-08-11 04:08:06 +00:00
|
|
|
WatchIndex uint64
|
2014-06-18 20:10:19 +00:00
|
|
|
// Write to this to prematurely stop a Watch that is running in a goroutine.
|
|
|
|
WatchInjectError chan<- error
|
|
|
|
WatchStop chan<- bool
|
2014-09-20 00:53:55 +00:00
|
|
|
// If non-nil, will be returned immediately when Watch is called.
|
|
|
|
WatchImmediateError error
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
|
2014-08-21 04:27:19 +00:00
|
|
|
func NewFakeEtcdClient(t TestLogger) *FakeEtcdClient {
|
2014-06-30 22:32:55 +00:00
|
|
|
ret := &FakeEtcdClient{
|
2014-07-31 11:33:18 +00:00
|
|
|
t: t,
|
|
|
|
expectNotFoundGetSet: map[string]struct{}{},
|
|
|
|
Data: map[string]EtcdResponseWithError{},
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
2014-07-01 18:21:17 +00:00
|
|
|
// There are three publicly accessible channels in FakeEtcdClient:
|
|
|
|
// - WatchResponse
|
|
|
|
// - WatchInjectError
|
|
|
|
// - WatchStop
|
|
|
|
// They are only available when Watch() is called. If users of
|
|
|
|
// FakeEtcdClient want to use any of these channels, they have to call
|
|
|
|
// WaitForWatchCompletion before any operation on these channels.
|
2014-07-03 00:30:56 +00:00
|
|
|
// Internally, FakeEtcdClient use watchCompletedChan to indicate if the
|
2014-07-01 18:21:17 +00:00
|
|
|
// Watch() method has been called. WaitForWatchCompletion() will wait
|
2014-07-03 00:30:56 +00:00
|
|
|
// on this channel. WaitForWatchCompletion() will return only when
|
|
|
|
// WatchResponse, WatchInjectError and WatchStop are ready to read/write.
|
|
|
|
ret.watchCompletedChan = make(chan bool)
|
2014-06-30 22:32:55 +00:00
|
|
|
return ret
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
|
2015-02-11 23:35:05 +00:00
|
|
|
func (f *FakeEtcdClient) SetError(err error) {
|
|
|
|
f.Err = err
|
|
|
|
}
|
|
|
|
|
2014-11-02 20:52:31 +00:00
|
|
|
func (f *FakeEtcdClient) GetCluster() []string {
|
|
|
|
return f.Machines
|
|
|
|
}
|
|
|
|
|
2014-07-31 11:33:18 +00:00
|
|
|
func (f *FakeEtcdClient) ExpectNotFoundGet(key string) {
|
|
|
|
f.expectNotFoundGetSet[key] = struct{}{}
|
|
|
|
}
|
|
|
|
|
2015-02-11 23:35:05 +00:00
|
|
|
func (f *FakeEtcdClient) NewError(code int) *etcd.EtcdError {
|
|
|
|
return &etcd.EtcdError{
|
|
|
|
ErrorCode: code,
|
|
|
|
Index: f.ChangeIndex,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-30 10:31:35 +00:00
|
|
|
func (f *FakeEtcdClient) generateIndex() uint64 {
|
2014-07-31 07:01:28 +00:00
|
|
|
if !f.TestIndex {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2014-07-30 10:31:35 +00:00
|
|
|
f.ChangeIndex++
|
2014-08-08 20:25:36 +00:00
|
|
|
f.t.Logf("generating index %v", f.ChangeIndex)
|
2014-07-30 10:31:35 +00:00
|
|
|
return f.ChangeIndex
|
|
|
|
}
|
|
|
|
|
2014-08-04 21:42:51 +00:00
|
|
|
// Requires that f.Mutex be held.
|
|
|
|
func (f *FakeEtcdClient) updateResponse(key string) {
|
|
|
|
resp, found := f.Data[key]
|
|
|
|
if !found || resp.N == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
f.Data[key] = *resp.N
|
|
|
|
}
|
|
|
|
|
2014-06-06 23:40:48 +00:00
|
|
|
func (f *FakeEtcdClient) Get(key string, sort, recursive bool) (*etcd.Response, error) {
|
2015-02-11 23:35:05 +00:00
|
|
|
if f.Err != nil {
|
|
|
|
return nil, f.Err
|
|
|
|
}
|
|
|
|
|
2014-08-05 18:43:19 +00:00
|
|
|
f.Mutex.Lock()
|
|
|
|
defer f.Mutex.Unlock()
|
2014-08-04 21:42:51 +00:00
|
|
|
defer f.updateResponse(key)
|
2014-08-05 18:43:19 +00:00
|
|
|
|
2014-06-06 23:40:48 +00:00
|
|
|
result := f.Data[key]
|
|
|
|
if result.R == nil {
|
2014-07-31 11:33:18 +00:00
|
|
|
if _, ok := f.expectNotFoundGetSet[key]; !ok {
|
2015-08-11 07:20:21 +00:00
|
|
|
f.t.Logf("data for %s was not defined prior to invoking Get", key)
|
2014-07-31 11:33:18 +00:00
|
|
|
}
|
2015-02-11 23:35:05 +00:00
|
|
|
return &etcd.Response{}, f.NewError(EtcdErrorCodeNotFound)
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
2015-01-07 15:36:59 +00:00
|
|
|
f.t.Logf("returning %v: %#v %#v", key, result.R, result.E)
|
2015-01-26 22:03:48 +00:00
|
|
|
|
2015-08-18 15:03:54 +00:00
|
|
|
// Sort response, note this will alter result.R.
|
2015-01-26 22:03:48 +00:00
|
|
|
if result.R.Node != nil && result.R.Node.Nodes != nil && sort {
|
|
|
|
f.sortResponse(result.R.Node.Nodes)
|
|
|
|
}
|
2014-06-06 23:40:48 +00:00
|
|
|
return result.R, result.E
|
|
|
|
}
|
|
|
|
|
2015-01-26 22:03:48 +00:00
|
|
|
func (f *FakeEtcdClient) sortResponse(nodes etcd.Nodes) {
|
|
|
|
for i := range nodes {
|
|
|
|
if nodes[i].Dir {
|
|
|
|
f.sortResponse(nodes[i].Nodes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sort.Sort(nodes)
|
|
|
|
}
|
|
|
|
|
2014-07-31 10:25:42 +00:00
|
|
|
func (f *FakeEtcdClient) nodeExists(key string) bool {
|
|
|
|
result, ok := f.Data[key]
|
2014-08-08 20:25:36 +00:00
|
|
|
return ok && result.R != nil && result.R.Node != nil && result.E == nil
|
2014-07-31 10:25:42 +00:00
|
|
|
}
|
|
|
|
|
2014-08-05 18:43:19 +00:00
|
|
|
func (f *FakeEtcdClient) setLocked(key, value string, ttl uint64) (*etcd.Response, error) {
|
2014-09-24 00:47:05 +00:00
|
|
|
f.LastSetTTL = ttl
|
2014-07-31 07:15:03 +00:00
|
|
|
if f.Err != nil {
|
|
|
|
return nil, f.Err
|
|
|
|
}
|
|
|
|
|
2014-07-30 10:31:35 +00:00
|
|
|
i := f.generateIndex()
|
|
|
|
|
2014-07-31 10:25:42 +00:00
|
|
|
if f.nodeExists(key) {
|
|
|
|
prevResult := f.Data[key]
|
2014-07-30 10:31:35 +00:00
|
|
|
createdIndex := prevResult.R.Node.CreatedIndex
|
2015-02-11 23:35:05 +00:00
|
|
|
f.t.Logf("updating %v, index %v -> %v (ttl: %d)", key, createdIndex, i, ttl)
|
2014-07-30 10:31:35 +00:00
|
|
|
result := EtcdResponseWithError{
|
|
|
|
R: &etcd.Response{
|
|
|
|
Node: &etcd.Node{
|
|
|
|
Value: value,
|
|
|
|
CreatedIndex: createdIndex,
|
|
|
|
ModifiedIndex: i,
|
2015-02-05 08:05:36 +00:00
|
|
|
TTL: int64(ttl),
|
2014-07-30 10:31:35 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
f.Data[key] = result
|
2014-07-31 07:15:03 +00:00
|
|
|
return result.R, nil
|
2014-07-30 10:31:35 +00:00
|
|
|
}
|
|
|
|
|
2015-02-11 23:35:05 +00:00
|
|
|
f.t.Logf("creating %v, index %v (ttl: %d)", key, i, ttl)
|
2014-06-06 23:40:48 +00:00
|
|
|
result := EtcdResponseWithError{
|
|
|
|
R: &etcd.Response{
|
|
|
|
Node: &etcd.Node{
|
2014-07-30 10:31:35 +00:00
|
|
|
Value: value,
|
|
|
|
CreatedIndex: i,
|
|
|
|
ModifiedIndex: i,
|
2014-10-09 20:56:30 +00:00
|
|
|
TTL: int64(ttl),
|
2014-06-06 23:40:48 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
f.Data[key] = result
|
2014-07-31 07:15:03 +00:00
|
|
|
return result.R, nil
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
2014-06-27 03:24:10 +00:00
|
|
|
|
2014-08-05 18:43:19 +00:00
|
|
|
func (f *FakeEtcdClient) Set(key, value string, ttl uint64) (*etcd.Response, error) {
|
|
|
|
f.Mutex.Lock()
|
|
|
|
defer f.Mutex.Unlock()
|
2014-08-04 21:42:51 +00:00
|
|
|
defer f.updateResponse(key)
|
2014-08-05 18:43:19 +00:00
|
|
|
|
|
|
|
return f.setLocked(key, value, ttl)
|
|
|
|
}
|
|
|
|
|
2014-06-27 03:24:10 +00:00
|
|
|
func (f *FakeEtcdClient) CompareAndSwap(key, value string, ttl uint64, prevValue string, prevIndex uint64) (*etcd.Response, error) {
|
2014-07-31 10:25:42 +00:00
|
|
|
if f.Err != nil {
|
2014-08-08 20:25:36 +00:00
|
|
|
f.t.Logf("c&s: returning err %v", f.Err)
|
2014-07-31 10:25:42 +00:00
|
|
|
return nil, f.Err
|
|
|
|
}
|
2015-03-06 05:53:27 +00:00
|
|
|
if f.CasErr != nil {
|
|
|
|
f.t.Logf("c&s: returning err %v", f.CasErr)
|
|
|
|
return nil, f.CasErr
|
|
|
|
}
|
2014-07-31 10:25:42 +00:00
|
|
|
|
|
|
|
if !f.TestIndex {
|
|
|
|
f.t.Errorf("Enable TestIndex for test involving CompareAndSwap")
|
|
|
|
return nil, errors.New("Enable TestIndex for test involving CompareAndSwap")
|
|
|
|
}
|
|
|
|
|
|
|
|
if prevValue == "" && prevIndex == 0 {
|
|
|
|
return nil, errors.New("Either prevValue or prevIndex must be specified.")
|
|
|
|
}
|
|
|
|
|
2014-08-05 18:43:19 +00:00
|
|
|
f.Mutex.Lock()
|
|
|
|
defer f.Mutex.Unlock()
|
2014-08-04 21:42:51 +00:00
|
|
|
defer f.updateResponse(key)
|
2014-08-05 18:43:19 +00:00
|
|
|
|
2014-07-31 10:25:42 +00:00
|
|
|
if !f.nodeExists(key) {
|
2014-08-08 20:25:36 +00:00
|
|
|
f.t.Logf("c&s: node doesn't exist")
|
2014-07-31 10:25:42 +00:00
|
|
|
return nil, EtcdErrorNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
prevNode := f.Data[key].R.Node
|
|
|
|
|
|
|
|
if prevValue != "" && prevValue != prevNode.Value {
|
2014-08-08 20:25:36 +00:00
|
|
|
f.t.Logf("body didn't match")
|
2014-07-31 10:25:42 +00:00
|
|
|
return nil, EtcdErrorTestFailed
|
|
|
|
}
|
|
|
|
|
|
|
|
if prevIndex != 0 && prevIndex != prevNode.ModifiedIndex {
|
2014-08-08 20:25:36 +00:00
|
|
|
f.t.Logf("got index %v but needed %v", prevIndex, prevNode.ModifiedIndex)
|
2014-07-31 10:25:42 +00:00
|
|
|
return nil, EtcdErrorTestFailed
|
|
|
|
}
|
|
|
|
|
2014-08-05 18:43:19 +00:00
|
|
|
return f.setLocked(key, value, ttl)
|
2014-06-27 03:24:10 +00:00
|
|
|
}
|
|
|
|
|
2014-06-06 23:40:48 +00:00
|
|
|
func (f *FakeEtcdClient) Create(key, value string, ttl uint64) (*etcd.Response, error) {
|
2014-08-05 18:43:19 +00:00
|
|
|
f.Mutex.Lock()
|
|
|
|
defer f.Mutex.Unlock()
|
2014-08-04 21:42:51 +00:00
|
|
|
defer f.updateResponse(key)
|
2014-08-05 18:43:19 +00:00
|
|
|
|
2014-07-31 10:25:42 +00:00
|
|
|
if f.nodeExists(key) {
|
2014-07-31 09:23:43 +00:00
|
|
|
return nil, EtcdErrorNodeExist
|
|
|
|
}
|
|
|
|
|
2014-08-05 18:43:19 +00:00
|
|
|
return f.setLocked(key, value, ttl)
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
2014-07-31 09:23:43 +00:00
|
|
|
|
2014-06-06 23:40:48 +00:00
|
|
|
func (f *FakeEtcdClient) Delete(key string, recursive bool) (*etcd.Response, error) {
|
2014-07-31 07:15:03 +00:00
|
|
|
if f.Err != nil {
|
2014-07-31 09:23:43 +00:00
|
|
|
return nil, f.Err
|
2014-07-31 07:15:03 +00:00
|
|
|
}
|
|
|
|
|
2014-11-01 04:35:38 +00:00
|
|
|
f.Mutex.Lock()
|
|
|
|
defer f.Mutex.Unlock()
|
2015-03-05 03:34:16 +00:00
|
|
|
existing, ok := f.Data[key]
|
|
|
|
if !ok {
|
|
|
|
return &etcd.Response{}, &etcd.EtcdError{
|
|
|
|
ErrorCode: EtcdErrorCodeNotFound,
|
|
|
|
Index: f.ChangeIndex,
|
|
|
|
}
|
|
|
|
}
|
2015-07-30 11:27:18 +00:00
|
|
|
etcdError, ok := existing.E.(*etcd.EtcdError)
|
|
|
|
if ok && etcdError != nil && etcdError.ErrorCode == EtcdErrorCodeNotFound {
|
2015-03-05 05:17:29 +00:00
|
|
|
f.DeletedKeys = append(f.DeletedKeys, key)
|
|
|
|
return existing.R, existing.E
|
|
|
|
}
|
2015-02-11 23:35:05 +00:00
|
|
|
index := f.generateIndex()
|
2014-06-27 19:54:45 +00:00
|
|
|
f.Data[key] = EtcdResponseWithError{
|
2015-02-11 23:35:05 +00:00
|
|
|
R: &etcd.Response{},
|
|
|
|
E: &etcd.EtcdError{
|
|
|
|
ErrorCode: EtcdErrorCodeNotFound,
|
|
|
|
Index: index,
|
2014-06-27 19:54:45 +00:00
|
|
|
},
|
2015-02-11 23:35:05 +00:00
|
|
|
}
|
|
|
|
res := &etcd.Response{
|
|
|
|
Action: "delete",
|
|
|
|
Node: nil,
|
|
|
|
PrevNode: nil,
|
|
|
|
EtcdIndex: index,
|
|
|
|
}
|
|
|
|
if existing.R != nil && existing.R.Node != nil {
|
|
|
|
res.PrevNode = existing.R.Node
|
2014-06-27 19:54:45 +00:00
|
|
|
}
|
|
|
|
|
2014-06-17 23:23:52 +00:00
|
|
|
f.DeletedKeys = append(f.DeletedKeys, key)
|
2015-02-11 23:35:05 +00:00
|
|
|
return res, nil
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
|
2014-06-30 23:09:15 +00:00
|
|
|
func (f *FakeEtcdClient) WaitForWatchCompletion() {
|
2014-07-03 00:30:56 +00:00
|
|
|
<-f.watchCompletedChan
|
2014-06-30 21:48:57 +00:00
|
|
|
}
|
|
|
|
|
2014-06-06 23:40:48 +00:00
|
|
|
func (f *FakeEtcdClient) Watch(prefix string, waitIndex uint64, recursive bool, receiver chan *etcd.Response, stop chan bool) (*etcd.Response, error) {
|
2015-01-07 15:36:59 +00:00
|
|
|
f.Mutex.Lock()
|
2014-09-20 00:53:55 +00:00
|
|
|
if f.WatchImmediateError != nil {
|
|
|
|
return nil, f.WatchImmediateError
|
|
|
|
}
|
2014-06-18 20:10:19 +00:00
|
|
|
f.WatchResponse = receiver
|
|
|
|
f.WatchStop = stop
|
2014-08-11 04:08:06 +00:00
|
|
|
f.WatchIndex = waitIndex
|
2014-06-18 20:10:19 +00:00
|
|
|
injectedError := make(chan error)
|
2014-06-30 21:48:57 +00:00
|
|
|
|
2014-06-18 20:10:19 +00:00
|
|
|
defer close(injectedError)
|
|
|
|
f.WatchInjectError = injectedError
|
2014-06-30 23:07:46 +00:00
|
|
|
|
2015-01-07 15:36:59 +00:00
|
|
|
f.Mutex.Unlock()
|
2014-07-15 14:52:39 +00:00
|
|
|
if receiver == nil {
|
|
|
|
return f.Get(prefix, false, recursive)
|
2014-07-20 19:00:52 +00:00
|
|
|
} else {
|
|
|
|
// Emulate etcd's behavior. (I think.)
|
|
|
|
defer close(receiver)
|
2014-07-15 14:52:39 +00:00
|
|
|
}
|
|
|
|
|
2014-07-03 00:30:56 +00:00
|
|
|
f.watchCompletedChan <- true
|
2014-06-18 20:10:19 +00:00
|
|
|
select {
|
|
|
|
case <-stop:
|
|
|
|
return nil, etcd.ErrWatchStoppedByUser
|
|
|
|
case err := <-injectedError:
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|