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

trt-1204: option to save job run identifier json #2

Merged
merged 1 commit into from
Oct 20, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,30 @@ $ ./gangway-cli \
--latest "registry.ci.openshift.org/ocp/release:4.14.0-0.nightly-2023-07-05-071214" \
--job-name periodic-ci-openshift-release-master-ci-4.14-e2e-aws-ovn
```


Use --jobs-file-path to capture JobRunIdentifier JSON which can be fed into other tooling like aggregation analysis

```
$ ./gangway-cli \
--api-url="https://gangway-ci.apps.somecluster.org" \
--initial "registry.ci.openshift.org/ocp/release:4.14.0-0.nightly-2023-07-05-071214" \
--latest "registry.ci.openshift.org/ocp/release:4.14.0-0.nightly-2023-07-05-071214" \
--job-name periodic-ci-openshift-release-master-ci-4.15-upgrade-from-stable-4.14-e2e-aws-ovn-upgrade \
--n 10 \
--jobs-file-path="/path/to/results/for/gangway/"
```

Example input to job-run-aggregator

```
./job-run-aggregator analyze-job-runs \
--timeout=7h \
--working-dir /path/to/results/for/testaggregation \
--google-service-account-credential-file /path/to/gcp/credentials.json \
--job periodic-ci-openshift-release-master-ci-4.15-upgrade-from-stable-4.14-e2e-aws-ovn-upgrade \
--payload-tag GANGWAY \
--job-start-time 2023-10-18T13:20:13Z \
--static-run-info-path=/path/to/results/for/gangway/gangway_periodic-ci-openshift-release-master-ci-4.15-upgrade-from-stable-4.14-e2e-aws-ovn-upgrade_2023-10-18T13:20:13.15912424-04:00.json

