2016-05-13 06:49:40 +03:00
|
|
|
// Copyright 2016 The etcd Authors
|
2016-04-20 10:31:58 +03: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.
|
|
|
|
|
|
|
|
package etcdserver
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"path"
|
|
|
|
"time"
|
|
|
|
|
2016-07-26 21:33:11 +03:00
|
|
|
"github.com/coreos/etcd/etcdserver/api"
|
2016-04-20 10:31:58 +03:00
|
|
|
"github.com/coreos/etcd/etcdserver/membership"
|
|
|
|
"github.com/coreos/etcd/pkg/pbutil"
|
|
|
|
"github.com/coreos/etcd/store"
|
|
|
|
"github.com/coreos/go-semver/semver"
|
|
|
|
)
|
|
|
|
|
2016-05-17 00:40:52 +03:00
|
|
|
// ApplierV2 is the interface for processing V2 raft messages
|
|
|
|
type ApplierV2 interface {
|
2017-08-07 13:55:18 +03:00
|
|
|
Delete(r *RequestV2) Response
|
|
|
|
Post(r *RequestV2) Response
|
|
|
|
Put(r *RequestV2) Response
|
|
|
|
QGet(r *RequestV2) Response
|
|
|
|
Sync(r *RequestV2) Response
|
2016-04-20 10:31:58 +03:00
|
|
|
}
|
|
|
|
|
2016-05-17 00:40:52 +03:00
|
|
|
func NewApplierV2(s store.Store, c *membership.RaftCluster) ApplierV2 {
|
|
|
|
return &applierV2store{store: s, cluster: c}
|
|
|
|
}
|
|
|
|
|
|
|
|
type applierV2store struct {
|
|
|
|
store store.Store
|
|
|
|
cluster *membership.RaftCluster
|
|
|
|
}
|
2016-04-20 10:31:58 +03:00
|
|
|
|
2017-08-07 13:55:18 +03:00
|
|
|
func (a *applierV2store) Delete(r *RequestV2) Response {
|
2016-04-20 10:31:58 +03:00
|
|
|
switch {
|
|
|
|
case r.PrevIndex > 0 || r.PrevValue != "":
|
2016-05-17 00:40:52 +03:00
|
|
|
return toResponse(a.store.CompareAndDelete(r.Path, r.PrevValue, r.PrevIndex))
|
2016-04-20 10:31:58 +03:00
|
|
|
default:
|
2016-05-17 00:40:52 +03:00
|
|
|
return toResponse(a.store.Delete(r.Path, r.Dir, r.Recursive))
|
2016-04-20 10:31:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-07 13:55:18 +03:00
|
|
|
func (a *applierV2store) Post(r *RequestV2) Response {
|
|
|
|
return toResponse(a.store.Create(r.Path, r.Dir, r.Val, true, r.TTLOptions()))
|
2016-04-20 10:31:58 +03:00
|
|
|
}
|
|
|
|
|
2017-08-07 13:55:18 +03:00
|
|
|
func (a *applierV2store) Put(r *RequestV2) Response {
|
|
|
|
ttlOptions := r.TTLOptions()
|
2016-04-20 10:31:58 +03:00
|
|
|
exists, existsSet := pbutil.GetBool(r.PrevExist)
|
|
|
|
switch {
|
|
|
|
case existsSet:
|
|
|
|
if exists {
|
|
|
|
if r.PrevIndex == 0 && r.PrevValue == "" {
|
2016-05-17 00:40:52 +03:00
|
|
|
return toResponse(a.store.Update(r.Path, r.Val, ttlOptions))
|
2016-04-20 10:31:58 +03:00
|
|
|
}
|
2016-06-22 22:01:52 +03:00
|
|
|
return toResponse(a.store.CompareAndSwap(r.Path, r.PrevValue, r.PrevIndex, r.Val, ttlOptions))
|
2016-04-20 10:31:58 +03:00
|
|
|
}
|
2016-05-17 00:40:52 +03:00
|
|
|
return toResponse(a.store.Create(r.Path, r.Dir, r.Val, false, ttlOptions))
|
2016-04-20 10:31:58 +03:00
|
|
|
case r.PrevIndex > 0 || r.PrevValue != "":
|
2016-05-17 00:40:52 +03:00
|
|
|
return toResponse(a.store.CompareAndSwap(r.Path, r.PrevValue, r.PrevIndex, r.Val, ttlOptions))
|
2016-04-20 10:31:58 +03:00
|
|
|
default:
|
|
|
|
if storeMemberAttributeRegexp.MatchString(r.Path) {
|
|
|
|
id := membership.MustParseMemberIDFromKey(path.Dir(r.Path))
|
|
|
|
var attr membership.Attributes
|
|
|
|
if err := json.Unmarshal([]byte(r.Val), &attr); err != nil {
|
|
|
|
plog.Panicf("unmarshal %s should never fail: %v", r.Val, err)
|
|
|
|
}
|
2016-05-17 00:40:52 +03:00
|
|
|
if a.cluster != nil {
|
|
|
|
a.cluster.UpdateAttributes(id, attr)
|
|
|
|
}
|
2016-04-20 10:31:58 +03:00
|
|
|
// return an empty response since there is no consumer.
|
|
|
|
return Response{}
|
|
|
|
}
|
|
|
|
if r.Path == membership.StoreClusterVersionKey() {
|
2016-05-17 00:40:52 +03:00
|
|
|
if a.cluster != nil {
|
2016-07-26 21:33:11 +03:00
|
|
|
a.cluster.SetVersion(semver.Must(semver.NewVersion(r.Val)), api.UpdateCapability)
|
2016-05-17 00:40:52 +03:00
|
|
|
}
|
2016-04-20 10:31:58 +03:00
|
|
|
// return an empty response since there is no consumer.
|
|
|
|
return Response{}
|
|
|
|
}
|
2016-05-17 00:40:52 +03:00
|
|
|
return toResponse(a.store.Set(r.Path, r.Dir, r.Val, ttlOptions))
|
2016-04-20 10:31:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-07 13:55:18 +03:00
|
|
|
func (a *applierV2store) QGet(r *RequestV2) Response {
|
2016-05-17 00:40:52 +03:00
|
|
|
return toResponse(a.store.Get(r.Path, r.Recursive, r.Sorted))
|
2016-04-20 10:31:58 +03:00
|
|
|
}
|
|
|
|
|
2017-08-07 13:55:18 +03:00
|
|
|
func (a *applierV2store) Sync(r *RequestV2) Response {
|
2016-05-17 00:40:52 +03:00
|
|
|
a.store.DeleteExpiredKeys(time.Unix(0, r.Time))
|
2016-04-20 10:31:58 +03:00
|
|
|
return Response{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// applyV2Request interprets r as a call to store.X and returns a Response interpreted
|
|
|
|
// from store.Event
|
2017-08-07 13:55:18 +03:00
|
|
|
func (s *EtcdServer) applyV2Request(r *RequestV2) Response {
|
2016-04-20 10:31:58 +03:00
|
|
|
switch r.Method {
|
|
|
|
case "POST":
|
|
|
|
return s.applyV2.Post(r)
|
|
|
|
case "PUT":
|
|
|
|
return s.applyV2.Put(r)
|
|
|
|
case "DELETE":
|
|
|
|
return s.applyV2.Delete(r)
|
|
|
|
case "QGET":
|
|
|
|
return s.applyV2.QGet(r)
|
|
|
|
case "SYNC":
|
|
|
|
return s.applyV2.Sync(r)
|
|
|
|
default:
|
|
|
|
// This should never be reached, but just in case:
|
2017-08-07 13:55:18 +03:00
|
|
|
return Response{Err: ErrUnknownMethod}
|
2016-04-20 10:31:58 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-07 13:55:18 +03:00
|
|
|
func (r *RequestV2) TTLOptions() store.TTLOptionSet {
|
2016-04-20 10:31:58 +03:00
|
|
|
refresh, _ := pbutil.GetBool(r.Refresh)
|
|
|
|
ttlOptions := store.TTLOptionSet{Refresh: refresh}
|
|
|
|
if r.Expiration != 0 {
|
|
|
|
ttlOptions.ExpireTime = time.Unix(0, r.Expiration)
|
|
|
|
}
|
|
|
|
return ttlOptions
|
|
|
|
}
|
|
|
|
|
|
|
|
func toResponse(ev *store.Event, err error) Response {
|
2017-08-07 13:55:18 +03:00
|
|
|
return Response{Event: ev, Err: err}
|
2016-04-20 10:31:58 +03:00
|
|
|
}
|