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

feat: Add -d/--dev common command-line flag to put service in Dev Mode #516

Merged
merged 2 commits into from
Apr 26, 2023
Merged
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
29 changes: 29 additions & 0 deletions bootstrap/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,35 @@ func (cp *Processor) Process(
// Now that configuration has been loaded and overrides applied the log level can be set as configured.
err = cp.lc.SetLogLevel(serviceConfig.GetLogLevel())

if cp.flags.InDevMode() {
// Dev mode is for when running service with Config Provider in hybrid mode (all other service running in Docker).
// All the host values are set to the docker names in the common configuration, so must be overridden here with "localhost"
host := "localhost"
config := serviceConfig.GetBootstrap()

if config.Service != nil {
config.Service.Host = host
}

if config.MessageBus != nil {
config.MessageBus.Host = host
}

if config.Registry != nil {
config.Registry.Host = host
}

if config.Database != nil {
config.Database.Host = host
}

if config.Clients != nil {
for _, client := range *config.Clients {
client.Host = host
}
}
}

return err
}

Expand Down
7 changes: 1 addition & 6 deletions bootstrap/config/configmock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ func (c *ConfigurationMockStruct) UpdateWritableFromRaw(rawWritable interface{})

func (c *ConfigurationMockStruct) GetBootstrap() config.BootstrapConfiguration {
return config.BootstrapConfiguration{
Service: c.transformToBootstrapServiceInfo(),
Registry: c.Registry,
Registry: &c.Registry,
}
}

Expand All @@ -85,10 +84,6 @@ func (c *ConfigurationMockStruct) GetTelemetryInfo() *config.TelemetryInfo {
return &c.Writable.Telemetry
}

func (c *ConfigurationMockStruct) transformToBootstrapServiceInfo() config.ServiceInfo {
return config.ServiceInfo{}
}