```
75 changes: 64 additions & 11 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,21 @@ import (
const strFmt = "%-3s | %-38s | %-80s\n"
const maxJobs = 20

// JobRunIdentifier is mirrored to https://github.com/openshift/ci-tools/blob/91aeed7425cb6738b6e36e7f511787b55c9e5267/pkg/jobrunaggregator/jobrunaggregatorlib/util.go#L43
// the output of this command can be fed into ci-tools aggregation commands for further analysis
type JobRunIdentifier struct {
JobName string
JobRunID string
}

var opts struct {
initial string
latest string
envVariables []string
jobName string
apiURL string
num int
jobsFilePath string
}

var cmd = &cobra.Command{
Expand All @@ -36,7 +44,7 @@ var cmd = &cobra.Command{
// Get the MY_APPCI_TOKEN environment variable
appCIToken := os.Getenv("MY_APPCI_TOKEN")
if appCIToken == "" {
cmd.Usage() //nolint
cmd.Usage() // nolint
return fmt.Errorf("cluster token required; please set the MY_APPCI_TOKEN variable")
}

Expand All @@ -55,7 +63,7 @@ var cmd = &cobra.Command{
for _, envVar := range opts.envVariables {
splitVar := strings.SplitN(envVar, "=", 2)
if len(splitVar) != 2 {
cmd.Usage() //nolint
cmd.Usage() // nolint
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lack of space is intentional golangci/golangci-lint#3109 (comment)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although lint seems to pass with the space, not sure why, but directive format in go is // followed by the directive directly without whitespace.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the change wasn't intentional on my part. But goland is pretty intentional about it...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird, golang doesn't touch it for me.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you using a go locally less than 1.19?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go version go1.20 linux/amd64

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can turn Reformat on save off

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I swear lint was failing with the space when I changed it, but if it's passing now we can fix it again if it becomes a problem

return fmt.Errorf("invalid environment variable, should be in format of VAR=VALUE: %q", envVar)
}
spec.PodSpecOptions.Envs[splitVar[0]] = splitVar[1]
Expand All @@ -72,6 +80,8 @@ var cmd = &cobra.Command{
return fmt.Errorf("aborting since %d exceeds max value of --n which is %d", opts.num, maxJobs)
}

var jobRunIdentifiers = make([]JobRunIdentifier, 0)

fmt.Println(string(data))

fmt.Printf(strFmt, "Job", "ID", "URL")
Expand All @@ -88,24 +98,32 @@ var cmd = &cobra.Command{
ID string `json:"id"`
}
if err := json.NewDecoder(resp.Body).Decode(&jobInfo); err != nil {
resp.Body.Close() //nolint
resp.Body.Close() // nolint
return err
}
resp.Body.Close() //nolint
resp.Body.Close() // nolint

// Get the job URL from prow easy access
jobURL, err := getJobURL(jobInfo.ID)
jobURL, buildID, err := getJobURL(jobInfo.ID)
if err != nil {
return err
}

jobRunIdentifier := JobRunIdentifier{
JobName: opts.jobName,
JobRunID: buildID,
}
jobRunIdentifiers = append(jobRunIdentifiers, jobRunIdentifier)

// Print job info in tabular format
fmt.Printf(strFmt, strconv.Itoa(i+1), jobInfo.ID, jobURL)

// Sleep to avoid hitting the api too hard
time.Sleep(time.Second)
}

outputJobRunIdentifiers(opts.jobsFilePath, jobRunIdentifiers)

return nil
},
}
Expand All @@ -117,6 +135,7 @@ func NewCommand() *cobra.Command {
cmd.Flags().StringVarP(&opts.jobName, "job-name", "j", "", "Job name")
cmd.Flags().StringVarP(&opts.apiURL, "api-url", "u", "", "API URL")
cmd.Flags().IntVarP(&opts.num, "n", "n", 1, fmt.Sprintf("Number of times to launch the job (max is %d)", maxJobs))
cmd.Flags().StringVarP(&opts.jobsFilePath, "jobs-file-path", "p", "", "Save JobRunIdentifier JSON in the specified file path")

return cmd
}
Expand All @@ -143,7 +162,7 @@ func launchJob(appCIToken, apiURL string, data []byte) (*http.Response, error) {

// getJobURL gets the url from prow so the user has a place to browse to see the
// status of the prow job. Prow does not immediately have the prow job so we wait.
func getJobURL(jobID string) (string, error) {
func getJobURL(jobID string) (string, string, error) {
const maxAttempts = 5
const retryDelay = time.Second
url := "https://prow.ci.openshift.org/prowjob?prowjob=" + jobID
Expand All @@ -160,22 +179,27 @@ func getJobURL(jobID string) (string, error) {
body, err := io.ReadAll(resp.Body)
if err != nil {
resp.Body.Close()
return "", fmt.Errorf("error reading response body: %v", err)
return "", "", fmt.Errorf("error reading response body: %v", err)
}
resp.Body.Close()

// Search YAML document parts to find the section with status.url
documents := strings.Split(string(body), "---")
var statusURL string
var statusURL, buildID string
for _, doc := range documents {
var jobInfo map[string]interface{}
if err := yaml.Unmarshal([]byte(doc), &jobInfo); err != nil {
continue
}
if status, ok := jobInfo["status"].(map[interface{}]interface{}); ok {

// try to get the build_id as well
// but only return when we have a valid URL
buildID = status["build_id"].(string)

if url, ok := status["url"].(string); ok {
statusURL = url
return statusURL, nil
return statusURL, buildID, nil
}
}
}
Expand All @@ -184,7 +208,36 @@ func getJobURL(jobID string) (string, error) {
time.Sleep(retryDelay)
continue
}
return statusURL, nil
return statusURL, buildID, nil
}
return "", fmt.Errorf("status.url not found in response after %d retries", maxAttempts)
return "", "", fmt.Errorf("status.url not found in response after %d retries", maxAttempts)
}

func outputJobRunIdentifiers(jobsFilePath string, jobRunIdentifiers []JobRunIdentifier) {

fileName := fmt.Sprintf("gangway_%s_%s.json", opts.jobName, time.Now().Format(time.RFC3339Nano))
output, err := json.Marshal(jobRunIdentifiers)
if err != nil {
fmt.Printf("Failed to marshal JSON for JobRunIdentifiers: %v", err)
} else {

// check to see if we end with path separator
// if so set it to empty string
delimiter := string(os.PathSeparator)
if len(opts.jobsFilePath) > 0 {
if strings.HasSuffix(jobsFilePath, delimiter) {
delimiter = ""
}
err := os.MkdirAll(jobsFilePath, os.ModePerm)
if err != nil {
fmt.Printf("Error creating directory: %v", err)
} else {
err := os.WriteFile(fmt.Sprintf("%s%s%s", jobsFilePath, delimiter, fileName), output, os.ModePerm)
if err != nil {
fmt.Printf("Error writing file: %v", err)
}
}
}
}

}