Skip to content
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
4 changes: 3 additions & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//
// Copyright (c) 2019 Intel Corporation
// Copyright (c) 2025 IOTech Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -15,5 +16,6 @@
//

edgeXBuildGoMod (
project: 'go-mod-bootstrap'
project: 'go-mod-bootstrap',
goVersion: '1.23'
)
99 changes: 58 additions & 41 deletions bootstrap/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,20 +179,6 @@ func (cp *Processor) Process(
serviceKey); err != nil {
return err
}

// listen for changes on Writable
cp.listenForPrivateChanges(serviceConfig, cp.privateConfigClient, utils.BuildBaseKey(configStem, serviceKey))
cp.lc.Infof("listening for private config changes")
cp.listenForCommonChanges(serviceConfig, cp.commonConfigClient, cp.privateConfigClient, utils.BuildBaseKey(configStem, common.CoreCommonConfigServiceKey, allServicesKey))
cp.lc.Infof("listening for all services common config changes")
if cp.appConfigClient != nil {
cp.listenForCommonChanges(serviceConfig, cp.appConfigClient, cp.privateConfigClient, utils.BuildBaseKey(configStem, common.CoreCommonConfigServiceKey, appServicesKey))
cp.lc.Infof("listening for application service common config changes")
}
if cp.deviceConfigClient != nil {
cp.listenForCommonChanges(serviceConfig, cp.deviceConfigClient, cp.privateConfigClient, utils.BuildBaseKey(configStem, common.CoreCommonConfigServiceKey, deviceServicesKey))
cp.lc.Infof("listening for device service common config changes")
}
} else {
// Now load common configuration from local file if not using config provider and -cc/--commonConfig flag is used.
// NOTE: Some security services don't use any common configuration and don't use the configuration provider.
Expand Down Expand Up @@ -330,6 +316,7 @@ func (cp *Processor) loadConfigByProvider(
cp.lc.Info("Private configuration has been pushed to into Configuration Provider with overrides applied")
}

// listen for changes on Writable
cp.listenForPrivateChanges(serviceConfig, cp.privateConfigClient, utils.BuildBaseKey(configStem, serviceKey))
cp.lc.Infof("listening for private config changes")
cp.listenForCommonChanges(serviceConfig, cp.commonConfigClient, cp.privateConfigClient, utils.BuildBaseKey(configStem, common.CoreCommonConfigServiceKey, allServicesKey))
Expand Down Expand Up @@ -684,10 +671,7 @@ func (cp *Processor) ListenForCustomConfigChanges(
defer cp.wg.Done()

errorStream := make(chan error)
defer close(errorStream)

updateStream := make(chan any)
defer close(updateStream)

go cp.privateConfigClient.WatchForChanges(updateStream, errorStream, configToWatch, sectionName, cp.getMessageClient)

Expand Down Expand Up @@ -797,10 +781,7 @@ func (cp *Processor) listenForPrivateChanges(serviceConfig interfaces.Configurat
defer cp.wg.Done()

errorStream := make(chan error)
defer close(errorStream)

updateStream := make(chan any)
defer close(updateStream)

go privateConfigClient.WatchForChanges(updateStream, errorStream, serviceConfig.EmptyWritablePtr(), writableKey, cp.getMessageClient)

Expand All @@ -814,29 +795,19 @@ func (cp *Processor) listenForPrivateChanges(serviceConfig interfaces.Configurat
case ex := <-errorStream:
lc.Errorf("error occurred during listening to the configuration changes: %s", ex.Error())

case raw, ok := <-updateStream:
case _, ok := <-updateStream:
if !ok {
return
}

usedKeys, err := privateConfigClient.GetConfigurationKeys(writableKey)
if err != nil {
lc.Errorf("failed to get list of private configuration keys for %s: %v", writableKey, err)
}

rawMap, err := utils.RemoveUnusedSettings(raw, utils.BuildBaseKey(baseKey, writableKey), utils.StringSliceToMap(usedKeys))
if err != nil {
lc.Errorf("failed to remove unused private settings in %s: %v", writableKey, err)
}

// Config Provider sends an update as soon as the watcher is connected even though there are not
// any changes to the configuration. This causes an issue during start-up if there is an
// envVars override of one of the Writable fields, so we must ignore the first update.
if isFirstUpdate {
isFirstUpdate = false
continue
}
cp.applyWritableUpdates(serviceConfig, rawMap)
cp.applyWritableUpdates(serviceConfig)
}
}
}()
Expand All @@ -859,10 +830,7 @@ func (cp *Processor) listenForCommonChanges(fullServiceConfig interfaces.Configu
var previousCommonWritable any

errorStream := make(chan error)
defer close(errorStream)

updateStream := make(chan any)
defer close(updateStream)

go commonConfigClient.WatchForChanges(updateStream, errorStream, fullServiceConfig.EmptyWritablePtr(), writableKey, cp.getMessageClient)

Expand Down Expand Up @@ -898,6 +866,16 @@ func (cp *Processor) listenForCommonChanges(fullServiceConfig interfaces.Configu
// envVars override of one of the Writable fields, so on the first update we can just save a copy of the
// common writable for comparison for future writable updates.
if isFirstUpdate {
// When the configuration provider sends an initial update (raw == nil), it indicates the watcher has connected
// but no configuration changes have occurred. In this case, we use the writable configuration directly from
// the full service configuration to establish a baseline for future comparisons.
if raw == nil {
w := fullServiceConfig.GetWritablePtr()
rawMap, err = utils.RemoveUnusedSettings(w, baseKey, utils.StringSliceToMap(usedKeys))
if err != nil {
lc.Errorf("failed to remove unused common settings in %s: %v", writableKey, err)
}
}
isFirstUpdate = false
previousCommonWritable = rawMap
continue
Expand All @@ -908,7 +886,7 @@ func (cp *Processor) listenForCommonChanges(fullServiceConfig interfaces.Configu
}

// ensure that the local copy of the common writable gets updated no matter what
previousCommonWritable = raw
previousCommonWritable = rawMap
}
}
}(fullServiceConfig, configClient, privateConfigClient, baseKey)
Expand All @@ -927,7 +905,7 @@ func (cp *Processor) processCommonConfigChange(fullServiceConfig interfaces.Conf

// This case should not happen at this point, but need to guard against nil pointer
if otherConfigClient != nil {
if cp.isKeyInConfig(configClient, changedKey) {
if cp.isKeyInConfig(otherConfigClient, changedKey) {
cp.lc.Warnf("ignoring changed writable key %s overwritten in App or Device common writable", changedKey)
return nil
}
Expand All @@ -942,7 +920,7 @@ func (cp *Processor) processCommonConfigChange(fullServiceConfig interfaces.Conf
}
}

cp.applyWritableUpdates(fullServiceConfig, raw)
cp.applyWritableUpdates(fullServiceConfig)
return nil
}

Expand All @@ -969,7 +947,7 @@ func (cp *Processor) findChangedKey(previous any, updated any) (string, bool) {
return changedKey, true
}

func (cp *Processor) applyWritableUpdates(serviceConfig interfaces.Configuration, raw any) {
func (cp *Processor) applyWritableUpdates(serviceConfig interfaces.Configuration) {
lc := cp.lc
previousLogLevel := serviceConfig.GetLogLevel()
previousTelemetryInterval := serviceConfig.GetTelemetryInfo().Interval
Expand All @@ -979,10 +957,49 @@ func (cp *Processor) applyWritableUpdates(serviceConfig interfaces.Configuration
lc.Errorf("failed to deep copy insecure secrets: %v", err)
}

if err := utils.MergeValues(serviceConfig.GetWritablePtr(), raw); err != nil {
lc.Errorf("failed to apply Writable change to service configuration: %v", err)
serviceConfigCopy, err := copyConfigurationStruct(serviceConfig)
if err != nil {
lc.Errorf("failed to copy configuration struct: %v", err)
return
}
v := reflect.ValueOf(serviceConfigCopy)
if v.Kind() == reflect.Ptr && !v.IsNil() {
v = v.Elem()
v.Set(reflect.Zero(v.Type()))
}

err = cp.loadConfigFromProvider(serviceConfigCopy, cp.commonConfigClient)
if err != nil {
lc.Errorf("failed to load common configuration: %v", err)
return
}

if cp.deviceConfigClient != nil {
err = cp.loadConfigFromProvider(serviceConfigCopy, cp.deviceConfigClient)
if err != nil {
lc.Errorf("failed to load device service common configuration: %v", err)
return
}
}

if cp.appConfigClient != nil {
err = cp.loadConfigFromProvider(serviceConfigCopy, cp.appConfigClient)
if err != nil {
lc.Errorf("failed to load application service common configuration: %v", err)
return
}
}

if cp.privateConfigClient != nil {
err = cp.loadConfigFromProvider(serviceConfigCopy, cp.privateConfigClient)
if err != nil {
lc.Errorf("failed to load private configuration: %v", err)
return
}
}

serviceConfig.UpdateFromRaw(serviceConfigCopy)

currentInsecureSecrets := serviceConfig.GetInsecureSecrets()
currentLogLevel := serviceConfig.GetLogLevel()
currentTelemetryInterval := serviceConfig.GetTelemetryInfo().Interval
Expand Down