blob: b76bcf1fc4c0e14ae7de67098d07a9ab01e33b54 [file] [log] [blame]
package main
import (
"context"
"flag"
"fmt"
"strings"
"time"
"go.skia.org/infra/go/common"
"go.skia.org/infra/go/sklog"
"go.skia.org/infra/go/util"
"go.skia.org/infra/task_scheduler/go/db"
"go.skia.org/infra/task_scheduler/go/db/firestore"
"go.skia.org/infra/task_scheduler/go/types"
"golang.org/x/oauth2/google"
)
const (
TIME_CHUNK = 24 * time.Hour
)
var (
local = flag.Bool("local", false, "True if running locally.")
fsInstance = flag.String("firestore_instance", "", "Firestore instance to validate.")
BEGINNING_OF_TIME = time.Date(2016, time.September, 1, 0, 0, 0, 0, time.UTC)
NOW = time.Now()
)
type timeChunk struct {
start time.Time
end time.Time
}
func validateTask(task *types.Task) error {
if task.Id == "" {
return fmt.Errorf("Id not set.")
}
if util.TimeIsZero(task.DbModified) {
return fmt.Errorf("DbModified not set. Task %s DbModified time is %s.", task.Id, task.DbModified)
}
if util.TimeIsZero(task.Created) {
return fmt.Errorf("Created not set. Task %s created time is %s.", task.Id, task.Created)
}
if !time.Now().After(task.DbModified) {
return fmt.Errorf("Task %s modification time is in the future: %s (current time is %s).", task.Id, task.DbModified, NOW)
}
return task.Validate()
}
func validateTasks(ctx context.Context, d db.TaskReader, chunks []*timeChunk) ([]string, error) {
invalidIds := []string{}
for _, chunk := range chunks {
sklog.Infof("Validating tasks in %s - %s", chunk.start, chunk.end)
tasks, err := d.GetTasksFromDateRange(ctx, chunk.start, chunk.end, "")
if err != nil {
return nil, err
}
for _, t := range tasks {
if err := validateTask(t); err != nil {
sklog.Errorf("%s %+v", err, t)
invalidIds = append(invalidIds, t.Id)
continue
}
}
}
return invalidIds, nil
}
func validateJob(job *types.Job) error {
if job.Id == "" {
return fmt.Errorf("Id not set.")
}
if util.TimeIsZero(job.Created) {
return fmt.Errorf("Created not set. Job %s created time is %s.", job.Id, job.Created)
}
if util.TimeIsZero(job.DbModified) {
return fmt.Errorf("DbModified not set. Job %s DbModified time is %s.", job.Id, job.DbModified)
}
if !time.Now().After(job.DbModified) {
return fmt.Errorf("Job %s modification time is in the future: %s (current time is %s).", job.Id, job.DbModified, NOW)
}
if !job.RepoState.Valid() {
return fmt.Errorf("Job %s RepoState is invalid.", job.Id)
}
return nil
}
func validateJobs(ctx context.Context, d db.JobReader, chunks []*timeChunk) ([]string, error) {
invalidIds := []string{}
for _, chunk := range chunks {
sklog.Infof("Validating jobs in %s - %s", chunk.start, chunk.end)
jobs, err := d.GetJobsFromDateRange(ctx, chunk.start, chunk.end, "")
if err != nil {
return nil, err
}
for _, j := range jobs {
if err := validateJob(j); err != nil {
sklog.Errorf("%s %+v", err, j)
invalidIds = append(invalidIds, j.Id)
}
}
}
return invalidIds, nil
}
func validate(ctx context.Context, d db.DB) error {
chunks := make([]*timeChunk, 0, 1000)
if err := util.IterTimeChunks(BEGINNING_OF_TIME, NOW, TIME_CHUNK, func(start, end time.Time) error {
chunks = append(chunks, &timeChunk{
start: start,
end: end,
})
return nil
}); err != nil {
return err
}
// Reverse.
for i, j := 0, len(chunks)-1; i < j; i, j = i+1, j-1 {
chunks[i], chunks[j] = chunks[j], chunks[i]
}
invalidTasks, err := validateTasks(ctx, d, chunks)
if err != nil {
return err
}
invalidJobs, err := validateJobs(ctx, d, chunks)
if err != nil {
return err
}
if len(invalidTasks) != 0 || len(invalidJobs) != 0 {
return fmt.Errorf("Found %d invalid tasks and %d invalid jobs.\nTasks:\n%s\nJobs:\n%s", len(invalidTasks), len(invalidJobs), strings.Join(invalidTasks, "\n"), strings.Join(invalidJobs, "\n"))
}
sklog.Infof("All tasks and jobs are valid!")
return nil
}
func main() {
common.Init()
if *fsInstance == "" {
sklog.Fatal("--firestore_instance is required.")
}
ctx := context.Background()
ts, err := google.DefaultTokenSource(ctx)
if err != nil {
sklog.Fatal(err)
}
d, err := firestore.NewDBWithParams(ctx, firestore.FIRESTORE_PROJECT, *fsInstance, ts)
if err != nil {
sklog.Fatal(err)
}
defer util.Close(d)
if err := validate(ctx, d); err != nil {
sklog.Fatal(err)
}
}