src/client/client.go
func NewOnUserMachine(reportMetrics bool, portForward bool, prefix string, options ...Option) (*APIClient, error) {
	cfg, err := config.Read()
	if err != nil {
		// metrics errors are non fatal
		log.Warningf("error loading user config from ~/.pachyderm/config: %v", err)
	}

	// create new pachctl client
	var fw *PortForwarder
	addr, cfgOptions, err := getUserMachineAddrAndOpts(cfg)
	if err != nil {
		return nil, err
	}
	if addr == "" {
		addr = fmt.Sprintf("0.0.0.0:%s", DefaultPachdNodePort)

		if portForward {
			fw = portForwarder()
		}
	}

	client, err := NewFromAddress(addr, append(options, cfgOptions...)...)
	if err != nil {
		if strings.Contains(err.Error(), "context deadline exceeded") {
			// port always starts after last colon, but net.SplitHostPort returns an
			// error on a hostport without a colon, which this might be
			port := ""
			if colonIdx := strings.LastIndexByte(addr, ':'); colonIdx >= 0 {
				port = addr[colonIdx+1:]
			}
			// Check for errors in approximate order of helpfulness
			if port != "" && port != DefaultPachdPort && port != DefaultPachdNodePort {
				return nil, fmt.Errorf("could not connect (note: port is usually "+
					"%s or %s, but is currently set to %q--is this right?): %v", DefaultPachdNodePort, DefaultPachdPort, port, err)
			}
			if strings.HasPrefix(addr, "0.0.0.0") ||
				strings.HasPrefix(addr, "127.0.0.1") ||
				strings.HasPrefix(addr, "[::1]") ||
				strings.HasPrefix(addr, "localhost") {
				return nil, fmt.Errorf("could not connect (note: address %q looks "+
					"like loopback, check that 'pachctl port-forward' is running): %v",
					addr, err)
			}
			if port == "" {
				return nil, fmt.Errorf("could not connect (note: address %q does not "+
					"seem to be host:port): %v", addr, err)
			}
		}
		return nil, fmt.Errorf("could not connect to pachd at %q: %v", addr, err)
	}

	// Add metrics info & authentication token
	client.metricsPrefix = prefix
	if cfg != nil && cfg.UserID != "" && reportMetrics {
		client.metricsUserID = cfg.UserID
	}
	if cfg != nil && cfg.V1 != nil && cfg.V1.SessionToken != "" {
		client.authenticationToken = cfg.V1.SessionToken
	}

	// Add port forwarding. This will set it to nil if port forwarding is
	// disabled, or an address is explicitly set.
	client.portForwarder = fw

	return client, nil
}
func NewOnUserMachine(reportMetrics bool, portForward bool, prefix string, options ...Option) (*APIClient, error) {
	cfg, err := config.Read()
	if err != nil {
		// metrics errors are non fatal
		log.Warningf("error loading user config from ~/.pachyderm/config: %v", err)
	}

	// create new pachctl client
	var fw *PortForwarder
	addr, cfgOptions, err := getUserMachineAddrAndOpts(cfg)
	if err != nil {
		return nil, err
	}
	if addr == "" {
		addr = fmt.Sprintf("0.0.0.0:%s", DefaultPachdNodePort)

		if portForward {
			fw = portForwarder()
		}
	}

	client, err := NewFromAddress(addr, append(options, cfgOptions...)...)
	if err != nil {
		if strings.Contains(err.Error(), "context deadline exceeded") {
			// port always starts after last colon, but net.SplitHostPort returns an
			// error on a hostport without a colon, which this might be
			port := ""
			if colonIdx := strings.LastIndexByte(addr, ':'); colonIdx >= 0 {
				port = addr[colonIdx+1:]
			}
			// Check for errors in approximate order of helpfulness
			if port != "" && port != DefaultPachdPort && port != DefaultPachdNodePort {
				return nil, fmt.Errorf("could not connect (note: port is usually "+
					"%s or %s, but is currently set to %q--is this right?): %v", DefaultPachdNodePort, DefaultPachdPort, port, err)
			}
			if strings.HasPrefix(addr, "0.0.0.0") ||
				strings.HasPrefix(addr, "127.0.0.1") ||
				strings.HasPrefix(addr, "[::1]") ||
				strings.HasPrefix(addr, "localhost") {
				return nil, fmt.Errorf("could not connect (note: address %q looks "+
					"like loopback, check that 'pachctl port-forward' is running): %v",
					addr, err)
			}
			if port == "" {
				return nil, fmt.Errorf("could not connect (note: address %q does not "+
					"seem to be host:port): %v", addr, err)
			}
		}
		return nil, fmt.Errorf("could not connect to pachd at %q: %v", addr, err)
	}

	// Add metrics info & authentication token
	client.metricsPrefix = prefix
	if cfg != nil && cfg.UserID != "" && reportMetrics {
		client.metricsUserID = cfg.UserID
	}
	if cfg != nil && cfg.V1 != nil && cfg.V1.SessionToken != "" {
		client.authenticationToken = cfg.V1.SessionToken
	}

	// Add port forwarding. This will set it to nil if port forwarding is
	// disabled, or an address is explicitly set.
	client.portForwarder = fw

	return client, nil
}
// NewOnUserMachine constructs a new APIClient using env vars that may be set
// on a user's machine (i.e. PACHD_ADDRESS), as well as
// $HOME/.pachyderm/config if it  exists. This is primarily intended to be
// used with the pachctl binary, but may also be useful in tests.
//
// TODO(msteffen) this logic is fairly linux/unix specific, and makes the
// pachyderm client library incompatible with Windows. We may want to move this
// (and similar) logic into src/server and have it call a NewFromOptions()
// constructor.