Skip to content

Commit 01de19f

Browse files
committed
Initial commit
rh-pre-commit.version: 2.3.2 rh-pre-commit.check-secrets: ENABLED
1 parent 8aa1813 commit 01de19f

File tree

20 files changed

+3094
-80
lines changed

20 files changed

+3094
-80
lines changed

Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ version=v${build_date}-${git_commit}
1414

1515
SOURCE_GIT_TAG=v1.0.0+$(shell git rev-parse --short=7 HEAD)
1616

17+
GOLINT=golangci-lint
18+
1719
GO_LD_EXTRAFLAGS=-X github.com/openshift/release-controller/vendor/k8s.io/client-go/pkg/version.gitCommit=$(shell git rev-parse HEAD) -X github.com/openshift/release-controller/vendor/k8s.io/client-go/pkg/version.gitVersion=${SOURCE_GIT_TAG} -X k8s.io/test-infra/prow/version.Name=release-controller -X k8s.io/test-infra/prow/version.Version=${version}
1820
GO_TEST_FLAGS=
1921
export CGO_ENABLED := 0
@@ -34,6 +36,15 @@ sonar-reports:
3436
golangci-lint run ./... --verbose --no-config --out-format checkstyle --issues-exit-code 0 > golangci-lint.out
3537
.PHONY: sonar-reports
3638

39+
lint:
40+
@echo "Running linter..."
41+
@if command -v $(GOLINT) >/dev/null 2>&1; then \
42+
$(GOLINT) run ./$(BINARY_PATH)/...; \
43+
else \
44+
echo "golangci-lint not installed. Install with: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest"; \
45+
fi
46+
.PHONY: lint
47+
3748
# Legacy targets
3849
image:
3950
imagebuilder -t openshift/release-controller:latest .

cmd/release-controller/config_validate.go

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,22 @@ import (
99
"time"
1010

1111
releasecontroller "github.com/openshift/release-controller/pkg/release-controller"
12-
12+
"github.com/openshift/release-controller/pkg/releasequalifiers"
1313
"gopkg.in/robfig/cron.v2"
14+
"gopkg.in/yaml.v2"
1415
utilerrors "k8s.io/apimachinery/pkg/util/errors"
1516
)
1617

