...
 
Commits (52)
......@@ -35,3 +35,4 @@ prime/*
stage/*
snap/.snapcraft/*
go.sum
squashfs-root/
......@@ -38,15 +38,18 @@ func main() {
start := time.Now()
var useRegistry bool
var useProfile string
var useLocalSecrets bool
flag.BoolVar(&useRegistry, "registry", false, "Indicates the service should use registry.")
flag.BoolVar(&useRegistry, "r", false, "Indicates the service should use registry.")
flag.StringVar(&useProfile, "profile", "", "Specify a profile other than default.")
flag.StringVar(&useProfile, "p", "", "Specify a profile other than default.")
flag.BoolVar(&useLocalSecrets, "secrets", false, "Indicates the service should not use a secure secret store.")
flag.BoolVar(&useLocalSecrets, "s", false, "Indicates the service should not use a secure secret store.")
flag.Usage = usage.HelpCallback
flag.Parse()
params := startup.BootParams{UseRegistry: useRegistry, UseProfile: useProfile, BootTimeout: internal.BootTimeoutDefault}
params := startup.BootParams{UseRegistry: useRegistry, UseProfile: useProfile, UseLocalSecrets: useLocalSecrets, BootTimeout: internal.BootTimeoutDefault}
startup.Bootstrap(params, command.Retry, logBeforeInit)
ok := command.Init(useRegistry)
......
......@@ -39,15 +39,18 @@ func main() {
start := time.Now()
var useRegistry bool
var useProfile string
var useLocalSecrets bool
flag.BoolVar(&useRegistry, "registry", false, "Indicates the service should use registry service.")
flag.BoolVar(&useRegistry, "r", false, "Indicates the service should use registry service.")
flag.StringVar(&useProfile, "profile", "", "Specify a profile other than default.")
flag.StringVar(&useProfile, "p", "", "Specify a profile other than default.")
flag.BoolVar(&useLocalSecrets, "secrets", false, "Indicates the service should not use a secure secret store.")
flag.BoolVar(&useLocalSecrets, "s", false, "Indicates the service should not use a secure secret store.")
flag.Usage = usage.HelpCallback
flag.Parse()
params := startup.BootParams{UseRegistry: useRegistry, UseProfile: useProfile, BootTimeout: internal.BootTimeoutDefault}
params := startup.BootParams{UseRegistry: useRegistry, UseProfile: useProfile, UseLocalSecrets: useLocalSecrets, BootTimeout: internal.BootTimeoutDefault}
startup.Bootstrap(params, data.Retry, logBeforeInit)
ok := data.Init(useRegistry)
......
......@@ -54,3 +54,13 @@ Host = '*'
Port = 5563
Type = 'zero'
Topic = 'events'
[Secrets]
Host = '127.0.0.1'
Port = 8200
Path = '/v1/secret/edgex/coredata/mongodb'
Provider = 'http'
Protocol = "https"
[Secrets.Authentication]
AuthType = 'X-Vault-Token'
AuthToken = 'roots'
......@@ -8,6 +8,8 @@
package main
import (
"github.com/edgexfoundry/go-mod-core-contracts/clients"
"github.com/edgexfoundry/go-mod-core-contracts/clients/logger"
"testing"
"github.com/edgexfoundry/edgex-go/internal/core/data"
......@@ -15,6 +17,8 @@ import (
)
func TestToml(t *testing.T) {
config.LoggingClient = logger.NewClient(clients.CoreDataServiceKey, false, "./logs/edgex-core-data.log", "DEBUG")
configuration := &data.ConfigurationStruct{}
if err := config.VerifyTomlFiles(configuration); err != nil {
t.Fatalf("%v", err)
......
......@@ -38,15 +38,18 @@ func main() {
start := time.Now()
var useRegistry bool
var useProfile string
var useLocalSecrets bool
flag.BoolVar(&useRegistry, "registry", false, "Indicates the service should use registry service.")
flag.BoolVar(&useRegistry, "r", false, "Indicates the service should use registry service.")
flag.StringVar(&useProfile, "profile", "", "Specify a profile other than default.")
flag.StringVar(&useProfile, "p", "", "Specify a profile other than default.")
flag.BoolVar(&useLocalSecrets, "secrets", false, "Indicates the service should not use a secure secret store.")
flag.BoolVar(&useLocalSecrets, "s", false, "Indicates the service should not use a secure secret store.")
flag.Usage = usage.HelpCallback
flag.Parse()
params := startup.BootParams{UseRegistry: useRegistry, UseProfile: useProfile, BootTimeout: internal.BootTimeoutDefault}
params := startup.BootParams{UseRegistry: useRegistry, UseProfile: useProfile, UseLocalSecrets: useLocalSecrets, BootTimeout: internal.BootTimeoutDefault}
startup.Bootstrap(params, metadata.Retry, logBeforeInit)
ok := metadata.Init(useRegistry)
......
......@@ -31,18 +31,21 @@ import (
func main() {
start := time.Now()
var (
useRegistry bool
useProfile string
useRegistry bool
useProfile string
useLocalSecrets bool
)
flag.BoolVar(&useRegistry, "registry", false, "Indicates the service should use Registry.")
flag.BoolVar(&useRegistry, "r", false, "Indicates the service should use Registry.")
flag.StringVar(&useProfile, "profile", "", "Specify a profile other than default.")
flag.StringVar(&useProfile, "p", "", "Specify a profile other than default.")
flag.BoolVar(&useLocalSecrets, "secrets", false, "Indicates the service should not use a secure secret store.")
flag.BoolVar(&useLocalSecrets, "s", false, "Indicates the service should not use a secure secret store.")
flag.Usage = usage.HelpCallback
flag.Parse()
params := startup.BootParams{UseRegistry: useRegistry, UseProfile: useProfile, BootTimeout: internal.BootTimeoutDefault}
params := startup.BootParams{UseRegistry: useRegistry, UseProfile: useProfile, UseLocalSecrets: useLocalSecrets, BootTimeout: internal.BootTimeoutDefault}
startup.Bootstrap(params, client.Retry, logBeforeInit)
ok := client.Init(useRegistry)
......
......@@ -33,12 +33,15 @@ import (
func main() {
var useRegistry bool
var useProfile string
var useLocalSecrets bool
start := time.Now()
flag.BoolVar(&useRegistry, "registry", false, "Indicates the service should use Registry.")
flag.BoolVar(&useRegistry, "r", false, "Indicates the service should use Registry.")
flag.StringVar(&useProfile, "profile", "", "Specify a profile other than default.")
flag.StringVar(&useProfile, "p", "", "Specify a profile other than default.")
flag.BoolVar(&useLocalSecrets, "secrets", false, "Indicates the service should not use a secure secret store.")
flag.BoolVar(&useLocalSecrets, "s", false, "Indicates the service should not use a secure secret store.")
flag.Usage = usage.HelpCallback
flag.Parse()
......
......@@ -32,11 +32,14 @@ func main() {
start := time.Now()
var useRegistry bool
var useProfile string
var useLocalSecrets bool
flag.BoolVar(&useRegistry, "registry", false, "Indicates the service should use Registry.")
flag.BoolVar(&useRegistry, "r", false, "Indicates the service should use Registry.")
flag.StringVar(&useProfile, "profile", "", "Specify a profile other than default.")
flag.StringVar(&useProfile, "p", "", "Specify a profile other than default.")
flag.BoolVar(&useLocalSecrets, "secrets", false, "Indicates the service should not use a secure secret store.")
flag.BoolVar(&useLocalSecrets, "s", false, "Indicates the service should not use a secure secret store.")
flag.Usage = usage.HelpCallback
flag.Parse()
......
......@@ -42,15 +42,18 @@ func main() {
start := time.Now()
var useRegistry bool
var useProfile string
var useLocalSecrets bool
flag.BoolVar(&useRegistry, "registry", false, "Indicates the service should use Registry.")
flag.BoolVar(&useRegistry, "r", false, "Indicates the service should use Registry.")
flag.StringVar(&useProfile, "profile", "", "Specify a profile other than default.")
flag.StringVar(&useProfile, "p", "", "Specify a profile other than default.")
flag.BoolVar(&useLocalSecrets, "secrets", false, "Indicates the service should not use a secure secret store.")
flag.BoolVar(&useLocalSecrets, "s", false, "Indicates the service should not use a secure secret store.")
flag.Usage = usage.HelpCallback
flag.Parse()
params := startup.BootParams{UseRegistry: useRegistry, UseProfile: useProfile, BootTimeout: internal.BootTimeoutDefault}
params := startup.BootParams{UseRegistry: useRegistry, UseProfile: useProfile, UseLocalSecrets: useLocalSecrets, BootTimeout: internal.BootTimeoutDefault}
startup.Bootstrap(params, notifications.Retry, logBeforeInit)
ok := notifications.Init(useRegistry)
......
......@@ -26,15 +26,18 @@ func main() {
start := time.Now()
var useRegistry bool
var useProfile string
var useLocalSecrets bool
flag.BoolVar(&useRegistry, "registry", false, "Indicates the service should use Registry.")
flag.BoolVar(&useRegistry, "r", false, "Indicates the service should use Registry.")
flag.StringVar(&useProfile, "profile", "", "Specify a profile other than default.")
flag.StringVar(&useProfile, "p", "", "Specify a profile other than default.")
flag.BoolVar(&useLocalSecrets, "secrets", false, "Indicates the service should not use a secure secret store.")
flag.BoolVar(&useLocalSecrets, "s", false, "Indicates the service should not use a secure secret store.")
flag.Usage = usage.HelpCallback
flag.Parse()
params := startup.BootParams{UseRegistry: useRegistry, UseProfile: useProfile, BootTimeout: internal.BootTimeoutDefault}
params := startup.BootParams{UseRegistry: useRegistry, UseProfile: useProfile, UseLocalSecrets: useLocalSecrets, BootTimeout: internal.BootTimeoutDefault}
startup.Bootstrap(params, scheduler.Retry, logBeforeInit)
ok := scheduler.Init(useRegistry)
......
......@@ -39,15 +39,18 @@ func main() {
start := time.Now()
var useRegistry bool
var useProfile string
var useLocalSecrets bool
flag.BoolVar(&useRegistry, "registry", false, "Indicates the service should use registry service.")
flag.BoolVar(&useRegistry, "r", false, "Indicates the service should use registry service.")
flag.StringVar(&useProfile, "profile", "", "Specify a profile other than default.")
flag.StringVar(&useProfile, "p", "", "Specify a profile other than default.")
flag.BoolVar(&useLocalSecrets, "secrets", false, "Indicates the service should not use a secure secret store.")
flag.BoolVar(&useLocalSecrets, "s", false, "Indicates the service should not use a secure secret store.")
flag.Usage = usage.HelpCallback
flag.Parse()
params := startup.BootParams{UseRegistry: useRegistry, UseProfile: useProfile, BootTimeout: internal.BootTimeoutDefault}
params := startup.BootParams{UseRegistry: useRegistry, UseProfile: useProfile, UseLocalSecrets: useLocalSecrets, BootTimeout: internal.BootTimeoutDefault}
startup.Bootstrap(params, agent.Retry, logBeforeInit)
ok := agent.Init(useRegistry)
......
......@@ -12,20 +12,21 @@ To build a device service using the EdgeX C SDK you'll need the following:
* libmicrohttpd
* libcurl
* libyaml
* libcbor
You can install these on Ubuntu by running::
sudo apt install libcurl4-openssl-dev libmicrohttpd-dev libyaml-dev
sudo apt install libcurl4-openssl-dev libmicrohttpd-dev libyaml-dev libcbor-dev
===============================
Get the EdgeX Device SDK for C
===============================
The next step is to download and build the EdgeX Device SDK for C. You always want to use the release of the SDK that matches the release of EdgeX you are targeting. As of this writing the `delhi` release is the current stable release of EdgeX, so we will be using the `delhi` branch of the C SDK.
The next step is to download and build the EdgeX Device SDK for C. You always want to use the release of the SDK that matches the release of EdgeX you are targeting. As of this writing the `edinburgh` release is the current stable release of EdgeX, so we will be using the `edinburgh` branch of the C SDK.
#. First, clone the delhi branch of device-sdk-c from Github::
#. First, clone the edinburgh branch of device-sdk-c from Github::
git clone -b delhi https://github.com/edgexfoundry/device-sdk-c.git
git clone -b edinburgh https://github.com/edgexfoundry/device-sdk-c.git
cd ./device-sdk-c
#. Then, build the device-sdk-c::
......@@ -40,15 +41,12 @@ Starting a new Device Service project
For this guide we're going to use the example template provided by the C SDK as a starting point, and will modify it to generate random integer values.
#. Begin by copying the contents of src/c/examples/ into a new directory named `example-device-c`::
#. Begin by copying the template example source into a new directory named `example-device-c`::
cp -r ./src/c/examples ../example-device-c
mkdir -p ../example-device-c/res
cp ./src/c/examples/template.c ../example-device-c
cd ../example-device-c
#. You can delete the CMakeLists.txt as we don't need it anymore::
rm CMakeLists.txt
=========================
Build your Device Service
......@@ -58,25 +56,28 @@ Now you are ready to build your new device service using the C SDK you compiled
#. Tell the compiler where to find the C SDK files::
export CSDK_DIR=../device-sdk-c/build/release/_CPack_Packages/Linux/TGZ/csdk-0.7.2
export CSDK_DIR=../device-sdk-c/build/release/_CPack_Packages/Linux/TGZ/csdk-1.0.0
.. note:: The exact path to your compiled CSDK_DIR may differ, depending on the tagged version number on the SDK
#. Now you can build your device service executable::
make
gcc -I$CSDK_DIR/include -L$CSDK_DIR/lib -o device-example-c template.c -lcsdk
=============================
Customize your Device Service
=============================
Up to now you've been building the example device service provided by the C SDK. In order to change it to a device service that generates random numbers, you need to modify your `template.c` method **template_get_handler** so that the **while** block looks like this:
Up to now you've been building the example device service provided by the C SDK. In order to change it to a device service that generates random numbers, you need to modify your `template.c` method **template_get_handler** so that it reads as follows:
.. code-block:: c
:linenos:
:lineno-start: 92
:emphasize-lines: 3,8,11,14
:lineno-start: 97
:emphasize-lines: 7,12,15,18
for (uint32_t i = 0; i < nreadings; i++)
{
const edgex_nvpairs * current = requests[i].attributes;
while (current!=NULL)
{
if (strcmp (current->name, "type") ==0 )
......@@ -92,6 +93,8 @@ Up to now you've been building the example device service provided by the C SDK.
}
current = current->next;
}
}
return true;
============================
......@@ -100,9 +103,9 @@ Creating your Device Profile
A Device Profile is a YAML file that describes a class of device to EdgeX. General characteristics about the type of device, the data these devices provide, and how to command the device is all provided in a Device Profile. Device Services use the Device Profile to understand what data is being collected from the Device (in some cases providing information used by the Device Service to know how to communicate with the device and get the desired sensor readings). A Device Profile is needed to describe the data that will be collected from the simple random number generating Device Service.
#. Explore the files in the src/c/examples/res folder. Take note of the example Device Profile YAML file that is already there (ExampleProfile.yml). You can explore the contents of this file to see how devices are represented by YAML. In particular, note how fields or properties of a sensor are represented by “deviceResources”. Commands to be issued to the device are represented by “commands”.
#. Explore the files in the src/c/examples/res folder. Take note of the example Device Profile YAML file that is already there (TemplateProfile.yaml). You can explore the contents of this file to see how devices are represented by YAML. In particular, note how fields or properties of a sensor are represented by “deviceResources”. Commands to be issued to the device are represented by “coreCommands”.
#. Download this :download:`random-generator-device.yaml <random-generator-device.yaml>` into the ./res folder.
#. Download this :download:`random-generator-device.yaml <random-generator-device.yaml>` into the ./res folder.
You can open random-generator-device.yaml in a text editor. In this Device Profile, you are suggesting that the device you are describing to EdgeX has a single property (or deviceResource) which EdgeX should know about - in this case, the property is the “randomnumber”. Note how the deviceResource is typed.
......@@ -116,7 +119,7 @@ Configuring your Device Service
You will now update the configuration for your new Device Service – changing the port it operates on (so as not to conflict with other Device Services), altering the scheduled times of when the data is collected from the Device Service (every 10 seconds), and setting up the initial provisioning of the random number generating device when the service starts.
* Downlod this :download:`configuration.toml <configuration.toml>` to the ./res folder (this will overwrite an existing file – that’s ok).
* Download this :download:`configuration.toml <configuration.toml>` to the ./res folder.
If you will be running EdgeX inside of Docker containers (which you will at the bottom of this guide) you need to tell your new Device Service to listen on the Docker host IP address (172.17.0.1) instead of **localhost**. To do that, modify the configuration.toml file so that the top section looks like this:
......@@ -137,7 +140,7 @@ Now you have your new Device Service, modified to return a random number, a Devi
#. Rebuild your Device Service to reflect the changes that you have made::
make
gcc -I$CSDK_DIR/include -L$CSDK_DIR/lib -o device-example-c template.c -lcsdk
=======================
......@@ -162,7 +165,12 @@ Allow your newly created Device Service, which was formed out of the Device Serv
docker logs -f edgex-core-data
Which would print an Event record every time your Device Service is called. Note that the value of the "randomnumber" reading is an integer between 0 and 100::
Which would print an Event record every time your Device Service is called.
#. You can manually generate an event using curl to query the device service directly::
curl 0:49992/api/v1/device/name/RandNum-Device01/Random
Note that the value of the "randomnumber" reading is an integer between 0 and 100::
INFO: 2019/02/05 20:27:05 Posting Event: {"id":"","pushed":0,"device":"RandNum-Device01","created":0,"modified":0,"origin":1549398425000,"schedule":null,"event":null,"readings":[{"id":"","pushed":0,"created":0,"origin":0,"modified":0,"device":null,"name":"randomnumber","value":"63"}]}
INFO: 2019/02/05 20:27:05 Putting event on message queue
{"device":"RandNum-Device01","origin":1559317102457,"readings":[{"name":"randomnumber","value":"63"}]}
......@@ -52,25 +52,17 @@ EnableRemote = false
File = "./device-simple.log"
Level = "DEBUG"
# Pre-define Schedule Configuration
[[Schedules]]
Name = "10sec-schedule"
Frequency = "PT10S"
[[ScheduleEvents]]
Name = "readRandom"
Schedule = "10sec-schedule"
[ScheduleEvents.Addressable]
HTTPMethod = "GET"
Path = "/api/v1/device/name/RandNum-Device01/Random"
# Pre-define Devices
[[DeviceList]]
Name = "RandNum-Device01"
Profile = "RandNum-Device"
Description = "Random Number Generator Device"
Labels = [ "random", "test" ]
[DeviceList.Addressable]
Address = "random"
Port = 300
Protocol = "OTHER"
\ No newline at end of file
[DeviceList.Protocols]
[DeviceList.Protocols.Other]
Address = "random"
Port = 300
[[DeviceList.AutoEvents]]
Resource = "Random"
OnChange = false
Frequency = "10s"
......@@ -14,18 +14,18 @@ deviceResources:
{ type: "random" }
properties:
value:
{ type: "INT32", readWrite: "R", defaultValue: "0.00", minimum: "0.00", maximum: "100.00" }
{ type: "Int32", readWrite: "R", minimum: "0.00", maximum: "100.00" }
units:
{ type: "String", readWrite: "R", defaultValue: "" }
resources:
deviceCommands:
-
name: "Random"
get:
-
{ operation: "get", object: "randomnumber", property: "value", parameter: "Random" }
commands:
coreCommands:
-
name: "Random"
get:
......@@ -38,4 +38,4 @@ commands:
-
code: "503"
description: "service unavailable"
expectedValues: []
\ No newline at end of file
expectedValues: []
......@@ -8,13 +8,14 @@ require (
github.com/edgexfoundry/go-mod-core-contracts v0.1.0
github.com/edgexfoundry/go-mod-messaging v0.1.0
github.com/edgexfoundry/go-mod-registry v0.1.0
github.com/edgexfoundry/go-mod-secrets v0.0.0-20190607152139-03819e341fe2
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
github.com/go-kit/kit v0.8.0
github.com/go-stack/stack v1.8.0 // indirect
github.com/gomodule/redigo v2.0.0+incompatible
github.com/google/uuid v1.1.0
github.com/gorilla/context v1.1.1
github.com/gorilla/mux v1.7.0
github.com/gorilla/mux v1.7.1
github.com/hashicorp/consul v1.4.2
github.com/imdario/mergo v0.3.6
github.com/magiconair/properties v1.8.0
......
......@@ -44,17 +44,17 @@ var registryClient registry.Client
var registryErrors chan error //A channel for "config wait errors" sourced from Registry
var registryUpdates chan interface{} //A channel for "config updates" sourced from Registry
func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGroup, ch chan error) {
func Retry(params startup.BootParams, wait *sync.WaitGroup, ch chan error) {
now := time.Now()
until := now.Add(time.Millisecond * time.Duration(timeout))
until := now.Add(time.Millisecond * time.Duration(params.BootTimeout))
for time.Now().Before(until) {
var err error
//When looping, only handle configuration if it hasn't already been set.
if Configuration == nil {
Configuration, err = initializeConfiguration(useRegistry, useProfile)
Configuration, err = initializeConfiguration(params.UseRegistry, params.UseProfile)
if err != nil {
ch <- err
if !useRegistry {
if !params.UseRegistry {
//Error occurred when attempting to read from local filesystem. Fail fast.
close(ch)
wait.Done()
......@@ -62,7 +62,7 @@ func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGrou
}
} else {
//Check against boot timeout default
if Configuration.Service.BootTimeout != timeout {
if Configuration.Service.BootTimeout != params.BootTimeout {
until = now.Add(time.Millisecond * time.Duration(Configuration.Service.BootTimeout))
}
// Setup Logging
......@@ -70,7 +70,7 @@ func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGrou
LoggingClient = logger.NewClient(clients.CoreCommandServiceKey, Configuration.Logging.EnableRemote, logTarget, Configuration.Writable.LogLevel)
//Initialize service clients
initializeClients(useRegistry)
initializeClients(params.UseRegistry)
}
}
......
......@@ -13,7 +13,11 @@
*******************************************************************************/
package data
import "github.com/edgexfoundry/edgex-go/internal/pkg/config"
import (
"github.com/edgexfoundry/go-mod-secrets/pkg/providers/vault"
"github.com/edgexfoundry/edgex-go/internal/pkg/config"
)
type ConfigurationStruct struct {
Writable WritableInfo
......@@ -23,6 +27,7 @@ type ConfigurationStruct struct {
Logging config.LoggingInfo
Registry config.RegistryInfo
Service config.ServiceInfo
Secrets vault.SecretConfig
}
type WritableInfo struct {
......
......@@ -23,6 +23,8 @@ import (
"syscall"
"time"
"github.com/edgexfoundry/go-mod-secrets/pkg/providers/vault"
"github.com/edgexfoundry/go-mod-core-contracts/clients"
"github.com/edgexfoundry/go-mod-core-contracts/clients/logger"
"github.com/edgexfoundry/go-mod-core-contracts/clients/metadata"
......@@ -31,6 +33,7 @@ import (
msgTypes "github.com/edgexfoundry/go-mod-messaging/pkg/types"
registryTypes "github.com/edgexfoundry/go-mod-registry/pkg/types"
"github.com/edgexfoundry/go-mod-registry/registry"
"github.com/edgexfoundry/go-mod-secrets/pkg"
"github.com/edgexfoundry/edgex-go/internal"
"github.com/edgexfoundry/edgex-go/internal/core/data/interfaces"
......@@ -47,6 +50,7 @@ var Configuration *ConfigurationStruct
var dbClient interfaces.DBClient
var LoggingClient logger.LoggingClient
var registryClient registry.Client
var secretsClient pkg.SecretClient
// TODO: Refactor names in separate PR: See comments on PR #1133
var chEvents chan interface{} // A channel for "domain events" sourced from event operations
......@@ -57,16 +61,16 @@ var msgClient messaging.MessageClient
var mdc metadata.DeviceClient
var msc metadata.DeviceServiceClient
func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGroup, ch chan error) {
until := time.Now().Add(time.Millisecond * time.Duration(timeout))
func Retry(params startup.BootParams, wait *sync.WaitGroup, ch chan error) {
until := time.Now().Add(time.Millisecond * time.Duration(params.BootTimeout))
for time.Now().Before(until) {
var err error
// When looping, only handle configuration if it hasn't already been set.
if Configuration == nil {
Configuration, err = initializeConfiguration(useRegistry, useProfile)
Configuration, err = initializeConfiguration(params)
if err != nil {
ch <- err
if !useRegistry {
if !params.UseRegistry {
// Error occurred when attempting to read from local filesystem. Fail fast.
close(ch)
wait.Done()
......@@ -78,12 +82,26 @@ func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGrou
LoggingClient = logger.NewClient(clients.CoreDataServiceKey, Configuration.Logging.EnableRemote, logTarget, Configuration.Writable.LogLevel)
// Initialize service clients
initializeClients(useRegistry)
initializeClients(params.UseRegistry)
}
}
// Only attempt to connect to database if configuration has been populated
if Configuration != nil {
// Attempt to connect to secrets service. Fall back to local config on failure.
if !params.UseLocalSecrets {
err = connectAndPollSecrets()
// Error occurred trying to read remote secrets. Fail fast.
if err != nil {
ch <- err
ch <- fmt.Errorf("could not fetch remote secrets, shutting down")
close(ch)
wait.Done()
return
}
}
// Only attempt to connect to database if configuration has been populated
err := connectToDatabase()
if err != nil {
ch <- err
......@@ -172,15 +190,15 @@ func newDBClient(dbType string) (interfaces.DBClient, error) {
}
}
func initializeConfiguration(useRegistry bool, useProfile string) (*ConfigurationStruct, error) {
func initializeConfiguration(params startup.BootParams) (*ConfigurationStruct, error) {
// We currently have to load configuration from filesystem first in order to obtain Registry Host/Port
configuration := &ConfigurationStruct{}
err := config.LoadFromFile(useProfile, configuration)
err := config.LoadFromFile(params.UseProfile, configuration)
if err != nil {
return nil, err
}
if useRegistry {
if params.UseRegistry {
err = connectToRegistry(configuration)
if err != nil {
return nil, err
......@@ -207,6 +225,36 @@ func initializeConfiguration(useRegistry bool, useProfile string) (*Configuratio
return configuration, nil
}
func connectAndPollSecrets() error {
var err error
secretsClient, err = vault.NewSecretClient(Configuration.Secrets)
if err != nil {
return err
}
username, err := secretsClient.GetSecret("User")
if err != nil {
return err
}
password, err := secretsClient.GetSecret("Passwd")
if err != nil {
return err
}
Configuration.Databases["Primary"] = config.DatabaseInfo{
Type: Configuration.Databases["Primary"].Type,
Timeout: Configuration.Databases["Primary"].Timeout,
Host: Configuration.Databases["Primary"].Host,
Port: Configuration.Databases["Primary"].Port,
Username: username,
Password: password,
Name: Configuration.Databases["Primary"].Name,
}
return nil
}
func connectToRegistry(conf *ConfigurationStruct) error {
var err error
registryConfig := registryTypes.Config{
......
......@@ -49,16 +49,16 @@ var nc notifications.NotificationsClient
var registryErrors chan error //A channel for "config wait errors" sourced from Registry
var registryUpdates chan interface{} //A channel for "config updates" sourced from Registry.
func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGroup, ch chan error) {
until := time.Now().Add(time.Millisecond * time.Duration(timeout))
func Retry(params startup.BootParams, wait *sync.WaitGroup, ch chan error) {
until := time.Now().Add(time.Millisecond * time.Duration(params.BootTimeout))
for time.Now().Before(until) {
var err error
//When looping, only handle configuration if it hasn't already been set.
if Configuration == nil {
Configuration, err = initializeConfiguration(useRegistry, useProfile)
Configuration, err = initializeConfiguration(params.UseRegistry, params.UseProfile)
if err != nil {
ch <- err
if !useRegistry {
if !params.UseRegistry {
//Error occurred when attempting to read from local filesystem. Fail fast.
close(ch)
wait.Done()
......@@ -66,7 +66,7 @@ func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGrou
}
} else {
// Initialize notificationsClient based on configuration
initializeClients(useRegistry)
initializeClients(params.UseRegistry)
// Setup Logging
logTarget := setLoggingTarget()
LoggingClient = logger.NewClient(clients.CoreMetaDataServiceKey, Configuration.Logging.EnableRemote, logTarget, Configuration.Writable.LogLevel)
......
......@@ -362,17 +362,19 @@ func deleteDeviceProfile(dp models.DeviceProfile, w http.ResponseWriter) error {
http.Error(w, err.Error(), http.StatusConflict)
return err
}
// Delete the profile
if err := dbClient.DeleteDeviceProfileById(dp.Id); err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return err
}
for _, command := range dp.CoreCommands {
if err := dbClient.DeleteCommandById(command.Id); err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return err
}
}
// Delete the profile
if err := dbClient.DeleteDeviceProfileById(dp.Id); err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return err
}
return nil
}
......
......@@ -42,16 +42,16 @@ var registryClient registry.Client
var registryErrors chan error //A channel for "config wait errors" sourced from Registry
var registryUpdates chan interface{} //A channel for "config updates" sourced from Registry
func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGroup, ch chan error) {
until := time.Now().Add(time.Millisecond * time.Duration(timeout))
func Retry(params startup.BootParams, wait *sync.WaitGroup, ch chan error) {
until := time.Now().Add(time.Millisecond * time.Duration(params.BootTimeout))
for time.Now().Before(until) {
var err error
//When looping, only handle configuration if it hasn't already been set.
if Configuration == nil {
Configuration, err = initializeConfiguration(useRegistry, useProfile)
Configuration, err = initializeConfiguration(params.UseRegistry, params.UseProfile)
if err != nil {
ch <- err
if !useRegistry {
if !params.UseRegistry {
//Error occurred when attempting to read from local filesystem. Fail fast.
close(ch)
wait.Done()
......@@ -63,7 +63,7 @@ func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGrou
LoggingClient = logger.NewClient(clients.ExportClientServiceKey, Configuration.Logging.EnableRemote, logTarget, Configuration.Writable.LogLevel)
//Initialize service clients
initializeClients(useRegistry)
initializeClients(params.UseRegistry)
}
}
......
......@@ -42,16 +42,16 @@ var messageErrors chan error
var messageEnvelopes chan msgTypes.MessageEnvelope
var processStop chan bool
func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGroup, ch chan error) {
until := time.Now().Add(time.Millisecond * time.Duration(timeout))
func Retry(params startup.BootParams, wait *sync.WaitGroup, ch chan error) {
until := time.Now().Add(time.Millisecond * time.Duration(params.BootTimeout))
for time.Now().Before(until) {
var err error
//When looping, only handle configuration if it hasn't already been set.
if Configuration == nil {
Configuration, err = initializeConfiguration(useRegistry, useProfile)
Configuration, err = initializeConfiguration(params.UseRegistry, params.UseProfile)
if err != nil {
ch <- err
if !useRegistry {
if !params.UseRegistry {
//Error occurred when attempting to read from local filesystem. Fail fast.
close(ch)
wait.Done()
......@@ -63,7 +63,7 @@ func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGrou
LoggingClient = logger.NewClient(clients.ExportDistroServiceKey, Configuration.Logging.EnableRemote, logTarget, Configuration.Writable.LogLevel)
//Initialize service clients
initializeClients(useRegistry)
initializeClients(params.UseRegistry)
}
} else {
// once config is initialized, stop looping
......
......@@ -18,7 +18,6 @@ import (
"strconv"
"github.com/edgexfoundry/edgex-go/internal/pkg/db"
dataBase "github.com/edgexfoundry/edgex-go/internal/pkg/db"
contract "github.com/edgexfoundry/go-mod-core-contracts/models"
"github.com/gomodule/redigo/redis"
"github.com/google/uuid"
......@@ -256,15 +255,7 @@ func (c *Client) GetAllDevices() ([]contract.Device, error) {
}
func (c *Client) GetDevicesByProfileId(id string) ([]contract.Device, error) {
d, err := c.getDevicesByValue(db.Device + ":profile:" + id)
// XXX This is here only because test/db_metadata.go is inconsistent when testing for _not found_. It
// should always be checking for database.ErrNotFound but too often it is checking for nil
if len(d) == 0 {
err = dataBase.ErrNotFound
}
return d, err
return c.getDevicesByValue(db.Device + ":profile:" + id)
}
func (c *Client) GetDeviceById(id string) (contract.Device, error) {
......@@ -286,15 +277,7 @@ func (c *Client) GetDeviceByName(n string) (contract.Device, error) {
}
func (c *Client) GetDevicesByServiceId(id string) ([]contract.Device, error) {
d, err := c.getDevicesByValue(db.Device + ":service:" + id)
// XXX This is here only because test/db_metadata.go is inconsistent when testing for _not found_. It
// should always be checking for database.ErrNotFound but too often it is checking for nil
if len(d) == 0 {
err = dataBase.ErrNotFound
}
return d, err
return c.getDevicesByValue(db.Device + ":service:" + id)
}
func (c *Client) GetDevicesWithLabel(l string) ([]contract.Device, error) {
......@@ -439,15 +422,7 @@ func (c *Client) GetDeviceProfileByName(n string) (contract.DeviceProfile, error
}
func (c *Client) GetDeviceProfilesByCommandId(id string) ([]contract.DeviceProfile, error) {
dp, err := c.getDeviceProfilesByValues(db.DeviceProfile + ":command:" + id)
// XXX This is here only because test/db_metadata.go is inconsistent when testing for _not found_. It
// should always be checking for database.ErrNotFound but too often it is checking for nil
if len(dp) == 0 {
err = dataBase.ErrNotFound
}
return dp, err
return c.getDeviceProfilesByValues(db.DeviceProfile + ":command:" + id)
}
// Get device profiles with the passed query
......@@ -557,7 +532,7 @@ func deleteDeviceProfile(conn redis.Conn, id string) error {
}
dp := contract.DeviceProfile{}
_ = unmarshalObject(object, &dp)
_ = unmarshalDeviceProfile(object, &dp)
_ = conn.Send("MULTI")
_ = conn.Send("DEL", id)
......@@ -798,13 +773,6 @@ func (c *Client) GetDeviceServicesByAddressableId(id string) ([]contract.DeviceS
return []contract.DeviceService{}, err
}
// XXX This should really return an ErrNotFound. It's not to be consistent with existing code
// assumptions
//
// if len(objects) == 0 {
// return []contract.DeviceService{}, dataBase.ErrNotFound
// }
d := make([]contract.DeviceService, len(objects))
for i, object := range objects {
err = unmarshalDeviceService(object, &d[i])
......@@ -988,27 +956,11 @@ func (c *Client) GetProvisionWatchersByIdentifier(k string, v string) (pw []cont
}
func (c *Client) GetProvisionWatchersByServiceId(id string) ([]contract.ProvisionWatcher, error) {
pw, err := c.getProvisionWatchersByValue(db.ProvisionWatcher + ":service:" + id)
// XXX This is here only because test/db_metadata.go is inconsistent when testing for _not found_. It
// should always be checking for database.ErrNotFound but too often it is checking for nil
if len(pw) == 0 {
err = dataBase.ErrNotFound
}
return pw, err
return c.getProvisionWatchersByValue(db.ProvisionWatcher + ":service:" + id)
}
func (c *Client) GetProvisionWatchersByProfileId(id string) ([]contract.ProvisionWatcher, error) {
pw, err := c.getProvisionWatchersByValue(db.ProvisionWatcher + ":profile:" + id)
// XXX This is here only because test/db_metadata.go is inconsistent when testing for _not found_. It
// should always be checking for database.ErrNotFound but too often it is checking for nil
if len(pw) == 0 {
err = dataBase.ErrNotFound
}
return pw, err
return c.getProvisionWatchersByValue(db.ProvisionWatcher + ":profile:" + id)
}
func (c *Client) GetProvisionWatcherById(id string) (contract.ProvisionWatcher, error) {
......
......@@ -877,7 +877,7 @@ func testDBDeviceProfile(t *testing.T, db interfaces.DBClient) {
}
deviceProfiles, err = db.GetDeviceProfilesByCommandId(uuid.New().String())
if err != dataBase.ErrNotFound {
if (err != nil && err != dataBase.ErrNotFound) || len(deviceProfiles) != 0 {
t.Fatalf("Error getting deviceProfiles %v", err)
}
if len(deviceProfiles) != 0 {
......@@ -972,7 +972,7 @@ func testDBDevice(t *testing.T, db interfaces.DBClient) {
}
devices, err = db.GetDevicesByProfileId(uuid.New().String())
if err != dataBase.ErrNotFound {
if (err != nil && err != dataBase.ErrNotFound) || len(devices) != 0 {
t.Fatalf("Error getting devices %v", err)
}
if len(devices) != 0 {
......@@ -988,7 +988,7 @@ func testDBDevice(t *testing.T, db interfaces.DBClient) {
}
devices, err = db.GetDevicesByServiceId(uuid.New().String())
if err != dataBase.ErrNotFound {
if (err != nil && err != dataBase.ErrNotFound) || len(devices) != 0 {
t.Fatalf("Error getting devices %v", err)
}
if len(devices) != 0 {
......@@ -1095,7 +1095,7 @@ func testDBProvisionWatcher(t *testing.T, db interfaces.DBClient) {
}
provisionWatchers, err = db.GetProvisionWatchersByServiceId(uuid.New().String())
if err != dataBase.ErrNotFound {
if (err != nil && err != dataBase.ErrNotFound) || len(provisionWatchers) != 0 {
t.Fatalf("Error getting provisionWatchers %v", err)
}
if len(provisionWatchers) != 0 {
......@@ -1111,7 +1111,7 @@ func testDBProvisionWatcher(t *testing.T, db interfaces.DBClient) {
}
provisionWatchers, err = db.GetProvisionWatchersByProfileId(uuid.New().String())
if err != dataBase.ErrNotFound {
if (err != nil && err != dataBase.ErrNotFound) || len(provisionWatchers) != 0 {
t.Fatalf("Error getting provisionWatchers %v", err)
}
if len(provisionWatchers) != 0 {
......
......@@ -15,21 +15,22 @@ package startup
import "sync"
type RetryFunc func(UseRegistry bool, useProfile string, timeout int, wait *sync.WaitGroup, ch chan error)
type RetryFunc func(params BootParams, wait *sync.WaitGroup, ch chan error)
type LogFunc func(err error)
type BootParams struct {
UseRegistry bool
UseProfile string
BootTimeout int
UseRegistry bool
UseProfile string
UseLocalSecrets bool
BootTimeout int
}
func Bootstrap(params BootParams, retry RetryFunc, log LogFunc) {
deps := make(chan error, 2)
wg := sync.WaitGroup{}
wg.Add(1)
go retry(params.UseRegistry, params.UseProfile, params.BootTimeout, &wg, deps)
go retry(params, &wg, deps)
go func(ch chan error) {
for {
select {
......
......@@ -39,7 +39,7 @@ func clearVars() {
func testPass(t *testing.T) {
clearVars()
p := BootParams{true, "", timeoutPass}
p := BootParams{true, "", false, timeoutPass}
Bootstrap(p, mockRetry, mockLog)
if !checkInit {
t.Error("checkInit should be true.")
......@@ -51,7 +51,7 @@ func testPass(t *testing.T) {
func testFail(t *testing.T) {
clearVars()
p := BootParams{true, "", timeoutFail}
p := BootParams{true, "", false, timeoutFail}
Bootstrap(p, mockRetry, mockLog)
time.Sleep(time.Millisecond * time.Duration(25)) //goroutine timing
if checkInit {
......@@ -66,10 +66,10 @@ func testFail(t *testing.T) {
//Different test cases are toggled according to the timeout value
//SUCCESS = short duration 100ms
//FAIL = long duration 1000ms
func mockRetry(UseRegistry bool, useProfile string, timeout int, wait *sync.WaitGroup, ch chan error) {
until := time.Now().Add(time.Millisecond * time.Duration(timeout))
func mockRetry(params BootParams, wait *sync.WaitGroup, ch chan error) {
until := time.Now().Add(time.Millisecond * time.Duration(params.BootTimeout))
for time.Now().Before(until) {
if timeout == timeoutFail {
if params.BootTimeout == timeoutFail {
err := fmt.Errorf("Timeout Fail caught")
ch <- err
close(ch)
......
......@@ -10,6 +10,7 @@ package logging
import (
"errors"
"fmt"
"github.com/edgexfoundry/edgex-go/internal/pkg/startup"
"os"
"os/signal"
"sync"
......@@ -33,17 +34,17 @@ var registryClient registry.Client
var registryErrors chan error //A channel for "config wait errors" sourced from Registry
var registryUpdates chan interface{} //A channel for "config updates" sourced from Registry
func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGroup, ch chan error) {
func Retry(params startup.BootParams, wait *sync.WaitGroup, ch chan error) {
LoggingClient = newPrivateLogger()
until := time.Now().Add(time.Millisecond * time.Duration(timeout))
until := time.Now().Add(time.Millisecond * time.Duration(params.BootTimeout))
for time.Now().Before(until) {
var err error
//When looping, only handle configuration if it hasn't already been set.
if Configuration == nil {
Configuration, err = initializeConfiguration(useRegistry, useProfile)
Configuration, err = initializeConfiguration(params.UseRegistry, params.UseProfile)
if err != nil {
ch <- err
if !useRegistry {
if !params.UseRegistry {
//Error occurred when attempting to read from local filesystem. Fail fast.
close(ch)
wait.Done()
......
......@@ -20,6 +20,7 @@ package notifications
import (
"errors"
"fmt"
"github.com/edgexfoundry/edgex-go/internal/pkg/startup"
"os"
"os/signal"
"sync"
......@@ -47,16 +48,16 @@ var registryClient registry.Client
var registryErrors chan error //A channel for "config wait errors" sourced from Registry
var registerUpdates chan interface{} //A channel for "config updates" sourced from Registry
func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGroup, ch chan error) {
until := time.Now().Add(time.Millisecond * time.Duration(timeout))
func Retry(params startup.BootParams, wait *sync.WaitGroup, ch chan error) {
until := time.Now().Add(time.Millisecond * time.Duration(params.BootTimeout))
for time.Now().Before(until) {
var err error
//When looping, only handle configuration if it hasn't already been set.
if Configuration == nil {
Configuration, err = initializeConfiguration(useRegistry, useProfile)
Configuration, err = initializeConfiguration(params.UseRegistry, params.UseProfile)
if err != nil {
ch <- err
if !useRegistry {
if !params.UseRegistry {
//Error occurred when attempting to read from local filesystem. Fail fast.
close(ch)
wait.Done()
......
......@@ -17,6 +17,7 @@ package scheduler
import (
"errors"
"fmt"
"github.com/edgexfoundry/edgex-go/internal/pkg/startup"
"os"
"os/signal"
"sync"
......@@ -47,17 +48,17 @@ var registryUpdates chan interface{} //A channel for "config updates" sourced fr
var ticker *time.Ticker
func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGroup, ch chan error) {
func Retry(params startup.BootParams, wait *sync.WaitGroup, ch chan error) {
now := time.Now()
until := now.Add(time.Millisecond * time.Duration(timeout))
until := now.Add(time.Millisecond * time.Duration(params.BootTimeout))
for time.Now().Before(until) {
var err error
//When looping, only handle configuration if it hasn't already been set.
if Configuration == nil {
Configuration, err = initializeConfiguration(useRegistry, useProfile)
Configuration, err = initializeConfiguration(params.UseRegistry, params.UseProfile)
if err != nil {
ch <- err
if !useRegistry {
if !params.UseRegistry {
//Error occurred when attempting to read from local filesystem. Fail fast.
close(ch)
wait.Done()
......@@ -65,7 +66,7 @@ func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGrou
}
} else {
//Check against boot timeout default
if Configuration.Service.BootTimeout != timeout {
if Configuration.Service.BootTimeout != params.BootTimeout {
until = now.Add(time.Millisecond * time.Duration(Configuration.Service.BootTimeout))
}
// Setup Logging
......
......@@ -56,8 +56,8 @@ var services = map[string]string{
clients.SupportSchedulerServiceKey: "Scheduler",
}
func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGroup, ch chan error) {
until := time.Now().Add(time.Millisecond * time.Duration(timeout))
func Retry(params startup.BootParams, wait *sync.WaitGroup, ch chan error) {
until := time.Now().Add(time.Millisecond * time.Duration(params.BootTimeout))
for time.Now().Before(until) {
var err error
// When looping, only handle configuration if it hasn't already been set.
......@@ -65,10 +65,10 @@ func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGrou
// Read in those setting, too, which specifies details for those services
// (Those setting were _previously_ to be found in a now-defunct TOML manifest file).
if Configuration == nil {
Configuration, err = initializeConfiguration(useRegistry, useProfile)
Configuration, err = initializeConfiguration(params.UseRegistry, params.UseProfile)
if err != nil {
ch <- err
if !useRegistry {
if !params.UseRegistry {
//Error occurred when attempting to read from local filesystem. Fail fast.
close(ch)
wait.Done()
......@@ -80,7 +80,7 @@ func Retry(useRegistry bool, useProfile string, timeout int, wait *sync.WaitGrou
LoggingClient = logger.NewClient(clients.SystemManagementAgentServiceKey, Configuration.Logging.EnableRemote, logTarget, Configuration.Writable.LogLevel)
//Initialize service clients
initializeClients(useRegistry)
initializeClients(params.UseRegistry)
}
}
......
FROM ubuntu:16.04
# allow specifying the architecture from the build arg command line
ARG ARCH
# this is essentially the same as the upstream dockerfile
# here: https://github.com/snapcore/s