Skip to content

Commit f6f0170

Browse files
committed
remove effective_on attribute from tracker, use expires_on only
- Remove effective_on field from taskRecord struct - Replace effective_on logic with expires_on-only approach - Most recent records (index 0) have no expires_on (always valid) - Older records expire after 60 days with day-boundary rounding - Maintain backward compatibility for --in-effect-days flag (ignored) - Update all tests to reflect new expires_on-only behavior resolves: EC-423
1 parent 361042f commit f6f0170

File tree

5 files changed

+153
-160
lines changed

5 files changed

+153
-160
lines changed

cmd/track/track_bundle.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,14 @@ func trackBundleCmd(track trackBundleFn, pullImage pullImageFn, pushImage pushIm
6262
or a digest is required.
6363
6464
The output is meant to assist enforcement of policies that ensure the
65-
most recent Tekton Bundle is used. As such, each entry contains an
66-
"effective_on" date which is set to 30 days from today. This indicates
67-
the Tekton Bundle usage should be updated within that period.
68-
69-
If --prune is set, on by default, non-acceptable entries are removed.
70-
Any entry with an effective_on date in the future, and the entry with
71-
the most recent effective_on date *not* in the future are considered
72-
acceptable.
65+
most recent Tekton Bundle is used. Each entry contains an "expires_on"
66+
date which indicates when that specific bundle version should no longer
67+
be used. The most recent bundle has no expiration date until it is
68+
replaced by a newer version.
69+
70+
If --prune is set, on by default, expired entries are removed.
71+
Any entry with an expires_on date in the future (or no expires_on date)
72+
is considered acceptable and will be kept.
7373
`),
7474

7575
Example: hd.Doc(`
@@ -181,7 +181,7 @@ func trackBundleCmd(track trackBundleFn, pullImage pullImageFn, pushImage pushIm
181181

182182
cmd.Flags().BoolVar(&params.freshen, "freshen", params.freshen, "resolve image tags to catch updates and use the latest image for the tag")
183183

184-
cmd.Flags().IntVar(&params.inEffectDays, "in-effect-days", params.inEffectDays, "number of days representing when the added reference becomes effective")
184+
cmd.Flags().IntVar(&params.inEffectDays, "in-effect-days", params.inEffectDays, "number of days after which older bundle entries expire (most recent entry stays valid until replaced)")
185185

186186
cmd.MarkFlagsOneRequired("bundle", "git", "input")
187187

cmd/track/track_bundle_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"testing"
2626

2727
"github.com/spf13/afero"
28+
"github.com/spf13/pflag"
2829
"github.com/stretchr/testify/assert"
2930

3031
"github.com/conforma/cli/cmd/root"
@@ -204,6 +205,19 @@ func Test_TrackBundleCommand(t *testing.T) {
204205
expectPrune: true,
205206
expectInEffectDays: 666,
206207
},
208+
{
209+
name: "verify backward compatibility of in-effect-days flag",
210+
args: []string{
211+
"--bundle",
212+
"registry/image:tag",
213+
"--in-effect-days",
214+
"100",
215+
},
216+
expectUrls: []string{"registry/image:tag"},
217+
expectStdout: true,
218+
expectPrune: true,
219+
expectInEffectDays: 100,
220+
},
207221
}
208222

209223
for _, c := range cases {
@@ -269,6 +283,33 @@ func Test_TrackBundleCommand(t *testing.T) {
269283
}
270284
}
271285

286+
// TestBundleCommandHelp tests that the command help reflects the new expires_on behavior
287+
func TestBundleCommandHelp(t *testing.T) {
288+
trackBundleCmd := trackBundleCmd(nil, nil, nil)
289+
290+
// Verify the long description mentions expires_on
291+
assert.Contains(t, trackBundleCmd.Long, "expires_on",
292+
"Command help should mention expires_on")
293+
294+
// Verify it explains the new behavior
295+
assert.Contains(t, trackBundleCmd.Long, "no expiration date",
296+
"Command help should explain that recent bundles have no expiration")
297+
298+
// Verify pruning explanation is updated
299+
assert.Contains(t, trackBundleCmd.Long, "expired entries are removed",
300+
"Command help should explain pruning removes expired entries")
301+
302+
// Verify the in-effect-days flag is still documented (backward compatibility)
303+
foundFlag := false
304+
trackBundleCmd.Flags().VisitAll(func(flag *pflag.Flag) {
305+
if flag.Name == "in-effect-days" {
306+
foundFlag = true
307+
assert.Equal(t, "30", flag.DefValue, "Default value should be 30")
308+
}
309+
})
310+
assert.True(t, foundFlag, "in-effect-days flag should still exist for backward compatibility")
311+
}
312+
272313
func TestPreRunE(t *testing.T) {
273314
cases := []struct {
274315
name string

docs/modules/ROOT/pages/ec_track_bundle.adoc

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ command will query the registry to determine its value. Either a tag
1212
or a digest is required.
1313

1414
The output is meant to assist enforcement of policies that ensure the
15-
most recent Tekton Bundle is used. As such, each entry contains an
16-
"effective_on" date which is set to 30 days from today. This indicates
17-
the Tekton Bundle usage should be updated within that period.
18-
19-
If --prune is set, on by default, non-acceptable entries are removed.
20-
Any entry with an effective_on date in the future, and the entry with
21-
the most recent effective_on date *not* in the future are considered
22-
acceptable.
15+
most recent Tekton Bundle is used. Each entry contains an "expires_on"
16+
date which indicates when that specific bundle version should no longer
17+
be used. The most recent bundle has no expiration date, making it the
18+
current active version.
19+
20+
If --prune is set, on by default, expired entries are removed.
21+
Any entry with an expires_on date in the future (or no expires_on date)
22+
is considered acceptable and will be kept.
2323

2424
[source,shell]
2525
----

internal/tracker/tracker.go

Lines changed: 31 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ import (
3434
const ociPrefix = "oci://"
3535

3636
type taskRecord struct {
37-
Ref string `json:"ref"`
38-
EffectiveOn time.Time `json:"effective_on"`
37+
Ref string `json:"ref"`
3938
// ExpiresOn should be omitted if there isn't a value. Not using a pointer means it will always
4039
// have a value, e.g. 0001-01-01T00:00:00Z.
4140
ExpiresOn *time.Time `json:"expires_on,omitempty"`
@@ -111,20 +110,20 @@ func Track(ctx context.Context, urls []string, input []byte, prune bool, freshen
111110

112111
imageUrls, gitUrls := groupUrls(urls)
113112

114-
days := oneDay * time.Duration(inEffectDays)
115-
effectiveOn := time.Now().Add(days).UTC().Round(oneDay)
113+
// Note: inEffectDays parameter is kept for backward compatibility but no longer used
114+
// Records now use creation timestamps instead of future effective dates
116115

117-
if err := t.trackImageReferences(ctx, imageUrls, freshen, effectiveOn); err != nil {
116+
if err := t.trackImageReferences(ctx, imageUrls, freshen); err != nil {
118117
return nil, err
119118
}
120119

121-
if err := t.trackGitReferences(ctx, gitUrls, freshen, effectiveOn); err != nil {
120+
if err := t.trackGitReferences(ctx, gitUrls, freshen); err != nil {
122121
return nil, err
123122
}
124123

125124
t.filterBundles(prune)
126125

127-
t.setExpiration()
126+
t.setExpiration(inEffectDays)
128127

129128
return t.Output()
130129
}
@@ -143,7 +142,7 @@ func groupUrls(urls []string) ([]string, []string) {
143142
return imgs, gits
144143
}
145144

146-
func (t *Tracker) trackImageReferences(ctx context.Context, urls []string, freshen bool, effectiveOn time.Time) error {
145+
func (t *Tracker) trackImageReferences(ctx context.Context, urls []string, freshen bool) error {
147146
refs, err := image.ParseAndResolveAll(ctx, urls, name.StrictValidation)
148147
if err != nil {
149148
return err
@@ -168,18 +167,17 @@ func (t *Tracker) trackImageReferences(ctx context.Context, urls []string, fresh
168167

169168
if hasTask {
170169
t.addTrustedTaskRecord(ociPrefix, taskRecord{
171-
Ref: ref.Digest,
172-
Tag: ref.Tag,
173-
EffectiveOn: effectiveOn,
174-
Repository: ref.Repository,
170+
Ref: ref.Digest,
171+
Tag: ref.Tag,
172+
Repository: ref.Repository,
175173
})
176174
}
177175
}
178176

179177
return nil
180178
}
181179

182-
func (t *Tracker) trackGitReferences(ctx context.Context, urls []string, freshen bool, effectiveOn time.Time) error {
180+
func (t *Tracker) trackGitReferences(ctx context.Context, urls []string, freshen bool) error {
183181
if freshen {
184182
log.Debug("Freshen is enabled")
185183

@@ -224,9 +222,8 @@ func (t *Tracker) trackGitReferences(ctx context.Context, urls []string, freshen
224222
}
225223

226224
t.addTrustedTaskRecord("", taskRecord{
227-
Repository: fmt.Sprintf("%s//%s", repository, path),
228-
Ref: rev,
229-
EffectiveOn: effectiveOn,
225+
Repository: fmt.Sprintf("%s//%s", repository, path),
226+
Ref: rev,
230227
})
231228
}
232229

@@ -263,9 +260,7 @@ func (t *Tracker) filterBundles(prune bool) {
263260

264261
// filterRecords reduces the list of records by removing superfluous entries.
265262
// It removes records that have the same reference in a certain group. If prune is
266-
// true, it skips any record that is no longer acceptable. Any record with an
267-
// EffectiveOn date in the future, and the record with the most recent
268-
// EffectiveOn date *not* in the future are considered acceptable.
263+
// true, it removes records that have already expired based on their expires_on date.
269264
func filterRecords(records []taskRecord, prune bool) []taskRecord {
270265
now := time.Now().UTC()
271266

@@ -290,17 +285,10 @@ func filterRecords(records []taskRecord, prune bool) []taskRecord {
290285

291286
var relevant []taskRecord
292287
if prune {
293-
// skip tracks when records should start to be pruned.
294-
skip := false
295288
for _, r := range unique {
296-
if skip {
297-
continue
298-
}
299-
relevant = append(relevant, r)
300-
if !skip {
301-
if now.After(r.EffectiveOn) {
302-
skip = true
303-
}
289+
// Keep records that haven't expired yet, or records with no expiration date
290+
if r.ExpiresOn == nil || now.Before(*r.ExpiresOn) {
291+
relevant = append(relevant, r)
304292
}
305293
}
306294
} else {
@@ -314,20 +302,23 @@ func filterRecords(records []taskRecord, prune bool) []taskRecord {
314302
return relevant
315303
}
316304

317-
// setExpiration sets the expires_on attribute on records. The expires_on value for record N is the
318-
// effective_on value of the n-1 record. The first record on the list does not contain an expires_on
319-
// value since there is no newer record that invalidates it in the future.
320-
// TODO: Probably need to compute the expires_on value without requiring the "effective_on" value so
321-
// we don't have to always require both values. But this may be required during some transition
322-
// period.
323-
func (t *Tracker) setExpiration() {
305+
// setExpiration sets the expires_on attribute on records using a duration-based approach.
306+
// Each record expires after a configurable duration based on inEffectDays from when it's added.
307+
// The most recent record (index 0) gets no expiration date, making it the current active record.
308+
func (t *Tracker) setExpiration(inEffectDays int) {
309+
expirationDuration := time.Duration(inEffectDays) * oneDay // Use --in-effect-days flag value
310+
now := time.Now().UTC().Round(oneDay)
311+
324312
for _, records := range t.TrustedTasks {
325-
var expiration *time.Time
326313
for i := range records {
327-
if expiration != nil {
328-
records[i].ExpiresOn = expiration
314+
if i == 0 {
315+
// Most recent record doesn't expire
316+
records[i].ExpiresOn = nil
317+
} else {
318+
// Older records expire after the duration
319+
expiresOn := now.Add(expirationDuration)
320+
records[i].ExpiresOn = &expiresOn
329321
}
330-
expiration = &records[i].EffectiveOn
331322
}
332323
}
333324
}

0 commit comments

Comments
 (0)