17-
func validateConfigs(configDir string) error {
18+
func validateConfigs(options *options) error {
19+
errors := []error{}
20+
errors = append(errors, validateReleaseConfigs(options.validateConfigs))
21+
if len(options.ReleaseQualifiersConfigPath) > 0 {
22+
errors = append(errors, validateReleaseQualifiersConfig(options.ReleaseQualifiersConfigPath)...)
23+
}
24+
return utilerrors.NewAggregate(errors)
25+
}
26+
27+
func validateReleaseConfigs(configDir string) error {
1828
errors := []error{}
1929
releaseConfigs := []releasecontroller.ReleaseConfig{}
2030
err := filepath.Walk(configDir, func(path string, info os.FileInfo, err error) error {
@@ -37,8 +47,10 @@ func validateConfigs(configDir string) error {
3747
if err != nil {
3848
return fmt.Errorf("error encountered while trying to read config files: %w", err)
3949
}
50+
errors = append(errors, validateUpgradeJobs(releaseConfigs)...)
4051
errors = append(errors, verifyPeriodicFields(releaseConfigs)...)
4152
errors = append(errors, findDuplicatePeriodics(releaseConfigs)...)
53+
errors = append(errors, validateQualifiers(releaseConfigs)...)
4254
return utilerrors.NewAggregate(errors)
4355
}
4456

@@ -105,3 +117,46 @@ func findDuplicatePeriodics(releaseConfigs []releasecontroller.ReleaseConfig) []
105117
}
106118
return duplicates
107119
}
120+
121+
// validateQualifiers validates the contents of releasequalifiers.ReleaseQualifiers defined in releasecontroller.ReleaseConfig
122+
func validateQualifiers(releaseConfigs []releasecontroller.ReleaseConfig) []error {
123+
errors := []error{}
124+
for _, config := range releaseConfigs {
125+
for verificationName, verification := range config.Verify {
126+
for qualifierId, overrides := range verification.Qualifiers {
127+
if err := validateQualifier(qualifierId, overrides); err != nil {
128+
errors = append(errors, fmt.Errorf("unable to validate %q release qualifier %s: %v", verificationName, qualifierId, err))
129+
}
130+
}
131+
}
132+
}
133+
return errors
134+
}
135+
136+
// validateReleaseQualifiersConfig validates the contents of the file located at options.ReleaseQualifiersConfigPath
137+
func validateReleaseQualifiersConfig(configPath string) []error {
138+
errors := []error{}
139+
raw, err := os.ReadFile(configPath)
140+
if err != nil {
141+
return errors
142+
}
143+
config := releasecontroller.ReleaseQualifiersConfig{}
144+
dec := yaml.NewDecoder(bytes.NewReader(raw))
145+
dec.SetStrict(true)
146+
if err = dec.Decode(&config); err != nil {
147+
errors = append(errors, fmt.Errorf("failed to unmarshal release qualifier configuration file %s: %v", configPath, err))
148+
return errors
149+
}
150+
for qualifierId, overrides := range config.Qualifiers {
151+
errors = append(errors, validateQualifier(qualifierId, overrides))
152+
}
153+
return errors
154+
}
155+
156+
func validateQualifier(name releasequalifiers.QualifierId, qualifier releasequalifiers.ReleaseQualifier) error {
157+
err := qualifier.Validate()
158+
if err != nil {
159+
return fmt.Errorf("unable to validate qualifier %s: %v", name, err)
160+
}
161+
return nil
162+
}

cmd/release-controller/config_validate_test.go

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"testing"
55

66
releasecontroller "github.com/openshift/release-controller/pkg/release-controller"
7-
7+
"github.com/openshift/release-controller/pkg/releasequalifiers"
88
utilerrors "k8s.io/apimachinery/pkg/util/errors"
99
)
1010

@@ -356,3 +356,102 @@ func TestValidateUpgradeJobs(t *testing.T) {
356356
}
357357
}
358358
}
359+
360+
func TestValidateQualifiersConfiguration(t *testing.T) {
361+
testCases := []struct {
362+
name string
363+
configs []releasecontroller.ReleaseConfig
364+
expectedErr bool
365+
}{
366+
{
367+
name: "Good config with no qualifiers",
368+
configs: []releasecontroller.ReleaseConfig{{
369+
Name: "4.19.0-0.nightly",
370+
Verify: map[string]releasecontroller.ReleaseVerification{
371+
"osd-aws": {
372+
Optional: true,
373+
MaxRetries: 2,
374+
ProwJob: &releasecontroller.ProwJobVerification{
375+
Name: "periodic-ci-openshift-release-master-nightly-4.19-osd-aws",
376+
},
377+
},
378+
},
379+
},
380+
},
381+
expectedErr: false,
382+
},
383+
{
384+
name: "Good config with nil qualifier",
385+
configs: []releasecontroller.ReleaseConfig{{
386+
Name: "4.19.0-0.nightly",
387+
Verify: map[string]releasecontroller.ReleaseVerification{
388+
"osd-aws": {
389+
Optional: true,
390+
MaxRetries: 2,
391+
ProwJob: &releasecontroller.ProwJobVerification{
392+
Name: "periodic-ci-openshift-release-master-nightly-4.19-osd-aws",
393+
},
394+
Qualifiers: releasequalifiers.ReleaseQualifiers{
395+
"rosa": {},
396+
},
397+
},
398+
},
399+
},
400+
},
401+
expectedErr: false,
402+
},
403+
{
404+
name: "Good config with empty qualifier",
405+
configs: []releasecontroller.ReleaseConfig{{
406+
Name: "4.19.0-0.nightly",
407+
Verify: map[string]releasecontroller.ReleaseVerification{
408+
"osd-aws": {
409+
Optional: true,
410+
MaxRetries: 2,
411+
ProwJob: &releasecontroller.ProwJobVerification{
412+
Name: "periodic-ci-openshift-release-master-nightly-4.19-osd-aws",
413+
},
414+
Qualifiers: releasequalifiers.ReleaseQualifiers{
415+
"rosa": {},
416+
},
417+
},
418+
},
419+
},
420+
},
421+
expectedErr: false,
422+
},
423+
{
424+
name: "Good config with simple overrides",
425+
configs: []releasecontroller.ReleaseConfig{{
426+
Name: "4.19.0-0.nightly",
427+
Verify: map[string]releasecontroller.ReleaseVerification{
428+
"osd-aws": {
429+
Optional: true,
430+
MaxRetries: 2,
431+
ProwJob: &releasecontroller.ProwJobVerification{
432+
Name: "periodic-ci-openshift-release-master-nightly-4.19-osd-aws",
433+
},
434+
Qualifiers: releasequalifiers.ReleaseQualifiers{
435+
"hcm": {
436+
Enabled: releasequalifiers.BoolPtr(false),
437+
BadgeName: "HCM Updated",
438+
Description: "An updated description when displaying badge details",
439+
PayloadBadge: releasequalifiers.PayloadBadgeNo,
440+
},
441+
},
442+
},
443+
},
444+
}},
445+
expectedErr: false,
446+
},
447+
}
448+
for _, testCase := range testCases {
449+
errs := validateQualifiers(testCase.configs)
450+
if len(errs) > 0 && !testCase.expectedErr {
451+
t.Errorf("%s: got error when error was not expected: %v", testCase.name, errs)
452+
}
453+
if len(errs) == 0 && testCase.expectedErr {
454+
t.Errorf("%s: did not get error when error was expected", testCase.name)
455+
}
456+
}
457+
}

