write checker framework
Signed-off-by: Nathan13888 <29968201+Nathan13888@users.noreply.github.com>
This commit is contained in:
parent
4823e28fe0
commit
4502ee4171
|
@ -0,0 +1,115 @@
|
||||||
|
package checkers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// "Projects" which have valid configs.
|
||||||
|
var EnabledProjects = make(map[string]*Project)
|
||||||
|
|
||||||
|
// TODO: refactor all errors into variables
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Project, Project Properties, Project Checker
|
||||||
|
|
||||||
|
type Project struct {
|
||||||
|
Name string
|
||||||
|
Properties ProjectProperties
|
||||||
|
NumOfCheckers int
|
||||||
|
Checkers []*ProjectChecker
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectProperties struct {
|
||||||
|
OOSSince time.Time //`json:"out_of_sync_since"`
|
||||||
|
OOSInterval int64 `json:"out_of_sync_interval"` // in seconds
|
||||||
|
CSC string `json:"csc"`
|
||||||
|
Upstream string `json:"upstream"`
|
||||||
|
File string `json:"file"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectChecker struct {
|
||||||
|
CheckProject func() CheckerResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// //////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Checker Status and Checker Result
|
||||||
|
type CheckerStatus string
|
||||||
|
|
||||||
|
var CHECKER_SUCCESS CheckerStatus = "success"
|
||||||
|
var CHECKER_PROGRESS CheckerStatus = "progress"
|
||||||
|
var CHECKER_ERROR CheckerStatus = "error"
|
||||||
|
var CHECKER_FAIL CheckerStatus = "fail"
|
||||||
|
|
||||||
|
type CheckerResult struct {
|
||||||
|
ProjectName string `json:"project_name"`
|
||||||
|
CheckerName string `json:"checker_name"`
|
||||||
|
Time time.Time `json:"time"`
|
||||||
|
Status CheckerStatus `json:"status"`
|
||||||
|
Error error `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CheckerResultCallback func(CheckerResult)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Given a project, all checks are initiated.
|
||||||
|
func (p Project) RunChecksAsync(callback CheckerResultCallback) error {
|
||||||
|
checks := p.Checkers
|
||||||
|
n := len(checks)
|
||||||
|
|
||||||
|
// TODO: assert other checkers?
|
||||||
|
// assert if the number of checkers match the expected number of checkers
|
||||||
|
if n != p.NumOfCheckers {
|
||||||
|
return errors.New("Number of checkers does not match the expected number of checkers.")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info().Msgf("Running %d checks for project %s", n, p.Name)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
check := checks[i]
|
||||||
|
log.Info().Str("project", p.Name).Msgf("Running check %d", i)
|
||||||
|
|
||||||
|
check.RunCheckAsync(callback)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a check, run the check asynchronously and call the callback function.
|
||||||
|
func (c ProjectChecker) RunCheckAsync(callback CheckerResultCallback) error {
|
||||||
|
go func() {
|
||||||
|
res := c.CheckProject()
|
||||||
|
callback(res)
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Looks up a project by name, and returns only if the project is enabled.
|
||||||
|
func GetProject(name string) (*Project, error) {
|
||||||
|
res, exists := EnabledProjects[name]
|
||||||
|
if !exists {
|
||||||
|
return res, errors.New("Requested project not found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads a project by name, and returns if the project is found.
|
||||||
|
func LoadProject(name string) (*Project, error) {
|
||||||
|
res, exists := SupportedProjects[name]
|
||||||
|
if !exists {
|
||||||
|
return res, errors.New("Requested project not found.")
|
||||||
|
}
|
||||||
|
if res.Properties == DefaultProjectProperties {
|
||||||
|
log.Debug().Str("csc", res.Properties.CSC).
|
||||||
|
Msgf("Requested project %s has default properties.", name)
|
||||||
|
return res, errors.New("Requested project has invalid properties. Please check the project config.")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().Msgf("Loading Project %s", name)
|
||||||
|
EnabledProjects[name] = res
|
||||||
|
return res, nil
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package checkers
|
||||||
|
|
||||||
|
var DebianProject Project = Project{
|
||||||
|
Name: "debian",
|
||||||
|
Properties: DefaultProjectProperties,
|
||||||
|
NumOfCheckers: 1,
|
||||||
|
Checkers: []*ProjectChecker{
|
||||||
|
GetDefaultChecker("debian"),
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package checkers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
var DefaultProjectProperties = ProjectProperties{
|
||||||
|
OOSSince: time.Now(),
|
||||||
|
OOSInterval: 3600,
|
||||||
|
CSC: "unknown",
|
||||||
|
Upstream: "unknown",
|
||||||
|
File: "unknown",
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultCheckerResult CheckerResult = CheckerResult{
|
||||||
|
ProjectName: "unknown",
|
||||||
|
CheckerName: "unknown",
|
||||||
|
Status: CHECKER_PROGRESS,
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// All "projects" which have been implemented.
|
||||||
|
var SupportedProjects map[string]*Project = map[string]*Project{
|
||||||
|
"debian": &DebianProject,
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadDefaultProjects() {
|
||||||
|
// Load all projects that's implemented
|
||||||
|
|
||||||
|
log.Info().Msg("Loading Default Projects.")
|
||||||
|
|
||||||
|
LoadProject("debian")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDefaultChecker(name string) *ProjectChecker {
|
||||||
|
return &ProjectChecker{
|
||||||
|
CheckProject: func() CheckerResult {
|
||||||
|
// TODO: implement
|
||||||
|
return DefaultCheckerResult
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,14 @@ func LoadFromFile(path string) error {
|
||||||
for proj, prop := range data {
|
for proj, prop := range data {
|
||||||
log.Info().Str("project", proj).Msg("Enabled Project.")
|
log.Info().Str("project", proj).Msg("Enabled Project.")
|
||||||
|
|
||||||
|
sp, exists := checkers.SupportedProjects[NormalizeName(proj)]
|
||||||
|
if !exists {
|
||||||
|
log.Warn().Str("project", proj).Msg("Project not supported.")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sp.Properties = prop
|
||||||
|
|
||||||
log.Debug().
|
log.Debug().
|
||||||
Str("distribution", proj).
|
Str("distribution", proj).
|
||||||
// Time("out_of_sync_since", prop.OOSSince).
|
// Time("out_of_sync_since", prop.OOSSince).
|
||||||
|
|
65
main.go
65
main.go
|
@ -2,7 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/Nathan13888/mirror-checker2/v2/checkers"
|
"github.com/Nathan13888/mirror-checker2/v2/checkers"
|
||||||
"github.com/Nathan13888/mirror-checker2/v2/config"
|
"github.com/Nathan13888/mirror-checker2/v2/config"
|
||||||
|
@ -11,17 +10,20 @@ import (
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getAbsPath(path string) string {
|
var configPath string
|
||||||
absPath, err := filepath.Abs(path)
|
|
||||||
|
func loadConfig() {
|
||||||
|
path := config.GetAbsPath(configPath)
|
||||||
|
// TODO: check if file exists
|
||||||
|
|
||||||
|
log.Info().Str("path", path).Msg("Loading config file.")
|
||||||
|
err := config.LoadFromFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to get absolute path.")
|
log.Fatal().Err(err).Msg("Failed to load config file.")
|
||||||
return path
|
|
||||||
}
|
}
|
||||||
return absPath
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
|
||||||
config.SetupLogger(true) // TODO: flag for debug mode
|
config.SetupLogger(true) // TODO: flag for debug mode
|
||||||
|
|
||||||
// Start CLI
|
// Start CLI
|
||||||
|
@ -33,6 +35,16 @@ func main() {
|
||||||
EnableBashCompletion: true,
|
EnableBashCompletion: true,
|
||||||
// https://cli.urfave.org/v2/examples/combining-short-options/
|
// https://cli.urfave.org/v2/examples/combining-short-options/
|
||||||
// TODO: flags for config file (mirrors.json), defaults to mirrors.json
|
// TODO: flags for config file (mirrors.json), defaults to mirrors.json
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "config",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "path to config file",
|
||||||
|
Destination: &configPath,
|
||||||
|
Value: "data/mirrors.json",
|
||||||
|
EnvVars: []string{"MC_CONFIG_FILE"},
|
||||||
|
},
|
||||||
|
},
|
||||||
Commands: []*cli.Command{
|
Commands: []*cli.Command{
|
||||||
{
|
{
|
||||||
Name: "daemon",
|
Name: "daemon",
|
||||||
|
@ -45,12 +57,7 @@ func main() {
|
||||||
// TODO: enable all projects by default
|
// TODO: enable all projects by default
|
||||||
// checkers.LoadDefaultProjects()
|
// checkers.LoadDefaultProjects()
|
||||||
|
|
||||||
path := getAbsPath("data/mirrors.json")
|
loadConfig()
|
||||||
log.Info().Str("path", path).Msg("Loading config file.")
|
|
||||||
err = config.LoadFromFile(path)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal().Err(err).Msg("Failed to load config file.")
|
|
||||||
}
|
|
||||||
|
|
||||||
return web.StartServer()
|
return web.StartServer()
|
||||||
},
|
},
|
||||||
|
@ -61,14 +68,22 @@ func main() {
|
||||||
Usage: "checks particular mirror (once)",
|
Usage: "checks particular mirror (once)",
|
||||||
Action: func(cCtx *cli.Context) error {
|
Action: func(cCtx *cli.Context) error {
|
||||||
checkers.LoadDefaultProjects()
|
checkers.LoadDefaultProjects()
|
||||||
|
loadConfig()
|
||||||
|
|
||||||
// verify all projects are enabled
|
// verify all projects are enabled
|
||||||
// TODO:
|
// TODO: ???
|
||||||
|
|
||||||
// attempt to look up and check all projects
|
// attempt to look up and check all projects
|
||||||
for _, arg := range cCtx.Args().Slice() {
|
projects := cCtx.Args().Slice()
|
||||||
log.Printf("\nPulling project information for '%s'\n\n", arg)
|
if len(projects) == 0 {
|
||||||
proj, err := checkers.GetProject(arg)
|
log.Fatal().Msg("No projects specified.")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug().Msgf("Checking all specified projects.")
|
||||||
|
|
||||||
|
for _, arg := range projects {
|
||||||
|
log.Info().Msgf("Pulling project information for '%s'.", arg)
|
||||||
|
proj, err := checkers.LoadProject(arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).
|
log.Fatal().Err(err).
|
||||||
Str("project", arg).
|
Str("project", arg).
|
||||||
|
@ -76,7 +91,21 @@ func main() {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
proj.RunChecksAsync()
|
err = proj.RunChecksAsync(func(res checkers.CheckerResult) {
|
||||||
|
if res.Error != nil {
|
||||||
|
log.Error().Err(res.Error).
|
||||||
|
Time("time", res.Time).
|
||||||
|
Msgf("Failed check %s for project %s", res.CheckerName, res.ProjectName)
|
||||||
|
} else {
|
||||||
|
log.Info().
|
||||||
|
Time("time", res.Time).
|
||||||
|
Str("status", string(res.Status)).
|
||||||
|
Msgf("Completed check %s for project %s", res.CheckerName, res.ProjectName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Failed to check project.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in New Issue