From 46682b71eb9a423b130b5bde464be527527328df Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Fri, 15 Jan 2016 17:28:26 -0800 Subject: [PATCH 1/2] if we can't connect to the server when setting up, return a nil roundtripper. Check roundtripper when initializing HTTPStore and substitute an OfflineStore if it is nil. Signed-off-by: David Lawrence (github: endophage) --- cmd/notary/tuf.go | 4 +++- tuf/store/httpstore.go | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/notary/tuf.go b/cmd/notary/tuf.go index 9bafb8c565..3c68dc0bc5 100644 --- a/cmd/notary/tuf.go +++ b/cmd/notary/tuf.go @@ -426,7 +426,9 @@ func tokenAuth(config *viper.Viper, baseTransport *http.Transport, gun string, } resp, err := pingClient.Do(req) if err != nil { - fatalf(err.Error()) + logrus.Errorf("could not reach %s: %s", trustServerURL, err.Error()) + logrus.Info("continuing in offline mode") + return nil } defer resp.Body.Close() diff --git a/tuf/store/httpstore.go b/tuf/store/httpstore.go index 63f9a57f16..2c4e67b875 100644 --- a/tuf/store/httpstore.go +++ b/tuf/store/httpstore.go @@ -85,6 +85,9 @@ func NewHTTPStore(baseURL, metaPrefix, metaExtension, targetsPrefix, keyExtensio if !base.IsAbs() { return nil, errors.New("HTTPStore requires an absolute baseURL") } + if roundTrip == nil { + return &OfflineStore{}, nil + } return &HTTPStore{ baseURL: *base, metaPrefix: metaPrefix, From 33fee1d35656a77e6e054bfd9f6b9009691fa99b Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Fri, 15 Jan 2016 18:08:09 -0800 Subject: [PATCH 2/2] test for returning nil roundtripper from tokenAuth Signed-off-by: David Lawrence (github: endophage) --- cmd/notary/tuf.go | 15 ++++++++++++--- cmd/notary/tuf_test.go | 34 ++++++++++++++++++++++++++++++++++ tuf/store/httpstore_test.go | 6 ++++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 cmd/notary/tuf_test.go diff --git a/cmd/notary/tuf.go b/cmd/notary/tuf.go index 3c68dc0bc5..2ffbfa5acc 100644 --- a/cmd/notary/tuf.go +++ b/cmd/notary/tuf.go @@ -395,10 +395,11 @@ func getTransport(config *viper.Viper, gun string, readOnly bool) http.RoundTrip TLSClientConfig: tlsConfig, DisableKeepAlives: true, } - return tokenAuth(config, base, gun, readOnly) + trustServerURL := getRemoteTrustServer(config) + return tokenAuth(trustServerURL, base, gun, readOnly) } -func tokenAuth(config *viper.Viper, baseTransport *http.Transport, gun string, +func tokenAuth(trustServerURL string, baseTransport *http.Transport, gun string, readOnly bool) http.RoundTripper { // TODO(dmcgowan): add notary specific headers @@ -407,7 +408,6 @@ func tokenAuth(config *viper.Viper, baseTransport *http.Transport, gun string, Transport: authTransport, Timeout: 5 * time.Second, } - trustServerURL := getRemoteTrustServer(config) endpoint, err := url.Parse(trustServerURL) if err != nil { fatalf("Could not parse remote trust server url (%s): %s", trustServerURL, err.Error()) @@ -430,7 +430,16 @@ func tokenAuth(config *viper.Viper, baseTransport *http.Transport, gun string, logrus.Info("continuing in offline mode") return nil } + // non-nil err means we must close body defer resp.Body.Close() + if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices { + // If we didn't get a 2XX range status code, we're not talking to a notary server. + // The http client should be configured to handle redirects so at this point, 3XX is + // not a valid status code. + logrus.Errorf("could not reach %s: %d", trustServerURL, resp.StatusCode) + logrus.Info("continuing in offline mode") + return nil + } challengeManager := auth.NewSimpleChallengeManager() if err := challengeManager.AddResponse(resp); err != nil { diff --git a/cmd/notary/tuf_test.go b/cmd/notary/tuf_test.go new file mode 100644 index 0000000000..9b42d720c1 --- /dev/null +++ b/cmd/notary/tuf_test.go @@ -0,0 +1,34 @@ +package main + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestTokenAuth(t *testing.T) { + var ( + readOnly bool + baseTransport = &http.Transport{} + gun = "test" + ) + require.Nil(t, tokenAuth("https://localhost:9999", baseTransport, gun, readOnly)) +} + +func NotFoundTestHandler(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(404) +} + +func TestTokenAuthNon200Status(t *testing.T) { + var ( + readOnly bool + baseTransport = &http.Transport{} + gun = "test" + ) + s := httptest.NewServer(http.HandlerFunc(NotFoundTestHandler)) + defer s.Close() + + require.Nil(t, tokenAuth(s.URL, baseTransport, gun, readOnly)) +} diff --git a/tuf/store/httpstore_test.go b/tuf/store/httpstore_test.go index bf2f8075f9..49c88c3629 100644 --- a/tuf/store/httpstore_test.go +++ b/tuf/store/httpstore_test.go @@ -260,3 +260,9 @@ func TestHTTPStoreRemoveAll(t *testing.T) { err = store.RemoveAll() assert.Error(t, err) } + +func TestHTTPOffline(t *testing.T) { + s, err := NewHTTPStore("https://localhost/", "", "", "", "", nil) + assert.NoError(t, err) + assert.IsType(t, &OfflineStore{}, s) +}