V2 Migration Guide
EdgeX 2.0
For the EdgeX 2.0 (Ireland) release there are many backward breaking changes. These changes require custom Application Services and custom profiles (app-service-configurable) to be migrated. This section outlines the necessary steps for this migration.
Custom Application Services
Configuration
The migration of any Application Service's configuration starts with migrating configuration common to all EdgeX services. See the V2 Migration of Common Configuration section for details. The remainder of this section focuses on configuration specific to Application Services.
SecretStoreExclusive
The SecretStoreExclusive
section has been removed in EdgeX 2.0. With EdgeX 2.0 all SecretStores are exclusive, so the existing SecretStore
section is all that is required. Services requiring known secrets
such as redisdb
must inform the Security SecretStore Setup
service (via environment variables) that the application service requires the secret added to its SecretStore. See the Configuring Add-on Services section for more details.
Clients
The client used for the version validation check has changed to being from Core Metadata, rather than Core Data. This is because Core Data is now optional when persistence isn't required since all Device Services publish directly to the EdgeX MessageBus. The configuration for Core Metadata is the only Clients
entry required, all other (see below) are optional based on use case needs.
Note
The port numbers for all EdgeX services have changed which must be reflected in the Clients
configuration. Please see the Default Service Ports section for complete list of the new port assignments.
Example - Core Metadata client configuration
[Clients]
[Clients.core-metadata]
Protocol = "http"
Host = "localhost"
Port = 59881
Example - All available clients configured with new port numbers
[Clients]
# Used for version check on start-up
# Also used for DeviceService, DeviceProfile and Device clients
[Clients.core-metadata]
Protocol = "http"
Host = "localhost"
Port = 59881
# Used for Event client which is used by PushToCoreData function
[Clients.core-data]
Protocol = "http"
Host = "localhost"
Port = 59880
# Used for Command client
[Clients.core-command]
Protocol = "http"
Host = "localhost"
Port = 59882
# Used for Notification and Subscription clients
[Clients.support-notifications]
Protocol = "http"
Host = "localhost"
Port = 59860
Trigger
The Trigger
section (previously named Binding
) has been restructured with EdgexMessageBus
(previously named MessageBus
) and ExternalMqtt
(previously named MqttBroker
) moved under it. The SubscribeTopics
(previously named SubscribeTopic
) has been moved under the EdgexMessageBus.SubscribeHost
and ExternalMqtt
sections. The PublishTopic
has been moved under the EdgexMessageBus.PublishHost
and ExternalMqtt
sections.
EdgeX MessageBus
If your Application Service is using the EdgeX MessageBus trigger, you can then simply copy the complete Trigger
configuration from the example below and tweak it as needed.
Example - EdgeX MessageBus trigger configuration
[Trigger]
Type="edgex-messagebus"
[Trigger.EdgexMessageBus]
Type = "redis"
[Trigger.EdgexMessageBus.SubscribeHost]
Host = "localhost"
Port = 6379
Protocol = "redis"
SubscribeTopics="edgex/events/#"
[Trigger.EdgexMessageBus.PublishHost]
Host = "localhost"
Port = 6379
Protocol = "redis"
PublishTopic="example"
[Trigger.EdgexMessageBus.Optional]
AuthMode = "usernamepassword" # required for redis messagebus (secure or insecure).
SecretName = "redisdb"
From the above example you can see the improved structure and the following changes:
- Default
EdgexMessageBus
type has changed fromZeroMQ
toRedis
. - Type value for
Redis
has changed fromredistreams
toredis
. This is because the implementation no longer uses Redis Streams. It now uses Redis Pub/Sub. SubscribeTopics
is now plural since it now accepts a comma separated list of topics. The default value uses a multi-level topic with a wild card. This is because Core Data and Device Services now publish to a multi-level topics which haveedgex/events
as their base. This allows Application Services to filter by topic rather then receive the data and then filter it out via a pipeline filter function. See the Filter By Topics section for more details.- The EdgeX MessageBus using Redis is a Secure MessageBus, thus the addition of the
AuthMode
andSecretName
settings which allow the credentials to be pulled from the service's SecretStore. See the Secure MessageBus secure for more details.
External MQTT
If your Application service is using the External MQTT trigger do the following:
- Move your existing
MqttBroker
configuration under theTrigger
section (renaming it toExternalMqtt
) - Move your
SubscribeTopic
(renaming it toSubscribeTopics
) under theExternalMqtt
section. - Move your
PublishTopic
under theExternalMqtt
section.
Example - External MQTT trigger configuration
[Trigger]
Type="external-mqtt"
[Trigger.ExternalMqtt]
Url = "tcp://broker.hivemq.com:1883"
SubscribeTopics = "edgex-trigger"
PublishTopic = "edgex-trigger-response"
ClientId = "app-my-service"
ConnectTimeout = "30s"
AutoReconnect = false
KeepAlive = 60
QoS = 0
Retain = false
SkipCertVerify = false
SecretPath = ""
AuthMode = "none"
HTTP
The HTTP trigger configuration has not changed beyond the renaming of Binding
to Trigger
.
Example - HTTP trigger configuration
[Trigger]
Type="http"
Code
Dependencies
You first need to update the go.mod
file to specify go 1.16
and the V2 versions of the App Functions SDK and any EdgeX go-mods directly used by your service. Note the extra /v2
for the modules.
Example go.mod for V2
module <your service>
go 1.16
require (
github.com/edgexfoundry/app-functions-sdk-go/v2 v2.0.0
github.com/edgexfoundry/go-mod-core-contracts/v2 v2.0.0
)
Once that is complete then the import statements for these dependencies must be updated to include the /v2
in the path.
Example import statements for V2
import (
...
"github.com/edgexfoundry/app-functions-sdk-go/v2/pkg/interfaces"
"github.com/edgexfoundry/go-mod-core-contracts/v2/dtos"
)
New APIs
Next changes you will encounter in your code are that the AppFunctionsSDK
and Context
structs have been abstracted into the new ApplicationService
and AppFunctionContext
APIs. See the Application Service API and App Function Context API sections for complete details on these new APIs. The following sections cover migrating your code for these new APIs.
main()
The following changes to your main()
function will be necessary.
Create and Initialize
Your main()
will change to use a factory function to create and initialize the Application Service instance, rather than create instance of AppFunctionsSDK
and call Initialize()
Example - Create Application Service instance
const serviceKey = "app-myservice"
...
service, ok := pkg.NewAppService(serviceKey)
if !ok {
os.Exit(-1)
}
Example - Create Application Service instance with Target Type specified
const serviceKey = "app-myservice"
...
service, ok := pkg.NewAppServiceWithTargetType(serviceKey, &[]byte{})
if !ok {
os.Exit(-1)
}
Since the factory function logs all errors, all you need to do is exit if it returns false
.
Logging Client
The Logging
client is now accessible from the service.LoggingClient()
API.
New extended Logging Client API
The Logging Client API now has formatted
versions of all the logging APIs, which are Infof
, Debugf
, Tracef
, Warnf
and Errorf
. If your code uses fmt.Sprintf
to format your log messages then it can now be simplified by using these new APIs.
Application Settings
The access functions for retrieving the service's custom Application Settings (ApplicationSettings
, GetAppSettingStrings
, and GetAppSetting
) have not changed. An improved capability to have structured custom configuration has been added. See the Structure Custom Configuration section for more details.
Functions Pipeline
Setting the Functions Pipeline has not changed, but the name of some built in functions have changed and new ones have been added. See the Built-In Pipeline Functions section for more details.
Example - Setting Functions Pipeline
if err := service.SetFunctionsPipeline(
transforms.NewFilterFor(deviceNames).FilterByDeviceName,
transforms.NewConversion().TransformToXML,
transforms.NewHTTPSender(exportUrl, "application/xml", false).HTTPPost,
); err != nil {
lc.Errorf("SetFunctionsPipeline returned error: %s", err.Error())
os.Exit(-1)
}
MakeItRun
The MakeItRun
API has not changed.
Example - Call to MakeItRun
err = service.MakeItRun()
if err != nil {
lc.Errorf("MakeItRun returned error: %s", err.Error())
os.Exit(-1)
}
Custom Pipeline Functions
Pipeline Function signature
The major change to custom Pipeline Functions for EdgeX 2.0 is the new function signature which drives all the other changes.
Example - New Pipeline Function signature
type AppFunction = func(ctx AppFunctionContext, data interface{}) (bool, interface{})
This function signature passes in an instance of the new AppFunctionContext API for the context and now has only a single data
instance for the function to operate on.
Return Values
The definitions for the Pipeline Function return values have not changed.
Data
The data
passed in either set to a single instance for the function to process or nil. Now you no longer need to check the length of the incoming data.
Example
if data == nil {
return false, errors.New("No Data Received")
}
Logging Client
The Logging
client is now accessible from the ctx.LoggingClient()
API.
Clients
The available clients have changed with a few additions and ValueDescriptorClient
has been removed. See the Context Clients section for complete list of available clients.
ResponseData
The SetResponseData
and ResponseData
APIs replace the previous Complete
function and direct access to the OutputData
field.
ResponseContentType
The SetResponseContentType
and ResponseContentType
APIs replace the previous direct access to the ResponseContentType
field.
RetryData
The SetRetryData
API replaces the SetRetryData
function and direct access to the RetryData
field.
MarkAsPushed
The MarkAsPushed
capability has been removed
PushToCore
The PushToCore
API replaces the PushToCoreData
function. The API signature has changed. See the PushToCore section for more details.
New Capabilities
Some new capabilities have been added to the new AppFunctionContext
API. See the App Function Context API section for complete details.
App Service Configurable Profiles
Custom profiles used with App Service Configurable are configuration files. These follow the same migration above for custom Application Service configuration, except for the Configurable Functions Pipeline items. The following are the changes for the Configurable Functions Pipeline:
FilterByValueDescriptor
changed toFilterByResourceName
. See the FilterByResourceName section for details.TransformToXML
andTransformToJSON
have been collapsed intoTransform
with additional parameters. See the Transform section for more details.CompressWithGZIP
andCompressWithZLIB
have been collapsed intoCompress
with additional parameters. See the Compress section for more details.EncryptWithAES
has been changed toEncrypt
with additional parameters. See the Encrypt section for more details.BatchByCount
,BatchByTime
andBatchByTimeAndCount
have been collapsed intoBatch
with additional parameters. See the Batch section for more details.SetOutputData
has been renamed toSetResponseData
. See the SetResponseData section for more details.PushToCore
parameters have changed. See the PushToCore section for more details.HTTPPost
,HTTPPostJSON
,HTTPPostXML
,HTTPPut
,HTTPPutJSON
andHTTPPutXML
have been collapsed intoHTTPExport
with additional parameters. See the HTTPExport section for more details.MQTTSecretSend
has been renamed toMQTTExport
with additional parameters. See the MQTTExport section for more details.MarkAsPushed
has been removed. The mark as push capability has been removed from Core Data, which this depended on.MQTTSend
has been removed. This has been replaced byMQTTExport
. See the MQTTExport section for more details.FilterByProfileName
andFilterBySourceName
have been added. See the FilterByProfileName and FilterBySourceName sections for more details.- Ability to define multiple instances of the same Configurable Pipeline Function has been added. See the Multiple Instances of Function section for more details.