func (c *ConfigurationMockStruct) GetWritablePtr() any {
return &c.Writable
}
11 changes: 11 additions & 0 deletions bootstrap/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const (
type Common interface {
OverwriteConfig() bool
UseRegistry() bool
InDevMode() bool
ConfigProviderUrl() string
Profile() string
ConfigDirectory() string
Expand All @@ -45,6 +46,7 @@ type Default struct {
additionalUsage string
overwriteConfig bool
useRegistry bool
devMode bool
configProviderUrl string
commonConfig string
profile string
Expand Down Expand Up @@ -97,6 +99,8 @@ func (d *Default) Parse(arguments []string) {
d.FlagSet.StringVar(&d.configDir, "cd", "", "")
d.FlagSet.BoolVar(&d.useRegistry, "registry", false, "")
d.FlagSet.BoolVar(&d.useRegistry, "r", false, "")
d.FlagSet.BoolVar(&d.devMode, "dev", false, "")
d.FlagSet.BoolVar(&d.devMode, "d", false, "")

d.FlagSet.Usage = d.helpCallback

Expand All @@ -117,6 +121,11 @@ func (d *Default) UseRegistry() bool {
return d.useRegistry
}

// InDevMode returns whether running in dev mode or not
func (d *Default) InDevMode() bool {
return d.devMode
}

// ConfigProviderUrl returns the url for the Configuration Provider, if one was specified.
func (d *Default) ConfigProviderUrl() string {
return d.configProviderUrl
Expand Down Expand Up @@ -163,6 +172,8 @@ func (d *Default) helpCallback() {
" -p, --profile <name> Indicate configuration profile other than default\n"+
" -cd, --configDir Specify local configuration directory\n"+
" -r, --registry Indicates service should use Registry.\n"+
" -d, --dev Indicates service to run in developer mode which causes Host configuration values to be overridden.\n"+
" with `localhost`. This is so that it will run with other services running in Docker (aka hybrid mode)\n"+
"%s\n"+
"Common Options:\n"+
" -h, --help Show this message\n",
Expand Down
176 changes: 90 additions & 86 deletions bootstrap/handlers/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,15 @@ import (

// ClientsBootstrap contains data to boostrap the configured clients
type ClientsBootstrap struct {
registry registry.Client
registry registry.Client
inDevMode bool
}

// NewClientsBootstrap is a factory method that returns the initialized "ClientsBootstrap" receiver struct.
func NewClientsBootstrap() *ClientsBootstrap {
return &ClientsBootstrap{}
func NewClientsBootstrap(devMode bool) *ClientsBootstrap {
return &ClientsBootstrap{
inDevMode: devMode,
}
}

// BootstrapHandler fulfills the BootstrapHandler contract.
Expand All @@ -60,103 +63,104 @@ func (cb *ClientsBootstrap) BootstrapHandler(
cb.registry = container.RegistryFrom(dic.Get)
jwtSecretProvider := secret.NewJWTSecretProvider(container.SecretProviderExtFrom(dic.Get))

for serviceKey, serviceInfo := range config.GetBootstrap().Clients {
var url string
var err error

if !serviceInfo.UseMessageBus {
url, err = cb.getClientUrl(serviceKey, serviceInfo.Url(), startupTimer, lc)
if err != nil {
lc.Error(err.Error())
return false
}
}
if config.GetBootstrap().Clients != nil {
for serviceKey, serviceInfo := range *config.GetBootstrap().Clients {
var url string
var err error

switch serviceKey {
case common.CoreDataServiceKey:
dic.Update(di.ServiceConstructorMap{
container.EventClientName: func(get di.Get) interface{} {
return clients.NewEventClient(url, jwtSecretProvider)
},
})
case common.CoreMetaDataServiceKey:
dic.Update(di.ServiceConstructorMap{
container.DeviceClientName: func(get di.Get) interface{} {
return clients.NewDeviceClient(url, jwtSecretProvider)
},
container.DeviceServiceClientName: func(get di.Get) interface{} {
return clients.NewDeviceServiceClient(url, jwtSecretProvider)
},
container.DeviceProfileClientName: func(get di.Get) interface{} {
return clients.NewDeviceProfileClient(url, jwtSecretProvider)
},
container.ProvisionWatcherClientName: func(get di.Get) interface{} {
return clients.NewProvisionWatcherClient(url, jwtSecretProvider)
},
})

case common.CoreCommandServiceKey:
var client interfaces.CommandClient

if serviceInfo.UseMessageBus {
// TODO: Move following outside loop when multiple messaging based clients exist
messageClient := container.MessagingClientFrom(dic.Get)
if messageClient == nil {
lc.Errorf("Unable to create Command client using MessageBus: %s", "MessageBus Client was not created")
if !serviceInfo.UseMessageBus {
url, err = cb.getClientUrl(serviceKey, serviceInfo.Url(), startupTimer, lc)
if err != nil {
lc.Error(err.Error())
return false
}
}

// TODO: Move following outside loop when multiple messaging based clients exist
timeout, err := time.ParseDuration(config.GetBootstrap().Service.RequestTimeout)
if err != nil {
lc.Errorf("Unable to parse Service.RequestTimeout as a time duration: %v", err)
return false
switch serviceKey {
case common.CoreDataServiceKey:
dic.Update(di.ServiceConstructorMap{
container.EventClientName: func(get di.Get) interface{} {
return clients.NewEventClient(url, jwtSecretProvider)
},
})
case common.CoreMetaDataServiceKey:
dic.Update(di.ServiceConstructorMap{
container.DeviceClientName: func(get di.Get) interface{} {
return clients.NewDeviceClient(url, jwtSecretProvider)
},
container.DeviceServiceClientName: func(get di.Get) interface{} {
return clients.NewDeviceServiceClient(url, jwtSecretProvider)
},
container.DeviceProfileClientName: func(get di.Get) interface{} {
return clients.NewDeviceProfileClient(url, jwtSecretProvider)
},
container.ProvisionWatcherClientName: func(get di.Get) interface{} {
return clients.NewProvisionWatcherClient(url, jwtSecretProvider)
},
})

case common.CoreCommandServiceKey:
var client interfaces.CommandClient

if serviceInfo.UseMessageBus {
// TODO: Move following outside loop when multiple messaging based clients exist
messageClient := container.MessagingClientFrom(dic.Get)
if messageClient == nil {
lc.Errorf("Unable to create Command client using MessageBus: %s", "MessageBus Client was not created")
return false
}

// TODO: Move following outside loop when multiple messaging based clients exist
timeout, err := time.ParseDuration(config.GetBootstrap().Service.RequestTimeout)
if err != nil {
lc.Errorf("Unable to parse Service.RequestTimeout as a time duration: %v", err)
return false
}

baseTopic := config.GetBootstrap().MessageBus.GetBaseTopicPrefix()
client = clientsMessaging.NewCommandClient(messageClient, baseTopic, timeout)

lc.Infof("Using messaging for '%s' clients", serviceKey)
} else {
client = clients.NewCommandClient(url, jwtSecretProvider)
}

baseTopic := config.GetBootstrap().MessageBus.GetBaseTopicPrefix()
client = clientsMessaging.NewCommandClient(messageClient, baseTopic, timeout)
dic.Update(di.ServiceConstructorMap{
container.CommandClientName: func(get di.Get) interface{} {
return client
},
})

case common.SupportNotificationsServiceKey:
dic.Update(di.ServiceConstructorMap{
container.NotificationClientName: func(get di.Get) interface{} {
return clients.NewNotificationClient(url, jwtSecretProvider)
},
container.SubscriptionClientName: func(get di.Get) interface{} {
return clients.NewSubscriptionClient(url, jwtSecretProvider)
},
})

case common.SupportSchedulerServiceKey:
dic.Update(di.ServiceConstructorMap{
container.IntervalClientName: func(get di.Get) interface{} {
return clients.NewIntervalClient(url, jwtSecretProvider)
},
container.IntervalActionClientName: func(get di.Get) interface{} {
return clients.NewIntervalActionClient(url, jwtSecretProvider)
},
})

default:

lc.Infof("Using messaging for '%s' clients", serviceKey)
} else {
client = clients.NewCommandClient(url, jwtSecretProvider)
}

dic.Update(di.ServiceConstructorMap{
container.CommandClientName: func(get di.Get) interface{} {
return client
},
})

case common.SupportNotificationsServiceKey:
dic.Update(di.ServiceConstructorMap{
container.NotificationClientName: func(get di.Get) interface{} {
return clients.NewNotificationClient(url, jwtSecretProvider)
},
container.SubscriptionClientName: func(get di.Get) interface{} {
return clients.NewSubscriptionClient(url, jwtSecretProvider)
},
})

case common.SupportSchedulerServiceKey:
dic.Update(di.ServiceConstructorMap{
container.IntervalClientName: func(get di.Get) interface{} {
return clients.NewIntervalClient(url, jwtSecretProvider)
},
container.IntervalActionClientName: func(get di.Get) interface{} {
return clients.NewIntervalActionClient(url, jwtSecretProvider)
},
})

default:

}
}

return true
}

func (cb *ClientsBootstrap) getClientUrl(serviceKey string, defaultUrl string, startupTimer startup.Timer, lc logger.LoggingClient) (string, error) {
if cb.registry == nil {
if cb.registry == nil || cb.inDevMode {
lc.Infof("Using REST for '%s' clients @ %s", serviceKey, defaultUrl)
return defaultUrl, nil
}
Expand Down
29 changes: 15 additions & 14 deletions bootstrap/handlers/clients_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,33 +173,34 @@ func TestClientsBootstrapHandler(t *testing.T) {

for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
clients := make(map[string]config.ClientInfo)
clients := make(config.ClientsCollection)

if test.CoreDataClientInfo != nil {
clients[common.CoreDataServiceKey] = *test.CoreDataClientInfo
clients[common.CoreDataServiceKey] = test.CoreDataClientInfo
}

if test.CommandClientInfo != nil {
clients[common.CoreCommandServiceKey] = *test.CommandClientInfo
clients[common.CoreCommandServiceKey] = test.CommandClientInfo
}

if test.MetadataClientInfo != nil {
clients[common.CoreMetaDataServiceKey] = *test.MetadataClientInfo
clients[common.CoreMetaDataServiceKey] = test.MetadataClientInfo
}

if test.NotificationClientInfo != nil {
clients[common.SupportNotificationsServiceKey] = *test.NotificationClientInfo
clients[common.SupportNotificationsServiceKey] = test.NotificationClientInfo
}

if test.SchedulerClientInfo != nil {
clients[common.SupportSchedulerServiceKey] = *test.SchedulerClientInfo
clients[common.SupportSchedulerServiceKey] = test.SchedulerClientInfo
}

bootstrapConfig := config.BootstrapConfiguration{
Service: config.ServiceInfo{
Service: &config.ServiceInfo{
RequestTimeout: "30s",
},
Clients: clients,
Clients: &clients,
MessageBus: &config.MessageBusInfo{},
}

configMock := &mocks.Configuration{}
Expand All @@ -223,7 +224,7 @@ func TestClientsBootstrapHandler(t *testing.T) {
},
})

actualResult := NewClientsBootstrap().BootstrapHandler(context.Background(), &sync.WaitGroup{}, startupTimer, dic)
actualResult := NewClientsBootstrap(false).BootstrapHandler(context.Background(), &sync.WaitGroup{}, startupTimer, dic)
require.Equal(t, actualResult, test.ExpectedResult)
if test.ExpectedResult == false {
return
Expand Down Expand Up @@ -308,16 +309,16 @@ func TestCommandMessagingClientErrors(t *testing.T) {

mockMessaging := &messagingMocks.MessageClient{}

clients := make(map[string]config.ClientInfo)
clients[common.CoreCommandServiceKey] = config.ClientInfo{
clients := make(config.ClientsCollection)
clients[common.CoreCommandServiceKey] = &config.ClientInfo{
UseMessageBus: true,
}

bootstrapConfig := config.BootstrapConfiguration{
Service: config.ServiceInfo{
Service: &config.ServiceInfo{
RequestTimeout: test.TimeoutDuration,
},
Clients: clients,
Clients: &clients,
}

configMock := &mocks.Configuration{}
Expand All @@ -340,7 +341,7 @@ func TestCommandMessagingClientErrors(t *testing.T) {
})

startupTimer := startup.NewTimer(1, 1)
actualResult := NewClientsBootstrap().BootstrapHandler(context.Background(), &sync.WaitGroup{}, startupTimer, dic)
actualResult := NewClientsBootstrap(false).BootstrapHandler(context.Background(), &sync.WaitGroup{}, startupTimer, dic)
require.False(t, actualResult)

mockLogger.AssertNumberOfCalls(t, "Errorf", 1)
Expand Down
Loading