edgexfoundry / app-functions-sdk-go

Owner: Applications WG
Apache License 2.0
43 stars 81 forks source link

get read-only common configs for custom application #1596

Open kamauz opened 2 months ago

kamauz commented 2 months ago

Situation:

Goal:

Given the following snippet of code, I need to populate config.ServiceConfig.Database with service.config.Database , however service properties are not accessible

func main() {
    service, ok := pkg.NewAppServiceWithTargetType(serviceKey, &[]byte{})
    if !ok {
        service.LoggingClient().Errorf("App Service initialization failed for %s", serviceKey)
        os.Exit(-1)
    }

    lc := service.LoggingClient()

    config := config.ServiceConfig{}

        ////////////////////////////
        // <--- 
        // service.config.Database = service.config.Database   (config is a private property, not accessible)
        ////////////////////////////

    if err := service.LoadCustomConfig(&config, "CDC"); err != nil {
        lc.Errorf("LoadCustomConfig returned error: %s", err.Error())
        os.Exit(-1)
    }

    service.RegisterCustomTriggerFactory("cdc", func(config interfaces.TriggerConfig) (interfaces.Trigger, error) {
        return trigger.NewCDCTrigger(config), nil
    })
}

So I moved the focus on "LoadCustomConfig" function, but it would expect to read common configurations from a Consul. Otherwise the private configuration is loaded as-is from the default path (res/configuration.yaml) here's the code I'm referring to:

bootstrap/config/config.go

func (cp *Processor) LoadCustomConfigSection(updatableConfig interfaces.UpdatableConfig, sectionName string) error {
    if cp.envVars == nil {
        cp.envVars = environment.NewVariables(cp.lc)
    }

    configClient := container.ConfigClientFrom(cp.dic.Get)

        // no Configuration provider specified
    if configClient == nil {

/////////////// START /////////////////////

        cp.lc.Info("Skipping use of Configuration Provider for custom configuration: Provider not available")
        filePath := GetConfigFileLocation(cp.lc, cp.flags)
        configMap, err := cp.loadConfigYamlFromFile(filePath)
        if err != nil {
            return err
        }

        err = utils.ConvertFromMap(configMap, updatableConfig)
        if err != nil {
            return fmt.Errorf("failed to convert custom configuration into service's configuration: %v", err)
        }

/////////////// END /////////////////////

    } else {
        cp.lc.Infof("Checking if custom configuration ('%s') exists in Configuration Provider", sectionName)

        exists, err := configClient.HasSubConfiguration(sectionName)
............
cloudxxx8 commented 1 month ago

If you don't want to use Consul to manage your config, you can disable the config provider via the environment variable https://docs.edgexfoundry.org/3.1/microservices/configuration/CommonEnvironmentVariables/#edgex_config_provider EDGEX_CONFIG_PROVIDER: "none"

Then, you can manage your CustomConfig in your file system. However, the config should be read-only at runtime, and the credentials should not be passed from common config. If you don't persist your credentials in the secret store, you can use SecretProvider() bootstrapInterfaces.SecretProvider to retrieve the credentials from the insecure section. https://github.com/edgexfoundry/app-functions-sdk-go/blob/d5728ffaae65ea0eca3f1f6683c2d3dae45a3477/pkg/interfaces/service.go#L149

Config example: https://github.com/edgexfoundry/app-service-configurable/blob/99abc15c93f70f8386f8be99e96e68a0293bb7bf/res/mqtt-export/configuration.yaml#L70 https://docs.edgexfoundry.org/3.1/design/adr/014-Secret-Provider-For-All/