Skip to content

Commit 1107a03

Browse files
Implement bridge-device functionality for onboarding non-OCF compliant devices
Implement a bridge-device service facilitating access to bridged devices, incorporating extensions beyond the OCF specification for local access. Notable distinctions include the use of a single opened UDP (potentially DTLS in the future) for Plgd bridged devices, contrasting with OCF bridged devices that utilize individual UDP/DTLS/TCP/TCP-TLS for both IPv4 and IPv6. * Key Points: - Access devices by including the query parameter `di=<deviceID>` in the request. - The discovery resource (`/oic/res`) without the `di` parameter provides links for all devices. Each link contains a deviceID, specifying the required value for the `di` query parameter. - Accessing another device without the `di` parameter results in failure. * Features: - Enables setting a Certificate Authority (CA) for cloud connection for each bridged device. - Implements a cloud connector for each bridged device with certificate validation for cloud connections. - Supports custom loggers. - Provides an API for configuring update/get/observe handlers for resources. - Supports both IPv4 and IPv6. --------- Co-authored-by: Daniel Adam <daniel.adam1922@protonmail.com>
1 parent e22e783 commit 1107a03

File tree

150 files changed

+9826
-648
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

150 files changed

+9826
-648
lines changed

.codecov.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
ignore:
22
- "client/app/app.go"
3-
- "cmd/ocfclient/ocfclient.go"
3+
- "cmd/bridge-device/*.go"
4+
- "cmd/ocfclient/*.go"
45
- "**/main.go"
6+
- "**/test/**/*.go"
57
- "**/*.pb.go"
68
- "**/*.pb.gw.go"
79
- "**/*_test.go"

.github/workflows/build-publish-cfg.yaml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ on:
1212
description: Name of the container
1313
type: string
1414
required: true
15-
directory:
16-
description: Directory of service
17-
type: string
18-
required: true
1915
file:
2016
description: Dockerfile to build
2117
type: string
@@ -102,7 +98,6 @@ jobs:
10298
platforms: linux/amd64,linux/arm64
10399
builder: ${{ steps.buildx.outputs.name }}
104100
build-args: |
105-
DIRECTORY=${{ inputs.directory }}
106101
NAME=${{ inputs.name }}
107102
COMMIT_DATE=${{ steps.build-args.outputs.commit_date }}
108103
SHORT_COMMIT=${{ steps.build-args.outputs.short_commit }}
@@ -123,7 +118,6 @@ jobs:
123118
platforms: linux/amd64,linux/arm64
124119
builder: ${{ steps.buildx.outputs.name }}
125120
build-args: |
126-
DIRECTORY=${{ inputs.directory }}
127121
NAME=${{ inputs.name }}
128122
COMMIT_DATE=${{ steps.build-args.outputs.commit_date }}
129123
SHORT_COMMIT=${{ steps.build-args.outputs.short_commit }}

.github/workflows/build-publish.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ jobs:
2727
matrix:
2828
include:
2929
- name: test-cloud-server
30-
directory: test/cloud-server
3130
file: test/cloud-server/Dockerfile
31+
- name: bridge-device
32+
file: cmd/bridge-device/Dockerfile
3233
uses: ./.github/workflows/build-publish-cfg.yaml
3334
with:
3435
name: ${{ matrix.name }}
35-
directory: ${{ matrix.directory }}
3636
file: ${{ matrix.file }}
3737

.github/workflows/test-with-cfg.yml

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ on:
1111
name:
1212
type: string
1313
required: true
14+
bridgetest-enabled:
15+
type: boolean
16+
required: false
17+
default: false
1418
coverage:
1519
type: boolean
1620
required: false
@@ -22,10 +26,10 @@ on:
2226
type: string
2327
required: false
2428
default: ""
25-
unittest-args:
26-
type: string
29+
unittest-enabled:
30+
type: boolean
2731
required: false
28-
default: ""
32+
default: false
2933
jobs:
3034
test:
3135
runs-on: ubuntu-latest
@@ -51,11 +55,16 @@ jobs:
5155
if: ${{ failure() }}
5256
run: docker logs -t devsim-net-host && cat .tmp/devsim-net-host/0.log
5357

