Skip to content
This repository has been archived by the owner on Oct 11, 2019. It is now read-only.

Commit

Permalink
prepare 4.2.2 release (#128)
Browse files Browse the repository at this point in the history
  • Loading branch information
eli-darkly authored Aug 3, 2018
1 parent 93524bc commit 5ef0f15
Show file tree
Hide file tree
Showing 14 changed files with 70 additions and 20 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

All notable changes to the LaunchDarkly Go SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org).

## [4.2.2] - 2018-08-03
### Fixed:
- Fixed a bug that caused a panic if an I/O error occurred while reading the response body for a polling request.
- Fixed a bug that caused a panic if a prerequisite feature flag evaluated to a non-scalar value (array or map/hash).
- Receiving an HTTP 400 error from LaunchDarkly should not make the client give up on sending any more requests to LaunchDarkly (unlike a 401 or 403).

## [4.2.1] - 2018-06-27
### Fixed:
- Polling processor regressed to polling only once in release 4.1.0.  This has been fixed.
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ LaunchDarkly SDK for Go

[![Circle CI](https://circleci.com/gh/launchdarkly/go-client.svg?style=svg)](https://circleci.com/gh/launchdarkly/go-client)

Go runtime compatibility
-------------------------

This version of the LaunchDarkly SDK has been tested with Go 1.8 through 1.10.

Quick setup
-----------

Expand Down
7 changes: 3 additions & 4 deletions event_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,10 +304,9 @@ func (ed *eventDispatcher) isDisabled() bool {
}

func (ed *eventDispatcher) handleResponse(resp *http.Response) {
err := checkStatusCode(resp.StatusCode, resp.Request.URL.String())
if err != nil {
ed.config.Logger.Println(httpErrorMessage(err.Code, "posting events", "some events were dropped"))
if err != nil && !isHTTPErrorRecoverable(err.Code) {
if err := checkForHttpError(resp.StatusCode, resp.Request.URL.String()); err != nil {
ed.config.Logger.Println(httpErrorMessage(resp.StatusCode, "posting events", "some events were dropped"))
if !isHTTPErrorRecoverable(resp.StatusCode) {
ed.stateLock.Lock()
defer ed.stateLock.Unlock()
ed.disabled = true
Expand Down
1 change: 1 addition & 0 deletions event_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ var httpErrorTests = []struct {
status int
recoverable bool
}{
{400, true},
{401, false},
{403, false},
{408, true},
Expand Down
4 changes: 2 additions & 2 deletions flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ var Features FeatureFlagVersionedDataKind
// Rule xpresses a set of AND-ed matching conditions for a user, along with either a fixed
// variation or a set of rollout percentages
type Rule struct {
Id string `json:"id,omitempty" bson:"id,omitempty"`
VariationOrRollout `bson:",inline"`
Clauses []Clause `json:"clauses" bson:"clauses"`
}
Expand Down Expand Up @@ -234,8 +235,7 @@ func (f FeatureFlag) evaluateExplain(user User, store FeatureStore, events *[]Fe
}

*events = append(*events, NewFeatureRequestEvent(prereq.Key, prereqFeatureFlag, user, prereqIndex, prereqValue, nil, &f.Key))
variation, verr := prereqFeatureFlag.getVariation(&prereq.Variation)
if prereqValue == nil || verr != nil || prereqValue != variation {
if prereqIndex == nil || *prereqIndex != prereq.Variation {
failedPrereq = &prereq
}
} else {
Expand Down
34 changes: 34 additions & 0 deletions flag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,40 @@ func TestFlagReturnsFallthroughVariationAndEventIfPrerequisiteIsMetAndThereAreNo
assert.Equal(t, strPtr(f0.Key), e.PrereqOf)
}

func TestPrerequisiteCanMatchWithNonScalarValue(t *testing.T) {
f0 := FeatureFlag{
Key: "feature0",
On: true,
OffVariation: intPtr(1),
Prerequisites: []Prerequisite{Prerequisite{"feature1", 1}},
Fallthrough: VariationOrRollout{Variation: intPtr(0)},
Variations: []interface{}{"fall", "off", "on"},
Version: 1,
}
f1 := FeatureFlag{
Key: "feature1",
On: true,
OffVariation: intPtr(1),
Fallthrough: VariationOrRollout{Variation: intPtr(1)}, // this 1 matches the 1 in the prerequisites array
Variations: []interface{}{[]interface{}{"000"}, []interface{}{"001"}},
Version: 2,
}
featureStore := NewInMemoryFeatureStore(nil)
featureStore.Upsert(Features, &f1)

value, index, events := f0.Evaluate(flagUser, featureStore)
assert.Equal(t, "fall", value)
assert.Equal(t, intPtr(0), index)

assert.Equal(t, 1, len(events))
e := events[0]
assert.Equal(t, f1.Key, e.Key)
assert.Equal(t, []interface{}{"001"}, e.Value)
assert.Equal(t, intPtr(1), e.Variation)
assert.Equal(t, intPtr(f1.Version), e.Version)
assert.Equal(t, strPtr(f0.Key), e.PrereqOf)
}

func TestMultipleLevelsOfPrerequisiteProduceMultipleEvents(t *testing.T) {
f0 := FeatureFlag{
Key: "feature0",
Expand Down
2 changes: 1 addition & 1 deletion ldclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
)

// Version is the client version
const Version = "4.2.1"
const Version = "4.2.2"

// LDClient is the LaunchDarkly client. Client instances are thread-safe.
// Applications should instantiate a single instance for the lifetime
Expand Down
2 changes: 1 addition & 1 deletion polling.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (pp *pollingProcessor) Start(closeWhenReady chan<- struct{}) {
case <-ticker.C:
if err := pp.poll(); err != nil {
pp.config.Logger.Printf("ERROR: Error when requesting feature updates: %+v", err)
if hse, ok := err.(*HttpStatusError); ok {
if hse, ok := err.(HttpStatusError); ok {
pp.config.Logger.Printf("ERROR: %s", httpErrorMessage(hse.Code, "polling request", "will retry"))
if !isHTTPErrorRecoverable(hse.Code) {
notifyReady()
Expand Down
2 changes: 1 addition & 1 deletion polling_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func TestPollingProcessorRequestResponseCodes(t *testing.T) {
statusCode int
recoverable bool
}{
{400, false},
{400, true},
{401, false},
{403, false},
{405, false},
Expand Down
5 changes: 2 additions & 3 deletions requestor.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@ func (r *requestor) makeRequest(resource string) ([]byte, bool, error) {
_ = res.Body.Close()
}()

err := checkStatusCode(res.StatusCode, url)
if err != nil {
if err := checkForHttpError(res.StatusCode, url); err != nil {
return nil, false, err
}

Expand All @@ -119,7 +118,7 @@ func (r *requestor) makeRequest(resource string) ([]byte, bool, error) {
body, ioErr := ioutil.ReadAll(res.Body)

if ioErr != nil {
return nil, false, err
return nil, false, ioErr
}
return body, cached, nil
}
1 change: 1 addition & 0 deletions segment.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ var Segments SegmentVersionedDataKind

// SegmentRule describes a set of clauses that
type SegmentRule struct {
Id string `json:"id,omitempty" bson:"id,omitempty"`
Clauses []Clause `json:"clauses" bson:"clauses"`
Weight *int `json:"weight,omitempty" bson:"weight,omitempty"`
BucketBy *string `json:"bucketBy,omitempty" bson:"bucketBy,omitempty"`
Expand Down
4 changes: 1 addition & 3 deletions streaming.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,7 @@ func (sp *streamProcessor) events(closeWhenReady chan<- struct{}) {
if sp.checkIfPermanentFailure(err) {
sp.closeOnce.Do(func() {
sp.config.Logger.Printf("Closing event stream.")
// TODO: enable this when we trust stream.Close() never to panic (see https://github.com/donovanhide/eventsource/pull/33)
// Until we're able to Close it explicitly here, we won't be able to stop it from trying to reconnect after a 401 error.
// sp.stream.Close()
sp.stream.Close()
})
return
}
Expand Down
4 changes: 4 additions & 0 deletions streaming_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ func waitForDelete(t *testing.T, store FeatureStore, kind VersionedDataKind, key
assert.Nil(t, item)
}

func TestStreamProcessorDoesNotFailImmediatelyOn400(t *testing.T) {
testStreamProcessorRecoverableError(t, 400)
}

func TestStreamProcessorFailsImmediatelyOn401(t *testing.T) {
testStreamProcessorUnrecoverableError(t, 401)
}
Expand Down
13 changes: 8 additions & 5 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,21 +102,21 @@ func ToJsonRawMessage(input interface{}) (json.RawMessage, error) {
}
}

func checkStatusCode(statusCode int, url string) *HttpStatusError {
func checkForHttpError(statusCode int, url string) error {
if statusCode == http.StatusUnauthorized {
return &HttpStatusError{
return HttpStatusError{
Message: fmt.Sprintf("Invalid SDK key when accessing URL: %s. Verify that your SDK key is correct.", url),
Code: statusCode}
}

if statusCode == http.StatusNotFound {
return &HttpStatusError{
return HttpStatusError{
Message: fmt.Sprintf("Resource not found when accessing URL: %s. Verify that this resource exists.", url),
Code: statusCode}
}

if statusCode/100 != 2 {
return &HttpStatusError{
return HttpStatusError{
Message: fmt.Sprintf("Unexpected response code: %d when accessing URL: %s", statusCode, url),
Code: statusCode}
}
Expand All @@ -140,10 +140,13 @@ func MakeAllVersionedDataMap(
return allData
}

// Tests whether an HTTP error status represents a condition that might resolve on its own if we retry.
// Tests whether an HTTP error status represents a condition that might resolve on its own if we retry,
// or at least should not make us permanently stop sending requests.
func isHTTPErrorRecoverable(statusCode int) bool {
if statusCode >= 400 && statusCode < 500 {
switch statusCode {
case 400: // bad request
return true
case 408: // request timeout
return true
case 429: // too many requests
Expand Down

0 comments on commit 5ef0f15

Please sign in to comment.