cmd/release-controller/controller.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ type Controller struct {
147147
releasePayloadLister *releasecontroller.MultiReleasePayloadLister
148148

149149
manifestListMode bool
150+
151+
releaseQualifierConfig *releasecontroller.ReleaseQualifiersConfig
150152
}
151153

152154
// NewController instantiates a Controller to manage release objects.
@@ -168,6 +170,7 @@ func NewController(
168170
artSuffix string,
169171
releasePayloadClient releasepayloadclient.ReleasePayloadsGetter,
170172
manifestListMode bool,
173+
releaseQualifierConfig *releasecontroller.ReleaseQualifiersConfig,
171174
) *Controller {
172175

173176
// log events at v2 and send them to the server
@@ -184,16 +187,16 @@ func NewController(
184187

185188
c := &Controller{
186189
eventRecorder: recorder,
187-
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "releases"),
188-
gcQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "gc"),
190+
queue: workqueue.NewRateLimitingQueueWithConfig(workqueue.DefaultControllerRateLimiter(), workqueue.RateLimitingQueueConfig{Name: "releases"}),
191+
gcQueue: workqueue.NewRateLimitingQueueWithConfig(workqueue.DefaultControllerRateLimiter(), workqueue.RateLimitingQueueConfig{Name: "gc"}),
189192

190193
// rate limit the audit queue severely
191-
auditQueue: workqueue.NewNamedRateLimitingQueue(workqueue.NewMaxOfRateLimiter(
194+
auditQueue: workqueue.NewRateLimitingQueueWithConfig(workqueue.NewMaxOfRateLimiter(
192195
workqueue.NewItemExponentialFailureRateLimiter(5*time.Second, 2*time.Hour),
193196
&workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Every(5), 2)},
194-
), "audit"),
197+
), workqueue.RateLimitingQueueConfig{Name: "audit"}),
195198

196-
jiraQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "jira"),
199+
jiraQueue: workqueue.NewRateLimitingQueueWithConfig(workqueue.DefaultControllerRateLimiter(), workqueue.RateLimitingQueueConfig{Name: "jira"}),
197200

198201
expectations: newExpectations(),
199202
expectationDelay: 2 * time.Second,
@@ -229,9 +232,11 @@ func NewController(
229232
releasePayloadClient: releasePayloadClient,
230233
releasePayloadLister: &releasecontroller.MultiReleasePayloadLister{Listers: make(map[string]v1alpha1.ReleasePayloadNamespaceLister)},
231234

232-
legacyResultsQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "legacyResults"),
235+
legacyResultsQueue: workqueue.NewRateLimitingQueueWithConfig(workqueue.DefaultControllerRateLimiter(), workqueue.RateLimitingQueueConfig{Name: "legacyResults"}),
233236

234237
manifestListMode: manifestListMode,
238+
239+
releaseQualifierConfig: releaseQualifierConfig,
235240
}
236241

237242
c.auditTracker = NewAuditTracker(c.auditQueue)