54-
# Run after integration tests, because they first clean-up the output directory
58+
# Run after integration tests, because they always clean-up the output directory
59+
- name: Run bridge tests
60+
if: ${{ inputs.bridgetest-enabled }}
61+
run: |
62+
make test-bridge
63+
5564
- name: Run unit tests
56-
if: ${{ inputs.coverage }}
65+
if: ${{ inputs.unittest-enabled }}
5766
run: |
58-
make unit-test ${{ inputs.unittest-args }}
67+
make unit-test
5968
6069
- name: Get output file name
6170
if: ${{ inputs.coverage }}

.github/workflows/tests.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ jobs:
2121
matrix:
2222
include:
2323
- name: cloud-server-debug
24+
bridgetest-enabled: true
2425
coverage: true
2526
tag: ghcr.io/iotivity/iotivity-lite/cloud-server-debug:vnext
27+
unittest-enabled: true
2628
- name: cloud-server-debug-sha384
2729
test-args: CERT_TOOL_SIGN_ALG=ECDSA-SHA384 CERT_TOOL_ELLIPTIC_CURVE=P384
2830
coverage: true
@@ -37,9 +39,11 @@ jobs:
3739
uses: ./.github/workflows/test-with-cfg.yml
3840
with:
3941
name: ${{ matrix.name }}
42+
bridgetest-enabled: ${{ matrix.bridgetest-enabled || false }}
4043
coverage: ${{ matrix.coverage }}
4144
tag: ${{ matrix.tag }}
4245
test-args: ${{ matrix.test-args }}
46+
unittest-enabled: ${{ matrix.unittest-enabled || false }}
4347

4448
analysis:
4549
name: SonarCloud and codecov analysis

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@ vendor/
1212
.vscode/
1313
.tmp/
1414
debug
15-
15+
cmd/bridge-device/bridge-device
16+
cmd/ocfclient/ocfclient
17+
test/bridge-device/bridge-device

.vscode/settings.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"-race",
44
"-v",
55
"-cover",
6+
"-coverpkg=./...",
67
],
78
"go.testEnvVars": {
89
"ROOT_CA_CRT": "${workspaceFolder}/.tmp/pki_certs/cloudca.pem",
@@ -13,9 +14,12 @@
1314
"MFG_KEY": "${workspaceFolder}/.tmp/pki_certs/mfgkey.pem",
1415
"IDENTITY_CRT": "${workspaceFolder}/.tmp/pki_certs/identitycrt.pem",
1516
"IDENTITY_KEY": "${workspaceFolder}/.tmp/pki_certs/identitykey.pem",
17+
"COAP_CRT": "${workspaceFolder}/.tmp/pki_certs/coapcrt.pem",
18+
"COAP_KEY": "${workspaceFolder}/.tmp/pki_certs/coapkey.pem",
19+
"CLOUD_SID": "adebc667-1f2b-41e3-bf5c-6d6eabc68cc6",
1620
},
1721
"files.watcherExclude": {
18-
"**/plgd-dev/device/v2/**": true
19-
},
22+
"**/plgd-dev/device/v2/**": true
23+
},
2024
"go.testTimeout": "600s",
2125
}

Makefile

Lines changed: 117 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@ SHELL = /bin/bash
22
SERVICE_NAME = cloud-server-test
33
VERSION_TAG = vnext-$(shell git rev-parse --short=7 --verify HEAD)
44
SIMULATOR_NAME_SUFFIX ?= $(shell hostname)
5+
USER_ID := $(shell id -u)
6+
GROUP_ID := $(shell id -g)
57
TMP_PATH = $(shell pwd)/.tmp
68
CERT_PATH = $(TMP_PATH)/pki_certs
9+
CLOUD_SID ?= adebc667-1f2b-41e3-bf5c-6d6eabc68cc6
710
DEVSIM_NET_HOST_PATH = $(shell pwd)/.tmp/devsim-net-host
811
CERT_TOOL_IMAGE ?= ghcr.io/plgd-dev/hub/cert-tool:vnext
912
# supported values: ECDSA-SHA256, ECDSA-SHA384, ECDSA-SHA512
1013
CERT_TOOL_SIGN_ALG ?= ECDSA-SHA256
1114
# supported values: P256, P384, P521
1215
CERT_TOOL_ELLIPTIC_CURVE ?= P256
1316
DEVSIM_IMAGE ?= ghcr.io/iotivity/iotivity-lite/cloud-server-discovery-resource-observable-debug:vnext
17+
HUB_TEST_DEVICE_IMAGE = ghcr.io/plgd-dev/hub/test-cloud-server:vnext-pr1202
1418

