parallize checks per instance using go routines

This commit is contained in:
Bnyro 2023-07-21 23:49:00 +02:00
parent 2e081e3641
commit c369f2b7bc
1 changed files with 128 additions and 81 deletions

209
main.go
View File

@ -44,108 +44,155 @@ type FrontendConfig struct {
S3Enabled bool `json:"s3Enabled"`
}
func testUrl(url string) (*http.Response, error) {
resp, err := http.Get(url)
if err != nil {
return resp, err
}
if resp.StatusCode != 200 {
return resp, errors.New(fmt.Sprintf("Invalid response code at %s: %d", url, resp.StatusCode))
}
return resp, err
}
func testCaching(ApiUrl string) (bool, error) {
resp, err := testUrl(ApiUrl + "/trending?region=US")
if err != nil {
return false, err
}
oldTiming := resp.Header.Get("Server-Timing")
resp, err = testUrl(ApiUrl + "/trending?region=US")
if err != nil {
return false, err
}
newTiming := resp.Header.Get("Server-Timing")
cacheWorking := oldTiming == newTiming
return cacheWorking, nil
}
func getConfig(ApiUrl string) (FrontendConfig, error) {
resp, err := testUrl(ApiUrl + "/config")
if err != nil {
return FrontendConfig{}, err
}
bytes, err := io.ReadAll(resp.Body)
if err != nil {
return FrontendConfig{}, err
}
var config FrontendConfig
err = json.Unmarshal(bytes, &config)
if err != nil {
return FrontendConfig{}, err
}
return config, nil
}
func getInstanceDetails(line string, latest string) (Instance, error) {
split := strings.Split(line, "|")
if len(split) >= 5 {
ApiUrl := strings.TrimSpace(split[1])
if len(split) < 5 {
return Instance{}, errors.New(fmt.Sprintf("Invalid line: %s", line))
}
ApiUrl := strings.TrimSpace(split[1])
resp, err := http.Get(ApiUrl + "/healthcheck")
wg := sync.WaitGroup{}
errorChannel := make(chan error)
// the amount of tests to do
wg.Add(6)
var lastChecked int64
var registered int64
var config FrontendConfig
var hash string
var version string
var cacheWorking bool
go func() {
wg.Wait()
close(errorChannel)
}()
go func() {
defer wg.Done()
if _, err := testUrl(ApiUrl + "/healthcheck"); err != nil {
errorChannel <- err
return
}
lastChecked = time.Now().Unix()
}()
go func() {
defer wg.Done()
resp, err := testUrl(ApiUrl + "/registered/badge")
if err != nil {
return Instance{}, err
errorChannel <- err
return
}
LastChecked := time.Now().Unix()
if resp.StatusCode != 200 {
return Instance{}, errors.New("Invalid response code")
}
resp, err = http.Get(ApiUrl + "/registered/badge")
registered, err = strconv.ParseInt(number_re.FindString(resp.Request.URL.Path), 10, 32)
if err != nil {
return Instance{}, err
errorChannel <- err
}
registered, err := strconv.ParseInt(number_re.FindString(resp.Request.URL.Path), 10, 32)
}()
go func() {
defer wg.Done()
resp, err := testUrl(ApiUrl + "/version")
if err != nil {
return Instance{}, err
}
resp, err = http.Get(ApiUrl + "/version")
if err != nil {
return Instance{}, err
}
if resp.StatusCode != 200 {
return Instance{}, errors.New("Invalid response code")
errorChannel <- err
return
}
buf := new(strings.Builder)
_, err = io.Copy(buf, resp.Body)
if err != nil {
return Instance{}, err
errorChannel <- err
return
}
version := strings.TrimSpace(buf.String())
version = strings.TrimSpace(buf.String())
version_split := strings.Split(version, "-")
hash := version_split[len(version_split)-1]
hash = version_split[len(version_split)-1]
}()
resp, err = http.Get(ApiUrl + "/config")
go func() {
defer wg.Done()
var err error
config, err = getConfig(ApiUrl)
if err != nil {
return Instance{}, err
}
if resp.StatusCode != 200 {
return Instance{}, errors.New("Invalid response code")
errorChannel <- err
}
}()
bytes, err := io.ReadAll(resp.Body)
go func() {
defer wg.Done()
var err error
cacheWorking, err = testCaching(ApiUrl)
if err != nil {
return Instance{}, err
}
var config FrontendConfig
err = json.Unmarshal(bytes, &config)
if err != nil {
return Instance{}, err
}
cache_working := false
resp, err = http.Get(ApiUrl + "/trending?region=US")
if err != nil {
return Instance{}, err
}
if resp.StatusCode == 200 {
old_timing := resp.Header.Get("Server-Timing")
resp, err = http.Get(ApiUrl + "/trending?region=US")
if err != nil {
return Instance{}, err
}
if resp.StatusCode == 200 {
new_timing := resp.Header.Get("Server-Timing")
if old_timing == new_timing {
cache_working = true
}
}
errorChannel <- err
}
}()
go func() {
defer wg.Done()
// check if instance can fetch videos
resp, err = http.Get(ApiUrl + "/streams/jNQXAC9IVRw")
if err != nil {
return Instance{}, err
}
if resp.StatusCode != 200 {
return Instance{}, errors.New("Invalid status code")
if _, err := testUrl(ApiUrl + "/streams/jNQXAC9IVRw"); err != nil {
errorChannel <- err
}
}()
return Instance{
Name: strings.TrimSpace(split[0]),
ApiUrl: ApiUrl,
Locations: strings.TrimSpace(split[2]),
Cdn: strings.TrimSpace(split[3]) == "Yes",
Registered: int(registered),
LastChecked: LastChecked,
Version: version,
UpToDate: strings.Contains(latest, hash),
Cache: cache_working,
S3Enabled: config.S3Enabled,
}, nil
for err := range errorChannel {
return Instance{}, err
}
return Instance{}, errors.New("Invalid line")
return Instance{
Name: strings.TrimSpace(split[0]),
ApiUrl: ApiUrl,
Locations: strings.TrimSpace(split[2]),
Cdn: strings.TrimSpace(split[3]) == "Yes",
Registered: int(registered),
LastChecked: lastChecked,
Version: version,
UpToDate: strings.Contains(latest, hash),
Cache: cacheWorking,
S3Enabled: config.S3Enabled,
}, nil
}
func monitorInstances() {
@ -184,7 +231,6 @@ func monitorInstances() {
}
if resp.StatusCode == 200 {
// parse the response
buf := new(strings.Builder)
_, err := io.Copy(buf, resp.Body)
@ -200,20 +246,20 @@ func monitorInstances() {
wg := sync.WaitGroup{}
for index, line := range lines {
if index < 3 {
fmt.Println(line)
// skip first two and last line
if index < 2 || index == len(lines)-1 {
continue
}
wg.Add(1)
go func(line string) {
defer wg.Done()
instance, err := getInstanceDetails(line, latest)
if err == nil {
instances = append(instances, instance)
} else {
log.Print(err)
}
wg.Done()
}(line)
}
wg.Wait()
@ -237,5 +283,6 @@ func main() {
return c.JSON(monitored_instances)
})
fmt.Println("Listening on http://localhost:3000")
app.Listen(":3000")
}