blob: c77df1970617dfa23aab4a6cbd00620019fe46b9 [file] [log] [blame]
khenaidooab1f7bd2019-11-14 14:00:27 -05001// Copyright 2016 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package etcdserver
16
17import (
18 "encoding/json"
19 "path"
20 "time"
21
22 "go.etcd.io/etcd/etcdserver/api"
23 "go.etcd.io/etcd/etcdserver/api/membership"
24 "go.etcd.io/etcd/etcdserver/api/v2store"
25 "go.etcd.io/etcd/pkg/pbutil"
26
27 "github.com/coreos/go-semver/semver"
28 "go.uber.org/zap"
29)
30
31// ApplierV2 is the interface for processing V2 raft messages
32type ApplierV2 interface {
33 Delete(r *RequestV2) Response
34 Post(r *RequestV2) Response
35 Put(r *RequestV2) Response
36 QGet(r *RequestV2) Response
37 Sync(r *RequestV2) Response
38}
39
40func NewApplierV2(lg *zap.Logger, s v2store.Store, c *membership.RaftCluster) ApplierV2 {
41 return &applierV2store{lg: lg, store: s, cluster: c}
42}
43
44type applierV2store struct {
45 lg *zap.Logger
46 store v2store.Store
47 cluster *membership.RaftCluster
48}
49
50func (a *applierV2store) Delete(r *RequestV2) Response {
51 switch {
52 case r.PrevIndex > 0 || r.PrevValue != "":
53 return toResponse(a.store.CompareAndDelete(r.Path, r.PrevValue, r.PrevIndex))
54 default:
55 return toResponse(a.store.Delete(r.Path, r.Dir, r.Recursive))
56 }
57}
58
59func (a *applierV2store) Post(r *RequestV2) Response {
60 return toResponse(a.store.Create(r.Path, r.Dir, r.Val, true, r.TTLOptions()))
61}
62
63func (a *applierV2store) Put(r *RequestV2) Response {
64 ttlOptions := r.TTLOptions()
65 exists, existsSet := pbutil.GetBool(r.PrevExist)
66 switch {
67 case existsSet:
68 if exists {
69 if r.PrevIndex == 0 && r.PrevValue == "" {
70 return toResponse(a.store.Update(r.Path, r.Val, ttlOptions))
71 }
72 return toResponse(a.store.CompareAndSwap(r.Path, r.PrevValue, r.PrevIndex, r.Val, ttlOptions))
73 }
74 return toResponse(a.store.Create(r.Path, r.Dir, r.Val, false, ttlOptions))
75 case r.PrevIndex > 0 || r.PrevValue != "":
76 return toResponse(a.store.CompareAndSwap(r.Path, r.PrevValue, r.PrevIndex, r.Val, ttlOptions))
77 default:
78 if storeMemberAttributeRegexp.MatchString(r.Path) {
79 id := membership.MustParseMemberIDFromKey(path.Dir(r.Path))
80 var attr membership.Attributes
81 if err := json.Unmarshal([]byte(r.Val), &attr); err != nil {
82 if a.lg != nil {
83 a.lg.Panic("failed to unmarshal", zap.String("value", r.Val), zap.Error(err))
84 } else {
85 plog.Panicf("unmarshal %s should never fail: %v", r.Val, err)
86 }
87 }
88 if a.cluster != nil {
89 a.cluster.UpdateAttributes(id, attr)
90 }
91 // return an empty response since there is no consumer.
92 return Response{}
93 }
94 if r.Path == membership.StoreClusterVersionKey() {
95 if a.cluster != nil {
96 a.cluster.SetVersion(semver.Must(semver.NewVersion(r.Val)), api.UpdateCapability)
97 }
98 // return an empty response since there is no consumer.
99 return Response{}
100 }
101 return toResponse(a.store.Set(r.Path, r.Dir, r.Val, ttlOptions))
102 }
103}
104
105func (a *applierV2store) QGet(r *RequestV2) Response {
106 return toResponse(a.store.Get(r.Path, r.Recursive, r.Sorted))
107}
108
109func (a *applierV2store) Sync(r *RequestV2) Response {
110 a.store.DeleteExpiredKeys(time.Unix(0, r.Time))
111 return Response{}
112}
113
114// applyV2Request interprets r as a call to v2store.X
115// and returns a Response interpreted from v2store.Event
116func (s *EtcdServer) applyV2Request(r *RequestV2) Response {
117 defer warnOfExpensiveRequest(s.getLogger(), time.Now(), r, nil, nil)
118
119 switch r.Method {
120 case "POST":
121 return s.applyV2.Post(r)
122 case "PUT":
123 return s.applyV2.Put(r)
124 case "DELETE":
125 return s.applyV2.Delete(r)
126 case "QGET":
127 return s.applyV2.QGet(r)
128 case "SYNC":
129 return s.applyV2.Sync(r)
130 default:
131 // This should never be reached, but just in case:
132 return Response{Err: ErrUnknownMethod}
133 }
134}
135
136func (r *RequestV2) TTLOptions() v2store.TTLOptionSet {
137 refresh, _ := pbutil.GetBool(r.Refresh)
138 ttlOptions := v2store.TTLOptionSet{Refresh: refresh}
139 if r.Expiration != 0 {
140 ttlOptions.ExpireTime = time.Unix(0, r.Expiration)
141 }
142 return ttlOptions
143}
144
145func toResponse(ev *v2store.Event, err error) Response {
146 return Response{Event: ev, Err: err}
147}