1519
default: build
1620

@@ -28,21 +32,58 @@ build-testcontainer:
2832

2933
build: build-testcontainer
3034

35+
ROOT_CA_CRT = $(CERT_PATH)/cloudca.pem
36+
ROOT_CA_KEY = $(CERT_PATH)/cloudcakey.pem
37+
INTERMEDIATE_CA_CRT = $(CERT_PATH)/intermediatecacrt.pem
38+
INTERMEDIATE_CA_KEY = $(CERT_PATH)/intermediatecakey.pem
39+
MFG_CRT = $(CERT_PATH)/mfgcrt.pem
40+
MFG_KEY = $(CERT_PATH)/mfgkey.pem
41+
COAP_CRT = $(CERT_PATH)/coapcrt.pem
42+
COAP_KEY = $(CERT_PATH)/coapkey.pem
43+
3144
certificates:
3245
mkdir -p $(CERT_PATH)
3346
chmod 0777 $(CERT_PATH)
3447
docker pull $(CERT_TOOL_IMAGE)
35-
docker run --rm -v $(CERT_PATH):/out $(CERT_TOOL_IMAGE) --outCert=/out/cloudca.pem --outKey=/out/cloudcakey.pem \
36-
--cert.subject.cn="ca" --cert.signatureAlgorithm=$(CERT_TOOL_SIGN_ALG) --cert.ellipticCurve=$(CERT_TOOL_ELLIPTIC_CURVE) \
37-
--cmd.generateRootCA
38-
docker run --rm -v $(CERT_PATH):/out $(CERT_TOOL_IMAGE) --signerCert=/out/cloudca.pem --signerKey=/out/cloudcakey.pem \
39-
--outCert=/out/intermediatecacrt.pem --outKey=/out/intermediatecakey.pem --cert.basicConstraints.maxPathLen=0 \
40-
--cert.subject.cn="intermediateCA" --cert.signatureAlgorithm=$(CERT_TOOL_SIGN_ALG) \
41-
--cert.ellipticCurve=$(CERT_TOOL_ELLIPTIC_CURVE) --cmd.generateIntermediateCA
42-
docker run --rm -v $(CERT_PATH):/out $(CERT_TOOL_IMAGE) --signerCert=/out/intermediatecacrt.pem \
43-
--signerKey=/out/intermediatecakey.pem --outCert=/out/mfgcrt.pem --outKey=/out/mfgkey.pem --cert.san.domain=localhost \
44-
--cert.san.ip=127.0.0.1 --cert.subject.cn="mfg" --cert.signatureAlgorithm=$(CERT_TOOL_SIGN_ALG) \
45-
--cert.ellipticCurve=$(CERT_TOOL_ELLIPTIC_CURVE) --cmd.generateCertificate
48+
49+
docker run \
50+
--rm -v $(CERT_PATH):/out \
51+
--user $(USER_ID):$(GROUP_ID) \
52+
$(CERT_TOOL_IMAGE) \
53+
--outCert=/out/cloudca.pem --outKey=/out/cloudcakey.pem \
54+
--cert.subject.cn="ca" --cert.signatureAlgorithm=$(CERT_TOOL_SIGN_ALG) --cert.ellipticCurve=$(CERT_TOOL_ELLIPTIC_CURVE) \
55+
--cmd.generateRootCA
56+
57+
docker run \
58+
--rm -v $(CERT_PATH):/out \
59+
--user $(USER_ID):$(GROUP_ID) \
60+
$(CERT_TOOL_IMAGE) \
61+
--signerCert=/out/cloudca.pem --signerKey=/out/cloudcakey.pem \
62+
--outCert=/out/intermediatecacrt.pem --outKey=/out/intermediatecakey.pem \
63+
--cert.basicConstraints.maxPathLen=0 --cert.subject.cn="intermediateCA" \
64+
--cert.ellipticCurve=$(CERT_TOOL_ELLIPTIC_CURVE) --cert.signatureAlgorithm=$(CERT_TOOL_SIGN_ALG) \
65+
--cmd.generateIntermediateCA
66+
67+
docker run \
68+
--rm -v $(CERT_PATH):/out \
69+
--user $(USER_ID):$(GROUP_ID) \
70+
$(CERT_TOOL_IMAGE) \
71+
--signerCert=/out/intermediatecacrt.pem --signerKey=/out/intermediatecakey.pem \
72+
--outCert=/out/mfgcrt.pem --outKey=/out/mfgkey.pem --cert.san.domain=localhost \
73+
--cert.san.ip=127.0.0.1 --cert.subject.cn="mfg" \
74+
--cert.signatureAlgorithm=$(CERT_TOOL_SIGN_ALG) --cert.ellipticCurve=$(CERT_TOOL_ELLIPTIC_CURVE) \
75+
--cmd.generateCertificate
76+
77+
docker run \
78+
--rm -v $(CERT_PATH):/out \
79+
--user $(USER_ID):$(GROUP_ID) \
80+
${CERT_TOOL_IMAGE} \
81+
--signerCert=/out/cloudca.pem --signerKey=/out/cloudcakey.pem \
82+
--outCert=/out/coapcrt.pem --outKey=/out/coapkey.pem \
83+
--cert.san.ip=127.0.0.1 --cert.san.domain=localhost \
84+
--cert.signatureAlgorithm=$(CERT_TOOL_SIGN_ALG) --cert.ellipticCurve=$(CERT_TOOL_ELLIPTIC_CURVE) \
85+
--cmd.generateCertificate --cert.subject.cn=uuid:$(CLOUD_SID)
86+
4687
sudo chown -R $(shell whoami) $(CERT_PATH)
4788
chmod -R 0777 $(CERT_PATH)
4889

