From b7afc2aba7a1c5861c6c21c42b42dc1283ea5949 Mon Sep 17 00:00:00 2001 From: Chris Hung Date: Thu, 23 Feb 2023 04:04:29 +0800 Subject: [PATCH] feat!: replace REST device validation callback with MessageBus (#1325) BREAKING CHANGE: remove /validate/device REST endpoint closes #1260 Signed-off-by: Chris Hung --- internal/controller/http/restrouter.go | 2 - internal/controller/http/validation.go | 44 ------ internal/controller/http/validation_test.go | 122 --------------- internal/controller/messaging/callback.go | 4 +- internal/controller/messaging/command.go | 4 +- internal/controller/messaging/validation.go | 106 +++++++++++++ .../controller/messaging/validation_test.go | 144 ++++++++++++++++++ openapi/v3/changes.txt | 1 + openapi/v3/device-sdk.yaml | 35 ----- pkg/service/main.go | 10 +- 10 files changed, 261 insertions(+), 211 deletions(-) delete mode 100644 internal/controller/http/validation.go delete mode 100644 internal/controller/http/validation_test.go create mode 100644 internal/controller/messaging/validation.go create mode 100644 internal/controller/messaging/validation_test.go diff --git a/internal/controller/http/restrouter.go b/internal/controller/http/restrouter.go index 20b3923df..bc658670a 100644 --- a/internal/controller/http/restrouter.go +++ b/internal/controller/http/restrouter.go @@ -61,8 +61,6 @@ func (c *RestController) InitRestRoutes() { c.addReservedRoute(common.ApiSecretRoute, c.Secret).Methods(http.MethodPost) // discovery c.addReservedRoute(common.ApiDiscoveryRoute, c.Discovery).Methods(http.MethodPost) - // validate - c.addReservedRoute(common.ApiDeviceValidationRoute, c.ValidateDevice).Methods(http.MethodPost) // device command c.addReservedRoute(common.ApiDeviceNameCommandNameRoute, c.GetCommand).Methods(http.MethodGet) c.addReservedRoute(common.ApiDeviceNameCommandNameRoute, c.SetCommand).Methods(http.MethodPut) diff --git a/internal/controller/http/validation.go b/internal/controller/http/validation.go deleted file mode 100644 index 6886ca71f..000000000 --- a/internal/controller/http/validation.go +++ /dev/null @@ -1,44 +0,0 @@ -// -// Copyright (C) 2022 IOTech Ltd -// -// SPDX-License-Identifier: Apache-2.0 - -package http - -import ( - "encoding/json" - "net/http" - - "github.com/edgexfoundry/go-mod-core-contracts/v3/common" - "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos" - commonDTO "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/common" - "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/requests" - "github.com/edgexfoundry/go-mod-core-contracts/v3/errors" - - "github.com/edgexfoundry/device-sdk-go/v3/internal/container" -) - -func (c *RestController) ValidateDevice(writer http.ResponseWriter, request *http.Request) { - defer request.Body.Close() - - var deviceRequest requests.AddDeviceRequest - validator := container.DeviceValidatorFrom(c.dic.Get) - if validator != nil { - err := json.NewDecoder(request.Body).Decode(&deviceRequest) - if err != nil { - edgexErr := errors.NewCommonEdgeX(errors.KindContractInvalid, "json decoding failed", err) - c.sendEdgexError(writer, request, edgexErr, common.ApiDeviceValidationRoute) - return - } - - err = validator.ValidateDevice(dtos.ToDeviceModel(deviceRequest.Device)) - if err != nil { - edgexErr := errors.NewCommonEdgeX(errors.KindServerError, "Device validation failed", err) - c.sendEdgexError(writer, request, edgexErr, common.ApiDeviceValidationRoute) - return - } - } - - res := commonDTO.NewBaseResponse(deviceRequest.RequestId, "", http.StatusOK) - c.sendResponse(writer, request, common.ApiDeviceValidationRoute, res, http.StatusOK) -} diff --git a/internal/controller/http/validation_test.go b/internal/controller/http/validation_test.go deleted file mode 100644 index 156e0c5cb..000000000 --- a/internal/controller/http/validation_test.go +++ /dev/null @@ -1,122 +0,0 @@ -// -// Copyright (C) 2022 IOTech Ltd -// -// SPDX-License-Identifier: Apache-2.0 - -package http - -import ( - "encoding/json" - "errors" - "net/http" - "net/http/httptest" - "strings" - "testing" - - bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" - "github.com/edgexfoundry/go-mod-bootstrap/v3/di" - "github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger" - "github.com/edgexfoundry/go-mod-core-contracts/v3/common" - "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos" - commonDTO "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/common" - "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/requests" - "github.com/edgexfoundry/go-mod-core-contracts/v3/models" - "github.com/google/uuid" - "github.com/gorilla/mux" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/edgexfoundry/device-sdk-go/v3/internal/container" - "github.com/edgexfoundry/device-sdk-go/v3/pkg/models/mocks" -) - -var mockDevice = requests.AddDeviceRequest{ - BaseRequest: commonDTO.NewBaseRequest(), - Device: dtos.Device{ - Name: "test-device", - Description: "", - AdminState: models.Unlocked, - OperatingState: models.Up, - ServiceName: "test-service", - ProfileName: "test-profile", - Protocols: nil, - }, -} - -var validProtocols = map[string]models.ProtocolProperties{"valid": {}} -var invalidProtocols = map[string]models.ProtocolProperties{"invalid": {}} - -func TestRestController_Validate(t *testing.T) { - validDeviceRequest := mockDevice - validDeviceRequest.Device.Protocols = dtos.FromProtocolModelsToDTOs(validProtocols) - invalidDeviceRequest := mockDevice - invalidDeviceRequest.Device.Protocols = dtos.FromProtocolModelsToDTOs(invalidProtocols) - - validatorMock := &mocks.DeviceValidator{} - validatorMock.On("ValidateDevice", dtos.ToDeviceModel(validDeviceRequest.Device)).Return(nil) - validatorMock.On("ValidateDevice", dtos.ToDeviceModel(invalidDeviceRequest.Device)).Return(errors.New("invalid")) - - dic := di.NewContainer(di.ServiceConstructorMap{ - bootstrapContainer.LoggingClientInterfaceName: func(get di.Get) interface{} { - return logger.NewMockClient() - }, - container.DeviceValidatorName: func(get di.Get) interface{} { - return validatorMock - }, - }) - - tests := []struct { - name string - deviceRequest interface{} - expectedStatusCode int - }{ - {"Valid - validation succeed", validDeviceRequest, http.StatusOK}, - {"Invalid - validation failed", invalidDeviceRequest, http.StatusInternalServerError}, - {"Invalid - bad request body", "invalid", http.StatusBadRequest}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - jsonData, err := json.Marshal(tt.deviceRequest) - require.NoError(t, err) - reader := strings.NewReader(string(jsonData)) - - req, err := http.NewRequest(http.MethodPost, common.ApiDeviceValidationRoute, reader) - require.NoError(t, err) - - controller := NewRestController(mux.NewRouter(), dic, uuid.NewString()) - recorder := httptest.NewRecorder() - handler := http.HandlerFunc(controller.ValidateDevice) - handler.ServeHTTP(recorder, req) - - assert.Equal(t, tt.expectedStatusCode, recorder.Result().StatusCode, "Wrong status code") - }) - } -} - -func TestRestController_Validate_Not_Implemented(t *testing.T) { - dic := di.NewContainer(di.ServiceConstructorMap{ - bootstrapContainer.LoggingClientInterfaceName: func(get di.Get) interface{} { - return logger.NewMockClient() - }, - container.DeviceValidatorName: func(get di.Get) interface{} { - return nil - }, - }) - - validDevice := mockDevice - validDevice.Device.Protocols = dtos.FromProtocolModelsToDTOs(validProtocols) - - jsonData, err := json.Marshal(validProtocols) - require.NoError(t, err) - reader := strings.NewReader(string(jsonData)) - - req, err := http.NewRequest(http.MethodPost, common.ApiDeviceValidationRoute, reader) - require.NoError(t, err) - - controller := NewRestController(mux.NewRouter(), dic, uuid.NewString()) - recorder := httptest.NewRecorder() - handler := http.HandlerFunc(controller.ValidateDevice) - handler.ServeHTTP(recorder, req) - - assert.Equal(t, http.StatusOK, recorder.Result().StatusCode, "Wrong status code") -} diff --git a/internal/controller/messaging/callback.go b/internal/controller/messaging/callback.go index 68340d3a7..d06c94b2c 100644 --- a/internal/controller/messaging/callback.go +++ b/internal/controller/messaging/callback.go @@ -22,8 +22,6 @@ import ( "github.com/edgexfoundry/device-sdk-go/v3/internal/container" ) -const MetadataSystemEventTopic = "MetadataSystemEventTopic" - func MetadataSystemEventCallback(ctx context.Context, dic *di.Container) errors.EdgeX { lc := bootstrapContainer.LoggingClientFrom(dic.Get) messageBusInfo := container.ConfigurationFrom(dic.Get).MessageBus @@ -57,7 +55,7 @@ func MetadataSystemEventCallback(ctx context.Context, dic *di.Container) errors. case err = <-messageErrors: lc.Error(err.Error()) case msgEnvelope := <-messages: - lc.Debugf("System event received on message queue. Topic: %s, Correlation-id: %s ", metadataSystemEventTopic, msgEnvelope.CorrelationID) + lc.Debugf("System event received on message queue. Topic: %s, Correlation-id: %s", msgEnvelope.ReceivedTopic, msgEnvelope.CorrelationID) var systemEvent dtos.SystemEvent err := json.Unmarshal(msgEnvelope.Payload, &systemEvent) diff --git a/internal/controller/messaging/command.go b/internal/controller/messaging/command.go index 69fcd407f..b079a01df 100644 --- a/internal/controller/messaging/command.go +++ b/internal/controller/messaging/command.go @@ -59,7 +59,7 @@ func SubscribeCommands(ctx context.Context, dic *di.Container) errors.EdgeX { case err = <-messageErrors: lc.Error(err.Error()) case msgEnvelope := <-messages: - lc.Debugf("Command request received on message queue. Topic: %s, Correlation-id: %s ", requestSubscribeTopic, msgEnvelope.CorrelationID) + lc.Debugf("Command request received on message queue. Topic: %s, Correlation-id: %s", msgEnvelope.ReceivedTopic, msgEnvelope.CorrelationID) // expected command request topic scheme: #//// topicLevels := strings.Split(msgEnvelope.ReceivedTopic, "/") @@ -86,7 +86,7 @@ func SubscribeCommands(ctx context.Context, dic *di.Container) errors.EdgeX { continue } - lc.Debugf("Command response published on message queue. Topic: %s, Correlation-id: %s ", responsePublishTopic, msgEnvelope.CorrelationID) + lc.Debugf("Command response published on message queue. Topic: %s, Correlation-id: %s", responsePublishTopic, msgEnvelope.CorrelationID) } } }() diff --git a/internal/controller/messaging/validation.go b/internal/controller/messaging/validation.go new file mode 100644 index 000000000..37d78240f --- /dev/null +++ b/internal/controller/messaging/validation.go @@ -0,0 +1,106 @@ +// +// Copyright (C) 2023 IOTech Ltd +// +// SPDX-License-Identifier: Apache-2.0 + +package messaging + +import ( + "context" + "encoding/json" + + bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" + "github.com/edgexfoundry/go-mod-bootstrap/v3/di" + "github.com/edgexfoundry/go-mod-core-contracts/v3/common" + "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos" + "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/requests" + "github.com/edgexfoundry/go-mod-core-contracts/v3/errors" + "github.com/edgexfoundry/go-mod-messaging/v3/pkg/types" + + "github.com/edgexfoundry/device-sdk-go/v3/internal/container" +) + +func SubscribeDeviceValidation(ctx context.Context, dic *di.Container) errors.EdgeX { + lc := bootstrapContainer.LoggingClientFrom(dic.Get) + messageBusInfo := container.ConfigurationFrom(dic.Get).MessageBus + serviceName := container.DeviceServiceFrom(dic.Get).Name + + requestTopic := common.BuildTopic(messageBusInfo.GetBaseTopicPrefix(), serviceName, common.ValidateDeviceSubscribeTopic) + lc.Infof("Subscribing to device validation requests on topic: %s", requestTopic) + + responseTopicPrefix := common.BuildTopic(messageBusInfo.GetBaseTopicPrefix(), common.ResponseTopic, serviceName) + lc.Infof("Responses to device validation requests will be published on topic: %s/", responseTopicPrefix) + + messages := make(chan types.MessageEnvelope) + messageErrors := make(chan error) + topics := []types.TopicChannel{ + { + Topic: requestTopic, + Messages: messages, + }, + } + + messageBus := bootstrapContainer.MessagingClientFrom(dic.Get) + err := messageBus.Subscribe(topics, messageErrors) + if err != nil { + return errors.NewCommonEdgeXWrapper(err) + } + + go func() { + for { + select { + case <-ctx.Done(): + lc.Infof("Exiting waiting for MessageBus '%s' topic messages", requestTopic) + return + case err = <-messageErrors: + lc.Error(err.Error()) + case msgEnvelope := <-messages: + lc.Debugf("Device validation request received on message queue. Topic: %s, Correlation-id: %s", msgEnvelope.ReceivedTopic, msgEnvelope.CorrelationID) + + responseTopic := common.BuildTopic(responseTopicPrefix, msgEnvelope.RequestID) + + validator := container.DeviceValidatorFrom(dic.Get) + if validator != nil { + var deviceRequest requests.AddDeviceRequest + err = json.Unmarshal(msgEnvelope.Payload, &deviceRequest) + if err != nil { + lc.Errorf("Failed to JSON decoding AddDeviceRequest: %s", err.Error()) + res := types.NewMessageEnvelopeWithError(msgEnvelope.RequestID, err.Error()) + err = messageBus.Publish(res, responseTopic) + if err != nil { + lc.Errorf("Failed to publish device validation error response: %s", err.Error()) + } + continue + } + + err = validator.ValidateDevice(dtos.ToDeviceModel(deviceRequest.Device)) + if err != nil { + lc.Errorf("Device validation failed: %s", err.Error()) + res := types.NewMessageEnvelopeWithError(msgEnvelope.RequestID, err.Error()) + err = messageBus.Publish(res, responseTopic) + if err != nil { + lc.Errorf("Failed to publish device validation error response: %s", err.Error()) + } + continue + } + } + + res, err := types.NewMessageEnvelopeForResponse(nil, msgEnvelope.RequestID, msgEnvelope.CorrelationID, common.ContentTypeJSON) + if err != nil { + lc.Errorf("Failed to create device validation response envelope: %s", err.Error()) + continue + } + + err = messageBus.Publish(res, responseTopic) + if err != nil { + lc.Errorf("Failed to publish device validation response: %s", err.Error()) + continue + } + + lc.Debugf("Device validation response published on message queue. Topic: %s, Correlation-id: %s", responseTopic, msgEnvelope.CorrelationID) + } + } + }() + + return nil +} diff --git a/internal/controller/messaging/validation_test.go b/internal/controller/messaging/validation_test.go new file mode 100644 index 000000000..bf8ba75ad --- /dev/null +++ b/internal/controller/messaging/validation_test.go @@ -0,0 +1,144 @@ +// +// Copyright (C) 2023 IOTech Ltd +// +// SPDX-License-Identifier: Apache-2.0 + +package messaging + +import ( + "context" + "encoding/json" + "errors" + "sync" + "testing" + "time" + + bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" + "github.com/edgexfoundry/go-mod-bootstrap/v3/di" + loggerMocks "github.com/edgexfoundry/go-mod-core-contracts/v3/clients/logger/mocks" + "github.com/edgexfoundry/go-mod-core-contracts/v3/common" + "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos" + "github.com/edgexfoundry/go-mod-core-contracts/v3/dtos/requests" + "github.com/edgexfoundry/go-mod-core-contracts/v3/models" + messagingMocks "github.com/edgexfoundry/go-mod-messaging/v3/messaging/mocks" + "github.com/edgexfoundry/go-mod-messaging/v3/pkg/types" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/edgexfoundry/device-sdk-go/v3/internal/config" + "github.com/edgexfoundry/device-sdk-go/v3/internal/container" + "github.com/edgexfoundry/device-sdk-go/v3/pkg/models/mocks" +) + +const ( + testDeviceName = "testDevice" + testServiceName = "testService" + testProfileName = "testProfile" + testProtocolName = "testProtocol" +) + +func TestDeviceValidation(t *testing.T) { + var wg sync.WaitGroup + expectedRequestId := uuid.NewString() + expectedCorrelationId := uuid.NewString() + expectedRequestTopic := common.BuildTopic(common.DefaultBaseTopic, testServiceName, common.ValidateDeviceSubscribeTopic) + expectedResponseTopic := common.BuildTopic(common.DefaultBaseTopic, common.ResponseTopic, testServiceName, expectedRequestId) + expectedDevice := dtos.Device{ + Name: testDeviceName, + AdminState: models.Locked, + OperatingState: models.Up, + ServiceName: testServiceName, + ProfileName: testProfileName, + Protocols: map[string]dtos.ProtocolProperties{ + testProtocolName: {"key": "value"}, + }, + } + validationFailedDevice := expectedDevice + validationFailedDevice.Name = "validationFailedDevice" + expectedAddDeviceRequestBytes, err := json.Marshal(requests.NewAddDeviceRequest(expectedDevice)) + require.NoError(t, err) + validationFailedDeviceBytes, err := json.Marshal(requests.NewAddDeviceRequest(validationFailedDevice)) + require.NoError(t, err) + + mockLogger := &loggerMocks.LoggingClient{} + mockLogger.On("Infof", mock.Anything, mock.Anything, mock.Anything).Return(nil) + mockLogger.On("Debugf", mock.Anything, mock.Anything, mock.Anything).Return(nil) + mockLogger.On("Errorf", mock.Anything, mock.Anything).Return(nil) + + mockValidator := &mocks.DeviceValidator{} + mockValidator.On("ValidateDevice", dtos.ToDeviceModel(expectedDevice)).Return(nil) + mockValidator.On("ValidateDevice", dtos.ToDeviceModel(validationFailedDevice)).Return(errors.New("validation failed")) + + dic := di.NewContainer(di.ServiceConstructorMap{ + container.ConfigurationName: func(get di.Get) any { + return &config.ConfigurationStruct{} + }, + container.DeviceValidatorName: func(get di.Get) any { + return mockValidator + }, + container.DeviceServiceName: func(get di.Get) any { + return &models.DeviceService{Name: testServiceName} + }, + bootstrapContainer.LoggingClientInterfaceName: func(get di.Get) any { + return mockLogger + }, + }) + + tests := []struct { + name string + requestBytes []byte + expectedError bool + }{ + {"valid - device validation succeed", expectedAddDeviceRequestBytes, false}, + {"valid - device validation failed", validationFailedDeviceBytes, true}, + {"invalid - message payload is not AddDeviceRequest", []byte("invalid"), true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockMessaging := &messagingMocks.MessageClient{} + mockMessaging.On("Subscribe", mock.Anything, mock.Anything).Run(func(args mock.Arguments) { + topics := args.Get(0).([]types.TopicChannel) + require.Len(t, topics, 1) + require.Equal(t, expectedRequestTopic, topics[0].Topic) + wg.Add(1) + go func() { + defer wg.Done() + topics[0].Messages <- types.MessageEnvelope{ + RequestID: expectedRequestId, + CorrelationID: expectedCorrelationId, + ReceivedTopic: expectedRequestTopic, + Payload: tt.requestBytes, + } + time.Sleep(time.Second * 1) + }() + }).Return(nil) + mockMessaging.On("Publish", mock.Anything, expectedResponseTopic).Run(func(args mock.Arguments) { + response := args.Get(0).(types.MessageEnvelope) + assert.Equal(t, expectedRequestId, response.RequestID) + if tt.expectedError { + assert.Equal(t, response.ErrorCode, 1) + assert.NotEmpty(t, response.Payload) + assert.Equal(t, response.ContentType, types.ContentTypeText) + } else { + assert.Equal(t, expectedCorrelationId, response.CorrelationID) + assert.Equal(t, response.ErrorCode, 0) + assert.Empty(t, response.Payload) + assert.Equal(t, response.ContentType, types.ContentTypeJSON) + } + }).Return(nil) + + dic.Update(di.ServiceConstructorMap{ + bootstrapContainer.MessagingClientName: func(get di.Get) any { + return mockMessaging + }, + }) + err := SubscribeDeviceValidation(context.Background(), dic) + require.NoError(t, err) + + wg.Wait() + mockMessaging.AssertExpectations(t) + }) + } +} diff --git a/openapi/v3/changes.txt b/openapi/v3/changes.txt index 71e4dc4b3..a92d957a7 100644 --- a/openapi/v3/changes.txt +++ b/openapi/v3/changes.txt @@ -5,5 +5,6 @@ API changes from v2 * Remove /callback/profile endpoints * Remove /callback/watcher endpoints * Remove /callback/service endpoints +* Remove /validate/device endpoints * Rename path on SecretRequest to secretName * Rename the NewDeviceRequest to DeviceValidationRequest, and add "tags" and "properties" fields into the schema of DeviceValidationRequest diff --git a/openapi/v3/device-sdk.yaml b/openapi/v3/device-sdk.yaml index f8a8b2b9b..7bee3029e 100644 --- a/openapi/v3/device-sdk.yaml +++ b/openapi/v3/device-sdk.yaml @@ -543,41 +543,6 @@ paths: schema: $ref: '#/components/schemas/ErrorResponse' - /validate/device: - post: - summary: Run the device-specific validation for a Device's Properties. - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/DeviceValidationRequest' - required: true - responses: - '200': - description: Validation succeed or validator implicitly not implemented - headers: - X-Correlation-ID: - $ref: '#/components/headers/correlatedResponseHeader' - content: - application/json: - schema: - $ref: '#/components/schemas/BaseResponse' - '400': - description: Invalid validation request. - headers: - X-Correlation-ID: - $ref: '#/components/headers/correlatedResponseHeader' - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '500': - description: Validation failed. - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - /discovery: post: description: Run the discovery request for a Device Service. No request body is required. diff --git a/pkg/service/main.go b/pkg/service/main.go index f55cc09ae..5c33ac5b6 100644 --- a/pkg/service/main.go +++ b/pkg/service/main.go @@ -11,17 +11,15 @@ import ( "os" "sync" - "github.com/edgexfoundry/go-mod-bootstrap/v3/config" - "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap" bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/flags" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/handlers" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/interfaces" "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/startup" + "github.com/edgexfoundry/go-mod-bootstrap/v3/config" "github.com/edgexfoundry/go-mod-bootstrap/v3/di" "github.com/edgexfoundry/go-mod-core-contracts/v3/common" - "github.com/gorilla/mux" "github.com/edgexfoundry/device-sdk-go/v3/internal/autodiscovery" @@ -114,6 +112,12 @@ func messageBusBootstrapHandler(ctx context.Context, wg *sync.WaitGroup, startup return false } + err = messaging.SubscribeDeviceValidation(ctx, dic) + if err != nil { + lc.Errorf("Failed to subscribe device validation request: %v", err) + return false + } + return true }