Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: add ding talk report support #143

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ cmd/external-plugins/cherrypicker/cherrypicker
cmd/external-plugins/needs-rebase/needs-rebase
cmd/external-plugins/refresh/refresh
cmd/ghproxy/ghproxy
pj.yaml
pod.yaml
*.min.js
vendor/


# Generated files by hugo
site/public/
Expand All @@ -77,4 +82,4 @@ hugo.linux

# Folders of common IDEs
.idea/
.vscode/
.vscode/
19 changes: 18 additions & 1 deletion cmd/crier/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"sigs.k8s.io/prow/pkg/config"
"sigs.k8s.io/prow/pkg/config/secret"
"sigs.k8s.io/prow/pkg/crier"
dingtalkreporter "sigs.k8s.io/prow/pkg/crier/reporters/dingtalk"
gcsreporter "sigs.k8s.io/prow/pkg/crier/reporters/gcs"
k8sgcsreporter "sigs.k8s.io/prow/pkg/crier/reporters/gcs/kubernetes"
gerritreporter "sigs.k8s.io/prow/pkg/crier/reporters/gerrit"
Expand Down Expand Up @@ -66,6 +67,7 @@ type options struct {
blobStorageWorkers int
k8sBlobStorageWorkers int
resultStoreWorkers int
dingTalkWorkers int

slackTokenFile string
additionalSlackTokenFiles slackclient.HostsFlag
Expand All @@ -83,7 +85,7 @@ type options struct {
}

func (o *options) validate() error {
if o.gerritWorkers+o.pubsubWorkers+o.githubWorkers+o.slackWorkers+o.blobStorageWorkers+o.k8sBlobStorageWorkers+o.resultStoreWorkers <= 0 {
if o.gerritWorkers+o.pubsubWorkers+o.githubWorkers+o.slackWorkers+o.blobStorageWorkers+o.k8sBlobStorageWorkers+o.resultStoreWorkers+o.dingTalkWorkers <= 0 {
return errors.New("crier need to have at least one report worker to start")
}

Expand Down Expand Up @@ -127,6 +129,7 @@ func (o *options) parseArgs(fs *flag.FlagSet, args []string) error {
fs.IntVar(&o.pubsubWorkers, "pubsub-workers", 0, "Number of pubsub report workers (0 means disabled)")
fs.IntVar(&o.githubWorkers, "github-workers", 0, "Number of github report workers (0 means disabled)")
fs.IntVar(&o.slackWorkers, "slack-workers", 0, "Number of Slack report workers (0 means disabled)")
fs.IntVar(&o.dingTalkWorkers, "dingtalk-workers", 0, "Number of DingTalk report workers (0 means disabled)")
fs.Var(&o.additionalSlackTokenFiles, "additional-slack-token-files", "Map of additional slack token files. example: --additional-slack-token-files=foo=/etc/foo-slack-tokens/token, repeat flag for each host")
fs.IntVar(&o.blobStorageWorkers, "blob-storage-workers", 0, "Number of blob storage report workers (0 means disabled)")
fs.IntVar(&o.k8sBlobStorageWorkers, "kubernetes-blob-storage-workers", 0, "Number of Kubernetes-specific blob storage report workers (0 means disabled)")
Expand Down Expand Up @@ -313,6 +316,20 @@ func main() {
}
}

if o.dingTalkWorkers > 0 {
hasReporter = true
if cfg().DingTalkReporterConfigs == nil {
logrus.Fatal("dingtalkreporter is enabled but has no config")
}
dingTalkConfig := func(refs *prowapi.Refs) config.DingTalkReporter {
return cfg().DingTalkReporterConfigs.GetDingTalkReporter(refs)
}
dingTalkReporter := dingtalkreporter.New(dingTalkConfig, o.dryrun)
if err := crier.New(mgr, dingTalkReporter, o.dingTalkWorkers, o.githubEnablement.EnablementChecker()); err != nil {
logrus.WithError(err).Fatal("failed to construct slack reporter controller")
}
}

if !hasReporter {
logrus.Fatalf("should have at least one controller to start crier.")
}
Expand Down
36 changes: 36 additions & 0 deletions cmd/crier/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,42 @@ func TestOptions(t *testing.T) {
instrumentationOptions: flagutil.DefaultInstrumentationOptions(),
},
},
//DingTalk Reporter
{
name: "dingTalk workers, sets workers",
args: []string{"--dingtalk-workers=13", "--config-path=foo"},
expected: &options{
dingTalkWorkers: 13,
config: configflagutil.ConfigOptions{
ConfigPathFlagName: "config-path",
JobConfigPathFlagName: "job-config-path",
ConfigPath: "foo",
SupplementalProwConfigsFileNameSuffix: "_prowconfig.yaml",
InRepoConfigCacheSize: 200,
},
github: defaultGitHubOptions,
k8sReportFraction: 1.0,
instrumentationOptions: flagutil.DefaultInstrumentationOptions(),
},
},
{
name: "dingTalk with --dry-run, sets",
args: []string{"--dingtalk-workers=13", "--config-path=foo", "--dry-run"},
expected: &options{
dingTalkWorkers: 13,
config: configflagutil.ConfigOptions{
ConfigPathFlagName: "config-path",
JobConfigPathFlagName: "job-config-path",
ConfigPath: "foo",
SupplementalProwConfigsFileNameSuffix: "_prowconfig.yaml",
InRepoConfigCacheSize: 200,
},
dryrun: true,
github: defaultGitHubOptions,
k8sReportFraction: 1.0,
instrumentationOptions: flagutil.DefaultInstrumentationOptions(),
},
},
{
name: "k8s-gcs enables k8s-gcs",
args: []string{"--kubernetes-blob-storage-workers=3", "--config-path=foo"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9633,6 +9633,30 @@ spec:
reporter_config:
description: ReporterConfig holds reporter-specific configuration
properties:
dingtalk:
properties:
job_states_to_report:
items:
description: ProwJobState specifies whether the job is running
type: string
type: array
report:
description: |-
Report is derived from JobStatesToReport, it's used for differentiating
nil from empty slice, as yaml roundtrip by design can't tell the
difference when omitempty is supplied.
See https://github.com/kubernetes/test-infra/pull/24168 for details
Priority-wise, it goes by following order:
- `report: true/false`` in job config
- `JobStatesToReport: <anything including empty slice>` in job config
- `report: true/false`` in global config
- `JobStatesToReport:` in global config
type: boolean
report_template:
type: string
token:
type: string
type: object
slack:
properties:
channel:
Expand Down
49 changes: 49 additions & 0 deletions pkg/apis/prowjobs/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,8 @@ func (rac *RerunAuthConfig) IsAllowAnyone() bool {

type ReporterConfig struct {
Slack *SlackReporterConfig `json:"slack,omitempty"`

DingTalk *DingTalkReporterConfig `json:"dingtalk,omitempty"`
}

type SlackReporterConfig struct {
Expand Down Expand Up @@ -410,6 +412,53 @@ func (src *SlackReporterConfig) ApplyDefault(def *SlackReporterConfig) *SlackRep
return &merged
}

type DingTalkReporterConfig struct {
Token string `json:"token,omitempty"`
JobStatesToReport []ProwJobState `json:"job_states_to_report,omitempty"`
ReportTemplate string `json:"report_template,omitempty"`
// Report is derived from JobStatesToReport, it's used for differentiating
// nil from empty slice, as yaml roundtrip by design can't tell the
// difference when omitempty is supplied.
// See https://github.com/kubernetes/test-infra/pull/24168 for details
// Priority-wise, it goes by following order:
// - `report: true/false`` in job config
// - `JobStatesToReport: <anything including empty slice>` in job config
// - `report: true/false`` in global config
// - `JobStatesToReport:` in global config
Report *bool `json:"report,omitempty"`
}

// / ApplyDefault is called by jobConfig.ApplyDefault(globalConfig)
func (src *DingTalkReporterConfig) ApplyDefault(def *DingTalkReporterConfig) *DingTalkReporterConfig {
if src == nil && def == nil {
return nil
}
var merged DingTalkReporterConfig
if src != nil {
merged = *src.DeepCopy()
} else {
merged = *def.DeepCopy()
}
if src == nil || def == nil {
return &merged
}

if merged.Token == "" {
merged.Token = def.Token
}
// Note: `job_states_to_report: []` also results in JobStatesToReport == nil
if merged.JobStatesToReport == nil {
merged.JobStatesToReport = def.JobStatesToReport
}
if merged.ReportTemplate == "" {
merged.ReportTemplate = def.ReportTemplate
}
if merged.Report == nil {
merged.Report = def.Report
}
return &merged
}

// Duration is a wrapper around time.Duration that parses times in either
// 'integer number of nanoseconds' or 'duration string' formats and serializes
// to 'duration string' format.
Expand Down
29 changes: 29 additions & 0 deletions pkg/apis/prowjobs/v1/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,35 @@ func TestSlackConfigApplyDefaultsAppliesDefaultsForAllFields(t *testing.T) {
}
}

func TestDingTalkConfigApplyDefaultsAppliesDefaultsForAllFields(t *testing.T) {
t.Parallel()
seed := time.Now().UnixNano()
// Print the seed so failures can easily be reproduced
t.Logf("Seed: %d", seed)
fuzzer := fuzz.NewWithSeed(seed)
for i := 0; i < 100; i++ {
t.Run(strconv.Itoa(i), func(t *testing.T) {
def := &DingTalkReporterConfig{}
fuzzer.Fuzz(def)

// Each of those three has its own DeepCopy and in case it is nil,
// we just call that and return. In order to make this test verify
// that copying of their fields also works, we have to set them to
// something non-nil.
toDefault := &DingTalkReporterConfig{
Token: "",
JobStatesToReport: nil,
ReportTemplate: "",
}
defaulted := toDefault.ApplyDefault(def)

if diff := cmp.Diff(def, defaulted); diff != "" {
t.Errorf("defaulted decoration config didn't get all fields defaulted: %s", diff)
}
})
}
}

func TestRefsToString(t *testing.T) {
var tests = []struct {
name string
Expand Down
31 changes: 31 additions & 0 deletions pkg/apis/prowjobs/v1/zz_generated.deepcopy.go

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

Loading