github.com/Axway/agent-sdk@v1.1.101/pkg/config/usagereportingconfig.go (about)

     1  package config
     2  
     3  import (
     4  	"os"
     5  	"strconv"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/Axway/agent-sdk/pkg/cmd/properties"
    10  	"github.com/Axway/agent-sdk/pkg/util/exception"
    11  	"github.com/Axway/agent-sdk/pkg/util/log"
    12  	"github.com/gorhill/cronexpr"
    13  )
    14  
    15  const (
    16  	// DEPRECATE remove old and new env vars as well as checks below
    17  	oldUsageReportingPublishEnvVar  = "CENTRAL_PUBLISHUSAGE"
    18  	newUsageReportingPublishEnvVar  = "CENTRAL_USAGEREPORTING_PUBLISH"
    19  	oldUsageReportingScheduleEnvVar = "CENTRAL_USAGEREPORTING_USAGESCHEDULE"
    20  	newUsageReportingScheduleEnvVar = "CENTRAL_USAGEREPORTING_SCHEDULE"
    21  
    22  	// QA EnvVars
    23  	qaUsageReportingScheduleEnvVar        = "QA_CENTRAL_USAGEREPORTING_OFFLINESCHEDULE"
    24  	qaUsageReportingOfflineScheduleEnvVar = "QA_CENTRAL_USAGEREPORTING_OFFLINEREPORTSCHEDULE"
    25  	qaUsageReportingUsageScheduleEnvVar   = "QA_CENTRAL_USAGEREPORTING_SCHEDULE"
    26  
    27  	// Config paths
    28  	pathUsageReportingPublish         = "central.usagereporting.publish"
    29  	pathUsageReportingSchedule        = "central.usagereporting.schedule"
    30  	pathUsageReportingOffline         = "central.usagereporting.offline"
    31  	pathUsageReportingOfflineSchedule = "central.usagereporting.offlineSchedule"
    32  )
    33  
    34  // UsageReportingConfig - Interface to get usage reporting config
    35  type UsageReportingConfig interface {
    36  	GetURL() string
    37  	CanPublish() bool
    38  	GetReportInterval() time.Duration
    39  	GetSchedule() string
    40  	IsOfflineMode() bool
    41  	GetOfflineSchedule() string
    42  	GetReportSchedule() string
    43  	GetReportGranularity() int
    44  	UsingQAVars() bool
    45  	Validate()
    46  }
    47  
    48  // UsageReportingConfiguration - structure to hold all usage reporting settings
    49  type UsageReportingConfiguration struct {
    50  	UsageReportingConfig
    51  	Publish           bool   `config:"publish"`
    52  	Schedule          string `config:"schedule"`
    53  	Offline           bool   `config:"offline"`
    54  	OfflineSchedule   string `config:"offlineSchedule"`
    55  	URL               string
    56  	reportSchedule    string
    57  	reportGranularity int
    58  	qaVars            bool
    59  }
    60  
    61  // NewUsageReporting - Creates the default usage reporting config
    62  func NewUsageReporting(platformURL string) UsageReportingConfig {
    63  	return &UsageReportingConfiguration{
    64  		URL:             platformURL,
    65  		Publish:         true,
    66  		Schedule:        "@daily",
    67  		Offline:         false,
    68  		OfflineSchedule: "@hourly",
    69  		reportSchedule:  "@monthly",
    70  		qaVars:          false,
    71  	}
    72  }
    73  
    74  func (u *UsageReportingConfiguration) validatePublish() {
    75  	if val := os.Getenv(newUsageReportingPublishEnvVar); val != "" {
    76  		return // this env var is set use what has been parsed
    77  	}
    78  
    79  	// check if the old env var had a value
    80  	val := os.Getenv(oldUsageReportingPublishEnvVar)
    81  	if val != "" {
    82  		if value, err := strconv.ParseBool(val); err == nil {
    83  			log.DeprecationWarningReplace(oldUsageReportingPublishEnvVar, newUsageReportingPublishEnvVar)
    84  			u.Publish = value
    85  		}
    86  	}
    87  }
    88  
    89  // Validate -
    90  func (u *UsageReportingConfiguration) Validate() {
    91  	u.validatePublish()
    92  
    93  	if !u.Offline {
    94  		u.validateUsageSchedule()
    95  	} else {
    96  		u.validateOffline()
    97  	}
    98  }
    99  
   100  func (u *UsageReportingConfiguration) validateUsageSchedule() {
   101  	// check if the qa env var is set
   102  	if val := os.Getenv(qaUsageReportingUsageScheduleEnvVar); val != "" {
   103  		if _, err := cronexpr.Parse(val); err != nil {
   104  			log.Tracef("Could not use %s (%s) it is not a proper cron schedule", qaUsageReportingUsageScheduleEnvVar, val)
   105  		} else {
   106  			log.Tracef("Using %s (%s) rather than the default (%s) for non-QA", qaUsageReportingUsageScheduleEnvVar, val, u.Schedule)
   107  			u.Schedule = val
   108  			u.qaVars = true
   109  		}
   110  		return
   111  	}
   112  
   113  	if val := os.Getenv(newUsageReportingScheduleEnvVar); val == "" {
   114  		if val = os.Getenv(oldUsageReportingScheduleEnvVar); val != "" {
   115  			log.DeprecationWarningReplace(oldUsageReportingScheduleEnvVar, newUsageReportingScheduleEnvVar)
   116  			u.Schedule = val
   117  		}
   118  	}
   119  
   120  	// Check the cron expressions
   121  	cron, err := cronexpr.Parse(u.Schedule)
   122  	if err != nil {
   123  		exception.Throw(ErrBadConfig.FormatError(pathUsageReportingSchedule))
   124  	}
   125  	checks := 5
   126  	nextRuns := cron.NextN(time.Now(), uint(checks))
   127  	if len(nextRuns) != checks {
   128  		exception.Throw(ErrBadConfig.FormatError(pathUsageReportingSchedule))
   129  	}
   130  	for i := 1; i < checks-1; i++ {
   131  		delta := nextRuns[i].Sub(nextRuns[i-1])
   132  		if delta < time.Hour {
   133  			log.Tracef("%s must be at 1 hour apart", pathUsageReportingSchedule)
   134  			exception.Throw(ErrBadConfig.FormatError(pathUsageReportingSchedule))
   135  		}
   136  	}
   137  }
   138  
   139  func (u *UsageReportingConfiguration) validateOffline() {
   140  	if _, err := cronexpr.Parse(u.OfflineSchedule); err != nil {
   141  		exception.Throw(ErrBadConfig.FormatError(pathUsageReportingOfflineSchedule))
   142  	}
   143  
   144  	// reporting is offline, lets read the QA env vars
   145  	if val := os.Getenv(qaUsageReportingScheduleEnvVar); val != "" {
   146  		if _, err := cronexpr.Parse(val); err != nil {
   147  			log.Tracef("Could not use %s (%s) it is not a proper cron schedule", qaUsageReportingScheduleEnvVar, val)
   148  		} else {
   149  			log.Tracef("Using %s (%s) rather than the default (%s) for non-QA", qaUsageReportingScheduleEnvVar, val, u.OfflineSchedule)
   150  			u.OfflineSchedule = val
   151  			u.qaVars = true
   152  		}
   153  	}
   154  
   155  	if val := os.Getenv(qaUsageReportingOfflineScheduleEnvVar); val != "" {
   156  		if _, err := cronexpr.Parse(val); err != nil {
   157  			log.Tracef("Could not use %s (%s) it is not a proper cron schedule", qaUsageReportingOfflineScheduleEnvVar, val)
   158  		} else {
   159  			log.Tracef("Using %s (%s) rather than the default (%s) for non-QA", qaUsageReportingOfflineScheduleEnvVar, val, u.reportSchedule)
   160  			u.reportSchedule = val
   161  			u.qaVars = true
   162  		}
   163  	}
   164  
   165  	// Check the cron expressions
   166  	cron, err := cronexpr.Parse(u.OfflineSchedule)
   167  	if err != nil {
   168  		exception.Throw(ErrBadConfig.FormatError(pathUsageReportingOfflineSchedule))
   169  	}
   170  	nextTwoRuns := cron.NextN(time.Now(), 2)
   171  	if len(nextTwoRuns) != 2 {
   172  		exception.Throw(ErrBadConfig.FormatError(pathUsageReportingOfflineSchedule))
   173  	}
   174  	u.reportGranularity = int(nextTwoRuns[1].Sub(nextTwoRuns[0]).Milliseconds())
   175  
   176  	// if no QA env vars are set then validate the schedule is at least hourly
   177  	if nextTwoRuns[1].Sub(nextTwoRuns[0]) < time.Hour && !u.qaVars {
   178  		exception.Throw(ErrBadConfig.FormatError(pathUsageReportingOfflineSchedule))
   179  	}
   180  }
   181  
   182  // GetURL - Returns the usage reporting URL
   183  func (u *UsageReportingConfiguration) GetURL() string {
   184  	return u.URL
   185  }
   186  
   187  // CanPublish - Returns the publish boolean
   188  func (u *UsageReportingConfiguration) CanPublish() bool {
   189  	return u.Publish
   190  }
   191  
   192  // IsOfflineMode - Returns the offline boolean
   193  func (u *UsageReportingConfiguration) IsOfflineMode() bool {
   194  	return u.Offline
   195  }
   196  
   197  // GetSchedule - Returns the schedule string
   198  func (u *UsageReportingConfiguration) GetOfflineSchedule() string {
   199  	return u.OfflineSchedule
   200  }
   201  
   202  // GetSchedule - Returns the schedule string for publishing reports
   203  func (u *UsageReportingConfiguration) GetSchedule() string {
   204  	return u.Schedule
   205  }
   206  
   207  // GetReportSchedule - Returns the offline schedule string for creating reports
   208  func (u *UsageReportingConfiguration) GetReportSchedule() string {
   209  	return u.reportSchedule
   210  }
   211  
   212  // GetReportGranularity - Returns the granularity used in the offline reports
   213  func (u *UsageReportingConfiguration) GetReportGranularity() int {
   214  	return u.reportGranularity
   215  }
   216  
   217  // UsingQAVars - Returns the offline boolean
   218  func (u *UsageReportingConfiguration) UsingQAVars() bool {
   219  	return u.qaVars
   220  }
   221  
   222  // AddUsageReportingProperties - Adds the command properties needed for Usage Reporting Settings
   223  func AddUsageReportingProperties(props properties.Properties) {
   224  	props.AddBoolProperty(pathUsageReportingPublish, true, "Indicates if the agent can publish usage events to Amplify platform. Default to true")
   225  	props.AddStringProperty(pathUsageReportingSchedule, "@daily", "The schedule at usage events are sent to the platform")
   226  	props.AddBoolProperty(pathUsageReportingOffline, false, "Turn this on to save the usage events to disk for manual upload")
   227  	props.AddStringProperty(pathUsageReportingOfflineSchedule, "@hourly", "The schedule at which usage events are generated, for offline mode only")
   228  }
   229  
   230  // ParseUsageReportingConfig - Parses the Usage Reporting Config values from the command line
   231  func ParseUsageReportingConfig(props properties.Properties) UsageReportingConfig {
   232  	// Start with the default config
   233  	platformURL := strings.TrimRight(props.StringPropertyValue(pathPlatformURL), urlCutSet)
   234  	cfg := NewUsageReporting(platformURL).(*UsageReportingConfiguration)
   235  
   236  	// update the config
   237  	cfg.Publish = props.BoolPropertyValue(pathUsageReportingPublish)
   238  	cfg.Schedule = props.StringPropertyValue(pathUsageReportingSchedule)
   239  	cfg.Offline = props.BoolPropertyValue(pathUsageReportingOffline)
   240  	cfg.OfflineSchedule = props.StringPropertyValue(pathUsageReportingOfflineSchedule)
   241  
   242  	return cfg
   243  }