@@ -64,8 +105,15 @@ env: clean certificates
64105

65106
unit-test: certificates
66107
mkdir -p $(TMP_PATH)
67-
go test -race -v ./schema/... -covermode=atomic -coverprofile=$(TMP_PATH)/schema.coverage.txt
68-
ROOT_CA_CRT="$(CERT_PATH)/cloudca.pem" ROOT_CA_KEY="$(CERT_PATH)/cloudcakey.pem" go test -race -v ./pkg/... -covermode=atomic -coverprofile=$(TMP_PATH)/pkg.coverage.txt
108+
ROOT_CA_CRT="$(ROOT_CA_CRT)" ROOT_CA_KEY="$(ROOT_CA_KEY)" \
109+
MFG_CRT="$(MFG_CRT)" MFG_KEY="$(MFG_KEY)" \
110+
INTERMEDIATE_CA_CRT="$(INTERMEDIATE_CA_CRT)" INTERMEDIATE_CA_KEY=$(INTERMEDIATE_CA_KEY) \
111+
COAP_CRT="$(COAP_CRT)" COAP_KEY="$(COAP_KEY)" \
112+
CLOUD_SID=$(CLOUD_SID) \
113+
go test -race -parallel 1 -v ./bridge/... -coverpkg=./... -covermode=atomic -coverprofile=$(TMP_PATH)/bridge.unit.coverage.txt
114+
go test -race -v ./schema/... -covermode=atomic -coverprofile=$(TMP_PATH)/schema.unit.coverage.txt
115+
ROOT_CA_CRT="$(ROOT_CA_CRT)" ROOT_CA_KEY="$(ROOT_CA_KEY)" \
116+
go test -race -v ./pkg/... -covermode=atomic -coverprofile=$(TMP_PATH)/pkg.unit.coverage.txt
69117

70118
test: env build-testcontainer
71119
docker run \
@@ -75,8 +123,63 @@ test: env build-testcontainer
75123
-v $(TMP_PATH):/tmp \
76124
$(SERVICE_NAME):$(VERSION_TAG) -test.parallel 1 -test.v -test.coverprofile=/tmp/coverage.txt
77125

