Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

client: add retryable client with default #859

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.12.0
github.com/hashicorp/go-retryablehttp v0.7.1
github.com/hashicorp/golang-lru v0.5.4
github.com/magiconair/properties v1.8.6
github.com/prometheus/client_golang v1.13.0
Expand Down Expand Up @@ -93,7 +94,6 @@ require (
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-plugin v1.4.5 // indirect
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect
Expand Down
37 changes: 27 additions & 10 deletions pkg/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (
"net/url"
"path"
"time"

"github.com/hashicorp/go-retryablehttp"
)

type CertificateResponse struct {
Expand Down Expand Up @@ -61,8 +63,12 @@ const (
rootCertPath = "/api/v1/rootCert"
)

// SigstorePublicServerURL is the URL of Sigstore's public Fulcio service.
const SigstorePublicServerURL = "https://fulcio.sigstore.dev"
const (
// SigstorePublicServerURL is the URL of Sigstore's public Fulcio service.
SigstorePublicServerURL = "https://fulcio.sigstore.dev"
// DefaultRetryCount is the default number of retries.
DefaultRetryCount = 3
)

// LegacyClient is the interface for accessing the Fulcio API.
type LegacyClient interface {
Expand All @@ -80,13 +86,15 @@ type ClientOption func(*clientOptions)
func NewClient(url *url.URL, opts ...ClientOption) LegacyClient {
o := makeOptions(opts...)

retryableClient := retryablehttp.NewClient()
retryableClient.RetryMax = int(o.RetryCount)

httpClient := retryableClient.StandardClient()
httpClient.Timeout = o.Timeout
httpClient.Transport = createRoundTripper(httpClient.Transport, o)
return &client{
baseURL: url,
client: &http.Client{
Transport: createRoundTripper(http.DefaultTransport, o),
Timeout: o.Timeout,
},
}
client: httpClient}
}

type client struct {
Expand Down Expand Up @@ -179,13 +187,15 @@ func (c *client) RootCert() (*RootResponse, error) {
}

type clientOptions struct {
UserAgent string
Timeout time.Duration
UserAgent string
Timeout time.Duration
RetryCount uint
}

func makeOptions(opts ...ClientOption) *clientOptions {
o := &clientOptions{
UserAgent: "",
UserAgent: "",
RetryCount: DefaultRetryCount,
}

for _, opt := range opts {
Expand All @@ -209,6 +219,13 @@ func WithUserAgent(userAgent string) ClientOption {
}
}

// WithRetryCount sets the number of retries.
func WithRetryCount(retryCount uint) ClientOption {
return func(o *clientOptions) {
o.RetryCount = retryCount
}
}

type roundTripper struct {
http.RoundTripper
UserAgent string
Expand Down
29 changes: 29 additions & 0 deletions pkg/api/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,32 @@ func TestTimeoutOption(t *testing.T) {
t.Error("expected client to specifically have a timeout error")
}
}

func TestRetryCountOption(t *testing.T) {
currentCount := 0
expectedCount := 2
ts := httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
currentCount++
file := []byte{}

if currentCount < expectedCount {
w.WriteHeader(http.StatusInternalServerError)
} else {
w.WriteHeader(http.StatusOK)
_, _ = w.Write(file)
}

}))

lc := NewClient(nil, WithRetryCount(2))
c, ok := lc.(*client)
if !ok {
t.Fatal("wrong legacy client implementation")
}

_, err := c.client.Get(ts.URL)
if err != nil {
t.Error("expected client to retry requestt")
}
}