Skip to content

Commit bafafe2

Browse files
authored
Merge pull request #845 from fluxcd/retry-http-log-errors
loader: log HTTP errors to provide faster feedback
2 parents 259b8f8 + 07e2046 commit bafafe2

File tree

3 files changed

+68
-19
lines changed

3 files changed

+68
-19
lines changed

internal/controller/helmrelease_controller.go

+4-13
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"strings"
2424
"time"
2525

26-
"github.com/hashicorp/go-retryablehttp"
2726
corev1 "k8s.io/api/core/v1"
2827
apiequality "k8s.io/apimachinery/pkg/api/equality"
2928
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -90,8 +89,8 @@ type HelmReleaseReconciler struct {
9089
FieldManager string
9190
DefaultServiceAccount string
9291

93-
httpClient *retryablehttp.Client
94-
requeueDependency time.Duration
92+
requeueDependency time.Duration
93+
artifactFetchRetries int
9594
}
9695

9796
type HelmReleaseReconcilerOptions struct {
@@ -122,15 +121,7 @@ func (r *HelmReleaseReconciler) SetupWithManager(ctx context.Context, mgr ctrl.M
122121
}
123122

124123
r.requeueDependency = opts.DependencyRequeueInterval
125-
126-
// Configure the retryable http client used for fetching artifacts.
127-
// By default, it retries 10 times within a 3.5 minutes window.
128-
httpClient := retryablehttp.NewClient()
129-
httpClient.RetryWaitMin = 5 * time.Second
130-
httpClient.RetryWaitMax = 30 * time.Second
131-
httpClient.RetryMax = opts.HTTPRetry
132-
httpClient.Logger = nil
133-
r.httpClient = httpClient
124+
r.artifactFetchRetries = opts.HTTPRetry
134125

135126
return ctrl.NewControllerManagedBy(mgr).
136127
For(&v2.HelmRelease{}, builder.WithPredicates(
@@ -294,7 +285,7 @@ func (r *HelmReleaseReconciler) reconcileRelease(ctx context.Context, patchHelpe
294285
}
295286

296287
// Load chart from artifact.
297-
loadedChart, err := loader.SecureLoadChartFromURL(r.httpClient, hc.GetArtifact().URL, hc.GetArtifact().Digest)
288+
loadedChart, err := loader.SecureLoadChartFromURL(loader.NewRetryableHTTPClient(ctx, r.artifactFetchRetries), hc.GetArtifact().URL, hc.GetArtifact().Digest)
298289
if err != nil {
299290
if errors.Is(err, loader.ErrFileNotFound) {
300291
msg := fmt.Sprintf("Chart not ready: artifact not found. Retrying in %s", r.requeueDependency.String())

internal/controller/helmrelease_controller_test.go

-6
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"testing"
2424
"time"
2525

26-
"github.com/hashicorp/go-retryablehttp"
2726
. "github.com/onsi/gomega"
2827
"github.com/opencontainers/go-digest"
2928
helmrelease "helm.sh/helm/v3/pkg/release"
@@ -433,7 +432,6 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) {
433432
WithStatusSubresource(&v2.HelmRelease{}).
434433
WithObjects(chart, obj).
435434
Build(),
436-
httpClient: retryablehttp.NewClient(),
437435
requeueDependency: 10 * time.Second,
438436
}
439437

@@ -526,7 +524,6 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) {
526524
Client: c,
527525
GetClusterConfig: GetTestClusterConfig,
528526
EventRecorder: record.NewFakeRecorder(32),
529-
httpClient: retryablehttp.NewClient(),
530527
}
531528

532529
// Store the Helm release mock in the test namespace.
@@ -607,7 +604,6 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) {
607604
Client: c,
608605
GetClusterConfig: GetTestClusterConfig,
609606
EventRecorder: record.NewFakeRecorder(32),
610-
httpClient: retryablehttp.NewClient(),
611607
}
612608

613609
res, err := r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, c), obj)
@@ -683,7 +679,6 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) {
683679
Client: c,
684680
GetClusterConfig: GetTestClusterConfig,
685681
EventRecorder: record.NewFakeRecorder(32),
686-
httpClient: retryablehttp.NewClient(),
687682
}
688683

689684
_, err = r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, c), obj)
@@ -755,7 +750,6 @@ func TestHelmReleaseReconciler_reconcileRelease(t *testing.T) {
755750
Client: c,
756751
GetClusterConfig: GetTestClusterConfig,
757752
EventRecorder: record.NewFakeRecorder(32),
758-
httpClient: retryablehttp.NewClient(),
759753
}
760754

761755
_, err = r.reconcileRelease(context.TODO(), patch.NewSerialPatcher(obj, c), obj)

internal/loader/client.go

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
Copyright 2023 The Flux authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package loader
18+
19+
import (
20+
"context"
21+
"time"
22+
23+
"github.com/go-logr/logr"
24+
"github.com/hashicorp/go-retryablehttp"
25+
ctrl "sigs.k8s.io/controller-runtime"
26+
)
27+
28+
// NewRetryableHTTPClient returns a new retrying HTTP client for loading
29+
// artifacts. The client will retry up to the given number of times before
30+
// giving up. The context is used to log errors.
31+
func NewRetryableHTTPClient(ctx context.Context, retries int) *retryablehttp.Client {
32+
httpClient := retryablehttp.NewClient()
33+
httpClient.RetryWaitMin = 5 * time.Second
34+
httpClient.RetryWaitMax = 30 * time.Second
35+
httpClient.RetryMax = retries
36+
httpClient.Logger = newLoggerForContext(ctx)
37+
return httpClient
38+
}
39+
40+
func newLoggerForContext(ctx context.Context) retryablehttp.LeveledLogger {
41+
return &errorLogger{log: ctrl.LoggerFrom(ctx)}
42+
}
43+
44+
// errorLogger is a wrapper around logr.Logger that implements the
45+
// retryablehttp.LeveledLogger interface while only logging errors.
46+
type errorLogger struct {
47+
log logr.Logger
48+
}
49+
50+
func (l *errorLogger) Error(msg string, keysAndValues ...interface{}) {
51+
l.log.Info(msg, keysAndValues...)
52+
}
53+
54+
func (l *errorLogger) Info(msg string, keysAndValues ...interface{}) {
55+
// Do nothing.
56+
}
57+
58+
func (l *errorLogger) Debug(msg string, keysAndValues ...interface{}) {
59+
// Do nothing.
60+
}
61+
62+
func (l *errorLogger) Warn(msg string, keysAndValues ...interface{}) {
63+
// Do nothing.
64+
}

0 commit comments

Comments
 (0)