Repository: etcd-io/etcd
File: server/embed/config.go (Go)

Code before:
// Validate ensures that '*embed.Config' fields are properly configured.
func (cfg *Config) Validate() error {
	if err := cfg.setupLogging(); err != nil {
		return err
	}
	if err := checkBindURLs(cfg.ListenPeerUrls); err != nil {
		return err
	}
	if err := checkBindURLs(cfg.ListenClientUrls); err != nil {
		return err
	}
	if err := checkBindURLs(cfg.ListenClientHttpUrls); err != nil {
		return err
	}
	if len(cfg.ListenClientHttpUrls) == 0 {
		cfg.logger.Warn("Running http and grpc server on single port. This is not recommended for production.")
	}
	if err := checkBindURLs(cfg.ListenMetricsUrls); err != nil {
		return err
	}
	if err := checkHostURLs(cfg.AdvertisePeerUrls); err != nil {
		addrs := cfg.getAdvertisePeerURLs()
		return fmt.Errorf(`--initial-advertise-peer-urls %q must be "host:port" (%w)`, strings.Join(addrs, ","), err)
	}
	if err := checkBindURLs(cfg.AdvertiseClientUrls); err != nil {
		return err
	}
	// Check if conflicting flags are passed.
	nSet := 0
	for _, v := range []bool{cfg.Durl != "", cfg.InitialCluster != "", cfg.DNSCluster != "", len(cfg.DiscoveryCfg.Endpoints) > 0} {
		if v {
			nSet++
		}
	}

	if cfg.ClusterState != ClusterStateFlagNew && cfg.ClusterState != ClusterStateFlagExisting {
		return fmt.Errorf("unexpected clusterState %q", cfg.ClusterState)
	}

	if nSet > 1 {
		return ErrConflictBootstrapFlags
	}

	// Check if both v2 discovery and v3 discovery flags are passed.
	v2discoveryFlagsExist := cfg.Dproxy != ""
	v3discoveryFlagsExist := len(cfg.DiscoveryCfg.Endpoints) > 0 ||
		cfg.DiscoveryCfg.Token != "" ||
		cfg.DiscoveryCfg.Secure.Cert != "" ||
		cfg.DiscoveryCfg.Secure.Key != "" ||
		cfg.DiscoveryCfg.Secure.Cacert != "" ||
		cfg.DiscoveryCfg.Auth.Username != "" ||

Review comment (suggestion):
I am afraid this isn't correct. For `AdvertiseClientUrls`, it accepts domain names or IP address, while `checkBindURLs` requires IP.

Since we support Unix socket for `--listen-client-urls`, so it makes sense to support unix socket as well for `--advertise-client-urls`.

Proposed change below,
```
func checkHostURLs(urls []url.URL) error {
	for _, url := range urls {
		if url.Scheme == "unix" || url.Scheme == "unixs" {
			continue
		}
		host, _, err := net.SplitHostPort(url.Host)
		if err != nil {
			return err
		}
		if host == "" {
			return fmt.Errorf("unexpected empty host (%s)", url.String())
		}
	}
	return nil
}
```

Code after:
// Validate ensures that '*embed.Config' fields are properly configured.
func (cfg *Config) Validate() error {
	if err := cfg.setupLogging(); err != nil {
		return err
	}
	if err := checkBindURLs(cfg.ListenPeerUrls); err != nil {
		return err
	}
	if err := checkBindURLs(cfg.ListenClientUrls); err != nil {
		return err
	}
	if err := checkBindURLs(cfg.ListenClientHttpUrls); err != nil {
		return err
	}
	if len(cfg.ListenClientHttpUrls) == 0 {
		cfg.logger.Warn("Running http and grpc server on single port. This is not recommended for production.")
	}
	if err := checkBindURLs(cfg.ListenMetricsUrls); err != nil {
		return err
	}
	if err := checkHostURLs(cfg.AdvertisePeerUrls); err != nil {
		addrs := cfg.getAdvertisePeerURLs()
		return fmt.Errorf(`--initial-advertise-peer-urls %q must be "host:port" (%w)`, strings.Join(addrs, ","), err)
	}
	if err := checkHostURLs(cfg.AdvertiseClientUrls); err != nil {
		addrs := cfg.getAdvertiseClientURLs()
		return fmt.Errorf(`--advertise-client-urls %q must be in the format "host:port", "unix:/path/to/socket" or "unixs:/path/to/socket" (%w)`, strings.Join(addrs, ","), err)
	}
	// Check if conflicting flags are passed.
	nSet := 0
	for _, v := range []bool{cfg.Durl != "", cfg.InitialCluster != "", cfg.DNSCluster != "", len(cfg.DiscoveryCfg.Endpoints) > 0} {
		if v {
			nSet++
		}
	}

	if cfg.ClusterState != ClusterStateFlagNew && cfg.ClusterState != ClusterStateFlagExisting {
		return fmt.Errorf("unexpected clusterState %q", cfg.ClusterState)
	}

	if nSet > 1 {
		return ErrConflictBootstrapFlags
	}

	// Check if both v2 discovery and v3 discovery flags are passed.
	v2discoveryFlagsExist := cfg.Dproxy != ""
	v3discoveryFlagsExist := len(cfg.DiscoveryCfg.Endpoints) > 0 ||
		cfg.DiscoveryCfg.Token != "" ||
		cfg.DiscoveryCfg.Secure.Cert != "" ||
		cfg.DiscoveryCfg.Secure.Key != "" ||
		cfg.DiscoveryCfg.Secure.Cacert != "" ||