plugins/teststeps/exec/ocp_parser.go (167 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
package exec
import (
"fmt"
"github.com/facebookincubator/contest/pkg/event/testevent"
"github.com/facebookincubator/contest/pkg/target"
"github.com/facebookincubator/contest/pkg/xcontext"
)
type Severity string
const (
SeverityInfo = Severity("INFO")
SeverityDebug = Severity("DEBUG")
SeverityWarning = Severity("WARNING")
SeverityError = Severity("ERROR")
SeverityFatal = Severity("FATAL")
)
type Status string
const (
StatusUnkown = Status("UNKNOWN")
StatusComplete = Status("COMPLETE")
StatusError = Status("ERROR")
StatusSkipped = Status("SKIPPED")
)
type Result string
const (
ResultPass = Result("PASS")
ResultFail = Result("FAIL")
ResultNA = Result("NOT_APPLICABLE")
)
// TODO: these should just be temporary until the go:generate tool
// TODO: this should also mean refactoring all the parser code
type Log struct {
Severity Severity `json:"severity,omitempty"`
Text string `json:"text,omitempty"`
}
type RunStart struct {
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`
}
type RunEnd struct {
Name string `json:"name,omitempty"`
Status Status `json:"status,omitempty"`
Result Result `json:"result,omitempty"`
}
type RunArtifact struct {
RunStart *RunStart `json:"testRunStart,omitempty"`
RunEnd *RunEnd `json:"testRunEnd,omitempty"`
Log *Log `json:"log,omitempty"`
}
type StepStart struct {
Name string `json:"name,omitempty"`
}
type StepEnd struct {
Name string `json:"name,omitempty"`
Status Status `json:"status,omitempty"`
}
type StepArtifact struct {
StepId string `json:"testStepId,omitempty"`
StepStart *StepStart `json:"testStepStart,omitempty"`
StepEnd *StepEnd `json:"testStepEnd,omitempty"`
Log *Log `json:"log,omitempty"`
}
type OCPRoot struct {
SequenceNumber int `json:"sequenceNumber"`
Timestamp string `json:"timestamp"`
RunArtifact *RunArtifact `json:"testRunArtifact,omitempty"`
StepArtifact *StepArtifact `json:"testStepArtifact,omitempty"`
}
// TODO: check if there can be multiple runs in the same output
type OCPState struct {
RunEnd *RunEnd
}
func (s OCPState) Error() error {
if s.RunEnd == nil {
return fmt.Errorf("did not see a complete run")
}
if s.RunEnd.Result != ResultPass {
// TODO: add a concat log of errors?
return fmt.Errorf("test failed")
}
return nil
}
type OCPEventParser struct {
target *target.Target
ev testevent.Emitter
state OCPState
}
func NewOCPEventParser(target *target.Target, ev testevent.Emitter) *OCPEventParser {
return &OCPEventParser{
target: target,
ev: ev,
state: OCPState{},
}
}
func (p *OCPEventParser) parseRun(ctx xcontext.Context, node *RunArtifact, root *OCPRoot) error {
if node.RunStart != nil {
payload := testStartEventPayload{
SequenceNumber: root.SequenceNumber,
Timestamp: root.Timestamp,
Name: node.RunStart.Name,
Version: node.RunStart.Version,
}
return emitEvent(ctx, TestStartEvent, payload, p.target, p.ev)
}
if node.RunEnd != nil {
if node.RunEnd.Status == StatusComplete {
p.state.RunEnd = node.RunEnd
}
payload := testEndEventPayload{
SequenceNumber: root.SequenceNumber,
Timestamp: root.Timestamp,
Name: node.RunEnd.Name,
Status: string(node.RunEnd.Status),
Result: string(node.RunEnd.Result),
}
return emitEvent(ctx, TestEndEvent, payload, p.target, p.ev)
}
if node.Log != nil {
payload := testLogEventPayload{
SequenceNumber: root.SequenceNumber,
Timestamp: root.Timestamp,
Severity: string(node.Log.Severity),
Message: node.Log.Text,
}
return emitEvent(ctx, TestLogEvent, payload, p.target, p.ev)
}
return nil
}
func (p *OCPEventParser) parseStep(ctx xcontext.Context, node *StepArtifact, root *OCPRoot) error {
if node.StepStart != nil {
payload := stepStartEventPayload{
SequenceNumber: root.SequenceNumber,
Timestamp: root.Timestamp,
StepId: node.StepId,
Name: node.StepStart.Name,
}
return emitEvent(ctx, StepStartEvent, payload, p.target, p.ev)
}
if node.StepEnd != nil {
payload := stepEndEventPayload{
SequenceNumber: root.SequenceNumber,
Timestamp: root.Timestamp,
StepId: node.StepId,
Name: node.StepEnd.Name,
Status: string(node.StepEnd.Status),
}
return emitEvent(ctx, StepEndEvent, payload, p.target, p.ev)
}
if node.Log != nil {
payload := stepLogEventPayload{
SequenceNumber: root.SequenceNumber,
Timestamp: root.Timestamp,
StepId: node.StepId,
Severity: string(node.Log.Severity),
Message: node.Log.Text,
}
return emitEvent(ctx, StepLogEvent, payload, p.target, p.ev)
}
return nil
}
func (ep *OCPEventParser) Parse(ctx xcontext.Context, root *OCPRoot) error {
if root.RunArtifact != nil {
return ep.parseRun(ctx, root.RunArtifact, root)
}
if root.StepArtifact != nil {
return ep.parseStep(ctx, root.StepArtifact, root)
}
return nil
}
func (ep *OCPEventParser) Error() error {
return ep.state.Error()
}