This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new ee41354  Acme async (#5738)
ee41354 is described below

commit ee413544850bd549baa9a7ae990872be5c61a85e
Author: mattjackson220 <[email protected]>
AuthorDate: Fri Apr 23 00:33:48 2021 -0600

    Acme async (#5738)
    
    * Added async status to ACME generation
    
    * updated changelog
    
    * updated to require authtype in acme request and to recover from panic
    
    * fixed tabs/spaces per comment
    
    * fixed recovery and auth type
    
    * fixed errors
    
    * updated per comments
---
 CHANGELOG.md                                       |   1 +
 ...liveryservices_sslkeys_generate_letsencrypt.rst |   2 +-
 ...liveryservices_sslkeys_generate_letsencrypt.rst |   2 +-
 ...liveryservices_sslkeys_generate_letsencrypt.rst |   2 +-
 .../v4/deliveryservices_sslkeys_generate_acme.rst  |   4 +-
 ...liveryservices_sslkeys_generate_letsencrypt.rst |   2 +-
 lib/go-tc/deliveryservice_ssl_keys.go              |  16 ++-
 traffic_ops/traffic_ops_golang/api/async_status.go |   3 +
 .../traffic_ops_golang/deliveryservice/acme.go     | 147 ++++++++++++++++-----
 .../deliveryservice/autorenewcerts.go              |  24 ++--
 .../common/api/DeliveryServiceSslKeysService.js    |  88 ++++++------
 ...FormGenerateDeliveryServiceSslKeysController.js |   2 +-
 12 files changed, 197 insertions(+), 96 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 95ce6db..a0c2a40 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -34,6 +34,7 @@ The format is based on [Keep a 
Changelog](http://keepachangelog.com/en/1.0.0/).
 - ORT config generation: Added a rule to ip_allow such that PURGE requests are 
allowed over localhost
 - Added integration to use ACME to generate new SSL certificates.
 - Add a Federation to the Ansible Dataset Loader
+- Added asynchronous status to ACME certificate generation.
 
 ### Fixed
 - [#5690](https://github.com/apache/trafficcontrol/issues/5690) - Fixed github 
action for added/modified db migration file.
diff --git 
a/docs/source/api/v1/deliveryservices_sslkeys_generate_letsencrypt.rst 
b/docs/source/api/v1/deliveryservices_sslkeys_generate_letsencrypt.rst
index c73c40c..bdf923f 100644
--- a/docs/source/api/v1/deliveryservices_sslkeys_generate_letsencrypt.rst
+++ b/docs/source/api/v1/deliveryservices_sslkeys_generate_letsencrypt.rst
@@ -62,7 +62,7 @@ Response Structure
 
        { "alerts": [{
                "level": "success",
-               "text": "Beginning async call to Let's Encrypt for ds-01. This 
may take a few minutes."
+               "text": "Beginning async ACME call for demo1 using Lets 
Encrypt. This may take a few minutes. Status updates can be found here: 
/api/4.0/async_status/1"
        }]}
 
 .. [#needOne] Either the ``key`` or the ``deliveryservice`` field must be 
provided. If both are provided, then they must match.
diff --git 
a/docs/source/api/v2/deliveryservices_sslkeys_generate_letsencrypt.rst 
b/docs/source/api/v2/deliveryservices_sslkeys_generate_letsencrypt.rst
index 42dc4b9..54c5288 100644
--- a/docs/source/api/v2/deliveryservices_sslkeys_generate_letsencrypt.rst
+++ b/docs/source/api/v2/deliveryservices_sslkeys_generate_letsencrypt.rst
@@ -60,7 +60,7 @@ Response Structure
 
        { "alerts": [{
                "level": "success",
-               "text": "Beginning async call to Let's Encrypt for ds-01. This 
may take a few minutes."
+               "text": "Beginning async ACME call for demo1 using Lets 
Encrypt. This may take a few minutes. Status updates can be found here: 
/api/4.0/async_status/1"
        }]}
 
 .. [#needOne] Either the ``key`` or the ``deliveryservice`` field must be 
provided. If both are provided, then they must match.
diff --git 
a/docs/source/api/v3/deliveryservices_sslkeys_generate_letsencrypt.rst 
b/docs/source/api/v3/deliveryservices_sslkeys_generate_letsencrypt.rst
index 9f35f8d..c1bd2ed 100644
--- a/docs/source/api/v3/deliveryservices_sslkeys_generate_letsencrypt.rst
+++ b/docs/source/api/v3/deliveryservices_sslkeys_generate_letsencrypt.rst
@@ -60,7 +60,7 @@ Response Structure
 
        { "alerts": [{
                "level": "success",
-               "text": "Beginning async call to Let's Encrypt for ds-01. This 
may take a few minutes."
+               "text": "Beginning async ACME call for demo1 using Lets 
Encrypt. This may take a few minutes. Status updates can be found here: 
/api/4.0/async_status/1"
        }]}
 
 .. [#needOne] Either the ``key`` or the ``deliveryservice`` field must be 
provided. If both are provided, then they must match.
diff --git a/docs/source/api/v4/deliveryservices_sslkeys_generate_acme.rst 
b/docs/source/api/v4/deliveryservices_sslkeys_generate_acme.rst
index fac9a04..1df09ae 100644
--- a/docs/source/api/v4/deliveryservices_sslkeys_generate_acme.rst
+++ b/docs/source/api/v4/deliveryservices_sslkeys_generate_acme.rst
@@ -48,7 +48,7 @@ Request Structure
        Content-Type: application/json
 
        {
-               "authType": "Let's Encrypt",
+               "authType": "Lets Encrypt",
                "key": "ds-01",
                "deliveryservice": "ds-01",
                "version": "3",
@@ -64,7 +64,7 @@ Response Structure
 
        { "alerts": [{
                "level": "success",
-               "text": "Beginning async ACME call for ds-01 using Let's 
Encrypt. This may take a few minutes."
+               "text": "Beginning async ACME call for demo1 using Lets 
Encrypt. This may take a few minutes. Status updates can be found here: 
/api/4.0/async_status/1"
        }]}
 
 .. [#needOne] Either the ``key`` or the ``deliveryservice`` field must be 
provided. If both are provided, then they must match.
diff --git 
a/docs/source/api/v4/deliveryservices_sslkeys_generate_letsencrypt.rst 
b/docs/source/api/v4/deliveryservices_sslkeys_generate_letsencrypt.rst
index dbcba6c..d1f1294 100644
--- a/docs/source/api/v4/deliveryservices_sslkeys_generate_letsencrypt.rst
+++ b/docs/source/api/v4/deliveryservices_sslkeys_generate_letsencrypt.rst
@@ -65,7 +65,7 @@ Response Structure
                "text": "This endpoint is deprecated, please use 
/deliveryservices/sslkeys/generate/acme instead."
        },{
                "level": "success",
-               "text": "Beginning async call to Let's Encrypt for ds-01. This 
may take a few minutes."
+               "text": "Beginning async ACME call for demo1 using Lets 
Encrypt. This may take a few minutes. Status updates can be found here: 
/api/4.0/async_status/1"
        }]}
 
 .. [#needOne] Either the ``key`` or the ``deliveryservice`` field must be 
provided. If both are provided, then they must match.
diff --git a/lib/go-tc/deliveryservice_ssl_keys.go 
b/lib/go-tc/deliveryservice_ssl_keys.go
index be91837..b759eda 100644
--- a/lib/go-tc/deliveryservice_ssl_keys.go
+++ b/lib/go-tc/deliveryservice_ssl_keys.go
@@ -186,19 +186,31 @@ func (r *DeliveryServiceGenSSLKeysReq) Validate(tx 
*sql.Tx) error {
        return nil
 }
 
-type DeliveryServiceLetsEncryptSSLKeysReq struct {
+type DeliveryServiceAcmeSSLKeysReq struct {
        DeliveryServiceSSLKeysReq
 }
 
-func (r *DeliveryServiceLetsEncryptSSLKeysReq) Validate(tx *sql.Tx) error {
+func (r *DeliveryServiceAcmeSSLKeysReq) Validate(tx *sql.Tx) error {
        r.Sanitize()
        errs := r.validateSharedRequiredRequestFields()
        if len(errs) > 0 {
                return errors.New("missing fields: " + strings.Join(errs, "; "))
        }
+       errs = r.validateAcmeSpecificFields()
+       if len(errs) > 0 {
+               return errors.New("missing fields: " + strings.Join(errs, "; "))
+       }
        return nil
 }
 
+func (r *DeliveryServiceAcmeSSLKeysReq) validateAcmeSpecificFields() []string {
+       errs := []string{}
+       if checkNilOrEmpty(r.AuthType) {
+               errs = append(errs, "authType required")
+       }
+       return errs
+}
+
 func checkNilOrEmpty(s *string) bool {
        return s == nil || *s == ""
 }
diff --git a/traffic_ops/traffic_ops_golang/api/async_status.go 
b/traffic_ops/traffic_ops_golang/api/async_status.go
index eb55879..7a15df2 100644
--- a/traffic_ops/traffic_ops_golang/api/async_status.go
+++ b/traffic_ops/traffic_ops_golang/api/async_status.go
@@ -117,6 +117,9 @@ func InsertAsyncStatus(tx *sql.Tx, message string) (int, 
int, error, error) {
 
 // UpdateAsyncStatus updates the status table for an asynchronous job.
 func UpdateAsyncStatus(db *sqlx.DB, newStatus string, newMessage string, 
asyncStatusId int, finished bool) error {
+       if asyncStatusId == 0 {
+               return nil
+       }
        tx, err := db.Begin()
        if err != nil {
                return err
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/acme.go 
b/traffic_ops/traffic_ops_golang/deliveryservice/acme.go
index 4fee166..ce63298 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/acme.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/acme.go
@@ -29,6 +29,7 @@ import (
        "database/sql"
        "encoding/pem"
        "errors"
+       "fmt"
        "net/http"
        "strconv"
        "strings"
@@ -104,12 +105,12 @@ func (d *DNSProviderTrafficRouter) Present(domain, token, 
keyAuth string) error
        tx.Commit()
        if err != nil {
                log.Errorf("Inserting dns txt record for fqdn '" + fqdn + "' 
record '" + value + "': " + err.Error())
-               return errors.New("Inserting dns txt record for fqdn '" + fqdn 
+ "' record '" + value + "': " + err.Error())
+               return fmt.Errorf("Inserting dns txt record for fqdn '"+fqdn+"' 
record '"+value+"': %v", err)
        } else {
                rows, err := response.RowsAffected()
                if err != nil {
                        log.Errorf("Determining rows affected dns txt record 
for fqdn '" + fqdn + "' record '" + value + "': " + err.Error())
-                       return errors.New("Determining rows affected dns txt 
record for fqdn '" + fqdn + "' record '" + value + "': " + err.Error())
+                       return fmt.Errorf("Determining rows affected dns txt 
record for fqdn '"+fqdn+"' record '"+value+"': %v", err)
                }
                if rows == 0 {
                        log.Errorf("Zero rows affected when inserting dns txt 
record for fqdn '" + fqdn + "' record '" + value)
@@ -130,12 +131,12 @@ func (d *DNSProviderTrafficRouter) CleanUp(domain, token, 
keyAuth string) error
        tx.Commit()
        if err != nil {
                log.Errorf("Deleting dns txt record for fqdn '" + fqdn + "' 
record '" + value + "': " + err.Error())
-               return errors.New("Deleting dns txt record for fqdn '" + fqdn + 
"' record '" + value + "': " + err.Error())
+               return fmt.Errorf("Deleting dns txt record for fqdn '"+fqdn+"' 
record '"+value+"': %v", err)
        } else {
                rows, err := response.RowsAffected()
                if err != nil {
                        log.Errorf("Determining rows affected when deleting dns 
txt record for fqdn '" + fqdn + "' record '" + value + "': " + err.Error())
-                       return errors.New("Determining rows affected when 
deleting dns txt record for fqdn '" + fqdn + "' record '" + value + "': " + 
err.Error())
+                       return fmt.Errorf("Determining rows affected when 
deleting dns txt record for fqdn '"+fqdn+"' record '"+value+"': %v", err)
                }
                if rows == 0 {
                        log.Errorf("Zero rows affected when deleting dns txt 
record for fqdn '" + fqdn + "' record '" + value)
@@ -160,9 +161,9 @@ func GenerateAcmeCertificates(w http.ResponseWriter, r 
*http.Request) {
        }
        ctx, _ := context.WithTimeout(r.Context(), AcmeTimeout)
 
-       req := tc.DeliveryServiceLetsEncryptSSLKeysReq{}
+       req := tc.DeliveryServiceAcmeSSLKeysReq{}
        if err := api.Parse(r.Body, nil, &req); err != nil {
-               api.HandleErr(w, r, nil, http.StatusBadRequest, 
errors.New("parsing request: "+err.Error()), nil)
+               api.HandleErr(w, r, nil, http.StatusBadRequest, 
fmt.Errorf("parsing request: %v", err), nil)
                return
        }
        if *req.DeliveryService == "" {
@@ -171,7 +172,7 @@ func GenerateAcmeCertificates(w http.ResponseWriter, r 
*http.Request) {
 
        dsID, cdnName, ok, err := dbhelpers.GetDSIDAndCDNFromName(inf.Tx.Tx, 
*req.DeliveryService)
        if err != nil {
-               api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, 
nil, errors.New("deliveryservice.GenerateLetsEncryptCertificates: getting DS ID 
from name "+err.Error()))
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, 
nil, fmt.Errorf("deliveryservice.GenerateLetsEncryptCertificates: getting DS ID 
from name: %v", err))
                return
        } else if !ok {
                api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, 
errors.New("no DS with name "+*req.DeliveryService), nil)
@@ -186,7 +187,7 @@ func GenerateAcmeCertificates(w http.ResponseWriter, r 
*http.Request) {
 
        _, ok, err = dbhelpers.GetCDNIDFromName(inf.Tx.Tx, tc.CDNName(*req.CDN))
        if err != nil {
-               api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, 
nil, errors.New("checking CDN existence: "+err.Error()))
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, 
nil, fmt.Errorf("checking CDN existence: %v", err))
                return
        } else if !ok {
                api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, 
errors.New("cdn not found with name "+*req.CDN), nil)
@@ -198,9 +199,21 @@ func GenerateAcmeCertificates(w http.ResponseWriter, r 
*http.Request) {
                return
        }
 
-       go GetAcmeCertificates(inf.Config, req, ctx, inf.User, inf.Vault)
+       asyncStatusId, errCode, userErr, sysErr := 
api.InsertAsyncStatus(inf.Tx.Tx, "ACME async job has started.")
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+       }
+
+       go GetAcmeCertificates(inf.Config, req, ctx, inf.User, asyncStatusId, 
inf.Vault)
+
+       var alerts tc.Alerts
+       alerts.AddAlert(tc.Alert{
+               Text:  "Beginning async ACME call for " + *req.DeliveryService 
+ " using " + *req.AuthType + ". This may take a few minutes. Status updates 
can be found here: " + api.CurrentAsyncEndpoint + strconv.Itoa(asyncStatusId),
+               Level: tc.SuccessLevel.String(),
+       })
 
-       api.WriteRespAlert(w, r, tc.SuccessLevel, "Beginning async ACME call 
for "+*req.DeliveryService+" using "+*req.AuthType+". This may take a few 
minutes.")
+       w.Header().Add("Location", 
api.CurrentAsyncEndpoint+strconv.Itoa(asyncStatusId))
+       api.WriteAlerts(w, r, http.StatusAccepted, alerts)
 }
 
 // GenerateLetsEncryptCertificates gets and saves new certificates from Let's 
Encrypt.
@@ -219,9 +232,14 @@ func GenerateLetsEncryptCertificates(w 
http.ResponseWriter, r *http.Request) {
 
        ctx, _ := context.WithTimeout(r.Context(), AcmeTimeout)
 
-       req := tc.DeliveryServiceLetsEncryptSSLKeysReq{}
+       req := tc.DeliveryServiceAcmeSSLKeysReq{}
+       if req.AuthType == nil {
+               req.AuthType = new(string)
+               *req.AuthType = tc.LetsEncryptAuthType
+       }
+
        if err := api.Parse(r.Body, nil, &req); err != nil {
-               api.HandleErr(w, r, nil, http.StatusBadRequest, 
errors.New("parsing request: "+err.Error()), nil)
+               api.HandleErr(w, r, nil, http.StatusBadRequest, 
fmt.Errorf("parsing request: %v", err), nil)
                return
        }
        if *req.DeliveryService == "" {
@@ -230,7 +248,7 @@ func GenerateLetsEncryptCertificates(w http.ResponseWriter, 
r *http.Request) {
 
        dsID, cdnName, ok, err := dbhelpers.GetDSIDAndCDNFromName(inf.Tx.Tx, 
*req.DeliveryService)
        if err != nil {
-               api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, 
nil, errors.New("deliveryservice.GenerateLetsEncryptCertificates: getting DS ID 
from name "+err.Error()))
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, 
nil, fmt.Errorf("deliveryservice.GenerateLetsEncryptCertificates: getting DS ID 
from name: %v", err))
                return
        } else if !ok {
                api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, 
errors.New("no DS with name "+*req.DeliveryService), nil)
@@ -245,7 +263,7 @@ func GenerateLetsEncryptCertificates(w http.ResponseWriter, 
r *http.Request) {
 
        _, ok, err = dbhelpers.GetCDNIDFromName(inf.Tx.Tx, tc.CDNName(*req.CDN))
        if err != nil {
-               api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, 
nil, errors.New("checking CDN existence: "+err.Error()))
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, 
nil, fmt.Errorf("checking CDN existence: %v", err))
                return
        } else if !ok {
                api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, 
errors.New("cdn not found with name "+*req.CDN), nil)
@@ -257,36 +275,65 @@ func GenerateLetsEncryptCertificates(w 
http.ResponseWriter, r *http.Request) {
                return
        }
 
-       go GetAcmeCertificates(inf.Config, req, ctx, inf.User, inf.Vault)
+       asyncStatusId, errCode, userErr, sysErr := 
api.InsertAsyncStatus(inf.Tx.Tx, "ACME async job has started.")
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+       }
 
-       var alerts tc.Alerts
+       go GetAcmeCertificates(inf.Config, req, ctx, inf.User, asyncStatusId, 
inf.Vault)
 
+       var alerts tc.Alerts
        
alerts.AddAlerts(api.CreateDeprecationAlerts(util.StrPtr(API_ACME_GENERATE_LE)))
-
        alerts.AddAlert(tc.Alert{
-               Text:  "Beginning async call to Let's Encrypt for " + 
*req.DeliveryService + ". This may take a few minutes.",
+               Text:  "Beginning async call to Let's Encrypt for " + 
*req.DeliveryService + ". This may take a few minutes. Status updates can be 
found here: " + api.CurrentAsyncEndpoint + strconv.Itoa(asyncStatusId),
                Level: tc.SuccessLevel.String(),
        })
 
-       api.WriteAlerts(w, r, http.StatusOK, alerts)
+       w.Header().Add("Location", 
api.CurrentAsyncEndpoint+strconv.Itoa(asyncStatusId))
+       api.WriteAlerts(w, r, http.StatusAccepted, alerts)
 }
 
 // GetAcmeCertificates gets or creates an ACME account based on the provider, 
then gets new certificates for the delivery service requested and saves them to 
Vault.
-func GetAcmeCertificates(cfg *config.Config, req 
tc.DeliveryServiceLetsEncryptSSLKeysReq, ctx context.Context, currentUser 
*auth.CurrentUser, tv trafficvault.TrafficVault) error {
+func GetAcmeCertificates(cfg *config.Config, req 
tc.DeliveryServiceAcmeSSLKeysReq, ctx context.Context, currentUser 
*auth.CurrentUser, asyncStatusId int, tv trafficvault.TrafficVault) error {
+       defer func() {
+               if err := recover(); err != nil {
+                       db, dbErr := api.GetDB(ctx)
+                       if dbErr != nil {
+                               log.Errorf(*req.DeliveryService+": Error 
getting db for recover async update: %s", dbErr.Error())
+                               log.Errorf("panic: (err: %v) 
stacktrace:\n%s\n", err, util.Stacktrace())
+                               return
+                       }
+
+                       if asyncErr := api.UpdateAsyncStatus(db, 
api.AsyncFailed, "ACME renewal failed.", asyncStatusId, true); asyncErr != nil {
+                               log.Errorf("updating async status for id %v: 
%v", asyncStatusId, asyncErr)
+                       }
+                       log.Errorf("panic: (err: %v) stacktrace:\n%s\n", err, 
util.Stacktrace())
+                       return
+               }
+       }()
 
        db, err := api.GetDB(ctx)
        if err != nil {
                log.Errorf(*req.DeliveryService+": Error getting db: %s", 
err.Error())
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
+               }
                return err
        }
        tx, err := db.Begin()
        if err != nil {
                log.Errorf(*req.DeliveryService+": Error getting tx: %s", 
err.Error())
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
+               }
                return err
        }
        userTx, err := db.Begin()
        if err != nil {
                log.Errorf(*req.DeliveryService+": Error getting userTx: %s", 
err.Error())
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
+               }
                return err
        }
        defer userTx.Commit()
@@ -294,6 +341,9 @@ func GetAcmeCertificates(cfg *config.Config, req 
tc.DeliveryServiceLetsEncryptSS
        logTx, err := db.Begin()
        if err != nil {
                log.Errorf(*req.DeliveryService+": Error getting logTx: %s", 
err.Error())
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
+               }
                return err
        }
        defer logTx.Commit()
@@ -306,10 +356,16 @@ func GetAcmeCertificates(cfg *config.Config, req 
tc.DeliveryServiceLetsEncryptSS
        if err != nil {
                log.Errorf("deliveryservice.GenerateSSLKeys: getting DS ID from 
name " + err.Error())
                api.CreateChangeLogRawTx(api.ApiChange, "DS: 
"+*req.DeliveryService+", ID: "+strconv.Itoa(dsID)+", ACTION: FAILED to add SSL 
keys with "+provider, currentUser, logTx)
-               return errors.New("deliveryservice.GenerateSSLKeys: getting DS 
ID from name " + err.Error())
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
+               }
+               return fmt.Errorf("deliveryservice.GenerateSSLKeys: getting DS 
ID from name: %v", err)
        } else if !ok {
                log.Errorf("no DS with name " + *req.DeliveryService)
                api.CreateChangeLogRawTx(api.ApiChange, "DS: 
"+*req.DeliveryService+", ID: "+strconv.Itoa(dsID)+", ACTION: FAILED to add SSL 
keys with "+provider, currentUser, logTx)
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
+               }
                return errors.New("no DS with name " + *req.DeliveryService)
        }
        tx.Commit()
@@ -317,6 +373,9 @@ func GetAcmeCertificates(cfg *config.Config, req 
tc.DeliveryServiceLetsEncryptSS
        if cfg == nil {
                log.Errorf("acme: config was nil for provider %s", provider)
                api.CreateChangeLogRawTx(api.ApiChange, "DS: 
"+*req.DeliveryService+", ID: "+strconv.Itoa(dsID)+", ACTION: FAILED to add SSL 
keys with "+provider, currentUser, logTx)
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
+               }
                return errors.New("acme: config was nil")
        }
 
@@ -336,8 +395,11 @@ func GetAcmeCertificates(cfg *config.Config, req 
tc.DeliveryServiceLetsEncryptSS
        } else {
                acmeAccount := GetAcmeAccountConfig(cfg, provider)
                if acmeAccount == nil {
-                       log.Errorf("acme: no account information found for %s" 
+ provider)
+                       log.Errorf("acme: no account information found for %s", 
provider)
                        api.CreateChangeLogRawTx(api.ApiChange, "DS: 
"+*req.DeliveryService+", ID: "+strconv.Itoa(dsID)+", ACTION: FAILED to add SSL 
keys with "+provider, currentUser, logTx)
+                       if asycErr := api.UpdateAsyncStatus(db, 
api.AsyncFailed, "ACME renewal failed.", asyncStatusId, true); asycErr != nil {
+                               log.Errorf("updating async status for id %v: 
%v", asyncStatusId, asycErr)
+                       }
                        return errors.New("No acme account information in 
cdn.conf for " + provider)
                }
                account = acmeAccount
@@ -345,15 +407,21 @@ func GetAcmeCertificates(cfg *config.Config, req 
tc.DeliveryServiceLetsEncryptSS
 
        client, err := GetAcmeClient(account, userTx, db)
        if err != nil {
-               log.Errorf("acme: getting acme client for provider %s", 
provider)
+               log.Errorf("acme: getting acme client for provider %s: %v", 
provider, err)
                api.CreateChangeLogRawTx(api.ApiChange, "DS: 
"+*req.DeliveryService+", ID: "+strconv.Itoa(dsID)+", ACTION: FAILED to add SSL 
keys with "+provider, currentUser, logTx)
-               return errors.New("getting acme client: " + err.Error())
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
+               }
+               return fmt.Errorf("getting acme client: %v", err)
        }
 
        priv, err := rsa.GenerateKey(rand.Reader, 2048)
        if err != nil {
                log.Errorf(deliveryService + ": Error generating private key: " 
+ err.Error())
                api.CreateChangeLogRawTx(api.ApiChange, "DS: 
"+*req.DeliveryService+", ID: "+strconv.Itoa(dsID)+", ACTION: FAILED to add SSL 
keys with "+provider, currentUser, logTx)
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
+               }
                return err
        }
        request := certificate.ObtainRequest{
@@ -366,6 +434,9 @@ func GetAcmeCertificates(cfg *config.Config, req 
tc.DeliveryServiceLetsEncryptSS
        if err != nil {
                log.Errorf(deliveryService+": Error obtaining acme certificate 
from %s: %s", provider, err.Error())
                api.CreateChangeLogRawTx(api.ApiChange, "DS: 
"+*req.DeliveryService+", ID: "+strconv.Itoa(dsID)+", ACTION: FAILED to add SSL 
keys with "+provider, currentUser, logTx)
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
+               }
                return err
        }
 
@@ -383,6 +454,9 @@ func GetAcmeCertificates(cfg *config.Config, req 
tc.DeliveryServiceLetsEncryptSS
        if err != nil {
                log.Errorf(deliveryService + ": Error converting private key to 
PEM: " + err.Error())
                api.CreateChangeLogRawTx(api.ApiChange, "DS: 
"+*req.DeliveryService+", ID: "+strconv.Itoa(dsID)+", ACTION: FAILED to add SSL 
keys with "+provider, currentUser, logTx)
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
+               }
                return err
        }
 
@@ -398,23 +472,34 @@ func GetAcmeCertificates(cfg *config.Config, req 
tc.DeliveryServiceLetsEncryptSS
        if err := tv.PutDeliveryServiceSSLKeys(dsSSLKeys, tx); err != nil {
                log.Errorf("Error putting ACME certificate in Traffic Vault: 
%s", err.Error())
                api.CreateChangeLogRawTx(api.ApiChange, "DS: 
"+*req.DeliveryService+", ID: "+strconv.Itoa(dsID)+", ACTION: FAILED to add SSL 
keys with "+provider, currentUser, logTx)
-               return errors.New(deliveryService + ": putting keys in Traffic 
Vault: " + err.Error())
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
+               }
+               return fmt.Errorf(deliveryService+": putting keys in Traffic 
Vault: %v", err)
        }
 
        tx2, err := db.Begin()
        if err != nil {
                log.Errorf("starting sql transaction for delivery service " + 
*req.DeliveryService + ": " + err.Error())
-               return errors.New("starting sql transaction for delivery 
service " + *req.DeliveryService + ": " + err.Error())
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
+               }
+               return fmt.Errorf("starting sql transaction for delivery 
service "+*req.DeliveryService+": %v", err)
        }
 
        if err := updateSSLKeyVersion(*req.DeliveryService, 
req.Version.ToInt64(), tx2); err != nil {
                log.Errorf("updating SSL key version for delivery service '" + 
*req.DeliveryService + "': " + err.Error())
-               return errors.New("updating SSL key version for delivery 
service '" + *req.DeliveryService + "': " + err.Error())
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
+               }
+               return fmt.Errorf("updating SSL key version for delivery 
service '"+*req.DeliveryService+"': %v", err)
        }
        tx2.Commit()
 
        api.CreateChangeLogRawTx(api.ApiChange, "DS: "+*req.DeliveryService+", 
ID: "+strconv.Itoa(dsID)+", ACTION: Added SSL keys with "+provider, 
currentUser, logTx)
-
+       if asycErr := api.UpdateAsyncStatus(db, api.AsyncSucceeded, "ACME 
renewal complete.", asyncStatusId, true); asycErr != nil {
+               log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
+       }
        return nil
 }
 
@@ -542,7 +627,7 @@ func GetAcmeClient(acmeAccount *config.ConfigAcmeAccount, 
userTx *sql.Tx, db *sq
                err = storeAcmeAccountInfo(userTx, myUser.Email, 
string(userKeyPem), myUser.Registration.URI, acmeAccount.AcmeProvider)
                if err != nil {
                        log.Errorf("storing user account info: " + err.Error())
-                       return nil, errors.New("storing user account info: " + 
err.Error())
+                       return nil, fmt.Errorf("storing user account info: %v", 
err)
                }
        }
 
@@ -559,7 +644,7 @@ func ConvertPrivateKeyToKeyPem(userPrivateKey 
*rsa.PrivateKey) ([]byte, error) {
        userKeyBuf := bytes.Buffer{}
        if err := pem.Encode(&userKeyBuf, &pem.Block{Type: "RSA PRIVATE KEY", 
Bytes: userKeyDer}); err != nil {
                log.Errorf("pem-encoding private key: " + err.Error())
-               return nil, errors.New("pem-encoding private key: " + 
err.Error())
+               return nil, fmt.Errorf("pem-encoding private key: %v", err)
        }
        return userKeyBuf.Bytes(), nil
 }
@@ -579,7 +664,7 @@ func getStoredAcmeAccountInfo(tx *sql.Tx, email string, 
provider string) (*AcmeI
                if err == sql.ErrNoRows {
                        return nil, nil
                }
-               return nil, errors.New("getting ACME account record: " + 
err.Error())
+               return nil, fmt.Errorf("getting ACME account record: %v", err)
        }
 
        decodedKeyBlock, _ := pem.Decode([]byte(acmeInfo.Key))
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go 
b/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go
index 6374b59..4b5318e 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/autorenewcerts.go
@@ -129,16 +129,16 @@ func RunAutorenewal(existingCerts []ExistingCerts, cfg 
*config.Config, ctx conte
        db, err := api.GetDB(ctx)
        if err != nil {
                log.Errorf("Error getting db: %s", err.Error())
-               if err = api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); err != nil {
-                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, err)
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
                }
                return
        }
        tx, err := db.Begin()
        if err != nil {
                log.Errorf("Error getting tx: %s", err.Error())
-               if err = api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); err != nil {
-                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, err)
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
                }
                return
        }
@@ -146,8 +146,8 @@ func RunAutorenewal(existingCerts []ExistingCerts, cfg 
*config.Config, ctx conte
        logTx, err := db.Begin()
        if err != nil {
                log.Errorf("Error getting logTx: %s", err.Error())
-               if err = api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); err != nil {
-                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, err)
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncFailed, "ACME 
renewal failed.", asyncStatusId, true); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
                }
                return
        }
@@ -220,7 +220,7 @@ func RunAutorenewal(existingCerts []ExistingCerts, cfg 
*config.Config, ctx conte
                dsExpInfo.AuthType = keyObj.AuthType
 
                if keyObj.AuthType == tc.LetsEncryptAuthType || 
(keyObj.AuthType == tc.SelfSignedCertAuthType && 
cfg.ConfigLetsEncrypt.ConvertSelfSigned) {
-                       req := tc.DeliveryServiceLetsEncryptSSLKeysReq{
+                       req := tc.DeliveryServiceAcmeSSLKeysReq{
                                DeliveryServiceSSLKeysReq: 
tc.DeliveryServiceSSLKeysReq{
                                        HostName:        &keyObj.Hostname,
                                        DeliveryService: 
&keyObj.DeliveryService,
@@ -230,7 +230,7 @@ func RunAutorenewal(existingCerts []ExistingCerts, cfg 
*config.Config, ctx conte
                                },
                        }
 
-                       if err := GetAcmeCertificates(cfg, req, ctx, 
currentUser, tv); err != nil {
+                       if err := GetAcmeCertificates(cfg, req, ctx, 
currentUser, 0, tv); err != nil {
                                dsExpInfo.Error = err
                                errorCount++
                        } else {
@@ -263,8 +263,8 @@ func RunAutorenewal(existingCerts []ExistingCerts, cfg 
*config.Config, ctx conte
 
                }
 
-               if err = api.UpdateAsyncStatus(db, api.AsyncPending, "ACME 
renewal in progress. "+strconv.Itoa(renewedCount)+" certs renewed, 
"+strconv.Itoa(errorCount)+" errors.", asyncStatusId, false); err != nil {
-                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, err)
+               if asycErr := api.UpdateAsyncStatus(db, api.AsyncPending, "ACME 
renewal in progress. "+strconv.Itoa(renewedCount)+" certs renewed, 
"+strconv.Itoa(errorCount)+" errors.", asyncStatusId, false); asycErr != nil {
+                       log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
                }
 
        }
@@ -274,8 +274,8 @@ func RunAutorenewal(existingCerts []ExistingCerts, cfg 
*config.Config, ctx conte
        if errorCount > 0 && renewedCount == 0 {
                asyncStatus = api.AsyncFailed
        }
-       if err = api.UpdateAsyncStatus(db, asyncStatus, "ACME renewal complete. 
"+strconv.Itoa(renewedCount)+" certs renewed, "+strconv.Itoa(errorCount)+" 
errors.", asyncStatusId, true); err != nil {
-               log.Errorf("updating async status for id %v: %v", 
asyncStatusId, err)
+       if asycErr := api.UpdateAsyncStatus(db, asyncStatus, "ACME renewal 
complete. "+strconv.Itoa(renewedCount)+" certs renewed, 
"+strconv.Itoa(errorCount)+" errors.", asyncStatusId, true); asycErr != nil {
+               log.Errorf("updating async status for id %v: %v", 
asyncStatusId, asycErr)
        }
 
        if cfg.SMTP.Enabled && cfg.ConfigAcmeRenewal.SummaryEmail != "" {
diff --git a/traffic_portal/app/src/common/api/DeliveryServiceSslKeysService.js 
b/traffic_portal/app/src/common/api/DeliveryServiceSslKeysService.js
index 6c1ea81..2870f5d 100644
--- a/traffic_portal/app/src/common/api/DeliveryServiceSslKeysService.js
+++ b/traffic_portal/app/src/common/api/DeliveryServiceSslKeysService.js
@@ -21,33 +21,33 @@ var DeliveryServiceSslKeysService = function($http, 
locationUtils, messageModel,
     this.successMessage = 'SSL Keys generated and updated for ';
     this.acmeSuccessMessage = 'ACME call has been made successfully. This may 
take a few minutes. Please watch for a notification in the Change Log. Delivery 
Service = ';
 
-       this.generateSslKeys = function(deliveryService, sslKeys, 
generateSslKeyForm) {
-                return this.generateSslKeysBase(deliveryService, sslKeys, 
generateSslKeyForm, 'deliveryservices/sslkeys/generate', this.successMessage);
-       };
-
-    this.generateSslKeysWithLetsEncrypt = function(deliveryService, sslKeys, 
generateSslKeyForm) {
-        return this.generateSslKeysBase(deliveryService, sslKeys, 
generateSslKeyForm, 'deliveryservices/sslkeys/generate/acme', 'Lets Encrypt: ' 
+ this.acmeSuccessMessage);
+    this.generateSslKeys = function(deliveryService, sslKeys, 
generateSslKeyForm) {
+        return this.generateSslKeysBase(deliveryService, sslKeys, 
generateSslKeyForm, 'deliveryservices/sslkeys/generate', this.successMessage);
     };
 
-       this.generateSslKeysWithAcme = function(deliveryService, sslKeys, 
generateSslKeyForm, provider) {
-               return this.generateSslKeysBase(deliveryService, sslKeys, 
generateSslKeyForm, 'deliveryservices/sslkeys/generate/acme', provider + ": " + 
this.acmeSuccessMessage);
-       };
+    this.generateSslKeysWithAcme = function(deliveryService, sslKeys, 
generateSslKeyForm) {
+        return this.generateSslKeysBase(deliveryService, sslKeys, 
generateSslKeyForm, 'deliveryservices/sslkeys/generate/acme', null);
+    };
 
-       this.generateSslKeysBase = function(deliveryService, sslKeys, 
generateSslKeyForm, endpoint, message) {
+    this.generateSslKeysBase = function(deliveryService, sslKeys, 
generateSslKeyForm, endpoint, message) {
         if (sslKeys.hasOwnProperty('version')){
             generateSslKeyForm.version = parseInt(sslKeys.version, 10) + 1;
         } else {
             generateSslKeyForm.version = 1;
         }
 
-               generateSslKeyForm.cdn = deliveryService.cdnName;
-               generateSslKeyForm.deliveryservice = deliveryService.xmlId;
-               generateSslKeyForm.key = deliveryService.xmlId;
-               generateSslKeyForm.authType = sslKeys.authType;
+        generateSslKeyForm.cdn = deliveryService.cdnName;
+        generateSslKeyForm.deliveryservice = deliveryService.xmlId;
+        generateSslKeyForm.key = deliveryService.xmlId;
+        generateSslKeyForm.authType = sslKeys.authType;
 
         return $http.post(ENV.api['root'] + endpoint, generateSslKeyForm).then(
             function(result) {
-               messageModel.setMessages([{level: 'success', text: message + 
deliveryService.xmlId}], true);
+                if (message === null) {
+                    messageModel.setMessages(result.data.alerts, true);
+                } else {
+                    messageModel.setMessages([{level: 'success', text: message 
+ deliveryService.xmlId}], true);
+                }
                 return result.data.response;
             },
             function(err) {
@@ -57,24 +57,24 @@ var DeliveryServiceSslKeysService = function($http, 
locationUtils, messageModel,
                 throw err;
             }
         );
-       };
+    };
 
-       this.renewCert = function(deliveryService) {
-               return $http.post(ENV.api['root'] + "deliveryservices/xmlId/" + 
deliveryService.xmlId + "/sslkeys/renew").then(
-                       function(result) {
-                               messageModel.setMessages(result.data.alerts, 
false);
-                               return result.data.response;
-                       },
-                       function(err) {
-                               if (err.data && err.data.alerts) {
-                                       
messageModel.setMessages(err.data.alerts, false);
-                               }
-                               throw err;
-                       }
-               );
-       };
+    this.renewCert = function(deliveryService) {
+        return $http.post(ENV.api['root'] + "deliveryservices/xmlId/" + 
deliveryService.xmlId + "/sslkeys/renew").then(
+            function(result) {
+                messageModel.setMessages(result.data.alerts, false);
+                return result.data.response;
+            },
+            function(err) {
+                if (err.data && err.data.alerts) {
+                    messageModel.setMessages(err.data.alerts, false);
+                }
+                throw err;
+            }
+        );
+    };
 
-       this.addSslKeys = function(sslKeys, deliveryService) {
+    this.addSslKeys = function(sslKeys, deliveryService) {
 
         sslKeys.key = deliveryService.xmlId;
         if (sslKeys.hasOwnProperty('version')){
@@ -98,9 +98,9 @@ var DeliveryServiceSslKeysService = function($http, 
locationUtils, messageModel,
                 throw err;
             }
         );
-       };
+    };
 
-       this.getSslKeys = function(deliveryService) {
+    this.getSslKeys = function(deliveryService) {
         return $http.get(ENV.api['root'] + "deliveryservices/xmlId/" + 
deliveryService.xmlId + "/sslkeys", {params: {decode: "true"}}).then(
             function(result) {
                 return result.data.response;
@@ -112,18 +112,18 @@ var DeliveryServiceSslKeysService = function($http, 
locationUtils, messageModel,
                 throw err;
             }
         );
-       };
+    };
 
-       this.getAcmeProviders = function() {
-               return $http.get(ENV.api['root'] + 
'acme_accounts/providers').then(
-                       function (result) {
-                               return result.data.response;
-                       },
-                       function (err) {
-                               throw err;
-                       }
-               );
-       };
+    this.getAcmeProviders = function() {
+        return $http.get(ENV.api['root'] + 'acme_accounts/providers').then(
+            function (result) {
+                return result.data.response;
+            },
+            function (err) {
+                throw err;
+            }
+        );
+    };
 };
 
 DeliveryServiceSslKeysService.$inject = ['$http', 'locationUtils', 
'messageModel', 'ENV'];
diff --git 
a/traffic_portal/app/src/common/modules/form/deliveryServiceSslKeys/generate/FormGenerateDeliveryServiceSslKeysController.js
 
b/traffic_portal/app/src/common/modules/form/deliveryServiceSslKeys/generate/FormGenerateDeliveryServiceSslKeysController.js
index ceee39d..d8f3e0c 100644
--- 
a/traffic_portal/app/src/common/modules/form/deliveryServiceSslKeys/generate/FormGenerateDeliveryServiceSslKeysController.js
+++ 
b/traffic_portal/app/src/common/modules/form/deliveryServiceSslKeys/generate/FormGenerateDeliveryServiceSslKeysController.js
@@ -357,7 +357,7 @@ var FormGenerateDeliveryServiceSslKeysController = 
function(deliveryService, ssl
         });
         modalInstance.result.then(function() {
             sslKeys.authType = $scope.acmeProvider;
-            
deliveryServiceSslKeysService.generateSslKeysWithAcme(deliveryService, sslKeys, 
sslRequest, $scope.acmeProvider).then(
+            
deliveryServiceSslKeysService.generateSslKeysWithAcme(deliveryService, sslKeys, 
sslRequest).then(
                 function() {
                     locationUtils.navigateToPath('/delivery-services/' + 
deliveryService.id + '/ssl-keys');
                 });

Reply via email to