You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@trafficcontrol.apache.org by GitBox <gi...@apache.org> on 2018/11/26 20:02:27 UTC

[GitHub] ezelkow1 closed pull request #3042: Adding backup file support for the crconfig and tmconfig files in Traffic Monitor

ezelkow1 closed pull request #3042: Adding backup file support for the crconfig and tmconfig files in Traffic Monitor
URL: https://github.com/apache/trafficcontrol/pull/3042
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/docs/source/development/traffic_monitor.rst b/docs/source/development/traffic_monitor.rst
index 4be0fdb59..54fc9b52f 100644
--- a/docs/source/development/traffic_monitor.rst
+++ b/docs/source/development/traffic_monitor.rst
@@ -192,6 +192,10 @@ Shared Data
 -----------
 Processed and aggregated data must be shared between the end of the stat and health processing pipelines, and HTTP requests. The CSP paradigm of idiomatic Go does not work efficiently with storing and sharing state. While not idiomatic Go, shared mutexed data structures are faster and simpler than CSP manager microthreads for each data object. Traffic Monitor has many thread-safe shared data types and objects. All shared data objects can be seen in ``manager/manager.go:Start()``, where they are created and passed to the various pipeline stage microthreads that need them. Their respective types all include the word ``Threadsafe``, and can be found in ``traffic_monitor/threadsafe/`` as well as, for dependency reasons, various appropriate directories. Currently, all thread-safe shared data types use mutexes. In the future, these could be changed to lock-free or wait-free structures, if the performance needs outweighed the readability and correctness costs. They could also easily be changed to internally be manager microthreads and channels, if being idiomatic were deemed more important than readability or performance.
 
+Disk Backup
+------------
+The traffic monitor config and CR config are both stored as backup files (tmconfig.backup and crconfig.backup or what ever you set the values to in the config file). This allows the monitor to come up and continue serving even if traffic ops is down.  These files are updated any time a valid config is received from traffic ops, so if traffic ops goes down and the monitor is restarted it can still serve the previous data.  These files can also be manually edited and the changes will be reloaded in to traffic monitor so that if traffic ops is down or unreachable for an extended period of time manual updates can be done.
+
 Formatting Conventions
 ======================
 Go code should be formatted with ``gofmt``. See also ``CONTRIBUTING.md``.
