Skip to content

Commit 3c2fb56

Browse files
mstaebleclaude
andcommitted
Move release metadata from BigQuery to PostgreSQL
Create a release_definitions table to store release metadata (GA dates, development start dates, previous release, capabilities, etc.) that was previously only available in BigQuery. During the data load cycle, releases fetched from BQ are converted directly from ReleaseRow to ReleaseDefinition and synced to PostgreSQL. All release data consumers now read from PostgreSQL via GetReleasesFromDB and GetReleaseDatesFromDB. The BigQuery release functions (GetReleasesFromBigQuery, GetReleases, QueryReleaseDates) are removed, along with the hardcoded releaseMetadata map from the PostgreSQL data provider and the QueryReleases/QueryReleaseDates methods from the DataProvider interface. Internally, all consumers use models.ReleaseDefinition directly instead of converting through the v1.Release intermediary. Capability constants are defined in pkg/db/models alongside the ReleaseDefinition type. Ref: TRT-2734 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent dd64c4f commit 3c2fb56

39 files changed

Lines changed: 425 additions & 469 deletions

File tree

cmd/sippy/automatejira.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,12 @@ func NewAutomateJiraCommand() *cobra.Command {
152152
if err != nil {
153153
log.WithError(err).Fatal("unable to load views")
154154
}
155-
releases, err := api.GetReleases(context.Background(), bigQueryClient, false)
155+
156+
dbc, err := f.PostgresFlags.GetDBClient()
157+
if err != nil {
158+
log.WithError(err).Fatal("unable to connect to postgres")
159+
}
160+
releases, err := api.GetReleasesFromDB(ctx, dbc)
156161
if err != nil {
157162
log.WithError(err).Fatal("error querying releases")
158163
}
@@ -178,10 +183,6 @@ func NewAutomateJiraCommand() *cobra.Command {
178183
return errors.WithMessage(err, "error validating options")
179184
}
180185

181-
dbc, err := f.PostgresFlags.GetDBClient()
182-
if err != nil {
183-
log.WithError(err).Fatal("unable to connect to postgres")
184-
}
185186
j, err := jiraautomator.NewJiraAutomator(
186187
jiraClient, bigQueryClient, provider, dbc, cacheOpts,
187188
views.ComponentReadiness, releases, f.SippyURL, f.JiraAccount,

cmd/sippy/load.go

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"cloud.google.com/go/bigquery"
1212
"github.com/openshift/sippy/pkg/api"
1313
"github.com/openshift/sippy/pkg/api/componentreadiness"
14-
sippyv1 "github.com/openshift/sippy/pkg/apis/sippy/v1"
1514
"github.com/openshift/sippy/pkg/dataloader/regressioncacheloader"
1615
"github.com/pkg/errors"
1716
"github.com/prometheus/client_golang/prometheus"
@@ -37,9 +36,11 @@ import (
3736
"github.com/openshift/sippy/pkg/dataloader/prowloader"
3837
"github.com/openshift/sippy/pkg/dataloader/prowloader/gcs"
3938
"github.com/openshift/sippy/pkg/dataloader/prowloader/github"
39+
releasedefloader "github.com/openshift/sippy/pkg/dataloader/releasedefloader"
4040
"github.com/openshift/sippy/pkg/dataloader/releaseloader"
4141
"github.com/openshift/sippy/pkg/dataloader/testownershiploader"
4242
"github.com/openshift/sippy/pkg/db"
43+
"github.com/openshift/sippy/pkg/db/models"
4344
"github.com/openshift/sippy/pkg/flags"
4445
"github.com/openshift/sippy/pkg/github/commenter"
4546
)
@@ -99,7 +100,7 @@ func (f *LoadFlags) BindFlags(fs *pflag.FlagSet) {
99100
f.JiraFlags.BindFlags(fs)
100101

101102
fs.BoolVar(&f.InitDatabase, "init-database", false, "Migrate the DB before loading")
102-
fs.StringArrayVar(&f.Loaders, "loader", []string{"prow", "releases", "jira", "github", "bugs", "test-mapping", "feature-gates"}, "Which data sources to use for data loading")
103+
fs.StringArrayVar(&f.Loaders, "loader", []string{"release-definitions", "prow", "releases", "jira", "github", "bugs", "test-mapping", "feature-gates"}, "Which data sources to use for data loading")
103104
fs.StringArrayVar(&f.Releases, "release", f.Releases, "Which releases to load (one per arg instance)")
104105
fs.StringArrayVar(&f.Architectures, "arch", f.Architectures, "Which architectures to load (one per arg instance)")
105106
fs.StringVar(&f.JobVariantsInputFile, "job-variants-input-file", "expected-job-variants.json", "JSON input file for the job-variants loader")
@@ -151,8 +152,6 @@ func NewLoadCommand() *cobra.Command {
151152
cacheClient = nil // error hygiene, since we pass this down to quite a few functions
152153
}
153154

154-
releaseConfigs := []sippyv1.Release{}
155-
156155
// initializing a bigquery client different from the normal one
157156
opCtx, ctx := bqcachedclient.OpCtxForCronEnv(ctx, "load")
158157
bqc, bigqueryErr := bqcachedclient.New(
@@ -163,26 +162,14 @@ func NewLoadCommand() *cobra.Command {
163162
if f.CacheFlags.EnablePersistentCaching {
164163
bqc = f.CacheFlags.DecorateBiqQueryClientWithPersistentCache(bqc)
165164
}
166-
releaseConfigs, err = api.GetReleasesFromBigQuery(context.Background(), bqc)
167-
if err != nil {
168-
return errors.Wrapf(err, "error querying releases from bq")
169-
}
170165
}
171166

172-
// Ensure partitions exist for all releases (only when InitDatabase is true)
173-
if f.InitDatabase && dbErr == nil {
174-
err = ensurePartitionsForReleases(dbc, releaseConfigs)
175-
if err != nil {
176-
return errors.Wrapf(err, "error ensuring partitions")
177-
}
178-
179-
// Clean up old partitions
180-
detached, dropped, err := dbc.CleanupPartitions(false)
181-
if err != nil {
182-
log.WithError(err).Warning("failed to cleanup old partitions, continuing with load")
183-
} else {
184-
log.Infof("Partition cleanup complete: detached %d, dropped %d", detached, dropped)
185-
}
167+
// Read release definitions from PG for downstream loader construction.
168+
// On the first run the table may be empty; the release-definitions
169+
// loader will populate it for subsequent runs.
170+
var releaseDefs []models.ReleaseDefinition
171+
if dbErr == nil {
172+
releaseDefs, _ = api.GetReleasesFromDB(context.Background(), dbc)
186173
}
187174

188175
// Sippy Config
@@ -200,6 +187,17 @@ func NewLoadCommand() *cobra.Command {
200187

201188
var regressionCacheAdded bool
202189
for _, l := range f.Loaders {
190+
if l == "release-definitions" {
191+
if bigqueryErr != nil {
192+
return errors.Wrap(bigqueryErr, "CRITICAL error getting BigQuery client which prevents release-definitions loading")
193+
}
194+
if dbErr != nil {
195+
return errors.Wrap(dbErr, "CRITICAL error getting postgres client which prevents release-definitions loading")
196+
}
197+
rdl := releasedefloader.NewReleaseDefinitionLoader(ctx, dbc, bqc)
198+
loaders = append(loaders, rdl)
199+
}
200+
203201
// TODO: remove "component-readiness-cache" and "regression-tracker" once the cronjob
204202
// manifests are updated to use "regression-cache".
205203
if l == "component-readiness-cache" || l == "regression-tracker" || l == "regression-cache" {
@@ -236,7 +234,7 @@ func NewLoadCommand() *cobra.Command {
236234
regressionStore := componentreadiness.NewPostgresRegressionStore(dbc, jiraClient)
237235

238236
rcl, err := regressioncacheloader.New(
239-
dbc, bqc, config, views.ComponentReadiness, releaseConfigs,
237+
dbc, bqc, config, views.ComponentReadiness, releaseDefs,
240238
f.ComponentReadinessFlags.CRTimeRoundingFactor,
241239
f.ComponentReadinessFlags.CRTimeRoundingOffset,
242240
regressionStore,
@@ -251,7 +249,7 @@ func NewLoadCommand() *cobra.Command {
251249
if dbErr != nil {
252250
return dbErr
253251
}
254-
loaders = append(loaders, releaseloader.New(ctx, dbc, bqc, f.Releases, f.Architectures, releaseConfigs))
252+
loaders = append(loaders, releaseloader.New(ctx, dbc, bqc, f.Releases, f.Architectures, releaseDefs))
255253
}
256254

257255
// Prow Loader
@@ -260,7 +258,7 @@ func NewLoadCommand() *cobra.Command {
260258
if dbErr != nil {
261259
return dbErr
262260
}
263-
prowLoader, err := f.prowLoader(ctx, dbc, config, releaseConfigs, promPusher)
261+
prowLoader, err := f.prowLoader(ctx, dbc, config, releaseDefs, promPusher)
264262
if err != nil {
265263
return err
266264
}
@@ -330,7 +328,7 @@ func NewLoadCommand() *cobra.Command {
330328
// Feature gates
331329
if l == "feature-gates" {
332330
refreshMatviews = true
333-
fgLoader := featuregateloader.New(dbc, releaseConfigs)
331+
fgLoader := featuregateloader.New(dbc, releaseDefs)
334332
loaders = append(loaders, fgLoader)
335333
}
336334

@@ -343,6 +341,23 @@ func NewLoadCommand() *cobra.Command {
343341
allErrs = append(allErrs, l.Errors()...)
344342
}
345343

344+
// Ensure partitions exist for all releases (only when InitDatabase is true).
345+
// Done after loaders run so the release-definitions loader has populated the table.
346+
if f.InitDatabase && dbErr == nil {
347+
releaseDefs, _ := api.GetReleasesFromDB(context.Background(), dbc)
348+
err = ensurePartitionsForReleases(dbc, releaseDefs)
349+
if err != nil {
350+
return errors.Wrapf(err, "error ensuring partitions")
351+
}
352+
353+
detached, dropped, err := dbc.CleanupPartitions(false)
354+
if err != nil {
355+
log.WithError(err).Warning("failed to cleanup old partitions, continuing with load")
356+
} else {
357+
log.Infof("Partition cleanup complete: detached %d, dropped %d", detached, dropped)
358+
}
359+
}
360+
346361
elapsed := time.Since(start)
347362
log.WithField("elapsed", elapsed).Info("database load complete")
348363

@@ -417,7 +432,7 @@ func (f *LoadFlags) jobVariantsLoader(ctx context.Context) (dataloader.DataLoade
417432

418433
}
419434

420-
func (f *LoadFlags) prowLoader(ctx context.Context, dbc *db.DB, sippyConfig *v1.SippyConfig, releaseConfigs []sippyv1.Release, promPusher *push.Pusher) (dataloader.DataLoader, error) {
435+
func (f *LoadFlags) prowLoader(ctx context.Context, dbc *db.DB, sippyConfig *v1.SippyConfig, releaseDefs []models.ReleaseDefinition, promPusher *push.Pusher) (dataloader.DataLoader, error) {
421436
gcsClient, err := gcs.NewGCSClient(ctx,
422437
f.GoogleCloudFlags.ServiceAccountCredentialFile,
423438
f.GoogleCloudFlags.OAuthClientCredentialFile,
@@ -452,8 +467,8 @@ func (f *LoadFlags) prowLoader(ctx context.Context, dbc *db.DB, sippyConfig *v1.
452467

453468
releases := f.Releases
454469
if len(releases) == 0 { // if not specified, use those defined in the Releases table
455-
for _, config := range releaseConfigs {
456-
releases = append(releases, config.Release) // could filter by capability if needed
470+
for _, def := range releaseDefs {
471+
releases = append(releases, def.Release) // could filter by capability if needed
457472
}
458473
}
459474

@@ -503,10 +518,9 @@ func parseProwLoadSince(val string) (time.Time, error) {
503518
// ensurePartitionsForReleases creates partitions for all configured releases.
504519
// It uses a 7 day lookback window plus 2 days forward from today.
505520
// Errors are logged but ignored to prevent blocking the load process.
506-
func ensurePartitionsForReleases(dbc *db.DB, releaseConfigs []sippyv1.Release) error {
507-
// Extract release names from release configs
508-
releases := make([]string, 0, len(releaseConfigs))
509-
for _, r := range releaseConfigs {
521+
func ensurePartitionsForReleases(dbc *db.DB, releaseDefs []models.ReleaseDefinition) error {
522+
releases := make([]string, 0, len(releaseDefs))
523+
for _, r := range releaseDefs {
510524
releases = append(releases, r.Release)
511525
}
512526

cmd/sippy/seed_data.go

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/spf13/pflag"
1717
"gopkg.in/yaml.v3"
1818

19+
"github.com/openshift/sippy/pkg/api"
1920
componentreadiness "github.com/openshift/sippy/pkg/api/componentreadiness"
2021
pgprovider "github.com/openshift/sippy/pkg/api/componentreadiness/dataprovider/postgres"
2122
"github.com/openshift/sippy/pkg/api/componentreadiness/utils"
@@ -410,6 +411,11 @@ func seedSyntheticData(dbc *db.DB) error {
410411
return nil
411412
}
412413

414+
if err := seedReleaseDefinitions(dbc); err != nil {
415+
return errors.WithMessage(err, "failed to seed release definitions")
416+
}
417+
log.Info("Seeded release definitions")
418+
413419
if err := createTestSuite(dbc, "synthetic"); err != nil {
414420
return errors.WithMessage(err, "failed to create test suite")
415421
}
@@ -445,6 +451,53 @@ func seedSyntheticData(dbc *db.DB) error {
445451
return nil
446452
}
447453

454+
func seedReleaseDefinitions(dbc *db.DB) error {
455+
now := time.Now().UTC()
456+
allCaps := pq.StringArray{"componentReadiness", "featureGates", "metrics", "payloadTags", "sippyClassic"}
457+
458+
type relMeta struct {
459+
previous string
460+
gaDays int // negative = days before now; 0 = no GA (in development)
461+
}
462+
meta := map[string]relMeta{
463+
"4.19": {previous: "4.18", gaDays: -289},
464+
"4.20": {previous: "4.19", gaDays: -163},
465+
"4.21": {previous: "4.20", gaDays: -58},
466+
"4.22": {previous: "4.21"},
467+
}
468+
469+
for _, release := range syntheticReleases {
470+
m := meta[release]
471+
parts := strings.Split(release, ".")
472+
major, minor := 0, 0
473+
if len(parts) >= 2 {
474+
fmt.Sscanf(parts[0], "%d", &major)
475+
fmt.Sscanf(parts[1], "%d", &minor)
476+
}
477+
478+
develStart := now.AddDate(0, 0, m.gaDays-180)
479+
def := models.ReleaseDefinition{
480+
Release: release,
481+
Major: major,
482+
Minor: minor,
483+
PreviousRelease: m.previous,
484+
DevelopmentStartDate: &develStart,
485+
Product: "OCP",
486+
Status: "Full Support",
487+
Capabilities: allCaps,
488+
}
489+
if m.gaDays != 0 {
490+
ga := now.AddDate(0, 0, m.gaDays)
491+
def.GADate = &ga
492+
}
493+
494+
if err := dbc.DB.Where("release = ?", release).FirstOrCreate(&def).Error; err != nil {
495+
return fmt.Errorf("failed to create release definition %s: %w", release, err)
496+
}
497+
}
498+
return nil
499+
}
500+
448501
func seedProwJobs(dbc *db.DB) error {
449502
for _, release := range syntheticReleases {
450503
for _, job := range syntheticJobs {
@@ -692,7 +745,7 @@ func syncRegressions(dbc *db.DB) error {
692745
provider := pgprovider.NewPostgresProvider(dbc, nil)
693746
ctx := context.Background()
694747

695-
releases, err := provider.QueryReleases(ctx)
748+
releases, err := api.GetReleasesFromDB(ctx, dbc)
696749
if err != nil {
697750
return fmt.Errorf("querying releases: %w", err)
698751
}

pkg/api/componentreadiness/component_report.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ import (
3131
"github.com/openshift/sippy/pkg/api/componentreadiness/utils"
3232
crtype "github.com/openshift/sippy/pkg/apis/api/componentreport"
3333
"github.com/openshift/sippy/pkg/apis/cache"
34-
v1 "github.com/openshift/sippy/pkg/apis/sippy/v1"
3534
"github.com/openshift/sippy/pkg/db"
35+
"github.com/openshift/sippy/pkg/db/models"
3636
"github.com/openshift/sippy/pkg/util/sets"
3737
)
3838

@@ -82,7 +82,7 @@ func GetComponentReport(
8282
reqOptions reqopts.RequestOptions,
8383
baseURL string,
8484
) (report crtype.ComponentReport, errs []error) {
85-
releaseConfigs, err := provider.QueryReleases(ctx)
85+
releaseConfigs, err := api.GetReleasesFromDB(ctx, dbc)
8686
if err != nil {
8787
return report, []error{err}
8888
}
@@ -151,7 +151,7 @@ func (c *ComponentReportGenerator) PostAnalysis(report *crtype.ComponentReport)
151151
return nil
152152
}
153153

154-
func NewComponentReportGenerator(provider dataprovider.DataProvider, reqOptions reqopts.RequestOptions, dbc *db.DB, releaseConfigs []v1.Release, baseURL string) ComponentReportGenerator {
154+
func NewComponentReportGenerator(provider dataprovider.DataProvider, reqOptions reqopts.RequestOptions, dbc *db.DB, releaseConfigs []models.ReleaseDefinition, baseURL string) ComponentReportGenerator {
155155
slices.Sort(reqOptions.Capabilities) // normalize ordering so cache keys match
156156
generator := ComponentReportGenerator{
157157
dataProvider: provider,
@@ -175,7 +175,7 @@ type ComponentReportGenerator struct {
175175
dbc *db.DB
176176
ReqOptions reqopts.RequestOptions
177177
middlewares middleware.List
178-
releaseConfigs []v1.Release
178+
releaseConfigs []models.ReleaseDefinition
179179
baseURL string
180180
}
181181

@@ -278,7 +278,7 @@ func (c *ComponentReportGenerator) initializeMiddleware() {
278278
c.middlewares = middleware.List{}
279279
// Initialize all our middleware applicable to this request.
280280
if c.ReqOptions.AdvancedOption.IncludeMultiReleaseAnalysis && c.ReqOptions.SampleRelease.PullRequestOptions == nil {
281-
c.middlewares = append(c.middlewares, releasefallback.NewReleaseFallbackMiddleware(c.dataProvider, c.ReqOptions, c.releaseConfigs))
281+
c.middlewares = append(c.middlewares, releasefallback.NewReleaseFallbackMiddleware(c.dataProvider, c.dbc, c.ReqOptions, c.releaseConfigs))
282282
}
283283
if c.dbc != nil {
284284
c.middlewares = append(c.middlewares, regressiontracker.NewRegressionTrackerMiddleware(c.dbc, c.ReqOptions))

pkg/api/componentreadiness/dataprovider/bigquery/provider.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
"github.com/openshift/sippy/pkg/apis/api/componentreport/crtest"
1919
"github.com/openshift/sippy/pkg/apis/api/componentreport/reqopts"
2020
apiCache "github.com/openshift/sippy/pkg/apis/cache"
21-
v1 "github.com/openshift/sippy/pkg/apis/sippy/v1"
2221
bqcachedclient "github.com/openshift/sippy/pkg/bigquery"
2322
"github.com/openshift/sippy/pkg/bigquery/bqlabel"
2423
"github.com/openshift/sippy/pkg/util/param"
@@ -170,14 +169,6 @@ func (p *BigQueryProvider) QueryJobVariants(ctx context.Context) (crtest.JobVari
170169
return variants, nil
171170
}
172171

173-
func (p *BigQueryProvider) QueryReleaseDates(ctx context.Context, reqOptions reqopts.RequestOptions) ([]crtest.ReleaseTimeRange, []error) {
174-
return GetReleaseDatesFromBigQuery(ctx, p.client, reqOptions)
175-
}
176-
177-
func (p *BigQueryProvider) QueryReleases(ctx context.Context) ([]v1.Release, error) {
178-
return apiPkg.GetReleasesFromBigQuery(ctx, p.client)
179-
}
180-
181172
func (p *BigQueryProvider) QueryUniqueVariantValues(ctx context.Context, field string, nested bool) ([]string, error) {
182173
unnest := ""
183174
if nested {

0 commit comments

Comments
 (0)