roughed in limit warning email actions (#276)

This commit is contained in:
Michael Quigley 2023-03-27 15:29:25 -04:00
parent bb61bdb664
commit d279fbb8cb
No known key found for this signature in database
GPG Key ID: 9B60314A9DD20A62
9 changed files with 152 additions and 26 deletions

View File

@ -1,6 +1,7 @@
package config package config
import ( import (
"github.com/openziti/zrok/controller/emailUi"
"github.com/openziti/zrok/controller/env" "github.com/openziti/zrok/controller/env"
"github.com/openziti/zrok/controller/limits" "github.com/openziti/zrok/controller/limits"
"github.com/openziti/zrok/controller/metrics" "github.com/openziti/zrok/controller/metrics"
@ -19,7 +20,7 @@ type Config struct {
Admin *AdminConfig Admin *AdminConfig
Bridge *metrics.BridgeConfig Bridge *metrics.BridgeConfig
Endpoint *EndpointConfig Endpoint *EndpointConfig
Email *EmailConfig Email *emailUi.Config
Limits *limits.Config Limits *limits.Config
Maintenance *MaintenanceConfig Maintenance *MaintenanceConfig
Metrics *metrics.Config Metrics *metrics.Config
@ -39,14 +40,6 @@ type EndpointConfig struct {
Port int Port int
} }
type EmailConfig struct {
Host string
Port int
Username string
Password string `cf:"+secret"`
From string
}
type RegistrationConfig struct { type RegistrationConfig struct {
RegistrationUrlTemplate string RegistrationUrlTemplate string
TokenStrategy string TokenStrategy string

View File

@ -84,7 +84,7 @@ func Run(inCfg *config.Config) error {
defer func() { ma.Stop() }() defer func() { ma.Stop() }()
if cfg.Limits != nil && cfg.Limits.Enforcing { if cfg.Limits != nil && cfg.Limits.Enforcing {
limitsAgent, err = limits.NewAgent(cfg.Limits, cfg.Metrics.Influx, cfg.Ziti, str) limitsAgent, err = limits.NewAgent(cfg.Limits, cfg.Metrics.Influx, cfg.Ziti, cfg.Email, str)
if err != nil { if err != nil {
return errors.Wrap(err, "error creating limits agent") return errors.Wrap(err, "error creating limits agent")
} }

View File

@ -0,0 +1,9 @@
package emailUi
type Config struct {
Host string
Port int
Username string
Password string `cf:"+secret"`
From string
}

View File

@ -0,0 +1,24 @@
package emailUi
import (
"bytes"
"github.com/pkg/errors"
"html/template"
)
type WarningEmail struct {
EmailAddress string
Detail string
}
func (we WarningEmail) MergeTemplate(filename string) (string, error) {
t, err := template.ParseFS(FS, filename)
if err != nil {
return "", errors.Wrapf(err, "error parsing warning email template '%v'", filename)
}
buf := new(bytes.Buffer)
if err := t.Execute(buf, we); err != nil {
return "", errors.Wrapf(err, "error executing warning email template '%v'", filename)
}
return buf.String(), nil
}

View File

@ -3,20 +3,28 @@ package limits
import ( import (
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/openziti/edge/rest_management_api_client" "github.com/openziti/edge/rest_management_api_client"
"github.com/openziti/zrok/controller/emailUi"
"github.com/openziti/zrok/controller/store" "github.com/openziti/zrok/controller/store"
"github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
type accountWarningAction struct { type accountWarningAction struct {
str *store.Store str *store.Store
edge *rest_management_api_client.ZitiEdgeManagement edge *rest_management_api_client.ZitiEdgeManagement
cfg *emailUi.Config
} }
func newAccountWarningAction(str *store.Store, edge *rest_management_api_client.ZitiEdgeManagement) *accountWarningAction { func newAccountWarningAction(cfg *emailUi.Config, str *store.Store, edge *rest_management_api_client.ZitiEdgeManagement) *accountWarningAction {
return &accountWarningAction{str, edge} return &accountWarningAction{str, edge, cfg}
} }
func (a *accountWarningAction) HandleAccount(acct *store.Account, rxBytes, txBytes int64, limit *BandwidthPerPeriod, trx *sqlx.Tx) error { func (a *accountWarningAction) HandleAccount(acct *store.Account, rxBytes, txBytes int64, limit *BandwidthPerPeriod, trx *sqlx.Tx) error {
logrus.Infof("warning '%v'", acct.Email) logrus.Infof("warning '%v'", acct.Email)
if err := sendLimitWarningEmail(a.cfg, acct.Email, limit, rxBytes, txBytes); err != nil {
return errors.Wrapf(err, "error sending limit warning email to '%v'", acct.Email)
}
return nil return nil
} }

View File

@ -3,6 +3,7 @@ package limits
import ( import (
"fmt" "fmt"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/openziti/zrok/controller/emailUi"
"github.com/openziti/zrok/controller/metrics" "github.com/openziti/zrok/controller/metrics"
"github.com/openziti/zrok/controller/store" "github.com/openziti/zrok/controller/store"
"github.com/openziti/zrok/controller/zrokEdgeSdk" "github.com/openziti/zrok/controller/zrokEdgeSdk"
@ -31,7 +32,7 @@ type Agent struct {
join chan struct{} join chan struct{}
} }
func NewAgent(cfg *Config, ifxCfg *metrics.InfluxConfig, zCfg *zrokEdgeSdk.Config, str *store.Store) (*Agent, error) { func NewAgent(cfg *Config, ifxCfg *metrics.InfluxConfig, zCfg *zrokEdgeSdk.Config, emailCfg *emailUi.Config, str *store.Store) (*Agent, error) {
edge, err := zrokEdgeSdk.Client(zCfg) edge, err := zrokEdgeSdk.Client(zCfg)
if err != nil { if err != nil {
return nil, err return nil, err
@ -42,13 +43,13 @@ func NewAgent(cfg *Config, ifxCfg *metrics.InfluxConfig, zCfg *zrokEdgeSdk.Confi
zCfg: zCfg, zCfg: zCfg,
str: str, str: str,
queue: make(chan *metrics.Usage, 1024), queue: make(chan *metrics.Usage, 1024),
acctWarningActions: []AccountAction{newAccountWarningAction(str, edge)}, acctWarningActions: []AccountAction{newAccountWarningAction(emailCfg, str, edge)},
acctLimitActions: []AccountAction{newAccountLimitAction(str, edge)}, acctLimitActions: []AccountAction{newAccountLimitAction(str, edge)},
acctRelaxActions: []AccountAction{newAccountRelaxAction(str, edge)}, acctRelaxActions: []AccountAction{newAccountRelaxAction(str, edge)},
envWarningActions: []EnvironmentAction{newEnvironmentWarningAction(str, edge)}, envWarningActions: []EnvironmentAction{newEnvironmentWarningAction(emailCfg, str, edge)},
envLimitActions: []EnvironmentAction{newEnvironmentLimitAction(str, edge)}, envLimitActions: []EnvironmentAction{newEnvironmentLimitAction(str, edge)},
envRelaxActions: []EnvironmentAction{newEnvironmentRelaxAction(str, edge)}, envRelaxActions: []EnvironmentAction{newEnvironmentRelaxAction(str, edge)},
shrWarningActions: []ShareAction{newShareWarningAction(str, edge)}, shrWarningActions: []ShareAction{newShareWarningAction(emailCfg, str, edge)},
shrLimitActions: []ShareAction{newShareLimitAction(str, edge)}, shrLimitActions: []ShareAction{newShareLimitAction(str, edge)},
shrRelaxActions: []ShareAction{newShareRelaxAction(str, edge)}, shrRelaxActions: []ShareAction{newShareRelaxAction(str, edge)},
close: make(chan struct{}), close: make(chan struct{}),
@ -550,7 +551,7 @@ func (a *Agent) checkShareLimit(shrToken string) (enforce, warning bool, rxBytes
enforce, warning = a.checkLimit(limit, rx, tx) enforce, warning = a.checkLimit(limit, rx, tx)
if enforce || warning { if enforce || warning {
logrus.Debugf("'%v': %v", shrToken, a.describeLimit(limit, rx, tx)) logrus.Debugf("'%v': %v", shrToken, describeLimit(limit, rx, tx))
} }
return enforce, warning, rx, tx, nil return enforce, warning, rx, tx, nil
@ -580,7 +581,7 @@ func (a *Agent) checkLimit(cfg *BandwidthPerPeriod, rx, tx int64) (enforce, warn
return false, false return false, false
} }
func (a *Agent) describeLimit(cfg *BandwidthPerPeriod, rx, tx int64) string { func describeLimit(cfg *BandwidthPerPeriod, rx, tx int64) string {
out := "" out := ""
if cfg.Limit.Rx != Unlimited && rx > cfg.Limit.Rx { if cfg.Limit.Rx != Unlimited && rx > cfg.Limit.Rx {

View File

@ -0,0 +1,58 @@
package limits
import (
"github.com/openziti/zrok/controller/emailUi"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/wneessen/go-mail"
)
func sendLimitWarningEmail(cfg *emailUi.Config, emailTo string, limit *BandwidthPerPeriod, rxBytes, txBytes int64) error {
emailData := &emailUi.WarningEmail{
EmailAddress: emailTo,
Detail: describeLimit(limit, rxBytes, txBytes),
}
plainBody, err := emailData.MergeTemplate("limitWarning.gotext")
if err != nil {
return err
}
htmlBody, err := emailData.MergeTemplate("resetPassword.gohtml")
if err != nil {
return err
}
msg := mail.NewMsg()
if err := msg.From(cfg.From); err != nil {
return errors.Wrap(err, "failed to set from address in limit warning email")
}
if err := msg.To(emailTo); err != nil {
return errors.Wrap(err, "failed to set to address in limit warning email")
}
msg.Subject("Limit Warning Notification")
msg.SetDate()
msg.SetMessageID()
msg.SetBulk()
msg.SetImportance(mail.ImportanceHigh)
msg.SetBodyString(mail.TypeTextPlain, plainBody)
msg.SetBodyString(mail.TypeTextHTML, htmlBody)
client, err := mail.NewClient(cfg.Host,
mail.WithPort(cfg.Port),
mail.WithSMTPAuth(mail.SMTPAuthPlain),
mail.WithUsername(cfg.Username),
mail.WithPassword(cfg.Password),
mail.WithTLSPolicy(mail.TLSMandatory),
)
if err != nil {
return errors.Wrap(err, "error creating limit warning email client")
}
if err := client.DialAndSend(msg); err != nil {
return errors.Wrap(err, "error sending limit warning email")
}
logrus.Infof("limit warning email sent to '%v'", emailTo)
return nil
}

View File

@ -3,20 +3,35 @@ package limits
import ( import (
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/openziti/edge/rest_management_api_client" "github.com/openziti/edge/rest_management_api_client"
"github.com/openziti/zrok/controller/emailUi"
"github.com/openziti/zrok/controller/store" "github.com/openziti/zrok/controller/store"
"github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
type environmentWarningAction struct { type environmentWarningAction struct {
str *store.Store str *store.Store
edge *rest_management_api_client.ZitiEdgeManagement edge *rest_management_api_client.ZitiEdgeManagement
cfg *emailUi.Config
} }
func newEnvironmentWarningAction(str *store.Store, edge *rest_management_api_client.ZitiEdgeManagement) *environmentWarningAction { func newEnvironmentWarningAction(cfg *emailUi.Config, str *store.Store, edge *rest_management_api_client.ZitiEdgeManagement) *environmentWarningAction {
return &environmentWarningAction{str, edge} return &environmentWarningAction{str, edge, cfg}
}
func (a *environmentWarningAction) HandleEnvironment(env *store.Environment, rxBytes, txBytes int64, limit *BandwidthPerPeriod, trx *sqlx.Tx) error {
logrus.Infof("warning '%v'", env.ZId)
if env.AccountId != nil {
acct, err := a.str.GetAccount(*env.AccountId, trx)
if err != nil {
return err
}
if err := sendLimitWarningEmail(a.cfg, acct.Email, limit, rxBytes, txBytes); err != nil {
return errors.Wrapf(err, "error sending limit warning email to '%v'", acct.Email)
}
} }
func (a *environmentWarningAction) HandleEnvironment(e *store.Environment, rxBytes, txBytes int64, limit *BandwidthPerPeriod, trx *sqlx.Tx) error {
logrus.Infof("warning '%v'", e.ZId)
return nil return nil
} }

View File

@ -3,20 +3,38 @@ package limits
import ( import (
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/openziti/edge/rest_management_api_client" "github.com/openziti/edge/rest_management_api_client"
"github.com/openziti/zrok/controller/emailUi"
"github.com/openziti/zrok/controller/store" "github.com/openziti/zrok/controller/store"
"github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
type shareWarningAction struct { type shareWarningAction struct {
str *store.Store str *store.Store
edge *rest_management_api_client.ZitiEdgeManagement edge *rest_management_api_client.ZitiEdgeManagement
cfg *emailUi.Config
} }
func newShareWarningAction(str *store.Store, edge *rest_management_api_client.ZitiEdgeManagement) *shareWarningAction { func newShareWarningAction(cfg *emailUi.Config, str *store.Store, edge *rest_management_api_client.ZitiEdgeManagement) *shareWarningAction {
return &shareWarningAction{str, edge} return &shareWarningAction{str, edge, cfg}
}
func (a *shareWarningAction) HandleShare(shr *store.Share, rxBytes, txBytes int64, limit *BandwidthPerPeriod, trx *sqlx.Tx) error {
logrus.Infof("warning '%v'", shr.Token)
env, err := a.str.GetEnvironment(shr.EnvironmentId, trx)
if err != nil {
return err
}
acct, err := a.str.GetAccount(env.Id, trx)
if err != nil {
return err
}
if err := sendLimitWarningEmail(a.cfg, acct.Email, limit, rxBytes, txBytes); err != nil {
return errors.Wrapf(err, "error sending limit warning email to '%v'", acct.Email)
} }
func (a *shareWarningAction) HandleShare(s *store.Share, rxBytes, txBytes int64, limit *BandwidthPerPeriod, trx *sqlx.Tx) error {
logrus.Infof("warning '%v'", s.Token)
return nil return nil
} }