to record at init time whether it's in use
(ctx context.Context)
| 96 | var _ = envknob.RegisterBool("TS_USE_CONTROL_DIAL_PLAN") // to record at init time whether it's in use |
| 97 | |
| 98 | func (a *Dialer) dial(ctx context.Context) (*ClientConn, error) { |
| 99 | |
| 100 | a.logPort80Failure.Store(true) |
| 101 | |
| 102 | // If we don't have a dial plan, just fall back to dialing the single |
| 103 | // host we know about. |
| 104 | useDialPlan := envknob.BoolDefaultTrue("TS_USE_CONTROL_DIAL_PLAN") |
| 105 | if !useDialPlan || a.DialPlan == nil || len(a.DialPlan.Candidates) == 0 { |
| 106 | return a.dialHost(ctx) |
| 107 | } |
| 108 | candidates := a.DialPlan.Candidates |
| 109 | |
| 110 | // Create a context to be canceled as we return, so once we get a good connection, |
| 111 | // we can drop all the other ones. |
| 112 | ctx, cancel := context.WithCancel(ctx) |
| 113 | defer cancel() |
| 114 | |
| 115 | // Now, for each candidate, kick off a dial in parallel. |
| 116 | type dialResult struct { |
| 117 | conn *ClientConn |
| 118 | err error |
| 119 | } |
| 120 | resultsCh := make(chan dialResult) // unbuffered, never closed |
| 121 | |
| 122 | dialCand := func(cand tailcfg.ControlIPCandidate) (*ClientConn, error) { |
| 123 | if cand.ACEHost != "" { |
| 124 | a.logf("[v2] controlhttp: waited %.2f seconds, dialing %q via ACE %s (%s)", cand.DialStartDelaySec, a.Hostname, cand.ACEHost, cmp.Or(cand.IP.String(), "dns")) |
| 125 | } else { |
| 126 | a.logf("[v2] controlhttp: waited %.2f seconds, dialing %q @ %s", cand.DialStartDelaySec, a.Hostname, cand.IP.String()) |
| 127 | } |
| 128 | |
| 129 | ctx, cancel := context.WithTimeout(ctx, time.Duration(cand.DialTimeoutSec*float64(time.Second))) |
| 130 | defer cancel() |
| 131 | return a.dialHostOpt(ctx, cand.IP, cand.ACEHost) |
| 132 | } |
| 133 | |
| 134 | for _, cand := range candidates { |
| 135 | timer := time.AfterFunc(time.Duration(cand.DialStartDelaySec*float64(time.Second)), func() { |
| 136 | go func() { |
| 137 | conn, err := dialCand(cand) |
| 138 | select { |
| 139 | case resultsCh <- dialResult{conn, err}: |
| 140 | if err == nil { |
| 141 | a.logf("[v1] controlhttp: succeeded dialing %q @ %v from dial plan", a.Hostname, cmp.Or(cand.ACEHost, cand.IP.String())) |
| 142 | } |
| 143 | case <-ctx.Done(): |
| 144 | if conn != nil { |
| 145 | conn.Close() |
| 146 | } |
| 147 | } |
| 148 | }() |
| 149 | }) |
| 150 | defer timer.Stop() |
| 151 | } |
| 152 | |
| 153 | var errs []error |
| 154 | for { |
| 155 | select { |