diff --git a/traffic_monitor/config/config.go b/traffic_monitor/config/config.go
index c64d250b1..14e7651ea 100644
--- a/traffic_monitor/config/config.go
+++ b/traffic_monitor/config/config.go
@@ -40,33 +40,41 @@ const (
 	LogLocationNull = "null"
 	//StaticFileDir is the directory that contains static html and js files.
 	StaticFileDir = "/opt/traffic_monitor/static/"
+	//CrConfigBackupFile is the default file name to store the last crconfig
+	CrConfigBackupFile = "crconfig.backup"
+	//TmConfigBackupFile is the default file name to store the last tmconfig
+	TmConfigBackupFile = "tmconfig.backup"
 )
 
 // Config is the configuration for the application. It includes myriad data, such as polling intervals and log locations.
 type Config struct {
-	CacheHealthPollingInterval   time.Duration `json:"-"`
-	CacheStatPollingInterval     time.Duration `json:"-"`
-	MonitorConfigPollingInterval time.Duration `json:"-"`
-	HTTPTimeout                  time.Duration `json:"-"`
-	PeerPollingInterval          time.Duration `json:"-"`
-	PeerOptimistic               bool          `json:"peer_optimistic"`
-	MaxEvents                    uint64        `json:"max_events"`
-	MaxStatHistory               uint64        `json:"max_stat_history"`
-	MaxHealthHistory             uint64        `json:"max_health_history"`
-	HealthFlushInterval          time.Duration `json:"-"`
-	StatFlushInterval            time.Duration `json:"-"`
-	LogLocationError             string        `json:"log_location_error"`
-	LogLocationWarning           string        `json:"log_location_warning"`
-	LogLocationInfo              string        `json:"log_location_info"`
-	LogLocationDebug             string        `json:"log_location_debug"`
-	LogLocationEvent             string        `json:"log_location_event"`
-	ServeReadTimeout             time.Duration `json:"-"`
-	ServeWriteTimeout            time.Duration `json:"-"`
-	HealthToStatRatio            uint64        `json:"health_to_stat_ratio"`
-	StaticFileDir                string        `json:"static_file_dir"`
-	CRConfigHistoryCount         uint64        `json:"crconfig_history_count"`
-	TrafficOpsMinRetryInterval   time.Duration `json:"-"`
-	TrafficOpsMaxRetryInterval   time.Duration `json:"-"`
+	CacheHealthPollingInterval    time.Duration `json:"-"`
+	CacheStatPollingInterval      time.Duration `json:"-"`
+	MonitorConfigPollingInterval  time.Duration `json:"-"`
+	HTTPTimeout                   time.Duration `json:"-"`
+	PeerPollingInterval           time.Duration `json:"-"`
+	PeerOptimistic                bool          `json:"peer_optimistic"`
+	MaxEvents                     uint64        `json:"max_events"`
+	MaxStatHistory                uint64        `json:"max_stat_history"`
+	MaxHealthHistory              uint64        `json:"max_health_history"`
+	HealthFlushInterval           time.Duration `json:"-"`
+	StatFlushInterval             time.Duration `json:"-"`
+	LogLocationError              string        `json:"log_location_error"`
+	LogLocationWarning            string        `json:"log_location_warning"`
+	LogLocationInfo               string        `json:"log_location_info"`
+	LogLocationDebug              string        `json:"log_location_debug"`
+	LogLocationEvent              string        `json:"log_location_event"`
+	ServeReadTimeout              time.Duration `json:"-"`
+	ServeWriteTimeout             time.Duration `json:"-"`
+	HealthToStatRatio             uint64        `json:"health_to_stat_ratio"`
+	StaticFileDir                 string        `json:"static_file_dir"`
+	CRConfigHistoryCount          uint64        `json:"crconfig_history_count"`
+	TrafficOpsMinRetryInterval    time.Duration `json:"-"`
+	TrafficOpsMaxRetryInterval    time.Duration `json:"-"`
+	CrConfigBackupFile            string        `json:"crconfig_backup_file"`
+	TmConfigBackupFile            string        `json:"tmconfig_backup_file"`
+	TrafficOpsFileFallbackRetries uint64        `json:"-"`
+	FileRetryTime                 time.Duration `json:"-"`
 }
 
 func (c Config) ErrorLog() log.LogLocation   { return log.LogLocation(c.LogLocationError) }
