Skip to content

Commit

Permalink
Merge pull request #164 from launchdarkly/eb/ch82207/status-detail-2
Browse files Browse the repository at this point in the history
(v6 - #3) add env key etc. to status in auto-config mode
  • Loading branch information
eli-darkly authored Aug 14, 2020
2 parents 450ca53 + b5b8bd4 commit eba5b95
Show file tree
Hide file tree
Showing 23 changed files with 513 additions and 174 deletions.
8 changes: 4 additions & 4 deletions core/relay_core.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func NewRelayCore(
loggers.Warnf("environment config was nil for environment %q; ignoring", envName)
continue
}
env, resultCh, err := r.AddEnvironment(envName, *envConfig)
env, resultCh, err := r.AddEnvironment(relayenv.EnvIdentifiers{ConfiguredName: envName}, *envConfig)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -163,7 +163,7 @@ func (r *RelayCore) GetAllEnvironments() []relayenv.EnvContext {
// AddEnvironment attempts to add a new environment. It returns an error only if the configuration
// is invalid; it does not wait to see whether the connection to LaunchDarkly succeeded.
func (r *RelayCore) AddEnvironment(
envName string,
identifiers relayenv.EnvIdentifiers,
envConfig config.EnvConfig,
) (relayenv.EnvContext, <-chan relayenv.EnvContext, error) {
r.lock.Lock()
Expand Down Expand Up @@ -222,7 +222,7 @@ func (r *RelayCore) AddEnvironment(
}

clientContext, err := relayenv.NewEnvContext(
envName,
identifiers,
envConfig,
r.config,
r.clientFactory,
Expand All @@ -236,7 +236,7 @@ func (r *RelayCore) AddEnvironment(
resultCh,
)
if err != nil {
return nil, nil, errNewClientContextFailed(envName, err)
return nil, nil, errNewClientContextFailed(identifiers.GetDisplayName(), err)
}

r.allEnvironments = append(r.allEnvironments, clientContext)
Expand Down
17 changes: 15 additions & 2 deletions core/relay_core_endpoints_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,14 @@ func statusHandler(core *RelayCore) http.Handler {

healthy := true
for _, clientCtx := range core.GetAllEnvironments() {
var status environmentStatusRep
identifiers := clientCtx.GetIdentifiers()

status := environmentStatusRep{
EnvKey: identifiers.EnvKey, // these will only be non-empty if we're in auto-configured mode
EnvName: identifiers.EnvName,
ProjKey: identifiers.ProjKey,
ProjName: identifiers.ProjName,
}

for _, c := range clientCtx.GetCredentials() {
switch c := c.(type) {
Expand All @@ -97,6 +104,12 @@ func statusHandler(core *RelayCore) http.Handler {
}
}

for _, c := range clientCtx.GetDeprecatedCredentials() {
if key, ok := c.(config.SDKKey); ok {
status.ExpiringSDKKey = ObscureKey(string(key))
}
}

client := clientCtx.GetClient()
if client == nil {
status.Status = statusEnvDisconnected
Expand Down Expand Up @@ -131,7 +144,7 @@ func statusHandler(core *RelayCore) http.Handler {
}
}

statusKey := clientCtx.GetName()
statusKey := identifiers.GetDisplayName()
if core.envLogNameMode == relayenv.LogNameIsEnvID {
// If we're identifying environments by environment ID in the log (which we do if there's any
// chance that the environment name could change) then we should also identify them that way here.
Expand Down
19 changes: 10 additions & 9 deletions core/relay_core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"

c "github.com/launchdarkly/ld-relay/v6/core/config"
"github.com/launchdarkly/ld-relay/v6/core/relayenv"
"github.com/launchdarkly/ld-relay/v6/core/sdks"
st "github.com/launchdarkly/ld-relay/v6/core/sharedtest"
"github.com/launchdarkly/ld-relay/v6/core/sharedtest/testclient"
Expand Down Expand Up @@ -40,21 +41,21 @@ func TestRelayCoreGetEnvironment(t *testing.T) {
defer core.Close()

if assert.NotNil(t, core.GetEnvironment(st.EnvMain.Config.SDKKey)) {
assert.Equal(t, st.EnvMain.Name, core.GetEnvironment(st.EnvMain.Config.SDKKey).GetName())
assert.Equal(t, st.EnvMain.Name, core.GetEnvironment(st.EnvMain.Config.SDKKey).GetIdentifiers().ConfiguredName)
}
if assert.NotNil(t, core.GetEnvironment(st.EnvMobile.Config.SDKKey)) {
assert.Equal(t, st.EnvMobile.Name, core.GetEnvironment(st.EnvMobile.Config.SDKKey).GetName())
assert.Equal(t, st.EnvMobile.Name, core.GetEnvironment(st.EnvMobile.Config.SDKKey).GetIdentifiers().ConfiguredName)
}
if assert.NotNil(t, core.GetEnvironment(st.EnvClientSide.Config.SDKKey)) {
assert.Equal(t, st.EnvClientSide.Name, core.GetEnvironment(st.EnvClientSide.Config.SDKKey).GetName())
assert.Equal(t, st.EnvClientSide.Name, core.GetEnvironment(st.EnvClientSide.Config.SDKKey).GetIdentifiers().ConfiguredName)
}

if assert.NotNil(t, core.GetEnvironment(st.EnvMobile.Config.MobileKey)) {
assert.Equal(t, st.EnvMobile.Name, core.GetEnvironment(st.EnvMobile.Config.MobileKey).GetName())
assert.Equal(t, st.EnvMobile.Name, core.GetEnvironment(st.EnvMobile.Config.MobileKey).GetIdentifiers().ConfiguredName)
}

if assert.NotNil(t, core.GetEnvironment(st.EnvClientSide.Config.EnvID)) {
assert.Equal(t, st.EnvClientSide.Name, core.GetEnvironment(st.EnvClientSide.Config.EnvID).GetName())
assert.Equal(t, st.EnvClientSide.Name, core.GetEnvironment(st.EnvClientSide.Config.EnvID).GetIdentifiers().ConfiguredName)
}

assert.Nil(t, core.GetEnvironment(st.UndefinedSDKKey))
Expand All @@ -74,7 +75,7 @@ func TestRelayCoreGetAllEnvironments(t *testing.T) {
assert.Len(t, envs, 3)
var names []string
for _, e := range envs {
names = append(names, e.GetName())
names = append(names, e.GetIdentifiers().ConfiguredName)
}
assert.Contains(t, names, st.EnvMain.Name)
assert.Contains(t, names, st.EnvMobile.Name)
Expand All @@ -89,11 +90,11 @@ func TestRelayCoreAddEnvironment(t *testing.T) {
require.NoError(t, err)
defer core.Close()

env, resultCh, err := core.AddEnvironment(st.EnvMobile.Name, st.EnvMobile.Config)
env, resultCh, err := core.AddEnvironment(relayenv.EnvIdentifiers{ConfiguredName: st.EnvMobile.Name}, st.EnvMobile.Config)
require.NoError(t, err)
require.NotNil(t, env)
require.NotNil(t, resultCh)
assert.Equal(t, st.EnvMobile.Name, env.GetName())
assert.Equal(t, st.EnvMobile.Name, env.GetIdentifiers().ConfiguredName)

if assert.NotNil(t, core.GetEnvironment(st.EnvMobile.Config.SDKKey)) {
assert.Equal(t, env, core.GetEnvironment(st.EnvMobile.Config.SDKKey))
Expand All @@ -117,7 +118,7 @@ func TestRelayCoreRemoveEnvironment(t *testing.T) {

env := core.GetEnvironment(st.EnvMobile.Config.SDKKey)
require.NotNil(t, env)
assert.Equal(t, st.EnvMobile.Name, env.GetName())
assert.Equal(t, st.EnvMobile.Name, env.GetIdentifiers().ConfiguredName)

removed := core.RemoveEnvironment(env)
assert.True(t, removed)
Expand Down
46 changes: 41 additions & 5 deletions core/relayenv/env_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package relayenv

import (
"context"
"fmt"
"io"
"net/http"
"time"
Expand All @@ -23,16 +24,18 @@ import (
type EnvContext interface {
io.Closer

// GetName returns the name of the environment. This is the display name that was specified in the
// configuration, or, for an auto-configured environment, the concenated project + environment names.
GetName() string
// GetIdentifiers returns information about the environment and project names and keys.
GetIdentifiers() EnvIdentifiers

// SetName updates the name of the environment.
SetName(string)
// SetIdentifiers updates the environment and project names and keys.
SetIdentifiers(EnvIdentifiers)

// GetCredentials returns all currently enabled and non-deprecated credentials for the environment.
GetCredentials() []config.SDKCredential

// GetDeprecatedCredentials returns all deprecated and not-yet-removed credentials for the environment.
GetDeprecatedCredentials() []config.SDKCredential

// AddCredential adds a new credential for the environment.
//
// If the credential is an SDK key, then a new SDK client is started with that SDK key, and event forwarding
Expand Down Expand Up @@ -97,6 +100,39 @@ type EnvContext interface {
GetCreationTime() time.Time
}

// EnvIdentifiers contains environment and project name and key properties.
//
// When running in Relay Proxy Enterprise's auto-configuration mode, EnvKey, EnvName, ProjKey, and ProjName are
// copied from the LaunchDarkly dashboard settings. Otherwise, those are all blank and ConfiguredName is set in
// the local configuration.
type EnvIdentifiers struct {
// EnvKey is the environment key (normally a lowercase string like "production").
EnvKey string

// EnvName is the environment name (normally a title-cased string like "Production").
EnvName string

// ProjKey is the project key (normally a lowercase string like "my-application").
ProjKey string

// ProjName is the project name (normally a title-cased string like "My Application").
ProjName string

// ConfiguredName is a human-readable unique name for this environment, if the user specified one. When
// using a local configuration, this is always set; in auto-configuration mode, it is always empty (but
// EnvIdentifiers.GetDisplayName() will compute one).
ConfiguredName string
}

// GetDisplayName returns a human-readable unique name for this environment. If none was set in the
// configuration, it computes one in the format "ProjName EnvName".
func (ei EnvIdentifiers) GetDisplayName() string {
if ei.ConfiguredName == "" {
return fmt.Sprintf("%s %s", ei.ProjName, ei.EnvName)
}
return ei.ConfiguredName
}

// GetEnvironmentID is a helper for extracting the EnvironmentID, if any, from the set of credentials.
func GetEnvironmentID(env EnvContext) config.EnvironmentID {
for _, c := range env.GetCredentials() {
Expand Down
37 changes: 22 additions & 15 deletions core/relayenv/env_context_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ type envContextImpl struct {
storeAdapter *store.SSERelayDataStoreAdapter
loggers ldlog.Loggers
credentials map[config.SDKCredential]bool // true if not deprecated
name string
identifiers EnvIdentifiers
secureMode bool
envStreams *streams.EnvStreams
streamProviders []streams.StreamProvider
Expand Down Expand Up @@ -86,7 +86,7 @@ type envContextStoreQueries struct {
// NewEnvContext can also immediately return an error, with a nil EnvContext, if the configuration is
// invalid.
func NewEnvContext(
envName string,
identifiers EnvIdentifiers,
envConfig config.EnvConfig,
allConfig config.Config,
clientFactory sdks.ClientFactoryFunc,
Expand Down Expand Up @@ -125,7 +125,7 @@ func NewEnvContext(
}

envContext := &envContextImpl{
name: envName,
identifiers: identifiers,
clients: make(map[config.SDKKey]sdks.LDClientContext),
credentials: credentials,
loggers: envLoggers,
Expand Down Expand Up @@ -193,7 +193,7 @@ func NewEnvContext(
thingsToCleanUp.AddFunc(eventsPublisher.Close)
envContext.metricsEventPub = eventsPublisher

em, err = metricsManager.AddEnvironment(envName, eventsPublisher)
em, err = metricsManager.AddEnvironment(identifiers.GetDisplayName(), eventsPublisher)
if err != nil {
return nil, errInitMetrics(err)
}
Expand Down Expand Up @@ -228,46 +228,53 @@ func (c *envContextImpl) startSDKClient(sdkKey config.SDKKey, readyCh chan<- Env
if err != nil {
c.initErr = err
if suppressErrors {
c.globalLoggers.Warnf("Ignoring error initializing LaunchDarkly client for %q: %+v", c.name, err)
c.globalLoggers.Warnf("Ignoring error initializing LaunchDarkly client for %q: %+v",
c.identifiers.GetDisplayName(), err)
} else {
c.globalLoggers.Errorf("Error initializing LaunchDarkly client for %q: %+v", c.name, err)
c.globalLoggers.Errorf("Error initializing LaunchDarkly client for %q: %+v",
c.identifiers.GetDisplayName(), err)
if readyCh != nil {
readyCh <- c
}
return
}
} else {
c.globalLoggers.Infof("Initialized LaunchDarkly client for %q", c.name)
c.globalLoggers.Infof("Initialized LaunchDarkly client for %q", c.identifiers.GetDisplayName())
}
if readyCh != nil {
readyCh <- c
}
}

func (c *envContextImpl) GetName() string {
func (c *envContextImpl) GetIdentifiers() EnvIdentifiers {
c.mu.RLock()
defer c.mu.RUnlock()

return c.name
return c.identifiers
}

func (c *envContextImpl) SetName(newName string) {
func (c *envContextImpl) SetIdentifiers(ei EnvIdentifiers) {
c.mu.Lock()
defer c.mu.Unlock()

if newName == c.name {
return
}
c.name = newName
c.identifiers = ei
}

func (c *envContextImpl) GetCredentials() []config.SDKCredential {
return c.getCredentialsInternal(true)
}

func (c *envContextImpl) GetDeprecatedCredentials() []config.SDKCredential {
return c.getCredentialsInternal(false)
}

func (c *envContextImpl) getCredentialsInternal(preferred bool) []config.SDKCredential {
c.mu.RLock()
defer c.mu.RUnlock()

ret := make([]config.SDKCredential, 0, len(c.credentials))
for c, nonDeprecated := range c.credentials {
if nonDeprecated {
if nonDeprecated == preferred {
ret = append(ret, c)
}
}
Expand Down
16 changes: 12 additions & 4 deletions core/relayenv/env_context_impl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func requireClientReady(t *testing.T, clientCh chan *testclient.FakeLDClient) *t
func makeBasicEnv(t *testing.T, envConfig config.EnvConfig, clientFactory sdks.ClientFactoryFunc,
loggers ldlog.Loggers, readyCh chan EnvContext) EnvContext {
env, err := NewEnvContext(
envName,
EnvIdentifiers{ConfiguredName: envName},
envConfig,
config.Config{},
clientFactory,
Expand Down Expand Up @@ -80,7 +80,7 @@ func TestConstructorBasicProperties(t *testing.T) {
env := makeBasicEnv(t, envConfig, clientFactory, mockLog.Loggers, readyCh)
defer env.Close()

assert.Equal(t, envName, env.GetName())
assert.Equal(t, envName, env.GetIdentifiers().ConfiguredName)
assert.Equal(t, time.Hour, env.GetTTL())
assert.True(t, env.IsSecureMode())
assert.Nil(t, env.GetEventDispatcher()) // events were not enabled
Expand Down Expand Up @@ -123,7 +123,7 @@ func TestConstructorWithJSClientContext(t *testing.T) {
envConfig := st.EnvWithAllCredentials.Config
jsClientContext := JSClientContext{Origins: []string{"origin"}}
env, err := NewEnvContext(
envName,
EnvIdentifiers{ConfiguredName: envName},
envConfig,
config.Config{},
testclient.FakeLDClientFactory(true),
Expand All @@ -148,7 +148,7 @@ func TestLogPrefix(t *testing.T) {
envConfig := config.EnvConfig{SDKKey: sdkKey, EnvID: envID}
mockLog := ldlogtest.NewMockLog()
env, err := NewEnvContext(
"name",
EnvIdentifiers{ConfiguredName: "name"},
envConfig,
config.Config{},
testclient.FakeLDClientFactory(true),
Expand Down Expand Up @@ -290,3 +290,11 @@ func TestSDKClientCreationFails(t *testing.T) {
assert.Equal(t, fakeError, env.GetInitError())
assert.Nil(t, env.GetStore())
}

func TestDisplayName(t *testing.T) {
ei1 := EnvIdentifiers{ProjName: "a", EnvName: "b", ConfiguredName: "thing"}
assert.Equal(t, "thing", ei1.GetDisplayName())

ei2 := EnvIdentifiers{ProjName: "a", EnvName: "b"}
assert.Equal(t, "a b", ei2.GetDisplayName())
}
Loading

0 comments on commit eba5b95

Please sign in to comment.