Skip to content

Commit

Permalink
Merge pull request #1270 from hahattan/issue-1100
Browse files Browse the repository at this point in the history
feat: implement support for ProvisionWatchersDir
  • Loading branch information
Lenny Goodell authored Jan 9, 2023
2 parents 00b0104 + 1f95b19 commit 8025d41
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 37 deletions.
5 changes: 2 additions & 3 deletions example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,5 @@ Dynamic Device Discovery is triggered either by internal timer(see `Device/Disco

The following steps show how to trigger discovery on device-simple:
1. Set `Device/Discovery/Enabled` to true in [configuration file](cmd/device-simple/res/configuration.toml)
2. Post the [provided provisionwatcher](cmd/device-simple/res/provisionwatcher.json) into core-metadata endpoint: http://edgex-core-metadata:59881/api/v2/provisionwatcher
3. Trigger discovery by sending POST request to DS endpoint: http://edgex-device-simple:59999/api/v2/discovery
4. `Simple-Device02` will be discovered and added to EdgeX.
2. Trigger discovery by sending POST request to DS endpoint: http://edgex-device-simple:59999/api/v2/discovery
3. `Simple-Device02` will be discovered and added to EdgeX.
1 change: 1 addition & 0 deletions example/cmd/device-simple/res/configuration.toml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ TokenFile = "/tmp/edgex/secrets/device-simple/secrets-token.json"
MaxCmdValueLen = 256
ProfilesDir = "./res/profiles"
DevicesDir = "./res/devices"
ProvisionWatchersDir = "./res/provisionwatchers"
UpdateLastConnected = false
AsyncBufferSize = 1
EnableAsyncReadings = true
Expand Down
31 changes: 0 additions & 31 deletions example/cmd/device-simple/res/provisionwatcher.json

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "Simple-Provision-Watcher",
"labels": [
"simple"
],
"identifiers": {
"Address":"simple[0-9]+",
"Port":"3[0-9]{2}"
},
"blockingIdentifiers": {
"Port": [
"397",
"398",
"399"
]
},
"profileName": "Simple-Device",
"serviceName": "device-simple",
"adminState": "UNLOCKED",
"autoEvents": [
{
"interval": "15s",
"sourceName": "SwitchButton"
}
]
}
4 changes: 3 additions & 1 deletion internal/config/types.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// -*- mode: Go; indent-tabs-mode: t -*-
//
// Copyright (C) 2017-2018 Canonical Ltd
// Copyright (C) 2018-2021 IOTech Ltd
// Copyright (C) 2018-2023 IOTech Ltd
// Copyright (c) 2021 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
Expand Down Expand Up @@ -44,6 +44,8 @@ type DeviceInfo struct {
ProfilesDir string
// DevicesDir specifies a directory contains devices files which should be imported on startup.
DevicesDir string
// ProvisionWatchersDir specifies a directory contains provision watcher files which should be imported on startup.
ProvisionWatchersDir string
// UpdateLastConnected specifies whether to update device's LastConnected
// timestamp in metadata.
UpdateLastConnected bool
Expand Down
90 changes: 90 additions & 0 deletions internal/provision/provisionwatchers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//
// Copyright (C) 2023 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0

package provision

import (
"context"
"encoding/json"
"os"
"path/filepath"
"strings"

"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/google/uuid"

"github.com/edgexfoundry/device-sdk-go/v3/internal/cache"
)

func LoadProvisionWatchers(path string, dic *di.Container) errors.EdgeX {
if path == "" {
return nil
}

absPath, err := filepath.Abs(path)
if err != nil {
return errors.NewCommonEdgeX(errors.KindServerError, "failed to create absolute path", err)
}

files, err := os.ReadDir(absPath)
if err != nil {
return errors.NewCommonEdgeX(errors.KindServerError, "failed to read directory", err)
}

if len(files) == 0 {
return nil
}

lc := container.LoggingClientFrom(dic.Get)
lc.Infof("Loading pre-defined provision watchers from %s(%d files found)", absPath, len(files))

var addProvisionWatchersReq []requests.AddProvisionWatcherRequest
for _, file := range files {
var watcher dtos.ProvisionWatcher
filename := filepath.Join(absPath, file.Name())
if !strings.HasSuffix(filename, jsonExt) {
continue
}

data, err := os.ReadFile(filename)
if err != nil {
lc.Errorf("failed to read %s: %v", filename, err)
continue
}

if err := json.Unmarshal(data, &watcher); err != nil {
lc.Errorf("failed to JSON decode %s: %v", filename, err)
continue
}

err = common.Validate(watcher)
if err != nil {
lc.Errorf("ProvisionWatcher %s validation failed: %v", watcher.Name, err)
continue
}

if _, ok := cache.ProvisionWatchers().ForName(watcher.Name); ok {
lc.Infof("ProvisionWatcher %s exists, using the existing one", watcher.Name)
} else {
lc.Infof("ProvisionWatcher %s not found in Metadata, adding it...", watcher.Name)
req := requests.NewAddProvisionWatcherRequest(watcher)
addProvisionWatchersReq = append(addProvisionWatchersReq, req)
}
}

if len(addProvisionWatchersReq) == 0 {
return nil
}

pwc := container.ProvisionWatcherClientFrom(dic.Get)
ctx := context.WithValue(context.Background(), common.CorrelationHeader, uuid.NewString()) //nolint: staticcheck
_, edgexErr := pwc.Add(ctx, addProvisionWatchersReq)
return edgexErr
}
11 changes: 9 additions & 2 deletions pkg/service/init.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// -*- Mode: Go; indent-tabs-mode: t -*-
//
// Copyright (C) 2020-2022 IOTech Ltd
// Copyright (C) 2020-2023 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0

Expand All @@ -10,11 +10,12 @@ import (
"context"
"sync"

"github.com/edgexfoundry/device-sdk-go/v3/internal/common"
"github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/startup"
"github.com/edgexfoundry/go-mod-bootstrap/v3/di"
"github.com/gorilla/mux"

"github.com/edgexfoundry/device-sdk-go/v3/internal/common"

"github.com/edgexfoundry/device-sdk-go/v3/internal/cache"
"github.com/edgexfoundry/device-sdk-go/v3/internal/provision"
"github.com/edgexfoundry/device-sdk-go/v3/pkg/models"
Expand Down Expand Up @@ -78,6 +79,12 @@ func (b *Bootstrap) BootstrapHandler(ctx context.Context, wg *sync.WaitGroup, st
return false
}

err = provision.LoadProvisionWatchers(ds.config.Device.ProvisionWatchersDir, dic)
if err != nil {
ds.LoggingClient.Errorf("Failed to create the pre-defined provision watchers: %v", err)
return false
}

ds.manager.StartAutoEvents()

// Very important that this handler is called after the NewServiceMetrics handler so
Expand Down

0 comments on commit 8025d41

Please sign in to comment.