/
cmd.go
148 lines (126 loc) · 4.6 KB
/
cmd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package jira_watcher_controller
import (
"bytes"
"context"
"errors"
"github.com/openshift/ci-search/jira"
"github.com/openshift/ci-search/pkg/bigquery"
"github.com/openshift/library-go/pkg/controller/controllercmd"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/pkg/version"
"k8s.io/klog/v2"
"os"
jiraClient "sigs.k8s.io/prow/prow/jira"
"time"
)
type Options struct {
controllerContext *controllercmd.ControllerContext
DryRun bool
// Jira Options
JiraURL string
JiraSearch string
JiraTokenPath string
ShowPrivateMessages bool
// BigQuery Options
GoogleProjectID string
GoogleServiceAccountCredentialFile string
BigQueryRefreshInterval time.Duration
}
func NewJiraWatcherControllerCommand(name string) *cobra.Command {
o := &Options{
BigQueryRefreshInterval: 1 * time.Minute,
JiraURL: "https://issues.redhat.com",
JiraSearch: "(project=OCPBUGS&updated>='-14d'&affectedVersion IN versionMatch('4\\\\.\\\\d+')&level IN (null)) OR (project=TRT&updated>='-14d'&level IN (null))",
}
ccc := controllercmd.NewControllerCommandConfig("jira-watcher-controller", version.Get(), func(ctx context.Context, controllerContext *controllercmd.ControllerContext) error {
o.controllerContext = controllerContext
err := o.Validate(ctx)
if err != nil {
return err
}
err = o.Run(ctx)
if err != nil {
return err
}
return nil
})
ccc.DisableLeaderElection = true
cmd := ccc.NewCommandWithContext(context.Background())
cmd.Use = name
cmd.Short = "Start the JIRA Watcher Controller"
o.AddFlags(cmd.Flags())
return cmd
}
func (o *Options) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&o.JiraURL, "jira-url", o.JiraURL, "The URL of a Jira server to index issues from.")
fs.StringVar(&o.JiraTokenPath, "jira-token-file", o.JiraTokenPath, "A file to read a Jira token from.")
fs.StringVar(&o.JiraSearch, "jira-search", o.JiraSearch, "A JQL query to search for issues to index.")
fs.BoolVar(&o.ShowPrivateMessages, "show-private-messages", o.ShowPrivateMessages, "Display Jira comments that are flagged as private.")
fs.StringVar(&o.GoogleProjectID, "google-project-id", os.Getenv("GOOGLE_PROJECT_ID"), "Google project name.")
fs.StringVar(&o.GoogleServiceAccountCredentialFile, "google-service-account-credential-file", os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"), "location of a credential file described by https://cloud.google.com/docs/authentication/production")
fs.DurationVar(&o.BigQueryRefreshInterval, "bigquery-refresh-interval", o.BigQueryRefreshInterval, "How often to push comments into BigQuery. Defaults to 15 minutes.")
fs.BoolVar(&o.DryRun, "dry-run", o.DryRun, "Perform no actions.")
}
func (o *Options) Validate(ctx context.Context) error {
if len(o.JiraURL) == 0 {
return errors.New("--jira-url flag must be set")
}
if len(o.JiraTokenPath) == 0 {
return errors.New("--jira-token-file flag must be set")
}
if len(o.JiraSearch) == 0 {
return errors.New("--jira-search flag must be set")
}
if len(o.GoogleProjectID) == 0 {
return errors.New("--google-project-id flag must be set")
}
if len(o.GoogleServiceAccountCredentialFile) == 0 {
return errors.New("--google-service-account-credential-file flag must be set")
}
return nil
}
func (o *Options) Run(ctx context.Context) error {
if len(o.JiraSearch) == 0 {
klog.Exitf("--jira-search is required")
}
tokenData, err := os.ReadFile(o.JiraTokenPath)
if err != nil {
klog.Exitf("Failed to load --jira-token-file: %v", err)
}
options := func(options *jiraClient.Options) {
options.BearerAuth = func() (token string) {
return string(bytes.TrimSpace(tokenData))
}
}
jc, _ := jiraClient.NewClient(o.JiraURL, options)
c := &jira.Client{
Client: jc,
}
jiraInformer := jira.NewInformer(
c,
10*time.Minute, // Time before watcher starts
2*time.Hour, // How often to resync from jira
0, // Never resync items already in store
func(metav1.ListOptions) jira.SearchIssuesArgs {
return jira.SearchIssuesArgs{
Jql: o.JiraSearch,
}
},
jira.FilterPrivateIssues,
)
jiraLister := jira.NewIssueLister(jiraInformer.GetIndexer())
bqc, err := bigquery.NewBigQueryClient(o.GoogleProjectID, o.GoogleServiceAccountCredentialFile)
if err != nil {
klog.Fatalf("Unable to configure bigquery client: %v", err)
}
jiraWatcherController, err := NewJiraWatcherController(c, jiraInformer, jiraLister, o.ShowPrivateMessages, bqc, o.DryRun)
if err != nil {
return err
}
go jiraInformer.Run(ctx.Done())
go jiraWatcherController.RunWorkers(ctx, 1)
<-ctx.Done()
return nil
}