|  | @@ -0,0 +1,501 @@
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Copyright 2015, Google Inc.
 | 
	
		
			
				|  |  | + * All rights reserved.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Redistribution and use in source and binary forms, with or without
 | 
	
		
			
				|  |  | + * modification, are permitted provided that the following conditions are
 | 
	
		
			
				|  |  | + * met:
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + *     * Redistributions of source code must retain the above copyright
 | 
	
		
			
				|  |  | + * notice, this list of conditions and the following disclaimer.
 | 
	
		
			
				|  |  | + *     * Redistributions in binary form must reproduce the above
 | 
	
		
			
				|  |  | + * copyright notice, this list of conditions and the following disclaimer
 | 
	
		
			
				|  |  | + * in the documentation and/or other materials provided with the
 | 
	
		
			
				|  |  | + * distribution.
 | 
	
		
			
				|  |  | + *     * Neither the name of Google Inc. nor the names of its
 | 
	
		
			
				|  |  | + * contributors may be used to endorse or promote products derived from
 | 
	
		
			
				|  |  | + * this software without specific prior written permission.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
	
		
			
				|  |  | + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
	
		
			
				|  |  | + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
	
		
			
				|  |  | + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
	
		
			
				|  |  | + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
	
		
			
				|  |  | + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
	
		
			
				|  |  | + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
	
		
			
				|  |  | + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
	
		
			
				|  |  | + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
	
		
			
				|  |  | + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
	
		
			
				|  |  | + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include "src/core/client_config/resolvers/zookeeper_resolver.h"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include <string.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include <grpc/support/alloc.h>
 | 
	
		
			
				|  |  | +#include <grpc/support/string_util.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include <grpc/grpc_zookeeper.h>
 | 
	
		
			
				|  |  | +#include <zookeeper/zookeeper.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include "src/core/client_config/lb_policies/pick_first.h"
 | 
	
		
			
				|  |  | +#include "src/core/client_config/resolver_registry.h"
 | 
	
		
			
				|  |  | +#include "src/core/iomgr/resolve_address.h"
 | 
	
		
			
				|  |  | +#include "src/core/support/string.h"
 | 
	
		
			
				|  |  | +#include "src/core/json/json.h"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** Zookeeper session expiration time in milliseconds */
 | 
	
		
			
				|  |  | +#define GRPC_ZOOKEEPER_SESSION_TIMEOUT 15000
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +typedef struct {
 | 
	
		
			
				|  |  | +  /** base class: must be first */
 | 
	
		
			
				|  |  | +  grpc_resolver base;
 | 
	
		
			
				|  |  | +  /** refcount */
 | 
	
		
			
				|  |  | +  gpr_refcount refs;
 | 
	
		
			
				|  |  | +  /** name to resolve */
 | 
	
		
			
				|  |  | +  char *name;
 | 
	
		
			
				|  |  | +  /** subchannel factory */
 | 
	
		
			
				|  |  | +  grpc_subchannel_factory *subchannel_factory;
 | 
	
		
			
				|  |  | +  /** load balancing policy factory */
 | 
	
		
			
				|  |  | +  grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels,
 | 
	
		
			
				|  |  | +                                       size_t num_subchannels);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /** mutex guarding the rest of the state */
 | 
	
		
			
				|  |  | +  gpr_mu mu;
 | 
	
		
			
				|  |  | +  /** are we currently resolving? */
 | 
	
		
			
				|  |  | +  int resolving;
 | 
	
		
			
				|  |  | +  /** which version of resolved_config have we published? */
 | 
	
		
			
				|  |  | +  int published_version;
 | 
	
		
			
				|  |  | +  /** which version of resolved_config is current? */
 | 
	
		
			
				|  |  | +  int resolved_version;
 | 
	
		
			
				|  |  | +  /** pending next completion, or NULL */
 | 
	
		
			
				|  |  | +  grpc_iomgr_closure *next_completion;
 | 
	
		
			
				|  |  | +  /** target config address for next completion */
 | 
	
		
			
				|  |  | +  grpc_client_config **target_config;
 | 
	
		
			
				|  |  | +  /** current (fully resolved) config */
 | 
	
		
			
				|  |  | +  grpc_client_config *resolved_config;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /** zookeeper handle */
 | 
	
		
			
				|  |  | +  zhandle_t *zookeeper_handle;
 | 
	
		
			
				|  |  | +  /** zookeeper resolved addresses */
 | 
	
		
			
				|  |  | +  grpc_resolved_addresses *resolved_addrs;
 | 
	
		
			
				|  |  | +  /** total number of addresses to be resolved */
 | 
	
		
			
				|  |  | +  int resolved_total;
 | 
	
		
			
				|  |  | +  /** number of addresses resolved */
 | 
	
		
			
				|  |  | +  int resolved_num;
 | 
	
		
			
				|  |  | +} zookeeper_resolver;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_destroy(grpc_resolver *r);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_start_resolving_locked(zookeeper_resolver *r);
 | 
	
		
			
				|  |  | +static void zookeeper_maybe_finish_next_locked(zookeeper_resolver *r);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_shutdown(grpc_resolver *r);
 | 
	
		
			
				|  |  | +static void zookeeper_channel_saw_error(grpc_resolver *r,
 | 
	
		
			
				|  |  | +                                        struct sockaddr *failing_address,
 | 
	
		
			
				|  |  | +                                        int failing_address_len);
 | 
	
		
			
				|  |  | +static void zookeeper_next(grpc_resolver *r, grpc_client_config **target_config,
 | 
	
		
			
				|  |  | +                           grpc_iomgr_closure *on_complete);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static const grpc_resolver_vtable zookeeper_resolver_vtable = {
 | 
	
		
			
				|  |  | +    zookeeper_destroy, zookeeper_shutdown, zookeeper_channel_saw_error,
 | 
	
		
			
				|  |  | +    zookeeper_next};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_shutdown(grpc_resolver *resolver) {
 | 
	
		
			
				|  |  | +  zookeeper_resolver *r = (zookeeper_resolver *)resolver;
 | 
	
		
			
				|  |  | +  gpr_mu_lock(&r->mu);
 | 
	
		
			
				|  |  | +  if (r->next_completion != NULL) {
 | 
	
		
			
				|  |  | +    *r->target_config = NULL;
 | 
	
		
			
				|  |  | +    grpc_iomgr_add_callback(r->next_completion);
 | 
	
		
			
				|  |  | +    r->next_completion = NULL;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  zookeeper_close(r->zookeeper_handle);
 | 
	
		
			
				|  |  | +  gpr_mu_unlock(&r->mu);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_channel_saw_error(grpc_resolver *resolver,
 | 
	
		
			
				|  |  | +                                        struct sockaddr *sa, int len) {
 | 
	
		
			
				|  |  | +  zookeeper_resolver *r = (zookeeper_resolver *)resolver;
 | 
	
		
			
				|  |  | +  gpr_mu_lock(&r->mu);
 | 
	
		
			
				|  |  | +  if (r->resolving == 0) {
 | 
	
		
			
				|  |  | +    zookeeper_start_resolving_locked(r);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  gpr_mu_unlock(&r->mu);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_next(grpc_resolver *resolver,
 | 
	
		
			
				|  |  | +                           grpc_client_config **target_config,
 | 
	
		
			
				|  |  | +                           grpc_iomgr_closure *on_complete) {
 | 
	
		
			
				|  |  | +  zookeeper_resolver *r = (zookeeper_resolver *)resolver;
 | 
	
		
			
				|  |  | +  gpr_mu_lock(&r->mu);
 | 
	
		
			
				|  |  | +  GPR_ASSERT(r->next_completion == NULL);
 | 
	
		
			
				|  |  | +  r->next_completion = on_complete;
 | 
	
		
			
				|  |  | +  r->target_config = target_config;
 | 
	
		
			
				|  |  | +  if (r->resolved_version == 0 && r->resolving == 0) {
 | 
	
		
			
				|  |  | +    zookeeper_start_resolving_locked(r);
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    zookeeper_maybe_finish_next_locked(r);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  gpr_mu_unlock(&r->mu);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** Zookeeper global watcher for connection management 
 | 
	
		
			
				|  |  | +    TODO: better connection management besides logs */
 | 
	
		
			
				|  |  | +static void zookeeper_global_watcher(zhandle_t *zookeeper_handle, int type,
 | 
	
		
			
				|  |  | +                                     int state, const char *path,
 | 
	
		
			
				|  |  | +                                     void *watcher_ctx) {
 | 
	
		
			
				|  |  | +  if (type == ZOO_SESSION_EVENT) {
 | 
	
		
			
				|  |  | +    if (state == ZOO_EXPIRED_SESSION_STATE) {
 | 
	
		
			
				|  |  | +      gpr_log(GPR_ERROR, "Zookeeper session expired");
 | 
	
		
			
				|  |  | +    } else if (state == ZOO_AUTH_FAILED_STATE) {
 | 
	
		
			
				|  |  | +      gpr_log(GPR_ERROR, "Zookeeper authentication failed");
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** Zookeeper watcher triggered by changes to watched nodes
 | 
	
		
			
				|  |  | +    Once triggered, it tries to resolve again to get updated addresses */
 | 
	
		
			
				|  |  | +static void zookeeper_watcher(zhandle_t *zookeeper_handle, int type, int state,
 | 
	
		
			
				|  |  | +                              const char *path, void *watcher_ctx) {
 | 
	
		
			
				|  |  | +  if (watcher_ctx != NULL) {
 | 
	
		
			
				|  |  | +    zookeeper_resolver *r = (zookeeper_resolver *)watcher_ctx;
 | 
	
		
			
				|  |  | +    if (state == ZOO_CONNECTED_STATE) {
 | 
	
		
			
				|  |  | +      gpr_mu_lock(&r->mu);
 | 
	
		
			
				|  |  | +      if (r->resolving == 0) {
 | 
	
		
			
				|  |  | +        zookeeper_start_resolving_locked(r);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      gpr_mu_unlock(&r->mu);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** Callback function after getting all resolved addresses  
 | 
	
		
			
				|  |  | +    Creates a subchannel for each address */
 | 
	
		
			
				|  |  | +static void zookeeper_on_resolved(void *arg,
 | 
	
		
			
				|  |  | +                                  grpc_resolved_addresses *addresses) {
 | 
	
		
			
				|  |  | +  zookeeper_resolver *r = arg;
 | 
	
		
			
				|  |  | +  grpc_client_config *config = NULL;
 | 
	
		
			
				|  |  | +  grpc_subchannel **subchannels;
 | 
	
		
			
				|  |  | +  grpc_subchannel_args args;
 | 
	
		
			
				|  |  | +  grpc_lb_policy *lb_policy;
 | 
	
		
			
				|  |  | +  size_t i;
 | 
	
		
			
				|  |  | +  if (addresses != NULL) {
 | 
	
		
			
				|  |  | +    config = grpc_client_config_create();
 | 
	
		
			
				|  |  | +    subchannels = gpr_malloc(sizeof(grpc_subchannel *) * addresses->naddrs);
 | 
	
		
			
				|  |  | +    for (i = 0; i < addresses->naddrs; i++) {
 | 
	
		
			
				|  |  | +      memset(&args, 0, sizeof(args));
 | 
	
		
			
				|  |  | +      args.addr = (struct sockaddr *)(addresses->addrs[i].addr);
 | 
	
		
			
				|  |  | +      args.addr_len = addresses->addrs[i].len;
 | 
	
		
			
				|  |  | +      subchannels[i] = grpc_subchannel_factory_create_subchannel(
 | 
	
		
			
				|  |  | +          r->subchannel_factory, &args);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    lb_policy = r->lb_policy_factory(subchannels, addresses->naddrs);
 | 
	
		
			
				|  |  | +    grpc_client_config_set_lb_policy(config, lb_policy);
 | 
	
		
			
				|  |  | +    GRPC_LB_POLICY_UNREF(lb_policy, "construction");
 | 
	
		
			
				|  |  | +    grpc_resolved_addresses_destroy(addresses);
 | 
	
		
			
				|  |  | +    gpr_free(subchannels);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  gpr_mu_lock(&r->mu);
 | 
	
		
			
				|  |  | +  GPR_ASSERT(r->resolving == 1);
 | 
	
		
			
				|  |  | +  r->resolving = 0;
 | 
	
		
			
				|  |  | +  if (r->resolved_config != NULL) {
 | 
	
		
			
				|  |  | +    grpc_client_config_unref(r->resolved_config);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  r->resolved_config = config;
 | 
	
		
			
				|  |  | +  r->resolved_version++;
 | 
	
		
			
				|  |  | +  zookeeper_maybe_finish_next_locked(r);
 | 
	
		
			
				|  |  | +  gpr_mu_unlock(&r->mu);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  GRPC_RESOLVER_UNREF(&r->base, "zookeeper-resolving");
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** Callback function for each DNS resolved address */
 | 
	
		
			
				|  |  | +static void zookeeper_dns_resolved(void *arg,
 | 
	
		
			
				|  |  | +                                   grpc_resolved_addresses *addresses) {
 | 
	
		
			
				|  |  | +  size_t i;
 | 
	
		
			
				|  |  | +  zookeeper_resolver *r = arg;
 | 
	
		
			
				|  |  | +  int resolve_done = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  gpr_mu_lock(&r->mu);
 | 
	
		
			
				|  |  | +  r->resolved_num++;
 | 
	
		
			
				|  |  | +  r->resolved_addrs->addrs =
 | 
	
		
			
				|  |  | +      gpr_realloc(r->resolved_addrs->addrs,
 | 
	
		
			
				|  |  | +                  sizeof(grpc_resolved_address) *
 | 
	
		
			
				|  |  | +                      (r->resolved_addrs->naddrs + addresses->naddrs));
 | 
	
		
			
				|  |  | +  for (i = 0; i < addresses->naddrs; i++) {
 | 
	
		
			
				|  |  | +    memcpy(r->resolved_addrs->addrs[i + r->resolved_addrs->naddrs].addr,
 | 
	
		
			
				|  |  | +           addresses->addrs[i].addr, addresses->addrs[i].len);
 | 
	
		
			
				|  |  | +    r->resolved_addrs->addrs[i + r->resolved_addrs->naddrs].len =
 | 
	
		
			
				|  |  | +        addresses->addrs[i].len;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  r->resolved_addrs->naddrs += addresses->naddrs;
 | 
	
		
			
				|  |  | +  grpc_resolved_addresses_destroy(addresses);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /** Wait for all addresses to be resolved */
 | 
	
		
			
				|  |  | +  resolve_done = (r->resolved_num == r->resolved_total);
 | 
	
		
			
				|  |  | +  gpr_mu_unlock(&r->mu);
 | 
	
		
			
				|  |  | +  if (resolve_done) {
 | 
	
		
			
				|  |  | +    zookeeper_on_resolved(r, r->resolved_addrs);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/** Parses JSON format address of a zookeeper node */
 | 
	
		
			
				|  |  | +static char *zookeeper_parse_address(const char *value, int value_len) {
 | 
	
		
			
				|  |  | +  grpc_json *json;
 | 
	
		
			
				|  |  | +  grpc_json *cur;
 | 
	
		
			
				|  |  | +  const char *host;
 | 
	
		
			
				|  |  | +  const char *port;
 | 
	
		
			
				|  |  | +  char* buffer;
 | 
	
		
			
				|  |  | +  char *address = NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  buffer = gpr_malloc(value_len);
 | 
	
		
			
				|  |  | +  memcpy(buffer, value, value_len);
 | 
	
		
			
				|  |  | +  json = grpc_json_parse_string_with_len(buffer, value_len);
 | 
	
		
			
				|  |  | +  if (json != NULL) {
 | 
	
		
			
				|  |  | +    host = NULL;
 | 
	
		
			
				|  |  | +    port = NULL;
 | 
	
		
			
				|  |  | +    for (cur = json->child; cur != NULL; cur = cur->next) {
 | 
	
		
			
				|  |  | +      if (!strcmp(cur->key, "host")) {
 | 
	
		
			
				|  |  | +        host = cur->value;
 | 
	
		
			
				|  |  | +        if (port != NULL) {
 | 
	
		
			
				|  |  | +          break;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      } else if (!strcmp(cur->key, "port")) {
 | 
	
		
			
				|  |  | +        port = cur->value;
 | 
	
		
			
				|  |  | +        if (host != NULL) {
 | 
	
		
			
				|  |  | +          break;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    if (host != NULL && port != NULL) {
 | 
	
		
			
				|  |  | +      gpr_asprintf(&address, "%s:%s", host, port);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    grpc_json_destroy(json);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  gpr_free(buffer);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return address;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_get_children_node_completion(int rc, const char *value,
 | 
	
		
			
				|  |  | +                                                   int value_len,
 | 
	
		
			
				|  |  | +                                                   const struct Stat *stat,
 | 
	
		
			
				|  |  | +                                                   const void *arg) {
 | 
	
		
			
				|  |  | +  char *address = NULL;
 | 
	
		
			
				|  |  | +  zookeeper_resolver *r = (zookeeper_resolver *)arg;
 | 
	
		
			
				|  |  | +  int resolve_done = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (rc != 0) {
 | 
	
		
			
				|  |  | +    gpr_log(GPR_ERROR, "Error in getting a child node of %s", r->name);
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  address = zookeeper_parse_address(value, value_len);
 | 
	
		
			
				|  |  | +  if (address != NULL) {
 | 
	
		
			
				|  |  | +    /** Further resolves address by DNS */
 | 
	
		
			
				|  |  | +    grpc_resolve_address(address, NULL, zookeeper_dns_resolved, r);
 | 
	
		
			
				|  |  | +    gpr_free(address);
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    gpr_log(GPR_ERROR, "Error in resolving a child node of %s", r->name);
 | 
	
		
			
				|  |  | +    gpr_mu_lock(&r->mu);
 | 
	
		
			
				|  |  | +    r->resolved_total--;
 | 
	
		
			
				|  |  | +    resolve_done = (r->resolved_num == r->resolved_total);
 | 
	
		
			
				|  |  | +    gpr_mu_unlock(&r->mu);
 | 
	
		
			
				|  |  | +    if (resolve_done) {
 | 
	
		
			
				|  |  | +      zookeeper_on_resolved(r, r->resolved_addrs);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_get_children_completion(
 | 
	
		
			
				|  |  | +    int rc, const struct String_vector *children, const void *arg) {
 | 
	
		
			
				|  |  | +  char *path;
 | 
	
		
			
				|  |  | +  int status;
 | 
	
		
			
				|  |  | +  int i;
 | 
	
		
			
				|  |  | +  zookeeper_resolver *r = (zookeeper_resolver *)arg;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (rc != 0) {
 | 
	
		
			
				|  |  | +    gpr_log(GPR_ERROR, "Error in getting zookeeper children of %s", r->name);
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (children->count == 0) {
 | 
	
		
			
				|  |  | +    gpr_log(GPR_ERROR, "Error in resolving zookeeper address %s", r->name);
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  r->resolved_addrs = gpr_malloc(sizeof(grpc_resolved_addresses));
 | 
	
		
			
				|  |  | +  r->resolved_addrs->addrs = NULL;
 | 
	
		
			
				|  |  | +  r->resolved_addrs->naddrs = 0;
 | 
	
		
			
				|  |  | +  r->resolved_total = children->count;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /** TODO: Replace expensive heap allocation with stack
 | 
	
		
			
				|  |  | +      if we can get maximum length of zookeeper path */
 | 
	
		
			
				|  |  | +  for (i = 0; i < children->count; i++) {
 | 
	
		
			
				|  |  | +    gpr_asprintf(&path, "%s/%s", r->name, children->data[i]);
 | 
	
		
			
				|  |  | +    status = zoo_awget(r->zookeeper_handle, path, zookeeper_watcher, r,
 | 
	
		
			
				|  |  | +                       zookeeper_get_children_node_completion, r);
 | 
	
		
			
				|  |  | +    gpr_free(path);
 | 
	
		
			
				|  |  | +    if (status != 0) {
 | 
	
		
			
				|  |  | +      gpr_log(GPR_ERROR, "Error in getting zookeeper node %s", path);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_get_node_completion(int rc, const char *value,
 | 
	
		
			
				|  |  | +                                          int value_len,
 | 
	
		
			
				|  |  | +                                          const struct Stat *stat,
 | 
	
		
			
				|  |  | +                                          const void *arg) {
 | 
	
		
			
				|  |  | +  int status;
 | 
	
		
			
				|  |  | +  char *address = NULL;
 | 
	
		
			
				|  |  | +  zookeeper_resolver *r = (zookeeper_resolver *)arg;
 | 
	
		
			
				|  |  | +  r->resolved_addrs = NULL;
 | 
	
		
			
				|  |  | +  r->resolved_total = 0;
 | 
	
		
			
				|  |  | +  r->resolved_num = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (rc != 0) {
 | 
	
		
			
				|  |  | +    gpr_log(GPR_ERROR, "Error in getting zookeeper node %s", r->name);
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /** If zookeeper node of path r->name does not have address
 | 
	
		
			
				|  |  | +      (i.e. service node), get its children */
 | 
	
		
			
				|  |  | +  address = zookeeper_parse_address(value, value_len);
 | 
	
		
			
				|  |  | +  if (address != NULL) {
 | 
	
		
			
				|  |  | +    r->resolved_addrs = gpr_malloc(sizeof(grpc_resolved_addresses));
 | 
	
		
			
				|  |  | +    r->resolved_addrs->addrs = NULL;
 | 
	
		
			
				|  |  | +    r->resolved_addrs->naddrs = 0;
 | 
	
		
			
				|  |  | +    r->resolved_total = 1;
 | 
	
		
			
				|  |  | +    /** Further resolves address by DNS */
 | 
	
		
			
				|  |  | +    grpc_resolve_address(address, NULL, zookeeper_dns_resolved, r);
 | 
	
		
			
				|  |  | +    gpr_free(address);
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  status = zoo_awget_children(r->zookeeper_handle, r->name, zookeeper_watcher,
 | 
	
		
			
				|  |  | +                              r, zookeeper_get_children_completion, r);
 | 
	
		
			
				|  |  | +  if (status != 0) {
 | 
	
		
			
				|  |  | +    gpr_log(GPR_ERROR, "Error in getting zookeeper children of %s", r->name);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_resolve_address(zookeeper_resolver *r) {
 | 
	
		
			
				|  |  | +  int status;
 | 
	
		
			
				|  |  | +  status = zoo_awget(r->zookeeper_handle, r->name, zookeeper_watcher, r,
 | 
	
		
			
				|  |  | +                     zookeeper_get_node_completion, r);
 | 
	
		
			
				|  |  | +  if (status != 0) {
 | 
	
		
			
				|  |  | +    gpr_log(GPR_ERROR, "Error in getting zookeeper node %s", r->name);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_start_resolving_locked(zookeeper_resolver *r) {
 | 
	
		
			
				|  |  | +  GRPC_RESOLVER_REF(&r->base, "zookeeper-resolving");
 | 
	
		
			
				|  |  | +  GPR_ASSERT(r->resolving == 0);
 | 
	
		
			
				|  |  | +  r->resolving = 1;
 | 
	
		
			
				|  |  | +  zookeeper_resolve_address(r);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_maybe_finish_next_locked(zookeeper_resolver *r) {
 | 
	
		
			
				|  |  | +  if (r->next_completion != NULL &&
 | 
	
		
			
				|  |  | +      r->resolved_version != r->published_version) {
 | 
	
		
			
				|  |  | +    *r->target_config = r->resolved_config;
 | 
	
		
			
				|  |  | +    if (r->resolved_config != NULL) {
 | 
	
		
			
				|  |  | +      grpc_client_config_ref(r->resolved_config);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    grpc_iomgr_add_callback(r->next_completion);
 | 
	
		
			
				|  |  | +    r->next_completion = NULL;
 | 
	
		
			
				|  |  | +    r->published_version = r->resolved_version;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_destroy(grpc_resolver *gr) {
 | 
	
		
			
				|  |  | +  zookeeper_resolver *r = (zookeeper_resolver *)gr;
 | 
	
		
			
				|  |  | +  gpr_mu_destroy(&r->mu);
 | 
	
		
			
				|  |  | +  if (r->resolved_config != NULL) {
 | 
	
		
			
				|  |  | +    grpc_client_config_unref(r->resolved_config);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  grpc_subchannel_factory_unref(r->subchannel_factory);
 | 
	
		
			
				|  |  | +  gpr_free(r->name);
 | 
	
		
			
				|  |  | +  gpr_free(r);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static grpc_resolver *zookeeper_create(
 | 
	
		
			
				|  |  | +    grpc_uri *uri,
 | 
	
		
			
				|  |  | +    grpc_lb_policy *(*lb_policy_factory)(grpc_subchannel **subchannels,
 | 
	
		
			
				|  |  | +                                         size_t num_subchannels),
 | 
	
		
			
				|  |  | +    grpc_subchannel_factory *subchannel_factory) {
 | 
	
		
			
				|  |  | +  zookeeper_resolver *r;
 | 
	
		
			
				|  |  | +  size_t length;
 | 
	
		
			
				|  |  | +  char *path = uri->path;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (0 == strcmp(uri->authority, "")) {
 | 
	
		
			
				|  |  | +    gpr_log(GPR_ERROR, "No authority specified in zookeeper uri");
 | 
	
		
			
				|  |  | +    return NULL;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /** Removes the trailing slash if exists */
 | 
	
		
			
				|  |  | +  length = strlen(path);
 | 
	
		
			
				|  |  | +  if (length > 1 && path[length - 1] == '/') {
 | 
	
		
			
				|  |  | +    path[length - 1] = 0;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  r = gpr_malloc(sizeof(zookeeper_resolver));
 | 
	
		
			
				|  |  | +  memset(r, 0, sizeof(*r));
 | 
	
		
			
				|  |  | +  gpr_ref_init(&r->refs, 1);
 | 
	
		
			
				|  |  | +  gpr_mu_init(&r->mu);
 | 
	
		
			
				|  |  | +  grpc_resolver_init(&r->base, &zookeeper_resolver_vtable);
 | 
	
		
			
				|  |  | +  r->name = gpr_strdup(path);
 | 
	
		
			
				|  |  | +  
 | 
	
		
			
				|  |  | +  r->subchannel_factory = subchannel_factory;
 | 
	
		
			
				|  |  | +  r->lb_policy_factory = lb_policy_factory;
 | 
	
		
			
				|  |  | +  grpc_subchannel_factory_ref(subchannel_factory);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /** Initializes zookeeper client */
 | 
	
		
			
				|  |  | +  zoo_set_debug_level(ZOO_LOG_LEVEL_WARN);
 | 
	
		
			
				|  |  | +  r->zookeeper_handle = zookeeper_init(uri->authority, zookeeper_global_watcher,
 | 
	
		
			
				|  |  | +                                       GRPC_ZOOKEEPER_SESSION_TIMEOUT, 0, 0, 0);
 | 
	
		
			
				|  |  | +  if (r->zookeeper_handle == NULL) {
 | 
	
		
			
				|  |  | +    gpr_log(GPR_ERROR, "Unable to connect to zookeeper server");
 | 
	
		
			
				|  |  | +    return NULL;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return &r->base;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_plugin_init() {
 | 
	
		
			
				|  |  | +  grpc_register_resolver_type("zookeeper",
 | 
	
		
			
				|  |  | +                              grpc_zookeeper_resolver_factory_create());
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void grpc_zookeeper_register() {
 | 
	
		
			
				|  |  | +  grpc_register_plugin(zookeeper_plugin_init, NULL);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * FACTORY
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_factory_ref(grpc_resolver_factory *factory) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void zookeeper_factory_unref(grpc_resolver_factory *factory) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static grpc_resolver *zookeeper_factory_create_resolver(
 | 
	
		
			
				|  |  | +    grpc_resolver_factory *factory, grpc_uri *uri,
 | 
	
		
			
				|  |  | +    grpc_subchannel_factory *subchannel_factory) {
 | 
	
		
			
				|  |  | +  return zookeeper_create(uri, grpc_create_pick_first_lb_policy,
 | 
	
		
			
				|  |  | +                          subchannel_factory);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static const grpc_resolver_factory_vtable zookeeper_factory_vtable = {
 | 
	
		
			
				|  |  | +    zookeeper_factory_ref, zookeeper_factory_unref,
 | 
	
		
			
				|  |  | +    zookeeper_factory_create_resolver};
 | 
	
		
			
				|  |  | +static grpc_resolver_factory zookeeper_resolver_factory = {
 | 
	
		
			
				|  |  | +    &zookeeper_factory_vtable};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +grpc_resolver_factory *grpc_zookeeper_resolver_factory_create() {
 | 
	
		
			
				|  |  | +  return &zookeeper_resolver_factory;
 | 
	
		
			
				|  |  | +}
 |