@@ -77,29 +85,33 @@ func (c Config) EventLog() log.LogLocation   { return log.LogLocation(c.LogLocat
 
 // DefaultConfig is the default configuration for the application, if no configuration file is given, or if a given config setting doesn't exist in the config file.
 var DefaultConfig = Config{
-	CacheHealthPollingInterval:   6 * time.Second,
-	CacheStatPollingInterval:     6 * time.Second,
-	MonitorConfigPollingInterval: 5 * time.Second,
-	HTTPTimeout:                  2 * time.Second,
-	PeerPollingInterval:          5 * time.Second,
-	PeerOptimistic:               true,
-	MaxEvents:                    200,
-	MaxStatHistory:               5,
-	MaxHealthHistory:             5,
-	HealthFlushInterval:          200 * time.Millisecond,
-	StatFlushInterval:            200 * time.Millisecond,
-	LogLocationError:             LogLocationStderr,
-	LogLocationWarning:           LogLocationStdout,
-	LogLocationInfo:              LogLocationNull,
-	LogLocationDebug:             LogLocationNull,
-	LogLocationEvent:             LogLocationStdout,
-	ServeReadTimeout:             10 * time.Second,
-	ServeWriteTimeout:            10 * time.Second,
-	HealthToStatRatio:            4,
-	StaticFileDir:                StaticFileDir,
-	CRConfigHistoryCount:         20000,
-	TrafficOpsMinRetryInterval:   100 * time.Millisecond,
-	TrafficOpsMaxRetryInterval:   60000 * time.Millisecond,
+	CacheHealthPollingInterval:    6 * time.Second,
+	CacheStatPollingInterval:      6 * time.Second,
+	MonitorConfigPollingInterval:  5 * time.Second,
+	HTTPTimeout:                   2 * time.Second,
+	PeerPollingInterval:           5 * time.Second,
+	PeerOptimistic:                true,
+	MaxEvents:                     200,
+	MaxStatHistory:                5,
+	MaxHealthHistory:              5,
+	HealthFlushInterval:           200 * time.Millisecond,
+	StatFlushInterval:             200 * time.Millisecond,
+	LogLocationError:              LogLocationStderr,
+	LogLocationWarning:            LogLocationStdout,
+	LogLocationInfo:               LogLocationNull,
+	LogLocationDebug:              LogLocationNull,
+	LogLocationEvent:              LogLocationStdout,
+	ServeReadTimeout:              10 * time.Second,
+	ServeWriteTimeout:             10 * time.Second,
+	HealthToStatRatio:             4,
+	StaticFileDir:                 StaticFileDir,
+	CRConfigHistoryCount:          20000,
+	TrafficOpsMinRetryInterval:    100 * time.Millisecond,
+	TrafficOpsMaxRetryInterval:    60000 * time.Millisecond,
+	CrConfigBackupFile:            CrConfigBackupFile,
+	TmConfigBackupFile:            TmConfigBackupFile,
+	TrafficOpsFileFallbackRetries: 7,
+	FileRetryTime:                 1 * time.Second,
 }
 
 // MarshalJSON marshals custom millisecond durations. Aliasing inspired by http://choly.ca/post/go-json-marshalling/
@@ -117,6 +129,7 @@ func (c *Config) MarshalJSON() ([]byte, error) {
 		StatFlushIntervalMs            uint64 `json:"stat_flush_interval_ms"`
 		ServeReadTimeoutMs             uint64 `json:"serve_read_timeout_ms"`
 		ServeWriteTimeoutMs            uint64 `json:"serve_write_timeout_ms"`
+		FileRetryTimeMs                uint64 `json:"file_retry_timeout_ms"`
 		*Alias
 	}{
 		CacheHealthPollingIntervalMs:   uint64(c.CacheHealthPollingInterval / time.Millisecond),
@@ -127,6 +140,7 @@ func (c *Config) MarshalJSON() ([]byte, error) {
 		PeerOptimistic:                 bool(true),
 		HealthFlushIntervalMs:          uint64(c.HealthFlushInterval / time.Millisecond),
 		StatFlushIntervalMs:            uint64(c.StatFlushInterval / time.Millisecond),
+		FileRetryTimeMs:                uint64(c.FileRetryTime / time.Millisecond),
 		Alias:                          (*Alias)(c),
 	})
 }
@@ -147,6 +161,7 @@ func (c *Config) UnmarshalJSON(data []byte) error {
 		ServeWriteTimeoutMs            *uint64 `json:"serve_write_timeout_ms"`
 		TrafficOpsMinRetryIntervalMs   *uint64 `json:"traffic_ops_min_retry_interval_ms"`
 		TrafficOpsMaxRetryIntervalMs   *uint64 `json:"traffic_ops_max_retry_interval_ms"`
+		FileRetryTimeMs                *uint64 `json:"file_retry_timeout_ms"`
 		*Alias
 	}{
 		Alias: (*Alias)(c),
@@ -192,6 +207,9 @@ func (c *Config) UnmarshalJSON(data []byte) error {
 	if aux.TrafficOpsMaxRetryIntervalMs != nil {
 		c.TrafficOpsMaxRetryInterval = time.Duration(*aux.TrafficOpsMaxRetryIntervalMs) * time.Millisecond
 	}
+	if aux.FileRetryTimeMs != nil {
+		c.FileRetryTime = time.Duration(*aux.FileRetryTimeMs) * time.Millisecond
+	}
 	return nil
 }
 
diff --git a/traffic_monitor/manager/opsconfig.go b/traffic_monitor/manager/opsconfig.go
index 7b34bcd25..9db8a7270 100644
--- a/traffic_monitor/manager/opsconfig.go
+++ b/traffic_monitor/manager/opsconfig.go
@@ -138,6 +138,7 @@ func StartOpsConfigManager(
 		trafficOpsRequestTimeout := time.Second * time.Duration(10)
 		var realToSession *to.Session
 		var toAddr net.Addr
+		var conTest uint64
 
 		// fixed an issue here where traffic_monitor loops forever, doing nothing useful if traffic_ops is down,
 		// and would never logging in again.  since traffic_monitor  is just starting up here, keep retrying until traffic_ops is reachable and a session can be established.
@@ -150,26 +151,43 @@ func StartOpsConfigManager(
 		for {
 			realToSession, toAddr, err = to.LoginWithAgent(newOpsConfig.Url, newOpsConfig.Username, newOpsConfig.Password, newOpsConfig.Insecure, staticAppData.UserAgent, useCache, trafficOpsRequestTimeout)
 			if err != nil {
+				conTest++
 				handleErr(fmt.Errorf("MonitorConfigPoller: error instantiating Session with traffic_ops (%v): %s\n", toAddr, err))
 				duration := backoff.BackoffDuration()
 				log.Errorf("retrying in %v\n", duration)
 				time.Sleep(duration)
-				continue
+				if conTest >= cfg.TrafficOpsFileFallbackRetries {
+					if toSession.FileSessionExists() {
+						log.Errorf("retry limit hit, using file")
+						realToSession = to.NewNoAuthSession(newOpsConfig.Url, true, staticAppData.UserAgent, false, -1)
+						toSession.Set(realToSession)
+						toSession.FileSessionSet(true)
+						conTest = 0
+						break
+					}
+				}
 			} else {
 				toSession.Set(realToSession)
+				toSession.FileSessionSet(false)
 				break
 			}
 		}
 
-		if cdn, err := getMonitorCDN(realToSession, staticAppData.Hostname); err != nil {
-			handleErr(fmt.Errorf("getting CDN name from Traffic Ops, using config CDN '%s': %s\n", newOpsConfig.CdnName, err))
-		} else {
-			if newOpsConfig.CdnName != "" && newOpsConfig.CdnName != cdn {
-				log.Warnf("%s Traffic Ops CDN '%s' doesn't match config CDN '%s' - using Traffic Ops CDN\n", staticAppData.Hostname, cdn, newOpsConfig.CdnName)
+		go startOpsConfigSessionPoller(realToSession, toSession, newOpsConfig, staticAppData, useCache, trafficOpsRequestTimeout, opsConfigChangeSubscribers, toChangeSubscribers, cfg.FileRetryTime)
+
+		if realToSession != nil {
+			if cdn, err := getMonitorCDN(realToSession, staticAppData.Hostname); err != nil {
+				handleErr(fmt.Errorf("getting CDN name from Traffic Ops, using config CDN '%s': %s\n", newOpsConfig.CdnName, err))
+			} else {
+				if newOpsConfig.CdnName != "" && newOpsConfig.CdnName != cdn {
+					log.Warnf("%s Traffic Ops CDN '%s' doesn't match config CDN '%s' - using Traffic Ops CDN\n", staticAppData.Hostname, cdn, newOpsConfig.CdnName)
+				}
+				newOpsConfig.CdnName = cdn
 			}
-			newOpsConfig.CdnName = cdn
 		}
 
+		//TODO Move all this to its own function so it can be called during polls?
+
 		// fixed an issue when traffic_monitor receives corrupt data, CRConfig, from traffic_ops.
 		// Will loop and retry until a good CRConfig is received from traffic_ops
 		backoff.Reset()
@@ -190,7 +208,9 @@ func StartOpsConfigManager(
 			go func(s chan<- handler.OpsConfig) { s <- newOpsConfig }(subscriber)
 		}
 		for _, subscriber := range toChangeSubscribers {
-			go func(s chan<- towrap.ITrafficOpsSession) { s <- toSession }(subscriber)
+			if toSession != nil {
+				go func(s chan<- towrap.ITrafficOpsSession) { s <- toSession }(subscriber)
+			}
 		}
 	}
 
@@ -205,6 +225,64 @@ func StartOpsConfigManager(
 	return opsConfig, nil
 }
 
+// startOpsConfigSession Poller monitors the to session to switch between file and live based
+func startOpsConfigSessionPoller(realtoSession *to.Session, toSession towrap.ITrafficOpsSession, newOpsConfig handler.OpsConfig, staticAppData config.StaticAppData, useCache bool, trafficOpsRequestTimeout time.Duration, opsConfigChangeSubscribers []chan<- handler.OpsConfig,
+	toChangeSubscribers []chan<- towrap.ITrafficOpsSession, interval time.Duration) {
+
+	tick := time.NewTicker(interval)
+	defer tick.Stop()
+	lastTime := time.Now()
+	for {
+		select {
+		case <-tick.C:
+			realInterval := time.Now().Sub(lastTime)
+			if realInterval > interval+(time.Millisecond*100) {
+				log.Debugf("Intended Duration: %v Actual Duration: %v\n", interval, realInterval)
+			}
+			if toSession.FileSessionGet() {
+				realSession, _, err := to.LoginWithAgent(newOpsConfig.Url, newOpsConfig.Username, newOpsConfig.Password, newOpsConfig.Insecure, staticAppData.UserAgent, useCache, time.Second*time.Duration(10))
+				if err == nil {
+					realtoSession = realSession
+					toSession.Set(realSession)
+					toSession.FileSessionSet(false)
+
+					log.Errorf("TO login accepted and currently using disk, switching to URL")
+
+					for _, subscriber := range opsConfigChangeSubscribers {
+						go func(s chan<- handler.OpsConfig) { s <- newOpsConfig }(subscriber)
+					}
+					for _, subscriber := range toChangeSubscribers {
+						if toSession != nil {
+							go func(s chan<- towrap.ITrafficOpsSession) { s <- toSession }(subscriber)
+						}
+					}
+
+				}
+			} else {
+				//ping to and switch to disk if down?
+				realSession, _, err := to.LoginWithAgent(newOpsConfig.Url, newOpsConfig.Username, newOpsConfig.Password, newOpsConfig.Insecure, staticAppData.UserAgent, useCache, time.Second*time.Duration(10))
+				if err != nil && toSession.FileSessionExists() {
+					realSession = to.NewNoAuthSession(newOpsConfig.Url, true, staticAppData.UserAgent, false, -1)
+					realtoSession = realSession
+					toSession.FileSessionSet(true)
+					toSession.Set(realtoSession)
+
+					log.Errorf("TO login failed, disk backup exists, switching to disk")
+
+					for _, subscriber := range opsConfigChangeSubscribers {
+						go func(s chan<- handler.OpsConfig) { s <- newOpsConfig }(subscriber)
+					}
+					for _, subscriber := range toChangeSubscribers {
+						if toSession != nil {
+							go func(s chan<- towrap.ITrafficOpsSession) { s <- toSession }(subscriber)
+						}
+					}
+				}
+			}
+		}
+	}
+}
+
 // getMonitorCDN returns the CDN of a given Traffic Monitor.
 // TODO change to get by name, when Traffic Ops supports querying a single server.
 func getMonitorCDN(toc *to.Session, monitorHostname string) (string, error) {
diff --git a/traffic_monitor/towrap/towrap.go b/traffic_monitor/towrap/towrap.go
index a017945b4..7acbd2602 100644
--- a/traffic_monitor/towrap/towrap.go
+++ b/traffic_monitor/towrap/towrap.go
@@ -22,12 +22,15 @@ package towrap
 import (
 	"errors"
 	"fmt"
+	"io/ioutil"
+	"os"
 	"strconv"
 	"sync"
 	"time"
 
 	"github.com/apache/trafficcontrol/lib/go-log"
 	"github.com/apache/trafficcontrol/lib/go-tc"
+	"github.com/apache/trafficcontrol/traffic_monitor/config"
 	"github.com/apache/trafficcontrol/traffic_ops/client"
 
 	"github.com/json-iterator/go"
@@ -47,10 +50,15 @@ type ITrafficOpsSession interface {
 	DeliveryServices() ([]tc.DeliveryService, error)
 	CacheGroups() ([]tc.CacheGroupNullable, error)
 	CRConfigHistory() []CRConfigStat
+	FileSessionSet(bool)
+	FileSessionGet() bool
+	FileSessionExists() bool
 }
 
 var ErrNilSession = fmt.Errorf("nil session")
 
+var fileSession bool = false
+
 // TODO rename CRConfigCacheObj
 type ByteTime struct {
 	bytes []byte
@@ -63,6 +71,26 @@ type ByteMapCache struct {
 	m     *sync.RWMutex
 }
 
+// FileSessionSet Sets file usage stat
+func (s TrafficOpsSessionThreadsafe) FileSessionSet(set bool) {
+	fileSession = set
+}
+
+// FileSessionGet Gets current file usage stat
+func (s TrafficOpsSessionThreadsafe) FileSessionGet() bool {
+	return fileSession
+}
+
+// FileSessionExists Returns state of backup files
+func (s TrafficOpsSessionThreadsafe) FileSessionExists() bool {
+	if _, err := os.Stat(config.CrConfigBackupFile); !os.IsNotExist(err) {
+		if _, err = os.Stat(config.TmConfigBackupFile); !os.IsNotExist(err) {
+			return true
+		}
+	}
+	return false
+}
+
 func NewByteMapCache() ByteMapCache {
 	return ByteMapCache{m: &sync.RWMutex{}, cache: &map[string]ByteTime{}}
 }
@@ -230,12 +258,28 @@ func (s *TrafficOpsSessionThreadsafe) CRConfigValid(crc *tc.CRConfig, cdn string
 // CRConfigRaw returns the CRConfig from the Traffic Ops. This is safe for multiple goroutines.
 func (s TrafficOpsSessionThreadsafe) CRConfigRaw(cdn string) ([]byte, error) {
 	ss := s.get()
-	if ss == nil {
-		return nil, ErrNilSession
+
+	var b []byte
+	var err error
+	var remoteAddr string
+	var reqInf client.ReqInf
+
+	if ss == nil || fileSession {
+		if s.FileSessionExists() {
+			b, _ = ioutil.ReadFile(config.CrConfigBackupFile)
+			remoteAddr = "127.0.0.1"
+		} else {
+			return nil, ErrNilSession
+		}
+	} else {
+		b, reqInf, err = ss.GetCRConfig(cdn)
+		if err == nil {
+			remoteAddr = reqInf.RemoteAddr.String()
+			ioutil.WriteFile(config.CrConfigBackupFile, b, 0644)
+		}
 	}
-	b, reqInf, err := ss.GetCRConfig(cdn)
 
-	hist := &CRConfigStat{time.Now(), reqInf.RemoteAddr.String(), tc.CRConfigStats{}, err}
+	hist := &CRConfigStat{time.Now(), remoteAddr, tc.CRConfigStats{}, err}
 	defer s.crConfigHist.Add(hist)
 
 	if err != nil {
@@ -256,7 +300,6 @@ func (s TrafficOpsSessionThreadsafe) CRConfigRaw(cdn string) ([]byte, error) {
 		hist.Err = err
 		return b, err
 	}
-
 	s.lastCRConfig.Set(cdn, b, &crc.Stats)
 	return b, nil
 }
@@ -277,8 +320,29 @@ func (s TrafficOpsSessionThreadsafe) trafficMonitorConfigMapRaw(cdn string) (*tc
 	if ss == nil {
 		return nil, ErrNilSession
 	}
-	configMap, _, error := ss.GetTrafficMonitorConfigMap(cdn)
-	return configMap, error
+
+	var configMap *tc.TrafficMonitorConfigMap
+
+	var err error
+
+	if fileSession {
+		data, _ := ioutil.ReadFile(config.TmConfigBackupFile)
+		json := jsoniter.ConfigFastest
+		err := json.Unmarshal(data, &configMap)
+		if err != nil {
+			log.Errorf("Error unmarshaling TmConfigBackupFile, ", err)
+		}
+	} else {
+		configMap, _, err = ss.GetTrafficMonitorConfigMap(cdn)
+		if configMap != nil {
+			json := jsoniter.ConfigFastest
+			data, err := json.Marshal(*configMap)
+			if err == nil {
+				ioutil.WriteFile(config.TmConfigBackupFile, data, 0644)
+			}
+		}
+	}
+	return configMap, err
 }
 
 // TrafficMonitorConfigMap returns the Traffic Monitor config map from the Traffic Ops. This is safe for multiple goroutines.
@@ -303,13 +367,14 @@ func (s TrafficOpsSessionThreadsafe) TrafficMonitorConfigMap(cdn string) (*tc.Tr
 	if err != nil {
 		return nil, fmt.Errorf("creating Traffic Monitor Config: %v", err)
 	}
-
 	return mc, nil
 }
 
 func CreateMonitorConfig(crConfig tc.CRConfig, mc *tc.TrafficMonitorConfigMap) (*tc.TrafficMonitorConfigMap, error) {
 	// Dump the "live" monitoring.json servers, and populate with the "snapshotted" CRConfig
-	mc.TrafficServer = map[string]tc.TrafficServer{}
+	if mc != nil {
+		mc.TrafficServer = map[string]tc.TrafficServer{}
+	}
 	for name, srv := range crConfig.ContentServers {
 		s := tc.TrafficServer{}
 		if srv.Profile != nil {
@@ -363,11 +428,14 @@ func CreateMonitorConfig(crConfig tc.CRConfig, mc *tc.TrafficMonitorConfigMap) (
 		} else {
 			log.Warnf("Creating monitor config: CRConfig server %s missing HashId field\n", name)
 		}
-		mc.TrafficServer[name] = s
+		if mc != nil {
+			mc.TrafficServer[name] = s
+		}
 	}
-
 	// Dump the "live" monitoring.json monitors, and populate with the "snapshotted" CRConfig
-	mc.TrafficMonitor = map[string]tc.TrafficMonitor{}
+	if mc != nil {
+		mc.TrafficMonitor = map[string]tc.TrafficMonitor{}
+	}
 	for name, mon := range crConfig.Monitors {
 		// monitorProfile = *mon.Profile
 		m := tc.TrafficMonitor{}
@@ -407,23 +475,26 @@ func CreateMonitorConfig(crConfig tc.CRConfig, mc *tc.TrafficMonitorConfigMap) (
 		} else {
 			log.Warnf("Creating monitor config: CRConfig monitor %s missing ServerStatus field\n", name)
 		}
-		mc.TrafficMonitor[name] = m
+		if mc != nil {
+			mc.TrafficMonitor[name] = m
+		}
 	}
-
 	// Dump the "live" monitoring.json DeliveryServices, and populate with the "snapshotted" CRConfig
 	// But keep using the monitoring.json thresholds, because they're not in the CRConfig.
-	rawDeliveryServices := mc.DeliveryService
-	mc.DeliveryService = map[string]tc.TMDeliveryService{}
-	for name, _ := range crConfig.DeliveryServices {
-		if rawDS, ok := rawDeliveryServices[name]; ok {
-			// use the raw DS if it exists, because the CRConfig doesn't have thresholds or statuses
-			mc.DeliveryService[name] = rawDS
-		} else {
-			mc.DeliveryService[name] = tc.TMDeliveryService{
-				XMLID:              name,
-				TotalTPSThreshold:  0,
-				ServerStatus:       "REPORTED",
-				TotalKbpsThreshold: 0,
+	if mc != nil {
+		rawDeliveryServices := mc.DeliveryService
+		mc.DeliveryService = map[string]tc.TMDeliveryService{}
+		for name, _ := range crConfig.DeliveryServices {
+			if rawDS, ok := rawDeliveryServices[name]; ok {
+				// use the raw DS if it exists, because the CRConfig doesn't have thresholds or statuses
+				mc.DeliveryService[name] = rawDS
+			} else {
+				mc.DeliveryService[name] = tc.TMDeliveryService{
+					XMLID:              name,
+					TotalTPSThreshold:  0,
+					ServerStatus:       "REPORTED",
+					TotalKbpsThreshold: 0,
+				}
 			}
 		}
 	}


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services