diff --git a/state/remote/atlas.go b/state/remote/atlas.go index 5343c0236..ead0acbcb 100644 --- a/state/remote/atlas.go +++ b/state/remote/atlas.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/md5" "crypto/tls" + "crypto/x509" "encoding/base64" "fmt" "io" @@ -276,9 +277,26 @@ func (c *AtlasClient) http() (*retryablehttp.Client, error) { return nil, err } rc := retryablehttp.NewClient() + + rc.CheckRetry = func(resp *http.Response, err error) (bool, error) { + if err != nil { + // don't bother retrying if the certs don't match + if err, ok := err.(*url.Error); ok { + if _, ok := err.Err.(x509.UnknownAuthorityError); ok { + return false, nil + } + } + // continue retrying + return true, nil + } + return retryablehttp.DefaultRetryPolicy(resp, err) + } + t := cleanhttp.DefaultTransport() t.TLSClientConfig = tlsConfig rc.HTTPClient.Transport = t + + c.HTTPClient = rc return rc, nil } diff --git a/state/remote/atlas_test.go b/state/remote/atlas_test.go index 9d4f226fe..060d79455 100644 --- a/state/remote/atlas_test.go +++ b/state/remote/atlas_test.go @@ -3,8 +3,11 @@ package remote import ( "bytes" "crypto/md5" + "crypto/tls" + "crypto/x509" "net/http" "net/http/httptest" + "net/url" "os" "testing" "time" @@ -36,6 +39,53 @@ func TestAtlasClient(t *testing.T) { testClient(t, client) } +func TestAtlasClient_noRetryOnBadCerts(t *testing.T) { + acctest.RemoteTestPrecheck(t) + + client, err := atlasFactory(map[string]string{ + "access_token": "NOT_REQUIRED", + "name": "hashicorp/test-remote-state", + }) + if err != nil { + t.Fatalf("bad: %s", err) + } + + ac := client.(*AtlasClient) + // trigger the AtlasClient to build the http client and assign HTTPClient + httpClient, err := ac.http() + if err != nil { + t.Fatal(err) + } + + // remove the CA certs from the client + brokenCfg := &tls.Config{ + RootCAs: new(x509.CertPool), + } + httpClient.HTTPClient.Transport.(*http.Transport).TLSClientConfig = brokenCfg + + // Instrument CheckRetry to make sure we didn't retry + retries := 0 + oldCheck := httpClient.CheckRetry + httpClient.CheckRetry = func(resp *http.Response, err error) (bool, error) { + if retries > 0 { + t.Fatal("retried after certificate error") + } + retries++ + return oldCheck(resp, err) + } + + _, err = client.Get() + if err != nil { + if err, ok := err.(*url.Error); ok { + if _, ok := err.Err.(x509.UnknownAuthorityError); ok { + return + } + } + } + + t.Fatalf("expected x509.UnknownAuthorityError, got %v", err) +} + func TestAtlasClient_ReportedConflictEqualStates(t *testing.T) { fakeAtlas := newFakeAtlas(t, testStateModuleOrderChange) srv := fakeAtlas.Server()