Skip to content

Commit

Permalink
tiny post-GA tool to reduce exec frequency of periodics
Browse files Browse the repository at this point in the history
Signed-off-by: Jakub Guzik <jguzik@redhat.com>
  • Loading branch information
jmguzik committed Jul 9, 2024
1 parent 64da4f1 commit f1bdb11
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 0 deletions.
213 changes: 213 additions & 0 deletions cmd/branchingconfigmanagers/frequency-reducer/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package main

import (
"flag"
"fmt"
"math/rand"
"regexp"
"strings"
"time"

"github.com/sirupsen/logrus"

utilerrors "k8s.io/apimachinery/pkg/util/errors"

"github.com/openshift/ci-tools/pkg/api"
"github.com/openshift/ci-tools/pkg/api/ocplifecycle"
"github.com/openshift/ci-tools/pkg/config"
"gopkg.in/robfig/cron.v2"
)

type options struct {
config.ConfirmableOptions
currentOCPVersion string
}

func (o options) validate() error {
var errs []error
if err := o.ConfirmableOptions.Validate(); err != nil {
errs = append(errs, err)
}

return utilerrors.NewAggregate(errs)
}

func gatherOptions() options {
o := options{}
flag.StringVar(&o.currentOCPVersion, "current-release", "", "Current OCP version")

o.Bind(flag.CommandLine)
flag.Parse()

return o
}

func main() {
o := gatherOptions()
if err := o.validate(); err != nil {
logrus.Fatalf("Invalid options: %v", err)
}

ocpVersion, err := ocplifecycle.ParseMajorMinor(o.currentOCPVersion)
if err != nil {
logrus.Fatalf("Not valid --current-release: %v", err)
}

if err := o.ConfirmableOptions.Complete(); err != nil {
logrus.Fatalf("Couldn't complete the config options: %v", err)
}

if err := o.OperateOnCIOperatorConfigDir(o.ConfigDir, func(configuration *api.ReleaseBuildConfiguration, info *config.Info) error {
output := config.DataWithInfo{Configuration: *configuration, Info: *info}
updateIntervalFieldsForMatchedSteps(&output, *ocpVersion)

if err := output.CommitTo(o.ConfigDir); err != nil {
logrus.WithError(err).Fatal("commitTo failed")
}
return nil
}); err != nil {
logrus.WithError(err).Fatal("Could not branch configurations.")
}

}

func updateIntervalFieldsForMatchedSteps(
configuration *config.DataWithInfo,
version ocplifecycle.MajorMinor,
) {
testVersion, err := ocplifecycle.ParseMajorMinor(extractVersion(configuration.Info.Metadata.Branch))
if err != nil {
return
}
if configuration.Info.Metadata.Org == "openshift" || configuration.Info.Metadata.Org == "openshift-priv" {
for _, test := range configuration.Configuration.Tests {
if !strings.Contains(test.As, "mirror-nightly-image") && !strings.Contains(test.As, "promote-") {
if test.Cron != nil {
// check if less then past past version
if testVersion.Less(ocplifecycle.MajorMinor{Major: version.Major, Minor: version.Minor - 2}) {
correctCron, err := isExecutedAtMostXTimesAMonth(*test.Cron, 1)
if err != nil {
logrus.Warningf("Can't parse cron string %s", *test.Cron)
continue
}
if !correctCron {
*test.Cron = generateMonthlyCron()
}
} else if testVersion.GetVersion() == version.GetPastPastVersion() {
correctCron, err := isExecutedAtMostXTimesAMonth(*test.Cron, 2)
if err != nil {
logrus.Warningf("Can't parse cron string %s", *test.Cron)
continue
}
if !correctCron {
*test.Cron = generateBiWeeklyCron()
}
} else if testVersion.GetVersion() == version.GetPastVersion() {
correctCron, err := isExecutedAtMostXTimesAMonth(*test.Cron, 4)
if err != nil {
logrus.Warningf("Can't parse cron string %s", *test.Cron)
continue
}
if !correctCron {
*test.Cron = generateWeeklyWeekendCron()
}
}
}
if test.Interval != nil {
if testVersion.Less(ocplifecycle.MajorMinor{Major: version.Major, Minor: version.Minor - 2}) {
duration, err := time.ParseDuration(*test.Interval)
if err != nil {
logrus.Warningf("Can't parse interval string %s", *test.Cron)
continue
}
if duration < time.Hour*24*28 {
cronExpr := generateWeeklyWeekendCron()
test.Cron = &cronExpr
test.Interval = nil
}
} else if testVersion.GetVersion() == version.GetPastPastVersion() {
duration, err := time.ParseDuration(*test.Interval)
if err != nil {
logrus.Warningf("Can't parse interval string %s", *test.Cron)
continue
}
if duration < time.Hour*24*14 {
cronExpr := generateBiWeeklyCron()
test.Cron = &cronExpr
test.Interval = nil
}
} else if testVersion.GetVersion() == version.GetPastVersion() {
duration, err := time.ParseDuration(*test.Interval)
if err != nil {
logrus.Warningf("Can't parse interval string %s", *test.Cron)
continue
}
if duration < time.Hour*24*7 {
cronExpr := generateWeeklyWeekendCron()
test.Cron = &cronExpr
test.Interval = nil
}
}
}
}
}
}
}

func isExecutedAtMostXTimesAMonth(cronExpr string, x int) (bool, error) {
switch strings.ToLower(cronExpr) {
case "@daily":
cronExpr = "0 0 * * *"
case "@weekly":
cronExpr = "0 0 * * 0"
case "@monthly":
cronExpr = "0 0 1 * *"
case "@yearly", "@annually":
cronExpr = "0 0 1 1 *"
}

schedule, err := cron.Parse(cronExpr)
if err != nil {
return false, err
}
start := time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC)
end := start.AddDate(0, 1, 0)

executionCount := 0
for {
next := schedule.Next(start)
if next.After(end) {
break
}
executionCount++
start = next
}

return executionCount <= x, nil
}

func generateWeeklyWeekendCron() string {
randDay := rand.Intn(2)
selectedDay := randDay * 6
return fmt.Sprintf("%d %d * * %d", rand.Intn(60), rand.Intn(24), selectedDay)
}

func generateBiWeeklyCron() string {
return fmt.Sprintf("%d %d %d,%d * *", rand.Intn(60), rand.Intn(24), rand.Intn(10)+5, rand.Intn(14)+15)
}

func generateMonthlyCron() string {
return fmt.Sprintf("%d %d %d * *", rand.Intn(60), rand.Intn(24), rand.Intn(28)+1)
}

func extractVersion(s string) string {
pattern := `^(release|openshift)-(\d+\.\d+)$`
re := regexp.MustCompile(pattern)

matches := re.FindStringSubmatch(s)

if len(matches) > 2 {
return matches[2]
}
return ""
}
4 changes: 4 additions & 0 deletions pkg/api/ocplifecycle/ocplifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ func (m MajorMinor) GetPastVersion() string {
return fmt.Sprintf("%d.%d", m.Major, m.Minor-1)
}

func (m MajorMinor) GetPastPastVersion() string {
return fmt.Sprintf("%d.%d", m.Major, m.Minor-1)
}

func (m MajorMinor) GetVersion() string {
return fmt.Sprintf("%d.%d", m.Major, m.Minor)
}
Expand Down

0 comments on commit f1bdb11

Please sign in to comment.