query.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*
  2. Copyright 2017 Google Inc.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package client
  14. import (
  15. "crypto/tls"
  16. "errors"
  17. "fmt"
  18. "regexp"
  19. "time"
  20. "github.com/golang/protobuf/proto"
  21. )
  22. // NotificationHandler is a type for the client specific handler function.
  23. //
  24. // Client implementations will pass all kinds of received notifications as they
  25. // arrive.
  26. type NotificationHandler func(Notification) error
  27. // ProtoHandler is a type for the raw handling of the RPC layer. Most users
  28. // should use NotificationHandler instead.
  29. type ProtoHandler func(proto.Message) error
  30. // Type defines the type of query in a Query.
  31. type Type int
  32. // NewType returns a new QueryType based on the provided string.
  33. func NewType(s string) Type {
  34. v, ok := typeConst[s]
  35. if !ok {
  36. return Unknown
  37. }
  38. return v
  39. }
  40. // String returns the string representation of the QueryType.
  41. func (q Type) String() string {
  42. return typeString[q]
  43. }
  44. const (
  45. // Unknown is an unknown query and should always be treated as an error.
  46. Unknown Type = iota
  47. // Once will perform a Once query against the agent.
  48. Once
  49. // Poll will perform a Polling query against the agent.
  50. Poll
  51. // Stream will perform a Streaming query against the agent.
  52. Stream
  53. )
  54. var (
  55. typeString = map[Type]string{
  56. Unknown: "unknown",
  57. Once: "once",
  58. Poll: "poll",
  59. Stream: "stream",
  60. }
  61. typeConst = map[string]Type{
  62. "unknown": Unknown,
  63. "once": Once,
  64. "poll": Poll,
  65. "stream": Stream,
  66. }
  67. // Pre-compiled regex to match ASCII characters between [\x20-\x7E]
  68. // i.e., printable ASCII characters and space
  69. // https://github.com/grpc/blob/master/doc/PROTOCOL-HTTP2.md
  70. printableASCII = regexp.MustCompile(`^[\x20-\x7E]*$`).MatchString
  71. )
  72. // Destination contains data used to connect to a server.
  73. type Destination struct {
  74. // Addrs is a slice of addresses by which a target may be reached. Most
  75. // clients will only handle the first element.
  76. Addrs []string
  77. // Target is the target of the query. Maybe empty if the query is performed
  78. // against an end target vs. a collector.
  79. Target string
  80. // Replica is the specific backend to contact. This field is implementation
  81. // specific and for direct agent communication should not be set. default is
  82. // first available.
  83. Replica int
  84. // Timeout is the connection timeout for the query. It will *not* prevent a
  85. // slow (or streaming) query from completing, this only affects the initial
  86. // connection and broken connection detection.
  87. //
  88. // If Timeout is not set, default is 1 minute.
  89. Timeout time.Duration
  90. // Credentials are used for authentication with the target. Optional.
  91. Credentials *Credentials
  92. // TLS config to use when connecting to target. Optional.
  93. TLS *tls.Config
  94. // Extra contains arbitrary additional metadata to be passed to the
  95. // target. Optional.
  96. Extra map[string]string
  97. }
  98. // Validate validates the fields of Destination.
  99. func (d Destination) Validate() error {
  100. if len(d.Addrs) == 0 {
  101. return errors.New("Destination.Addrs is empty")
  102. }
  103. if d.Credentials != nil {
  104. return d.Credentials.validate()
  105. }
  106. return nil
  107. }
  108. // Query contains all of the parameters necessary to initiate the query.
  109. type Query struct {
  110. // Addrs is a slice of addresses by which a target may be reached. Most
  111. // clients will only handle the first element.
  112. Addrs []string
  113. // Target is the target of the query. Maybe empty if the query is performed
  114. // against an end target vs. a collector.
  115. Target string
  116. // Replica is the specific backend to contact. This field is implementation
  117. // specific and for direct agent communication should not be set. default is
  118. // first available.
  119. Replica int
  120. // UpdatesOnly will only stream incremental updates rather than providing the
  121. // client with an initial snapshot. This again is implementation specific
  122. // if the agent doesn't not accept that query it is up the client library to
  123. // decide wheter to return an error or to make a normal subscription then
  124. // ignore the initial sync and only provide increment updates.
  125. UpdatesOnly bool
  126. // Queries contains the list of Paths to query.
  127. Queries []Path
  128. // Type of query to perform.
  129. Type Type
  130. // Timeout is the connection timeout for the query. It will *not* prevent a
  131. // slow (or streaming) query from completing, this only affects the initial
  132. // connection and broken connection detection.
  133. //
  134. // If Timeout is not set, default is 1 minute.
  135. Timeout time.Duration
  136. // NotificationHandler is the per notification callback handed to a vendor
  137. // specific implementation. For every notificaiton this call back will be
  138. // called.
  139. NotificationHandler NotificationHandler
  140. // ProtoHandler, if set, will receive all response protos sent by the
  141. // backend. Only one of NotificationHandler or ProtoHandler may be
  142. // set.
  143. ProtoHandler ProtoHandler
  144. // Credentials are used for authentication with the target. Optional.
  145. Credentials *Credentials
  146. // TLS config to use when connecting to target. Optional.
  147. TLS *tls.Config
  148. // Extra contains arbitrary additional metadata to be passed to the
  149. // target. Optional.
  150. Extra map[string]string
  151. }
  152. // Destination extracts a Destination instance out of Query fields.
  153. //
  154. // Ideally we would embed Destination in Query. But in order to not break the
  155. // existing API we have this workaround.
  156. func (q Query) Destination() Destination {
  157. return Destination{
  158. Addrs: q.Addrs,
  159. Target: q.Target,
  160. Replica: q.Replica,
  161. Timeout: q.Timeout,
  162. Credentials: q.Credentials,
  163. TLS: q.TLS,
  164. Extra: q.Extra,
  165. }
  166. }
  167. // Credentials contains information necessary to authenticate with the target.
  168. // Currently only username/password are supported, but may contain TLS client
  169. // certificate in the future.
  170. type Credentials struct {
  171. Username string
  172. Password string
  173. }
  174. // Validates the credentials against printable ASCII characters
  175. func (c Credentials) validate() error {
  176. if !printableASCII(c.Username) {
  177. return errors.New("Credentials.Username contains non printable ASCII characters")
  178. }
  179. if !printableASCII(c.Password) {
  180. return errors.New("Credentials.Password contains non printable ASCII characters")
  181. }
  182. return nil
  183. }
  184. // Validate validates that query contains valid values that any client should
  185. // be able use to form a valid backend request.
  186. func (q Query) Validate() error {
  187. if err := q.Destination().Validate(); err != nil {
  188. return err
  189. }
  190. switch {
  191. case q.Type == Unknown:
  192. return errors.New("Query type cannot be Unknown")
  193. case len(q.Queries) == 0:
  194. return errors.New("Query.Queries not set")
  195. case q.NotificationHandler != nil && q.ProtoHandler != nil:
  196. return errors.New("only one of Notification or ProtoHandler must be set")
  197. case q.NotificationHandler == nil && q.ProtoHandler == nil:
  198. return errors.New("one of Notification or ProtoHandler must be set")
  199. }
  200. return nil
  201. }
  202. // SetRequest contains slices of changes to apply.
  203. //
  204. // The difference between Update and Replace is that on non-primitive values
  205. // (lists/maps), Update acts as "append"; it only overwrites an existing value
  206. // in the container when the key matches.
  207. // Replace overwrites the entire container regardless of current contents.
  208. type SetRequest struct {
  209. Destination
  210. Delete []Path
  211. Update []Leaf
  212. Replace []Leaf
  213. }
  214. // Validate validates that SetRequest contains valid values that any client
  215. // should be able use to form a valid backend request.
  216. func (r SetRequest) Validate() error {
  217. if err := r.Destination.Validate(); err != nil {
  218. return fmt.Errorf("SetRequest.Destination validation failed: %v", err)
  219. }
  220. if len(r.Delete) == 0 && len(r.Update) == 0 && len(r.Replace) == 0 {
  221. return errors.New("at least one of Delete/Update/Replace must be set")
  222. }
  223. return nil
  224. }
  225. // SetResponse contains the timestamp of an applied SetRequest.
  226. type SetResponse struct {
  227. TS time.Time
  228. }