package job import ( "encoding/json" goerrors "errors" "fmt" "edge-infra.dev/pkg/lib/build/semver" ) var ( ErrIncomplete = goerrors.New("incomplete data pprovided to struct") ErrSemVerInvalid = goerrors.New("version is not valid") ) type StartedOption func(*Started) type Started struct { timestamp int64 // machine job was ran on, can be used to identify failures clustered around // one or more faulty nodes, or correlating failures with nodes under load, etc machine string // Optional PR number pull string commit string repo string // Complete SemVer, should be unique per commit to master. Either generated // by the job, or used to download artifacts produced by another job (e.g., periodic // e2e test) version string } type StartedJSON struct { Timestamp int64 `json:"timestamp,omitempty"` Machine string `json:"machine,omitempty"` Pull string `json:"pull,omitempty"` Commit string `json:"commit,omitempty"` Repo string `json:"repo,omitempty"` Version string `json:"version,omitempty"` } type FinishedOption func(*Finished) type Finished struct { // empty value indicates incomplete job timestamp int64 passed bool // TestGrid uses map[string]interface{} here, I think we can likely get away // with just strings metadata map[string]string } type FinishedJSON struct { Timestamp int64 `json:"timestamp,omitempty"` Passed bool `json:"passed,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` } func NewStarted(opts ...StartedOption) (*Started, error) { s := &Started{} for _, opt := range opts { opt(s) } // check that all the mandatory data exists if s.commit == "" || s.machine == "" || s.repo == "" || s.timestamp == 0 || s.version == "" { return &Started{}, fmt.Errorf("%w", ErrIncomplete) } // check that the semver is valid if err := semver.IsValidSemver(s.version); err != nil { return &Started{}, fmt.Errorf("%s %w", s.version, ErrSemVerInvalid) } return s, nil } func (s *Started) MarshalJSON() ([]byte, error) { return json.Marshal(&StartedJSON{ Timestamp: s.timestamp, Machine: s.machine, Pull: s.pull, Commit: s.commit, Repo: s.repo, Version: s.version, }) } func (s *Started) ToJSON() ([]byte, error) { startedjson, err := json.Marshal(s) if err != nil { return nil, err } return startedjson, nil } func WithCommit(commit string) StartedOption { return func(o *Started) { o.commit = commit } } func WithTimestamp(timestamp int64) StartedOption { return func(o *Started) { o.timestamp = timestamp } } func WithMachine(machine string) StartedOption { return func(o *Started) { o.machine = machine } } func WithPull(pull string) StartedOption { return func(o *Started) { o.pull = pull } } func WithRepo(repo string) StartedOption { return func(o *Started) { o.repo = repo } } func WithVersion(version string) StartedOption { return func(o *Started) { o.version = version } } func NewFinished(opts ...FinishedOption) (*Finished, error) { f := &Finished{} for _, opt := range opts { opt(f) } // check that all the mandatory data exists if f.timestamp == 0 { return &Finished{}, fmt.Errorf("%w", ErrIncomplete) } return f, nil } func (f *Finished) ToJSON() ([]byte, error) { jsonfile, err := json.Marshal(f) if err != nil { return nil, err } return jsonfile, nil } func (f *Finished) MarshalJSON() ([]byte, error) { return json.Marshal(&FinishedJSON{ Timestamp: f.timestamp, Passed: f.passed, Metadata: f.metadata, }) } func WithFinishedTimestamp(timestamp int64) FinishedOption { return func(o *Finished) { o.timestamp = timestamp } } func WithPassed(passed bool) FinishedOption { return func(f *Finished) { f.passed = passed } } func WithMetadata(metadata map[string]string) FinishedOption { return func(f *Finished) { f.metadata = metadata } }