func getHosts() []string {
	cassandraHostKey := getCassandraHostConfigKey()
	config.WaitUntilLoaded(5 * time.Second)
	port := config.AtPath("hailo", "service", "cassandra", "defaults", "thriftPort").AsInt(defaultPort)
	if hosts := config.AtPath("hailo", "service", "cassandra", cassandraHostKey).AsHostnameArray(port); len(hosts) > 0 {
		return hosts
	}

	// No hosts returned: try DNS
	tier := config.AtPath("hailo", "service", "cassandra", "tier").AsString("premium")
	hosts, err := dns.Hosts("cassandra-" + tier)
	if err != nil {
		log.Errorf("Failed to load Cassandra hosts from dns: %v", err)
		return defaultHosts
	}

	if len(hosts) == 0 {
		return defaultHosts
	}
	// We need to append the port to hosts coming from DNS
	for i, host := range hosts {
		hosts[i] = host + fmt.Sprintf(":%d", port)
	}

	return hosts
}
func (s *DefaultSubscriber) configLoop() {
	// Wait 5 mins for config to load. If we cannot load config by the
	// then there's most likely a major issue and we should panic.
	if !config.WaitUntilLoaded(5 * time.Minute) {
		panic("Waiting 5 mins for config to load")
	}

	s.loadFromConfig()
	ch := config.SubscribeChanges()
	for {
		select {
		case <-ch:
			s.loadFromConfig()
		case <-s.stop:
			return
		}
	}
}
func getKsConfig(ks string) (ksConfig, error) {
	if !config.WaitUntilLoaded(5 * time.Second) {
		return ksConfig{}, fmt.Errorf("Config not loaded")
	}

	username, password, err := ksAuth(ks)
	if err != nil {
		return ksConfig{}, err
	}

	c := ksConfig{
		ks:       ks,
		hosts:    getHosts(),
		username: username,
		password: password,
		retries:  config.AtPath("hailo", "service", "cassandra", "defaults", "maxRetries").AsInt(5),
		cl:       clFromString(config.AtPath("hailo", "service", "cassandra", "defaults", "consistencyLevel").AsString("")),
		timeout:  config.AtPath("hailo", "service", "cassandra", "defaults", "recvTimeout").AsDuration("1s"),
	}
	cc := gocql.NewCluster(c.hosts...)
	cc.ProtoVersion = config.AtPath("hailo", "service", "cassandra", "defaults", "protoVersion").AsInt(2)
	cc.Consistency = c.cl
	cc.Compressor = gocql.SnappyCompressor{}
	cc.DiscoverHosts = false
	cc.NumConns = config.AtPath("hailo", "service", "cassandra", "defaults", "maxHostConns").AsInt(2)
	cc.Authenticator = gocql.PasswordAuthenticator{
		Username: c.username,
		Password: c.password,
	}
	cc.Timeout = c.timeout
	cc.Keyspace = c.ks
	cc.RetryPolicy = &gocql.SimpleRetryPolicy{
		NumRetries: c.retries,
	}
	cc.PoolConfig.HostSelectionPolicy = gocql.HostPoolHostPolicy(
		hostpool.NewEpsilonGreedy(c.hosts, 5*time.Minute, &hostpool.LinearEpsilonValueCalculator{}),
	)
	c.cc = cc
	return c, nil
}
// loadFromConfig including contiuous retries until we have managed to load it
func (v *validatorImpl) loadFromConfig() {
	if !config.WaitUntilLoaded(waitForConfigDelay) {
		// put out a warning anyway, to make it clear we are going to struggle to load key
		log.Warnf("[Auth] Failed to load config after %v, kicking off public key loading anyway...", waitForConfigDelay)
	}

	// block until we load
	attempts := 0
	for {
		if err := v.load(); err != nil {
			attempts++
			delay := time.Duration(int64(startRetryDelay) * int64(attempts))
			if delay > maxRetryDelay {
				delay = maxRetryDelay
			}
			log.Tracef("[Auth] Failed to load public key from config: %v (sleeping for %v)", err, delay)
			time.Sleep(delay)
			continue
		}
		break
	}
}
// setup is a one-time action that loads PUB hosts from config and sets up a config subscriber
func (p *HostpoolPublisher) setup() {
	// Wait 5 mins for config to load. If we cannot load config by the
	// then there's most likely a major issue and we should panic.
	if !config.WaitUntilLoaded(5 * time.Minute) {
		panic("Waiting 5 mins for config to load")
	}

	ch := config.SubscribeChanges()
	p.loadFromConfig()
	go func() {
		for {
			<-ch
			for {
				if err := p.loadFromConfig(); err != nil {
					log.Warnf("Failed to load NSQ PUB config: %v", err)
					time.Sleep(configRetryDelay)
				} else {
					break
				}
			}
		}
	}()
}