Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions pkg/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,25 @@ func IsPromotionJob(jobLabels map[string]string) bool {
return ok
}

var (
DefaultSlackReporterJobStatesToReport = []prowv1.ProwJobState{
prowv1.SuccessState,
prowv1.FailureState,
prowv1.ErrorState,
}
DefaultSlackReporterReportTemplate = `{{if eq .Status.State "success"}} :slack-green: Job *{{.Spec.Job}}* ended with *{{.Status.State}}*. <{{.Status.URL}}|View logs> {{else}} :failed: Job *{{.Spec.Job}}* ended with *{{.Status.State}}*. <{{.Status.URL}}|View logs> {{end}}`
)

type SlackReporterConfig struct {
Channel string `json:"channel"`
JobStatesToReport []prowv1.ProwJobState `json:"job_states_to_report,omitempty"`
ReportTemplate string `json:"report_template,omitempty"`
// ReportPresubmit controls whether the presubmit job generated from a
// periodic test with `presubmit: true` also gets this slack config.
// Only valid when the test has `presubmit: true`.
ReportPresubmit bool `json:"report_presubmit,omitempty"`
}

type ProwgenOverrides struct {
DisableRehearsals bool `json:"disable_rehearsals,omitempty"`
SkipOperatorPresubmits bool `json:"skip_operator_presubmits,omitempty"`
Expand Down Expand Up @@ -885,6 +904,9 @@ type TestStepConfiguration struct {
// MaxConcurrency sets the maximum number of this job running concurrently. 0 means no limit.
MaxConcurrency int `json:"max_concurrency,omitempty"`

// SlackReporterConfig configures Slack notifications for this test's generated jobs.
SlackReporterConfig *SlackReporterConfig `json:"reporter_config,omitempty"`

// Only one of the following can be not-null.
ContainerTestConfiguration *ContainerTestConfiguration `json:"container,omitempty"`
MultiStageTestConfiguration *MultiStageTestConfiguration `json:"steps,omitempty"`
Expand Down
47 changes: 36 additions & 11 deletions pkg/api/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

85 changes: 55 additions & 30 deletions pkg/prowgen/prowgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,24 +98,26 @@ func GenerateJobs(configSpec *cioperatorapi.ReleaseBuildConfiguration, info *Pro
options.DisableRehearsal = disableRehearsal
options.Retry = element.Retry
options.MaxConcurrency = element.MaxConcurrency
options.SlackReporterConfig = element.SlackReporterConfig
})
periodics = append(periodics, *periodic)
if element.Presubmit {
handlePresubmit(g, element, info, name, disableRehearsal, configSpec.Resources.RequirementsForStep(element.As).Requests, presubmits, orgrepo)
handlePresubmit(g, element, info, name, disableRehearsal, configSpec.Resources.RequirementsForStep(element.As).Requests, presubmits, orgrepo, true)
}
} else if element.Postsubmit {
postsubmit := generatePostsubmitForTest(g, info, func(options *generatePostsubmitOptions) {
options.runIfChanged = element.RunIfChanged
options.Capabilities = element.Capabilities
options.skipIfOnlyChanged = element.SkipIfOnlyChanged
options.slackReporterConfig = element.SlackReporterConfig
})
postsubmit.MaxConcurrency = 1
if element.MaxConcurrency != 0 {
postsubmit.MaxConcurrency = element.MaxConcurrency
}
postsubmits[orgrepo] = append(postsubmits[orgrepo], *postsubmit)
} else {
handlePresubmit(g, element, info, name, disableRehearsal, configSpec.Resources.RequirementsForStep(element.As).Requests, presubmits, orgrepo)
handlePresubmit(g, element, info, name, disableRehearsal, configSpec.Resources.RequirementsForStep(element.As).Requests, presubmits, orgrepo, false)
}
}
}
Expand Down Expand Up @@ -222,7 +224,11 @@ func GenerateJobs(configSpec *cioperatorapi.ReleaseBuildConfiguration, info *Pro
}, nil
}

