1package dependency 2 3import ( 4 "net/url" 5 "regexp" 6 "sort" 7 "strconv" 8 "time" 9 10 consulapi "github.com/hashicorp/consul/api" 11) 12 13const ( 14 dcRe = `(@(?P<dc>[[:word:]\.\-\_]+))?` 15 keyRe = `/?(?P<key>[^@]+)` 16 filterRe = `(\|(?P<filter>[[:word:]\,]+))?` 17 serviceNameRe = `(?P<name>[[:word:]\-\_]+)` 18 nodeNameRe = `(?P<name>[[:word:]\.\-\_]+)` 19 nearRe = `(~(?P<near>[[:word:]\.\-\_]+))?` 20 prefixRe = `/?(?P<prefix>[^@]+)` 21 tagRe = `((?P<tag>[[:word:]=:\.\-\_]+)\.)?` 22) 23 24type Type int 25 26const ( 27 TypeConsul Type = iota 28 TypeVault 29 TypeLocal 30) 31 32// Dependency is an interface for a dependency that Consul Template is capable 33// of watching. 34type Dependency interface { 35 Fetch(*ClientSet, *QueryOptions) (interface{}, *ResponseMetadata, error) 36 CanShare() bool 37 String() string 38 Stop() 39 Type() Type 40} 41 42// ServiceTags is a slice of tags assigned to a Service 43type ServiceTags []string 44 45// QueryOptions is a list of options to send with the query. These options are 46// client-agnostic, and the dependency determines which, if any, of the options 47// to use. 48type QueryOptions struct { 49 AllowStale bool 50 Datacenter string 51 Near string 52 RequireConsistent bool 53 VaultGrace time.Duration 54 WaitIndex uint64 55 WaitTime time.Duration 56} 57 58func (q *QueryOptions) Merge(o *QueryOptions) *QueryOptions { 59 var r QueryOptions 60 61 if q == nil { 62 if o == nil { 63 return &QueryOptions{} 64 } 65 r = *o 66 return &r 67 } 68 69 r = *q 70 71 if o == nil { 72 return &r 73 } 74 75 if o.AllowStale != false { 76 r.AllowStale = o.AllowStale 77 } 78 79 if o.Datacenter != "" { 80 r.Datacenter = o.Datacenter 81 } 82 83 if o.Near != "" { 84 r.Near = o.Near 85 } 86 87 if o.RequireConsistent != false { 88 r.RequireConsistent = o.RequireConsistent 89 } 90 91 if o.WaitIndex != 0 { 92 r.WaitIndex = o.WaitIndex 93 } 94 95 if o.WaitTime != 0 { 96 r.WaitTime = o.WaitTime 97 } 98 99 return &r 100} 101 102func (q *QueryOptions) ToConsulOpts() *consulapi.QueryOptions { 103 return &consulapi.QueryOptions{ 104 AllowStale: q.AllowStale, 105 Datacenter: q.Datacenter, 106 Near: q.Near, 107 RequireConsistent: q.RequireConsistent, 108 WaitIndex: q.WaitIndex, 109 WaitTime: q.WaitTime, 110 } 111} 112 113func (q *QueryOptions) String() string { 114 u := &url.Values{} 115 116 if q.AllowStale { 117 u.Add("stale", strconv.FormatBool(q.AllowStale)) 118 } 119 120 if q.Datacenter != "" { 121 u.Add("dc", q.Datacenter) 122 } 123 124 if q.Near != "" { 125 u.Add("near", q.Near) 126 } 127 128 if q.RequireConsistent { 129 u.Add("consistent", strconv.FormatBool(q.RequireConsistent)) 130 } 131 132 if q.WaitIndex != 0 { 133 u.Add("index", strconv.FormatUint(q.WaitIndex, 10)) 134 } 135 136 if q.WaitTime != 0 { 137 u.Add("wait", q.WaitTime.String()) 138 } 139 140 return u.Encode() 141} 142 143// ResponseMetadata is a struct that contains metadata about the response. This 144// is returned from a Fetch function call. 145type ResponseMetadata struct { 146 LastIndex uint64 147 LastContact time.Duration 148 Block bool 149} 150 151// deepCopyAndSortTags deep copies the tags in the given string slice and then 152// sorts and returns the copied result. 153func deepCopyAndSortTags(tags []string) []string { 154 newTags := make([]string, 0, len(tags)) 155 for _, tag := range tags { 156 newTags = append(newTags, tag) 157 } 158 sort.Strings(newTags) 159 return newTags 160} 161 162// respWithMetadata is a short wrapper to return the given interface with fake 163// response metadata for non-Consul dependencies. 164func respWithMetadata(i interface{}) (interface{}, *ResponseMetadata, error) { 165 return i, &ResponseMetadata{ 166 LastContact: 0, 167 LastIndex: uint64(time.Now().Unix()), 168 }, nil 169} 170 171// regexpMatch matches the given regexp and extracts the match groups into a 172// named map. 173func regexpMatch(re *regexp.Regexp, q string) map[string]string { 174 names := re.SubexpNames() 175 match := re.FindAllStringSubmatch(q, -1) 176 177 if len(match) == 0 { 178 return map[string]string{} 179 } 180 181 m := map[string]string{} 182 for i, n := range match[0] { 183 if names[i] != "" { 184 m[names[i]] = n 185 } 186 } 187 188 return m 189} 190