126+
test-bridge:
127+
sudo rm -rf $(TMP_PATH)/data || :
128+
mkdir -p $(TMP_PATH)/data
129+
# pull image
130+
docker pull $(HUB_TEST_DEVICE_IMAGE)
131+
# prepare environment
132+
docker run \
133+
--rm \
134+
--network=host \
135+
--name hub-device-tests-environment \
136+
--env PREPARE_ENV=true \
137+
--env RUN=false \
138+
--env COAP_GATEWAY_CLOUD_ID="$(CLOUD_SID)" \
139+
-v $(TMP_PATH):/tmp \
140+
-v $(TMP_PATH)/data:/data \
141+
$(HUB_TEST_DEVICE_IMAGE)
142+
143+
# start device
144+
rm -rf $(TMP_PATH)/bridge || :
145+
mkdir -p $(TMP_PATH)/bridge
146+
go build -C ./test/bridge-device -cover -o ./bridge-device
147+
pkill -KILL bridge-device || :
148+
CLOUD_SID=$(CLOUD_SID) CA_POOL=$(TMP_PATH)/data/certs/root_ca.crt \
149+
CERT_FILE=$(TMP_PATH)/data/certs/external/coap-gateway.crt \
150+
KEY_FILE=$(TMP_PATH)/data/certs/external/coap-gateway.key \
151+
GOCOVERDIR=$(TMP_PATH)/bridge \
152+
./test/bridge-device/bridge-device &
153+
154+
# run tests
155+
docker run \
156+
--rm \
157+
--network=host \
158+
--name hub-device-tests \
159+
--env PREPARE_ENV=false \
160+
--env RUN=true \
161+
--env COAP_GATEWAY_CLOUD_ID="$(CLOUD_SID)" \
162+
--env TEST_DEVICE_NAME="bridged-device-0" \
163+
--env TEST_DEVICE_TYPE="bridged" \
164+
--env GRPC_GATEWAY_TEST_DISABLED=1 \
165+
--env IOTIVITY_LITE_TEST_RUN="(TestOffboard|TestOffboardWithoutSignIn|TestOffboardWithRepeat|TestRepublishAfterRefresh)$$" \
166+
-v $(TMP_PATH):/tmp \
167+
-v $(TMP_PATH)/data:/data \
168+
$(HUB_TEST_DEVICE_IMAGE)
169+
170+
# stop device
171+
pkill -TERM bridge-device || :
172+
while pgrep -x bridge-device > /dev/null; do \
173+
echo "waiting for bridge-device to exit"; \
174+
sleep 1; \
175+
done
176+
go tool covdata textfmt -i=$(TMP_PATH)/bridge -o $(TMP_PATH)/bridge.coverage.txt
177+
78178
clean:
79-
docker rm -f devsim-net-host || true
179+
docker rm -f devsim-net-host || :
180+
docker rm -f hub-device-tests-environment || :
181+
docker rm -f hub-device-tests || :
182+
pkill -KILL bridge-device || :
80183
sudo rm -rf .tmp/*
81184

82185
.PHONY: build-testcontainer build certificates clean env test unit-test

bridge/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# TODO
2+
3+
- Set RootCAs to the cloud connection
4+
- Unit tests
5+
- Set logger to package and propagate to package

bridge/device/cloud/config.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/****************************************************************************
2+
*
3+
* Copyright (c) 2023 plgd.dev s.r.o.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License"),
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14+
* either express or implied. See the License for the specific
15+
* language governing permissions and limitations under the License.
16+
*
17+
****************************************************************************/
18+
19+
package cloud
20+
21+
import (
22+
"github.com/plgd-dev/device/v2/schema/cloud"
23+
)
24+
25+
type Configuration struct {
26+
ResourceTypes []string `yaml:"-" json:"rt"`
27+
Interfaces []string `yaml:"-" json:"if"`
28+
Name string `yaml:"-" json:"n"`
29+
AuthorizationProvider string `yaml:"authorizationProvider" json:"apn"`
30+
CloudID string `yaml:"cloudID" json:"sid"`
31+
URL string `yaml:"cloudEndpoint" json:"cis"`
32+
LastErrorCode int `yaml:"-" json:"clec"`
33+
ProvisioningStatus cloud.ProvisioningStatus `yaml:"-" json:"cps"`
34+
AuthorizationCode string `yaml:"-" json:"-"`
35+
}

0 commit comments

Comments
 (0)