diff --git a/acmev2/client.go b/acmev2/client.go index 55407303..3698f0ec 100644 --- a/acmev2/client.go +++ b/acmev2/client.go @@ -265,7 +265,7 @@ func (c *Client) QueryRegistration() (*RegistrationResource, error) { // your issued certificate as a bundle. // This function will never return a partial certificate. If one domain in the list fails, // the whole certificate will fail. -func (c *Client) ObtainCertificateForCSR(csr x509.CertificateRequest, bundle bool) (CertificateResource, map[string]error) { +func (c *Client) ObtainCertificateForCSR(csr x509.CertificateRequest, bundle bool) (CertificateResource, error) { // figure out what domains it concerns // start with the common name domains := []string{csr.Subject.CommonName} @@ -292,11 +292,7 @@ DNSNames: order, err := c.createOrderForIdentifiers(domains) if err != nil { - identErrors := make(map[string]error) - for _, auth := range order.Identifiers { - identErrors[auth.Value] = err - } - return CertificateResource{}, identErrors + return CertificateResource{}, err } authz, failures := c.getAuthzForOrder(order) // If any challenge fails - return. Do not generate partial SAN certificates. @@ -338,7 +334,11 @@ DNSNames: // your issued certificate as a bundle. // This function will never return a partial certificate. If one domain in the list fails, // the whole certificate will fail. -func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, map[string]error) { +func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, error) { + if len(domains) == 0 { + return CertificateResource{}, errors.New("Passed no domains into ObtainCertificate") + } + if bundle { logf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", ")) } else { @@ -347,11 +347,7 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto order, err := c.createOrderForIdentifiers(domains) if err != nil { - identErrors := make(map[string]error) - for _, domain := range domains { - identErrors[domain] = err - } - return CertificateResource{}, identErrors + return CertificateResource{}, err } authz, failures := c.getAuthzForOrder(order) // If any challenge fails - return. Do not generate partial SAN certificates. @@ -433,7 +429,7 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b return CertificateResource{}, err } newCert, failures := c.ObtainCertificateForCSR(*csr, bundle) - return newCert, failures[cert.Domain] + return newCert, failures } var privKey crypto.PrivateKey @@ -445,7 +441,6 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b } var domains []string - var failures map[string]error // check for SAN certificate if len(x509Cert.DNSNames) > 1 { domains = append(domains, x509Cert.Subject.CommonName) @@ -459,8 +454,8 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b domains = append(domains, x509Cert.Subject.CommonName) } - newCert, failures := c.ObtainCertificate(domains, bundle, privKey, mustStaple) - return newCert, failures[cert.Domain] + newCert, err := c.ObtainCertificate(domains, bundle, privKey, mustStaple) + return newCert, err } func (c *Client) createOrderForIdentifiers(domains []string) (orderResource, error) { @@ -490,9 +485,9 @@ func (c *Client) createOrderForIdentifiers(domains []string) (orderResource, err // Looks through the challenge combinations to find a solvable match. // Then solves the challenges in series and returns. -func (c *Client) solveChallengeForAuthz(authorizations []authorization) map[string]error { +func (c *Client) solveChallengeForAuthz(authorizations []authorization) ObtainError { // loop through the resources, basically through the domains. - failures := make(map[string]error) + failures := make(ObtainError) for _, authz := range authorizations { if authz.Status == "valid" { // Boulder might recycle recent validated authz (see issue #267) @@ -528,7 +523,7 @@ func (c *Client) chooseSolver(auth authorization, domain string) (int, solver) { } // Get the challenges needed to proof our identifier to the ACME server. -func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, map[string]error) { +func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, ObtainError) { resc, errc := make(chan authorization), make(chan domainError) delay := time.Second / overallRequestLimit diff --git a/acmev2/error.go b/acmev2/error.go index 650270b1..78694deb 100644 --- a/acmev2/error.go +++ b/acmev2/error.go @@ -1,6 +1,7 @@ package acme import ( + "bytes" "encoding/json" "fmt" "io/ioutil" @@ -42,6 +43,18 @@ type domainError struct { Error error } +// ObtainError is returned when there are specific errors available +// per domain. For example in ObtainCertificate +type ObtainError map[string]error + +func (e ObtainError) Error() string { + buffer := bytes.NewBufferString("acme: Error -> One or more domains had a problem:\n") + for dom, err := range e { + buffer.WriteString(fmt.Sprintf("[%s] %s\n", dom, err)) + } + return buffer.String() +} + func handleHTTPError(resp *http.Response) error { var errorDetail RemoteError