func handlePresubmit(g *prowJobBaseBuilder, element cioperatorapi.TestStepConfiguration, info *ProwgenInfo, name string, disableRehearsal bool, requests cioperatorapi.ResourceList, presubmits map[string][]prowconfig.Presubmit, orgrepo string) {
func handlePresubmit(g *prowJobBaseBuilder, element cioperatorapi.TestStepConfiguration, info *ProwgenInfo, name string, disableRehearsal bool, requests cioperatorapi.ResourceList, presubmits map[string][]prowconfig.Presubmit, orgrepo string, fromPeriodic bool) {
slackConfig := element.SlackReporterConfig
if fromPeriodic && (slackConfig == nil || !slackConfig.ReportPresubmit) {
slackConfig = nil
}
presubmit := generatePresubmitForTest(g, name, info, func(options *generatePresubmitOptions) {
options.pipelineRunIfChanged = element.PipelineRunIfChanged
options.pipelineSkipIfOnlyChanged = element.PipelineSkipIfOnlyChanged
Expand All @@ -233,6 +239,7 @@ func handlePresubmit(g *prowJobBaseBuilder, element cioperatorapi.TestStepConfig
options.optional = element.Optional
options.disableRehearsal = disableRehearsal
options.maxConcurrency = element.MaxConcurrency
options.slackReporterConfig = slackConfig
})
v, requestingKVM := requests[cioperatorapi.KVMDeviceLabel]
if requestingKVM {
Expand All @@ -251,6 +258,7 @@ type generatePresubmitOptions struct {
optional bool
disableRehearsal bool
maxConcurrency int
slackReporterConfig *cioperatorapi.SlackReporterConfig
}

func (opts *generatePresubmitOptions) shouldAlwaysRun() bool {
Expand All @@ -259,18 +267,36 @@ func (opts *generatePresubmitOptions) shouldAlwaysRun() bool {

type generatePresubmitOption func(options *generatePresubmitOptions)

// addSlackReporterConfig sets the Slack reporter configuration on a job base if one is found
func addSlackReporterConfig(base *prowconfig.JobBase, jobName, testName string, info *ProwgenInfo) {
if slackReporter := info.Config.GetSlackReporterConfigForJobName(jobName, testName, info.Metadata.Variant); slackReporter != nil {
if base.ReporterConfig == nil {
base.ReporterConfig = &prowv1.ReporterConfig{}
// slackReporterConfig returns the Slack reporter configuration for a job.
// Per-test config (from ci-operator config) takes precedence over .config.prowgen.
func slackReporterConfig(jobName, testName string, testSlackConfig *cioperatorapi.SlackReporterConfig, info *ProwgenInfo) *prowv1.ReporterConfig {
if testSlackConfig != nil {
jobStatesToReport := testSlackConfig.JobStatesToReport
if len(jobStatesToReport) == 0 {
jobStatesToReport = cioperatorapi.DefaultSlackReporterJobStatesToReport
}
reportTemplate := testSlackConfig.ReportTemplate
if reportTemplate == "" {
reportTemplate = cioperatorapi.DefaultSlackReporterReportTemplate
}
base.ReporterConfig.Slack = &prowv1.SlackReporterConfig{
Channel: slackReporter.Channel,
JobStatesToReport: slackReporter.JobStatesToReport,
ReportTemplate: slackReporter.ReportTemplate,
return &prowv1.ReporterConfig{
Slack: &prowv1.SlackReporterConfig{
Channel: testSlackConfig.Channel,
JobStatesToReport: jobStatesToReport,
ReportTemplate: reportTemplate,
},
}
}
if slackReporter := info.Config.GetSlackReporterConfigForJobName(jobName, testName, info.Metadata.Variant); slackReporter != nil {
return &prowv1.ReporterConfig{
Slack: &prowv1.SlackReporterConfig{
Channel: slackReporter.Channel,
JobStatesToReport: slackReporter.JobStatesToReport,
ReportTemplate: slackReporter.ReportTemplate,
},
}
}
return nil
}

func generatePresubmitForTest(jobBaseBuilder *prowJobBaseBuilder, name string, info *ProwgenInfo, options ...generatePresubmitOption) *prowconfig.Presubmit {
Expand All @@ -282,9 +308,8 @@ func generatePresubmitForTest(jobBaseBuilder *prowJobBaseBuilder, name string, i
shortName := info.TestName(name)
base := jobBaseBuilder.Rehearsable(!opts.disableRehearsal).Build(jc.PresubmitPrefix)

// Set slack reporter config using full job name for proper excluded_job_patterns matching
fullJobName := info.JobName(jc.PresubmitPrefix, name)
addSlackReporterConfig(&base, fullJobName, name, info)
base.ReporterConfig = slackReporterConfig(fullJobName, name, opts.slackReporterConfig, info)

pipelineOpt := false
if opts.pipelineRunIfChanged != "" {
Expand Down Expand Up @@ -326,9 +351,10 @@ func generatePresubmitForTest(jobBaseBuilder *prowJobBaseBuilder, name string, i
}

type generatePostsubmitOptions struct {
runIfChanged string
Capabilities []string
skipIfOnlyChanged string
runIfChanged string
Capabilities []string
skipIfOnlyChanged string
slackReporterConfig *cioperatorapi.SlackReporterConfig
}

type generatePostsubmitOption func(options *generatePostsubmitOptions)
Expand All @@ -341,10 +367,9 @@ func generatePostsubmitForTest(jobBaseBuilder *prowJobBaseBuilder, info *Prowgen

base := jobBaseBuilder.Build(jc.PostsubmitPrefix)

// Set slack reporter config using full job name for proper excluded_job_patterns matching
testName := jobBaseBuilder.testName
fullJobName := info.JobName(jc.PostsubmitPrefix, testName)
addSlackReporterConfig(&base, fullJobName, testName, info)
base.ReporterConfig = slackReporterConfig(fullJobName, testName, opts.slackReporterConfig, info)

alwaysRun := opts.runIfChanged == "" && opts.skipIfOnlyChanged == ""
pj := &prowconfig.Postsubmit{
Expand Down Expand Up @@ -373,15 +398,16 @@ func hashDailyCron(job string) string {
}

type GeneratePeriodicOptions struct {
Interval string
MinimumInterval string
Capabilities []string
Cron string
ReleaseController bool
PathAlias *string
DisableRehearsal bool
Retry *prowconfig.Retry
MaxConcurrency int
Interval string
MinimumInterval string
Capabilities []string
Cron string
ReleaseController bool
PathAlias *string
DisableRehearsal bool
Retry *prowconfig.Retry
MaxConcurrency int
SlackReporterConfig *cioperatorapi.SlackReporterConfig
}

type GeneratePeriodicOption func(options *GeneratePeriodicOptions)
Expand All @@ -401,10 +427,9 @@ func GeneratePeriodicForTest(jobBaseBuilder *prowJobBaseBuilder, info *ProwgenIn
// We are resetting PathAlias because it will be set on the `ExtraRefs` item
base := jobBaseBuilder.Rehearsable(!opts.DisableRehearsal).PathAlias("").Build(jc.PeriodicPrefix)

// Set slack reporter config using full job name for proper excluded_job_patterns matching
testName := jobBaseBuilder.testName
fullJobName := info.JobName(jc.PeriodicPrefix, testName)
addSlackReporterConfig(&base, fullJobName, testName, info)
base.ReporterConfig = slackReporterConfig(fullJobName, testName, opts.SlackReporterConfig, info)

cron := opts.Cron
if cron == "@daily" {
Expand Down
9 changes: 9 additions & 0 deletions pkg/validation/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,15 @@ func (v *Validator) validateTestStepConfiguration(
}
}

if test.SlackReporterConfig != nil {
if test.SlackReporterConfig.Channel == "" {
validationErrors = append(validationErrors, fmt.Errorf("%s.reporter_config.channel: must be set", fieldRootN))
}
if test.SlackReporterConfig.ReportPresubmit && !test.Presubmit {
validationErrors = append(validationErrors, fmt.Errorf("%s.reporter_config.report_presubmit: can only be set when the test has `presubmit: true`", fieldRootN))
}
}

maxJobTimeout := time.Hour * 72
if test.Timeout != nil && test.Timeout.Duration > maxJobTimeout {
validationErrors = append(validationErrors, fmt.Errorf("%s: job timeout is limited to %s", fieldRootN, maxJobTimeout))
Expand Down
20 changes: 20 additions & 0 deletions pkg/webreg/zz_generated.ci_operator_reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,16 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" +
" # The job must be configured as a verification or periodic job in a\n" +
" # release-controller config file when this field is set to `true`.\n" +
" release_controller: true\n" +
" # SlackReporterConfig configures Slack notifications for this test's generated jobs.\n" +
" reporter_config:\n" +
" channel: ' '\n" +
" job_states_to_report:\n" +
" - \"\"\n" +
" # ReportPresubmit controls whether the presubmit job generated from a\n" +
" # periodic test with `presubmit: true` also gets this slack config.\n" +
" # Only valid when the test has `presubmit: true`.\n" +
" report_presubmit: true\n" +
" report_template: ' '\n" +
" # RestrictNetworkAccess restricts network access to RedHat intranet.\n" +
" restrict_network_access: false\n" +
" # Retry is a configuration entry for retrying periodic prowjobs\n" +
Expand Down Expand Up @@ -1997,6 +2007,16 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" +
" # The job must be configured as a verification or periodic job in a\n" +
" # release-controller config file when this field is set to `true`.\n" +
" release_controller: true\n" +
" # SlackReporterConfig configures Slack notifications for this test's generated jobs.\n" +
" reporter_config:\n" +
" channel: ' '\n" +
" job_states_to_report:\n" +
" - \"\"\n" +
" # ReportPresubmit controls whether the presubmit job generated from a\n" +
" # periodic test with `presubmit: true` also gets this slack config.\n" +
" # Only valid when the test has `presubmit: true`.\n" +
" report_presubmit: true\n" +
" report_template: ' '\n" +
" # RestrictNetworkAccess restricts network access to RedHat intranet.\n" +
" restrict_network_access: false\n" +
" # Retry is a configuration entry for retrying periodic prowjobs\n" +
Expand Down
Loading