@@ -162,118 +162,128 @@ func (c *Client) Save() error {
162162 namespace = "/" + namespace
163163 }
164164
165- return c .save (c .config , namespace )
165+ return c .saveField (c .config , namespace )
166166}
167167
168- func (c * Client ) save (config reflect.Value , prefix string ) error {
169- if config .Kind () == reflect .Ptr {
170- config = config .Elem ()
171- } else if config .Kind () != reflect .Struct {
172- return ErrInvalidConfig
168+ // SaveField saves a specific field from the configuration structure.
169+ // Works in the same way of Save, but it can be used to save specific parts of the configuration,
170+ // avoiding excessive requests to etcd cluster
171+ func (c * Client ) SaveField (field interface {}) error {
172+ path , _ , err := c .getInfo (field )
173+ if err != nil {
174+ return err
173175 }
174176
175- for i := 0 ; i < config .NumField (); i ++ {
176- field := config .Field (i )
177- fieldType := config .Type ().Field (i )
177+ return c .saveField (reflect .ValueOf (field ), path )
178+ }
178179
179- path := normalizeTag (fieldType .Tag .Get ("etcd" ))
180- if len (path ) == 0 {
181- continue
182- }
183- path = prefix + "/" + path
180+ func (c * Client ) saveField (field reflect.Value , prefix string ) error {
181+ if field .Kind () == reflect .Ptr {
182+ field = field .Elem ()
183+ }
184184
185- switch field .Kind () {
186- case reflect .Struct :
187- if err := c .save (field , path ); err != nil {
188- return err
185+ switch field .Kind () {
186+ case reflect .Struct :
187+ for i := 0 ; i < field .NumField (); i ++ {
188+ subfield := field .Field (i )
189+ subfieldType := field .Type ().Field (i )
190+
191+ path := normalizeTag (subfieldType .Tag .Get ("etcd" ))
192+ if len (path ) == 0 {
193+ continue
189194 }
195+ path = prefix + "/" + path
190196
191- case reflect .Map :
192- if _ , err := c .etcdClient .CreateDir (path , 0 ); err != nil && ! alreadyExistsError (err ) {
197+ if err := c .saveField (subfield , path ); err != nil {
193198 return err
194199 }
200+ }
195201
196- for _ , key := range field .MapKeys () {
197- value := field .MapIndex (key )
198- tmpPath := path + "/" + key .String ()
202+ case reflect .Map :
203+ if _ , err := c .etcdClient .CreateDir (prefix , 0 ); err != nil && ! alreadyExistsError (err ) {
204+ return err
205+ }
199206
200- switch value .Kind () {
201- case reflect .Struct :
202- if err := c .save (value , tmpPath ); err != nil {
203- return err
204- }
207+ for _ , key := range field .MapKeys () {
208+ value := field .MapIndex (key )
209+ path := prefix + "/" + key .String ()
205210
206- case reflect . String :
207- if _ , err := c . etcdClient . Set ( tmpPath , value . String (), 0 ); err != nil {
208- return err
209- }
211+ switch value . Kind () {
212+ case reflect . Struct :
213+ if err := c . saveField ( value , path ); err != nil {
214+ return err
210215 }
211- }
212216
213- case reflect .Slice :
214- if _ , err := c .etcdClient .CreateDir (path , 0 ); err != nil && ! alreadyExistsError (err ) {
215- return err
217+ case reflect .String :
218+ if _ , err := c .etcdClient .Set (path , value .String (), 0 ); err != nil {
219+ return err
220+ }
216221 }
222+ }
217223
218- for i := 0 ; i < field .Len (); i ++ {
219- item := field .Index (i )
224+ case reflect .Slice :
225+ if _ , err := c .etcdClient .CreateDir (prefix , 0 ); err != nil && ! alreadyExistsError (err ) {
226+ return err
227+ }
220228
221- if item . Kind () == reflect . Struct {
222- tmpPath := fmt . Sprintf ( "%s/%d" , path , i )
229+ for i := 0 ; i < field . Len (); i ++ {
230+ item := field . Index ( i )
223231
224- if _ , err := c .etcdClient .CreateDir (tmpPath , 0 ); err != nil && ! alreadyExistsError (err ) {
225- return err
226- }
232+ if item .Kind () == reflect .Struct {
233+ path := fmt .Sprintf ("%s/%d" , prefix , i )
227234
228- if err := c .save ( item , tmpPath ); err != nil {
229- return err
230- }
235+ if _ , err := c .etcdClient . CreateDir ( path , 0 ); err != nil && ! alreadyExistsError ( err ) {
236+ return err
237+ }
231238
232- } else {
233- if _ , err := c .etcdClient .CreateInOrder (path , item .String (), 0 ); err != nil {
234- return err
235- }
239+ if err := c .saveField (item , path ); err != nil {
240+ return err
236241 }
237- }
238242
239- case reflect . String :
240- value := field . Interface ().( string )
241- if _ , err := c . etcdClient . Set ( path , value , 0 ); err != nil {
242- return err
243+ } else {
244+ if _ , err := c . etcdClient . CreateInOrder ( prefix , item . String (), 0 ); err != nil {
245+ return err
246+ }
243247 }
248+ }
244249
245- case reflect .Int :
246- value := field .Interface ().(int )
247- if _ , err := c .etcdClient .Set (path , strconv . FormatInt ( int64 ( value ), 10 ) , 0 ); err != nil {
248- return err
249- }
250+ case reflect .String :
251+ value := field .Interface ().(string )
252+ if _ , err := c .etcdClient .Set (prefix , value , 0 ); err != nil {
253+ return err
254+ }
250255
251- case reflect .Int64 :
252- value := field .Interface ().(int64 )
253- if _ , err := c .etcdClient .Set (path , strconv .FormatInt (value , 10 ), 0 ); err != nil {
254- return err
255- }
256+ case reflect .Int :
257+ value := field .Interface ().(int )
258+ if _ , err := c .etcdClient .Set (prefix , strconv .FormatInt (int64 ( value ) , 10 ), 0 ); err != nil {
259+ return err
260+ }
256261
257- case reflect .Bool :
258- value := field .Interface ().(bool )
262+ case reflect .Int64 :
263+ value := field .Interface ().(int64 )
264+ if _ , err := c .etcdClient .Set (prefix , strconv .FormatInt (value , 10 ), 0 ); err != nil {
265+ return err
266+ }
259267
260- var valueStr string
261- if value {
262- valueStr = "true"
263- } else {
264- valueStr = "false"
265- }
268+ case reflect .Bool :
269+ value := field .Interface ().(bool )
266270
267- if _ , err := c .etcdClient .Set (path , valueStr , 0 ); err != nil {
268- return err
269- }
271+ var valueStr string
272+ if value {
273+ valueStr = "true"
274+ } else {
275+ valueStr = "false"
270276 }
271277
272- c . info [ path ] = info {
273- field : field ,
278+ if _ , err := c . etcdClient . Set ( prefix , valueStr , 0 ); err != nil {
279+ return err
274280 }
275281 }
276282
283+ c .info [prefix ] = info {
284+ field : field ,
285+ }
286+
277287 return nil
278288}
279289
@@ -335,32 +345,14 @@ func (c *Client) load(config reflect.Value, prefix string) error {
335345// could have a strange behavior since there are two go routines listening on it (go-etcd and
336346// etcetera watch functions)
337347func (c * Client ) Watch (field interface {}, callback func ()) (chan <- bool , error ) {
348+ path , _ , err := c .getInfo (field )
349+ if err != nil {
350+ return nil , err
351+ }
352+
338353 fieldValue := reflect .ValueOf (field )
339354 if fieldValue .Kind () == reflect .Ptr {
340355 fieldValue = fieldValue .Elem ()
341-
342- } else if ! fieldValue .CanAddr () {
343- return nil , ErrFieldNotAddr
344- }
345-
346- var path string
347- var info info
348-
349- found := false
350- for path , info = range c .info {
351- // Match the pointer, type and name to avoid problems for struct and first field that have the
352- // same memory address
353- if info .field .Addr ().Pointer () == fieldValue .Addr ().Pointer () &&
354- info .field .Type ().Name () == fieldValue .Type ().Name () &&
355- info .field .Kind () == fieldValue .Kind () {
356-
357- found = true
358- break
359- }
360- }
361-
362- if ! found {
363- return nil , ErrFieldNotMapped
364356 }
365357
366358 stop := make (chan bool )
@@ -543,26 +535,41 @@ func (c *Client) fillField(field reflect.Value, node *etcd.Node, prefix string)
543535// It does not query etcd for the latest version. When the field was not retrieved from etcd yet,
544536// the version 0 is returned
545537func (c * Client ) Version (field interface {}) (uint64 , error ) {
538+ _ , info , err := c .getInfo (field )
539+ if err != nil {
540+ return 0 , err
541+ }
542+ return info .version , nil
543+ }
544+
545+ func (c * Client ) getInfo (field interface {}) (path string , info info , err error ) {
546546 fieldValue := reflect .ValueOf (field )
547547 if fieldValue .Kind () == reflect .Ptr {
548548 fieldValue = fieldValue .Elem ()
549549
550550 } else if ! fieldValue .CanAddr () {
551- return 0 , ErrFieldNotAddr
551+ err = ErrFieldNotAddr
552+ return
552553 }
553554
554- for _ , info := range c .info {
555+ found := false
556+ for path , info = range c .info {
555557 // Match the pointer, type and name to avoid problems for struct and first field that have the
556558 // same memory address
557559 if info .field .Addr ().Pointer () == fieldValue .Addr ().Pointer () &&
558560 info .field .Type ().Name () == fieldValue .Type ().Name () &&
559561 info .field .Kind () == fieldValue .Kind () {
560562
561- return info .version , nil
563+ found = true
564+ break
562565 }
563566 }
564567
565- return 0 , ErrFieldNotMapped
568+ if ! found {
569+ err = ErrFieldNotMapped
570+ }
571+
572+ return
566573}
567574
568575// normalizeTag removes the slash from the beggining or end of the tag name and replace the other
0 commit comments