cmd/release-controller/legacy_results.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func (c *Controller) syncLegacyResults(key queueKey) error {
9191

9292
// Ensure the existing state is preserved. This is a big hammer, but it's the only way we have to guarantee that
9393
// the ReleasePayload's status matches the status of the ImageStream's Annotation.
94-
releasePayload := newReleasePayload(release, tag.Name, c.jobNamespace, c.prowNamespace, verificationJobs, release.Config.Upgrade, v1alpha1.PayloadVerificationDataSourceImageStream)
94+
releasePayload := newReleasePayload(release, tag.Name, c.jobNamespace, c.prowNamespace, verificationJobs, release.Config.Upgrade, v1alpha1.PayloadVerificationDataSourceImageStream, c.releaseQualifierConfig)
9595
setPayloadOverride(tag, releasePayload)
9696

9797
// Create the payload

cmd/release-controller/main.go

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ import (
1111
"strings"
1212
"time"
1313

14+
releasepayloadinformers "github.com/openshift/release-controller/pkg/client/informers/externalversions"
15+
"github.com/openshift/release-controller/pkg/releasequalifiers"
1416
"golang.org/x/text/cases"
1517
"golang.org/x/text/language"
16-
17-
releasepayloadinformers "github.com/openshift/release-controller/pkg/client/informers/externalversions"
18+
"gopkg.in/yaml.v2"
1819

1920
releasepayloadclient "github.com/openshift/release-controller/pkg/client/clientset/versioned"
2021
"github.com/openshift/release-controller/pkg/jira"
@@ -112,6 +113,8 @@ type options struct {
112113

113114
ProcessLegacyResults bool
114115
ManifestListMode bool
116+
117+
ReleaseQualifiersConfigPath string
115118
}
116119

117120
// Add metrics for jira verifier errors
@@ -221,6 +224,8 @@ func main() {
221224
flagset.BoolVar(&opt.ProcessLegacyResults, "process-legacy-results", opt.ProcessLegacyResults, "enable the migration of imagestream based results to ReleasePayloads")
222225
flagset.BoolVar(&opt.ManifestListMode, "manifest-list-mode", opt.ManifestListMode, "enable manifest list support for oc operations")
223226

227+
flagset.StringVar(&opt.ReleaseQualifiersConfigPath, "release-qualifiers-config-path", "", "Path to release qualifiers config file")
228+
224229
goFlagSet := flag.NewFlagSet("prowflags", flag.ContinueOnError)
225230
opt.github.AddFlags(goFlagSet)
226231
opt.jira.AddFlags(goFlagSet)
@@ -241,7 +246,7 @@ func (o *options) Run() error {
241246
// report liveness on default prow health port 8081
242247
health := pjutil.NewHealthOnPort(flagutil.DefaultHealthPort)
243248
if o.validateConfigs != "" {
244-
return validateConfigs(o.validateConfigs)
249+
return validateConfigs(o)
245250
}
246251
tagParts := strings.Split(o.ToolsImageStreamTag, ":")
247252
if len(tagParts) != 2 || len(tagParts[1]) == 0 {
@@ -391,6 +396,11 @@ func (o *options) Run() error {
391396
klog.Fatal(err)
392397
}
393398

399+
releaseQualifierConfig := releasecontroller.ReleaseQualifiersConfig{}
400+
if len(o.ReleaseQualifiersConfigPath) > 0 {
401+
go manageReleaseQualifierConfig(o.ReleaseQualifiersConfigPath, &releaseQualifierConfig)
402+
}
403+
394404
c := NewController(
395405
client.CoreV1(),
396406
imageClient.ImageV1(),
@@ -409,6 +419,7 @@ func (o *options) Run() error {
409419
o.ARTSuffix,
410420
releasePayloadClient.ReleaseV1alpha1(),
411421
o.ManifestListMode,
422+
&releaseQualifierConfig,
412423
)
413424

414425
if o.VerifyJira {
@@ -714,3 +725,28 @@ func setupKubeconfigWatches(o *options) error {
714725

715726
return nil
716727
}
728+
729+
func manageReleaseQualifierConfig(path string, qualifiers *releasecontroller.ReleaseQualifiersConfig) {
730+
for {
731+
// To prevent the release-controller from crashlooping due to a bad config change,
732+
// we will only log that the config is broken and set the qualifiers to an empty map.
733+
// To prevent broken configs in the future, a presubmit should be creating for
734+
// openshift/release that verifies this config.
735+
var config releasecontroller.ReleaseQualifiersConfig
736+
rawConfig, err := os.ReadFile(path)
737+
if err != nil {
738+
klog.Errorf("Failed to load release qualifier config file at %s: %v", path, err)
739+
} else if err := yaml.Unmarshal(rawConfig, &config); err != nil {
740+
klog.Errorf("Failed to unmarshal release qualifier config: %v", err)
741+
}
742+
743+
qualifiers.Mutex.Lock()
744+
if config.Qualifiers != nil {
745+
qualifiers.Qualifiers = config.Qualifiers
746+
} else {
747+
qualifiers.Qualifiers = make(map[releasequalifiers.QualifierId]releasequalifiers.ReleaseQualifier)
748+
}
749+
qualifiers.Mutex.Unlock()
750+
time.Sleep(2 * time.Minute)
751+
}
752+
}

0 commit comments

Comments
 (0)