func doDNSSECKeyRefresh()

in traffic_ops/traffic_ops_golang/cdn/dnssecrefresh.go [132:355]


func doDNSSECKeyRefresh(tx *sql.Tx, asyncDB *sqlx.DB, tv trafficvault.TrafficVault, jobID int, user *auth.CurrentUser) {
	doCommit := true
	defer func() {
		if doCommit {
			tx.Commit()
		} else {
			tx.Rollback()
		}
	}()
	defer unsetInDNSSECKeyRefresh()

	cdnDNSSECKeyParams, err := getDNSSECKeyRefreshParams(tx)
	if err != nil {
		log.Errorln("refreshing DNSSEC Keys: getting cdn parameters: " + err.Error())
		doCommit = false
		if asyncErr := api.UpdateAsyncStatus(asyncDB, api.AsyncFailed, "DNSSEC refresh failed", jobID, true); asyncErr != nil {
			log.Errorf("updating async status for id %d: %v", jobID, asyncErr)
		}
		return
	}
	cdns := []string{}
	for _, inf := range cdnDNSSECKeyParams {
		if inf.DNSSECEnabled {
			cdns = append(cdns, string(inf.CDNName))
		}
	}
	// TODO change to return a slice, map is slow and unnecessary
	dsInfo, err := getDNSSECKeyRefreshDSInfo(tx, cdns)
	if err != nil {
		log.Errorln("refreshing DNSSEC Keys: getting ds info: " + err.Error())
		doCommit = false
		if asyncErr := api.UpdateAsyncStatus(asyncDB, api.AsyncFailed, "DNSSEC refresh failed", jobID, true); asyncErr != nil {
			log.Errorf("updating async status for id %d: %v", jobID, asyncErr)
		}
		return
	}
	dses := []string{}
	for ds, _ := range dsInfo {
		dses = append(dses, string(ds))
	}

	dsMatchlists, err := deliveryservice.GetDeliveryServicesMatchLists(dses, tx)
	if err != nil {
		log.Errorln("refreshing DNSSEC Keys: getting ds matchlists: " + err.Error())
		doCommit = false
		if asyncErr := api.UpdateAsyncStatus(asyncDB, api.AsyncFailed, "DNSSEC refresh failed", jobID, true); asyncErr != nil {
			log.Errorf("updating async status for id %d: %v", jobID, asyncErr)
		}
		return
	}
	exampleURLs := map[tc.DeliveryServiceName][]string{}
	for ds, inf := range dsInfo {
		exampleURLs[ds] = deliveryservice.MakeExampleURLs(inf.Protocol, inf.Type, inf.RoutingName, dsMatchlists[string(ds)], inf.CDNDomain)
	}

	errCount := 0
	updateCount := 0
	putErr := false
	for _, cdnInf := range cdnDNSSECKeyParams {
		keys, ok, err := tv.GetDNSSECKeys(string(cdnInf.CDNName), tx, context.Background()) // TODO get all in a map beforehand
		if err != nil {
			log.Warnln("refreshing DNSSEC Keys: getting cdn '" + string(cdnInf.CDNName) + "' keys from Traffic Vault, skipping: " + err.Error())
			continue
		}
		if !ok {
			log.Warnln("refreshing DNSSEC Keys: cdn '" + string(cdnInf.CDNName) + "' has no keys in Traffic Vault, skipping")
			continue
		}

		ttl := DNSSECKeyRefreshDefaultTTL
		if cdnInf.TLDTTLsDNSKEY != nil {
			ttl = time.Duration(*cdnInf.TLDTTLsDNSKEY) * time.Second
		}

		genMultiplier := DNSSECKeyRefreshDefaultGenerationMultiplier
		if cdnInf.DNSKEYGenerationMultiplier != nil {
			genMultiplier = *cdnInf.DNSKEYGenerationMultiplier
		}

		effectiveMultiplier := DNSSECKeyRefreshDefaultEffectiveMultiplier
		if cdnInf.DNSKEYEffectiveMultiplier != nil {
			effectiveMultiplier = *cdnInf.DNSKEYEffectiveMultiplier
		}

		nowPlusTTL := time.Now().Add(ttl * time.Duration(genMultiplier)) // "key_expiration" in the Perl this was transliterated from

		defaultKSKExpiration := DNSSECKeyRefreshDefaultKSKExpiration
		for _, key := range keys[string(cdnInf.CDNName)].KSK {
			if key.Status != tc.DNSSECKeyStatusNew {
				continue
			}
			defaultKSKExpiration = time.Unix(key.ExpirationDateUnix, 0).Sub(time.Unix(key.InceptionDateUnix, 0))
			break
		}

		defaultZSKExpiration := DNSSECKeyRefreshDefaultZSKExpiration
		for _, key := range keys[string(cdnInf.CDNName)].ZSK {
			if key.Status != tc.DNSSECKeyStatusNew {
				continue
			}
			expiration := time.Unix(key.ExpirationDateUnix, 0)
			inception := time.Unix(key.InceptionDateUnix, 0)
			defaultZSKExpiration = expiration.Sub(inception)

			if expiration.After(nowPlusTTL) {
				continue
			}
			log.Infoln("The ZSK keys for '" + string(cdnInf.CDNName) + "' are expired! Regenerating them now.")
			effectiveDate := expiration.Add(ttl * time.Duration(effectiveMultiplier) * -1) // -1 to subtract
			isKSK := false
			cdnDNSDomain := cdnInf.CDNDomain + "."
			newKeys, err := regenExpiredKeys(isKSK, cdnDNSDomain, keys[string(cdnInf.CDNName)], effectiveDate, false, false)
			if err != nil {
				log.Errorln("refreshing DNSSEC Keys: regenerating expired ZSK keys: " + err.Error())
				errCount++
			} else {
				keys[string(cdnInf.CDNName)] = newKeys
				updateCount++
			}
		}

		for _, ds := range dsInfo {
			if ds.CDNName != cdnInf.CDNName {
				continue
			}
			if t := ds.Type; !t.UsesDNSSECKeys() {
				continue
			}

			dsKeys, dsKeysExist := keys[string(ds.DSName)]
			if !dsKeysExist {
				log.Infoln("Keys do not exist for ds '" + string(ds.DSName) + "'")

				cdnKeys, ok := keys[string(ds.CDNName)]
				if !ok {
					log.Errorln("refreshing DNSSEC Keys: cdn has no keys, cannot create ds keys")
					errCount++
					continue
				}

				overrideTTL := false
				dsKeys, err := deliveryservice.CreateDNSSECKeys(exampleURLs[ds.DSName], cdnKeys, defaultKSKExpiration, defaultZSKExpiration, ttl, overrideTTL)
				if err != nil {
					log.Errorln("refreshing DNSSEC Keys: creating missing ds keys: " + err.Error())
					errCount++
				}
				keys[string(ds.DSName)] = dsKeys
				updateCount++
				continue
			}

			for _, key := range dsKeys.KSK {
				if key.Status != tc.DNSSECKeyStatusNew {
					continue
				}
				expiration := time.Unix(key.ExpirationDateUnix, 0)
				if expiration.After(nowPlusTTL) {
					continue
				}
				log.Infoln("The KSK keys for '" + ds.DSName + "' are expired! Regenerating them now.")
				effectiveDate := expiration.Add(ttl * time.Duration(effectiveMultiplier) * -1) // -1 to subtract
				isKSK := true
				newKeys, err := regenExpiredKeys(isKSK, string(ds.DSName), dsKeys, effectiveDate, false, false)
				if err != nil {
					log.Errorln("refreshing DNSSEC Keys: regenerating expired KSK keys for ds '" + string(ds.DSName) + "': " + err.Error())
					errCount++
				} else {
					keys[string(ds.DSName)] = newKeys
					updateCount++
				}
			}

			for _, key := range dsKeys.ZSK {
				if key.Status != tc.DNSSECKeyStatusNew {
					continue
				}
				expiration := time.Unix(key.ExpirationDateUnix, 0)
				if expiration.After(nowPlusTTL) {
					continue
				}
				log.Infoln("The ZSK keys for '" + ds.DSName + "' are expired! Regenerating them now.")
				effectiveDate := expiration.Add(ttl * time.Duration(effectiveMultiplier) * -1) // -1 to subtract
				isKSK := false
				newKeys, err := regenExpiredKeys(isKSK, string(ds.DSName), dsKeys, effectiveDate, false, false)
				if err != nil {
					log.Errorln("refreshing DNSSEC Keys: regenerating expired ZSK keys for ds '" + string(ds.DSName) + "': " + err.Error())
					errCount++
				} else {
					if existingNewKeys, ok := keys[string(ds.DSName)]; ok {
						existingNewKeys.ZSK = newKeys.ZSK
						newKeys = existingNewKeys
					}
					keys[string(ds.DSName)] = newKeys
					updateCount++
				}
			}
		}
		if updateCount > 0 {
			if err := tv.PutDNSSECKeys(string(cdnInf.CDNName), keys, tx, context.Background()); err != nil {
				log.Errorln("refreshing DNSSEC Keys: putting keys into Traffic Vault for cdn '" + string(cdnInf.CDNName) + "': " + err.Error())
				putErr = true
			}
		}
	}
	clMsg := fmt.Sprintf("Refreshed %d DNSSEC keys", updateCount)
	status := api.AsyncSucceeded
	msg := fmt.Sprintf("DNSSEC refresh completed successfully (%d keys were updated)", updateCount)
	if putErr {
		status = api.AsyncFailed
		msg = fmt.Sprintf("DNSSEC refresh failed (attempted to update %d keys, but an error occurred while attempting to store in Traffic Vault)", updateCount)
		clMsg = fmt.Sprintf("Attempted to refresh %d DNSSEC keys, but an error occurred while attempting to store in Traffic Vault", updateCount)
	} else if errCount > 0 {
		status = api.AsyncFailed
		msg = fmt.Sprintf("DNSSEC refresh failed (updated %d keys, but %d errors occurred)", updateCount, errCount)
		clMsg = fmt.Sprintf("Refreshed %d DNSSEC keys, but %d errors occurred", updateCount, errCount)
	}
	if updateCount > 0 || errCount > 0 || putErr {
		api.CreateChangeLogRawTx(api.ApiChange, clMsg, user, tx)
	}
	if asyncErr := api.UpdateAsyncStatus(asyncDB, status, msg, jobID, true); asyncErr != nil {
		log.Errorf("updating async status for id %d: %v", jobID, asyncErr)
	}
	log.Infoln("Done refreshing DNSSEC keys")
}