// Completes a given challenge, polling it until it is complete. Can be // cancelled using ctx. // // dnsName is the hostname which is being authorized. webPaths and priorKeyFunc // are passed to responders. // // The return value indicates whether the whole authorization has been invalidated // (set to "failed" status) as a result of an error. In this case a new authorization // must be created. func CompleteChallenge(c *acmeapi.Client, ch *acmeapi.Challenge, dnsName string, ccfg responder.ChallengeConfig, ctx context.Context) (invalidated bool, err error) { log.Debugf("attempting challenge type %s", ch.Type) var certs [][]byte for _, c := range ch.Certs { certs = append(certs, c) } r, err := responder.New(responder.Config{ Type: ch.Type, Token: ch.Token, AccountKey: c.AccountKey, Hostname: dnsName, AcceptableCertificates: certs, ChallengeConfig: ccfg, }) if err != nil { log.Debuge(err, "challenge instantiation failed") return false, err } err = r.Start() if err != nil { log.Debuge(err, "challenge start failed") return false, err } defer r.Stop() err = c.RespondToChallenge(ch, r.Validation(), r.ValidationSigningKey(), ctx) if err != nil { return false /* ??? */, err } b := denet.Backoff{ InitialDelay: 5 * time.Second, MaxDelay: 30 * time.Second, } for { log.Debug("waiting to poll challenge") select { case <-ctx.Done(): return true, ctx.Err() case <-r.RequestDetectedChan(): log.Debug("request detected") case <-time.After(b.NextDelay()): } log.Debug("querying challenge status") err := c.WaitLoadChallenge(ch, ctx) if err != nil { return false, err } if ch.Status.Final() { log.Debug("challenge now in final state") break } } if ch.Status != "valid" { return true, fmt.Errorf("challenge failed with status %#v", ch.Status) } return false, nil }